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.
254 lines
5.9 KiB
254 lines
5.9 KiB
<?php |
|
/** |
|
* Class AMP_Instagram_Embed_Handler |
|
* |
|
* @package AMP |
|
*/ |
|
|
|
/** |
|
* Class AMP_Instagram_Embed_Handler |
|
* |
|
* Much of this class is borrowed from Jetpack embeds |
|
*/ |
|
class AMP_Instagram_Embed_Handler extends AMP_Base_Embed_Handler { |
|
const SHORT_URL_HOST = 'instagr.am'; |
|
const URL_PATTERN = '#http(s?)://(www\.)?instagr(\.am|am\.com)/p/([^/?]+)#i'; |
|
|
|
/** |
|
* Default width. |
|
* |
|
* @var int |
|
*/ |
|
protected $DEFAULT_WIDTH = 600; |
|
|
|
/** |
|
* Default height. |
|
* |
|
* @var int |
|
*/ |
|
protected $DEFAULT_HEIGHT = 600; |
|
|
|
/** |
|
* Tag. |
|
* |
|
* @var string embed HTML blockquote tag to identify and replace with AMP version. |
|
*/ |
|
protected $sanitize_tag = 'blockquote'; |
|
|
|
/** |
|
* Tag. |
|
* |
|
* @var string AMP amp-facebook tag |
|
*/ |
|
private $amp_tag = 'amp-instagram'; |
|
|
|
/** |
|
* Registers embed. |
|
*/ |
|
public function register_embed() { |
|
wp_embed_register_handler( $this->amp_tag, self::URL_PATTERN, [ $this, 'oembed' ], -1 ); |
|
add_shortcode( 'instagram', [ $this, 'shortcode' ] ); |
|
} |
|
|
|
/** |
|
* Unregisters embed. |
|
*/ |
|
public function unregister_embed() { |
|
wp_embed_unregister_handler( $this->amp_tag, -1 ); |
|
remove_shortcode( 'instagram' ); |
|
} |
|
|
|
/** |
|
* WordPress shortcode rendering callback. |
|
* |
|
* @param array $attr Shortcode attributes. |
|
* @return string HTML markup for rendered embed. |
|
*/ |
|
public function shortcode( $attr ) { |
|
$url = false; |
|
|
|
if ( isset( $attr['url'] ) ) { |
|
$url = trim( $attr['url'] ); |
|
} |
|
|
|
if ( empty( $url ) ) { |
|
return ''; |
|
} |
|
|
|
$instagram_id = $this->get_instagram_id_from_url( $url ); |
|
|
|
return $this->render( |
|
[ |
|
'url' => $url, |
|
'instagram_id' => $instagram_id, |
|
] |
|
); |
|
} |
|
|
|
/** |
|
* WordPress OEmbed rendering callback. |
|
* |
|
* @param array $matches URL pattern matches. |
|
* @param array $attr Matched attributes. |
|
* @param string $url Matched URL. |
|
* @return string HTML markup for rendered embed. |
|
*/ |
|
public function oembed( $matches, $attr, $url ) { |
|
return $this->render( |
|
[ |
|
'url' => $url, |
|
'instagram_id' => end( $matches ), |
|
] |
|
); |
|
} |
|
|
|
/** |
|
* Gets the rendered embed markup. |
|
* |
|
* @param array $args Embed rendering arguments. |
|
* @return string HTML markup for rendered embed. |
|
*/ |
|
public function render( $args ) { |
|
$args = wp_parse_args( |
|
$args, |
|
[ |
|
'url' => false, |
|
'instagram_id' => false, |
|
] |
|
); |
|
|
|
if ( empty( $args['instagram_id'] ) ) { |
|
return AMP_HTML_Utils::build_tag( |
|
'a', |
|
[ |
|
'href' => esc_url( $args['url'] ), |
|
'class' => 'amp-wp-embed-fallback', |
|
], |
|
esc_html( $args['url'] ) |
|
); |
|
} |
|
|
|
$this->did_convert_elements = true; |
|
|
|
return AMP_HTML_Utils::build_tag( |
|
$this->amp_tag, |
|
[ |
|
'data-shortcode' => $args['instagram_id'], |
|
'data-captioned' => '', |
|
'layout' => 'responsive', |
|
'width' => $this->args['width'], |
|
'height' => $this->args['height'], |
|
] |
|
); |
|
} |
|
|
|
/** |
|
* Get Instagram ID from URL. |
|
* |
|
* @param string $url URL. |
|
* @return string|false The ID parsed from the URL or false if not found. |
|
*/ |
|
private function get_instagram_id_from_url( $url ) { |
|
$found = preg_match( self::URL_PATTERN, $url, $matches ); |
|
|
|
if ( ! $found ) { |
|
return false; |
|
} |
|
|
|
return end( $matches ); |
|
} |
|
|
|
/** |
|
* Sanitized <blockquote class="instagram-media"> tags to <amp-instagram> |
|
* |
|
* @param DOMDocument $dom DOM. |
|
*/ |
|
public function sanitize_raw_embeds( $dom ) { |
|
/** |
|
* Node list. |
|
* |
|
* @var DOMNodeList $node |
|
*/ |
|
$nodes = $dom->getElementsByTagName( $this->sanitize_tag ); |
|
$num_nodes = $nodes->length; |
|
|
|
if ( 0 === $num_nodes ) { |
|
return; |
|
} |
|
|
|
for ( $i = $num_nodes - 1; $i >= 0; $i-- ) { |
|
$node = $nodes->item( $i ); |
|
if ( ! $node instanceof DOMElement ) { |
|
continue; |
|
} |
|
|
|
if ( $node->hasAttribute( 'data-instgrm-permalink' ) ) { |
|
$this->create_amp_instagram_and_replace_node( $dom, $node ); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Make final modifications to DOMNode |
|
* |
|
* @param DOMDocument $dom The HTML Document. |
|
* @param DOMElement $node The DOMNode to adjust and replace. |
|
*/ |
|
private function create_amp_instagram_and_replace_node( $dom, $node ) { |
|
$instagram_id = $this->get_instagram_id_from_url( $node->getAttribute( 'data-instgrm-permalink' ) ); |
|
|
|
$node_args = [ |
|
'data-shortcode' => $instagram_id, |
|
'layout' => 'responsive', |
|
'width' => $this->DEFAULT_WIDTH, |
|
'height' => $this->DEFAULT_HEIGHT, |
|
]; |
|
|
|
if ( true === $node->hasAttribute( 'data-instgrm-captioned' ) ) { |
|
$node_args['data-captioned'] = ''; |
|
} |
|
|
|
$new_node = AMP_DOM_Utils::create_node( $dom, $this->amp_tag, $node_args ); |
|
|
|
$this->sanitize_embed_script( $node ); |
|
|
|
$node->parentNode->replaceChild( $new_node, $node ); |
|
|
|
$this->did_convert_elements = true; |
|
} |
|
|
|
/** |
|
* Removes Instagram's embed <script> tag. |
|
* |
|
* @param DOMElement $node The DOMNode to whose sibling is the instagram script. |
|
*/ |
|
private function sanitize_embed_script( $node ) { |
|
$next_element_sibling = $node->nextSibling; |
|
while ( $next_element_sibling && ! ( $next_element_sibling instanceof DOMElement ) ) { |
|
$next_element_sibling = $next_element_sibling->nextSibling; |
|
} |
|
|
|
$script_src = 'instagram.com/embed.js'; |
|
|
|
// Handle case where script is wrapped in paragraph by wpautop. |
|
if ( $next_element_sibling instanceof DOMElement && 'p' === $next_element_sibling->nodeName ) { |
|
$children = $next_element_sibling->getElementsByTagName( '*' ); |
|
if ( 1 === $children->length && 'script' === $children->item( 0 )->nodeName && false !== strpos( $children->item( 0 )->getAttribute( 'src' ), $script_src ) ) { |
|
$next_element_sibling->parentNode->removeChild( $next_element_sibling ); |
|
return; |
|
} |
|
} |
|
|
|
// Handle case where script is immediately following. |
|
$is_embed_script = ( |
|
$next_element_sibling |
|
&& |
|
'script' === strtolower( $next_element_sibling->nodeName ) |
|
&& |
|
false !== strpos( $next_element_sibling->getAttribute( 'src' ), $script_src ) |
|
); |
|
if ( $is_embed_script ) { |
|
$next_element_sibling->parentNode->removeChild( $next_element_sibling ); |
|
} |
|
} |
|
}
|
|
|