diff --git a/wordpress/wp-content/plugins/amp/amp.php b/wordpress/wp-content/plugins/amp/amp.php index ee55ba1740..d84a4c5d13 100644 --- a/wordpress/wp-content/plugins/amp/amp.php +++ b/wordpress/wp-content/plugins/amp/amp.php @@ -5,7 +5,7 @@ * Plugin URI: https://amp-wp.org * Author: AMP Project Contributors * Author URI: https://github.com/ampproject/amp-wp/graphs/contributors - * Version: 2.0.10 + * Version: 2.0.11 * License: GPLv2 or later * Requires at least: 4.9 * Requires PHP: 5.6 @@ -15,7 +15,7 @@ define( 'AMP__FILE__', __FILE__ ); define( 'AMP__DIR__', dirname( __FILE__ ) ); -define( 'AMP__VERSION', '2.0.10' ); +define( 'AMP__VERSION', '2.0.11' ); /** * Errors encountered while loading the plugin. diff --git a/wordpress/wp-content/plugins/amp/includes/admin/class-amp-post-meta-box.php b/wordpress/wp-content/plugins/amp/includes/admin/class-amp-post-meta-box.php index 9b3da6bd85..afef137c91 100644 --- a/wordpress/wp-content/plugins/amp/includes/admin/class-amp-post-meta-box.php +++ b/wordpress/wp-content/plugins/amp/includes/admin/class-amp-post-meta-box.php @@ -209,7 +209,7 @@ public function enqueue_admin_assets() { */ public function enqueue_block_assets() { $post = get_post(); - if ( ! in_array( $post->post_type, AMP_Post_Type_Support::get_eligible_post_types(), true ) ) { + if ( ! $post instanceof WP_Post || ! in_array( $post->post_type, AMP_Post_Type_Support::get_eligible_post_types(), true ) ) { return; } @@ -438,7 +438,10 @@ public function get_error_messages( $errors ) { if ( in_array( 'skip-post', $errors, true ) ) { $error_messages[] = __( 'A plugin or theme has disabled AMP support.', 'amp' ); } - if ( count( array_diff( $errors, [ 'post-type-support', 'skip-post', 'template_unsupported', 'no_matching_template' ] ) ) > 0 ) { + if ( in_array( 'invalid-post', $errors, true ) ) { + $error_messages[] = __( 'The post data could not be successfully retrieved.', 'amp' ); + } + if ( count( array_diff( $errors, [ 'post-type-support', 'skip-post', 'template_unsupported', 'no_matching_template', 'invalid-post' ] ) ) > 0 ) { $error_messages[] = __( 'Unavailable for an unknown reason.', 'amp' ); } diff --git a/wordpress/wp-content/plugins/amp/includes/amp-post-template-functions.php b/wordpress/wp-content/plugins/amp/includes/amp-post-template-functions.php index a60c9775da..bd714001dc 100644 --- a/wordpress/wp-content/plugins/amp/includes/amp-post-template-functions.php +++ b/wordpress/wp-content/plugins/amp/includes/amp-post-template-functions.php @@ -11,7 +11,11 @@ * @internal */ function amp_post_template_init_hooks() { - add_action( 'amp_post_template_head', 'noindex' ); + if ( version_compare( strtok( get_bloginfo( 'version' ), '-' ), '5.7', '>=' ) ) { + add_action( 'amp_post_template_head', 'wp_robots' ); + } else { + add_action( 'amp_post_template_head', 'noindex' ); + } add_action( 'amp_post_template_head', 'amp_post_template_add_title' ); add_action( 'amp_post_template_head', 'amp_post_template_add_canonical' ); add_action( 'amp_post_template_head', 'amp_post_template_add_fonts' ); diff --git a/wordpress/wp-content/plugins/amp/includes/class-amp-post-type-support.php b/wordpress/wp-content/plugins/amp/includes/class-amp-post-type-support.php index 696bff11a3..3d45606419 100644 --- a/wordpress/wp-content/plugins/amp/includes/class-amp-post-type-support.php +++ b/wordpress/wp-content/plugins/amp/includes/class-amp-post-type-support.php @@ -119,9 +119,15 @@ public static function add_post_type_support() { * @return array Error codes for why a given post does not have AMP support. */ public static function get_support_errors( $post ) { - if ( ! ( $post instanceof WP_Post ) ) { + if ( ! $post instanceof WP_Post ) { $post = get_post( $post ); } + + // If there's still not a valid post, then we have to abort. + if ( ! $post instanceof WP_Post ) { + return [ 'invalid-post' ]; + } + $errors = []; if ( ! in_array( $post->post_type, self::get_supported_post_types(), true ) ) { diff --git a/wordpress/wp-content/plugins/amp/includes/embeds/class-amp-core-block-handler.php b/wordpress/wp-content/plugins/amp/includes/embeds/class-amp-core-block-handler.php index dac094ff38..d1a2cc20eb 100644 --- a/wordpress/wp-content/plugins/amp/includes/embeds/class-amp-core-block-handler.php +++ b/wordpress/wp-content/plugins/amp/includes/embeds/class-amp-core-block-handler.php @@ -215,7 +215,9 @@ public function ampify_video_block( $block_content, $block ) { /** * Ampify cover block. * - * This specifically fixes the layout of the block when a background video is assigned. + * This ensures that the background img/video in a cover block has object-fit=cover and the appropriate object-position + * attribute so that they will be carried over to to the amp-img/amp-video and propagated to the img/video in the + * light shadow DOM. * * @see \AMP_Video_Sanitizer::filter_video_dimensions() * @@ -224,14 +226,59 @@ public function ampify_video_block( $block_content, $block ) { * @return string Filtered block content. */ public function ampify_cover_block( $block_content, $block ) { - if ( isset( $block['attrs']['backgroundType'] ) && 'video' === $block['attrs']['backgroundType'] ) { - $block_content = preg_replace( - '/(?<= or a is not going to be used. + // See . + if ( ! ( $is_video_background || $is_image_element ) ) { + return $block_content; } - return $block_content; + + $pattern = sprintf( + '#<%s(?= )[^>]*? class="(?:[^"]*? )?wp-block-cover__%s-background(?: [^"]*?)?"#', + $is_video_background ? 'video' : 'img', + $is_video_background ? 'video' : 'image' + ); + return preg_replace_callback( + $pattern, + static function ( $matches ) use ( $block ) { + $replacement = $matches[0]; + + // The background image/video for the cover block by definition needs object-fit="cover" on the resulting amp-ing/amp-video. + $replacement .= ' object-fit="cover"'; + + // Add the fill layout to skip needlessly obtaining the dimensions. + $replacement .= ' layout="fill"'; + + // Add object-position from the block's attributes to add to the img/video to be copied onto the amp-img/amp-video. + // The AMP runtime copies object-position attribute onto the underlying img/video for a given amp-img/amp-video. + // This is needed since the object-position property directly on an amp-img/amp-video will have no effect since + // since it is merely a wrapper for the underlying img/video element which actually supports the CSS property. + if ( isset( $block['attrs']['focalPoint']['x'], $block['attrs']['focalPoint']['y'] ) ) { + // See logic in Gutenberg for writing focal point to object-position attr: + // . + $replacement .= sprintf( + ' object-position="%d%% %d%%"', + round( (float) $block['attrs']['focalPoint']['x'] * 100 ), + round( (float) $block['attrs']['focalPoint']['y'] * 100 ) + ); + } + + return $replacement; + }, + $block_content, + 1 + ); } /** diff --git a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-base-sanitizer.php b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-base-sanitizer.php index 32badf4604..6f246e6b78 100644 --- a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-base-sanitizer.php +++ b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-base-sanitizer.php @@ -595,14 +595,34 @@ public function prepare_validation_error( array $error = [], array $data = [] ) } // Capture element contents. - if ( - ( 'script' === $node->nodeName && ! $node->hasAttribute( 'src' ) ) - || - // Include stylesheet text except for amp-custom and amp-keyframes since it is large and since it should - // already be detailed in the stylesheets metabox. - ( 'style' === $node->nodeName && ! $node->hasAttribute( 'amp-custom' ) && ! $node->hasAttribute( 'amp-keyframes' ) ) - ) { - $error['text'] = $node->textContent; + $is_inline_script = ( 'script' === $node->nodeName && ! $node->hasAttribute( 'src' ) ); + $is_inline_style = ( 'style' === $node->nodeName && ! $node->hasAttribute( 'amp-custom' ) && ! $node->hasAttribute( 'amp-keyframes' ) ); + if ( $is_inline_script || $is_inline_style ) { + $text_content = $node->textContent; + if ( $is_inline_script ) { + // For inline scripts, normalize string and number literals to prevent nonces, random numbers, and timestamps + // from generating endless number of validation errors. + $error['text'] = preg_replace( + [ + // Regex credit to . + '/"[^"\\\\\n]*(?:\\\\.[^"\\\\\n]*)*"/s', + '/\'[^\'\\\\\n]*(?:\\\\.[^\'\\\\\n]*)*\'/s', + '/(\b|-)\d+\.\d+\b/', + '/(\b|-)\d+\b/', + ], + [ + '__DOUBLE_QUOTED_STRING__', + '__SINGLE_QUOTED_STRING__', + '__FLOAT__', + '__INT__', + ], + $text_content + ); + } elseif ( $is_inline_style ) { + // Include stylesheet text except for amp-custom and amp-keyframes since it is large and since it should + // already be detailed in the stylesheets metabox. + $error['text'] = $text_content; + } } // Suppress 'ver' param from enqueued scripts and styles. diff --git a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-comments-sanitizer.php b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-comments-sanitizer.php index cf590eba2f..39b1a72715 100644 --- a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-comments-sanitizer.php +++ b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-comments-sanitizer.php @@ -66,31 +66,27 @@ public function sanitize() { * @param DOMElement $comment_form Comment form. */ protected function process_comment_form( $comment_form ) { - /** - * Element. - * - * @var DOMElement $element - */ - - /** - * Named input elements. - * - * @var DOMElement[][] $form_fields - */ $form_fields = []; foreach ( $comment_form->getElementsByTagName( 'input' ) as $element ) { + /** @var DOMElement $element */ $name = $element->getAttribute( 'name' ); if ( $name ) { $form_fields[ $name ][] = $element; } } foreach ( $comment_form->getElementsByTagName( 'textarea' ) as $element ) { + /** @var DOMElement $element */ $name = $element->getAttribute( 'name' ); if ( $name ) { $form_fields[ $name ][] = $element; } } + /** + * Named input elements. + * + * @var DOMElement[][] $form_fields + */ if ( empty( $form_fields['comment_post_ID'] ) ) { return; } diff --git a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-video-sanitizer.php b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-video-sanitizer.php index 0b0e3a3419..f929566b58 100644 --- a/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-video-sanitizer.php +++ b/wordpress/wp-content/plugins/amp/includes/sanitizers/class-amp-video-sanitizer.php @@ -294,6 +294,12 @@ private function filter_attributes( $attributes ) { $out['noloading'] = $value; break; + // Skip copying playsinline attributes which are automatically added by amp-video: + // . + case 'playsinline': + case 'webkit-playsinline': + break; + default: $out[ $name ] = $value; } diff --git a/wordpress/wp-content/plugins/amp/includes/utils/class-amp-dom-utils.php b/wordpress/wp-content/plugins/amp/includes/utils/class-amp-dom-utils.php index c453950fd9..0a6858af1f 100644 --- a/wordpress/wp-content/plugins/amp/includes/utils/class-amp-dom-utils.php +++ b/wordpress/wp-content/plugins/amp/includes/utils/class-amp-dom-utils.php @@ -13,7 +13,7 @@ * * Functionality to simplify working with Dom\Documents and DOMElements. * - * @internal + * @since 0.2 */ class AMP_DOM_Utils { diff --git a/wordpress/wp-content/plugins/amp/includes/validation/class-amp-validation-manager.php b/wordpress/wp-content/plugins/amp/includes/validation/class-amp-validation-manager.php index ce2ffb487a..2cb82b4593 100644 --- a/wordpress/wp-content/plugins/amp/includes/validation/class-amp-validation-manager.php +++ b/wordpress/wp-content/plugins/amp/includes/validation/class-amp-validation-manager.php @@ -79,10 +79,9 @@ class AMP_Validation_Manager { /** * The errors encountered when validating. * - * @var array[][] { - * @type array $error Error code. - * @type bool $sanitized Whether sanitized. - * @type string $slug Hash of the error. + * @var array[] { + * @type array $error Error data. + * @type bool $sanitized Whether sanitized. * } */ public static $validation_results = []; diff --git a/wordpress/wp-content/plugins/amp/readme.txt b/wordpress/wp-content/plugins/amp/readme.txt index 3c751c1f21..8f604e6d4f 100644 --- a/wordpress/wp-content/plugins/amp/readme.txt +++ b/wordpress/wp-content/plugins/amp/readme.txt @@ -2,8 +2,8 @@ Contributors: google, xwp, automattic, westonruter, albertomedina, schlessera, swissspidy, pierlo, johnwatkins0, joshuawold, ryankienstra Tags: amp, mobile, optimization, accelerated mobile pages, framework, components, blocks, performance, ux, seo, official Requires at least: 4.9 -Tested up to: 5.6 -Stable tag: 2.0.10 +Tested up to: 5.7 +Stable tag: 2.0.11 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Requires PHP: 5.6 diff --git a/wordpress/wp-content/plugins/amp/src/Admin/OnboardingWizardSubmenuPage.php b/wordpress/wp-content/plugins/amp/src/Admin/OnboardingWizardSubmenuPage.php index 25562a37d0..9a18557a90 100644 --- a/wordpress/wp-content/plugins/amp/src/Admin/OnboardingWizardSubmenuPage.php +++ b/wordpress/wp-content/plugins/amp/src/Admin/OnboardingWizardSubmenuPage.php @@ -9,13 +9,11 @@ namespace AmpProject\AmpWP\Admin; use AMP_Options_Manager; -use AmpProject\AmpWP\AmpSlugCustomizationWatcher; use AmpProject\AmpWP\DevTools\UserAccess; use AmpProject\AmpWP\Infrastructure\Conditional; use AmpProject\AmpWP\Infrastructure\Delayed; use AmpProject\AmpWP\Infrastructure\Registerable; use AmpProject\AmpWP\Infrastructure\Service; -use AmpProject\AmpWP\Option; use AmpProject\AmpWP\QueryVar; use AmpProject\AmpWP\Services; @@ -217,7 +215,7 @@ public function enqueue_assets( $hook_suffix ) { $theme = wp_get_theme(); $is_reader_theme = $this->reader_themes->theme_data_exists( get_stylesheet() ); - $exit_link = menu_page_url( AMP_Options_Manager::OPTION_NAME, false ); + $amp_settings_link = menu_page_url( AMP_Options_Manager::OPTION_NAME, false ); $setup_wizard_data = [ 'AMP_OPTIONS_KEY' => AMP_Options_Manager::OPTION_NAME, @@ -228,11 +226,11 @@ public function enqueue_assets( $hook_suffix ) { 'APP_ROOT_ID' => self::APP_ROOT_ID, 'CUSTOMIZER_LINK' => add_query_arg( [ - 'return' => rawurlencode( $exit_link ), + 'return' => rawurlencode( $amp_settings_link ), ], admin_url( 'customize.php' ) ), - 'CLOSE_LINK' => wp_get_referer() ?: $exit_link, + 'CLOSE_LINK' => $this->get_close_link(), // @todo As of June 2020, an upcoming WP release will allow this to be retrieved via REST. 'CURRENT_THEME' => [ 'name' => $theme->get( 'Name' ), @@ -242,7 +240,7 @@ public function enqueue_assets( $hook_suffix ) { 'url' => $theme->get( 'ThemeURI' ), ], 'USING_FALLBACK_READER_THEME' => $this->reader_themes->using_fallback_theme(), - 'FINISH_LINK' => $exit_link, + 'FINISH_LINK' => $amp_settings_link, 'OPTIONS_REST_PATH' => '/amp/v1/options', 'READER_THEMES_REST_PATH' => '/amp/v1/reader-themes', 'UPDATES_NONCE' => wp_create_nonce( 'updates' ), @@ -290,4 +288,20 @@ protected function add_preload_rest_paths() { $this->rest_preloader->add_preloaded_path( $path ); } } + + /** + * Determine URL that should be used to close the Onboarding Wizard. + * + * @return string Close link. + */ + public function get_close_link() { + $referer = wp_get_referer(); + + if ( $referer && 'wp-login.php' !== wp_basename( wp_parse_url( $referer, PHP_URL_PATH ) ) ) { + return $referer; + } + + // Default to the AMP Settings page if a referrer link could not be determined. + return menu_page_url( AMP_Options_Manager::OPTION_NAME, false ); + } } diff --git a/wordpress/wp-content/plugins/amp/src/Admin/OptionsMenu.php b/wordpress/wp-content/plugins/amp/src/Admin/OptionsMenu.php index ddc0976e33..20ea011353 100644 --- a/wordpress/wp-content/plugins/amp/src/Admin/OptionsMenu.php +++ b/wordpress/wp-content/plugins/amp/src/Admin/OptionsMenu.php @@ -13,7 +13,6 @@ use AmpProject\AmpWP\Infrastructure\Conditional; use AmpProject\AmpWP\Infrastructure\Registerable; use AmpProject\AmpWP\Infrastructure\Service; -use AmpProject\AmpWP\Option; /** * OptionsMenu class. @@ -134,6 +133,8 @@ public function get_menu_slug() { * Add menu. */ public function add_menu_items() { + require_once ABSPATH . '/wp-admin/includes/plugin.php'; + /* * Note that the admin items for Validated URLs and Validation Errors will also be placed under this admin menu * page when the current user can manage_options. diff --git a/wordpress/wp-content/plugins/amp/src/Admin/PluginRowMeta.php b/wordpress/wp-content/plugins/amp/src/Admin/PluginRowMeta.php new file mode 100644 index 0000000000..3b50ae5308 --- /dev/null +++ b/wordpress/wp-content/plugins/amp/src/Admin/PluginRowMeta.php @@ -0,0 +1,58 @@ +' . esc_html__( 'Contact support', 'amp' ) . '', + '' . esc_html__( 'Leave review', 'amp' ) . '', + ]; + + return array_merge( $meta, $additional_meta ); + } +} diff --git a/wordpress/wp-content/plugins/amp/src/AmpWpPlugin.php b/wordpress/wp-content/plugins/amp/src/AmpWpPlugin.php index 18ae82e38d..fcc9158b64 100644 --- a/wordpress/wp-content/plugins/amp/src/AmpWpPlugin.php +++ b/wordpress/wp-content/plugins/amp/src/AmpWpPlugin.php @@ -61,6 +61,7 @@ final class AmpWpPlugin extends ServiceBasedPlugin { 'admin.onboarding_wizard' => Admin\OnboardingWizardSubmenuPage::class, 'admin.options_menu' => Admin\OptionsMenu::class, 'admin.polyfills' => Admin\Polyfills::class, + 'admin.plugin_row_meta' => Admin\PluginRowMeta::class, 'amp_slug_customization_watcher' => AmpSlugCustomizationWatcher::class, 'css_transient_cache.ajax_handler' => Admin\ReenableCssTransientCachingAjaxAction::class, 'css_transient_cache.monitor' => BackgroundTask\MonitorCssTransientCaching::class, diff --git a/wordpress/wp-content/plugins/amp/src/BackgroundTask/MonitorCssTransientCaching.php b/wordpress/wp-content/plugins/amp/src/BackgroundTask/MonitorCssTransientCaching.php index fda860ddad..950dd4814d 100644 --- a/wordpress/wp-content/plugins/amp/src/BackgroundTask/MonitorCssTransientCaching.php +++ b/wordpress/wp-content/plugins/amp/src/BackgroundTask/MonitorCssTransientCaching.php @@ -92,21 +92,21 @@ protected function get_event_name() { * @todo This has arbitrary arguments to allow for testing, as we don't have dependency injection for services. * With dependency injection, we could for example inject a Clock object and mock it for testing. * - * @param DateTimeInterface $date Optional. Date to use for timestamping the processing (for testing). - * @param int $transient_count Optional. Count of transients to use for the processing (for testing). + * @param DateTimeInterface|string $date Optional. Date to use for timestamping the processing (for testing). An empty string is provided during cron. + * @param int|string $transient_count Optional. Count of transients to use for the processing (for testing). An empty string is provided during cron. * @return void * @throws Exception If a date could not be instantiated. */ - public function process( DateTimeInterface $date = null, $transient_count = null ) { + public function process( $date = '', $transient_count = '' ) { if ( wp_using_ext_object_cache() || $this->is_css_transient_caching_disabled() ) { return; } - if ( null === $date ) { + if ( ! $date instanceof DateTimeInterface ) { $date = new DateTimeImmutable(); } - if ( null === $transient_count ) { + if ( ! is_int( $transient_count ) ) { $transient_count = $this->query_css_transient_count(); } diff --git a/wordpress/wp-content/plugins/amp/src/Instrumentation/Event.php b/wordpress/wp-content/plugins/amp/src/Instrumentation/Event.php index 2e728e4643..891d19810d 100644 --- a/wordpress/wp-content/plugins/amp/src/Instrumentation/Event.php +++ b/wordpress/wp-content/plugins/amp/src/Instrumentation/Event.php @@ -96,6 +96,16 @@ public function add_properties( $properties ) { } } + /** + * Sanitize key to use it for an HTTP header label (alphanumeric and dashes/underscores only). + * + * @param string $key Unsanitized key. + * @return string Sanitized key. + */ + private function sanitize_key( $key ) { + return preg_replace( '/[^a-zA-Z0-9_-]+/', '_', $key ); + } + /** * Get the server timing header string. * @@ -108,19 +118,19 @@ public function get_header_string() { if ( is_float( $value ) ) { $property_strings[] = sprintf( ';%s="%.1f"', - addslashes( $property ), + $this->sanitize_key( $property ), $value ); } else { $property_strings[] = sprintf( ';%s="%s"', - addslashes( $property ), + $this->sanitize_key( $property ), addslashes( $value ) ); } } - $event_string = addslashes( $this->get_name() ); + $event_string = $this->sanitize_key( $this->get_name() ); $description = $this->get_description(); if ( ! empty( $description ) ) { diff --git a/wordpress/wp-content/plugins/amp/src/Instrumentation/ServerTiming.php b/wordpress/wp-content/plugins/amp/src/Instrumentation/ServerTiming.php index a67840eed9..bf626c1752 100644 --- a/wordpress/wp-content/plugins/amp/src/Instrumentation/ServerTiming.php +++ b/wordpress/wp-content/plugins/amp/src/Instrumentation/ServerTiming.php @@ -158,7 +158,7 @@ public function get_header_string() { return implode( ',', array_map( - static function ( $event ) { + static function ( Event $event ) { return $event->get_header_string(); }, $this->events diff --git a/wordpress/wp-content/plugins/amp/src/MobileRedirection.php b/wordpress/wp-content/plugins/amp/src/MobileRedirection.php index b5a5c75026..cc8d027f53 100644 --- a/wordpress/wp-content/plugins/amp/src/MobileRedirection.php +++ b/wordpress/wp-content/plugins/amp/src/MobileRedirection.php @@ -386,7 +386,63 @@ public function add_mobile_redirect_script() { $source = preg_replace( '/\bAMP_MOBILE_REDIRECTION\b/', wp_json_encode( $exports ), $source ); - printf( '', $source ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if ( function_exists( 'wp_print_inline_script_tag' ) ) { + wp_print_inline_script_tag( $source ); + } else { + echo $this->get_inline_script_tag( $source ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } + + /** + * Wraps inline JavaScript in `\n", $this->sanitize_script_attributes( $attributes ), $javascript ); + } + + /** + * Sanitizes an attributes array into an attributes string to be placed inside a `