You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
5.9 KiB

<?php
/**
* Class AMP_Story_Export_Sanitizer.
*
* @package AMP
*/
/**
* Class AMP_Story_Export_Sanitizer
*
* Sanitizes AMP Stories during export.
*
* @since 1.2.1
*/
class AMP_Story_Export_Sanitizer extends AMP_Base_Sanitizer {
/**
* Default args.
*
* @var array
*/
protected $DEFAULT_ARGS = [
'base_url' => '',
'canonical_url' => '',
];
/**
* Default assets.
*
* @var string[]
*/
protected $assets = [];
/**
* Sanitize the AMP Story.
*/
public function sanitize() {
// AMP Story Node.
$this->sanitize_assets(
'amp-story',
[
'publisher-logo-src',
'publisher',
'poster-portrait-src',
'poster-square-src',
'poster-landscape-src',
]
);
// AMP Image Nodes.
$this->sanitize_assets(
'amp-img',
[
'src',
'srcset',
]
);
// AMP Video Nodes.
$this->sanitize_assets(
'amp-video',
[
'src',
'poster',
]
);
$xpath = new DOMXPath( $this->dom );
$schema_org_meta = $xpath->query( '//head/script[@type="application/ld+json"]' )->item( 0 );
if ( $schema_org_meta && $schema_org_meta->firstChild instanceof DOMText ) {
$metadata = json_decode( $schema_org_meta->firstChild->nodeValue );
// Adds the image to the assets array.
if ( isset( $metadata->image->url ) ) {
$image_url = $metadata->image->url;
if ( $image_url && ! in_array( $image_url, $this->assets, true ) ) {
$this->assets[] = $image_url;
}
}
// Adds the logo image to the assets array.
if ( isset( $metadata->publisher->logo->url ) ) {
$logo_url = $metadata->publisher->logo->url;
if ( $logo_url && ! in_array( $logo_url, $this->assets, true ) ) {
$this->assets[] = $logo_url;
}
}
if ( $this->args['base_url'] && $this->args['canonical_url'] ) {
// Replace the image URL.
if ( isset( $image_url ) ) {
$args = [
$this->args['canonical_url'],
'assets',
AMP_Story_Post_Type::export_image_basename( $image_url ),
];
$metadata->image->url = implode( '/', $args );
}
// Replace the logo URL.
if ( isset( $logo_url ) ) {
$args = [
$this->args['canonical_url'],
'assets',
AMP_Story_Post_Type::export_image_basename( $logo_url ),
];
$metadata->publisher->logo->url = implode( '/', $args );
}
// Replace the Canonical URL.
$metadata->mainEntityOfPage = $this->args['canonical_url']; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
$schema_org_meta->firstChild->nodeValue = wp_json_encode( $metadata, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
}
// Add or update Canonical URL in the document head.
if ( $this->args['base_url'] && $this->args['canonical_url'] ) {
$canonical_link = $xpath->query( '/html/head/link[ @rel = "canonical" ]' )->item( 0 );
if ( $canonical_link instanceof DOMElement ) {
$canonical_link->setAttribute( 'href', $this->args['canonical_url'] );
} else {
$canonical_link = AMP_DOM_Utils::create_node(
$this->dom,
'link',
[
'rel' => 'canonical',
'href' => $this->args['canonical_url'],
]
);
$this->dom->getElementsByTagName( 'head' )->item( 0 )->appendChild( $canonical_link );
}
}
// Add the export assets as an HTML comment.
$encoded = wp_json_encode( $this->assets, JSON_PRETTY_PRINT );
$encoded = str_replace( '--', '\u002d\u002d', $encoded ); // Prevent "--" in strings from breaking out of HTML comments.
$comment = $this->dom->createComment( 'AMP_EXPORT_ASSETS:' . $encoded . "\n" );
$this->dom->documentElement->appendChild( $comment );
}
/**
* Add and sanitize the export assets for the AMP Story.
*
* @param string $name The DOMElement name.
* @param string[] $attributes The DOMElement attributes.
*/
public function sanitize_assets( $name, array $attributes ) {
$nodes = $this->dom->getElementsByTagName( $name );
$num_nodes = $nodes->length;
// Verify we have a value to update the paths with.
$update_path = ! empty( $this->args['canonical_url'] ) ? $this->args['canonical_url'] : false;
/**
* Generates the new asset path.
*
* @param string $asset The original URL.
*
* @return bool|string Returns false when $update_path is false, else the new URL.
*/
$get_asset_path = function( $asset ) use ( $update_path ) {
if ( $asset && $update_path ) {
$args = [
$update_path,
'assets',
AMP_Story_Post_Type::export_image_basename( $asset ),
];
return implode( '/', $args );
}
return false;
};
if ( 0 < $num_nodes ) {
foreach ( $nodes as $node ) {
foreach ( $attributes as $attribute ) {
if ( 'amp-story' === $name && 'publisher' === $attribute ) {
if ( $update_path ) {
$parse = wp_parse_url( $update_path );
if ( ! empty( $parse['host'] ) ) {
$node->setAttribute( $attribute, $parse['host'] );
}
}
} else {
$asset = $node->getAttribute( $attribute );
// Replace the asset.
if ( $asset ) {
if ( 'srcset' === $attribute ) {
$images = array_filter(
array_map(
static function ( $srcset_part ) {
// Remove descriptors for width and pixel density.
return preg_replace( '/\s.*$/', '', trim( $srcset_part ) );
},
preg_split( '/\s*,\s*/', $asset )
)
);
if ( ! empty( $images ) ) {
foreach ( $images as $image ) {
if ( $update_path ) {
$asset = str_replace( $image, $get_asset_path( $image ), $asset );
}
// Add to assets array.
$this->assets[] = $image;
}
// Reset the attribute after replacing all the images.
$node->setAttribute( $attribute, $asset );
}
} else {
if ( $update_path ) {
$node->setAttribute( $attribute, $get_asset_path( $asset ) );
}
// Add to assets array.
$this->assets[] = $asset;
}
}
}
}
}
}
}
}