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 missing error titles for error codes #4405

Merged
merged 14 commits into from
Mar 21, 2020

Conversation

pierlon
Copy link
Contributor

@pierlon pierlon commented Mar 19, 2020

Summary

This PR adds the missing error titles (and appropriate labels for error sources) for the error codes which have none.

Fixes #1420

Todo

  • Add error titles for custom error codes

Checklist

  • My pull request is addressing an open issue (please create one otherwise).
  • My code is tested and passes existing tests.
  • My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).

@googlebot googlebot added the cla: yes Signed the Google CLA label Mar 19, 2020
@westonruter
Copy link
Member

@pierlon Thanks for opening this draft PR. However, I am not sure this is the right approach for the immediate term (v1.5). Namely, the error codes that we are using are not 1:1 mappings with the error codes used by the AMP validator. There are many error codes that we don't use at all (e.g. DOCUMENT_TOO_COMPLEX) whereas others that the validator doesn't use at all (e.g. MISSING_REQUIRED_PROPERTY_VALUE). So I don't see a lot of value in automating the extraction of the strings from the AMP validator spec right now. What's more is that if we make all of these strings translatable, then translators may end up translating a bunch of strings that are not ever used.

I think the initial target to solve #1420 should be less ambitious. And that is to extend what we have today in \AMP_Validation_Error_Taxonomy::get_error_title_from_code() to add additional case statements for the error codes we aren't currently accounting for, and incorporating the error messages from the validator where it makes sense.

So I think the patch should largely be limited to making changes to this method:

/**
* Get Error Title from Code
*
* @todo The message here should be constructed in the sanitizer that emitted the validation error in the first place.
*
* @param array $validation_error Validation error.
* @return string Title with some formatting markup.
*/
public static function get_error_title_from_code( $validation_error ) {
switch ( $validation_error['code'] ) {
case AMP_Tag_And_Attribute_Sanitizer::DISALLOWED_TAG:
if ( self::is_validation_error_for_js_script_element( $validation_error ) ) {
if ( isset( $validation_error['node_attributes']['src'] ) ) {
$title = esc_html__( 'Invalid script', 'amp' );
$basename = basename( wp_parse_url( $validation_error['node_attributes']['src'], PHP_URL_PATH ) );
if ( $basename ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $basename ) );
}
} else {
$title = esc_html__( 'Invalid inline script', 'amp' );
}
} else {
$title = esc_html__( 'Invalid element', 'amp' );
$title .= sprintf( ': <code>&lt;%s&gt;</code>', esc_html( $validation_error['node_name'] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::DISALLOWED_ATTR:
return sprintf(
'%s: <code>%s</code>',
esc_html__( 'Invalid attribute', 'amp' ),
esc_html( $validation_error['node_name'] )
);
case AMP_Tag_And_Attribute_Sanitizer::DISALLOWED_PROCESSING_INSTRUCTION:
return sprintf(
'%s: <code>&lt;%s%s&hellip;%s&gt;</code>',
esc_html__( 'Invalid processing instruction', 'amp' ),
'?',
esc_html( $validation_error['node_name'] ),
'?'
);
case AMP_Style_Sanitizer::STYLESHEET_TOO_LONG:
return esc_html__( 'Excessive CSS', 'amp' );
case AMP_Style_Sanitizer::CSS_SYNTAX_INVALID_AT_RULE:
return sprintf(
'%s: <code>@%s</code>',
esc_html__( 'Illegal CSS at-rule', 'amp' ),
esc_html( $validation_error['at_rule'] )
);
case AMP_Tag_And_Attribute_Sanitizer::DUPLICATE_UNIQUE_TAG:
return sprintf(
'%s: <code>&lt;%s&gt;</code>',
esc_html__( 'Duplicate element', 'amp' ),
esc_html( $validation_error['node_name'] )
);
case AMP_Style_Sanitizer::CSS_SYNTAX_INVALID_DECLARATION:
return esc_html__( 'Unrecognized CSS', 'amp' );
case AMP_Style_Sanitizer::CSS_SYNTAX_PARSE_ERROR:
return esc_html__( 'CSS parse error', 'amp' );
case AMP_Style_Sanitizer::STYLESHEET_FETCH_ERROR:
return esc_html__( 'Stylesheet fetch error', 'amp' );
case AMP_Style_Sanitizer::CSS_SYNTAX_INVALID_PROPERTY:
case AMP_Style_Sanitizer::CSS_SYNTAX_INVALID_PROPERTY_NOLIST:
$title = esc_html__( 'Illegal CSS property', 'amp' );
if ( isset( $validation_error['css_property_name'] ) ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $validation_error['css_property_name'] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::CDATA_TOO_LONG:
case AMP_Tag_And_Attribute_Sanitizer::MANDATORY_CDATA_MISSING_OR_INCORRECT:
case AMP_Tag_And_Attribute_Sanitizer::INVALID_CDATA_HTML_COMMENTS:
case AMP_Tag_And_Attribute_Sanitizer::INVALID_CDATA_CSS_IMPORTANT:
case AMP_Tag_And_Attribute_Sanitizer::CDATA_VIOLATES_BLACKLIST:
return esc_html__( 'Illegal text content', 'amp' );
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_CTRL_CHAR:
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_DEPTH:
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_EMPTY:
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_STATE_MISMATCH:
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_SYNTAX:
case AMP_Tag_And_Attribute_Sanitizer::JSON_ERROR_UTF8:
return esc_html__( 'Invalid JSON', 'amp' );
case AMP_Style_Sanitizer::CSS_SYNTAX_INVALID_IMPORTANT:
$title = esc_html__( 'Illegal CSS !important property', 'amp' );
if ( isset( $validation_error['css_property_name'] ) ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $validation_error['css_property_name'] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::DISALLOWED_PROPERTY_IN_ATTR_VALUE:
$title = esc_html__( 'Invalid property', 'amp' );
if ( isset( $validation_error['meta_property_name'] ) ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $validation_error['meta_property_name'] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::MISSING_MANDATORY_PROPERTY:
$title = esc_html__( 'Missing required property', 'amp' );
if ( isset( $validation_error['meta_property_name'] ) ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $validation_error['meta_property_name'] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::MISSING_REQUIRED_PROPERTY_VALUE:
$title = sprintf(
/* translators: %1$s is the property name, %2$s is the value for the property */
wp_kses( __( 'Invalid value for <code>%1$s</code> property: <code>%2$s</code>', 'amp' ), [ 'code' => '' ] ),
esc_html( $validation_error['meta_property_name'] ),
esc_html( $validation_error['meta_property_value'] )
);
return $title;
case AMP_Tag_And_Attribute_Sanitizer::ATTR_REQUIRED_BUT_MISSING:
$title = esc_html__( 'Missing required attribute', 'amp' );
if ( isset( $validation_error['attributes'][0] ) ) {
$title .= sprintf( ': <code>%s</code>', esc_html( $validation_error['attributes'][0] ) );
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::DUPLICATE_ONEOF_ATTRS:
$title = __( 'Mutually exclusive attributes encountered', 'amp' );
if ( ! empty( $validation_error['duplicate_oneof_attrs'] ) ) {
$title .= ': ';
$title .= implode(
', ',
array_map(
static function ( $attribute_name ) {
return sprintf( '<code>%s</code>', $attribute_name );
},
$validation_error['duplicate_oneof_attrs']
)
);
}
return $title;
case AMP_Tag_And_Attribute_Sanitizer::MANDATORY_ONEOF_ATTR_MISSING:
case AMP_Tag_And_Attribute_Sanitizer::MANDATORY_ANYOF_ATTR_MISSING:
$attributes_key = null;
if ( AMP_Tag_And_Attribute_Sanitizer::MANDATORY_ONEOF_ATTR_MISSING === $validation_error['code'] ) {
$title = __( 'Missing exclusive mandatory attribute', 'amp' );
$attributes_key = 'mandatory_oneof_attrs';
} else {
$title = __( 'Missing at least one mandatory attribute', 'amp' );
$attributes_key = 'mandatory_anyof_attrs';
}
// @todo This should not be needed because we can look it up from the spec. See https://github.com/ampproject/amp-wp/pull/3817.
if ( ! empty( $validation_error[ $attributes_key ] ) ) {
$title .= ': ';
$title .= implode(
', ',
array_map(
static function ( $attribute_name ) {
return sprintf( '<code>%s</code>', $attribute_name );
},
$validation_error[ $attributes_key ]
)
);
}
return $title;
default:
/* translators: %s error code */
return sprintf( __( 'Unknown error (%s)', 'amp' ), $validation_error['code'] );
}
}

@westonruter
Copy link
Member

I say this in part because I already tried going down that road in 8ae5c3b and 0b30e4a, but I decided ultimately to abandon it in a6d0fc9. This was done in the context of #3780.

@pierlon pierlon changed the title Standardize AMP error codes and error formats Add missing error titles for error codes Mar 19, 2020
@pierlon
Copy link
Contributor Author

pierlon commented Mar 19, 2020

Ah OK, I'm changing the direction of this PR from automating the extraction of the error codes to identifying the missing error titles for error codes and adding them.

@@ -993,14 +993,16 @@ private function validate_tag_spec_for_node( DOMElement $node, $tag_spec ) {

if ( ! empty( $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) && ! $this->has_parent( $node, $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ] ) ) {
return [
'code' => self::WRONG_PARENT_TAG,
'code' => self::WRONG_PARENT_TAG,
'required_parent_name' => $tag_spec[ AMP_Rule_Spec::MANDATORY_PARENT ],
Copy link
Member

Choose a reason for hiding this comment

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

Note that at some point we will not need to capture the required_parent_name in the error. We could instead capture the spec_name alone. Then the spec_name could be looked up to obtain the required_parent_name (or other properties depending on the error). This will be made possible by #3817. However, that is not yet available, so what you are doing here is the perfect approach in the time being.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep I initially was retrieving it by looking up the tag spec in AMP_Allowed_Tags_Generated::class, but I realized it was available in the sanitizer so I took it from there.

This will be made possible by #3817.

Awesome, made a note of that.


case AMP_Tag_And_Attribute_Sanitizer::INVALID_URL_PROTOCOL:
$parsed_url = wp_parse_url( $validation_error['element_attributes'][ $validation_error['node_name'] ] );
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : '';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what to do here if the scheme could not be parsed, defaulting to an empty string for now.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe null would be more appropriate?

Copy link
Member

Choose a reason for hiding this comment

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

Oh I see, it's used in a string. Yeah, an empty string seems fine for now.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, since code is now being used as opposed to quotes, then I think something else is needed:

Suggested change
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : '';
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . ':' : '(failure)';

@pierlon pierlon marked this pull request as ready for review March 20, 2020 11:43
@@ -27,7 +27,6 @@
class AMP_Tag_And_Attribute_Sanitizer extends AMP_Base_Sanitizer {

const DISALLOWED_TAG = 'DISALLOWED_TAG';
const DISALLOWED_TAG_MULTIPLE_CHOICES = 'DISALLOWED_TAG_MULTIPLE_CHOICES';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've went ahead and removed the DISALLOWED_TAG_MULTIPLE_CHOICES error code as I don't see a meaningful way of displaying the multiple validation errors on the page. Instead, each error will be sanitized separately which allows for each to be viewed.

unset( $validation_error['spec_name'] );
unset(
$validation_error['spec_name'],
// Remove other keys that may not not make the error unique.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm removing any other keys that could potentially make the validation error unique (but it should not). Take for example this HTML element:

<stop offset="5%" stop-color="gold" />

This would raise two separate MANDATORY_TAG_ANCESTOR validation errors, but each would have its own required_ancestor_name (lineargradient and radialgradient), making each validation error unique.


case AMP_Tag_And_Attribute_Sanitizer::INVALID_URL_PROTOCOL:
$parsed_url = wp_parse_url( $validation_error['element_attributes'][ $validation_error['node_name'] ] );
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : '';
Copy link
Member

Choose a reason for hiding this comment

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

Actually, since code is now being used as opposed to quotes, then I think something else is needed:

Suggested change
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : '';
$invalid_protocol = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . ':' : '(failure)';

Co-authored-by: Weston Ruter <westonruter@google.com>
Comment on lines -530 to +541
$this->remove_invalid_child(
$node,
[
'code' => self::DISALLOWED_TAG_MULTIPLE_CHOICES,
'errors' => $validation_errors,
]
);
foreach ( $validation_errors as $validation_error ) {
if ( true === $this->remove_invalid_child( $node, $validation_error ) ) {
break; // Once removed, ignore remaining errors.
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Given a Custom HTML block with the following markup:

<amp-accordion id="my-accordion" disable-session-states="">
  <section>
    <h2>Section 1</h2>
    <p>Content in section 12.</p>
  </section>
  <section>
    <h2>Section 2</h2>
    <div>Content in section 2.</div>
  </section>
  <section expanded="">
    <!--<h2>Section 3</h2>-->
    <amp-img src="/static/inline-examples/images/squirrel.jpg" width="320" height="256"></amp-img>
  </section>
</amp-accordion>

This is invalid because of the h2 being commented out. The amp-accordion component requires an h2 followed by a div or a p. The validation errors for this are not expected, however:

image

Notice the amp-img lacks the expected Custom HTML source.

I've fixed this here in feababc by breaking the loop of validation error reporting once a markup causing validation error is removed.

This will probably make this code no longer necessary:

if ( null === $node->parentNode ) {
// Node no longer exists.
return $should_remove;
}

Though it doesn't hurt to have it in there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Notice the amp-img lacks the expected Custom HTML source.

Good catch!

This will probably make this code no longer necessary:

In this context yes, but it could potentially prevent an error being thrown when attempting to remove a node multiple times.

…nt/1420-expose-validation-errors

* 'develop' of github.com:ampproject/amp-wp: (39 commits)
  Fix CachedRemoteGetRequest handling for failures
  Avoid max-age falling through to 1-month expiry
  Use wp_remote_get instead of private _wp_http_get_object
  Change expiry behavior regarding enforced minimum and failed requests
  Replace B with bytes abbr in summary table
  Add space before B and add abbr
  Update lables for stylesheet informatino panel
  Update dependency @wordpress/block-editor to v3.7.5 (#4393)
  Update dependency @wordpress/edit-post to v3.13.6 (#4395)
  Update dependency @wordpress/components to v9.2.4 (#4394)
  Update dependency browserslist to v4.10.0 (#4406)
  Show correct PHP requirement message to users
  Make use of headers more robust and add test
  Only do iterator_to_array() if Traversable
  Use static variable for computed constant string
  Improve phpdoc for Fonts
  Use iterator_to_array() instead of casting to array
  Cast Requests_Utility_CaseInsensitiveDictionary to an array
  Use WP_Http remote requests for external stylesheet fetching
  Use WP_Http remote requests for the optimizer
  ...
@westonruter westonruter merged commit fff8bed into develop Mar 21, 2020
@westonruter westonruter deleted the enhancement/1420-expose-validation-errors branch March 21, 2020 15:40
@westonruter westonruter added this to the v1.5 milestone Mar 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Signed the Google CLA
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose reason for why a validation error is raised
3 participants