AMP for WordPress - Version 1.4.3

Version Description

Download this release

Release Info

Developer westonruter
Plugin Icon 128x128 AMP for WordPress
Version 1.4.3
Comparing to
See all releases

Code changes from version 1.4.2 to 1.4.3

amp.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://amp-wp.org
6
  * Author: AMP Project Contributors
7
  * Author URI: https://github.com/ampproject/amp-wp/graphs/contributors
8
- * Version: 1.4.2
9
  * Text Domain: amp
10
  * Domain Path: /languages/
11
  * License: GPLv2 or later
@@ -15,7 +15,7 @@
15
 
16
  define( 'AMP__FILE__', __FILE__ );
17
  define( 'AMP__DIR__', dirname( __FILE__ ) );
18
- define( 'AMP__VERSION', '1.4.2' );
19
 
20
  /**
21
  * Errors encountered while loading the plugin.
@@ -374,9 +374,7 @@ function amp_init() {
374
  add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' );
375
  }
376
 
377
- if ( AMP_Options_Manager::is_stories_experience_enabled() ) {
378
- AMP_Story_Post_Type::register();
379
- }
380
 
381
  // Does its own is_stories_experience_enabled() check.
382
  add_action( 'wp_loaded', 'amp_story_templates' );
5
  * Plugin URI: https://amp-wp.org
6
  * Author: AMP Project Contributors
7
  * Author URI: https://github.com/ampproject/amp-wp/graphs/contributors
8
+ * Version: 1.4.3
9
  * Text Domain: amp
10
  * Domain Path: /languages/
11
  * License: GPLv2 or later
15
 
16
  define( 'AMP__FILE__', __FILE__ );
17
  define( 'AMP__DIR__', dirname( __FILE__ ) );
18
+ define( 'AMP__VERSION', '1.4.3' );
19
 
20
  /**
21
  * Errors encountered while loading the plugin.
374
  add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' );
375
  }
376
 
377
+ AMP_Story_Post_Type::register();
 
 
378
 
379
  // Does its own is_stories_experience_enabled() check.
380
  add_action( 'wp_loaded', 'amp_story_templates' );
includes/admin/class-amp-admin-pointers.php CHANGED
@@ -62,9 +62,9 @@ class AMP_Admin_Pointers {
62
  'amp_template_mode_pointer_10',
63
  [
64
  'selector' => '#toplevel_page_amp-options',
65
- 'heading' => __( 'AMP', 'amp' ),
66
- 'subheading' => __( 'New AMP Template Modes', 'amp' ),
67
- 'description' => __( 'You can now reuse your theme\'s templates and styles in AMP responses, in both “Transitional” and “Standard” modes.', 'amp' ),
68
  'position' => [
69
  'align' => 'middle',
70
  ],
@@ -74,34 +74,27 @@ class AMP_Admin_Pointers {
74
  ]
75
  ),
76
  new AMP_Admin_Pointer(
77
- 'amp_stories_support_pointer_12',
78
- [
79
- 'selector' => '#toplevel_page_amp-options',
80
- 'heading' => __( 'AMP', 'amp' ),
81
- 'subheading' => __( 'Stories', 'amp' ),
82
- 'description' => __( 'You can now enable Stories, a visual storytelling format for the open web which immerses your readers in fast-loading, full-screen, and visually rich experiences.', 'amp' ),
83
- 'position' => [
84
- 'align' => 'middle',
85
- ],
86
- 'active_callback' => static function( $hook_suffix ) {
87
- if ( 'toplevel_page_amp-options' === $hook_suffix ) {
88
- return false;
89
- }
90
- return ! AMP_Options_Manager::is_stories_experience_enabled();
91
- },
92
- ]
93
- ),
94
- new AMP_Admin_Pointer(
95
- 'amp_stories_menu_pointer_12',
96
  [
97
  'selector' => '#menu-posts-' . AMP_Story_Post_Type::POST_TYPE_SLUG,
98
- 'heading' => __( 'AMP', 'amp' ),
99
- 'description' => __( 'Head over here to create your first story.', 'amp' ),
 
 
 
 
 
 
 
 
 
 
 
100
  'position' => [
101
  'align' => 'middle',
102
  ],
103
- 'active_callback' => static function( $hook_suffix ) {
104
- if ( 'edit.php' === $hook_suffix && AMP_Story_Post_Type::POST_TYPE_SLUG === filter_input( INPUT_GET, 'post_type' ) ) {
105
  return false;
106
  }
107
  return AMP_Options_Manager::is_stories_experience_enabled();
62
  'amp_template_mode_pointer_10',
63
  [
64
  'selector' => '#toplevel_page_amp-options',
65
+ 'heading' => esc_html__( 'AMP', 'amp' ),
66
+ 'subheading' => esc_html__( 'New AMP Template Modes', 'amp' ),
67
+ 'description' => esc_html__( 'You can now reuse your theme\'s templates and styles in AMP responses, in both “Transitional” and “Standard” modes.', 'amp' ),
68
  'position' => [
69
  'align' => 'middle',
70
  ],
74
  ]
75
  ),
76
  new AMP_Admin_Pointer(
77
+ 'amp_stories_support_deprecated_pointer_143',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  [
79
  'selector' => '#menu-posts-' . AMP_Story_Post_Type::POST_TYPE_SLUG,
80
+ 'heading' => esc_html__( 'AMP', 'amp' ),
81
+ 'subheading' => esc_html__( 'Back up your Stories!', 'amp' ),
82
+ 'description' => implode(
83
+ ' ',
84
+ [
85
+ esc_html__( 'The Stories experience is being extracted from the AMP plugin into a separate standalone plugin which will be available soon. Please back up or export your existing Stories as they will not be available in the next version of the AMP plugin.', 'amp' ),
86
+ sprintf(
87
+ '<a href="%s" target="_blank">%s</a>',
88
+ esc_url( 'https://amp-wp.org/documentation/amp-stories/exporting-stories/' ),
89
+ esc_html__( 'View how to export your Stories', 'amp' )
90
+ ),
91
+ ]
92
+ ),
93
  'position' => [
94
  'align' => 'middle',
95
  ],
96
+ 'active_callback' => static function() {
97
+ if ( get_current_screen() && AMP_Story_Post_Type::POST_TYPE_SLUG === get_current_screen()->post_type ) {
98
  return false;
99
  }
100
  return AMP_Options_Manager::is_stories_experience_enabled();
includes/admin/class-amp-story-templates.php CHANGED
@@ -32,6 +32,10 @@ class AMP_Story_Templates {
32
  * Init.
33
  */
34
  public function init() {
 
 
 
 
35
  // Always hide the story templates.
36
  add_filter( 'pre_get_posts', [ $this, 'filter_pre_get_posts' ] );
37
 
@@ -41,10 +45,6 @@ class AMP_Story_Templates {
41
  // We need to register the taxonomy even if AMP Stories is disabled for tax_query.
42
  $this->register_taxonomy();
43
 
44
- if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) {
45
- return;
46
- }
47
-
48
  add_action( 'save_post_wp_block', [ $this, 'flag_template_as_modified' ] );
49
 
50
  $this->maybe_import_story_templates();
32
  * Init.
33
  */
34
  public function init() {
35
+ if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) {
36
+ return;
37
+ }
38
+
39
  // Always hide the story templates.
40
  add_filter( 'pre_get_posts', [ $this, 'filter_pre_get_posts' ] );
41
 
45
  // We need to register the taxonomy even if AMP Stories is disabled for tax_query.
46
  $this->register_taxonomy();
47
 
 
 
 
 
48
  add_action( 'save_post_wp_block', [ $this, 'flag_template_as_modified' ] );
49
 
50
  $this->maybe_import_story_templates();
includes/class-amp-story-post-type.php CHANGED
@@ -32,7 +32,14 @@ class AMP_Story_Post_Type {
32
  *
33
  * @var string
34
  */
35
- const REQUIRED_GUTENBERG_VERSION = '6.6';
 
 
 
 
 
 
 
36
 
37
  /**
38
  * The slug of the story card CSS file.
@@ -93,20 +100,22 @@ class AMP_Story_Post_Type {
93
  /**
94
  * Check if the required version of block capabilities available.
95
  *
96
- * Requires either Gutenberg 6.6+ or WordPress 5.3+ (which includes Gutenberg 6.6)
97
- *
98
- * @todo Eventually the Gutenberg requirement should be removed.
99
- *
100
  * @return bool Whether capabilities are available.
101
  */
102
  public static function has_required_block_capabilities() {
103
- return (
104
- ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE )
105
- ||
106
- ( defined( 'GUTENBERG_VERSION' ) && version_compare( GUTENBERG_VERSION, self::REQUIRED_GUTENBERG_VERSION, '>=' ) )
107
- ||
108
- version_compare( get_bloginfo( 'version' ), '5.3-RC2', '>=' )
109
- );
 
 
 
 
 
 
110
  }
111
 
112
  /**
@@ -115,7 +124,7 @@ class AMP_Story_Post_Type {
115
  * @return void
116
  */
117
  public static function register() {
118
- if ( ! AMP_Options_Manager::is_stories_experience_enabled() || ! self::has_required_block_capabilities() ) {
119
  return;
120
  }
121
 
@@ -2354,4 +2363,37 @@ class AMP_Story_Post_Type {
2354
  add_post_meta( $post_id, self::STORY_SETTINGS_META_PREFIX . $option_key, $sanitized_value, true );
2355
  }
2356
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2357
  }
32
  *
33
  * @var string
34
  */
35
+ const MIN_REQUIRED_GUTENBERG_VERSION = '6.6.0';
36
+
37
+ /**
38
+ * Maximum allowed version of Gutenberg.
39
+ *
40
+ * @var string
41
+ */
42
+ const MAX_REQUIRED_GUTENBERG_VERSION = '7.1.0';
43
 
44
  /**
45
  * The slug of the story card CSS file.
100
  /**
101
  * Check if the required version of block capabilities available.
102
  *
 
 
 
 
103
  * @return bool Whether capabilities are available.
104
  */
105
  public static function has_required_block_capabilities() {
106
+ if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) ) {
107
+ return false;
108
+ }
109
+
110
+ // If Gutenberg is installed only versions 6.6.0 - 7.1.0 are supported.
111
+ if ( defined( 'GUTENBERG_VERSION' ) ) {
112
+ return version_compare( GUTENBERG_VERSION, self::MIN_REQUIRED_GUTENBERG_VERSION, '>=' ) &&
113
+ version_compare( GUTENBERG_VERSION, self::MAX_REQUIRED_GUTENBERG_VERSION, '<=' );
114
+ }
115
+
116
+ // Only WordPress version 5.3 is supported (which includes Gutenberg 6.6.0).
117
+ return version_compare( get_bloginfo( 'version' ), '5.3-RC2', '>=' ) &&
118
+ version_compare( get_bloginfo( 'version' ), '5.4', '<' );
119
  }
120
 
121
  /**
124
  * @return void
125
  */
126
  public static function register() {
127
+ if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) {
128
  return;
129
  }
130
 
2363
  add_post_meta( $post_id, self::STORY_SETTINGS_META_PREFIX . $option_key, $sanitized_value, true );
2364
  }
2365
  }
2366
+
2367
+ /**
2368
+ * Returns total number of Story posts.
2369
+ *
2370
+ * @return int
2371
+ */
2372
+ public static function get_posts_count() {
2373
+ global $wpdb;
2374
+
2375
+ $cache_key = 'count-' . self::POST_TYPE_SLUG;
2376
+ $count = wp_cache_get( $cache_key );
2377
+ if ( false !== $count ) {
2378
+ return $count;
2379
+ }
2380
+
2381
+ // WPCS complains if the query isn't prepared directly inside $wpdb->get_col(); see <https://github.com/WordPress/WordPress-Coding-Standards/issues/1331>.
2382
+ $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM {$wpdb->posts} WHERE post_type = %s", self::POST_TYPE_SLUG ) );
2383
+
2384
+ $count = isset( $result ) ? (int) $result : 0;
2385
+
2386
+ wp_cache_set( $cache_key, $count );
2387
+
2388
+ return $count;
2389
+ }
2390
+
2391
+ /**
2392
+ * Check if there are any Story posts.
2393
+ *
2394
+ * @return bool
2395
+ */
2396
+ public static function has_posts() {
2397
+ return 0 < self::get_posts_count();
2398
+ }
2399
  }
includes/class-amp-theme-support.php CHANGED
@@ -1530,7 +1530,7 @@ class AMP_Theme_Support {
1530
  */
1531
  public static function filter_admin_bar_style_loader_tag( $tag, $handle ) {
1532
  if (
1533
- in_array( $handle, wp_styles()->registered['admin-bar']->deps, true ) ?
1534
  self::is_exclusively_dependent( wp_styles(), $handle, 'admin-bar' ) :
1535
  self::has_dependency( wp_styles(), $handle, 'admin-bar' )
1536
  ) {
@@ -1550,7 +1550,7 @@ class AMP_Theme_Support {
1550
  */
1551
  public static function filter_admin_bar_script_loader_tag( $tag, $handle ) {
1552
  if (
1553
- in_array( $handle, wp_scripts()->registered['admin-bar']->deps, true ) ?
1554
  self::is_exclusively_dependent( wp_scripts(), $handle, 'admin-bar' ) :
1555
  self::has_dependency( wp_scripts(), $handle, 'admin-bar' )
1556
  ) {
1530
  */
1531
  public static function filter_admin_bar_style_loader_tag( $tag, $handle ) {
1532
  if (
1533
+ is_array( wp_styles()->registered['admin-bar']->deps ) && in_array( $handle, wp_styles()->registered['admin-bar']->deps, true ) ?
1534
  self::is_exclusively_dependent( wp_styles(), $handle, 'admin-bar' ) :
1535
  self::has_dependency( wp_styles(), $handle, 'admin-bar' )
1536
  ) {
1550
  */
1551
  public static function filter_admin_bar_script_loader_tag( $tag, $handle ) {
1552
  if (
1553
+ is_array( wp_scripts()->registered['admin-bar']->deps ) && in_array( $handle, wp_scripts()->registered['admin-bar']->deps, true ) ?
1554
  self::is_exclusively_dependent( wp_scripts(), $handle, 'admin-bar' ) :
1555
  self::has_dependency( wp_scripts(), $handle, 'admin-bar' )
1556
  ) {
includes/embeds/class-amp-instagram-embed.php CHANGED
@@ -12,7 +12,7 @@
12
  */
13
  class AMP_Instagram_Embed_Handler extends AMP_Base_Embed_Handler {
14
  const SHORT_URL_HOST = 'instagr.am';
15
- const URL_PATTERN = '#http(s?)://(www\.)?instagr(\.am|am\.com)/p/([^/?]+)#i';
16
 
17
  /**
18
  * Default width.
12
  */
13
  class AMP_Instagram_Embed_Handler extends AMP_Base_Embed_Handler {
14
  const SHORT_URL_HOST = 'instagr.am';
15
+ const URL_PATTERN = '#https?:\/\/(www\.)?instagr(\.am|am\.com)\/(p|tv)\/([A-Za-z0-9-_]+)#i';
16
 
17
  /**
18
  * Default width.
includes/embeds/class-amp-vimeo-embed.php CHANGED
@@ -12,8 +12,18 @@
12
  */
13
  class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
14
 
15
- const URL_PATTERN = '#https?:\/\/(www\.)?vimeo\.com\/.*#i';
 
 
 
 
 
16
 
 
 
 
 
 
17
  const RATIO = 0.5625;
18
 
19
  /**
@@ -163,7 +173,7 @@ class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
163
 
164
  // @todo This will not get the private key for unlisted videos (which look like https://vimeo.com/123456789/abcdef0123), but amp-vimeo doesn't support them currently anyway.
165
  $video_id = '';
166
- if ( $path && preg_match( ':^/(\d+):', $path, $matches ) ) {
167
  $video_id = $matches[1];
168
  }
169
 
12
  */
13
  class AMP_Vimeo_Embed_Handler extends AMP_Base_Embed_Handler {
14
 
15
+ /**
16
+ * The embed URL pattern.
17
+ *
18
+ * @var string
19
+ */
20
+ const URL_PATTERN = '#https?:\/\/(.+\.)?vimeo\.com\/.*#i';
21
 
22
+ /**
23
+ * The aspect ratio.
24
+ *
25
+ * @var float
26
+ */
27
  const RATIO = 0.5625;
28
 
29
  /**
173
 
174
  // @todo This will not get the private key for unlisted videos (which look like https://vimeo.com/123456789/abcdef0123), but amp-vimeo doesn't support them currently anyway.
175
  $video_id = '';
176
+ if ( $path && preg_match( ':/(\d+):', $path, $matches ) ) {
177
  $video_id = $matches[1];
178
  }
179
 
includes/embeds/class-amp-wordpress-tv-embed-handler.php CHANGED
@@ -13,34 +13,40 @@
13
  */
14
  class AMP_WordPress_TV_Embed_Handler extends AMP_Base_Embed_Handler {
15
 
 
 
 
 
 
 
 
16
  /**
17
  * Register embed.
18
  */
19
  public function register_embed() {
20
- add_filter( 'render_block', [ $this, 'filter_rendered_block' ], 10, 2 );
21
  }
22
 
23
  /**
24
  * Unregister embed.
25
  */
26
  public function unregister_embed() {
27
- remove_filter( 'render_block', [ $this, 'filter_rendered_block' ], 10 );
28
  }
29
 
30
  /**
31
- * Filters the content of a single block to make it AMP valid.
32
  *
33
- * @param string $block_content The block content about to be appended.
34
- * @param array $block The full block, including name and attributes.
35
- * @return string Filtered block content.
36
  */
37
- public function filter_rendered_block( $block_content, $block ) {
38
- if ( ! isset( $block['blockName'] ) || 'core-embed/wordpress-tv' !== $block['blockName'] ) {
39
- return $block_content;
40
  }
41
 
42
- $modified_block_content = preg_replace( '#<script(?:\s.*?)?>.*?</script>#s', '', $block_content );
43
-
44
- return null !== $modified_block_content ? $modified_block_content : $block_content;
45
  }
46
  }
13
  */
14
  class AMP_WordPress_TV_Embed_Handler extends AMP_Base_Embed_Handler {
15
 
16
+ /**
17
+ * The URL pattern to determine if an embed URL is for this type, copied from WP_oEmbed.
18
+ *
19
+ * @see https://github.com/WordPress/wordpress-develop/blob/e13480/src/wp-includes/class-wp-oembed.php#L64
20
+ */
21
+ const URL_PATTERN = '#https?://wordpress\.tv/.*#i';
22
+
23
  /**
24
  * Register embed.
25
  */
26
  public function register_embed() {
27
+ add_filter( 'embed_oembed_html', [ $this, 'filter_oembed_html' ], 10, 2 );
28
  }
29
 
30
  /**
31
  * Unregister embed.
32
  */
33
  public function unregister_embed() {
34
+ remove_filter( 'embed_oembed_html', [ $this, 'filter_oembed_html' ], 10 );
35
  }
36
 
37
  /**
38
+ * Filters the oembed HTML to make it valid AMP.
39
  *
40
+ * @param mixed $cache The cached rendered markup.
41
+ * @param string $url The embed URL.
42
+ * @return string The filtered embed markup.
43
  */
44
+ public function filter_oembed_html( $cache, $url ) {
45
+ if ( ! preg_match( self::URL_PATTERN, $url ) ) {
46
+ return $cache;
47
  }
48
 
49
+ $modified_block_content = preg_replace( '#<script(?:\s.*?)?>.*?</script>#s', '', $cache );
50
+ return null !== $modified_block_content ? $modified_block_content : $cache;
 
51
  }
52
  }
includes/options/class-amp-options-manager.php CHANGED
@@ -66,10 +66,15 @@ class AMP_Options_Manager {
66
 
67
  add_action( 'update_option_' . self::OPTION_NAME, [ __CLASS__, 'maybe_flush_rewrite_rules' ], 10, 2 );
68
  add_action( 'admin_notices', [ __CLASS__, 'render_welcome_notice' ] );
 
69
  add_action( 'admin_notices', [ __CLASS__, 'persistent_object_caching_notice' ] );
70
  add_action( 'admin_notices', [ __CLASS__, 'render_cache_miss_notice' ] );
71
  add_action( 'admin_notices', [ __CLASS__, 'render_php_css_parser_conflict_notice' ] );
72
  add_action( 'admin_notices', [ __CLASS__, 'insecure_connection_notice' ] );
 
 
 
 
73
  }
74
 
75
  /**
@@ -202,11 +207,16 @@ class AMP_Options_Manager {
202
  * @return bool Enabled.
203
  */
204
  public static function is_stories_experience_enabled() {
205
- return (
206
- AMP_Story_Post_Type::has_required_block_capabilities()
207
- &&
208
- in_array( self::STORIES_EXPERIENCE, self::get_option( 'experiences' ), true )
209
- );
 
 
 
 
 
210
  }
211
 
212
  /**
@@ -223,8 +233,10 @@ class AMP_Options_Manager {
223
  }
224
 
225
  // Experiences.
226
- if ( isset( $new_options['experiences'] ) && is_array( $new_options['experiences'] ) ) {
227
-
 
 
228
  // Validate the selected experiences.
229
  $options['experiences'] = array_intersect(
230
  $new_options['experiences'],
@@ -343,16 +355,18 @@ class AMP_Options_Manager {
343
  AMP_Theme_Support::reset_cache_miss_url_option();
344
  }
345
 
346
- // Handle the base URL for exported stories.
347
- $options['story_export_base_url'] = isset( $new_options['story_export_base_url'] ) ? esc_url_raw( $new_options['story_export_base_url'], [ 'https' ] ) : '';
 
348
 
349
- // AMP stories settings definitions.
350
- $definitions = AMP_Story_Post_Type::get_stories_settings_definitions();
351
 
352
- // Handle the AMP stories settings sanitization.
353
- foreach ( $definitions as $option_name => $definition ) {
354
- $value = $new_options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ];
355
- $options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ] = call_user_func( $definition['meta_args']['sanitize_callback'], $value );
 
356
  }
357
 
358
  return $options;
@@ -420,6 +434,10 @@ class AMP_Options_Manager {
420
  * @return bool Whether update succeeded.
421
  */
422
  public static function update_option( $option, $value ) {
 
 
 
 
423
  $amp_options = self::get_options();
424
 
425
  $amp_options[ $option ] = $value;
@@ -564,6 +582,61 @@ class AMP_Options_Manager {
564
  }
565
  }
566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  /**
568
  * Render the cache miss admin notice.
569
  *
66
 
67
  add_action( 'update_option_' . self::OPTION_NAME, [ __CLASS__, 'maybe_flush_rewrite_rules' ], 10, 2 );
68
  add_action( 'admin_notices', [ __CLASS__, 'render_welcome_notice' ] );
69
+ add_action( 'admin_notices', [ __CLASS__, 'render_stories_deprecation_notice' ] );
70
  add_action( 'admin_notices', [ __CLASS__, 'persistent_object_caching_notice' ] );
71
  add_action( 'admin_notices', [ __CLASS__, 'render_cache_miss_notice' ] );
72
  add_action( 'admin_notices', [ __CLASS__, 'render_php_css_parser_conflict_notice' ] );
73
  add_action( 'admin_notices', [ __CLASS__, 'insecure_connection_notice' ] );
74
+
75
+ if ( self::is_stories_experience_enabled() ) {
76
+ add_action( 'enqueue_block_editor_assets', [ __CLASS__, 'render_stories_deprecation_editor_notice' ] );
77
+ }
78
  }
79
 
80
  /**
207
  * @return bool Enabled.
208
  */
209
  public static function is_stories_experience_enabled() {
210
+ $stories_enabled = in_array( self::STORIES_EXPERIENCE, self::get_option( 'experiences' ), true );
211
+
212
+ if ( $stories_enabled && ! AMP_Story_Post_Type::has_posts() ) {
213
+ if ( post_type_exists( AMP_Story_Post_Type::POST_TYPE_SLUG ) ) {
214
+ unregister_post_type( AMP_Story_Post_Type::POST_TYPE_SLUG );
215
+ }
216
+ return false;
217
+ }
218
+
219
+ return AMP_Story_Post_Type::has_required_block_capabilities() && $stories_enabled;
220
  }
221
 
222
  /**
233
  }
234
 
235
  // Experiences.
236
+ if ( ! isset( $new_options['experiences'][ self::STORIES_EXPERIENCE ] ) && ! AMP_Story_Post_Type::has_posts() ) {
237
+ // If there are no Story posts and the Story experience is disabled, only the Website experience is considered enabled.
238
+ $options['experiences'] = [ self::WEBSITE_EXPERIENCE ];
239
+ } elseif ( isset( $new_options['experiences'] ) && is_array( $new_options['experiences'] ) ) {
240
  // Validate the selected experiences.
241
  $options['experiences'] = array_intersect(
242
  $new_options['experiences'],
355
  AMP_Theme_Support::reset_cache_miss_url_option();
356
  }
357
 
358
+ if ( isset( $new_options['experiences'] ) && in_array( self::STORIES_EXPERIENCE, $new_options['experiences'], true ) ) {
359
+ // Handle the base URL for exported stories.
360
+ $options['story_export_base_url'] = isset( $new_options['story_export_base_url'] ) ? esc_url_raw( $new_options['story_export_base_url'], [ 'https' ] ) : '';
361
 
362
+ // AMP stories settings definitions.
363
+ $definitions = AMP_Story_Post_Type::get_stories_settings_definitions();
364
 
365
+ // Handle the AMP stories settings sanitization.
366
+ foreach ( $definitions as $option_name => $definition ) {
367
+ $value = $new_options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ];
368
+ $options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ] = call_user_func( $definition['meta_args']['sanitize_callback'], $value );
369
+ }
370
  }
371
 
372
  return $options;
434
  * @return bool Whether update succeeded.
435
  */
436
  public static function update_option( $option, $value ) {
437
+ if ( 'experiences' === $option && in_array( self::STORIES_EXPERIENCE, $value, true ) ) {
438
+ wp_cache_delete( 'count-' . AMP_Story_Post_Type::POST_TYPE_SLUG );
439
+ }
440
+
441
  $amp_options = self::get_options();
442
 
443
  $amp_options[ $option ] = $value;
582
  }
583
  }
584
 
585
+ /**
586
+ * Render the Stories deprecation admin notice.
587
+ */
588
+ public static function render_stories_deprecation_notice() {
589
+ if (
590
+ AMP_Story_Post_Type::has_posts() &&
591
+ (
592
+ 'edit-amp_story' === get_current_screen()->id ||
593
+ 'toplevel_page_' . self::OPTION_NAME === get_current_screen()->id
594
+ )
595
+ ) {
596
+ printf(
597
+ '<div class="notice notice-warning"><p>%s %s</p></div>',
598
+ esc_html__( 'The Stories experience is being extracted from the AMP plugin into a separate standalone plugin which will be available soon. Please back up or export your existing Stories as they will not be available in the next version of the AMP plugin.', 'amp' ),
599
+ sprintf(
600
+ '<a href="%s" target="_blank">%s</a>',
601
+ esc_url( 'https://amp-wp.org/documentation/amp-stories/exporting-stories/' ),
602
+ esc_html__( 'View how to export your Stories', 'amp' )
603
+ )
604
+ );
605
+ } elseif ( ! self::is_stories_experience_enabled() && 'toplevel_page_' . self::OPTION_NAME === get_current_screen()->id ) {
606
+ printf(
607
+ '<div class="notice notice-info"><p>%s</p></div>',
608
+ esc_html__( 'The Stories experience has been removed from the AMP plugin. This beta feature is being split into a separate standalone plugin which will be available for installation soon.', 'amp' )
609
+ );
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Render the Stories deprecation notice in the Story editor.
615
+ */
616
+ public static function render_stories_deprecation_editor_notice() {
617
+ $script = sprintf(
618
+ "( function( wp ) {
619
+ wp.data.dispatch( 'core/notices' ).createNotice(
620
+ 'warning',
621
+ %s,
622
+ {
623
+ isDismissible: false,
624
+ actions: [
625
+ {
626
+ url: 'https://amp-wp.org/documentation/amp-stories/exporting-stories/',
627
+ label: %s,
628
+ },
629
+ ],
630
+ }
631
+ );
632
+ } )( window.wp );",
633
+ wp_json_encode( __( 'The Stories experience is being extracted from the AMP plugin into a separate standalone plugin which will be available soon. Please back up or export your existing Stories as they will not be available in the next version of the AMP plugin.', 'amp' ) ),
634
+ wp_json_encode( __( 'View how to export your Stories', 'amp' ) )
635
+ );
636
+
637
+ wp_add_inline_script( AMP_Story_Post_Type::AMP_STORIES_SCRIPT_HANDLE, $script );
638
+ }
639
+
640
  /**
641
  * Render the cache miss admin notice.
642
  *
includes/options/class-amp-options-menu.php CHANGED
@@ -79,20 +79,22 @@ class AMP_Options_Menu {
79
  AMP_Options_Manager::OPTION_NAME
80
  );
81
 
82
- add_settings_field(
83
- 'experiences',
84
- __( 'Experiences', 'amp' ),
85
- [ $this, 'render_experiences' ],
86
- AMP_Options_Manager::OPTION_NAME,
87
- 'general',
88
- [
89
- 'class' => 'experiences',
90
- ]
91
- );
 
 
92
 
93
  add_settings_field(
94
  'theme_support',
95
- __( 'Website Mode', 'amp' ),
96
  [ $this, 'render_theme_support' ],
97
  AMP_Options_Manager::OPTION_NAME,
98
  'general',
@@ -112,31 +114,36 @@ class AMP_Options_Menu {
112
  ]
113
  );
114
 
115
- add_settings_field(
116
- 'stories_export',
117
- __( 'Stories Export', 'amp' ),
118
- [ $this, 'render_stories_export' ],
119
- AMP_Options_Manager::OPTION_NAME,
120
- 'general',
121
- [
122
- 'class' => 'amp-stories-export-field',
123
- ]
124
- );
 
125
 
126
- add_settings_field(
127
- 'stories_settings',
128
- __( 'Stories Settings', 'amp' ),
129
- [ $this, 'render_stories_settings' ],
130
- AMP_Options_Manager::OPTION_NAME,
131
- 'general',
132
- [
133
- 'class' => 'amp-stories-settings-field',
134
- ]
135
- );
 
136
 
137
  add_action(
138
  'admin_print_styles',
139
- function() {
 
 
 
140
  ?>
141
  <style>
142
  body:not(.amp-experience-website) .amp-website-mode,
@@ -218,42 +225,46 @@ class AMP_Options_Menu {
218
  );
219
  ?>
220
  </dd>
221
- <dt>
222
- <input type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[experiences][]' ); ?>" id="stories_experience" value="<?php echo esc_attr( AMP_Options_Manager::STORIES_EXPERIENCE ); ?>" <?php disabled( ! $has_required_block_capabilities ); ?> <?php checked( in_array( AMP_Options_Manager::STORIES_EXPERIENCE, $experiences, true ) ); ?>>
223
- <label for="stories_experience">
224
- <strong><?php echo wp_kses_post( __( 'Stories <span>Beta</span>', 'amp' ) ); ?></strong>
225
- </label>
226
- </dt>
227
- <dd>
228
- <?php if ( ! $has_required_block_capabilities ) : ?>
229
- <div class="notice notice-info notice-alt inline">
230
- <p>
231
- <?php
232
- $gutenberg = 'Gutenberg';
233
- // Link to Gutenberg plugin installation if eligible.
234
- if ( current_user_can( 'install_plugins' ) ) {
235
- $gutenberg = '<a href="' . esc_url( add_query_arg( 'tab', 'beta', admin_url( 'plugin-install.php' ) ) ) . '">' . $gutenberg . '</a>';
236
- }
237
- printf(
238
- /* translators: 1: WordPress version number. 2: Gutenberg plugin name */
239
- esc_html__( 'To use stories, you must be running WordPress %1$s or higher, or have the latest version of the %2$s plugin activated.', 'amp' ),
240
- '5.3',
241
- $gutenberg // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
242
- );
243
- ?>
244
- </p>
245
- </div>
246
- <?php endif; ?>
247
- <?php
248
- echo wp_kses_post(
249
- sprintf(
250
- /* translators: %s: Stories documentation URL. */
251
- __( 'Stories is a visual storytelling format for the open web which immerses your readers in fast-loading, full-screen, and visually rich experiences. Stories can be a great addition to your overall content strategy. Read more about <a href="%s" target="_blank">AMP Stories</a>.', 'amp' ),
252
- esc_url( 'https://amp.dev/about/stories' )
253
- )
254
- );
255
- ?>
256
- </dd>
 
 
 
 
257
  </dl>
258
  <script>
259
  /*
79
  AMP_Options_Manager::OPTION_NAME
80
  );
81
 
82
+ if ( AMP_Story_Post_Type::has_posts() ) {
83
+ add_settings_field(
84
+ 'experiences',
85
+ __( 'Experiences', 'amp' ),
86
+ [ $this, 'render_experiences' ],
87
+ AMP_Options_Manager::OPTION_NAME,
88
+ 'general',
89
+ [
90
+ 'class' => 'experiences',
91
+ ]
92
+ );
93
+ }
94
 
95
  add_settings_field(
96
  'theme_support',
97
+ __( 'Template Mode', 'amp' ),
98
  [ $this, 'render_theme_support' ],
99
  AMP_Options_Manager::OPTION_NAME,
100
  'general',
114
  ]
115
  );
116
 
117
+ if ( AMP_Story_Post_Type::has_posts() ) {
118
+ add_settings_field(
119
+ 'stories_export',
120
+ __( 'Stories Export', 'amp' ),
121
+ [ $this, 'render_stories_export' ],
122
+ AMP_Options_Manager::OPTION_NAME,
123
+ 'general',
124
+ [
125
+ 'class' => 'amp-stories-export-field',
126
+ ]
127
+ );
128
 
129
+ add_settings_field(
130
+ 'stories_settings',
131
+ __( 'Stories Settings', 'amp' ),
132
+ [ $this, 'render_stories_settings' ],
133
+ AMP_Options_Manager::OPTION_NAME,
134
+ 'general',
135
+ [
136
+ 'class' => 'amp-stories-settings-field',
137
+ ]
138
+ );
139
+ }
140
 
141
  add_action(
142
  'admin_print_styles',
143
+ static function() {
144
+ if ( ! AMP_Story_Post_Type::has_posts() ) {
145
+ return;
146
+ }
147
  ?>
148
  <style>
149
  body:not(.amp-experience-website) .amp-website-mode,
225
  );
226
  ?>
227
  </dd>
228
+ <?php if ( AMP_Story_Post_Type::has_posts() ) : ?>
229
+ <dt>
230
+ <input type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[experiences][]' ); ?>" id="stories_experience" value="<?php echo esc_attr( AMP_Options_Manager::STORIES_EXPERIENCE ); ?>" <?php disabled( ! $has_required_block_capabilities ); ?> <?php checked( in_array( AMP_Options_Manager::STORIES_EXPERIENCE, $experiences, true ) ); ?>>
231
+ <label for="stories_experience">
232
+ <strong><?php echo wp_kses_post( __( 'Stories <span>Beta</span>', 'amp' ) ); ?></strong>
233
+ </label>
234
+ </dt>
235
+ <dd>
236
+ <?php if ( ! $has_required_block_capabilities ) : ?>
237
+ <div class="notice notice-info notice-alt inline">
238
+ <p>
239
+ <?php
240
+ $gutenberg = 'Gutenberg';
241
+ // Link to Gutenberg plugin installation if eligible.
242
+ if ( current_user_can( 'install_plugins' ) ) {
243
+ $gutenberg = '<a href="' . esc_url( add_query_arg( 'tab', 'featured', admin_url( 'plugin-install.php' ) ) ) . '">' . $gutenberg . '</a>';
244
+ }
245
+ printf(
246
+ /* translators: 1: WordPress version number. 2: Gutenberg plugin name, */
247
+ esc_html__( 'To use Stories, you must be running WordPress %1$s, or have a version between %2$s and %3$s of the %4$s plugin activated.', 'amp' ),
248
+ '5.3',
249
+ '6.6.0',
250
+ '7.1.0',
251
+ $gutenberg // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
252
+ );
253
+ ?>
254
+ </p>
255
+ </div>
256
+ <?php endif; ?>
257
+ <?php
258
+ echo wp_kses_post(
259
+ sprintf(
260
+ /* translators: %s: Stories documentation URL. */
261
+ __( 'Stories is a visual storytelling format for the open web which immerses your readers in fast-loading, full-screen, and visually rich experiences. Stories can be a great addition to your overall content strategy. Read more about <a href="%s" target="_blank">AMP Stories</a>.', 'amp' ),
262
+ esc_url( 'https://amp.dev/about/stories' )
263
+ )
264
+ );
265
+ ?>
266
+ </dd>
267
+ <?php endif; ?>
268
  </dl>
269
  <script>
270
  /*
includes/sanitizers/class-amp-form-sanitizer.php CHANGED
@@ -58,20 +58,8 @@ class AMP_Form_Sanitizer extends AMP_Base_Sanitizer {
58
  $node->setAttribute( 'method', $method );
59
  }
60
 
61
- /*
62
- * In HTML, the default action is just the current URL that the page is served from.
63
- * The action "specifies a server endpoint to handle the form input. The value must be an
64
- * https URL and must not be a link to a CDN".
65
- */
66
- if ( ! $node->getAttribute( 'action' ) ) {
67
- $action_url = esc_url_raw( '//' . $_SERVER['HTTP_HOST'] . wp_unslash( $_SERVER['REQUEST_URI'] ) );
68
- } else {
69
- $action_url = $node->getAttribute( 'action' );
70
- // Check if action_url is a relative path and add the host to it.
71
- if ( ! preg_match( '#^(https?:)?//#', $action_url ) ) {
72
- $action_url = esc_url_raw( '//' . $_SERVER['HTTP_HOST'] . $action_url );
73
- }
74
- }
75
  $xhr_action = $node->getAttribute( 'action-xhr' );
76
 
77
  // Make HTTP URLs protocol-less, since HTTPS is required for forms.
@@ -121,6 +109,81 @@ class AMP_Form_Sanitizer extends AMP_Base_Sanitizer {
121
  }
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * Ensure that the form has a submit-success and submit-error element templates.
126
  *
58
  $node->setAttribute( 'method', $method );
59
  }
60
 
61
+ $action_url = $this->get_action_url( $node->getAttribute( 'action' ) );
62
+
 
 
 
 
 
 
 
 
 
 
 
 
63
  $xhr_action = $node->getAttribute( 'action-xhr' );
64
 
65
  // Make HTTP URLs protocol-less, since HTTPS is required for forms.
109
  }
110
  }
111
 
112
+ /**
113
+ * Get the action URL for the form element.
114
+ *
115
+ * @param string $action_url Action URL.
116
+ * @return string Action URL.
117
+ */
118
+ protected function get_action_url( $action_url ) {
119
+ /*
120
+ * In HTML, the default action is just the current URL that the page is served from.
121
+ * The action "specifies a server endpoint to handle the form input. The value must be an
122
+ * https URL and must not be a link to a CDN".
123
+ */
124
+ if ( ! $action_url ) {
125
+ return esc_url_raw( '//' . $_SERVER['HTTP_HOST'] . wp_unslash( $_SERVER['REQUEST_URI'] ) );
126
+ }
127
+
128
+ $parsed_url = wp_parse_url( $action_url );
129
+
130
+ if (
131
+ // Ignore a malformed URL - it will be later sanitized.
132
+ false === $parsed_url
133
+ ||
134
+ // Ignore HTTPS URLs, because there is nothing left to do.
135
+ ( isset( $parsed_url['scheme'] ) && 'https' === $parsed_url['scheme'] )
136
+ ||
137
+ // Ignore protocol-relative URLs, because there is also nothing left to do.
138
+ ( ! isset( $parsed_url['scheme'] ) && isset( $parsed_url['host'] ) )
139
+ ) {
140
+ return $action_url;
141
+ }
142
+
143
+ // Make URL protocol relative.
144
+ $parsed_url['scheme'] = '//';
145
+
146
+ // Set an empty path if none is defined but there is a host.
147
+ if ( ! isset( $parsed_url['path'] ) && isset( $parsed_url['host'] ) ) {
148
+ $parsed_url['path'] = '';
149
+ }
150
+
151
+ if ( ! isset( $parsed_url['host'] ) ) {
152
+ $parsed_url['host'] = $_SERVER['HTTP_HOST'];
153
+ }
154
+
155
+ if ( ! isset( $parsed_url['path'] ) ) {
156
+ // If there is action URL path, use the one from the request.
157
+ $parsed_url['path'] = trailingslashit( wp_unslash( $_SERVER['REQUEST_URI'] ) );
158
+ } elseif ( '' !== $parsed_url['path'] && '/' !== $parsed_url['path'][0] ) {
159
+ // If the path is relative, append it to the current request path.
160
+ $parsed_url['path'] = trailingslashit( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . trailingslashit( $parsed_url['path'] );
161
+ }
162
+
163
+ // Rebuild the URL.
164
+ $action_url = $parsed_url['scheme'];
165
+ if ( isset( $parsed_url['user'] ) ) {
166
+ $action_url .= $parsed_url['user'];
167
+ if ( isset( $parsed_url['pass'] ) ) {
168
+ $action_url .= ':' . $parsed_url['pass'];
169
+ }
170
+ $action_url .= '@';
171
+ }
172
+ $action_url .= $parsed_url['host'];
173
+ if ( isset( $parsed_url['port'] ) ) {
174
+ $action_url .= ':' . $parsed_url['port'];
175
+ }
176
+ $action_url .= $parsed_url['path'];
177
+ if ( isset( $parsed_url['query'] ) ) {
178
+ $action_url .= '?' . $parsed_url['query'];
179
+ }
180
+ if ( isset( $parsed_url['fragment'] ) ) {
181
+ $action_url .= '#' . $parsed_url['fragment'];
182
+ }
183
+
184
+ return esc_url_raw( $action_url );
185
+ }
186
+
187
  /**
188
  * Ensure that the form has a submit-success and submit-error element templates.
189
  *
readme.txt CHANGED
@@ -1,14 +1,14 @@
1
  === AMP ===
2
  Contributors: google, xwp, automattic, westonruter, swissspidy, miinasikk, ryankienstra, albertomedina, tweetythierry
3
- Tags: amp, stories, mobile, optimization, accelerated mobile pages, framework, components, blocks, performance, ux, seo, official
4
  Requires at least: 4.9
5
  Tested up to: 5.3.2
6
- Stable tag: 1.4.2
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Requires PHP: 5.4
10
 
11
- Official AMP plugin, supported by the AMP team. Formerly Accelerated Mobile Pages, AMP enables great experiences across mobile, desktop, and stories.
12
 
13
  == Description ==
14
 
@@ -20,7 +20,8 @@ Features and capabilities provided by the plugin include:
20
  - **Core Theme Support**: enabling AMP compatibility for all core themes, from Twenty Ten all the way through Twenty Twenty.
21
  - **Compatibility Tool**: when automatic conversion of markup to AMP is not possible, debug AMP validation errors with detailed information including the invalid markup and the specific components responsible on site (e.g theme, plugin, embed); validation errors are shown contextually with their respective blocks in the editor.
22
  - **CSS Tree Shaking**: automatically remove the majority of unused CSS to bring the total under AMP's 50KB limit; when the total after tree shaking is still over this limit, prioritization is used so that the all-important theme stylesheet important is retained, leaving less important ones to be excluded (e.g. print styles).
23
- - ✨ **AMP Stories** (beta): the AMP plugin enables the creation, editing, and publishing of [AMP Stories](https://amp.dev/about/stories) in WordPress; leverage the magic of storytelling the WordPress way!
 
24
 
25
  The plugin can be configured to follow one of three different template modes: Standard, Transitional, and Reader. In Standard mode you use AMP as the framework for your site, and there need not be any separate AMP and non-AMP versions. When configured to operate in Reader and Transitional modes, a given page will have a canonical URL as well as a corresponding (paired) AMP URL. The AMP plugin is not serving as a mobile theme; it does not redirect mobile devices to the AMP version. Instead, the AMP version is served to mobile visitors when they find the content on platforms such as Twitter, Pinterest, Google Search, and others. Reader mode only supports serving AMP for singular posts, pages, and other post types, whereas Standard and Transitional mode support serving the entire site as AMP.
26
 
@@ -44,21 +45,19 @@ If you are a developer, we encourage you to [follow along](https://github.com/am
44
 
45
  == Screenshots ==
46
 
47
- 1. Create great web experiences via AMP-powered websites or visually rich, engaging stories.
48
- 2. Story editor enables creation of pages in a horizontal, page-based interface, with background media, with blocks that can be dragged, rotated, and animated.
49
- 3. In the website experience, theme support enables you to reuse the active theme's templates and stylesheets; all WordPress features (menus, widgets, comments) are available in AMP.
50
- 4. All core themes are supported, and many themes can be served as AMP with minimal changes, Otherwise, behavior is often as if JavaScript is turned off in the browser since scripts are removed.
51
- 5. Reader mode templates are still available, but they are differ from the active theme.
52
- 6. Switch from Reader mode to Transitional or Standard mode in AMP settings screen.
53
- 7. Standard mode: Using AMP as the framework for your site, not having to maintain an AMP and non-AMP version. Mobile and desktop users get same experience.
54
- 8. Transitional mode: A path to making your site fully AMP-compatible, with tools to assist with debugging validation issues along the way.
55
- 9. Make the entire site available in AMP or pick specific post types and templates; you can also opt-out on per-post basis.
56
- 10. Plugin checks for AMP validity and will indicate when: no issues are found, new issues need review, or issues block AMP from being served.
57
- 11. The editor will surface validation issues during content authoring. The specific blocks with validation errors are indicated.
58
- 12. Each Validated URL shows the list of validation errors encountered, giving control over whether invalid markup is removed or kept. Keeping invalid markup disables AMP.
59
- 13. Each validation error provides a stack trace to identify which code is responsible for the invalid markup, whether a theme, plugin, embed, content block, and so on.
60
- 14. Styles added by themes and plugins are automatically concatenated, minified, and tree-shaken to try to keep the total under 50KB of inline CSS.
61
- 15. A WP-CLI command is provided to check the URLs on a site for AMP validity. Results are available in the admin for inspection.
62
 
63
  == Changelog ==
64
 
1
  === AMP ===
2
  Contributors: google, xwp, automattic, westonruter, swissspidy, miinasikk, ryankienstra, albertomedina, tweetythierry
3
+ Tags: amp, mobile, optimization, accelerated mobile pages, framework, components, blocks, performance, ux, seo, official
4
  Requires at least: 4.9
5
  Tested up to: 5.3.2
6
+ Stable tag: 1.4.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Requires PHP: 5.4
10
 
11
+ Official AMP plugin, supported by the AMP team. Formerly Accelerated Mobile Pages, AMP enables great experiences across both mobile and desktop.
12
 
13
  == Description ==
14
 
20
  - **Core Theme Support**: enabling AMP compatibility for all core themes, from Twenty Ten all the way through Twenty Twenty.
21
  - **Compatibility Tool**: when automatic conversion of markup to AMP is not possible, debug AMP validation errors with detailed information including the invalid markup and the specific components responsible on site (e.g theme, plugin, embed); validation errors are shown contextually with their respective blocks in the editor.
22
  - **CSS Tree Shaking**: automatically remove the majority of unused CSS to bring the total under AMP's 50KB limit; when the total after tree shaking is still over this limit, prioritization is used so that the all-important theme stylesheet important is retained, leaving less important ones to be excluded (e.g. print styles).
23
+
24
+ Please note that the [Stories](https://amp.dev/about/stories) experience is being removed from the AMP plugin in favor of the feature being released as a standalone plugin. It will be available soon!
25
 
26
  The plugin can be configured to follow one of three different template modes: Standard, Transitional, and Reader. In Standard mode you use AMP as the framework for your site, and there need not be any separate AMP and non-AMP versions. When configured to operate in Reader and Transitional modes, a given page will have a canonical URL as well as a corresponding (paired) AMP URL. The AMP plugin is not serving as a mobile theme; it does not redirect mobile devices to the AMP version. Instead, the AMP version is served to mobile visitors when they find the content on platforms such as Twitter, Pinterest, Google Search, and others. Reader mode only supports serving AMP for singular posts, pages, and other post types, whereas Standard and Transitional mode support serving the entire site as AMP.
27
 
45
 
46
  == Screenshots ==
47
 
48
+ 1. In the website experience, theme support enables you to reuse the active theme's templates and stylesheets; all WordPress features (menus, widgets, comments) are available in AMP.
49
+ 2. All core themes are supported, and many themes can be served as AMP with minimal changes, Otherwise, behavior is often as if JavaScript is turned off in the browser since scripts are removed.
50
+ 3. Reader mode templates are still available, but they are differ from the active theme.
51
+ 4. Switch from Reader mode to Transitional or Standard mode in AMP settings screen.
52
+ 5. Standard mode: Using AMP as the framework for your site, not having to maintain an AMP and non-AMP version. Mobile and desktop users get same experience.
53
+ 6. Transitional mode: A path to making your site fully AMP-compatible, with tools to assist with debugging validation issues along the way.
54
+ 7. Make the entire site available in AMP or pick specific post types and templates; you can also opt-out on per-post basis.
55
+ 8. Plugin checks for AMP validity and will indicate when: no issues are found, new issues need review, or issues block AMP from being served.
56
+ 9. The editor will surface validation issues during content authoring. The specific blocks with validation errors are indicated.
57
+ 10. Each Validated URL shows the list of validation errors encountered, giving control over whether invalid markup is removed or kept. Keeping invalid markup disables AMP.
58
+ 11. Each validation error provides a stack trace to identify which code is responsible for the invalid markup, whether a theme, plugin, embed, content block, and so on.
59
+ 12. Styles added by themes and plugins are automatically concatenated, minified, and tree-shaken to try to keep the total under 50KB of inline CSS.
60
+ 13. A WP-CLI command is provided to check the URLs on a site for AMP validity. Results are available in the admin for inspection.
 
 
61
 
62
  == Changelog ==
63
 
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInitcb388a4b6a7e40c8dc0a084811af68a2::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitebb5645c12770c55500ceb765fc622fb::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInitcb388a4b6a7e40c8dc0a084811af68a2
6
  {
7
  private static $loader;
8
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInitcb388a4b6a7e40c8dc0a084811af68a2
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInitcb388a4b6a7e40c8dc0a084811af68a2', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInitcb388a4b6a7e40c8dc0a084811af68a2', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitebb5645c12770c55500ceb765fc622fb
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitebb5645c12770c55500ceb765fc622fb', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitebb5645c12770c55500ceb765fc622fb', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
+ call_user_func(\Composer\Autoload\ComposerStaticInitebb5645c12770c55500ceb765fc622fb::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'W' =>
@@ -80,10 +80,10 @@ class ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2
80
  public static function getInitializer(ClassLoader $loader)
81
  {
82
  return \Closure::bind(function () use ($loader) {
83
- $loader->prefixLengthsPsr4 = ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2::$prefixLengthsPsr4;
84
- $loader->prefixDirsPsr4 = ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2::$prefixDirsPsr4;
85
- $loader->prefixesPsr0 = ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2::$prefixesPsr0;
86
- $loader->classMap = ComposerStaticInitcb388a4b6a7e40c8dc0a084811af68a2::$classMap;
87
 
88
  }, null, ClassLoader::class);
89
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInitebb5645c12770c55500ceb765fc622fb
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'W' =>
80
  public static function getInitializer(ClassLoader $loader)
81
  {
82
  return \Closure::bind(function () use ($loader) {
83
+ $loader->prefixLengthsPsr4 = ComposerStaticInitebb5645c12770c55500ceb765fc622fb::$prefixLengthsPsr4;
84
+ $loader->prefixDirsPsr4 = ComposerStaticInitebb5645c12770c55500ceb765fc622fb::$prefixDirsPsr4;
85
+ $loader->prefixesPsr0 = ComposerStaticInitebb5645c12770c55500ceb765fc622fb::$prefixesPsr0;
86
+ $loader->classMap = ComposerStaticInitebb5645c12770c55500ceb765fc622fb::$classMap;
87
 
88
  }, null, ClassLoader::class);
89
  }