397 lines
11 KiB
PHP
397 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* @package YoastSEO_AMP_Glue\Frontend
|
|
* @author Joost de Valk
|
|
* @copyright 2016 Yoast BV
|
|
* @license GPL-2.0+
|
|
*/
|
|
|
|
if ( ! class_exists( 'YoastSEO_AMP_Frontend' ) ) {
|
|
/**
|
|
* This class improves upon the AMP output by the default WordPress AMP plugin using Yoast SEO metadata.
|
|
*/
|
|
class YoastSEO_AMP_Frontend {
|
|
|
|
/**
|
|
* @var WPSEO_Frontend
|
|
*/
|
|
private $front;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $options;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $wpseo_options;
|
|
|
|
/**
|
|
* YoastSEO_AMP_Frontend constructor.
|
|
*/
|
|
public function __construct() {
|
|
$this->set_options();
|
|
|
|
add_action( 'amp_init', array( $this, 'post_types' ) );
|
|
|
|
add_action( 'amp_post_template_css', array( $this, 'additional_css' ) );
|
|
add_action( 'amp_post_template_head', array( $this, 'extra_head' ) );
|
|
add_action( 'amp_post_template_footer', array( $this, 'extra_footer' ) );
|
|
|
|
add_filter( 'amp_post_template_data', array( $this, 'fix_amp_post_data' ) );
|
|
add_filter( 'amp_post_template_metadata', array( $this, 'fix_amp_post_metadata' ), 10, 2 );
|
|
add_filter( 'amp_post_template_analytics', array( $this, 'analytics' ) );
|
|
|
|
add_filter( 'amp_content_sanitizers', array( $this, 'add_sanitizer' ) );
|
|
}
|
|
|
|
private function set_options() {
|
|
$this->wpseo_options = WPSEO_Options::get_all();
|
|
$this->options = YoastSEO_AMP_Options::get();
|
|
}
|
|
|
|
/**
|
|
* Add our own sanitizer to the array of sanitizers
|
|
*
|
|
* @param array $sanitizers
|
|
*
|
|
* @return array
|
|
*/
|
|
public function add_sanitizer( $sanitizers ) {
|
|
require_once 'class-sanitizer.php';
|
|
|
|
$sanitizers['Yoast_AMP_Blacklist_Sanitizer'] = array();
|
|
|
|
return $sanitizers;
|
|
}
|
|
|
|
/**
|
|
* If analytics tracking has been set, output it now.
|
|
*
|
|
* @param array $analytics
|
|
*
|
|
* @return array
|
|
*/
|
|
public function analytics( $analytics ) {
|
|
// If Monster Insights is outputting analytics, don't do anything.
|
|
if ( ! empty( $analytics['monsterinsights-googleanalytics'] ) ) {
|
|
// Clear analytics-extra options because Monster Insights is taking care of everything.
|
|
$this->options['analytics-extra'] = '';
|
|
|
|
return $analytics;
|
|
}
|
|
|
|
if ( ! empty( $this->options['analytics-extra'] ) ) {
|
|
return $analytics;
|
|
}
|
|
|
|
if ( ! class_exists( 'Yoast_GA_Options' ) || Yoast_GA_Options::instance()->get_tracking_code() === null ) {
|
|
return $analytics;
|
|
}
|
|
$UA = Yoast_GA_Options::instance()->get_tracking_code();
|
|
|
|
$analytics['yst-googleanalytics'] = array(
|
|
'type' => 'googleanalytics',
|
|
'attributes' => array(),
|
|
'config_data' => array(
|
|
'vars' => array(
|
|
'account' => $UA
|
|
),
|
|
'triggers' => array(
|
|
'trackPageview' => array(
|
|
'on' => 'visible',
|
|
'request' => 'pageview',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
return $analytics;
|
|
}
|
|
|
|
/**
|
|
* Make AMP work for all the post types we want it for
|
|
*/
|
|
public function post_types() {
|
|
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
|
if ( is_array( $post_types ) && $post_types !== array() ) {
|
|
foreach ( $post_types as $post_type ) {
|
|
|
|
$post_type_name = $post_type->name;
|
|
|
|
if ( ! isset( $this->options[ 'post_types-' . $post_type_name . '-amp' ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
// If AMP page support is not present, don't allow enabling it here.
|
|
if ( 'page' === $post_type_name && ! post_type_supports( 'page', AMP_QUERY_VAR ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( $this->options[ 'post_types-' . $post_type_name . '-amp' ] === 'on' ) {
|
|
add_post_type_support( $post_type_name, AMP_QUERY_VAR );
|
|
continue;
|
|
}
|
|
|
|
if ( 'post' === $post_type_name ) {
|
|
add_action( 'wp', array( $this, 'disable_amp_for_posts' ) );
|
|
continue;
|
|
}
|
|
|
|
remove_post_type_support( $post_type_name, AMP_QUERY_VAR );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disables AMP for posts specifically, run later because of AMP plugin internals
|
|
*/
|
|
public function disable_amp_for_posts() {
|
|
remove_post_type_support( 'post', AMP_QUERY_VAR );
|
|
}
|
|
|
|
/**
|
|
* Fix the basic AMP post data
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function fix_amp_post_data( $data ) {
|
|
if ( ! $this->front ) {
|
|
$this->front = WPSEO_Frontend::get_instance();
|
|
}
|
|
$data['canonical_url'] = $this->front->canonical( false );
|
|
|
|
if ( ! empty( $this->options['amp_site_icon'] ) ) {
|
|
$data['site_icon_url'] = $this->options['amp_site_icon'];
|
|
}
|
|
|
|
// If we are loading extra analytics, we need to load the module too.
|
|
if ( ! empty( $this->options['analytics-extra'] ) ) {
|
|
$data['amp_component_scripts']['amp-analytics'] = 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js';
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Fix the AMP metadata for a post
|
|
*
|
|
* @param array $metadata
|
|
* @param WP_Post $post
|
|
*
|
|
* @return array
|
|
*/
|
|
public function fix_amp_post_metadata( $metadata, $post ) {
|
|
if ( ! $this->front ) {
|
|
$this->front = WPSEO_Frontend::get_instance();
|
|
}
|
|
|
|
$this->build_organization_object( $metadata );
|
|
|
|
$desc = $this->front->metadesc( false );
|
|
if ( $desc ) {
|
|
$metadata['description'] = $desc;
|
|
}
|
|
|
|
$image = isset( $metadata['image'] ) ? $metadata['image'] : null;
|
|
|
|
$metadata['image'] = $this->get_image( $post, $image );
|
|
$metadata['@type'] = $this->get_post_schema_type( $post );
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* Add additional CSS to the AMP output
|
|
*/
|
|
public function additional_css() {
|
|
require 'views/additional-css.php';
|
|
|
|
$selectors = $this->get_class_selectors();
|
|
|
|
$css_builder = new YoastSEO_AMP_CSS_Builder();
|
|
$css_builder->add_option( 'header-color', $selectors[ 'header-color' ], 'background' );
|
|
$css_builder->add_option( 'headings-color', $selectors[ 'headings-color' ], 'color' );
|
|
$css_builder->add_option( 'text-color', $selectors[ 'text-color' ], 'color' );
|
|
|
|
$css_builder->add_option( 'blockquote-bg-color', $selectors[ 'blockquote-bg-color' ], 'background-color' );
|
|
$css_builder->add_option( 'blockquote-border-color', $selectors[ 'blockquote-border-color' ], 'border-color' );
|
|
$css_builder->add_option( 'blockquote-text-color', $selectors[ 'blockquote-text-color' ], 'color' );
|
|
|
|
$css_builder->add_option( 'link-color', $selectors[ 'link-color' ], 'color' );
|
|
$css_builder->add_option( 'link-color-hover', $selectors[ 'link-color-hover' ], 'color' );
|
|
|
|
$css_builder->add_option( 'meta-color', $selectors[ 'meta-color' ], 'color' );
|
|
|
|
echo $css_builder->build();
|
|
|
|
if ( ! empty( $this->options['extra-css'] ) ) {
|
|
$safe_text = strip_tags( $this->options['extra-css'] );
|
|
$safe_text = wp_check_invalid_utf8( $safe_text );
|
|
$safe_text = _wp_specialchars( $safe_text, ENT_NOQUOTES );
|
|
echo $safe_text;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Outputs extra code in the head, if set
|
|
*/
|
|
public function extra_head() {
|
|
$options = WPSEO_Options::get_option( 'wpseo_social' );
|
|
|
|
if ( $options['twitter'] === true ) {
|
|
WPSEO_Twitter::get_instance();
|
|
}
|
|
|
|
if ( $options['opengraph'] === true ) {
|
|
$GLOBALS['wpseo_og'] = new WPSEO_OpenGraph;
|
|
}
|
|
|
|
do_action( 'wpseo_opengraph' );
|
|
|
|
echo strip_tags( $this->options['extra-head'], '<link><meta>' );
|
|
}
|
|
|
|
/**
|
|
* Outputs analytics code in the footer, if set
|
|
*/
|
|
public function extra_footer() {
|
|
echo $this->options['analytics-extra'];
|
|
}
|
|
|
|
/**
|
|
* Builds the organization object if needed.
|
|
*
|
|
* @param array $metadata
|
|
*/
|
|
private function build_organization_object( &$metadata ) {
|
|
// While it's using the blog name, it's actually outputting the company name.
|
|
if ( ! empty( $this->wpseo_options['company_name'] ) ) {
|
|
$metadata['publisher']['name'] = $this->wpseo_options['company_name'];
|
|
}
|
|
|
|
// The logo needs to be 600px wide max, 60px high max.
|
|
$logo = $this->get_image_object( $this->wpseo_options['company_logo'], array( 600, 60 ) );
|
|
if ( is_array( $logo ) ) {
|
|
$metadata['publisher']['logo'] = $logo;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds an image object array from an image URL
|
|
*
|
|
* @param string $image_url Image URL to build URL for.
|
|
* @param string|array $size Optional. Image size. Accepts any valid image size, or an array of width
|
|
* and height values in pixels (in that order). Default 'full'.
|
|
*
|
|
* @return array|false
|
|
*/
|
|
private function get_image_object( $image_url, $size = 'full' ) {
|
|
if ( empty( $image_url ) ) {
|
|
return false;
|
|
}
|
|
|
|
$image_id = attachment_url_to_postid( $image_url );
|
|
$image_src = wp_get_attachment_image_src( $image_id, $size );
|
|
|
|
if ( is_array( $image_src ) ) {
|
|
return array(
|
|
'@type' => 'ImageObject',
|
|
'url' => $image_src[0],
|
|
'width' => $image_src[1],
|
|
'height' => $image_src[2]
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the Schema.org image for the post
|
|
*
|
|
* @param WP_Post $post Post to retrieve the data for.
|
|
* @param array|null $image The currently set post image.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_image( $post, $image ) {
|
|
$og_image = $this->get_image_object( WPSEO_Meta::get_value( 'opengraph-image', $post->ID ) );
|
|
if ( is_array( $og_image ) ) {
|
|
return $og_image;
|
|
}
|
|
|
|
// Posts without an image fail validation in Google, leading to Search Console errors
|
|
if ( ! is_array( $image ) && isset( $this->options['default_image'] ) ) {
|
|
return $this->get_image_object( $this->options['default_image'] );
|
|
}
|
|
|
|
return $image;
|
|
}
|
|
|
|
/**
|
|
* Gets the Schema.org type for the post, based on the post type.
|
|
*
|
|
* @param WP_Post $post
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_post_schema_type( $post ) {
|
|
$type = 'WebPage';
|
|
if ( 'post' === $post->post_type ) {
|
|
$type = 'Article';
|
|
}
|
|
|
|
/**
|
|
* Filter: 'yoastseo_amp_schema_type' - Allow changing the Schema.org type for the post
|
|
*
|
|
* @api string $type The Schema.org type for the $post
|
|
*
|
|
* @param WP_Post $post
|
|
*/
|
|
$type = apply_filters( 'yoastseo_amp_schema_type', $type, $post );
|
|
|
|
return $type;
|
|
}
|
|
|
|
/**
|
|
* Gets version dependent class names
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_class_selectors() {
|
|
$selectors = array(
|
|
'header-color' => 'nav.amp-wp-title-bar',
|
|
'headings-color' => '.amp-wp-title, h2, h3, h4',
|
|
'text-color' => '.amp-wp-content',
|
|
|
|
'blockquote-bg-color' => '.amp-wp-content blockquote',
|
|
'blockquote-border-color' => '.amp-wp-content blockquote',
|
|
'blockquote-text-color' => '.amp-wp-content blockquote',
|
|
|
|
'link-color' => 'a, a:active, a:visited',
|
|
'link-color-hover' => 'a:hover, a:focus',
|
|
|
|
'meta-color' => '.amp-wp-meta li, .amp-wp-meta li a',
|
|
);
|
|
|
|
// CSS classnames have been changed in version 0.4.0.
|
|
if ( version_compare( AMP__VERSION, '0.4.0', '>=' ) ) {
|
|
$selectors_v4 = array(
|
|
'header-color' => 'header.amp-wp-header, html',
|
|
'text-color' => 'div.amp-wp-article',
|
|
'blockquote-bg-color' => '.amp-wp-article-content blockquote',
|
|
'blockquote-border-color' => '.amp-wp-article-content blockquote',
|
|
'blockquote-text-color' => '.amp-wp-article-content blockquote',
|
|
'meta-color' => '.amp-wp-meta, .amp-wp-meta a',
|
|
);
|
|
$selectors = array_merge( $selectors, $selectors_v4 );
|
|
}
|
|
|
|
return $selectors;
|
|
}
|
|
}
|
|
}
|