jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/343953 )
Change subject: Custom campaign mixin param handlers
......................................................................
Custom campaign mixin param handlers
Related changes included in this commit:
- In mixin configurations, change the 'module' property to 'subscribingModule'.
- Also in mixin configurations, add the 'customAdminUIControlsModule' property.
- Tweak eslintrc.json for OOjs.
Bug: T144453
Change-Id: I32496b6dc11bef03853ccc6321eb96620c9ebf0b
---
M extension.json
M includes/CNChoiceDataResourceLoaderModule.php
M resources/infrastructure/campaignManager.js
M special/SpecialCentralNotice.php
4 files changed, 252 insertions(+), 24 deletions(-)
Approvals:
jenkins-bot: Verified
Ejegg: Looks good to me, approved
diff --git a/extension.json b/extension.json
index 294490c..91564e4 100644
--- a/extension.json
+++ b/extension.json
@@ -182,6 +182,7 @@
"remoteExtPath": "CentralNotice",
"dependencies": [
"ext.centralNotice.adminUi",
+ "oojs-ui",
"jquery.ui.dialog",
"jquery.ui.slider",
"jquery.throttle-debounce",
@@ -466,7 +467,7 @@
"CentralNoticeBannerMixins": [],
"CentralNoticeCampaignMixins": {
"bannerHistoryLogger": {
- "module":
"ext.centralNotice.bannerHistoryLogger",
+ "subscribingModule":
"ext.centralNotice.bannerHistoryLogger",
"nameMsg":
"centralnotice-banner-history-logger",
"helpMsg":
"centralnotice-banner-history-logger-help",
"parameters": {
@@ -493,7 +494,7 @@
}
},
"legacySupport": {
- "module": "ext.centralNotice.legacySupport",
+ "subscribingModule":
"ext.centralNotice.legacySupport",
"nameMsg": "centralnotice-legacy-support",
"helpMsg": "centralnotice-legacy-support-help",
"parameters": {
@@ -512,7 +513,7 @@
}
},
"impressionDiet": {
- "module": "ext.centralNotice.impressionDiet",
+ "subscribingModule":
"ext.centralNotice.impressionDiet",
"nameMsg": "centralnotice-impression-diet",
"helpMsg": "centralnotice-impression-diet-help",
"parameters": {
@@ -539,7 +540,7 @@
}
},
"largeBannerLimit": {
- "module": "ext.centralNotice.largeBannerLimit",
+ "subscribingModule":
"ext.centralNotice.largeBannerLimit",
"nameMsg": "centralnotice-large-banner-limit",
"helpMsg":
"centralnotice-large-banner-limit-help",
"parameters": {
diff --git a/includes/CNChoiceDataResourceLoaderModule.php
b/includes/CNChoiceDataResourceLoaderModule.php
index 763d40b..551b5f4 100644
--- a/includes/CNChoiceDataResourceLoaderModule.php
+++ b/includes/CNChoiceDataResourceLoaderModule.php
@@ -143,13 +143,13 @@
foreach ( $choices as $choice ) {
foreach ( $choice['mixins'] as $mixinName =>
$mixinParams ) {
- if (
!$wgCentralNoticeCampaignMixins[$mixinName]['module'] ) {
+ if (
!$wgCentralNoticeCampaignMixins[$mixinName]['subscribingModule'] ) {
throw new MWException(
- "No module for found campaign
mixin {$mixinName}" );
+ "No subscribing module for
found campaign mixin {$mixinName}" );
}
$dependencies[] =
-
$wgCentralNoticeCampaignMixins[$mixinName]['module'];
+
$wgCentralNoticeCampaignMixins[$mixinName]['subscribingModule'];
}
}
diff --git a/resources/infrastructure/campaignManager.js
b/resources/infrastructure/campaignManager.js
index a14855f..90a9fda 100644
--- a/resources/infrastructure/campaignManager.js
+++ b/resources/infrastructure/campaignManager.js
@@ -27,7 +27,163 @@
'ext.centralNotice.adminUi.campaignManager',
'campaignMixinParamControls.mustache'
),
- $mixinCheckboxes, $submitBtn;
+ $form, $submitBtn,
+ MixinCustomUiController, MixinCustomWidget,
+ mixinCustomUiControllerFactory = new OO.Factory(),
+ eventBus = new OO.EventEmitter();
+
+ /* MixinCustomUiController */
+
+ // Note: the following code uses two completely distinct meanings of
+ // "mixin". One is "campaign mixin", bits of JS code that can run in the
+ // browsers of users in specific campaigns. The other is the OOjs
concept
+ // of mixin, that is, a bit of functionality that can be added to a
+ // javascript class. For example, the MixinCustomWidget class provides a
+ // bit of UI for a campaign mixin, and it mixes in, in the OOjs sense,
+ // functionality from OO.ui.mixin.GroupElement.
+
+ /**
+ * Base class for custom campaign mixin UI controllers.
+ * Note: Subclasses are expected to be singletons.
+ *
+ * @abstract
+ * @class MixinCustomUiController
+ * @constructor
+ */
+ MixinCustomUiController = function () {
+
+ // Declare the abstract property here, but don't force it to
null, in case the
+ // subclass decides to set it before calling the constructor.
+ /**
+ * The element of the corresponding MixinCustomWidget.
+ * @abstract
+ * @property {jQuery}
+ */
+ this.$widgetElement = this.$widgetElement || null;
+ };
+
+ OO.initClass( MixinCustomUiController );
+
+ /**
+ * The name of the campaign mixin that this control group sets the
parameters for.
+ *
+ * @abstract
+ * @inheritable
+ * @static
+ * @property {string}
+ */
+ MixinCustomUiController.static.name = null;
+
+ /**
+ * Initialize the controller with the data provided.
+ *
+ * @method
+ * @abstract
+ * @param {Object} data Object in which properties and their values are
mixin
+ * parameter names and values. Format should coordinate with the
format sent
+ * to the client via the 'mixin-param-values' data value on the
checkbox that
+ * enables the mixin.
+ */
+ MixinCustomUiController.prototype.init = null;
+
+ /**
+ * Set the (string-encoded) value of a parameter for the mixin.
+ *
+ * @param {string} name The name of the parameter.
+ * @param {string} value The value (formatted as appropriate for form
submission).
+ */
+ MixinCustomUiController.prototype.setParam = function ( name, value ) {
+ var $input = this.getParamInputEl( name, true );
+
+ $input.val( value );
+ };
+
+ /**
+ * Remove a parameter for the mixin.
+ *
+ * @param {string} name The name of the parameter
+ */
+ MixinCustomUiController.prototype.removeParam = function ( name ) {
+ var $input = this.getParamInputEl( name, false );
+
+ if ( $input.length ) {
+ $input.remove();
+ }
+ };
+
+ /**
+ * Get the input element for a mixin parameter, if it exists. If
requested,
+ * create it if it doesn't exist.
+ *
+ * @private
+ * @param {string} name The name of the parameter
+ * @param {boolean} create Create the element if it doesn't exist
+ * @return {jQuery|null}
+ */
+ MixinCustomUiController.prototype.getParamInputEl = function ( name,
create ) {
+ var inputName = makeNoticeMixinControlName(
this.constructor.static.name, name ),
+ $input = $form.find( 'input[name="' + inputName + '"]'
);
+
+ if ( create && !( $input.length ) ) {
+
+ $input = $( '<input />' ).attr( {
+ name: inputName,
+ type: 'hidden'
+ } );
+
+ $form.append( $input );
+ }
+
+ return $input;
+ };
+
+ /* MixinCustomWidget */
+
+ /**
+ * Base class for custom campaign mixin widgets.
+ *
+ * @abstract
+ * @class MixinCustomWidget
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.GroupWidget
+ * @constructor
+ *
+ * @param {MixinCustomUiController} controller
+ * @param {Object} [config] Configuration options
+ */
+ MixinCustomWidget = function ( controller, config ) {
+
+ var $element = $( '<fieldset></fieldset>' ),
+ $group = $( '<div></div>' );
+
+ // Set up config with elements, CSS class and id. This should
coordinate with
+ // makeMixinParamControlSet() (below) and
+ // templates/campaignMixinParamControls.mustache (used for the
automatic creation
+ // of mixin param controls).
+ config = $.extend( {
+ $element: $element,
+
+ // This works because controller classes are singletons.
+ id: mixinParamControlsId(
controller.constructor.static.name )
+ }, config );
+
+ $group.addClass( 'campaignMixinControls' );
+ $element.append( $group );
+
+ // Call parent constructor
+ MixinCustomWidget.parent.call( this, config );
+
+ // Call mixin constructor
+ OO.ui.mixin.GroupElement.call(
+ this,
+ $.extend( {}, config, { $group: $group } )
+ );
+ };
+
+ OO.inheritClass( MixinCustomWidget, OO.ui.Widget );
+ OO.mixinClass( MixinCustomWidget, OO.ui.mixin.GroupElement );
+
+ /* Event handlers (non-OOjs-UI) and related logic */
function updateThrottle() {
if ( $( '#throttle-enabled' ).prop( 'checked' ) ) {
@@ -75,6 +231,9 @@
.prop( 'disabled', isBucketDisabled );
}
}
+
+ // Broadcast bucket change event
+ eventBus.emit( 'bucket-change', numBuckets );
}
function getNumBuckets() {
@@ -92,7 +251,7 @@
mixinName = $checkBox.data( 'mixin-name' ),
paramValues,
$paramControlSet = $( '#' + mixinParamControlsId(
mixinName ) ),
- $paramControls;
+ mixinCustomUiController, $paramControls;
if ( $checkBox.prop( 'checked' ) ) {
@@ -101,19 +260,33 @@
paramValues = $checkBox.data(
'mixin-param-values' );
- $paramControlSet = makeMixinParamControlSet(
- mixinName,
- paramValues
- );
+ // If the mixin uses a custom UI to set params,
instantiate that
+ if ( mixinDefs[ mixinName
].customAdminUIControls ) {
+ mixinCustomUiController =
mixinCustomUiControllerFactory
+ .create( mixinName );
+
+ mixinCustomUiController.init(
paramValues );
+ $paramControlSet =
mixinCustomUiController.$widgetElement;
+
+ } else {
+
+ // Otherwise, create generic controls
using a template
+ $paramControlSet =
makeMixinParamControlSet(
+ mixinName,
+ paramValues
+ );
+
+ // Hook up handler for verification
+ $paramControls = $paramControlSet.find(
'input' );
+ $paramControls.on(
+ 'keyup keydown change mouseup
cut paste focus blur',
+ $.debounce( 100,
verifyParamControl )
+ );
+ }
+
+ // Attach the controls
$checkBox.parent( 'div' ).append(
$paramControlSet );
-
- // Hook up handler for verification
- $paramControls = $paramControlSet.find( 'input'
);
- $paramControls.on(
- 'keyup keydown change mouseup cut paste
focus blur',
- $.debounce( 100, verifyParamControl )
- );
} else {
$paramControlSet.show();
@@ -275,9 +448,44 @@
}
}
- // Execute code that requires document ready: setup slider, set
handlers, set variables
- // for jQuery elements
- $( function () {
+ /* Exports */
+
+ module.exports = {
+
+ /**
+ * Base class for custom campaign mixin UI controllers.
+ * @see MixinCustomUiController
+ * @type {function}
+ */
+ MixinCustomUiController: MixinCustomUiController,
+
+ /**
+ * Base class for custom campaign mixin widgets.
+ * @see MixinCustomWidget
+ * @type {function}
+ */
+ MixinCustomWidget: MixinCustomWidget,
+
+ /**
+ * Factory for custom mixin UI controllers.
+ * @type {OO.Factory}
+ */
+ mixinCustomUiControllerFactory: mixinCustomUiControllerFactory,
+
+ /**
+ * Centralized object for emitting and subscribing to events.
+ * @type {OO.EventEmitter}
+ */
+ eventBus: eventBus
+ };
+
+ /* General setup */
+
+ /**
+ * Finalize setup: initialize slider, set handlers, set variables for
jQuery elements
+ */
+ function initialize() {
+ var $mixinCheckboxes = $( 'input.noticeMixinCheck' );
$( '#centralnotice-throttle-amount' ).slider( {
range: 'min',
@@ -294,6 +502,7 @@
} );
$submitBtn = $( '#noticeDetailSubmit' );
+ $form = $( '#centralnotice-notice-detail' );
updateThrottle();
updateWeightColumn();
@@ -303,9 +512,18 @@
$( '#balanced' ).click( updateWeightColumn );
$( 'select#buckets' ).change( updateBuckets );
- $mixinCheckboxes = $( 'input.noticeMixinCheck' );
$mixinCheckboxes.each( showOrHideCampaignMixinControls );
$mixinCheckboxes.change( showOrHideCampaignMixinControls );
+ }
+
+ // We have to wait for document ready and for custom controls modules
to be loaded
+ // before initializing everyhting
+ $( function () {
+ var customControlsModules = $.map( mixinDefs, function (
mixinDef ) {
+ return mixinDef.customAdminUIControlsModule;
+ } );
+
+ mw.loader.using( customControlsModules ).done( initialize );
} );
}( jQuery, mediaWiki ) );
diff --git a/special/SpecialCentralNotice.php b/special/SpecialCentralNotice.php
index c5a35b1..474a3da 100644
--- a/special/SpecialCentralNotice.php
+++ b/special/SpecialCentralNotice.php
@@ -471,11 +471,19 @@
* @param $notice string The name of the campaign to view
*/
function outputNoticeDetail( $notice ) {
+ global $wgCentralNoticeCampaignMixins;
$out = $this->getOutput();
// Output specific ResourceLoader module
$out->addModules( 'ext.centralNotice.adminUi.campaignManager' );
+
+ // Output ResourceLoader modules for campaign mixins with
custom controls
+ foreach ( $wgCentralNoticeCampaignMixins as $mixinConfig ) {
+ if ( !empty(
$mixinConfig['customAdminUIControlsModule'] ) ) {
+ $out->addModules(
$mixinConfig['customAdminUIControlsModule'] );
+ }
+ }
$this->outputEnclosingDivStartTag();
@@ -502,6 +510,7 @@
$htmlOut .= Xml::openElement( 'form',
array(
'method' => 'post',
+ 'id' => 'centralnotice-notice-detail',
'action' =>
$this->getPageTitle()->getLocalURL( array(
'subaction' => 'noticeDetail',
'notice' => $notice
--
To view, visit https://gerrit.wikimedia.org/r/343953
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I32496b6dc11bef03853ccc6321eb96620c9ebf0b
Gerrit-PatchSet: 26
Gerrit-Project: mediawiki/extensions/CentralNotice
Gerrit-Branch: master
Gerrit-Owner: AndyRussG <[email protected]>
Gerrit-Reviewer: AndyRussG <[email protected]>
Gerrit-Reviewer: Awight <[email protected]>
Gerrit-Reviewer: Cdentinger <[email protected]>
Gerrit-Reviewer: Ejegg <[email protected]>
Gerrit-Reviewer: Ssmith <[email protected]>
Gerrit-Reviewer: XenoRyet <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits