From c1565016560ec3dcb41e645fab282c6dfb131834 Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 09:56:18 +0100 Subject: [PATCH 01/21] Add Settings menu and page --- includes/settings/class-amp-settings.php | 93 ++++++++++++++++++++ templates/admin/settings/screen.php | 23 +++++ tests/test-class-amp-settings.php | 103 +++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 includes/settings/class-amp-settings.php create mode 100644 templates/admin/settings/screen.php create mode 100644 tests/test-class-amp-settings.php diff --git a/includes/settings/class-amp-settings.php b/includes/settings/class-amp-settings.php new file mode 100644 index 00000000000..88c7db3eacd --- /dev/null +++ b/includes/settings/class-amp-settings.php @@ -0,0 +1,93 @@ +get_svg_icon() + ); + add_submenu_page( + self::MENU_SLUG, + __( 'AMP Settings', 'amp' ), + __( 'Settings', 'amp' ), + 'manage_options', + self::MENU_SLUG, + array( $this, 'render_screen' ) + ); + } + + /** + * Display Settings. + * + * @return void Void on user capabilities check failure. + */ + public function render_screen() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + include_once AMP__DIR__ . '/templates/admin/settings/screen.php'; + } + + /** + * Getter for the AMP svg menu icon. + * + * @return object The AMP svg menu icon. + */ + public function get_svg_icon() { + return 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iNjJweCIgaGVpZ2h0PSI2MnB4IiB2aWV3Qm94PSIwIDAgNjIgNjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+QU1QLUJyYW5kLUJsYWNrLUljb248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iYW1wLWxvZ28taW50ZXJuYWwtc2l0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgICAgICA8ZyBpZD0iQU1QLUJyYW5kLUJsYWNrLUljb24iIGZpbGw9IiMwMDAwMDAiPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00MS42Mjg4NjY3LDI4LjE2MTQzMzMgTDI4LjYyNDM2NjcsNDkuODAzNTY2NyBMMjYuMjY4MzY2Nyw0OS44MDM1NjY3IEwyOC41OTc1LDM1LjcwMTY2NjcgTDIxLjM4MzgsMzUuNzEwOTY2NyBDMjEuMzgzOCwzNS43MTA5NjY3IDIxLjMxNTYsMzUuNzEzMDMzMyAyMS4yODM1NjY3LDM1LjcxMzAzMzMgQzIwLjYzMzYsMzUuNzEzMDMzMyAyMC4xMDc2MzMzLDM1LjE4NzA2NjcgMjAuMTA3NjMzMywzNC41MzcxIEMyMC4xMDc2MzMzLDM0LjI1ODEgMjAuMzY3LDMzLjc4NTg2NjcgMjAuMzY3LDMzLjc4NTg2NjcgTDMzLjMyOTEzMzMsMTIuMTY5NTY2NyBMMzUuNzI0NCwxMi4xNzk5IEwzMy4zMzYzNjY3LDI2LjMwMzUgTDQwLjU4NzI2NjcsMjYuMjk0MiBDNDAuNTg3MjY2NywyNi4yOTQyIDQwLjY2NDc2NjcsMjYuMjkzMTY2NyA0MC43MDE5NjY3LDI2LjI5MzE2NjcgQzQxLjM1MTkzMzMsMjYuMjkzMTY2NyA0MS44Nzc5LDI2LjgxOTEzMzMgNDEuODc3OSwyNy40NjkxIEM0MS44Nzc5LDI3LjczMjYgNDEuNzc0NTY2NywyNy45NjQwNjY3IDQxLjYyNzgzMzMsMjguMTYwNCBMNDEuNjI4ODY2NywyOC4xNjE0MzMzIFogTTMxLDAgQzEzLjg3ODcsMCAwLDEzLjg3OTczMzMgMCwzMSBDMCw0OC4xMjEzIDEzLjg3ODcsNjIgMzEsNjIgQzQ4LjEyMDI2NjcsNjIgNjIsNDguMTIxMyA2MiwzMSBDNjIsMTMuODc5NzMzMyA0OC4xMjAyNjY3LDAgMzEsMCBMMzEsMCBaIiBpZD0iRmlsbC0xIj48L3BhdGg+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4='; + } + + /** + * Get the instance of AMP_Settings. + * + * @return object $instance AMP_Settings instance. + */ + public static function get_instance() { + static $instance; + + if ( ! $instance instanceof AMP_Settings ) { + $instance = new AMP_Settings(); + } + + return $instance; + } + +} diff --git a/templates/admin/settings/screen.php b/templates/admin/settings/screen.php new file mode 100644 index 00000000000..280bd88f8c9 --- /dev/null +++ b/templates/admin/settings/screen.php @@ -0,0 +1,23 @@ + +
+

+ +
+ +
+
diff --git a/tests/test-class-amp-settings.php b/tests/test-class-amp-settings.php new file mode 100644 index 00000000000..5dc682fa40b --- /dev/null +++ b/tests/test-class-amp-settings.php @@ -0,0 +1,103 @@ +instance = AMP_Settings::get_instance(); + } + + /** + * Test init. + * + * @see AMP_Settings::init() + */ + public function test_init() { + $this->instance->init(); + $this->assertEquals( 10, has_action( 'admin_menu', array( $this->instance, 'admin_menu' ) ) ); + } + + /** + * Test admin_menu. + * + * @see AMP_Settings::admin_menu() + */ + public function test_admin_menu() { + global $_parent_pages, $submenu; + + wp_set_current_user( $this->factory->user->create( array( + 'role' => 'administrator', + ) ) ); + + $this->instance->admin_menu(); + $this->arrayHasKey( 'amp_settings', $_parent_pages ); + $this->arrayHasKey( 'amp_settings', $submenu ); + } + + /** + * Test render_screen for non admin users. + * + * @see AMP_Settings::render_screen() + */ + public function test_render_screen_for_non_admin_user() { + wp_set_current_user( $this->factory->user->create( array( + 'role' => 'subscriber', + ) ) ); + + $this->assertEmpty( $this->instance->render_screen() ); + } + + /** + * Test render_screen for admin users. + * + * @see AMP_Settings::render_screen() + */ + public function test_render_screen_for_admin_user() { + wp_set_current_user( $this->factory->user->create( array( + 'role' => 'administrator', + ) ) ); + + ob_start(); + $this->instance->render_screen(); + $this->assertContains( '
', ob_get_clean() ); + } + + /** + * Test get_svg_icon. + * + * @see AMP_Settings::get_svg_icon() + */ + public function test_get_svg_icon() { + $this->assertEquals( $this->instance->get_svg_icon(), 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iNjJweCIgaGVpZ2h0PSI2MnB4IiB2aWV3Qm94PSIwIDAgNjIgNjIiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+QU1QLUJyYW5kLUJsYWNrLUljb248L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+PC9kZWZzPiAgICA8ZyBpZD0iYW1wLWxvZ28taW50ZXJuYWwtc2l0ZSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgICAgICA8ZyBpZD0iQU1QLUJyYW5kLUJsYWNrLUljb24iIGZpbGw9IiMwMDAwMDAiPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00MS42Mjg4NjY3LDI4LjE2MTQzMzMgTDI4LjYyNDM2NjcsNDkuODAzNTY2NyBMMjYuMjY4MzY2Nyw0OS44MDM1NjY3IEwyOC41OTc1LDM1LjcwMTY2NjcgTDIxLjM4MzgsMzUuNzEwOTY2NyBDMjEuMzgzOCwzNS43MTA5NjY3IDIxLjMxNTYsMzUuNzEzMDMzMyAyMS4yODM1NjY3LDM1LjcxMzAzMzMgQzIwLjYzMzYsMzUuNzEzMDMzMyAyMC4xMDc2MzMzLDM1LjE4NzA2NjcgMjAuMTA3NjMzMywzNC41MzcxIEMyMC4xMDc2MzMzLDM0LjI1ODEgMjAuMzY3LDMzLjc4NTg2NjcgMjAuMzY3LDMzLjc4NTg2NjcgTDMzLjMyOTEzMzMsMTIuMTY5NTY2NyBMMzUuNzI0NCwxMi4xNzk5IEwzMy4zMzYzNjY3LDI2LjMwMzUgTDQwLjU4NzI2NjcsMjYuMjk0MiBDNDAuNTg3MjY2NywyNi4yOTQyIDQwLjY2NDc2NjcsMjYuMjkzMTY2NyA0MC43MDE5NjY3LDI2LjI5MzE2NjcgQzQxLjM1MTkzMzMsMjYuMjkzMTY2NyA0MS44Nzc5LDI2LjgxOTEzMzMgNDEuODc3OSwyNy40NjkxIEM0MS44Nzc5LDI3LjczMjYgNDEuNzc0NTY2NywyNy45NjQwNjY3IDQxLjYyNzgzMzMsMjguMTYwNCBMNDEuNjI4ODY2NywyOC4xNjE0MzMzIFogTTMxLDAgQzEzLjg3ODcsMCAwLDEzLjg3OTczMzMgMCwzMSBDMCw0OC4xMjEzIDEzLjg3ODcsNjIgMzEsNjIgQzQ4LjEyMDI2NjcsNjIgNjIsNDguMTIxMyA2MiwzMSBDNjIsMTMuODc5NzMzMyA0OC4xMjAyNjY3LDAgMzEsMCBMMzEsMCBaIiBpZD0iRmlsbC0xIj48L3BhdGg+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=' ); + } + + /** + * Test get_instance. + * + * @see AMP_Settings::get_instance() + */ + public function test_get_instance() { + $this->assertInstanceOf( 'AMP_Settings', AMP_Settings::get_instance() ); + } + +} From 5499ba601757de757299e7247cddbfc2133544ca Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 09:57:06 +0100 Subject: [PATCH 02/21] Add post types settings --- .../class-amp-settings-post-types.php | 163 ++++++++++++++++++ .../settings/fields/checkbox-post-types.php | 21 +++ tests/test-class-amp-settings-post-types.php | 149 ++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 includes/settings/class-amp-settings-post-types.php create mode 100644 templates/admin/settings/fields/checkbox-post-types.php create mode 100644 tests/test-class-amp-settings-post-types.php diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php new file mode 100644 index 00000000000..98843200b27 --- /dev/null +++ b/includes/settings/class-amp-settings-post-types.php @@ -0,0 +1,163 @@ +setting = array( + 'id' => 'post_types_support', + 'label' => __( 'Post Types Support', 'amp' ), + 'description' => __( 'Enable/disable AMP post type(s) support', 'amp' ), + ); + } + + /** + * Initialize. + */ + public function init() { + add_action( 'admin_init', array( $this, 'register_settings' ) ); + add_action( 'update_option_' . AMP_Settings::SETTINGS_KEY, 'flush_rewrite_rules' ); + } + + /** + * Register the current page settings. + */ + public function register_settings() { + register_setting( AMP_Settings::SETTINGS_KEY, AMP_Settings::SETTINGS_KEY ); + add_settings_section( + $this->section_id, + false, + '__return_false', + AMP_Settings::MENU_SLUG + ); + add_settings_field( + $this->setting['id'], + $this->setting['label'], + array( $this, 'render_setting' ), + AMP_Settings::MENU_SLUG, + $this->section_id + ); + } + + /** + * Getter for settings value. + * + * @param string $post_type The post type name. + * @return bool|array Return true if the post type is always on; the setting value otherwise. + */ + public function get_settings_value( $post_type = false ) { + $settings = get_option( AMP_Settings::SETTINGS_KEY, array() ); + + if ( false !== $post_type ) { + // Always return true if the post type is always on. + if ( true == $this->is_always_on( $post_type ) ) { + return true; + } + + if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { + return (bool) $settings[ $this->setting['id'] ][ $post_type ]; + } + + return false; + } + + if ( empty( $settings[ $this->setting['id'] ] ) || ! is_array( $settings[ $this->setting['id'] ] ) ) { + return array(); + } + + return array_map( 'boolval', $settings[ $this->setting['id'] ] ); + } + + /** + * Getter for the supported post types. + * + * @return object Supported post types list. + */ + public function get_supported_post_types() { + $core = get_post_types( array( + 'name' => 'post', + ), 'objects' ); + $cpt = get_post_types( array( + 'public' => true, + '_builtin' => false, + ), 'objects' ); + + return $core + $cpt; + } + + /** + * Getter for the setting HTML input name attribute. + * + * @param string $post_type The post type name. + * @return object The setting HTML input name attribute. + */ + public function get_setting_name( $post_type ) { + $id = $this->setting['id']; + + return AMP_Settings::SETTINGS_KEY . "[{$id}][{$post_type}]"; + } + + /** + * Check if a post type is always on. + * + * @param string $post_type The post type name. + * @return bool True if the post type is always on; false otherwise. + */ + public function is_always_on( $post_type ) { + return in_array( $post_type, $this->always_on, true ); + } + + /** + * Setting renderer. + */ + public function render_setting() { + require_once AMP__DIR__ . '/templates/admin/settings/fields/checkbox-post-types.php'; + } + + /** + * Get the instance of AMP_Settings_Post_Types. + * + * @return object $instance AMP_Settings_Post_Types instance. + */ + public static function get_instance() { + static $instance; + + if ( ! $instance instanceof AMP_Settings_Post_Types ) { + $instance = new AMP_Settings_Post_Types(); + } + + return $instance; + } + +} diff --git a/templates/admin/settings/fields/checkbox-post-types.php b/templates/admin/settings/fields/checkbox-post-types.php new file mode 100644 index 00000000000..76b17c4a63f --- /dev/null +++ b/templates/admin/settings/fields/checkbox-post-types.php @@ -0,0 +1,21 @@ + +
+ get_supported_post_types() as $post_type ) : ?> + +
+ +

setting['description'] ); ?>

+
diff --git a/tests/test-class-amp-settings-post-types.php b/tests/test-class-amp-settings-post-types.php new file mode 100644 index 00000000000..84a99727ff4 --- /dev/null +++ b/tests/test-class-amp-settings-post-types.php @@ -0,0 +1,149 @@ +instance = AMP_Settings_Post_Types::get_instance(); + } + + /** + * Test init. + * + * @see AMP_Settings_Post_Types::init() + */ + public function test_init() { + $this->instance->init(); + $this->assertEquals( 10, has_action( 'admin_init', array( $this->instance, 'register_settings' ) ) ); + $this->assertEquals( 10, has_action( 'update_option_' . AMP_Settings::SETTINGS_KEY, 'flush_rewrite_rules' ) ); + } + + /** + * Test register_settings. + * + * @see AMP_Settings_Post_Types::register_settings() + */ + public function test_register_settings() { + global $wp_settings_sections, $wp_settings_fields; + + $menu_slug = AMP_Settings::MENU_SLUG; + $option_group = AMP_Settings::SETTINGS_KEY; + $section_id = 'post_types'; + $setting_id = 'post_types_support'; + + $this->instance->register_settings(); + $this->assertArrayHasKey( $menu_slug, $wp_settings_sections ); + + if ( ! isset( $wp_settings_sections[ $menu_slug ] ) ) { + $this->markTestIncomplete( 'Setting sections page could not be found.' ); + } + + $sections = $wp_settings_sections[ $menu_slug ]; + $this->assertArrayHasKey( $section_id, $sections ); + + if ( ! isset( $sections[ $section_id ] ) ) { + $this->markTestIncomplete( 'Settings section could not be found.' ); + } + + $this->assertEquals( $section_id, $sections[ $section_id ]['id'] ); + + if ( ! isset( $wp_settings_fields[ $menu_slug ][ $section_id ][ $setting_id ] ) ) { + $this->markTestIncomplete( 'Settings field could not be found.' ); + } + + $this->assertEquals( 'post_types_support', $wp_settings_fields[ $menu_slug ][ $section_id ][ $setting_id ]['id'] ); + } + + /** + * Test get_settings_value. + * + * @see AMP_Settings_Post_Types::get_settings_value() + */ + public function test_get_settings_value() { + $this->assertEmpty( $this->instance->get_settings_value() ); + $this->assertInternalType( 'array', $this->instance->get_settings_value() ); + $this->assertFalse( $this->instance->get_settings_value( 'foo' ) ); + + update_option( AMP_Settings::SETTINGS_KEY, array( + 'post_types_support' => array( + 'post' => true, + ), + ) ); + + $this->assertContains( 'post', $this->instance->get_settings_value() ); + $this->assertInternalType( 'array', $this->instance->get_settings_value() ); + $this->assertTrue( $this->instance->get_settings_value( 'post' ) ); + + // Cleanup. + delete_option( AMP_Settings::SETTINGS_KEY ); + } + + /** + * Test get_supported_post_types. + * + * @see AMP_Settings_Post_Types::get_supported_post_types() + */ + public function test_get_supported_post_types() { + // It would be redundant to add further test already covered in Core. + $this->assertInternalType( 'array', $this->instance->get_supported_post_types() ); + } + + /** + * Test get_setting_name. + * + * @see AMP_Settings_Post_Types::get_setting_name() + */ + public function test_get_setting_name() { + $this->assertEquals( AMP_Settings::SETTINGS_KEY . '[post_types_support][post]', $this->instance->get_setting_name( 'post' ) ); + } + + /** + * Test is_always_on. + * + * @see AMP_Settings_Post_Types::is_always_on() + */ + public function test_is_always_on() { + $this->assertTrue( $this->instance->is_always_on( 'post' ) ); + } + + /** + * Test render_setting. + * + * @see AMP_Settings_Post_Types::render_setting() + */ + public function test_render_setting() { + ob_start(); + $this->instance->render_setting(); + $this->assertContains( '
', ob_get_clean() ); + } + + /** + * Test get_instance. + * + * @see AMP_Settings_Post_Types::get_instance() + */ + public function test_get_instance() { + $this->assertInstanceOf( 'AMP_Settings_Post_Types', AMP_Settings_Post_Types::get_instance() ); + } + +} From c5d0c99e989e8ba6c51a024f1b2cb53bd1c424e5 Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 09:57:58 +0100 Subject: [PATCH 03/21] Remove legacy option code --- includes/options/class-amp-options-menu.php | 42 +------------------ .../views/class-amp-options-menu-page.php | 17 -------- 2 files changed, 2 insertions(+), 57 deletions(-) delete mode 100644 includes/options/views/class-amp-options-menu-page.php diff --git a/includes/options/class-amp-options-menu.php b/includes/options/class-amp-options-menu.php index 15494fd209c..50b725c5272 100644 --- a/includes/options/class-amp-options-menu.php +++ b/includes/options/class-amp-options-menu.php @@ -1,54 +1,16 @@ menu_page = new AMP_Options_Menu_Page(); - $this->menu_slug = 'amp-plugin-options'; - } - public function init() { add_action( 'admin_post_amp_analytics_options', 'AMP_Options_Manager::handle_analytics_submit' ); - add_action( 'admin_menu', array( $this, 'add_menu_items' ) ); } public function add_menu_items() { - add_menu_page( - __( 'AMP Options', 'amp' ), - __( 'AMP', 'amp' ), - 'manage_options', - $this->menu_slug, - array( $this->menu_page, 'render' ), - self::ICON_BASE64_SVG - ); - - $submenus = array( - new AMP_Analytics_Options_Submenu( $this->menu_slug ), - ); - - // Create submenu items and calls on the Submenu Page object to render the actual contents of the page. - foreach ( $submenus as $submenu ) { - $submenu->init( $this->menu_slug ); - } - - $this->remove_toplevel_menu_item(); - } - - // Helper function to avoid having the top-level menu as - // the first menu item - function remove_toplevel_menu_item() { - global $submenu; - if ( isset( $submenu['amp-plugin-options'][0] ) ) { - unset( $submenu['amp-plugin-options'][0] ); - } + $submenu = new AMP_Analytics_Options_Submenu( AMP_Settings::MENU_SLUG ); + $submenu->init( AMP_Settings::MENU_SLUG ); } } diff --git a/includes/options/views/class-amp-options-menu-page.php b/includes/options/views/class-amp-options-menu-page.php deleted file mode 100644 index 1a729a3f65f..00000000000 --- a/includes/options/views/class-amp-options-menu-page.php +++ /dev/null @@ -1,17 +0,0 @@ - -
-

-

- -

-
- Date: Thu, 23 Nov 2017 09:58:44 +0100 Subject: [PATCH 04/21] Load new settings and add listener --- amp.php | 11 +++++++++++ includes/admin/functions.php | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/amp.php b/amp.php index 1cbd389113c..46070ea713b 100644 --- a/amp.php +++ b/amp.php @@ -21,6 +21,8 @@ require_once( AMP__DIR__ . '/includes/admin/class-amp-customizer.php' ); require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-settings.php' ); require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-design-settings.php' ); +require_once( AMP__DIR__ . '/includes/settings/class-amp-settings.php' ); +require_once( AMP__DIR__ . '/includes/settings/class-amp-settings-post-types.php' ); require_once( AMP__DIR__ . '/includes/actions/class-amp-frontend-actions.php' ); require_once( AMP__DIR__ . '/includes/actions/class-amp-paired-post-actions.php' ); @@ -62,6 +64,15 @@ function amp_init() { add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK ); add_post_type_support( 'post', AMP_QUERY_VAR ); + // Listen to post types settings. + $post_types_supported = AMP_Settings_Post_Types::get_instance()->get_settings_value(); + + foreach ( $post_types_supported as $post_type_name => $enabled ) { + if ( true === $enabled ) { + add_post_type_support( $post_type_name, AMP_QUERY_VAR ); + } + } + add_filter( 'request', 'amp_force_query_var_value' ); add_action( 'wp', 'amp_maybe_add_actions' ); diff --git a/includes/admin/functions.php b/includes/admin/functions.php index fd21e348385..4a2e7fba207 100644 --- a/includes/admin/functions.php +++ b/includes/admin/functions.php @@ -80,18 +80,28 @@ function amp_add_customizer_link() { } /** - * Registers a top-level menu for AMP configuration options + * Registers AMP settings. */ function amp_add_options_menu() { if ( ! is_admin() ) { return; } - $show_options_menu = apply_filters( 'amp_options_menu_is_enabled', true ); - if ( true !== $show_options_menu ) { + /** + * Filter whether to enable the AMP settings. + * + * @param bool $enable Whether to enable the AMP settings. Default true. + */ + $short_circuit = apply_filters( 'amp_options_menu_is_enabled', true ); + + if ( true !== $short_circuit ) { return; } + // Initialize settings. + AMP_Settings::get_instance()->init(); + AMP_Settings_Post_Types::get_instance()->init(); + $amp_options = new AMP_Options_Menu(); $amp_options->init(); } From 2949f25434e2f5a4e0aea9c48d3fe41a771360ef Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 10:10:14 +0100 Subject: [PATCH 05/21] WPCS compliancy --- amp.php | 21 +++++++++---------- includes/options/class-amp-options-menu.php | 21 +++++++++++++++++-- .../class-amp-settings-post-types.php | 4 ++-- tests/test-class-amp-settings-post-types.php | 6 +++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/amp.php b/amp.php index 46070ea713b..8a032d0d31e 100644 --- a/amp.php +++ b/amp.php @@ -15,17 +15,16 @@ define( 'AMP__DIR__', dirname( __FILE__ ) ); define( 'AMP__VERSION', '0.5.1' ); -require_once( AMP__DIR__ . '/back-compat/back-compat.php' ); -require_once( AMP__DIR__ . '/includes/amp-helper-functions.php' ); -require_once( AMP__DIR__ . '/includes/admin/functions.php' ); -require_once( AMP__DIR__ . '/includes/admin/class-amp-customizer.php' ); -require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-settings.php' ); -require_once( AMP__DIR__ . '/includes/settings/class-amp-customizer-design-settings.php' ); -require_once( AMP__DIR__ . '/includes/settings/class-amp-settings.php' ); -require_once( AMP__DIR__ . '/includes/settings/class-amp-settings-post-types.php' ); - -require_once( AMP__DIR__ . '/includes/actions/class-amp-frontend-actions.php' ); -require_once( AMP__DIR__ . '/includes/actions/class-amp-paired-post-actions.php' ); +require_once AMP__DIR__ . '/back-compat/back-compat.php'; +require_once AMP__DIR__ . '/includes/amp-helper-functions.php'; +require_once AMP__DIR__ . '/includes/admin/functions.php'; +require_once AMP__DIR__ . '/includes/admin/class-amp-customizer.php'; +require_once AMP__DIR__ . '/includes/settings/class-amp-customizer-settings.php'; +require_once AMP__DIR__ . '/includes/settings/class-amp-customizer-design-settings.php'; +require_once AMP__DIR__ . '/includes/settings/class-amp-settings.php'; +require_once AMP__DIR__ . '/includes/settings/class-amp-settings-post-types.php'; +require_once AMP__DIR__ . '/includes/actions/class-amp-frontend-actions.php'; +require_once AMP__DIR__ . '/includes/actions/class-amp-paired-post-actions.php'; register_activation_hook( __FILE__, 'amp_activate' ); function amp_activate() { diff --git a/includes/options/class-amp-options-menu.php b/includes/options/class-amp-options-menu.php index 50b725c5272..6acea40d8d6 100644 --- a/includes/options/class-amp-options-menu.php +++ b/includes/options/class-amp-options-menu.php @@ -1,16 +1,33 @@ init( AMP_Settings::MENU_SLUG ); } + } diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index 98843200b27..d2f818680e0 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -81,7 +81,7 @@ public function get_settings_value( $post_type = false ) { if ( false !== $post_type ) { // Always return true if the post type is always on. - if ( true == $this->is_always_on( $post_type ) ) { + if ( true === $this->is_always_on( $post_type ) ) { return true; } @@ -108,7 +108,7 @@ public function get_supported_post_types() { $core = get_post_types( array( 'name' => 'post', ), 'objects' ); - $cpt = get_post_types( array( + $cpt = get_post_types( array( 'public' => true, '_builtin' => false, ), 'objects' ); diff --git a/tests/test-class-amp-settings-post-types.php b/tests/test-class-amp-settings-post-types.php index 84a99727ff4..c4f25b0112e 100644 --- a/tests/test-class-amp-settings-post-types.php +++ b/tests/test-class-amp-settings-post-types.php @@ -46,10 +46,10 @@ public function test_init() { public function test_register_settings() { global $wp_settings_sections, $wp_settings_fields; - $menu_slug = AMP_Settings::MENU_SLUG; + $menu_slug = AMP_Settings::MENU_SLUG; $option_group = AMP_Settings::SETTINGS_KEY; - $section_id = 'post_types'; - $setting_id = 'post_types_support'; + $section_id = 'post_types'; + $setting_id = 'post_types_support'; $this->instance->register_settings(); $this->assertArrayHasKey( $menu_slug, $wp_settings_sections ); From d66bc621699fb475388624b81f777122e693835b Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 10:55:47 +0100 Subject: [PATCH 06/21] Move sanitization --- amp.php | 2 +- includes/settings/class-amp-settings-post-types.php | 6 ++++-- templates/admin/settings/fields/checkbox-post-types.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/amp.php b/amp.php index 8a032d0d31e..5e9d6acd192 100644 --- a/amp.php +++ b/amp.php @@ -67,7 +67,7 @@ function amp_init() { $post_types_supported = AMP_Settings_Post_Types::get_instance()->get_settings_value(); foreach ( $post_types_supported as $post_type_name => $enabled ) { - if ( true === $enabled ) { + if ( true === (bool) $enabled ) { add_post_type_support( $post_type_name, AMP_QUERY_VAR ); } } diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index d2f818680e0..0ce2050c861 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -73,6 +73,8 @@ public function register_settings() { /** * Getter for settings value. * + * The value(s) return are not sanitized. + * * @param string $post_type The post type name. * @return bool|array Return true if the post type is always on; the setting value otherwise. */ @@ -86,7 +88,7 @@ public function get_settings_value( $post_type = false ) { } if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { - return (bool) $settings[ $this->setting['id'] ][ $post_type ]; + return $settings[ $this->setting['id'] ][ $post_type ]; } return false; @@ -96,7 +98,7 @@ public function get_settings_value( $post_type = false ) { return array(); } - return array_map( 'boolval', $settings[ $this->setting['id'] ] ); + return $settings[ $this->setting['id'] ]; } /** diff --git a/templates/admin/settings/fields/checkbox-post-types.php b/templates/admin/settings/fields/checkbox-post-types.php index 76b17c4a63f..0bc1d8c0dde 100644 --- a/templates/admin/settings/fields/checkbox-post-types.php +++ b/templates/admin/settings/fields/checkbox-post-types.php @@ -13,7 +13,7 @@
get_supported_post_types() as $post_type ) : ?>
From 99e7bcf23d619ba12be8878d3a80e5f824f5847c Mon Sep 17 00:00:00 2001 From: ThierryA Date: Thu, 23 Nov 2017 12:32:28 +0100 Subject: [PATCH 07/21] Disable post type checkbox setting if AMP post type support is set --- amp.php | 9 ---- includes/amp-helper-functions.php | 7 +++- .../class-amp-settings-post-types.php | 42 +++---------------- .../settings/fields/checkbox-post-types.php | 2 +- tests/test-class-amp-settings-post-types.php | 13 ------ 5 files changed, 11 insertions(+), 62 deletions(-) diff --git a/amp.php b/amp.php index 5e9d6acd192..56756588371 100644 --- a/amp.php +++ b/amp.php @@ -63,15 +63,6 @@ function amp_init() { add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK ); add_post_type_support( 'post', AMP_QUERY_VAR ); - // Listen to post types settings. - $post_types_supported = AMP_Settings_Post_Types::get_instance()->get_settings_value(); - - foreach ( $post_types_supported as $post_type_name => $enabled ) { - if ( true === (bool) $enabled ) { - add_post_type_support( $post_type_name, AMP_QUERY_VAR ); - } - } - add_filter( 'request', 'amp_force_query_var_value' ); add_action( 'wp', 'amp_maybe_add_actions' ); diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php index d9c5d86ea93..62568471a31 100644 --- a/includes/amp-helper-functions.php +++ b/includes/amp-helper-functions.php @@ -18,8 +18,11 @@ function amp_get_permalink( $post_id ) { } function post_supports_amp( $post ) { - // Because `add_rewrite_endpoint` doesn't let us target specific post_types :( - if ( ! post_type_supports( $post->post_type, AMP_QUERY_VAR ) ) { + // Listen to post types settings. + $is_enabled_via_setting = (bool) AMP_Settings_Post_Types::get_instance()->get_settings_value( $post->post_type ); + + // Because `add_rewrite_endpoint` doesn't let us target specific post_types. + if ( ! post_type_supports( $post->post_type, AMP_QUERY_VAR ) && true !== $is_enabled_via_setting ) { return false; } diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index 0ce2050c861..b53b6c0727b 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -24,13 +24,6 @@ class AMP_Settings_Post_Types { */ protected $setting = array(); - /** - * List of post types which are always AMPlified. - * - * @var array - */ - protected $always_on = array( 'post' ); - /** * Constructor. */ @@ -73,32 +66,17 @@ public function register_settings() { /** * Getter for settings value. * - * The value(s) return are not sanitized. - * * @param string $post_type The post type name. - * @return bool|array Return true if the post type is always on; the setting value otherwise. + * @return bool Return the setting value. */ - public function get_settings_value( $post_type = false ) { + public function get_settings_value( $post_type ) { $settings = get_option( AMP_Settings::SETTINGS_KEY, array() ); - if ( false !== $post_type ) { - // Always return true if the post type is always on. - if ( true === $this->is_always_on( $post_type ) ) { - return true; - } - - if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { - return $settings[ $this->setting['id'] ][ $post_type ]; - } - - return false; + if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { + return (bool) $settings[ $this->setting['id'] ][ $post_type ]; } - if ( empty( $settings[ $this->setting['id'] ] ) || ! is_array( $settings[ $this->setting['id'] ] ) ) { - return array(); - } - - return $settings[ $this->setting['id'] ]; + return false; } /** @@ -130,16 +108,6 @@ public function get_setting_name( $post_type ) { return AMP_Settings::SETTINGS_KEY . "[{$id}][{$post_type}]"; } - /** - * Check if a post type is always on. - * - * @param string $post_type The post type name. - * @return bool True if the post type is always on; false otherwise. - */ - public function is_always_on( $post_type ) { - return in_array( $post_type, $this->always_on, true ); - } - /** * Setting renderer. */ diff --git a/templates/admin/settings/fields/checkbox-post-types.php b/templates/admin/settings/fields/checkbox-post-types.php index 0bc1d8c0dde..e34166eb0ff 100644 --- a/templates/admin/settings/fields/checkbox-post-types.php +++ b/templates/admin/settings/fields/checkbox-post-types.php @@ -13,7 +13,7 @@
get_supported_post_types() as $post_type ) : ?>
diff --git a/tests/test-class-amp-settings-post-types.php b/tests/test-class-amp-settings-post-types.php index c4f25b0112e..725c334fdaa 100644 --- a/tests/test-class-amp-settings-post-types.php +++ b/tests/test-class-amp-settings-post-types.php @@ -80,8 +80,6 @@ public function test_register_settings() { * @see AMP_Settings_Post_Types::get_settings_value() */ public function test_get_settings_value() { - $this->assertEmpty( $this->instance->get_settings_value() ); - $this->assertInternalType( 'array', $this->instance->get_settings_value() ); $this->assertFalse( $this->instance->get_settings_value( 'foo' ) ); update_option( AMP_Settings::SETTINGS_KEY, array( @@ -90,8 +88,6 @@ public function test_get_settings_value() { ), ) ); - $this->assertContains( 'post', $this->instance->get_settings_value() ); - $this->assertInternalType( 'array', $this->instance->get_settings_value() ); $this->assertTrue( $this->instance->get_settings_value( 'post' ) ); // Cleanup. @@ -117,15 +113,6 @@ public function test_get_setting_name() { $this->assertEquals( AMP_Settings::SETTINGS_KEY . '[post_types_support][post]', $this->instance->get_setting_name( 'post' ) ); } - /** - * Test is_always_on. - * - * @see AMP_Settings_Post_Types::is_always_on() - */ - public function test_is_always_on() { - $this->assertTrue( $this->instance->is_always_on( 'post' ) ); - } - /** * Test render_setting. * From 68c501708797d8afa6813a2a277e842771ac1e12 Mon Sep 17 00:00:00 2001 From: ThierryA Date: Fri, 24 Nov 2017 14:44:28 +0100 Subject: [PATCH 08/21] Declare post type support based on the admin setting and allow overwrite --- amp.php | 17 +++- includes/amp-helper-functions.php | 7 +- includes/amp-post-types-support.php | 35 +++++++ .../class-amp-settings-post-types.php | 96 ++++++++++++++++--- includes/settings/class-amp-settings.php | 6 ++ .../settings/fields/checkbox-post-types.php | 2 +- tests/test-amp-post-types-support.php | 45 +++++++++ tests/test-class-amp-settings-post-types.php | 75 ++++++++++++--- 8 files changed, 250 insertions(+), 33 deletions(-) create mode 100644 includes/amp-post-types-support.php create mode 100644 tests/test-amp-post-types-support.php diff --git a/amp.php b/amp.php index 56756588371..0e08b153977 100644 --- a/amp.php +++ b/amp.php @@ -17,6 +17,7 @@ require_once AMP__DIR__ . '/back-compat/back-compat.php'; require_once AMP__DIR__ . '/includes/amp-helper-functions.php'; +require_once AMP__DIR__ . '/includes/amp-post-types-support.php'; require_once AMP__DIR__ . '/includes/admin/functions.php'; require_once AMP__DIR__ . '/includes/admin/class-amp-customizer.php'; require_once AMP__DIR__ . '/includes/settings/class-amp-customizer-settings.php'; @@ -54,14 +55,11 @@ function amp_init() { return; } - define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) ); - do_action( 'amp_init' ); load_plugin_textdomain( 'amp', false, plugin_basename( AMP__DIR__ ) . '/languages' ); add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK ); - add_post_type_support( 'post', AMP_QUERY_VAR ); add_filter( 'request', 'amp_force_query_var_value' ); add_action( 'wp', 'amp_maybe_add_actions' ); @@ -74,6 +72,19 @@ function amp_init() { } } +/** + * Define AMP query var. + * + * This function must be invoked through the 'after_setup_theme' action to allow + * the AMP setting to declare the post types support earlier than plugins/theme. + * + * @since 0.6 + */ +function define_query_var() { + define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) ); +} +add_action( 'after_setup_theme', 'define_query_var', 7 ); + // Make sure the `amp` query var has an explicit value. // Avoids issues when filtering the deprecated `query_string` hook. function amp_force_query_var_value( $query_vars ) { diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php index 62568471a31..8ba5cdcac00 100644 --- a/includes/amp-helper-functions.php +++ b/includes/amp-helper-functions.php @@ -18,11 +18,8 @@ function amp_get_permalink( $post_id ) { } function post_supports_amp( $post ) { - // Listen to post types settings. - $is_enabled_via_setting = (bool) AMP_Settings_Post_Types::get_instance()->get_settings_value( $post->post_type ); - - // Because `add_rewrite_endpoint` doesn't let us target specific post_types. - if ( ! post_type_supports( $post->post_type, AMP_QUERY_VAR ) && true !== $is_enabled_via_setting ) { + // Because `add_rewrite_endpoint` doesn't let us target specific post_types :(. + if ( ! post_type_supports( $post->post_type, AMP_QUERY_VAR ) ) { return false; } diff --git a/includes/amp-post-types-support.php b/includes/amp-post-types-support.php new file mode 100644 index 00000000000..3a5a8683d64 --- /dev/null +++ b/includes/amp-post-types-support.php @@ -0,0 +1,35 @@ +get_settings() as $post_type => $enabled ) { + if ( true === $enabled ) { + add_post_type_support( $post_type, AMP_QUERY_VAR ); + } + } +} +add_action( 'after_setup_theme', 'amp_custom_post_types_support' ); diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index b53b6c0727b..cf70b2c9d54 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -41,13 +41,18 @@ public function __construct() { public function init() { add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'update_option_' . AMP_Settings::SETTINGS_KEY, 'flush_rewrite_rules' ); + add_action( 'amp_settings_screen', array( $this, 'errors' ) ); } /** * Register the current page settings. */ public function register_settings() { - register_setting( AMP_Settings::SETTINGS_KEY, AMP_Settings::SETTINGS_KEY ); + register_setting( + AMP_Settings::SETTINGS_KEY, + AMP_Settings::SETTINGS_KEY, + array( $this, 'validate' ) + ); add_settings_section( $this->section_id, false, @@ -57,7 +62,7 @@ public function register_settings() { add_settings_field( $this->setting['id'], $this->setting['label'], - array( $this, 'render_setting' ), + array( $this, 'render' ), AMP_Settings::MENU_SLUG, $this->section_id ); @@ -67,16 +72,24 @@ public function register_settings() { * Getter for settings value. * * @param string $post_type The post type name. - * @return bool Return the setting value. + * @return bool|array Return true if the post type is always on; the setting value otherwise. */ - public function get_settings_value( $post_type ) { - $settings = get_option( AMP_Settings::SETTINGS_KEY, array() ); + public function get_settings( $post_type = false ) { + $settings = $this->validate( get_option( AMP_Settings::SETTINGS_KEY, array() ) ); + + if ( false !== $post_type ) { + if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { + return $settings[ $this->setting['id'] ][ $post_type ]; + } - if ( isset( $settings[ $this->setting['id'] ][ $post_type ] ) ) { - return (bool) $settings[ $this->setting['id'] ][ $post_type ]; + return false; } - return false; + if ( empty( $settings[ $this->setting['id'] ] ) || ! is_array( $settings[ $this->setting['id'] ] ) ) { + return array(); + } + + return $settings[ $this->setting['id'] ]; } /** @@ -102,16 +115,77 @@ public function get_supported_post_types() { * @param string $post_type The post type name. * @return object The setting HTML input name attribute. */ - public function get_setting_name( $post_type ) { + public function get_name_attribute( $post_type ) { $id = $this->setting['id']; - return AMP_Settings::SETTINGS_KEY . "[{$id}][{$post_type}]"; } + /** + * Check whether the post type should be disabled or not. + * + * Since we can't flag a post type which is not enabled by setting and removed by plugin/theme, + * we can't disable the checkbox but the errors() takes care of this scenario. + * + * @param string $post_type The post type name. + * @return bool True if disabled; false otherwise. + */ + public function disabled( $post_type ) { + $settings = $this->get_settings(); + + // Disable if post type support was not added by setting and added by plugin/theme. + if ( post_type_supports( $post_type, AMP_QUERY_VAR ) && ! isset( $settings[ $post_type ] ) ) { + return true; + } + + return false; + } + + /** + * Handle errors. + */ + public function errors() { + $settings = $this->get_settings(); + + foreach ( $settings as $post_type => $value ) { + // Throw error if post type support was added by setting and removed by plugin/theme. + if ( true === $value && ! post_type_supports( $post_type, AMP_QUERY_VAR ) ) { + $post_type_object = get_post_type_object( $post_type ); + + add_settings_error( + $post_type, + $post_type, + sprintf( + /* Translators: %s: Post type name. */ + __( '"%s" could not be activated because support was removed by a plugin or theme', 'amp' ), + isset( $post_type_object->label ) ? $post_type_object->label : $post_type + ) + ); + } + } + } + + /** + * Validate and sanitize the settings. + * + * @param array $settings The post types settings. + * @return array The post types settings. + */ + public function validate( $settings ) { + if ( ! isset( $settings[ $this->setting['id'] ] ) || ! is_array( $settings[ $this->setting['id'] ] ) ) { + return array(); + } + + foreach ( $settings[ $this->setting['id'] ] as $key => $value ) { + $settings[ $this->setting['id'] ][ $key ] = (bool) $value; + } + + return $settings; + } + /** * Setting renderer. */ - public function render_setting() { + public function render() { require_once AMP__DIR__ . '/templates/admin/settings/fields/checkbox-post-types.php'; } diff --git a/includes/settings/class-amp-settings.php b/includes/settings/class-amp-settings.php index 88c7db3eacd..2ee29c78444 100644 --- a/includes/settings/class-amp-settings.php +++ b/includes/settings/class-amp-settings.php @@ -63,6 +63,12 @@ public function render_screen() { return; } + /** + * Fires before the AMP settings screen is rendered. + * + * @since 0.6 + */ + do_action( 'amp_settings_screen' ); include_once AMP__DIR__ . '/templates/admin/settings/screen.php'; } diff --git a/templates/admin/settings/fields/checkbox-post-types.php b/templates/admin/settings/fields/checkbox-post-types.php index e34166eb0ff..4321dc80515 100644 --- a/templates/admin/settings/fields/checkbox-post-types.php +++ b/templates/admin/settings/fields/checkbox-post-types.php @@ -13,7 +13,7 @@
get_supported_post_types() as $post_type ) : ?>
diff --git a/tests/test-amp-post-types-support.php b/tests/test-amp-post-types-support.php new file mode 100644 index 00000000000..e23c59be530 --- /dev/null +++ b/tests/test-amp-post-types-support.php @@ -0,0 +1,45 @@ +assertTrue( post_type_supports( 'post', AMP_QUERY_VAR ) ); + remove_post_type_support( 'post', AMP_QUERY_VAR ); + } + + /** + * Test amp_custom_post_types_support. + * + * @see amp_custom_post_types_support() + */ + public function test_amp_custom_post_types_support() { + update_option( AMP_Settings::SETTINGS_KEY, array( + 'post_types_support' => array( + 'foo' => true, + 'bar' => true, + ), + ) ); + amp_custom_post_types_support(); + $this->assertTrue( post_type_supports( 'foo', AMP_QUERY_VAR ) ); + $this->assertTrue( post_type_supports( 'bar', AMP_QUERY_VAR ) ); + delete_option( AMP_Settings::SETTINGS_KEY ); + remove_post_type_support( 'foo', AMP_QUERY_VAR ); + remove_post_type_support( 'bar', AMP_QUERY_VAR ); + } + +} diff --git a/tests/test-class-amp-settings-post-types.php b/tests/test-class-amp-settings-post-types.php index 725c334fdaa..f87890cbb6c 100644 --- a/tests/test-class-amp-settings-post-types.php +++ b/tests/test-class-amp-settings-post-types.php @@ -75,12 +75,14 @@ public function test_register_settings() { } /** - * Test get_settings_value. + * Test get_value. * - * @see AMP_Settings_Post_Types::get_settings_value() + * @see AMP_Settings_Post_Types::get_settings() */ - public function test_get_settings_value() { - $this->assertFalse( $this->instance->get_settings_value( 'foo' ) ); + public function test_get_settings() { + $this->assertEmpty( $this->instance->get_settings() ); + $this->assertInternalType( 'array', $this->instance->get_settings() ); + $this->assertFalse( $this->instance->get_settings( 'foo' ) ); update_option( AMP_Settings::SETTINGS_KEY, array( 'post_types_support' => array( @@ -88,7 +90,9 @@ public function test_get_settings_value() { ), ) ); - $this->assertTrue( $this->instance->get_settings_value( 'post' ) ); + $this->assertContains( 'post', $this->instance->get_settings() ); + $this->assertInternalType( 'array', $this->instance->get_settings() ); + $this->assertTrue( $this->instance->get_settings( 'post' ) ); // Cleanup. delete_option( AMP_Settings::SETTINGS_KEY ); @@ -105,22 +109,67 @@ public function test_get_supported_post_types() { } /** - * Test get_setting_name. + * Test get_name_attribute. * - * @see AMP_Settings_Post_Types::get_setting_name() + * @see AMP_Settings_Post_Types::get_name_attribute() */ - public function test_get_setting_name() { - $this->assertEquals( AMP_Settings::SETTINGS_KEY . '[post_types_support][post]', $this->instance->get_setting_name( 'post' ) ); + public function test_get_name_attribute() { + $this->assertEquals( AMP_Settings::SETTINGS_KEY . '[post_types_support][post]', $this->instance->get_name_attribute( 'post' ) ); } /** - * Test render_setting. + * Test disabled. * - * @see AMP_Settings_Post_Types::render_setting() + * @see AMP_Settings_Post_Types::disabled() */ - public function test_render_setting() { + public function test_disabled() { + $this->assertFalse( $this->instance->disabled( 'foo' ) ); + add_post_type_support( 'foo', AMP_QUERY_VAR ); + $this->assertTrue( $this->instance->disabled( 'foo' ) ); + } + + /** + * Test errors. + * + * @see AMP_Settings_Post_Types::errors() + */ + public function test_errors() { + update_option( AMP_Settings::SETTINGS_KEY, array( + 'post_types_support' => array( + 'foo' => true, + ), + ) ); + remove_post_type_support( 'foo', AMP_QUERY_VAR ); + $this->instance->errors(); + $this->assertNotEmpty( get_settings_errors() ); + delete_option( AMP_Settings::SETTINGS_KEY ); + } + + /** + * Test validate. + * + * @see AMP_Settings_Post_Types::validate() + */ + public function test_validate() { + $this->assertInternalType( 'array', $this->instance->validate( array() ) ); + update_option( AMP_Settings::SETTINGS_KEY, array( + 'post_types_support' => array( + 'foo' => true, + ), + ) ); + $settings = $this->instance->validate( get_option( AMP_Settings::SETTINGS_KEY ) ); + $this->assertInternalType( 'bool', $settings['post_types_support']['foo'] ); + delete_option( AMP_Settings::SETTINGS_KEY ); + } + + /** + * Test render. + * + * @see AMP_Settings_Post_Types::render() + */ + public function test_render() { ob_start(); - $this->instance->render_setting(); + $this->instance->render(); $this->assertContains( '
', ob_get_clean() ); } From dcadc4faa8f6ab3ba9e621c949299cfee9a8a669 Mon Sep 17 00:00:00 2001 From: ThierryA Date: Fri, 24 Nov 2017 15:09:37 +0100 Subject: [PATCH 09/21] Added missing since tag + minor code improvement --- includes/admin/functions.php | 1 + .../class-amp-settings-post-types.php | 19 +++++++++++++++++-- includes/settings/class-amp-settings.php | 14 +++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/includes/admin/functions.php b/includes/admin/functions.php index 4a2e7fba207..8155b44c865 100644 --- a/includes/admin/functions.php +++ b/includes/admin/functions.php @@ -90,6 +90,7 @@ function amp_add_options_menu() { /** * Filter whether to enable the AMP settings. * + * @since 0.5 * @param bool $enable Whether to enable the AMP settings. Default true. */ $short_circuit = apply_filters( 'amp_options_menu_is_enabled', true ); diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index cf70b2c9d54..a6ccbbc9f4e 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -3,16 +3,20 @@ * Post types settings. * * @package AMP + * @since 0.6 */ /** * Settings post types class. + * + * @since 0.6 */ class AMP_Settings_Post_Types { /** * Settings section id. * + * @since 0.6 * @var string */ protected $section_id = 'post_types'; @@ -20,6 +24,7 @@ class AMP_Settings_Post_Types { /** * Settings config. * + * @since 0.6 * @var array */ protected $setting = array(); @@ -71,6 +76,7 @@ public function register_settings() { /** * Getter for settings value. * + * @since 0.6 * @param string $post_type The post type name. * @return bool|array Return true if the post type is always on; the setting value otherwise. */ @@ -95,6 +101,7 @@ public function get_settings( $post_type = false ) { /** * Getter for the supported post types. * + * @since 0.6 * @return object Supported post types list. */ public function get_supported_post_types() { @@ -106,12 +113,13 @@ public function get_supported_post_types() { '_builtin' => false, ), 'objects' ); - return $core + $cpt; + return array_merge( $core, $cpt ); } /** * Getter for the setting HTML input name attribute. * + * @since 0.6 * @param string $post_type The post type name. * @return object The setting HTML input name attribute. */ @@ -126,6 +134,7 @@ public function get_name_attribute( $post_type ) { * Since we can't flag a post type which is not enabled by setting and removed by plugin/theme, * we can't disable the checkbox but the errors() takes care of this scenario. * + * @since 0.6 * @param string $post_type The post type name. * @return bool True if disabled; false otherwise. */ @@ -142,6 +151,8 @@ public function disabled( $post_type ) { /** * Handle errors. + * + * @since 0.6 */ public function errors() { $settings = $this->get_settings(); @@ -167,6 +178,7 @@ public function errors() { /** * Validate and sanitize the settings. * + * @since 0.6 * @param array $settings The post types settings. * @return array The post types settings. */ @@ -184,6 +196,8 @@ public function validate( $settings ) { /** * Setting renderer. + * + * @since 0.6 */ public function render() { require_once AMP__DIR__ . '/templates/admin/settings/fields/checkbox-post-types.php'; @@ -192,12 +206,13 @@ public function render() { /** * Get the instance of AMP_Settings_Post_Types. * + * @since 0.6 * @return object $instance AMP_Settings_Post_Types instance. */ public static function get_instance() { static $instance; - if ( ! $instance instanceof AMP_Settings_Post_Types ) { + if ( ! ( $instance instanceof AMP_Settings_Post_Types ) ) { $instance = new AMP_Settings_Post_Types(); } diff --git a/includes/settings/class-amp-settings.php b/includes/settings/class-amp-settings.php index 2ee29c78444..ac1a97a009b 100644 --- a/includes/settings/class-amp-settings.php +++ b/includes/settings/class-amp-settings.php @@ -3,10 +3,13 @@ * AMP Settings. * * @package AMP + * @since 0.6 */ /** * Settings class. + * + * @since 0.6 */ class AMP_Settings { @@ -14,6 +17,7 @@ class AMP_Settings { * Menu slug. * * @const string + * @since 0.6 */ const MENU_SLUG = 'amp_settings'; @@ -21,11 +25,14 @@ class AMP_Settings { * Settings key. * * @const string + * @since 0.6 */ const SETTINGS_KEY = 'amp'; /** * Initialize. + * + * @since 0.6 */ public function init() { add_action( 'admin_menu', array( $this, 'admin_menu' ) ); @@ -33,6 +40,8 @@ public function init() { /** * Add admin menu. + * + * @since 0.6 */ public function admin_menu() { add_menu_page( @@ -56,6 +65,7 @@ public function admin_menu() { /** * Display Settings. * + * @since 0.6 * @return void Void on user capabilities check failure. */ public function render_screen() { @@ -75,6 +85,7 @@ public function render_screen() { /** * Getter for the AMP svg menu icon. * + * @since 0.6 * @return object The AMP svg menu icon. */ public function get_svg_icon() { @@ -84,12 +95,13 @@ public function get_svg_icon() { /** * Get the instance of AMP_Settings. * + * @since 0.6 * @return object $instance AMP_Settings instance. */ public static function get_instance() { static $instance; - if ( ! $instance instanceof AMP_Settings ) { + if ( ! ( $instance instanceof AMP_Settings ) ) { $instance = new AMP_Settings(); } From cdf905837ccc3cda549b3813b280a429895f2e2b Mon Sep 17 00:00:00 2001 From: ThierryA Date: Tue, 28 Nov 2017 10:27:52 +0100 Subject: [PATCH 10/21] AMP Settings code improvements --- amp.php | 8 +++++++- includes/amp-post-types-support.php | 2 +- templates/admin/settings/fields/checkbox-post-types.php | 2 +- templates/admin/settings/screen.php | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/amp.php b/amp.php index 0e08b153977..ab9c236787f 100644 --- a/amp.php +++ b/amp.php @@ -81,9 +81,15 @@ function amp_init() { * @since 0.6 */ function define_query_var() { + /** + * Filter the AMP query variable. + * + * @since 0.3.2 + * @param string $query_var The AMP query variable. + */ define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) ); } -add_action( 'after_setup_theme', 'define_query_var', 7 ); +add_action( 'after_setup_theme', 'define_query_var', 3 ); // Make sure the `amp` query var has an explicit value. // Avoids issues when filtering the deprecated `query_string` hook. diff --git a/includes/amp-post-types-support.php b/includes/amp-post-types-support.php index 3a5a8683d64..5cf7c522f46 100644 --- a/includes/amp-post-types-support.php +++ b/includes/amp-post-types-support.php @@ -32,4 +32,4 @@ function amp_custom_post_types_support() { } } } -add_action( 'after_setup_theme', 'amp_custom_post_types_support' ); +add_action( 'after_setup_theme', 'amp_custom_post_types_support', 5 ); diff --git a/templates/admin/settings/fields/checkbox-post-types.php b/templates/admin/settings/fields/checkbox-post-types.php index 4321dc80515..a8845bc3123 100644 --- a/templates/admin/settings/fields/checkbox-post-types.php +++ b/templates/admin/settings/fields/checkbox-post-types.php @@ -6,7 +6,7 @@ */ // Check referrer. -if ( ! $this instanceof AMP_Settings_Post_Types ) { +if ( ! ( $this instanceof AMP_Settings_Post_Types ) ) { return; } ?> diff --git a/templates/admin/settings/screen.php b/templates/admin/settings/screen.php index 280bd88f8c9..fd1d8248f43 100644 --- a/templates/admin/settings/screen.php +++ b/templates/admin/settings/screen.php @@ -6,7 +6,7 @@ */ // Check referrer. -if ( ! $this instanceof AMP_Settings ) { +if ( ! ( $this instanceof AMP_Settings ) ) { return; } ?> From f6300624d84d1290215748ae92002c6de9f33725 Mon Sep 17 00:00:00 2001 From: ThierryA Date: Sun, 3 Dec 2017 15:30:55 -0600 Subject: [PATCH 11/21] Modified AMP admin post type support error handling --- .../class-amp-settings-post-types.php | 57 +- readme.md | 851 ++++-------------- .../settings/fields/checkbox-post-types.php | 2 +- tests/test-class-amp-settings-post-types.php | 16 +- 4 files changed, 198 insertions(+), 728 deletions(-) diff --git a/includes/settings/class-amp-settings-post-types.php b/includes/settings/class-amp-settings-post-types.php index a6ccbbc9f4e..35469e1b53a 100644 --- a/includes/settings/class-amp-settings-post-types.php +++ b/includes/settings/class-amp-settings-post-types.php @@ -129,46 +129,45 @@ public function get_name_attribute( $post_type ) { } /** - * Check whether the post type should be disabled or not. - * - * Since we can't flag a post type which is not enabled by setting and removed by plugin/theme, - * we can't disable the checkbox but the errors() takes care of this scenario. + * Handle errors. * * @since 0.6 - * @param string $post_type The post type name. - * @return bool True if disabled; false otherwise. */ - public function disabled( $post_type ) { - $settings = $this->get_settings(); + public function errors() { + $on_update = ( + isset( $_GET['settings-updated'] ) // WPCS: CSRF ok. + && + ! empty( (bool) wp_unslash( $_GET['settings-updated'] ) ) // WPCS: CSRF ok. + ); - // Disable if post type support was not added by setting and added by plugin/theme. - if ( post_type_supports( $post_type, AMP_QUERY_VAR ) && ! isset( $settings[ $post_type ] ) ) { - return true; + // Only apply on update. + if ( ! $on_update ) { + return; } - return false; - } + foreach ( $this->get_supported_post_types() as $post_type ) { + if ( ! isset( $post_type->name, $post_type->label ) ) { + continue; + } - /** - * Handle errors. - * - * @since 0.6 - */ - public function errors() { - $settings = $this->get_settings(); + $post_type_support = post_type_supports( $post_type->name, AMP_QUERY_VAR ); + $value = $this->get_settings( $post_type->name ); - foreach ( $settings as $post_type => $value ) { - // Throw error if post type support was added by setting and removed by plugin/theme. - if ( true === $value && ! post_type_supports( $post_type, AMP_QUERY_VAR ) ) { - $post_type_object = get_post_type_object( $post_type ); + if ( true === $value && true !== $post_type_support ) { + /* Translators: %s: Post type name. */ + $error = __( '"%s" could not be activated because support is removed by a plugin or theme', 'amp' ); + } elseif ( empty( $value ) && true === $post_type_support ) { + /* Translators: %s: Post type name. */ + $error = __( '"%s" could not be deactivated because support is added by a plugin or theme', 'amp' ); + } + if ( isset( $error ) ) { add_settings_error( - $post_type, - $post_type, + $post_type->name, + $post_type->name, sprintf( - /* Translators: %s: Post type name. */ - __( '"%s" could not be activated because support was removed by a plugin or theme', 'amp' ), - isset( $post_type_object->label ) ? $post_type_object->label : $post_type + $error, + $post_type->label ) ); } diff --git a/readme.md b/readme.md index 92608c02aea..4225e1c809a 100644 --- a/readme.md +++ b/readme.md @@ -1,698 +1,175 @@ -# AMP for WordPress + +# AMP -## Overview +Enable Accelerated Mobile Pages (AMP) on your WordPress site. -This plugin adds support for the [Accelerated Mobile Pages](https://www.ampproject.org) (AMP) Project, which is an open source initiative that aims to provide mobile optimized content that can load instantly everywhere. +**Contributors:** [batmoo](https://profiles.wordpress.org/batmoo), [joen](https://profiles.wordpress.org/joen), [automattic](https://profiles.wordpress.org/automattic), [potatomaster](https://profiles.wordpress.org/potatomaster), [albertomedina](https://profiles.wordpress.org/albertomedina) +**Tags:** [amp](https://wordpress.org/plugins/tags/amp), [mobile](https://wordpress.org/plugins/tags/mobile) +**Requires at least:** 4.7 +**Tested up to:** 4.8 +**Stable tag:** 0.5.1 +**License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) -With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/?p=123&=1` - -Note #1: that Pages and archives are not currently supported. - -Note #2: this plugin only creates AMP content but does not automatically display it to your users when they visit from a mobile device. That is handled by AMP consumers such as Google Search. For more details, see the [AMP Project FAQ](https://www.ampproject.org/docs/support/faqs.html). - -## Customization / Templating - -The plugin ships with a default template that looks nice and clean and we tried to find a good balance between ease and extensibility when it comes to customization. - -You can tweak small pieces of the template or the entire thing depending on your needs. - -### AMP Customizer - -The plugin ships with its own Customizer that you can use to tweak various parts of the default template like colors. - -#### Disabling the AMP Customizer - -If you're using a completely custom template, you may want to disable the AMP Customizer: - -``` -add_filter( 'amp_customizer_is_enabled', '__return_false' ); -``` - -Note that this needs to be called before the `after_setup_theme` hook to work. - -### Where Do I Put My Code? - -The code snippets below and any other code-level customizations should happen in one of the following locations. - -If you're using an off-the-shelf theme (like from the WordPress.org Theme Directory): - -- A [child theme](https://developer.wordpress.org/themes/advanced-topics/child-themes/). -- A custom plugin that you activate via the Dashboard. -- A [mu-plugin](https://codex.wordpress.org/Must_Use_Plugins). - -If you're using a custom theme: - -- `functions.php` (or via a 'require' call to files that load from `functions.php`). -- Any of the options above. - -### Theme Mods - -The default template will attempt to draw from various theme mods, such as site icon, if supported by the active theme. - -#### Site Icon - -If you add a site icon, we will automatically replace the WordPress logo in the template. - -If you'd prefer to do it via code: - -```php -add_filter( 'amp_post_template_data', 'xyz_amp_set_site_icon_url' ); - -function xyz_amp_set_site_icon_url( $data ) { - // Ideally a 32x32 image - $data[ 'site_icon_url' ] = get_stylesheet_directory_uri() . '/images/amp-site-icon.png'; - return $data; -} -``` - -#### Logo Only - -If you want to hide the site text and just show a logo, use the `amp_post_template_css` action. The following colors the title bar black, hides the site title, and replaces it with a centered logo: - -```php -add_action( 'amp_post_template_css', 'xyz_amp_additional_css_styles' ); - -function xyz_amp_additional_css_styles( $amp_template ) { - // only CSS here please... - ?> - header.amp-wp-header { - padding: 12px 0; - background: #000; - } - header.amp-wp-header a { - background-image: url( 'https://example.com/path/to/logo.png' ); - background-repeat: no-repeat; - background-size: contain; - display: block; - height: 28px; - width: 94px; - margin: 0 auto; - text-indent: -9999px; - } - 'ImageObject', - 'url' => get_template_directory_uri() . '/images/my-amp-metadata-logo.png', - 'height' => 60, - 'width' => 600, - ); - - return $metadata; -} -``` - -#### Template Meta (Author, Date, etc.) - -For the meta section of the template (i.e. author, date, taxonomies, etc.), you can override templates for the existing sections, remove them, add new ones. +[![Build Status](https://travis-ci.org/Automattic/amp-wp.svg?branch=master)](https://travis-ci.org/Automattic/amp-wp) -##### Example: Override Author Template from Theme - -Create a folder in your theme called `amp` and add a file called `meta-author.php` with the following: - -```php - -``` - -Replace the contents, as needed. - -##### Example: Override Taxonomy Template via filter - -This will load the file `t/meta-custom-tax.php` for the `taxonomy` section: - -```php -add_filter( 'amp_post_template_file', 'xyz_amp_set_custom_tax_meta_template', 10, 3 ); - -function xyz_amp_set_custom_tax_meta_template( $file, $type, $post ) { - if ( 'meta-taxonomy' === $type ) { - $file = dirname( __FILE__ ) . '/t/meta-custom-tax.php'; - } - return $file; -} -``` - -In `t/meta-custom-tax.php`, you can add something like the following to replace the default category and tags with your custom `author` taxonomy: - -```php -
  • - get( 'post_id' ), 'xyz-author', '', ', ' ); ?> -
  • -``` - -##### Example: Remove Author from `header_meta` - -This will completely remove the author section: - -```php -add_filter( 'amp_post_article_header_meta', 'xyz_amp_remove_author_meta' ); - -function xyz_amp_remove_author_meta( $meta_parts ) { - foreach ( array_keys( $meta_parts, 'meta-author', true ) as $key ) { - unset( $meta_parts[ $key ] ); - } - return $meta_parts; -} -``` - -##### Example: Add Comment Count to `footer_meta` - -This adds a new section to display the comment count: - -```php -add_filter( 'amp_post_article_footer_meta', 'xyz_amp_add_comment_count_meta' ); - -function xyz_amp_add_comment_count_meta( $meta_parts ) { - $meta_parts[] = 'xyz-meta-comment-count'; - return $meta_parts; -} - -add_filter( 'amp_post_template_file', 'xyz_amp_set_comment_count_meta_path', 10, 3 ); - -function xyz_amp_set_comment_count_meta_path( $file, $type, $post ) { - if ( 'xyz-meta-comment-count' === $type ) { - $file = dirname( __FILE__ ) . '/templates/xyz-meta-comment-count.php'; - } - return $file; -} -``` - -Then, in `templates/xyz-meta-comment-count.php`: - -```php -
  • - get( 'post' )->comment_count, 'xyz-text-domain' ) ); ?> -
  • -``` - -#### Custom CSS - -##### Rule Additions - -If you want to append to the existing CSS rules (e.g. styles for a custom embed handler), you can use the `amp_post_template_css` action: - -```php -add_action( 'amp_post_template_css', 'xyz_amp_my_additional_css_styles' ); - -function xyz_amp_my_additional_css_styles( $amp_template ) { - // only CSS here please... - ?> - .amp-wp-byline amp-img { - border-radius: 0; /* we don't want round avatars! */ - } - .my-custom-class { - color: blue; - } - ` opening and closing tag. - -#### Head and Footer - -If you want to add stuff to the head or footer of the default AMP template, use the `amp_post_template_head` and `amp_post_template_footer` actions. - -```php -add_action( 'amp_post_template_footer', 'xyz_amp_add_pixel' ); - -function xyz_amp_add_pixel( $amp_template ) { - $post_id = $amp_template->get( 'post_id' ); - ?> - - ` section: - -```php -do_action( 'amp_post_template_head', $this ); -``` - -* You must trigger the `amp_post_template_footer` action right before the `` tag: - -```php -do_action( 'amp_post_template_footer', $this ); -``` - -* Within your `amp-custom` `style` tags, you must trigger the `amp_post_template_css` action: - -```php -do_action( 'amp_post_template_css', $this ); -``` - -* You must include [all required mark-up](https://www.ampproject.org/docs/get_started/create/basic_markup.html) that isn't already output via the `amp_post_template_head` action. - -## Handling Media - -By default, the plugin attempts to gracefully handle the following media elements in your content: - -- images (converted from `img` => `amp-img` or `amp-anim`) -- videos (converted from `video` => `amp-video`; Note: Flash is not supported) -- audio (converted from `audio` => `amp-audio`) -- iframes (converted from `iframes` => `amp-iframes`) -- YouTube, Instagram, Twitter, and Vine oEmbeds and shortcodes (converted from the embed to the matching `amp-` component) - -For additional media content such as custom shortcodes, oEmbeds or manually inserted embeds, ads, etc. there are several customization options available and outlined below. - -### Do Nothing - -If your embeds/media use standard iframes, you can choose to do nothing and let the plugin handle things. They should "just work" in most cases. - -### `the_content` filter - -All existing hooks on `the_content` will continue to work. This can be a good or bad thing. Good, because existing plugin integrations will continue to work. Bad, because not all added content may make sense in an AMP context. - -You can add additional callbacks to `the_content` filter to output additional content as needed. Use the `is_amp_endpoint()` function to check if an AMP version of a post is being viewed. However, we recommend using an Embed Handler instead. - -Caveat: with this method, if you add a custom component that requires inclusion of a script, you will need to add that script manually to the template using the `amp_post_template_head` action. - -### Update Existing Shortcodes - -In your existing shortcode or oEmbed callbacks, you can branch using the `is_amp_endpoint()` and output customized content for AMP content. - -The same caveat about scripts for custom AMP components applies. - -### Custom Embed Handler - -Embed Handlers are helper classes to inject AMP-specific content for your oEmbeds and shortcodes. - -Embed Handlers register the embeds they handle using standard WordPress functions such as `add_shortcode`. For working examples, check out the existing implementations for Instagram, Twitter, etc. as guides to build your own. - -While the primary purpose of Embed Handlers is for use with embeds, you can use them for adding AMP-specific `the_content` callbacks as well. - -#### Step 1: Build the Embed Handler - -Your Embed Handler class needs to extend the `AMP_Base_Embed_Handler` class. - -Note: make sure to set proper priorities or remove existing callbacks for your regular content. - -In `classes/class-amp-related-posts-embed.php`: - -```php -class XYZ_AMP_Related_Posts_Embed extends AMP_Base_Embed_Handler { - public function register_embed() { - // If we have an existing callback we are overriding, remove it. - remove_filter( 'the_content', 'xyz_add_related_posts' ); - - // Add our new callback - add_filter( 'the_content', array( $this, 'add_related_posts' ) ); - } - - public function unregister_embed() { - // Let's clean up after ourselves, just in case. - add_filter( 'the_content', 'xyz_add_related_posts' ); - remove_filter( 'the_content', array( $this, 'add_related_posts' ) ); - } - - public function get_scripts() { - return array( - 'amp-mustache' => 'https://cdn.ampproject.org/v0/amp-mustache-0.1.js', - 'amp-list' => 'https://cdn.ampproject.org/v0/amp-list-0.1.js', - ); - } - - public function add_related_posts( $content ) { - // See https://github.com/ampproject/amphtml/blob/master/extensions/amp-list/amp-list.md for details on amp-list - $related_posts_list = ' - - -
    - Show more -
    -
    '; - - $content .= $related_posts_list; - - return $content; - } -} -``` - -#### Step 2: Load the Embed Handler - -```php -add_filter( 'amp_content_embed_handlers', 'xyz_amp_add_related_embed', 10, 2 ); - -function xyz_amp_add_related_embed( $embed_handler_classes, $post ) { - require_once( dirname( __FILE__ ) . '/classes/class-amp-related-posts-embed.php' ); - $embed_handler_classes[ 'XYZ_AMP_Related_Posts_Embed' ] = array(); - return $embed_handler_classes; -} -``` - -### Custom Sanitizer - -The name "sanitizer" is a bit of a misnomer. These are primarily used internally in the plugin to make your site's content compatible with the amp spec. This involves stripping unsupported tags and attributes and transforming media elements to their matching amp version (e.g. `img` => `amp-img`). - -Sanitizers are pretty versatile and, unlike Embed Handlers -- which work with HTML content as a string -- they can be used to manipulate your post's AMP content using [PHP's `DOM` library](http://php.net/manual/en/book.dom.php). We've included an example that shows you how to use a custom sanitizer to inject ads into your content. You can, of course, do many other things such as add related content. - -#### Step 1: Build the Sanitizer - -Your sanitizer needs to extend the `AMP_Base_Sanitizer`. In `classes/class-ad-inject-sanitizer.php`: - -```php -class XYZ_AMP_Ad_Injection_Sanitizer extends AMP_Base_Sanitizer { - public function sanitize() { - $body = $this->get_body_node(); - - // Build our amp-ad tag - $ad_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-ad', array( - // Taken from example at https://github.com/ampproject/amphtml/blob/master/builtins/amp-ad.md - 'width' => 300, - 'height' => 250, - 'type' => 'a9', - 'data-aax_size' => '300x250', - 'data-aax_pubname' => 'test123', - 'data-aax_src' => '302', - ) ); - - // Add a placeholder to show while loading - $fallback_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-img', array( - 'placeholder' => '', - 'layout' => 'fill', - 'src' => 'https://placehold.it/300X250', - ) ); - $ad_node->appendChild( $fallback_node ); - - // If we have a lot of paragraphs, insert before the 4th one. - // Otherwise, add it to the end. - $p_nodes = $body->getElementsByTagName( 'p' ); - if ( $p_nodes->length > 6 ) { - $p_nodes->item( 4 )->parentNode->insertBefore( $ad_node, $p_nodes->item( 4 )); - } else { - $body->appendChild( $ad_node ); - } - } -} -``` - -#### Step 2: Load the Sanitizer - -```php -add_filter( 'amp_content_sanitizers', 'xyz_amp_add_ad_sanitizer', 10, 2 ); - -function xyz_amp_add_ad_sanitizer( $sanitizer_classes, $post ) { - require_once( dirname( __FILE__ ) . '/classes/class-ad-inject-sanitizer.php' ); - $sanitizer_classes[ 'XYZ_AMP_Ad_Injection_Sanitizer' ] = array(); // the array can be used to pass args to your sanitizer and accessed within the class via `$this->args` - return $sanitizer_classes; -} -``` - -## Extracting Image Dimensions - -AMP requires images to have width and height attributes. When these attributes aren't present in an image tag, AMP-WP will -attempt to determine them for the image. - -### Extraction Methods - -#### Concurrent Dimension Extraction - PHP 5.3+ and cURL -If you're using PHP 5.3+ and have the cURL extension installed, AMP-WP will attempt to determine dimensions for all images -that need them concurrently. Only the minimum number of bytes required to determine the dimensions for a given image type -are retrieved. Dimensions are then cached via transients for subsequent requests. This is the fastest and therefore recommended method. -#### Sequential Dimension Extraction - PHP 5.2 or no cURL -If you're using PHP 5.2 or do not have the cURL extension installed, AMP-WP will attempt to determine image dimensions -sequentially. Only the minimum number of bytes required to determine the dimensions for a given image type are retrieved, -but the time it takes to retrieve each image's dimensions sequentially can still add up. Dimensions are then cached via transients for subsequent requests. -#### Custom Dimension Extraction -You can implement your own image dimension extraction method by adding a callback to the **amp_extract_image_dimensions_batch** filter. - -amp_extract_image_dimensions_batch callback functions take a single argument, *$dimensions* by convention, which is a map/array of image urls to either an array containing the -dimensions of the image at the url (if another callback for the filter was able to determine them), or false if the dimensions have yet to be determined, e.g. - -```php -array( - 'http://i0.wp.com/placehold.it/350x150.png' => array( - 'width' => 350, - 'height' => 150, - ), - 'http://i0.wp.com/placehold.it/1024x768.png' => false, -); -``` -Your custom dimension extraction callback would iterate through the mappings contained in this single argument, determining -dimensions via your custom method for all image url keys whose values are not arrays of dimensions, e.g. -```php -function my_custom_dimension_extraction_callback( $dimensions ) { - foreach ( $dimensions as $url => $value ) { - // Skip if dimensions have already been determined for this image. - if ( is_array( $value ) ) { - continue; - } - $width = - $height = - $dimensions[ $url ] = array( - 'width' => $width, - 'height' => $height, - ); - } - - return $dimensions; -``` -Your callback needs to return $dimensions so that the value either cascades to the next callback that was added to the *amp_extract_image_dimensions_batch* filter or is -returned to the apply_filter() call (if there are no more unprocessed callbacks). - -The default callback provided by WP-AMP described above, *extract_by_downloading_images*, will fire unless explicitly removed, so be sure -to remove it from the callback chain if you don't want it to, e.g. - -```php - remove_filter( 'amp_extract_image_dimensions_batch', array( 'AMP_Image_Dimension_Extractor', 'extract_by_downloading_images' ), 999, 1 ); -```` - -**Note that if you previously added a custom dimension extraction callback to the *amp_extract_image_dimensions* filter, -you need to update it to hook into the *amp_extract_image_dimensions_batch* filter instead and iterate over the key value -pairs in the single argument as per the example above.** - -## Analytics - -There are two options you can follow to include analytics tags in your posts. - -### Plugin Analytics Options - -The plugin defines an analytics option to enable the addition of -[amp-analytics](https://www.ampproject.org/docs/reference/components/amp-analytics) in your posts. When the plugin is - active, an AMP top-level menu appears in the Dashboard with one inner sub-menu called 'Analytics': - - ![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/amp-options-analytics.png) - - Selecting the `Analytics` sub-menu in the AMP options menu takes us to an Analytics Options entry page, where we can - define the analytics tags we want to have, by specifying the vendor type (e.g. Parsely), and the associated JSON - configuration. - - ![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/analytics-option-entries.png) - -Notice that the goal of this option of the plugin is to provide a simple mechanism to insert analytics tags; - it provides very simple validation based solely on the validity of the JSON string provided. It is the users - responsibility to make sure that the values in the configuration string and the vendor type used are coherent with - the analytics requirements of their site . Please review the documentation in the [AMP project ](https://github.com/ampproject/amphtml/blob/master/extensions/amp-analytics/amp-analytics.md) and in [AMPByExample](https://ampbyexample.com/components/amp-analytics/). - - The AMP Analytics options entry form provides a very simple validation feedback mechanism: if the JSON configuration - string entered is invalid (i.e. not valid JSON), an error message (in red) is displayed below the title of the - options window and the entry form is reloaded: - -![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/invalid-input.png) - -And, if the configuration provided is actually a valid JSON string, a success message (in green) is displayed at the -top of the window below the title, and again the entry form is reloaded. - -![AMP Options Menu](https://github.com/Automattic/amp-wp/blob/amedina/amp-analytics-customizer/readme-assets/options-saved.png) - -### Manually - -Alaternatively, you can use the `amp_post_template_analytics` filter: - -```php -add_filter( 'amp_post_template_analytics', 'xyz_amp_add_custom_analytics' ); -function xyz_amp_add_custom_analytics( $analytics ) { - if ( ! is_array( $analytics ) ) { - $analytics = array(); - } - - // https://developers.google.com/analytics/devguides/collection/amp-analytics/ - $analytics['xyz-googleanalytics'] = array( - 'type' => 'googleanalytics', - 'attributes' => array( - // 'data-credentials' => 'include', - ), - 'config_data' => array( - 'vars' => array( - 'account' => "UA-XXXXX-Y" - ), - 'triggers' => array( - 'trackPageview' => array( - 'on' => 'visible', - 'request' => 'pageview', - ), - ), - ), - ); - - // https://www.parsely.com/docs/integration/tracking/google-amp.html - $analytics['xyz-parsely'] = array( - 'type' => 'parsely', - 'attributes' => array(), - 'config_data' => array( - 'vars' => array( - 'apikey' => 'YOUR APIKEY GOES HERE', - ) - ), - ); - - return $analytics; -} -``` - -Each analytics entry must include a unique array key and the following attributes: - -- `type`: `(string)` one of the [valid vendors](https://github.com/ampproject/amphtml/blob/master/extensions/amp-analytics/amp-analytics.md#analytics-vendors) for amp-analytics. -- `attributes`: `(array)` any [additional valid attributes](https://github.com/ampproject/amphtml/blob/master/extensions/amp-analytics/amp-analytics.md#attributes) to add to the `amp-analytics` element. -- `config_data`: `(array)` the [config data](https://github.com/ampproject/amphtml/blob/master/extensions/amp-analytics/amp-analytics.md#configuration) to include in the `amp-analytics` script tag. This is `json_encode`-d on output. - -## Custom Post Type Support - -By default, the plugin only creates AMP content for posts. You can add support for other post_types using the post_type parameter used when registering the custom post type (assume our post_type is `xyz-review`): - -```php -add_action( 'amp_init', 'xyz_amp_add_review_cpt' ); -function xyz_amp_add_review_cpt() { - add_post_type_support( 'xyz-review', AMP_QUERY_VAR ); -} -``` - -You'll need to flush your rewrite rules after this. - -If you want a custom template for your post type: - -```php -add_filter( 'amp_post_template_file', 'xyz_amp_set_review_template', 10, 3 ); - -function xyz_amp_set_review_template( $file, $type, $post ) { - if ( 'single' === $type && 'xyz-review' === $post->post_type ) { - $file = dirname( __FILE__ ) . '/templates/my-amp-review-template.php'; - } - return $file; -} -``` - -We may provide better ways to handle this in the future. - -## Plugin integrations - -### Jetpack - -Jetpack integration is baked in. More support for things like Related Posts to come. - -### Parse.ly - -[Parse.ly's WordPress plugin](https://wordpress.org/plugins/wp-parsely/) automatically tracks AMP pages when enabled along with this plugin. +This plugin adds support for the [Accelerated Mobile Pages](https://www.ampproject.org) (AMP) Project, which is an an open source initiative that aims to provide mobile optimized content that can load instantly everywhere. +With the plugin active, all posts on your site will have dynamically generated AMP-compatible versions, accessible by appending `/amp/` to the end your post URLs. For example, if your post URL is `http://example.com/2016/01/01/amp-on/`, you can access the AMP version at `http://example.com/2016/01/01/amp-on/amp/`. If you do not have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22) enabled, you can do the same thing by appending `?amp=1`, i.e. `http://example.com/?p=123&=1` -### Yoast SEO +Note #1: that Pages and archives are not currently supported. Pages support is being worked on. -If you're using [Yoast SEO](https://wordpress.org/plugins/wordpress-seo/), check out the companion plugin here: https://github.com/Yoast/yoastseo-amp +Note #2: this plugin only creates AMP content but does not automatically display it to your users when they visit from a mobile device. That is handled by AMP consumers such as Google Search. For more details, see the [AMP Project FAQ](https://www.ampproject.org/docs/support/faqs.html). -## Compatibility Issues +Follow along with or contribute to the development of this plugin at https://github.com/Automattic/amp-wp + +## Installation ## + +1. Upload the folder to the `/wp-content/plugins/` directory +1. Activate the plugin through the 'Plugins' menu in WordPress +1. You may need to refresh your permalinks by going to `Settings > Permalinks` and tapping the `Save` button. + +## Frequently Asked Questions ## + +### How do I customize the AMP output for my site? ### +You can tweak a few things like colors from the AMP Customizer. From the Dashboard, go to `Appearance > AMP`. + +For deeper level customizations, please see the readme at https://github.com/Automattic/amp-wp/blob/master/readme.md + +### What about ads and shortcodes and such? ### +Check out https://github.com/Automattic/amp-wp/blob/master/readme.md#handling-media + +### What about analytics? ### +Many plugins are adding AMP support already. If you handling analytics yourself, please see https://github.com/Automattic/amp-wp/blob/master/readme.md#analytics + +### Google Webmaster Tools is reporting validation errors for my site. How do I fix them? ### +The best place to start is to open a new discussion in the [support forum](https://wordpress.org/support/plugin/amp) with details on what the specific validation error is. + +### Why aren't Pages supported yet ### +A wise green Yoda once said, "Patience you must have, my young padawan." We're working on it :) + + +## Changelog ## + +### 0.5.1 (2017-08-17) ### +- Fix: issues with invalid tags not being stripped out (e.g. script tags) (h/t tmmbecker, fahmi182, pppdog, seejacobscott, RavanH, jenniejj, lkraav, simonrperry for the reports). +- Fix: issues with dimension extraction for protocol-less and relative URLs (h/t ktmn for the report). + +### 0.5 (2017-08-04) ### +- Whitelist Sanitizer: Replace Blacklist Sanitizer with a whitelist-based approach using the AMP spec (props delputnam) +- Image Dimensions: Replace fastimage with fasterimage for PHP 5.4+. Enables faster downloads and wider support (props gititon) +- Embed Handlers: Added support for Vimeo, SoundCloud, Pinterest (props amedina) and PlayBuzz (props lysk88) +- Analytics: UI for easier addition of analytics tags (props amedina) +- Fix: parse query strings properly (props amyevans) +- Fix: Old slug redirect for AMP URLs (props rahulsprajapati) +- Fix: Handle issues with data uri images in CSS (props trepmal) +- Fix: Add amp-video js for amp-video tags (props ptbello) +- Fix: Output CSS for feature image (props mjangda) +- Fix: Fix attribute when adding AMP Mustache lib (props luigitec) +- Fix: Various documentation updates (props piersb, bhhaskin) +- Fix: PHP Warnings from `register_customizer_ui` (props jahvi) +- Fix: Coding Standards (props paulschreiber) + +### 0.4.2 (2016-10-13) ### +- Fix: Prevent validation errors for `html` tag (h/t Maxime2 and everyone else that reported this error) +- Fix: Handle variable name conflict that was causing content_max_width to be ignored (h/t mimancillas) +- Fix: Prevent errors when nodes don't have attributes (h/t stephenmax) +- Fix: Back-compat for 4.5 (add sanitize_hex_color function, h/t xotihcan) +- Fix: Handle gif featured images (h/t protocolil) +- Documentation updates (props troyxmccall) + +### 0.4.1 (2016-10-10) ### +- Fix: Don't fire the_content for featured image output +- Fix: Don't show comment link when disabled and no comments on post (h/t neotrope) +- Fix: strip `!important` from inline styles (h/t compointdesigner and enriccardonagmailcom) + +### 0.4 (2016-10-06) ### +- New template: spiffy, shiny, and has the fresh theme smell (props allancole and the Automattic Theme Team). +- *Warning*: The template update has potential breaking changes. Please see https://wordpress.org/support/topic/v0-4-whats-new-and-possible-breaking-changes/ +- AMP Customizer: Pick your colors and make the template your own (props DrewAPicture and 10up) +- Fix: support for inline styles (props coreymckrill). +- Fix: no more fatal errors when tags not supported by post type (props david-binda) +- Fix: no more unnecessary `
    ` tags. +- Fix: sanitize children of removed nodes (like empty `` tags) (props Maxime2). +- Fix: no more broken YouTube URLs with multiple ?s. +- Fix: properly handle tel and sms schemes (h/t soundstrategies). +- Fix: remove amp endpoint on deactivate. +- New filter: `amp_pre_get_permalink` if you want a completely custom AMP permalink. + +### 0.3.3 (Aug 18, 2016) ### +- Handle many more validation errors (props bcampeau and alleyinteractive). +- New filter: `amp_post_template_dir` (props mustafauysal). +- New template: Nav bar is now it's own template part (props jdevalk). +- Better ratio for YouTube embeds. +- Fix: better timezone handling (props rinatkhaziev). +- Fix: better handling of non-int dimensions (like `100%`). +- Fix: better handling of empty dimensions. +- Fix: `autoplay` is a bool-like value. +- Fix: breakage when using the `query_string` hook (h/t mkuplens). +- Fix: don't break really large Twitter IDs. +- Fix: don't break Instagram shortcodes when using URLs with querystrings. +- Readme improvements (props nickjohnford, sotayamashita) + +### 0.3.2 (Mar 4, 2016) ### +* Jetpack Stats support. +* Better version of Merriweather and use system fonts for sans-serif (props mattmiklic). +* Move font to stylesheet so it can be more easily overridden (props mattmiklic). +* Fix: Template loading issues on Windows. (Thanks to everyone who reported this, especially w33zy for pointing out the `validate_file` issue.) +* Fix: don't run AMP on post comment feeds (props kraftbj). +* Fix: un-break pagination when using a static home page with multiple pages. +* Fix: force amp-iframe to use https to validate correctly (props mister-ben). +* Fix: validation for `target` and `video`/`audio` attributes. +* Fix: clipped images in galleries (thanks tobaco). + +### 0.3.1 (Feb 24, 2016) ### +* Allow custom query var (props vaurdan). +* Fix AMP URLs for non-pretty permalinks (props rakuishi). +* Fix for password-protected posts. +* Fix dimension extraction for schema-less or relative image URLs. +* Better fallback for images with no dimensions. +* Validation fixes for `a` tags (props kraftbj). +* Updated AMP boilerplate. +* Allow `on` tags for elements (props Steven Evatt). +* Prefixed class names. + +### 0.3 (Feb 18, 2016) ### +* Fetch dimensions for hotlinked images. +* Add amp-facebook support. +* Add some new actions and filters (e.g. `amp_init`). +* Fix validation errors for [gallery] shortcodes. +* Fix issues with path validation on Windows. +* Fix issues with really squeezed layout. +* Breaking change: `style.css` no longer contains the `