Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capability to generate AMP-only content (AMP-as-Canonical) #668

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d409c07
Add capability to generate AMP-only content (AMP-as-Canonical)
amedina Mar 23, 2017
b306aa1
Add capability to generate AMP-only content (AMP-as-Canonical)
amedina Mar 23, 2017
cba87d8
Fix merge conflicts
amedina Mar 23, 2017
06513d4
First steps in postprocessing the AMP canonical html: add amp attr to…
amedina Mar 24, 2017
d355e39
Experimenting with getting the AMP Canonical mode working with the 20…
amedina Mar 25, 2017
26b7526
Refactor code to add modulatiry to posprocessing functinoality.
amedina Mar 26, 2017
5c57b70
Merge branch 'master' into amedina/amp-canonical
amedina Apr 19, 2017
74c58c0
Fix canonical link issue for index page
amedina Apr 25, 2017
9558d54
Merge branch 'master' into amedina/amp-canonical
amedina Apr 25, 2017
94deda7
Fix index page
amedina Apr 26, 2017
c03dc7c
Expand canonical generation to index page
amedina Apr 28, 2017
e386b6a
Reorg of code
amedina Apr 29, 2017
190edf3
Resolve conflict with master
amedina Apr 29, 2017
6d85380
Resolve conflict with master
amedina Apr 29, 2017
e07964f
Merge branch 'amedina/amp-canonical' of github.com:Automattic/amp-wp …
amedina Apr 29, 2017
94a5782
Add processing of canonical index page
amedina May 2, 2017
34db70f
Progress on AMPing of index page
amedina May 2, 2017
b0d7ac9
Do not remove sidebars from index page
amedina May 2, 2017
ee552be
Fixed include of AMPContent class
amedina May 2, 2017
30121f6
Rename postprocessing file/class; Define img-to-ampimg coverter
amedina May 5, 2017
f6799f0
Add carousel sanitzer; add amp link to REST API output
amedina May 16, 2017
5d70e94
Fix flush rewrite rules after changing settings
amedina May 26, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 85 additions & 15 deletions amp.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
require_once( AMP__DIR__ . '/includes/admin/functions.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/utils/class-amp-dom-utils.php');
require_once( AMP__DIR__ . '/option.php' );
require_once(AMP__DIR__ . '/post-processing/class-amp-sanitize-tweentyseventeen-theme-plain.php');
Copy link
Contributor

@paulschreiber paulschreiber Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add spaces inside parentheses.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely; will do.


register_activation_hook( __FILE__, 'amp_activate' );
function amp_activate() {
Expand Down Expand Up @@ -48,15 +51,20 @@ function amp_init() {
if ( false === apply_filters( 'amp_is_enabled', true ) ) {
return;
}
error_log("AMP INIT");

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 );
// If the amp_canonical option has not been setup, or the current
// theme does not provide AMP support, then follow the "paired" approach
if ( ! get_option('amp_canonical') || ! get_theme_support('amp')) {
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' );
Expand All @@ -67,6 +75,7 @@ function amp_init() {
if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
require_once( AMP__DIR__ . '/jetpack-helper.php' );
}

}

// Make sure the `amp` query var has an explicit value.
Expand All @@ -79,28 +88,30 @@ function amp_force_query_var_value( $query_vars ) {
}

function amp_maybe_add_actions() {
if ( ! is_singular() || is_feed() ) {
if ( is_feed() || (!get_option('amp_canonical') && !is_singular())) {
return;
}

$is_amp_endpoint = is_amp_endpoint();

// Cannot use `get_queried_object` before canonical redirect; see https://core.trac.wordpress.org/ticket/35344
global $wp_query;
$post = $wp_query->post;

$supports = post_supports_amp( $post );

if ( ! $supports ) {
if ( $is_amp_endpoint ) {
wp_safe_redirect( get_permalink( $post->ID ) );
exit;
$supports = true;
if (is_singular()) {
// Cannot use `get_queried_object` before canonical redirect; see https://core.trac.wordpress.org/ticket/35344
global $wp_query;
$post = $wp_query->post;
$supports = post_supports_amp($post);
if ( ! $supports ) {
if ( $is_amp_endpoint ) {
wp_safe_redirect( get_permalink( $post->ID ) );
exit;
}
return;
}
return;
}

if ( $is_amp_endpoint ) {
amp_prepare_render();
} else if( get_option('amp_canonical') && $supports && get_theme_support('amp') && is_singular() ) {
amp_add_canonical_actions();
} else {
amp_add_frontend_actions();
}
Expand Down Expand Up @@ -144,6 +155,65 @@ function amp_render_post( $post_id ) {
$template->load();
}

function amp_add_canonical_actions() {
// Load AMP canonical actions
require_once( AMP__DIR__ . '/includes/amp-canonical-actions.php');
// Load high-priority filters for canonical AMP
require_once( AMP__DIR__ . '/includes/amp-canonical-filters.php');
// Template redirect to postprocessing actions
add_action( 'template_redirect', 'amp_maybe_init_postprocess_html' );
}

function amp_maybe_init_postprocess_html() {
ob_start( 'amp_canonical_postprocess_html' );
}

/**
* Convert generated $html (plain 2017 Theme) to valid-AMP format
*/
function amp_canonical_postprocess_html( $html ) {

$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($html);
libxml_use_internal_errors(false);

// TODO (@amedina): Define how specific theme sanitations
// will be executed; user can select a specific one or a
// generic one could be executed

// Add amp attribute to html tag
AMP_Sanitize_TweentySeventeen_Theme::add_amp_attr($dom);
// Eliminate 3p JS
AMP_Sanitize_TweentySeventeen_Theme::eliminate_3p_js($dom);
// Eliminate external stylesheets
AMP_Sanitize_TweentySeventeen_Theme::eliminate_ext_css($dom);
// Eliminate sidebars
//AMP_Sanitize_TweentySeventeen_Theme::eliminate_sidebars($dom);
// Eliminate entry footers
AMP_Sanitize_TweentySeventeen_Theme::eliminate_entry_footers($dom);
// Eliminate overall footer
AMP_Sanitize_TweentySeventeen_Theme::eliminate_overall_footer($dom);
// Eliminate post navigation
//AMP_Sanitize_TweentySeventeen_Theme::eliminate_post_navigation($dom);
// Eliminate comments section
AMP_Sanitize_TweentySeventeen_Theme::eliminate_comments_section($dom);
// Set meta viewport
AMP_Sanitize_TweentySeventeen_Theme::set_meta_viewport($dom);
// Eliminate non-amp-custom Stylesheets
AMP_Sanitize_TweentySeventeen_Theme::eliminate_non_amp_custom_styles($dom);
// Inline theme CSS
AMP_Sanitize_TweentySeventeen_Theme::inline_theme_css($dom);
// AMP Custom-header img
AMP_Sanitize_TweentySeventeen_Theme::amp_custom_header_img($dom);
// Remove styled SVGs
AMP_Sanitize_TweentySeventeen_Theme::remove_styled_svgs($dom);
// Save new HTML contents
$amp_html = $dom->saveHTML();

return $amp_html;
}

/**
* Bootstraps the AMP customizer.
*
Expand Down
126 changes: 126 additions & 0 deletions includes/amp-canonical-actions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php
require_once( AMP__DIR__ . '/includes/utils/class-amp-dom-utils.php');
require_once( AMP__DIR__ . '/includes/utils/class-amp-html-utils.php');
require_once( AMP__DIR__ . '/includes/utils/class-amp-string-utils.php');

require_once( AMP__DIR__ . '/includes/class-amp-content.php');

require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-blacklist-sanitizer.php');
require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-img-sanitizer.php');
require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-video-sanitizer.php');
require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-iframe-sanitizer.php');
require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-audio-sanitizer.php');
require_once( AMP__DIR__ . '/includes/sanitizers/class-amp-style-sanitizer.php');

require_once( AMP__DIR__ . '/includes/embeds/class-amp-twitter-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-youtube-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-gallery-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-instagram-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-vine-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-facebook-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-dailymotion-embed.php');
require_once( AMP__DIR__ . '/includes/embeds/class-amp-soundcloud-embed.php');

function amp_canonical_retrieve_content() {

$post = get_post();

$content_max_width = 1200;

if ( isset( $GLOBALS['content_width']) && $GLOBALS['content_width'] > 0 ) {
$content_max_width = $GLOBALS['content_width'];
}

$amp_content = new AMP_Content(
$post->post_content,
array(
'AMP_Twitter_Embed_Handler' => array(),
'AMP_YouTube_Embed_Handler' => array(),
'AMP_DailyMotion_Embed_Handler' => array(),
'AMP_SoundCloud_Embed_Handler' => array(),
'AMP_Instagram_Embed_Handler' => array(),
'AMP_Vine_Embed_Handler' => array(),
'AMP_Facebook_Embed_Handler' => array(),
'AMP_Gallery_Embed_Handler' => array(),
),
array(
'AMP_Style_Sanitizer' => array(),
'AMP_Blacklist_Sanitizer' => array(),
'AMP_Img_Sanitizer' => array(),
'AMP_Video_Sanitizer' => array(),
'AMP_Audio_Sanitizer' => array(),
'AMP_Iframe_Sanitizer' => array(
'add_placeholder' => true,
),
),
array(
'content_max_width' => $content_max_width,
)
);

return $amp_content;

}

// Generate the AMP post content early on
// Runs the filters for the_content, but skips our filters below
$GLOBALS['amp_content'] = amp_canonical_retrieve_content();

// The filter for the_content hook was already invoked,
// attempt to remove all filters
remove_all_filters('the_content');

add_action( 'wp_head', 'amp_canonical_add_scripts' );
add_action( 'wp_head', 'amp_canonical_add_boilerplate_css' );
add_action( 'wp_footer', 'amp_deregister_scripts' );

// Add the filter that replaces the content, and ensure
// that no content filters run afterwards
add_filter( 'the_content', 'amp_the_content_filter', PHP_INT_MAX);
function amp_the_content_filter($content) {
if (isset($GLOBALS['amp_content'])) {
return $GLOBALS['amp_content']->get_amp_content();
} else {
return $content;
}
}

function amp_deregister_scripts() {
wp_deregister_script( 'wp_embed');
}

function amp_canonical_add_boilerplate_css() {
?>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<?php
}

function amp_canonical_add_scripts() {

$amp_runtime_script = 'https://cdn.ampproject.org/v0.js';

// Always include AMP form & analytics, as almost every template includes
// a search form and is tracking page views
$scripts = array_merge(
array('amp-form' => 'https://cdn.ampproject.org/v0/amp-form-0.1.js'),
array('amp-analytics' => 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js'),
$GLOBALS['amp_content']->get_amp_scripts()
);

foreach ( $scripts as $element => $script) : ?>
<script custom-element="<?php echo esc_attr( $element ); ?>" src="<?php echo esc_url( $script ); ?>" async></script>
<?php endforeach; ?>
<script src="<?php echo esc_url( $amp_runtime_script ); ?>" async></script>
<?php
}

// TODO (@amedina) Get the canonical URL [Check!]
if (!is_singular()) {
add_action( 'wp_head', 'amp_post_template_add_canonical');
}
function amp_post_template_add_canonical() {
error_log("CANONICAL: Adding canonical link")
?>
<link rel="canonical" href="<?php echo esc_url( get_site_url() ); ?>" />
<?php
}
15 changes: 15 additions & 0 deletions includes/amp-canonical-filters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

// before Jetpack ever gets loaded, we need to remove a
// link rel prefetch for canonical AMP support
// TODO[@amedina] These will be allowed by the validator in the future
// TODO[@amedina] remove as soon as it works

function amp_canonical_disable_jetpack_dns_fetch() {

if ( class_exists( 'Jetpack') ) {
remove_action( 'wp_head', array( 'Jetpack', 'dns_prefetch' ) );
}
}
// Hook action early on so we can unhook Jetpack
add_action( 'wp_head', 'amp_canonical_disable_jetpack_dns_fetch', 0);
2 changes: 1 addition & 1 deletion includes/amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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 ) ) {
if ( ! get_option( 'amp_canonical' ) && ! post_type_supports( $post->post_type, AMP_QUERY_VAR ) ) {
return false;
}

Expand Down
18 changes: 18 additions & 0 deletions includes/utils/class-amp-css-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

class AMP_CSS_Utils
{
public static function extract_and_minify_css_file($cssFile)
{
// Load CSS file contents
$minified_css = file_get_contents($cssFile);
// Remove comments
$minified_css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $minified_css);
// Remove space after colons
$minified_css = str_replace(': ', ':', $minified_css);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of lines 12 and 14 why not just this?

$minified_css = preg_replace( '#\s+#ms', '', $minified_css);`

// Remove whitespace
$minified_css = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $minified_css);

return $minified_css;
}
}
10 changes: 10 additions & 0 deletions includes/utils/class-amp-dom-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,14 @@ private static function is_self_closing_tag( $tag ) {

return in_array( $tag, $self_closing_tags, true );
}

/**
* Remove nodes from DOM element
*/
public static function remove_dom_nodes($nodes_to_remove) {
foreach ($nodes_to_remove as $node) {
$node->parentNode->removeChild($node);
}
}

}
Loading