jenkins-bot has submitted this change and it was merged.
Change subject: Implemented MessageProvider for valueView experts
......................................................................
Implemented MessageProvider for valueView experts
The MessageProvider returns specified default messages or mediaWiki messages
if the mediaWiki JS object is available. This allows getting rid of hard-coded
mediaWiki dependencies in most valueView experts.
Change-Id: I1100fa38ea3724dca6b8559e8fbadac67a67a1b7
---
M ValueView/ValueView.resources.php
M ValueView/ValueView.tests.qunit.php
M ValueView/resources/jquery.valueview/valueview.Expert.js
A ValueView/resources/jquery.valueview/valueview.MessageProvider.js
M
ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js
M ValueView/resources/jquery.valueview/valueview.experts/experts.EmptyValue.js
M
ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
M
ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateValue.js
M ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js
M ValueView/resources/jquery.valueview/valueview.experts/experts.TimeInput.js
M ValueView/resources/jquery.valueview/valueview.experts/experts.TimeValue.js
M
ValueView/resources/jquery.valueview/valueview.experts/experts.UnsupportedValue.js
A ValueView/tests/qunit/jquery.valueview/valueview.MessageProvider.tests.js
13 files changed, 216 insertions(+), 58 deletions(-)
Approvals:
Tobias Gritschacher: Looks good to me, approved
Daniel Werner: Looks good to me, but someone else must approve
jenkins-bot: Verified
diff --git a/ValueView/ValueView.resources.php
b/ValueView/ValueView.resources.php
index 3f46031..3f3de7f 100644
--- a/ValueView/ValueView.resources.php
+++ b/ValueView/ValueView.resources.php
@@ -99,6 +99,7 @@
),
'dependencies' => array(
'jquery.ui.widget',
+ 'jquery.valueview.MessageProvider',
'jquery.valueview.base',
'jquery.valueview.ViewState',
'jquery.valueview.experts', // because vv deals
with ExpertFactory
@@ -126,6 +127,15 @@
),
),
+ 'jquery.valueview.MessageProvider' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'jquery.valueview/valueview.MessageProvider.js',
+ ),
+ 'dependencies' => array(
+ 'jquery.valueview.base',
+ ),
+ ),
+
'jquery.valueview.BifidExpert' => $moduleTemplate + array(
'scripts' => array(
'jquery.valueview/valueview.BifidExpert.js',
diff --git a/ValueView/ValueView.tests.qunit.php
b/ValueView/ValueView.tests.qunit.php
index 3b4dac0..88ba53e 100644
--- a/ValueView/ValueView.tests.qunit.php
+++ b/ValueView/ValueView.tests.qunit.php
@@ -148,6 +148,15 @@
),
),
+ 'jquery.valueview.MessageProvider.tests' => array(
+ 'scripts' => array(
+
"$bp/jquery.valueview/valueview.MessageProvider.tests.js",
+ ),
+ 'dependencies' => array(
+ 'jquery.valueview.MessageProvider',
+ ),
+ ),
+
'jquery.valueview.experts.stringvalue.tests' => array(
'scripts' => array(
"$bp/jquery.valueview/valueview.experts/experts.StringValue.tests.js",
diff --git a/ValueView/resources/jquery.valueview/valueview.Expert.js
b/ValueView/resources/jquery.valueview/valueview.Expert.js
index c13701d..da531cb 100644
--- a/ValueView/resources/jquery.valueview/valueview.Expert.js
+++ b/ValueView/resources/jquery.valueview/valueview.Expert.js
@@ -103,6 +103,10 @@
this._options = $.extend( ( !this._options ) ? {} :
this._options, options || {} );
+ var defaultMessages = this._options.messages || {},
+ mediaWiki = this._options.mediaWiki || null;
+ this._messageProvider = new vv.MessageProvider(
defaultMessages, mediaWiki );
+
this._init();
};
@@ -139,6 +143,12 @@
* @type Object
*/
_options: null,
+
+ /**
+ * Message provider used to fetch messages from mediaWiki if
available.
+ * @type {jQuery.valueview.MessageProvider}
+ */
+ _messageProvider: null,
/**
* Will be called initially for new expert instances.
@@ -334,30 +344,6 @@
* @abstract
*/
draw: dv.util.abstractMember,
-
- /**
- * Tries to get a message via mediaWiki (if set in the options)
or from the default options.
- * @since 0.1
- *
- * @param {string} key
- * @param {string[]} [params] Message parameters (forwarded to
mediaWiki messages only).
- * @return {string|null}
- *
- * @todo Implement a MessageFetcher composited into this object
instead of this method.
- */
- _getMessage: function( key, params ) {
- params = params || [];
-
- if( this._options.mediaWiki ) {
- return this._options.mediaWiki.msg( key, params
);
- }
-
- if( this._options && this._options.messages &&
this._options.messages[key] ) {
- return this._options.messages[key];
- }
-
- return null;
- },
/**
* Will set the focus if there is some focusable input elements.
diff --git a/ValueView/resources/jquery.valueview/valueview.MessageProvider.js
b/ValueView/resources/jquery.valueview/valueview.MessageProvider.js
new file mode 100644
index 0000000..f107eff
--- /dev/null
+++ b/ValueView/resources/jquery.valueview/valueview.MessageProvider.js
@@ -0,0 +1,64 @@
+/**
+ * @file
+ * @ingroup ValueView
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+jQuery.valueview.MessageProvider = ( function( $ ) {
+ 'use strict';
+
+ /**
+ * Providing messages using specified default messages or MediaWiki
messages if available.
+ *
+ * @param {Object} defaultMessages Messages to use if mediaWiki is not
available. These should
+ * be keyed by the message key that is used for the
corresponding MediaWiki
+ * message. The value is the replacement message to use
when the MessageProvider
+ * is not loaded within MediaWiki context.
+ * @param {Object} [mediaWiki] mediaWiki JavaScript object.
+ * @constructor
+ */
+ function MessageProvider( defaultMessages, mediaWiki ) {
+ this._defaultMessages = defaultMessages;
+ this._mw = mediaWiki;
+ }
+
+ $.extend( MessageProvider.prototype, {
+
+ /**
+ * Default messages to use if the MessageProvider is not loaded
within MediaWiki context.
+ * @type {Object}
+ */
+ _defaultMessages: null,
+
+ /**
+ * Reference to the mediaWiki JavaScript object.
+ * @type {Object}
+ */
+ _mw: null,
+
+ /**
+ * Tries to get a message via mediaWiki (if set) or from the
default options.
+ *
+ * @param {string} key
+ * @param {string[]} [params] Message parameters (forwarded to
mediaWiki messages only).
+ * @return {string|null}
+ */
+ getMessage: function( key, params ) {
+ params = params || [];
+
+ if( this._mw ) {
+ return this._mw.msg( key, params );
+ }
+
+ if( this._defaultMessages && this._defaultMessages &&
this._defaultMessages[key] ) {
+ return this._defaultMessages[key];
+ }
+
+ return null;
+ }
+
+ } );
+
+ return MessageProvider;
+
+}( jQuery ) );
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js
index 7c70e3d..2c46ca7 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.CommonsMediaType.js
@@ -63,7 +63,12 @@
* @see jQuery.valueview.BifidExpert._staticExpertOptions
*/
_staticExpertOptions: {
- domBuilder: function( currentRawValue, viewState ) {
+ /**
+ * @param {time.Time|null} currentRawValue
+ * @param {jQuery.valueview.ViewState} viewState
+ * @param {jQuery.valueview.MessageProvider}
messageProvider
+ */
+ domBuilder: function( currentRawValue, viewState,
messageProvider ) {
return $( '<a/>', {
text: currentRawValue,
href: commonsUrl( currentRawValue )
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.EmptyValue.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.EmptyValue.js
index 7f1438a..ca9079b 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.EmptyValue.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.EmptyValue.js
@@ -4,7 +4,7 @@
* @licence GNU GPL v2+
* @author Daniel Werner < [email protected] >
*/
-( function( mw, dv, vp, $, vv ) {
+( function( dv, vp, $, vv ) {
'use strict';
var PARENT = vv.Expert;
@@ -19,6 +19,15 @@
* @extends jQuery.valueview.Expert
*/
vv.experts.EmptyValue = vv.expert( 'emptyvalue', {
+ /**
+ * Options.
+ * @type {Object}
+ */
+ _options: {
+ messages: {
+ 'valueview-expert-emptyvalue-empty': 'empty'
+ }
+ },
/**
* @see jQuery.valueview.Expert.destroy
@@ -47,8 +56,9 @@
*/
draw: function() {
this.$viewPort.text(
- mw.msg( 'valueview-expert-emptyvalue-empty' ) );
+ this._messageProvider.getMessage(
'valueview-expert-emptyvalue-empty' )
+ );
}
} );
-}( mediaWiki, dataValues, valueParsers, jQuery, jQuery.valueview ) );
+}( dataValues, valueParsers, jQuery, jQuery.valueview ) );
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
index ce41f16..1474c86 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateInput.js
@@ -6,8 +6,7 @@
* @author H. Snater < [email protected] >
* @author Daniel Werner < [email protected] >
*/
-// TODO: Remove mediaWiki dependency
-( function( dv, vp, $, vv, globeCoordinate, mw ) {
+( function( dv, vp, $, vv, globeCoordinate ) {
'use strict';
var GlobeCoordinate = globeCoordinate.GlobeCoordinate,
@@ -24,6 +23,17 @@
* @extends jQuery.valueview.Expert
*/
vv.experts.GlobeCoordinateInput = vv.expert( 'globecoordinateinput',
PARENT, {
+ /**
+ * Options.
+ * @type {Object}
+ */
+ _options: {
+ messages: {
+ 'valueview-expert-advancedadjustments':
'advanced adjustments',
+
'valueview-expert-globecoordinateinput-precision': 'Precision'
+ }
+ },
+
/**
* The the input element's node.
* @type {jQuery}
@@ -63,15 +73,14 @@
_init: function() {
var self = this,
notifier = this._viewNotifier,
+ precisionMsgKey =
'valueview-expert-globecoordinateinput-precision',
precisionValues = [],
listrotatorEvents = 'listrotatorauto
listrotatorselected'
.replace( /(\w+)/g, '$1.' +
this.uiBaseClass );
this.$precisionContainer = $( '<div/>' )
.addClass( this.uiBaseClass + '-precisioncontainer' )
- .append(
- $( '<div/>' ).text( mw.msg(
'valueview-expert-globecoordinateinput-precision' ) )
- );
+ .append( $( '<div/>' ).text(
this._messageProvider.getMessage( precisionMsgKey ) ) );
$.each( globeCoordinateSettings.precisions, function(
i, precisionDefinition ) {
var label = globeCoordinate.precisionText(
precisionDefinition.level );
@@ -110,7 +119,7 @@
var $toggler = $( '<a/>' )
.addClass( this.uiBaseClass + '-advancedtoggler' )
- .text( mw.msg( 'valueview-expert-advancedadjustments' )
);
+ .text( this._messageProvider.getMessage(
'valueview-expert-advancedadjustments' ) );
this.$input = $( '<input/>', {
type: 'text',
@@ -335,4 +344,4 @@
return precision;
}
-}( dataValues, valueParsers, jQuery, jQuery.valueview, globeCoordinate,
mediaWiki ) );
+}( dataValues, valueParsers, jQuery, jQuery.valueview, globeCoordinate ) );
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateValue.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateValue.js
index 34305f1..0f871ad 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateValue.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.GlobeCoordinateValue.js
@@ -42,8 +42,9 @@
/**
* @param {globeCoordinate.GlobeCoordinate|string|null}
currentRawValue
* @param {jQuery.valueview.ViewState} viewState
+ * @param {jQuery.valueview.MessageProvider}
messageProvider
*/
- domBuilder: function( currentRawValue, viewState ) {
+ domBuilder: function( currentRawValue, viewState,
messageProvider ) {
if( currentRawValue instanceof GlobeCoordinate
) {
currentRawValue =
currentRawValue.degreeText();
}
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js
index fd9d068..3055678 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.StaticDom.js
@@ -23,8 +23,10 @@
* @extends jQuery.valueview.Expert
*
* @option domBuilder {Function} A callback function called whenever
the DOM for displaying the
- * current raw value is required. First parameter of the
callback is the current raw
- * value of the expert, second parameter is the expert's
related ViewState object.
+ * current raw value is required. Parameters:
+ * (1) Expert's current raw value.
+ * (2) Expert's related ViewState object.
+ * (3) The message provider associated with this StaticDom
instance.
*
* @option baseExpert {Function} Constructor of an expert whose
"parser" and "rawValueCompare"
* functions will be borrowed. This is required because this
expert doesn't need to
@@ -90,7 +92,11 @@
*/
draw: function() {
// Build DOM as specified by callback:
- var $customDom = this._options.domBuilder(
this.rawValue(), this._viewState );
+ var $customDom = this._options.domBuilder(
+ this.rawValue(),
+ this._viewState,
+ this._messageProvider
+ );
this.$viewPort.empty().append( $customDom );
}
} );
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeInput.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeInput.js
index 16bb825..06d0dc2 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeInput.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeInput.js
@@ -99,7 +99,9 @@
this.$precisionContainer = $( '<div/>' )
.addClass( this.uiBaseClass + '-precisioncontainer' )
.append(
- $( '<div/>' ).text( this._getMessage(
'valueview-expert-timeinput-precision' ) )
+ $( '<div/>' ).text(
+ this._messageProvider.getMessage(
'valueview-expert-timeinput-precision' )
+ )
);
var precisionValues = [];
@@ -142,12 +144,14 @@
this.$calendarContainer = $( '<div/>' )
.addClass( this.uiBaseClass + '-calendarcontainer' )
.append(
- $( '<div/>' ).text( this._getMessage(
'valueview-expert-timeinput-calendar' ) )
+ $( '<div/>' ).text(
+ this._messageProvider.getMessage(
'valueview-expert-timeinput-calendar' )
+ )
);
var calendarValues = [];
$.each( timeSettings.calendarnames, function(
calendarKey, calendarTerms ) {
- var label = self._getMessage(
+ var label = self._messageProvider.getMessage(
'valueview-expert-timevalue-calendar-'
+ calendarTerms[0].toLowerCase()
);
calendarValues.push( { value: calendarTerms[0],
label: label } );
@@ -182,7 +186,7 @@
var $toggler = $( '<a/>' )
.addClass( this.uiBaseClass + '-advancedtoggler' )
- .text( this._getMessage(
'valueview-expert-advancedadjustments' ) );
+ .text( this._messageProvider.getMessage(
'valueview-expert-advancedadjustments' ) );
this.$calendarhint = $( '<div/>' )
.addClass( this.uiBaseClass + '-calendarhint' )
@@ -324,7 +328,7 @@
var msg = null;
if( value ) {
- msg = this._getMessage(
+ msg = this._messageProvider.getMessage(
'valueview-expert-timeinput-calendarhint-' + value.calendar().toLowerCase()
);
}
@@ -343,7 +347,7 @@
this.$calendarhint.children( '.' +
this.uiBaseClass + '-calendarhint-message' )
.text( msg );
- msg = this._getMessage(
+ msg = this._messageProvider.getMessage(
'valueview-expert-timeinput-calendarhint-switch-' + otherCalendar.toLowerCase()
);
if( msg ) {
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeValue.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeValue.js
index 3404d20..ae2b620 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeValue.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.TimeValue.js
@@ -5,8 +5,7 @@
* @author Daniel Werner < [email protected] >
* @author H. Snater < [email protected] >
*/
-// TODO: Get rid of mw dependency
-( function( dv, vp, $, vv, mw ) {
+( function( dv, vp, $, vv ) {
'use strict';
var PARENT = vv.BifidExpert,
@@ -43,8 +42,9 @@
/**
* @param {time.Time|null} currentRawValue
* @param {jQuery.valueview.ViewState} viewState
+ * @param {jQuery.valueview.MessageProvider}
messageProvider
*/
- domBuilder: function( currentRawValue, viewState ) {
+ domBuilder: function( currentRawValue, viewState,
messageProvider ) {
var $node = $( '<span/>' );
if( !currentRawValue ) {
@@ -59,7 +59,11 @@
// multiple calendars have been in use or if
the time value features a calendar that
// is uncommon for the specified time:
// TODO: This needs to be shaped more generic
instead of focusing on Gregorian/Julian calendar.
- var year = currentRawValue.year();
+ var year = currentRawValue.year(),
+ calendarKey =
currentRawValue.calendar().toLowerCase(),
+ calendarText =
messageProvider.getMessage(
+
'valueview-expert-timevalue-calendar-' + calendarKey
+ );
if(
currentRawValue.precision() > 10
@@ -68,15 +72,13 @@
|| year <= 1581 &&
currentRawValue.calendar() === 'Gregorian'
|| year >= 1930 &&
currentRawValue.calendar() === 'Julian'
)
+ && calendarText
) {
- var key =
currentRawValue.calendar().toLowerCase(),
- calendarText = mw.msg(
'valueview-expert-timevalue-calendar-' + key );
-
$node
- .append( $( '<span/>' ).text(
currentRawValue.text( { format: dateFormat }) ) )
+ .append( $( '<span/>' ).text(
currentRawValue.text( { format: dateFormat } ) ) )
.append( $( '<sup/>' ).text(
calendarText ) );
} else {
- $node.text( currentRawValue.text( {
format: dateFormat }) );
+ $node.text( currentRawValue.text( {
format: dateFormat } ) );
}
return $node;
diff --git
a/ValueView/resources/jquery.valueview/valueview.experts/experts.UnsupportedValue.js
b/ValueView/resources/jquery.valueview/valueview.experts/experts.UnsupportedValue.js
index 264ee9e..e27bdd2 100644
---
a/ValueView/resources/jquery.valueview/valueview.experts/experts.UnsupportedValue.js
+++
b/ValueView/resources/jquery.valueview/valueview.experts/experts.UnsupportedValue.js
@@ -4,7 +4,7 @@
* @licence GNU GPL v2+
* @author Daniel Werner < [email protected] >
*/
-( function( mw, dv, dt, vp, $, vv ) {
+( function( dv, dt, vp, $, vv ) {
'use strict';
var PARENT = vv.Expert;
@@ -19,6 +19,19 @@
* @extends jQuery.valueview.Expert
*/
vv.experts.UnsupportedValue = vv.expert( 'unsupportedvalue', {
+ /**
+ * Options.
+ * @type {Object}
+ */
+ _options: {
+ messages: {
+
'valueview-expert-unsupportedvalue-unsupporteddatatype':
+ 'Handling of this value is not yet
supported.',
+
'valueview-expert-unsupportedvalue-unsupporteddatavalue':
+ 'Handling of values for this data type
is not yet supported.'
+ }
+ },
+
/**
* The current value.
* @type dv.DataValue|null
@@ -60,7 +73,7 @@
if( unsupportedIndicator instanceof dt.DataType ) {
// no expert for values of that data type or
the data type's data value type
- unsupportedMsg = mw.msg(
+ unsupportedMsg =
this._messageProvider.getMessage(
'valueview-expert-unsupportedvalue-unsupporteddatatype',
unsupportedIndicator.getLabel()
);
@@ -69,7 +82,7 @@
}
else if( unsupportedIndicator instanceof dv.DataValue )
{
// no expert for the value's value type
- unsupportedMsg = mw.msg(
+ unsupportedMsg =
this._messageProvider.getMessage(
'valueview-expert-unsupportedvalue-unsupporteddatavalue',
unsupportedIndicator.getType()
);
@@ -84,4 +97,4 @@
}
} );
-}( mediaWiki, dataValues, dataTypes, valueParsers, jQuery, jQuery.valueview )
);
+}( dataValues, dataTypes, valueParsers, jQuery, jQuery.valueview ) );
diff --git
a/ValueView/tests/qunit/jquery.valueview/valueview.MessageProvider.tests.js
b/ValueView/tests/qunit/jquery.valueview/valueview.MessageProvider.tests.js
new file mode 100644
index 0000000..ff286e6
--- /dev/null
+++ b/ValueView/tests/qunit/jquery.valueview/valueview.MessageProvider.tests.js
@@ -0,0 +1,39 @@
+/**
+ * @since 0.1
+ * @ingroup ValueView
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+
+( function( $, QUnit, valueview, mw ) {
+ 'use strict';
+
+ QUnit.module( 'jquery.valueview.MessageProvider' );
+
+ QUnit.test( 'Basic message management', function( assert ) {
+ var messages = {
+ 'messageProviderTestMessage1': 'message1',
+ 'messageProviderTestMessage2': 'message2'
+ },
+ messageProvider = new valueview.MessageProvider(
messages );
+
+ assert.equal(
+ messageProvider.getMessage(
'messageProviderTestMessage2' ),
+ 'message2',
+ 'Fetched message out of mediaWiki context.'
+ );
+
+ if( typeof mw !== 'undefined' && mw.msg ) {
+ messageProvider = new valueview.MessageProvider(
messages, mw );
+
+ assert.equal(
+ messageProvider.getMessage(
'messageProviderTestMessage2' ),
+ '<messageProviderTestMessage2>',
+ 'Fetched message from mediaWiki context.'
+ );
+ }
+
+ } );
+
+}( jQuery, QUnit, jQuery.valueview, mediaWiki ) );
--
To view, visit https://gerrit.wikimedia.org/r/70611
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I1100fa38ea3724dca6b8559e8fbadac67a67a1b7
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Daniel Werner <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits