diff --git a/amp.php b/amp.php index b92cdd00b66..79c191b1c0e 100644 --- a/amp.php +++ b/amp.php @@ -383,9 +383,7 @@ function amp_init() { add_filter( 'old_slug_redirect_url', 'amp_redirect_old_slug_to_new_url' ); } - if ( AMP_Options_Manager::is_stories_experience_enabled() ) { - AMP_Story_Post_Type::register(); - } + AMP_Story_Post_Type::register(); // Does its own is_stories_experience_enabled() check. add_action( 'wp_loaded', 'amp_story_templates' ); diff --git a/includes/admin/class-amp-admin-pointers.php b/includes/admin/class-amp-admin-pointers.php index e0b8aca838f..4895ad1d339 100644 --- a/includes/admin/class-amp-admin-pointers.php +++ b/includes/admin/class-amp-admin-pointers.php @@ -62,9 +62,9 @@ private function get_pointers() { 'amp_template_mode_pointer_10', [ 'selector' => '#toplevel_page_amp-options', - 'heading' => __( 'AMP', 'amp' ), - 'subheading' => __( 'New AMP Template Modes', 'amp' ), - 'description' => __( 'You can now reuse your theme\'s templates and styles in AMP responses, in both “Transitional” and “Standard” modes.', 'amp' ), + 'heading' => esc_html__( 'AMP', 'amp' ), + 'subheading' => esc_html__( 'New AMP Template Modes', 'amp' ), + 'description' => esc_html__( 'You can now reuse your theme\'s templates and styles in AMP responses, in both “Transitional” and “Standard” modes.', 'amp' ), 'position' => [ 'align' => 'middle', ], @@ -74,34 +74,27 @@ private function get_pointers() { ] ), new AMP_Admin_Pointer( - 'amp_stories_support_pointer_12', - [ - 'selector' => '#toplevel_page_amp-options', - 'heading' => __( 'AMP', 'amp' ), - 'subheading' => __( 'Stories', 'amp' ), - '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' ), - 'position' => [ - 'align' => 'middle', - ], - 'active_callback' => static function( $hook_suffix ) { - if ( 'toplevel_page_amp-options' === $hook_suffix ) { - return false; - } - return ! AMP_Options_Manager::is_stories_experience_enabled(); - }, - ] - ), - new AMP_Admin_Pointer( - 'amp_stories_menu_pointer_12', + 'amp_stories_support_deprecated_pointer_143', [ 'selector' => '#menu-posts-' . AMP_Story_Post_Type::POST_TYPE_SLUG, - 'heading' => __( 'AMP', 'amp' ), - 'description' => __( 'Head over here to create your first story.', 'amp' ), + 'heading' => esc_html__( 'AMP', 'amp' ), + 'subheading' => esc_html__( 'Back up your Stories!', 'amp' ), + 'description' => implode( + ' ', + [ + 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' ), + sprintf( + '%s', + esc_url( 'https://amp-wp.org/documentation/amp-stories/exporting-stories/' ), + esc_html__( 'View how to export your Stories', 'amp' ) + ), + ] + ), 'position' => [ 'align' => 'middle', ], - 'active_callback' => static function( $hook_suffix ) { - if ( 'edit.php' === $hook_suffix && AMP_Story_Post_Type::POST_TYPE_SLUG === filter_input( INPUT_GET, 'post_type' ) ) { + 'active_callback' => static function() { + if ( get_current_screen() && AMP_Story_Post_Type::POST_TYPE_SLUG === get_current_screen()->post_type ) { return false; } return AMP_Options_Manager::is_stories_experience_enabled(); diff --git a/includes/admin/class-amp-story-templates.php b/includes/admin/class-amp-story-templates.php index 6ffcec3afcf..de6908eb620 100644 --- a/includes/admin/class-amp-story-templates.php +++ b/includes/admin/class-amp-story-templates.php @@ -32,6 +32,10 @@ class AMP_Story_Templates { * Init. */ public function init() { + if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) { + return; + } + // Always hide the story templates. add_filter( 'pre_get_posts', [ $this, 'filter_pre_get_posts' ] ); @@ -41,10 +45,6 @@ public function init() { // We need to register the taxonomy even if AMP Stories is disabled for tax_query. $this->register_taxonomy(); - if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) { - return; - } - add_action( 'save_post_wp_block', [ $this, 'flag_template_as_modified' ] ); $this->maybe_import_story_templates(); diff --git a/includes/class-amp-story-post-type.php b/includes/class-amp-story-post-type.php index ecddfdaa22a..749898f17e7 100644 --- a/includes/class-amp-story-post-type.php +++ b/includes/class-amp-story-post-type.php @@ -124,7 +124,7 @@ public static function has_required_block_capabilities() { * @return void */ public static function register() { - if ( ! AMP_Options_Manager::is_stories_experience_enabled() || ! self::has_required_block_capabilities() ) { + if ( ! AMP_Options_Manager::is_stories_experience_enabled() ) { return; } @@ -2361,4 +2361,37 @@ public static function add_story_settings_meta_to_new_story( $post_id, $post, $u add_post_meta( $post_id, self::STORY_SETTINGS_META_PREFIX . $option_key, $sanitized_value, true ); } } + + /** + * Returns total number of Story posts. + * + * @return int + */ + public static function get_posts_count() { + global $wpdb; + + $cache_key = 'count-' . self::POST_TYPE_SLUG; + $count = wp_cache_get( $cache_key ); + if ( false !== $count ) { + return $count; + } + + // WPCS complains if the query isn't prepared directly inside $wpdb->get_col(); see . + $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM {$wpdb->posts} WHERE post_type = %s", self::POST_TYPE_SLUG ) ); + + $count = isset( $result ) ? (int) $result : 0; + + wp_cache_set( $cache_key, $count ); + + return $count; + } + + /** + * Check if there are any Story posts. + * + * @return bool + */ + public static function has_posts() { + return 0 < self::get_posts_count(); + } } diff --git a/includes/options/class-amp-options-manager.php b/includes/options/class-amp-options-manager.php index 35711cd877a..d98d99923cb 100644 --- a/includes/options/class-amp-options-manager.php +++ b/includes/options/class-amp-options-manager.php @@ -66,10 +66,15 @@ public static function register_settings() { add_action( 'update_option_' . self::OPTION_NAME, [ __CLASS__, 'maybe_flush_rewrite_rules' ], 10, 2 ); add_action( 'admin_notices', [ __CLASS__, 'render_welcome_notice' ] ); + add_action( 'admin_notices', [ __CLASS__, 'render_stories_deprecation_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'persistent_object_caching_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'render_cache_miss_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'render_php_css_parser_conflict_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'insecure_connection_notice' ] ); + + if ( self::is_stories_experience_enabled() ) { + add_action( 'enqueue_block_editor_assets', [ __CLASS__, 'render_stories_deprecation_editor_notice' ] ); + } } /** @@ -202,11 +207,16 @@ public static function is_website_experience_enabled() { * @return bool Enabled. */ public static function is_stories_experience_enabled() { - return ( - AMP_Story_Post_Type::has_required_block_capabilities() - && - in_array( self::STORIES_EXPERIENCE, self::get_option( 'experiences' ), true ) - ); + $stories_enabled = in_array( self::STORIES_EXPERIENCE, self::get_option( 'experiences' ), true ); + + if ( $stories_enabled && ! AMP_Story_Post_Type::has_posts() ) { + if ( post_type_exists( AMP_Story_Post_Type::POST_TYPE_SLUG ) ) { + unregister_post_type( AMP_Story_Post_Type::POST_TYPE_SLUG ); + } + return false; + } + + return AMP_Story_Post_Type::has_required_block_capabilities() && $stories_enabled; } /** @@ -223,8 +233,10 @@ public static function validate_options( $new_options ) { } // Experiences. - if ( isset( $new_options['experiences'] ) && is_array( $new_options['experiences'] ) ) { - + if ( ! isset( $new_options['experiences'][ self::STORIES_EXPERIENCE ] ) && ! AMP_Story_Post_Type::has_posts() ) { + // If there are no Story posts and the Story experience is disabled, only the Website experience is considered enabled. + $options['experiences'] = [ self::WEBSITE_EXPERIENCE ]; + } elseif ( isset( $new_options['experiences'] ) && is_array( $new_options['experiences'] ) ) { // Validate the selected experiences. $options['experiences'] = array_intersect( $new_options['experiences'], @@ -343,16 +355,18 @@ public static function validate_options( $new_options ) { AMP_Theme_Support::reset_cache_miss_url_option(); } - // Handle the base URL for exported stories. - $options['story_export_base_url'] = isset( $new_options['story_export_base_url'] ) ? esc_url_raw( $new_options['story_export_base_url'], [ 'https' ] ) : ''; + if ( isset( $new_options['experiences'] ) && in_array( self::STORIES_EXPERIENCE, $new_options['experiences'], true ) ) { + // Handle the base URL for exported stories. + $options['story_export_base_url'] = isset( $new_options['story_export_base_url'] ) ? esc_url_raw( $new_options['story_export_base_url'], [ 'https' ] ) : ''; - // AMP stories settings definitions. - $definitions = AMP_Story_Post_Type::get_stories_settings_definitions(); + // AMP stories settings definitions. + $definitions = AMP_Story_Post_Type::get_stories_settings_definitions(); - // Handle the AMP stories settings sanitization. - foreach ( $definitions as $option_name => $definition ) { - $value = $new_options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ]; - $options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ] = call_user_func( $definition['meta_args']['sanitize_callback'], $value ); + // Handle the AMP stories settings sanitization. + foreach ( $definitions as $option_name => $definition ) { + $value = $new_options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ]; + $options[ AMP_Story_Post_Type::STORY_SETTINGS_OPTION ][ $option_name ] = call_user_func( $definition['meta_args']['sanitize_callback'], $value ); + } } return $options; @@ -420,6 +434,10 @@ public static function check_supported_post_type_update_errors() { * @return bool Whether update succeeded. */ public static function update_option( $option, $value ) { + if ( 'experiences' === $option && in_array( self::STORIES_EXPERIENCE, $value, true ) ) { + wp_cache_delete( 'count-' . AMP_Story_Post_Type::POST_TYPE_SLUG ); + } + $amp_options = self::get_options(); $amp_options[ $option ] = $value; @@ -562,6 +580,61 @@ public static function persistent_object_caching_notice() { } } + /** + * Render the Stories deprecation admin notice. + */ + public static function render_stories_deprecation_notice() { + if ( + AMP_Story_Post_Type::has_posts() && + ( + 'edit-amp_story' === get_current_screen()->id || + 'toplevel_page_' . self::OPTION_NAME === get_current_screen()->id + ) + ) { + printf( + '

%s %s

', + 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' ), + sprintf( + '%s', + esc_url( 'https://amp-wp.org/documentation/amp-stories/exporting-stories/' ), + esc_html__( 'View how to export your Stories', 'amp' ) + ) + ); + } elseif ( ! self::is_stories_experience_enabled() && 'toplevel_page_' . self::OPTION_NAME === get_current_screen()->id ) { + printf( + '

%s

', + 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' ) + ); + } + } + + /** + * Render the Stories deprecation notice in the Story editor. + */ + public static function render_stories_deprecation_editor_notice() { + $script = sprintf( + "( function( wp ) { + wp.data.dispatch( 'core/notices' ).createNotice( + 'warning', + %s, + { + isDismissible: false, + actions: [ + { + url: 'https://amp-wp.org/documentation/amp-stories/exporting-stories/', + label: %s, + }, + ], + } + ); + } )( window.wp );", + 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' ) ), + wp_json_encode( __( 'View how to export your Stories', 'amp' ) ) + ); + + wp_add_inline_script( AMP_Story_Post_Type::AMP_STORIES_SCRIPT_HANDLE, $script ); + } + /** * Render the cache miss admin notice. * diff --git a/includes/options/class-amp-options-menu.php b/includes/options/class-amp-options-menu.php index f56754794a5..b6160d94759 100644 --- a/includes/options/class-amp-options-menu.php +++ b/includes/options/class-amp-options-menu.php @@ -79,20 +79,22 @@ public function add_menu_items() { AMP_Options_Manager::OPTION_NAME ); - add_settings_field( - 'experiences', - __( 'Experiences', 'amp' ), - [ $this, 'render_experiences' ], - AMP_Options_Manager::OPTION_NAME, - 'general', - [ - 'class' => 'experiences', - ] - ); + if ( AMP_Story_Post_Type::has_posts() ) { + add_settings_field( + 'experiences', + __( 'Experiences', 'amp' ), + [ $this, 'render_experiences' ], + AMP_Options_Manager::OPTION_NAME, + 'general', + [ + 'class' => 'experiences', + ] + ); + } add_settings_field( 'theme_support', - __( 'Website Mode', 'amp' ), + __( 'Template Mode', 'amp' ), [ $this, 'render_theme_support' ], AMP_Options_Manager::OPTION_NAME, 'general', @@ -112,31 +114,36 @@ public function add_menu_items() { ] ); - add_settings_field( - 'stories_export', - __( 'Stories Export', 'amp' ), - [ $this, 'render_stories_export' ], - AMP_Options_Manager::OPTION_NAME, - 'general', - [ - 'class' => 'amp-stories-export-field', - ] - ); + if ( AMP_Story_Post_Type::has_posts() ) { + add_settings_field( + 'stories_export', + __( 'Stories Export', 'amp' ), + [ $this, 'render_stories_export' ], + AMP_Options_Manager::OPTION_NAME, + 'general', + [ + 'class' => 'amp-stories-export-field', + ] + ); - add_settings_field( - 'stories_settings', - __( 'Stories Settings', 'amp' ), - [ $this, 'render_stories_settings' ], - AMP_Options_Manager::OPTION_NAME, - 'general', - [ - 'class' => 'amp-stories-settings-field', - ] - ); + add_settings_field( + 'stories_settings', + __( 'Stories Settings', 'amp' ), + [ $this, 'render_stories_settings' ], + AMP_Options_Manager::OPTION_NAME, + 'general', + [ + 'class' => 'amp-stories-settings-field', + ] + ); + } add_action( 'admin_print_styles', - function() { + static function() { + if ( ! AMP_Story_Post_Type::has_posts() ) { + return; + } ?>