Anomie has uploaded a new change for review.
https://gerrit.wikimedia.org/r/53802
Change subject: Add option to display the limit report on preview
......................................................................
Add option to display the limit report on preview
While we've long had the "NewPP limit report" hidden in an HTML comment,
it is hard for users to find this as they're not likely to look for
profiling information hidden in an HTML comment. Even for those aware of
it, it's not particularly convenient to find.
This changeset adds a user preference (off by default) to show this
information at the bottom of the page preview. It also adds the ability
for this information to be added to the ParserOutput object in a
structured manner, and various messages so the report can be localized
for the end user.
Note that, for backwards compatability, the default English messages are
used for the "NewPP limit report" comment rather than the localized
messages.
Change-Id: Ie065c7b5a17bbf1aa484d0ae1f3ee0f5d41f8495
---
M RELEASE-NOTES-1.21
M docs/hooks.txt
M includes/DefaultSettings.php
M includes/EditPage.php
M includes/Preferences.php
M includes/parser/Parser.php
M includes/parser/ParserOutput.php
M languages/messages/MessagesEn.php
M languages/messages/MessagesQqq.php
M maintenance/language/messages.inc
10 files changed, 316 insertions(+), 11 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/02/53802/1
diff --git a/RELEASE-NOTES-1.21 b/RELEASE-NOTES-1.21
index e7e718d..869dc91 100644
--- a/RELEASE-NOTES-1.21
+++ b/RELEASE-NOTES-1.21
@@ -120,6 +120,10 @@
* (bug 45526) Add QUnit assertion helper "QUnit.assert.htmlEqual" for asserting
structual equality of HTML (ignoring insignificant differences like
quotmarks, order and whitespace in the attribute list).
+* A user preference is added to display the parser profiling data, formerly
+ only available in the "NewPP limit report" HTML comment, at the bottom of
+ page previews.
+* Added ParserPrepareLimitReport hook, deprecated ParserLimitReport hook.
=== Bug fixes in 1.21 ===
* (bug 40353) SpecialDoubleRedirect should support interwiki redirects.
diff --git a/docs/hooks.txt b/docs/hooks.txt
index 3670cbe..16d5e89 100644
--- a/docs/hooks.txt
+++ b/docs/hooks.txt
@@ -1754,7 +1754,8 @@
$parser: Parser object
$varCache: variable cache (array)
-'ParserLimitReport': Called at the end of Parser:parse() when the parser will
+'ParserLimitReport': DEPRECATED, use ParserPrepareLimitReport instead.
+Called at the end of Parser:parse() when the parser will
include comments about size of the text parsed.
$parser: Parser object
$limitReport: text that will be included (without comment tags)
@@ -1766,6 +1767,12 @@
&$params: 2-D array of parameters
$parser: Parser object that called the hook
+'ParserPrepareLimitReport': Called at the end of Parser:parse() when the
parser will
+include comments about size of the text parsed. Hooks should use
+$output->setLimitReportData() to populate data.
+$parser: Parser object
+$output: ParserOutput object
+
'ParserSectionCreate': Called each time the parser creates a document section
from wikitext. Use this to apply per-section modifications to HTML (like
wrapping the section in a DIV). Caveat: DIVs are valid wikitext, and a DIV
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 5d051d7..5a67db8 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -3666,6 +3666,7 @@
'numberheadings' => 0,
'previewonfirst' => 0,
'previewontop' => 1,
+ 'previewlimitreport' => 0,
'quickbar' => 5,
'rcdays' => 7,
'rclimit' => 50,
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 8b2dbb5..a069118 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -2816,6 +2816,58 @@
call_user_func_array( 'wfMessage', $copywarnMsg
)->plain() . "\n</div>";
}
+ /**
+ * Get the Limit report for page previews
+ *
+ * @since 1.21
+ * @param User $user User for which the preview is being made
+ * @param ParserOutput $output ParserOutput object from the parse
+ * @return string HTML
+ */
+ public static function getPreviewLimitReport( User $user, ParserOutput
$output ) {
+ if ( !$user->getOption( 'previewlimitreport' ) ) {
+ return '';
+ }
+
+ $limitReport = wfMessage( 'limitreport-top' )->parse() . "\n";
+
+ foreach ( $output->getLimitReportData() as $key => $data ) {
+ list( $value, $opts ) = $data;
+
+ $label = wfMessage( $key )->text();
+
+ $msg = wfMessage( "$key-value" );
+ if ( $msg->isDisabled() ){
+ $msg = new RawMessage( '$1' );
+ }
+ if ( in_array( 'multiline', $opts ) ) {
+ $valkey = 'limitreport-multiline-row';
+ $val = '';
+ $m = wfMessage( "$key-top" );
+ if ( !$m->isDisabled() ) {
+ $val .= $m->text() . "\n";
+ }
+ foreach ( $value as $v ) {
+ $m = clone( $msg );
+ $val .= $m->params( $v )->text() . "\n";
+ }
+ $m = wfMessage( "$key-bottom" );
+ if ( !$m->isDisabled() ) {
+ $val .= $m->text();
+ }
+ } else {
+ $valkey = 'limitreport-row';
+ $val = $msg->params( $value )->text() . "\n";
+ }
+
+ $limitReport .= wfMessage( $valkey, $key, $label, $val
)->parse() . "\n";
+ }
+
+ $limitReport .= wfMessage( 'limitreport-bottom' )->parse();
+
+ return $limitReport;
+ }
+
protected function showStandardInputs( &$tabindex = 2 ) {
global $wgOut;
$wgOut->addHTML( "<div class='editOptions'>\n" );
@@ -3102,6 +3154,7 @@
$attribs = array( 'lang' => $pageLang->getCode(), 'dir' =>
$pageLang->getDir(),
'class' => 'mw-content-' . $pageLang->getDir() );
$previewHTML = Html::rawElement( 'div', $attribs, $previewHTML
);
+ $previewHTML .= self::getPreviewLimitReport( $wgUser,
$this->mParserOutput );
wfProfileOut( __METHOD__ );
return $previewhead . $previewHTML .
$this->previewTextAfterContent;
diff --git a/includes/Preferences.php b/includes/Preferences.php
index 56dba05..7098886 100644
--- a/includes/Preferences.php
+++ b/includes/Preferences.php
@@ -774,6 +774,11 @@
'section' => 'editing/advancedediting',
'label-message' => 'tog-previewonfirst',
);
+ $defaultPreferences['previewlimitreport'] = array(
+ 'type' => 'toggle',
+ 'section' => 'editing/advancedediting',
+ 'label-message' => 'tog-previewlimitreport',
+ );
if ( $wgAllowUserCssPrefs ) {
$defaultPreferences['editsection'] = array(
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index 7f21449..6ea2530 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -361,6 +361,10 @@
$this->startParse( $title, $options, self::OT_HTML, $clearState
);
+ if ( $this->mOptions->getEnableLimitReport() ) {
+ $this->mOutput->resetParseStartTime();
+ }
+
# Remove the strip marker tag prefix from the input, if present.
if ( $clearState ) {
$text = str_replace( $this->mUniqPrefix, '', $text );
@@ -490,17 +494,79 @@
# Information on include size limits, for the benefit of users
who try to skirt them
if ( $this->mOptions->getEnableLimitReport() ) {
$max = $this->mOptions->getMaxIncludeSize();
- $PFreport = "Expensive parser function count:
{$this->mExpensiveFunctionCount}/{$this->mOptions->getExpensiveParserFunctionLimit()}\n";
- $limitReport =
- "NewPP limit report\n" .
- "Preprocessor visited node count:
{$this->mPPNodeCount}/{$this->mOptions->getMaxPPNodeCount()}\n" .
- "Preprocessor generated node count: " .
-
"{$this->mGeneratedPPNodeCount}/{$this->mOptions->getMaxGeneratedPPNodeCount()}\n"
.
- "Post-expand include size:
{$this->mIncludeSizes['post-expand']}/$max bytes\n" .
- "Template argument size:
{$this->mIncludeSizes['arg']}/$max bytes\n" .
- "Highest expansion depth:
{$this->mHighestExpansionDepth}/{$this->mOptions->getMaxPPExpandDepth()}\n" .
- $PFreport;
+
+ $cpuTime = $this->mOutput->getTimeSinceStart( 'cpu' );
+ if ( $cpuTime !== null ) {
+ $this->mOutput->setLimitReportData(
'limitreport-cputime',
+ sprintf( "%.3f", $cpuTime )
+ );
+ }
+
+ $wallTime = $this->mOutput->getTimeSinceStart( 'wall' );
+ $this->mOutput->setLimitReportData(
'limitreport-walltime',
+ sprintf( "%.3f", $wallTime )
+ );
+
+ $this->mOutput->setLimitReportData(
'limitreport-ppvisitednodes',
+ array( $this->mPPNodeCount,
$this->mOptions->getMaxPPNodeCount() )
+ );
+ $this->mOutput->setLimitReportData(
'limitreport-ppgeneratednodes',
+ array( $this->mGeneratedPPNodeCount,
$this->mOptions->getMaxGeneratedPPNodeCount() )
+ );
+ $this->mOutput->setLimitReportData(
'limitreport-postexpandincludesize',
+ array( $this->mIncludeSizes['post-expand'],
$max )
+ );
+ $this->mOutput->setLimitReportData(
'limitreport-templateargumentsize',
+ array( $this->mIncludeSizes['arg'], $max )
+ );
+ $this->mOutput->setLimitReportData(
'limitreport-expansiondepth',
+ array( $this->mHighestExpansionDepth,
$this->mOptions->getMaxPPExpandDepth() )
+ );
+ $this->mOutput->setLimitReportData(
'limitreport-expenseivefunctioncount',
+ array( $this->mExpensiveFunctionCount,
$this->mOptions->getExpensiveParserFunctionLimit() )
+ );
+ wfRunHooks( 'ParserPrepareLimitReport', array( $this,
$this->mOutput ) );
+
+ $limitReport = "NewPP limit report\n";
+ foreach ( $this->mOutput->getLimitReportData() as $key
=> $data ) {
+ list( $value, $opts ) = $data;
+ if ( in_array( 'noNewPP', $opts ) ) {
+ continue;
+ }
+
+ $label = wfMessage( $key )->inLanguage( 'en'
)->useDatabase( false )->text();
+ $limitReport .= "$label:";
+
+ $msg = wfMessage( array( "$key-value-text",
"$key-value" ) )
+ ->inLanguage( 'en' )->useDatabase(
false );
+ if ( $msg->isDisabled() ){
+ $msg = new RawMessage( '$1' );
+ }
+ if ( in_array( 'multiline', $opts ) ) {
+ $limitReport .= "\n";
+ $m = wfMessage( "$key-top-text" )
+ ->inLanguage( 'en'
)->useDatabase( false );
+ if ( !$m->isDisabled() ) {
+ $limitReport .= "
{$m->text()}\n";
+ }
+ foreach ( $value as $v ) {
+ $m = clone( $msg );
+ $limitReport .= "
{$m->params( $v )->text()}\n";
+ }
+ $m = wfMessage( "$key-bottom-text" )
+ ->inLanguage( 'en'
)->useDatabase( false );
+ if ( !$m->isDisabled() ) {
+ $limitReport .= "
{$m->text()}\n";
+ }
+ } else {
+ $limitReport .= " {$msg->params( $value
)->text()}\n";
+ }
+ }
+ // Since we're not really outputting HTML, decode the
entities and
+ // then re-encode the things that need hiding inside
HTML comments.
+ $limitReport = htmlspecialchars_decode( $limitReport );
wfRunHooks( 'ParserLimitReport', array( $this,
&$limitReport ) );
+ $limitReport = str_replace( array( '-', '&' ), array(
'‐', '&' ), $limitReport );
$text .= "\n<!-- \n$limitReport-->\n";
if ( $this->mGeneratedPPNodeCount >
$this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) {
diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php
index db649f1..ac88cdd 100644
--- a/includes/parser/ParserOutput.php
+++ b/includes/parser/ParserOutput.php
@@ -52,6 +52,8 @@
private $mAccessedOptions = array(); # List of ParserOptions
(stored in the keys)
private $mSecondaryDataUpdates = array(); # List of DataUpdate,
used to save info from the page somewhere else.
private $mExtensionData = array(); # extra data used by
extensions
+ private $mLimitReportData = array(); # Parser limit report data
+ private $mParseStartTime = array(); # Timestamps for
getTimeSinceStart()
const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)"
section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
@@ -120,6 +122,7 @@
function getIndexPolicy() { return $this->mIndexPolicy; }
function getTOCHTML() { return $this->mTOCHTML; }
function getTimestamp() { return $this->mTimestamp; }
+ function getLimitReportData() { return $this->mLimitReportData; }
function setText( $text ) { return wfSetVar( $this->mText,
$text ); }
function setLanguageLinks( $ll ) { return wfSetVar(
$this->mLanguageLinks, $ll ); }
@@ -544,4 +547,78 @@
return null;
}
+ private static function getTimes( $clock = null ) {
+ $ret = array();
+ if ( !$clock || $clock === 'wall' ) {
+ $ret['wall'] = microtime( true );
+ }
+ if ( ( !$clock || $clock === 'cpu' ) && function_exists(
'getrusage' ) ) {
+ $ru = getrusage();
+ $ret['cpu'] = $ru['ru_utime.tv_sec'] +
$ru['ru_utime.tv_usec'] / 1e6;
+ $ret['cpu'] += $ru['ru_stime.tv_sec'] +
$ru['ru_stime.tv_usec'] / 1e6;
+ }
+ return $ret;
+ }
+
+ /**
+ * Resets the parse start timestamps for future calls to
getTimeSinceStart()
+ * @since 1.21
+ */
+ function resetParseStartTime() {
+ $this->mParseStartTime = self::getTimes();
+ }
+
+ /**
+ * Returns the time since resetParseStartTime() was last called
+ *
+ * Clocks available are:
+ * wall: Wall clock time
+ * cpu: CPU time (requires getrusage)
+ *
+ * @since 1.21
+ * @param string $clock
+ * @return float|null
+ */
+ function getTimeSinceStart( $clock ) {
+ if ( !isset( $this->mParseStartTime[$clock] ) ) {
+ return null;
+ }
+
+ $end = self::getTimes( $clock );
+ return $end[$clock] - $this->mParseStartTime[$clock];
+ }
+
+ /**
+ * Sets parser limit report data for a key
+ *
+ * The key is used as the prefix for various messages used for
formatting:
+ * $key: The label for the field in the limit report
+ * $key-value: Message used to format the value. If missing, uses "$1".
+ * $key-top: Used in multi-line mode.
+ * $key-bottom: Used in multi-line mode.
+ * $key-value-text: Message used to format the value in the "NewPP
limit
+ * report" HTML comment. If missing, uses $key-format.
+ * $key-top-text: Used in multi-line mode for NewPP report. No
fallback.
+ * $key-bottom-text: Used in multi-line mode for NewPP report. No
fallback.
+ *
+ * Options are:
+ * multiline: Enable multiline mode. In multi-line mode, $value must
be an
+ * array. First the -top message is output, then the -value
+ * message is used for each element in the $value array, and then
the
+ * -bottom message is output.
+ * noNewPP: Exclude this from the "NewPP limit report" HTML comment.
+ *
+ * Note that all values are interpreted as wikitext, and so should be
+ * encoded with htmlspecialchars() as necessary, but should avoid
complex
+ * HTML for sanity of display in the "NewPP limit report" comment.
+ *
+ * @since 1.21
+ * @param string $key Message key
+ * @param mixed $value Appropriate for Message::params(), or an array
of such.
+ * @param array $opts Options
+ * @return null
+ */
+ function setLimitReportData( $key, $value, $opts = array() ) {
+ $this->mLimitReportData[$key] = array( $value, $opts );
+ }
}
diff --git a/languages/messages/MessagesEn.php
b/languages/messages/MessagesEn.php
index e07da4ff..2abf6b6 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -659,6 +659,7 @@
'tog-minordefault' => 'Mark all edits minor by default',
'tog-previewontop' => 'Show preview before edit box',
'tog-previewonfirst' => 'Show preview on first edit',
+'tog-previewlimitreport' => 'Show profiling information after the page
preview',
'tog-nocache' => 'Disable browser page caching',
'tog-enotifwatchlistpages' => 'Email me when a page or file on my watchlist
is changed',
'tog-enotifusertalkpages' => 'Email me when my user talk page is changed',
@@ -5005,4 +5006,28 @@
#Rotation
'rotate-comment' => 'Image rotated by $1 {{PLURAL:$1|degree|degrees}}
clockwise',
+# Limit report
+'limitreport-top' => '<table class="preview-limit-report wikitable
mw-collapsible mw-collapsed">
+<tr><th colspan="2">Parser profiling data</th></tr>',
+'limitreport-row' => '<tr class="limitreport-row
$1"><th>$2</th><td>$3</td></tr>',
+'limitreport-multiline-row' => '<tr class="limitreport-multiline-row $1"><th
colspan="2">$2</th></tr>
+<tr class="limitreport-multiline-row $1"><td colspan="2">$3</td></tr>',
+'limitreport-bottom' => '</table>',
+'limitreport-cputime' => 'CPU time usage',
+'limitreport-cputime-value' => '$1s',
+'limitreport-walltime' => 'Real time usage',
+'limitreport-walltime-value' => '$1s',
+'limitreport-ppvisitednodes' => 'Preprocessor visited node count',
+'limitreport-ppvisitednodes-value' => '$1/$2',
+'limitreport-ppgeneratednodes' => 'Preprocessor generated node count',
+'limitreport-ppgeneratednodes-value' => '$1/$2',
+'limitreport-postexpandincludesize' => 'Post-expand include size',
+'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
+'limitreport-templateargumentsize' => 'Template argument size',
+'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-expansiondepth' => 'Highest expansion depth',
+'limitreport-expansiondepth-value' => '$1/$2',
+'limitreport-expenseivefunctioncount' => 'Expensive parser function count',
+'limitreport-expenseivefunctioncount-value' => '$1/$2',
+
);
diff --git a/languages/messages/MessagesQqq.php
b/languages/messages/MessagesQqq.php
index c39e9eb..bd9a72e 100644
--- a/languages/messages/MessagesQqq.php
+++ b/languages/messages/MessagesQqq.php
@@ -175,6 +175,7 @@
'tog-minordefault' => "[[Special:Preferences]], tab 'Edit'. Offers user to
mark all edits minor by default. {{Gender}}",
'tog-previewontop' => 'Toggle option used in [[Special:Preferences]].
{{Gender}}',
'tog-previewonfirst' => 'Toggle option used in [[Special:Preferences]].
{{Gender}}',
+'tog-previewlimitreport' => 'Toggle option used in [[Special:Preferences]].
{{Gender}}',
'tog-nocache' => "[[Special:Preferences]], tab 'Misc.'. Offers the user the
option of disabling caching of pages in the browser. {{Gender}}",
'tog-enotifwatchlistpages' => 'Option in [[Special:Preferences]] >
{{int:prefs-personal}} > {{int:email}}. {{Gender}}',
'tog-enotifusertalkpages' => 'Option in [[Special:Preferences]] >
{{int:prefs-personal}} > {{int:email}}. {{Gender}}',
@@ -8789,4 +8790,46 @@
# Image rotation
'rotate-comment' => 'Edit summary for the act of rotating an image.',
+# Limit report
+'limitreport-top' => 'HTML for the start of the limit report table.',
+'limitreport-row' => 'HTML for non-multiline rows in the limit report table.
+* $1 is the key
+* $2 is the label for the row
+* $3 is the value wikitext',
+'limitreport-multiline-row' => 'HTML for multiline rows in the limit report
table.
+* $1 is the key
+* $2 is the label for the row
+* $3 is the value wikitext',
+'limitreport-bottom' => 'HTML for the end of the limit report table.',
+'limitreport-cputime' => 'Label for the "CPU time usage" row in the limit
report table',
+'limitreport-cputime-value' => 'Format for the "CPU time usage" value in the
limit report table.
+* $1 is the time usage in seconds',
+'limitreport-walltime' => 'Label for the "Real time usage" row in the limit
report table',
+'limitreport-walltime-value' => 'Format for the "Real time usage" value in the
limit report table.
+* $1 is the time usage in seconds',
+'limitreport-ppvisitednodes' => 'Label for the "Preprocessor visited node
count" row in the limit report table',
+'limitreport-ppvisitednodes-value' => 'Format for the "Preprocessor visited
node count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+'limitreport-ppgeneratednodes' => 'Label for the "Preprocessor generated node
count" row in the limit report table',
+'limitreport-ppgeneratednodes-value' => 'Format for the "Preprocessor
generated node count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+'limitreport-postexpandincludesize' => 'Label for the "Post-expand include
size" row in the limit report table',
+'limitreport-postexpandincludesize-value' => 'Format for the "Post-expand
include size" row in the limit report table.
+* $1 is the usage in bytes
+* $2 is the maximum',
+'limitreport-templateargumentsize' => 'Label for the "Template argument size"
row in the limit report table',
+'limitreport-templateargumentsize-value' => 'Format for the "Template argument
size" row in the limit report table.
+* $1 is the usage in bytes
+* $2 is the maximum',
+'limitreport-expansiondepth' => 'Label for the "Highest expansion depth" row
in the limit report table',
+'limitreport-expansiondepth-value' => 'Format for the "Highest expansion
depth" row in the limit report table.
+* $1 is the depth
+* $2 is the maximum',
+'limitreport-expenseivefunctioncount' => 'Label for the "Expensive parser
function count" row in the limit report table',
+'limitreport-expenseivefunctioncount-value' => 'Format for the "Expensive
parser function count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+
);
diff --git a/maintenance/language/messages.inc
b/maintenance/language/messages.inc
index 7c16df6..ab6fd20 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -48,6 +48,7 @@
'tog-minordefault',
'tog-previewontop',
'tog-previewonfirst',
+ 'tog-previewlimitreport',
'tog-nocache',
'tog-enotifwatchlistpages',
'tog-enotifusertalkpages',
@@ -3855,6 +3856,28 @@
'rotation' => array(
'rotate-comment',
),
+ 'limitreport' => array(
+ 'limitreport-begin',
+ 'limitreport-row',
+ 'limitreport-multiline-row',
+ 'limitreport-end',
+ 'limitreport-cputime',
+ 'limitreport-cputime-value',
+ 'limitreport-walltime',
+ 'limitreport-walltime-value',
+ 'limitreport-ppvisitednodes',
+ 'limitreport-ppvisitednodes-value',
+ 'limitreport-ppgeneratednodes',
+ 'limitreport-ppgeneratednodes-value',
+ 'limitreport-postexpandincludesize',
+ 'limitreport-postexpandincludesize-value',
+ 'limitreport-templateargumentsize',
+ 'limitreport-templateargumentsize-value',
+ 'limitreport-expansiondepth',
+ 'limitreport-expansiondepth-value',
+ 'limitreport-expenseivefunctioncount',
+ 'limitreport-expenseivefunctioncount-value',
+ ),
);
/** Comments for each block */
@@ -4099,4 +4122,5 @@
'duration' => 'Durations',
'cachedspecial' => 'SpecialCachedPage',
'rotation' => 'Image rotation',
+ 'limitreport' => 'Limit report',
);
--
To view, visit https://gerrit.wikimedia.org/r/53802
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie065c7b5a17bbf1aa484d0ae1f3ee0f5d41f8495
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits