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 theme support settings to admin screen; prevent serving dirty AMP #1199

Merged
merged 19 commits into from
Jun 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5a81073
Prevent making AMP blocks available when AMP is not native
westonruter Jun 5, 2018
92424fb
Remove menu-toggle in Twenty Fifteen if there is nothing to open
westonruter Jun 6, 2018
555c079
Add options for enabling amp theme support and configuring validation…
westonruter Jun 6, 2018
5bae33e
Remove dirty AMP support; rename Disabled to Classic
westonruter Jun 6, 2018
a5756d9
Add link to settings screen among plugin action links
westonruter Jun 6, 2018
0068e79
Rename Theme Support to Template Mode
westonruter Jun 6, 2018
be0557b
Fix undefined index notice
westonruter Jun 6, 2018
b94a830
Merge branch 'url-handling' into add/admin-theme-support-options
westonruter Jun 7, 2018
0c339d9
Merge branch 'develop' of https://github.com/Automattic/amp-wp into a…
westonruter Jun 7, 2018
9971e22
Omit displaying the amphtml link if there are known unaccepted valida…
westonruter Jun 7, 2018
32a093e
Include HTML comment explaining that amphtml version is not available…
westonruter Jun 7, 2018
f864d67
Simplify how validation errors get auto-accepted based on user prefer…
westonruter Jun 7, 2018
d890b6c
Introduce "Flagged" status for validation errors which are Rejected b…
westonruter Jun 8, 2018
ec95351
Merge flagged status into Rejected and Accepted
westonruter Jun 8, 2018
365d571
Prevent raising removed_unused_css_rules as error when tree-shaking i…
westonruter Jun 8, 2018
4aac14b
Improve UI to reflect sanitization status when required
westonruter Jun 8, 2018
5c6b486
Add AMP link to admin bar with indication of AMP status and re-valida…
westonruter Jun 8, 2018
616dad7
Omit AMP admin bar menu item from admin
westonruter Jun 8, 2018
421dc74
Add duplicate admin bar sub item link for sake of mobile
westonruter Jun 8, 2018
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
21 changes: 17 additions & 4 deletions assets/js/amp-block-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ var ampBlockValidation = ( function() { // eslint-disable-line no-unused-vars
*/
data: {
i18n: {},
ampValidityRestField: ''
ampValidityRestField: '',
isCanonical: false
},

/**
Expand Down Expand Up @@ -91,22 +92,28 @@ var ampBlockValidation = ( function() { // eslint-disable-line no-unused-vars
/**
* Handle state change regarding validation errors.
*
* This is essentially a JS implementation of \AMP_Validation_Manager::print_edit_form_validation_status() in PHP.
*
* @return {void}
*/
handleValidationErrorsStateChange: function handleValidationErrorsStateChange() {
var currentPost, validationErrors, blockValidationErrors, noticeElement, noticeMessage, blockErrorCount, ampValidity;
var currentPost, validationErrors, blockValidationErrors, noticeElement, noticeMessage, blockErrorCount, ampValidity, hasActuallyUnacceptedError;

// @todo Gutenberg currently is not persisting isDirty state if changes are made during save request. Block order mismatch.
// We can only align block validation errors with blocks in editor when in saved state, since only here will the blocks be aligned with the validation errors.
if ( wp.data.select( 'core/editor' ).isEditedPostDirty() ) {
return;
}

hasActuallyUnacceptedError = false;
currentPost = wp.data.select( 'core/editor' ).getCurrentPost();
ampValidity = currentPost[ module.data.ampValidityRestField ] || {};
validationErrors = _.map(
_.filter( ampValidity.results, function( result ) {
return ! result.sanitized;
if ( result.status !== 1 /* ACCEPTED */ ) {
hasActuallyUnacceptedError = true;
}
return result.term_status !== 1; /* ACCEPTED */
} ),
function( result ) {
return result.error;
Expand Down Expand Up @@ -179,7 +186,13 @@ var ampBlockValidation = ( function() { // eslint-disable-line no-unused-vars
);
}

noticeMessage += ' ' + wp.i18n.__( 'Non-accepted validation errors prevent AMP from being served.', 'amp' );
noticeMessage += ' ';
if ( hasActuallyUnacceptedError && ! module.data.isCanonical ) {
noticeMessage += wp.i18n.__( 'Non-accepted validation errors prevent AMP from being served, and the user will be redirected to the non-AMP version.', 'amp' );
} else {
noticeMessage += wp.i18n.__( 'The invalid markup will be automatically sanitized to ensure a valid AMP response is served.', 'amp' );
}

noticeElement = wp.element.createElement( 'p', {}, [
noticeMessage + ' ',
ampValidity.review_link && wp.element.createElement(
Expand Down
69 changes: 20 additions & 49 deletions includes/admin/class-amp-editor-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ public function init() {
if ( function_exists( 'gutenberg_init' ) ) {
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
add_filter( 'wp_kses_allowed_html', array( $this, 'whitelist_block_atts_in_wp_kses_allowed_html' ), 10, 2 );
add_filter( 'the_content', array( $this, 'tally_content_requiring_amp_scripts' ) );
add_action( 'wp_print_footer_scripts', array( $this, 'print_dirty_amp_scripts' ) );
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think the methods tally_content_requiring_amp_scripts() and print_dirty_amp_scripts() could be removed, given that they're not used anymore? Maybe the comment below could link to them.

Copy link
Member Author

@westonruter westonruter Jun 6, 2018

Choose a reason for hiding this comment

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

You're probably right. It's easy enough to look back in history to find the removal of these methods to resurrect them. 👍

Done in 5bae33e.

}
}

Expand Down Expand Up @@ -109,27 +107,28 @@ public function whitelist_block_atts_in_wp_kses_allowed_html( $tags, $context )
*/
public function enqueue_block_editor_assets() {

// Styles.
wp_enqueue_style(
'amp-editor-blocks-style',
amp_get_asset_url( 'css/amp-editor-blocks.css' ),
array(),
AMP__VERSION
);
// Enqueue script and style for AMP-specific blocks.
if ( amp_is_canonical() ) {
wp_enqueue_style(
'amp-editor-blocks-style',
amp_get_asset_url( 'css/amp-editor-blocks.css' ),
array(),
AMP__VERSION
);

// Scripts.
wp_enqueue_script(
'amp-editor-blocks-build',
amp_get_asset_url( 'js/amp-blocks-compiled.js' ),
array( 'wp-blocks', 'lodash', 'wp-i18n', 'wp-element', 'wp-components' ),
AMP__VERSION
);
wp_enqueue_script(
'amp-editor-blocks-build',
amp_get_asset_url( 'js/amp-blocks-compiled.js' ),
array( 'wp-blocks', 'lodash', 'wp-i18n', 'wp-element', 'wp-components' ),
AMP__VERSION
);

wp_add_inline_script(
'amp-editor-blocks-build',
'wp.i18n.setLocaleData( ' . wp_json_encode( gutenberg_get_jed_locale_data( 'amp' ) ) . ', "amp" );',
'before'
);
wp_add_inline_script(
'amp-editor-blocks-build',
'wp.i18n.setLocaleData( ' . wp_json_encode( gutenberg_get_jed_locale_data( 'amp' ) ) . ', "amp" );',
'before'
);
}

wp_enqueue_script(
'amp-editor-blocks',
Expand All @@ -146,32 +145,4 @@ public function enqueue_block_editor_assets() {
) ) )
);
}

/**
* Tally the AMP component scripts that are needed in a dirty AMP document.
*
* @param string $content Content.
* @return string Content (unmodified).
*/
public function tally_content_requiring_amp_scripts( $content ) {
if ( ! is_amp_endpoint() ) {
$pattern = sprintf( '/<(%s)\b.*?>/s', join( '|', $this->amp_blocks ) );
if ( preg_match_all( $pattern, $content, $matches ) ) {
$this->content_required_amp_scripts = array_merge(
$this->content_required_amp_scripts,
$matches[1]
);
}
}
return $content;
}

/**
* Print AMP scripts required for AMP components used in a non-AMP document (dirty AMP).
*/
public function print_dirty_amp_scripts() {
if ( ! is_amp_endpoint() && ! empty( $this->content_required_amp_scripts ) ) {
wp_scripts()->do_items( $this->content_required_amp_scripts );
}
}
}
1 change: 1 addition & 0 deletions includes/amp-frontend-actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*
* @since 0.2
* @since 1.0 Deprecated
* @see amp_add_amphtml_link()
*/
function amp_frontend_add_canonical() {
_deprecated_function( __FUNCTION__, '1.0', 'amp_add_amphtml_link' );
Expand Down
43 changes: 40 additions & 3 deletions includes/amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ function amp_remove_endpoint( $url ) {
/**
* Add amphtml link.
*
* If there are known validation errors for the current URL then do not output anything.
*
* @since 1.0
*/
function amp_add_amphtml_link() {
Expand All @@ -183,12 +185,47 @@ function amp_add_amphtml_link() {
return;
}

$current_url = amp_get_current_url();

$amp_url = null;
if ( is_singular() ) {
$amp_url = amp_get_permalink( get_queried_object_id() );
if ( current_theme_supports( 'amp' ) ) {
if ( AMP_Theme_Support::is_paired_available() ) {
$amp_url = add_query_arg( amp_get_slug(), '', $current_url );
}
} else {
$amp_url = add_query_arg( amp_get_slug(), '', amp_get_current_url() );
if ( is_singular() ) {
$amp_url = amp_get_permalink( get_queried_object_id() );
} else {
$amp_url = add_query_arg( amp_get_slug(), '', $current_url );
}
}

if ( ! $amp_url ) {
printf( '<!-- %s -->', esc_html__( 'There is no amphtml version available for this URL.', 'amp' ) );
return;
}

// Check to see if there are known unaccepted validation errors for this URL.
if ( current_theme_supports( 'amp' ) ) {
$validation_errors = AMP_Invalid_URL_Post_Type::get_invalid_url_validation_errors( $current_url, array( 'ignore_accepted' => true ) );
$error_count = count( $validation_errors );
if ( $error_count > 0 ) {
echo "<!--\n";
echo esc_html( sprintf(
/* translators: %s is error count */
_n(
'There is %s validation error that is blocking the amphtml version from being available.',
'There are %s validation errors that are blocking the amphtml version from being available.',
$error_count,
'amp'
),
number_format_i18n( $error_count )
) );
echo "\n-->";
return;
}
}

if ( $amp_url ) {
printf( '<link rel="amphtml" href="%s">', esc_url( $amp_url ) );
}
Expand Down
48 changes: 42 additions & 6 deletions includes/class-amp-theme-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class AMP_Theme_Support {
* @since 0.7
*/
public static function init() {
self::apply_options();
if ( ! current_theme_supports( 'amp' ) ) {
return;
}
Expand All @@ -117,7 +118,7 @@ public static function init() {
$args = array_shift( $support );
if ( ! is_array( $args ) ) {
trigger_error( esc_html__( 'Expected AMP theme support arg to be array.', 'amp' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
} elseif ( count( array_diff( array_keys( $args ), array( 'template_dir', 'available_callback', 'comments_live_list' ) ) ) !== 0 ) {
} elseif ( count( array_diff( array_keys( $args ), array( 'template_dir', 'available_callback', 'comments_live_list', '__added_via_option' ) ) ) !== 0 ) {
trigger_error( esc_html__( 'Expected AMP theme support to only have template_dir and/or available_callback.', 'amp' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
}
}
Expand All @@ -132,16 +133,36 @@ public static function init() {
add_action( 'wp', array( __CLASS__, 'finish_init' ), PHP_INT_MAX );
}

/**
* Apply options for whether theme support is enabled via admin and what sanitization is performed by default.
*
* @see AMP_Post_Type_Support::add_post_type_support() For where post type support is added, since it is irrespective of theme support.
*/
public static function apply_options() {
if ( ! current_theme_supports( 'amp' ) ) {
$theme_support_option = AMP_Options_Manager::get_option( 'theme_support' );
if ( 'disabled' === $theme_support_option ) {
return;
}

$args = array(
'__added_via_option' => true,
);
if ( 'paired' === $theme_support_option ) {
$args['template_dir'] = './';
}
add_theme_support( 'amp', $args );
Copy link
Contributor

Choose a reason for hiding this comment

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

It's nice how this can force theme support.

}
}

/**
* Finish initialization once query vars are set.
*
* @since 0.7
*/
public static function finish_init() {
if ( ! is_amp_endpoint() ) {
if ( self::is_paired_available() ) {
amp_add_frontend_actions();
}
amp_add_frontend_actions();
return;
}

Expand Down Expand Up @@ -1187,7 +1208,10 @@ public static function prepare_response( $response, $args = array() ) {
$dom_serialize_start = microtime( true );
self::ensure_required_markup( $dom );

if ( ! AMP_Validation_Manager::should_validate_response() && AMP_Validation_Manager::has_blocking_validation_errors() ) {
$blocking_error_count = AMP_Validation_Manager::count_blocking_validation_errors();
if ( ! AMP_Validation_Manager::should_validate_response() && $blocking_error_count > 0 ) {

// Note the canonical check will not currently ever be met because dirty AMP is not yet supported; all validation errors will forcibly be sanitized.
if ( amp_is_canonical() ) {
$dom->documentElement->removeAttribute( 'amp' );

Expand All @@ -1201,7 +1225,19 @@ public static function prepare_response( $response, $args = array() ) {
$head->appendChild( $script );
}
} else {
self::redirect_ampless_url( false );
$current_url = amp_get_current_url();
$ampless_url = amp_remove_endpoint( $current_url );
$ampless_url = add_query_arg(
AMP_Validation_Manager::VALIDATION_ERRORS_QUERY_VAR,
$blocking_error_count,
$ampless_url
);

/*
* Temporary redirect because AMP URL may return when blocking validation errors
* occur or when a non-canonical AMP theme is used.
*/
wp_safe_redirect( $ampless_url, 302 );
return esc_html__( 'Redirecting to non-AMP version.', 'amp' );
}
}
Expand Down
39 changes: 30 additions & 9 deletions includes/options/class-amp-options-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ class AMP_Options_Manager {
*/
const OPTION_NAME = 'amp-options';

/**
* Default option values.
*
* @var array
*/
protected static $defaults = array(
'theme_support' => 'disabled',
'supported_post_types' => array(),
'analytics' => array(),
'force_sanitization' => false,
'accept_tree_shaking' => false,
);

/**
* Register settings.
*/
Expand Down Expand Up @@ -58,7 +71,11 @@ public static function maybe_flush_rewrite_rules( $old_options, $new_options ) {
* @return array Options.
*/
public static function get_options() {
return get_option( self::OPTION_NAME, array() );
$options = get_option( self::OPTION_NAME, array() );
if ( empty( $options ) ) {
$options = array();
}
return array_merge( self::$defaults, $options );
}

/**
Expand Down Expand Up @@ -86,15 +103,20 @@ public static function get_option( $option, $default = false ) {
* @return array Options.
*/
public static function validate_options( $new_options ) {
$defaults = array(
'supported_post_types' => array(),
'analytics' => array(),
);
$options = self::get_options();

$options = array_merge(
$defaults,
self::get_options()
// Theme support.
$recognized_theme_supports = array(
'disabled',
'paired',
'native',
);
if ( isset( $new_options['theme_support'] ) && in_array( $new_options['theme_support'], $recognized_theme_supports, true ) ) {
$options['theme_support'] = $new_options['theme_support'];
}

$options['force_sanitization'] = ! empty( $new_options['force_sanitization'] );
$options['accept_tree_shaking'] = ! empty( $new_options['accept_tree_shaking'] );

// Validate post type support.
if ( isset( $new_options['supported_post_types'] ) ) {
Expand Down Expand Up @@ -156,7 +178,6 @@ public static function validate_options( $new_options ) {
return $options;
}


/**
* Check for errors with updating the supported post types.
*
Expand Down
Loading