Add tracking categories for errors

Two tracking categories are added:
* A category to track stylesheets with errors. While it's usually not
  possible to save a stylesheet with errors, it can happen if a
  server-side change makes formerly-valid CSS become invalid.
* A category to track pages displaying errors from incorrect use of
  the <templatestyles/> tag.

Bug: T195676
Change-Id: I123679d4bffe36cb28aca1688c052470027ea2a8
This commit is contained in:
Brad Jorsch 2018-05-26 17:45:34 -04:00
parent 351e1f715f
commit d1734fbeb2
5 changed files with 45 additions and 25 deletions

View file

@ -30,6 +30,10 @@
"ContentHandlers": { "ContentHandlers": {
"sanitized-css": "TemplateStylesContentHandler" "sanitized-css": "TemplateStylesContentHandler"
}, },
"TrackingCategories": [
"templatestyles-stylesheet-error-category",
"templatestyles-page-error-category"
],
"SyntaxHighlightModels": { "SyntaxHighlightModels": {
"sanitized-css": "css" "sanitized-css": "css"
}, },

View file

@ -15,6 +15,10 @@
"templatestyles-size-exceeded": "The stylesheet is larger than the maximum size of $2.", "templatestyles-size-exceeded": "The stylesheet is larger than the maximum size of $2.",
"templatestyles-end-tag-injection": "The supplied stylesheet contains <code>&lt;/style</code>, which is not allowed.", "templatestyles-end-tag-injection": "The supplied stylesheet contains <code>&lt;/style</code>, which is not allowed.",
"content-model-sanitized-css": "Sanitized CSS", "content-model-sanitized-css": "Sanitized CSS",
"templatestyles-stylesheet-error-category": "TemplateStyles stylesheets with errors",
"templatestyles-stylesheet-error-category-desc": "The TemplateStyles stylesheet has an error.",
"templatestyles-page-error-category": "Pages with TemplateStyles errors",
"templatestyles-page-error-category-desc": "There was an error when processing a <code><nowiki><templatestyles/></nowiki></code> tag on the page.",
"templatestyles-error-at-rule-block-not-allowed": "Block not allowed for <code>@$3</code> at line $1 character $2.", "templatestyles-error-at-rule-block-not-allowed": "Block not allowed for <code>@$3</code> at line $1 character $2.",
"templatestyles-error-at-rule-block-required": "Block required for <code>@$3</code> at line $1 character $2.", "templatestyles-error-at-rule-block-required": "Block required for <code>@$3</code> at line $1 character $2.",

View file

@ -17,6 +17,10 @@
"templatestyles-size-exceeded": "Error returned when the stylesheet is more than $wgTemplateStylesMaxStylesheetSize bytes. Parameters:\n* $1 - Maximum size in bytes\n* $2 - Maximum size in \"human units\" (i.e. KB, MB, GB, etc).", "templatestyles-size-exceeded": "Error returned when the stylesheet is more than $wgTemplateStylesMaxStylesheetSize bytes. Parameters:\n* $1 - Maximum size in bytes\n* $2 - Maximum size in \"human units\" (i.e. KB, MB, GB, etc).",
"templatestyles-end-tag-injection": "Error returned when the stylesheet contains <code>&lt;/style</code>, which is likely an attempt at HTML injection.", "templatestyles-end-tag-injection": "Error returned when the stylesheet contains <code>&lt;/style</code>, which is likely an attempt at HTML injection.",
"content-model-sanitized-css": "Name for TemplateStyles sanitized-css content model.", "content-model-sanitized-css": "Name for TemplateStyles sanitized-css content model.",
"templatestyles-stylesheet-error-category": "Tracking category for TemplateStyles stylesheets with errors.",
"templatestyles-stylesheet-error-category-desc": "Description on [[Special:TrackingCategories]] for the {{msg-mw|templatestyles-stylesheet-error-category}} tracking category.",
"templatestyles-page-error-category": "Tracking category for pages with errors from <code><nowiki><templatestyles/></nowiki></code>.",
"templatestyles-page-error-category-desc": "Description on [[Special:TrackingCategories]] for the {{msg-mw|templatestyles-page-error-category}} tracking category.",
"templatestyles-error-at-rule-block-not-allowed": "Error in CSS validation. Note \"block\" in this message refers to the part of CSS syntax inside the <code>{ ... }</code>. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.\n* $3 - Name of the at-rule in question.", "templatestyles-error-at-rule-block-not-allowed": "Error in CSS validation. Note \"block\" in this message refers to the part of CSS syntax inside the <code>{ ... }</code>. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.\n* $3 - Name of the at-rule in question.",
"templatestyles-error-at-rule-block-required": "Error in CSS validation. Note \"block\" in this message refers to the part of CSS syntax inside the <code>{ ... }</code>. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.\n* $3 - Name of the at-rule in question.", "templatestyles-error-at-rule-block-required": "Error in CSS validation. Note \"block\" in this message refers to the part of CSS syntax inside the <code>{ ... }</code>. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.\n* $3 - Name of the at-rule in question.",
"templatestyles-error-bad-character-in-url": "Error in CSS parsing. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.", "templatestyles-error-bad-character-in-url": "Error in CSS parsing. Parameters:\n* $1 - Line number of the error.\n* $2 - Location of the error within the line.",

View file

@ -129,10 +129,13 @@ class TemplateStylesContent extends TextContent {
// Inject our warnings into the resulting ParserOutput // Inject our warnings into the resulting ParserOutput
$po = parent::getParserOutput( $title, $revId, $options, $generateHtml ); $po = parent::getParserOutput( $title, $revId, $options, $generateHtml );
$status = $this->sanitize( [ 'novalue' => true, 'class' => $options->getWrapOutputClass() ] ); $status = $this->sanitize( [ 'novalue' => true, 'class' => $options->getWrapOutputClass() ] );
foreach ( $status->getErrors() as $error ) { if ( $status->getErrors() ) {
$po->addWarning( foreach ( $status->getErrors() as $error ) {
Message::newFromSpecifier( array_merge( [ $error['message'] ], $error['params'] ) )->parse() $po->addWarning(
); Message::newFromSpecifier( array_merge( [ $error['message'] ], $error['params'] ) )->parse()
);
}
$po->addTrackingCategory( 'templatestyles-stylesheet-error-category', $title );
} }
return $po; return $po;
} }

View file

@ -204,9 +204,7 @@ class TemplateStylesHooks {
} }
if ( !isset( $params['src'] ) || trim( $params['src'] ) === '' ) { if ( !isset( $params['src'] ) || trim( $params['src'] ) === '' ) {
return '<strong class="error">' . return self::formatTagError( $parser, [ 'templatestyles-missing-src' ] );
wfMessage( 'templatestyles-missing-src' )->inContentLanguage()->parse() .
'</strong>';
} }
// Default to the Template namespace because that's the most likely // Default to the Template namespace because that's the most likely
@ -215,9 +213,7 @@ class TemplateStylesHooks {
// wind up wanting to make that relative to the wrong page. // wind up wanting to make that relative to the wrong page.
$title = Title::newFromText( $params['src'], NS_TEMPLATE ); $title = Title::newFromText( $params['src'], NS_TEMPLATE );
if ( !$title ) { if ( !$title ) {
return '<strong class="error">' . return self::formatTagError( $parser, [ 'templatestyles-invalid-src' ] );
wfMessage( 'templatestyles-invalid-src' )->inContentLanguage()->parse() .
'</strong>';
} }
$rev = $parser->fetchCurrentRevisionOfTitle( $title ); $rev = $parser->fetchCurrentRevisionOfTitle( $title );
@ -229,24 +225,20 @@ class TemplateStylesHooks {
$content = $rev ? $rev->getContent() : null; $content = $rev ? $rev->getContent() : null;
if ( !$content ) { if ( !$content ) {
$title = $title->getPrefixedText(); $title = $title->getPrefixedText();
return '<strong class="error">' . return self::formatTagError( $parser, [
wfMessage( 'templatestyles-bad-src-missing',
'templatestyles-bad-src-missing', $title,
$title, wfEscapeWikiText( $title )
wfEscapeWikiText( $title ) ] );
)->inContentLanguage()->parse() .
'</strong>';
} }
if ( !$content instanceof TemplateStylesContent ) { if ( !$content instanceof TemplateStylesContent ) {
$title = $title->getPrefixedText(); $title = $title->getPrefixedText();
return '<strong class="error">' . return self::formatTagError( $parser, [
wfMessage( 'templatestyles-bad-src',
'templatestyles-bad-src', $title,
$title, wfEscapeWikiText( $title ),
wfEscapeWikiText( $title ), ContentHandler::getLocalizedName( $content->getModel() )
ContentHandler::getLocalizedName( $content->getModel() ) ] );
)->inContentLanguage()->parse() .
'</strong>';
} }
// If the revision actually has an ID, cache based on that. // If the revision actually has an ID, cache based on that.
@ -310,4 +302,17 @@ class TemplateStylesHooks {
return $ret; return $ret;
} }
/**
* Format an error in the `<templatestyles>` tag
* @param Parser $parser
* @param array $msg Arguments to wfMessage()
* @return string HTML
*/
private static function formatTagError( Parser $parser, array $msg ) {
$parser->addTrackingCategory( 'templatestyles-page-error-category' );
return '<strong class="error">' .
call_user_func_array( 'wfMessage', $msg )->inContentLanguage()->parse() .
'</strong>';
}
} }