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

Reply via email to