diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php index 86ff1f53d5c..4c319e98728 100644 --- a/includes/class-amp-theme-support.php +++ b/includes/class-amp-theme-support.php @@ -220,7 +220,6 @@ public static function add_hooks() { */ add_action( 'wp_print_styles', array( __CLASS__, 'print_amp_styles' ), 0 ); // Print boilerplate before theme and plugin stylesheets. add_action( 'wp_head', 'amp_add_generator_metadata', 20 ); - add_action( 'wp_head', 'amp_print_schemaorg_metadata' ); if ( is_customize_preview() ) { add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_customize_preview_scripts' ), 1000 ); @@ -754,7 +753,7 @@ public static function print_amp_styles() { * * @param DOMDocument $dom Doc. */ - protected static function ensure_required_markup( DOMDocument $dom ) { + public static function ensure_required_markup( DOMDocument $dom ) { $xpath = new DOMXPath( $dom ); // First ensure the mandatory amp attribute is present on the html element, as otherwise it will be stripped entirely. @@ -795,7 +794,19 @@ protected static function ensure_required_markup( DOMDocument $dom ) { ) ); $head->insertBefore( $meta_viewport, $meta_charset->nextSibling ); } - + // Prevent schema.org duplicates. + $has_schema_org_metadata = false; + foreach ( $head->getElementsByTagName( 'script' ) as $script ) { + if ( 'application/ld+json' === $script->getAttribute( 'type' ) && false !== strpos( $script->nodeValue, 'schema.org' ) ) { + $has_schema_org_metadata = true; + break; + } + } + if ( ! $has_schema_org_metadata ) { + $script = $dom->createElement( 'script', wp_json_encode( amp_get_schemaorg_metadata() ) ); + $script->setAttribute( 'type', 'application/ld+json' ); + $head->appendChild( $script ); + } // Ensure rel=canonical link. $rel_canonical = null; foreach ( $head->getElementsByTagName( 'link' ) as $link ) { diff --git a/tests/test-class-amp-theme-support.php b/tests/test-class-amp-theme-support.php index a6a3be40ce8..4273ee24718 100644 --- a/tests/test-class-amp-theme-support.php +++ b/tests/test-class-amp-theme-support.php @@ -330,4 +330,45 @@ public function test_handle_xhr_request() { AMP_Theme_Support::handle_xhr_request(); $this->assertContains( 'AMP-Access-Control-Allow-Source-Origin: https://example.org', xdebug_get_headers() ); } + + /** + * Test ensure_required_markup(). + * + * @dataProvider get_script_data + * @covers AMP_Theme_Support::ensure_required_markup() + * @param string $script The value of the script. + * @param boolean $expected The expected result. + */ + public function test_ensure_required_markup( $script, $expected ) { + $page = 'Test'; + $dom = new DOMDocument(); + $dom->loadHTML( sprintf( $page, $script ) ); + AMP_Theme_Support::ensure_required_markup( $dom ); + $this->assertEquals( $expected, substr_count( $dom->saveHTML(), 'schema.org' ) ); + } + /** + * Data provider for test_ensure_required_markup. + * + * @return array + */ + public function get_script_data() { + return array( + 'schema_org_not_present' => array( + '', + 1, + ), + 'schema_org_present' => array( + wp_json_encode( array( '@context' => 'http://schema.org' ) ), + 1, + ), + 'schema_org_output_not_escaped' => array( + '{"@context":"http://schema.org"', + 1, + ), + 'schema_org_another_key' => array( + wp_json_encode( array( '@anothercontext' => 'https://schema.org' ) ), + 1, + ), + ); + } }