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 validation error reporting for plugins and themes #971

Merged
merged 94 commits into from
Mar 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
8778ecf
Issue #842: Begin to validate plugins.
Feb 17, 2018
9eccf44
Issue #842: Align equals signs to prevent failed Travis build.
Feb 17, 2018
0d5e50d
Issue #842: Begin plugin callback validation.
Feb 19, 2018
f0c864c
Issue #842: Handle 'Closure' callbacks in get_plugin().
Feb 19, 2018
6c2bbec
Issue #842: Correct an issue in failed Travis build.
Feb 19, 2018
f77e022
Issue #842: Process comments for invalid plugin output.
Feb 20, 2018
780b746
Issue #842: Align equals signs to correct Travis issue.
Feb 20, 2018
33456a7
Issue #842: Output validation errors in header.
Feb 20, 2018
3c47072
Issue #842: Remove list() in favor of array access.
Feb 20, 2018
afdefdf
Issue #842: Align arrows vertically, remove extra constant.
Feb 20, 2018
6c587e2
Issue #842: Use the constant CALLBACK_KEY.
Feb 20, 2018
79856bc
Issue #842: Modify how callback function is called.
Feb 20, 2018
0484bd9
Replace hooks after iterating to prevent infinite loop in PHP<=5.4
westonruter Feb 21, 2018
7a06be3
Issue #842: Remove 'static' from methods, add assertions.
Feb 21, 2018
ab653e8
Issue #842: Register validation error post type.
Feb 21, 2018
ba46389
Issue #842: Store the validation errors in CPT.
Feb 22, 2018
8317283
Issue #842: Store the error URL in metadata.
Feb 22, 2018
4955686
Issue #842: Rename function to register_post_type().
Feb 23, 2018
fda14ea
Issue #842: Rename function to should_validate_front_end().
Feb 23, 2018
f1bf9f3
Issue #842: Change how nodes are processed.
Feb 23, 2018
3d1e6e3
Issue #842: Rename function to capture_current_source().
Feb 23, 2018
74a46f7
Issue #841: Rename property $current_source.
Feb 23, 2018
32a58fc
Issue #842: Change $current_sources to a stack.
Feb 24, 2018
66ab20d
Issue #842: Move get_plugin() into get_source().
Feb 24, 2018
87df66a
Issue #842: Change how the post meta is updated.
Feb 24, 2018
76ec4df
Issue #842: Add the source type to the comment.
Feb 24, 2018
ea16d64
Issue #842: Accommodate plugins and themes in get_source().
Feb 24, 2018
54e15cf
Issue #842: Remove the end of the plugin name.
Feb 24, 2018
7e049ae
Issue #842: Merge in develop, resolve conflicts.
Feb 26, 2018
0263e58
Use name instead of source; pass sources as arg in callback; improve …
westonruter Feb 27, 2018
4d0569f
Issue #842: Display warning on plugin activation.
Feb 28, 2018
fe0e6cc
Harden detection of validation errors caused by plugins
westonruter Mar 1, 2018
1e38e5d
Use add_post_meta() without update_post_meta() to log URL having vali…
westonruter Mar 1, 2018
8684ad1
Issue #842: Output validation errors in a post table.
Mar 1, 2018
0728a1e
Issue #842: Correct Travis issue and use constant.
Mar 1, 2018
2228520
Issue #842: Improve post actions, prevent muliple plugins.
Mar 1, 2018
c2664ca
Issue #842: Add a bulk action 're-check' linkon edit.php.
Mar 1, 2018
d2b033c
Issue #842: Prevent showing the same source multiple times.
Mar 1, 2018
816b77d
Add validation reporting of invalid shortcodes
westonruter Mar 1, 2018
2715cf1
Implement 'Re-check' bulk action.
Mar 2, 2018
76e2be5
Refactor how sources are determined for validation
westonruter Mar 2, 2018
975f364
Fix PHP 5.3 compatibility re: using self in closure; fix tests
westonruter Mar 2, 2018
5279dd7
Implement inline 'Re-check' action.
Mar 2, 2018
e79635e
Change spelling of re-check to recheck.
Mar 2, 2018
ee302fa
Use singular and plural in plugins.php warning.
Mar 2, 2018
2979969
Add a 'more details' link to the plugins.php warning.
Mar 2, 2018
ddd97ea
Refactor removed_nodes into more general validation_errors to allow f…
westonruter Mar 2, 2018
7c5402e
Schedule a twice daily cron event to check for validity.
Mar 2, 2018
9805f82
Address Travis error: Use a specific key, not array_shift().
Mar 2, 2018
8c4906e
Rename reset_validation_errors() to reset_validation_results().
Mar 2, 2018
c9806d9
Hide the 'Add New' link on the validation page.
Mar 2, 2018
26d773e
Rename validate_markup to handle_validate_request().
Mar 3, 2018
a341e79
Remove ERROR_KEY, and check if $validation_errors is empty().
Mar 3, 2018
f899646
Remove 2 extra conditional blocks for call_user_func_array().
Mar 3, 2018
1badf73
Prevent duplication of 'meta_input'
Mar 3, 2018
76093b6
Improve validation status post list table
westonruter Mar 3, 2018
73e5ac9
Improve handling of URLs associated with a given validation error post
westonruter Mar 3, 2018
b981d10
Ensure wrapped_callback adds source comments when multi-line elements…
westonruter Mar 3, 2018
469d7f2
Remove create_posts cap to hide Add New link for Validation Status posts
westonruter Mar 3, 2018
b0c3389
Use first URL as validation status post title; add column for (additi…
westonruter Mar 3, 2018
e20f10d
Prevent storing validation status when frontend requested in debug mode
westonruter Mar 3, 2018
6972d5c
Remove the 'Publish' meta box from the CPT post.php page.
Mar 3, 2018
1fbc9c6
Add debug row action to load frontend with validation but without inv…
westonruter Mar 4, 2018
1b4db07
Remove source comments before rendering when validating without debug…
westonruter Mar 4, 2018
907b054
Indicate that plugin with invalid markup *may* be invalid
westonruter Mar 4, 2018
be82b04
Add meta box on post.php in place of 'Publish' box.
Mar 4, 2018
fb20b71
Accomodate the 'amp-wp' directory of the plugin.
Mar 4, 2018
21195d3
Specify PROJECT_SLUG instead of accomodating the 'amp-wp' directory o…
westonruter Mar 5, 2018
b6bcaa8
Add full-width meta box on CPT post.php page.
Mar 5, 2018
1e0c32b
Don't esc_html() the <code> element.
Mar 5, 2018
1aed8fb
Omit AMP endpoint or query var when AMP is canonical
westonruter Mar 5, 2018
a18a8c1
Upon plugin activation, validate the most recently-published post (in…
westonruter Mar 5, 2018
51fc2fa
Fix initialization of theme support with paired mode
westonruter Mar 5, 2018
932b70d
Tweak validation status meta boxes
westonruter Mar 5, 2018
b8be81c
Only set transient when doing validation after plugin activation
westonruter Mar 5, 2018
15b291c
Include invalid element's attributes as context in validation errors
westonruter Mar 5, 2018
7ad03bd
Re-use existing validation status post when re-check results in change
westonruter Mar 5, 2018
81dcb8e
Display the URLs at the bottom of the CPT post.php page.
Mar 5, 2018
72b5981
Address Travis error by aligning => vertically.
Mar 5, 2018
0f8c848
Show pseudo markup for which element or attribute was removed
westonruter Mar 5, 2018
0ffd14a
Supply the actual URL to re-check in the inline re-check link
westonruter Mar 5, 2018
b33edc9
Add debug links with each URL and in status meta box
westonruter Mar 5, 2018
dd9d629
Add count of how many valiation error posts there are in the admin menu
westonruter Mar 6, 2018
e6d12c8
Add AMP Validation Errors to the Dashboard At a Glance widget
westonruter Mar 6, 2018
376e3cd
WIP: Report errors with script enqueues
westonruter Mar 6, 2018
f5568fa
Add initial validation error source reporting for enqueued scripts
westonruter Mar 6, 2018
4651a6a
Add more robust way of detecting source for each enqueued style and s…
westonruter Mar 7, 2018
23065f5
Improve validation error reporting for stylesheets
westonruter Mar 7, 2018
ba9365a
Pull out REST API endpoint and cron validation for 0.7 release
westonruter Mar 7, 2018
1c6a25d
Incorporate post frontend URL's validation status with content valida…
westonruter Mar 7, 2018
0efc859
Ensure illegal values for overflow-x and overflow-y are sanitized
westonruter Mar 8, 2018
da5f4ad
Recheck the validation status of a post on frontend after saving when…
westonruter Mar 8, 2018
bf82b8d
Un-trash validation post status to re-use instead of adding new post …
westonruter Mar 8, 2018
1548d37
Rename should_validate_front_end to should_validate_response
westonruter Mar 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
1 change: 1 addition & 0 deletions .dev-lib
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PATH_EXCLUDES_PATTERN=includes/lib/
DEFAULT_BASE_BRANCH=develop
ASSETS_DIR=wp-assets
PROJECT_SLUG=amp

function after_wp_install {
echo "Installing REST API..."
Expand Down
2 changes: 1 addition & 1 deletion amp.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ function amp_init() {

add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK );

AMP_Validation_Utils::init();
AMP_Theme_Support::init();
AMP_Post_Type_Support::add_post_type_support();
AMP_Validation_Utils::init();
add_filter( 'request', 'amp_force_query_var_value' );
add_action( 'admin_init', 'AMP_Options_Manager::register_settings' );
add_action( 'wp_loaded', 'amp_post_meta_box' );
Expand Down
33 changes: 28 additions & 5 deletions includes/amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ function amp_get_permalink( $post_id ) {
return $pre_url;
}

$parsed_url = wp_parse_url( get_permalink( $post_id ) );
$structure = get_option( 'permalink_structure' );
if ( empty( $structure ) || ! empty( $parsed_url['query'] ) || is_post_type_hierarchical( get_post_type( $post_id ) ) ) {
$amp_url = add_query_arg( AMP_QUERY_VAR, '', get_permalink( $post_id ) );
if ( amp_is_canonical() ) {
$amp_url = get_permalink( $post_id );
} else {
$amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( AMP_QUERY_VAR, 'single_amp' );
$parsed_url = wp_parse_url( get_permalink( $post_id ) );
$structure = get_option( 'permalink_structure' );
if ( empty( $structure ) || ! empty( $parsed_url['query'] ) || is_post_type_hierarchical( get_post_type( $post_id ) ) ) {
$amp_url = add_query_arg( AMP_QUERY_VAR, '', get_permalink( $post_id ) );
} else {
$amp_url = trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( AMP_QUERY_VAR, 'single_amp' );
}
}

/**
Expand All @@ -51,6 +55,25 @@ function amp_get_permalink( $post_id ) {
return apply_filters( 'amp_get_permalink', $amp_url, $post_id );
}

/**
* Remove the AMP endpoint (and query var) from a given URL.
*
* @since 0.7
*
* @param string $url URL.
* @return string URL with AMP stripped.
*/
function amp_remove_endpoint( $url ) {

// Strip endpoint.
$url = preg_replace( ':/' . preg_quote( AMP_QUERY_VAR, ':' ) . '(?=/?(\?|#|$)):', '', $url );

// Strip query var.
$url = remove_query_arg( AMP_QUERY_VAR, $url );

return $url;
}

/**
* Determine whether a given post supports AMP.
*
Expand Down
99 changes: 67 additions & 32 deletions includes/class-amp-theme-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,7 @@ public static function init() {

self::purge_amp_query_vars();
self::handle_xhr_request();

if ( ! is_amp_endpoint() ) {
amp_add_frontend_actions();
} else {
self::setup_commenting();
add_action( 'widgets_init', array( __CLASS__, 'register_widgets' ) );
}
self::add_temporary_discussion_restrictions();

require_once AMP__DIR__ . '/includes/amp-post-template-actions.php';

Expand All @@ -98,20 +92,55 @@ public static function init() {
}
}

if ( amp_is_canonical() ) {
add_action( 'widgets_init', array( __CLASS__, 'register_widgets' ) );

// Redirect to canonical URL if the AMP URL was loaded, since canonical is now AMP.
if ( false !== get_query_var( AMP_QUERY_VAR, false ) ) { // Because is_amp_endpoint() now returns true if amp_is_canonical().
wp_safe_redirect( self::get_current_canonical_url(), 302 ); // Temporary redirect because canonical may change in future.
exit;
}
/*
* Note that wp action is use instead of template_redirect because some themes/plugins output
* the response at this action and then short-circuit with exit. So this is why the the preceding
* action to template_redirect--the wp action--is used instead.
*/
add_action( 'wp', array( __CLASS__, 'finish_init' ), PHP_INT_MAX );
}

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

if ( amp_is_canonical() ) {
self::redirect_canonical_amp();
} else {
self::register_paired_hooks();
}

self::register_hooks();
self::$embed_handlers = self::register_content_embed_handlers();
self::add_hooks();
self::$sanitizer_classes = amp_get_content_sanitizers();
self::$embed_handlers = self::register_content_embed_handlers();
}

/**
* Redirect to canonical URL if the AMP URL was loaded, since canonical is now AMP.
*
* @since 0.7
*/
public static function redirect_canonical_amp() {
if ( false !== get_query_var( AMP_QUERY_VAR, false ) ) { // Because is_amp_endpoint() now returns true if amp_is_canonical().
$url = preg_replace( '#^(https?://.+?)(/.*)$#', '$1', home_url( '/' ) );
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
$url .= wp_unslash( $_SERVER['REQUEST_URI'] );
}

$url = amp_remove_endpoint( $url );

wp_safe_redirect( $url, 302 ); // Temporary redirect because canonical may change in future.
exit;
}
}

/**
Expand Down Expand Up @@ -166,7 +195,7 @@ public static function register_paired_hooks() {
/**
* Register hooks.
*/
public static function register_hooks() {
public static function add_hooks() {

// Remove core actions which are invalid AMP.
remove_action( 'wp_head', 'wp_post_preview_js', 1 );
Expand Down Expand Up @@ -216,6 +245,10 @@ public static function register_hooks() {
add_action( 'comment_form', array( __CLASS__, 'add_amp_comment_form_templates' ), 100 );
remove_action( 'comment_form', 'wp_comment_form_unfiltered_html_nonce' );

if ( AMP_Validation_Utils::should_validate_response() ) {
AMP_Validation_Utils::add_validation_hooks();
}

// @todo Add character conversion.
}

Expand Down Expand Up @@ -279,7 +312,7 @@ public static function purge_amp_query_vars() {
}

/**
* Hook into a form submissions, such as comment the form or some other .
* Hook into a form submissions, such as the comment form or some other form submission.
*
* @since 0.7.0
* @global string $pagenow
Expand Down Expand Up @@ -370,14 +403,14 @@ public static function intercept_post_request_redirect( $location ) {
}

/**
* Set up commenting.
* Set up some restrictions for commenting based on amp-live-list limitations.
*
* Temporarily force comments to be listed in descending order.
* The following hooks are temporary while waiting for amphtml#5396 to be resolved.
*
* @link https://github.com/ampproject/amphtml/issues/5396
*/
public static function setup_commenting() {
/*
* Temporarily force comments to be listed in descending order.
*
* The following hooks are temporary while waiting for amphtml#5396 to be resolved.
*/
protected static function add_temporary_discussion_restrictions() {
add_filter( 'option_comment_order', function() {
return 'desc';
}, PHP_INT_MAX );
Expand Down Expand Up @@ -584,13 +617,7 @@ public static function get_current_canonical_url() {
$url = add_query_arg( $added_query_vars, $url );
}

// Strip endpoint.
$url = preg_replace( ':/' . preg_quote( AMP_QUERY_VAR, ':' ) . '(?=/?(\?|#|$)):', '', $url );

// Strip query var.
$url = remove_query_arg( AMP_QUERY_VAR, $url );

return $url;
return amp_remove_endpoint( $url );
}

/**
Expand Down Expand Up @@ -953,13 +980,15 @@ public static function prepare_response( $response, $args = array() ) {
return $response;
}

$is_validation_debug_mode = ! empty( $_REQUEST[ AMP_Validation_Utils::DEBUG_QUERY_VAR ] ); // WPCS: csrf ok.

$args = array_merge(
array(
'content_max_width' => ! empty( $content_width ) ? $content_width : AMP_Post_Template::CONTENT_MAX_WIDTH, // Back-compat.
'use_document_element' => true,
'remove_invalid_callback' => null,
'allow_dirty_styles' => self::is_customize_preview_iframe(), // Dirty styles only needed when editing (e.g. for edit shortcodes).
'allow_dirty_scripts' => is_customize_preview(), // Scripts are always needed to inject changeset UUID.
'disable_invalid_removal' => $is_validation_debug_mode,
),
$args
);
Expand Down Expand Up @@ -994,6 +1023,12 @@ public static function prepare_response( $response, $args = array() ) {
trigger_error( esc_html( sprintf( __( 'The database has the %s encoding when it needs to be utf-8 to work with AMP.', 'amp' ), get_bloginfo( 'charset' ) ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
}

if ( AMP_Validation_Utils::should_validate_response() ) {
AMP_Validation_Utils::finalize_validation( $dom, array(
'remove_source_comments' => ! $is_validation_debug_mode,
) );
}

$response = "<!DOCTYPE html>\n";
$response .= AMP_DOM_Utils::get_content_from_dom_node( $dom, $dom->documentElement );

Expand Down
2 changes: 1 addition & 1 deletion includes/options/class-amp-options-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AMP_Options_Menu {
*/
public function init() {
add_action( 'admin_post_amp_analytics_options', 'AMP_Options_Manager::handle_analytics_submit' );
add_action( 'admin_menu', array( $this, 'add_menu_items' ) );
add_action( 'admin_menu', array( $this, 'add_menu_items' ), 9 );
}

/**
Expand Down
51 changes: 31 additions & 20 deletions includes/sanitizers/class-amp-base-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ abstract class AMP_Base_Sanitizer {
* @type array $amp_bind_placeholder_prefix
* @type bool $allow_dirty_styles
* @type bool $allow_dirty_scripts
* @type bool $disable_invalid_removal
* @type callable $remove_invalid_callback
* }
*/
Expand Down Expand Up @@ -320,17 +321,19 @@ public function maybe_enforce_https_src( $src, $force_https = false ) {
*
* @since 0.7
*
* @param DOMNode|DOMElement $child The node to remove.
* @param DOMNode|DOMElement $node The node to remove.
* @param array $args Additional args to pass to validation error callback.
*
* @return void
*/
public function remove_invalid_child( $child ) {
$parent = $child->parentNode;
$child->parentNode->removeChild( $child );
if ( isset( $this->args['remove_invalid_callback'] ) ) {
call_user_func( $this->args['remove_invalid_callback'], array(
'node' => $child,
'parent' => $parent,
) );
public function remove_invalid_child( $node, $args = array() ) {
if ( isset( $this->args['validation_error_callback'] ) ) {
call_user_func( $this->args['validation_error_callback'],
array_merge( compact( 'node' ), $args )
);
}
if ( empty( $this->args['disable_invalid_removal'] ) ) {
$node->parentNode->removeChild( $node );
}
}

Expand All @@ -344,25 +347,33 @@ public function remove_invalid_child( $child ) {
*
* @param DOMElement $element The node for which to remove the attribute.
* @param DOMAttr|string $attribute The attribute to remove from the element.
* @param array $args Additional args to pass to validation error callback.
* @return void
*/
public function remove_invalid_attribute( $element, $attribute ) {
if ( isset( $this->args['remove_invalid_callback'] ) ) {
public function remove_invalid_attribute( $element, $attribute, $args = array() ) {
if ( isset( $this->args['validation_error_callback'] ) ) {
if ( is_string( $attribute ) ) {
$attribute = $element->getAttributeNode( $attribute );
}
if ( $attribute ) {
call_user_func( $this->args['validation_error_callback'],
array_merge(
array(
'node' => $attribute,
),
$args
)
);
if ( empty( $this->args['disable_invalid_removal'] ) ) {
$element->removeAttributeNode( $attribute );
}
}
} elseif ( empty( $this->args['disable_invalid_removal'] ) ) {
if ( is_string( $attribute ) ) {
$element->removeAttribute( $attribute );
} else {
$element->removeAttributeNode( $attribute );
call_user_func( $this->args['remove_invalid_callback'], array(
'node' => $attribute,
'parent' => $element,
) );
}
} elseif ( is_string( $attribute ) ) {
$element->removeAttribute( $attribute );
} else {
$element->removeAttributeNode( $attribute );
}
}

}
Loading