Mwalker has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/57090


Change subject: WIP: Rewriting the CN UI
......................................................................

WIP: Rewriting the CN UI

Change-Id: I02b1f7f5f5dff7e95528b7c55105f7447227b329
---
M CentralNotice.i18n.php
M CentralNotice.php
M includes/Banner.php
A includes/CN_BannerPager.php
A modules/ext.centralNotice.adminUi.bannerManager/bannermanager.css
A modules/ext.centralNotice.adminUi.bannerManager/bannermanager.js
R modules/ext.centralNotice.adminUi/centralnotice.css
R modules/ext.centralNotice.adminUi/centralnotice.js
R modules/ext.centralNotice.adminUi/down-arrow-ltr.png
R modules/ext.centralNotice.adminUi/down-arrow-rtl.png
R modules/ext.centralNotice.adminUi/up-arrow-ltr.png
R modules/ext.centralNotice.adminUi/up-arrow-rtl.png
M special/SpecialBannerAllocation.php
M special/SpecialCentralNotice.php
A special/SpecialCentralNoticeBanners.php
M special/SpecialCentralNoticeLogs.php
M special/SpecialNoticeTemplate.php
17 files changed, 797 insertions(+), 9 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CentralNotice 
refs/changes/90/57090/1

diff --git a/CentralNotice.i18n.php b/CentralNotice.i18n.php
index 783c042..3ea5138 100644
--- a/CentralNotice.i18n.php
+++ b/CentralNotice.i18n.php
@@ -37,6 +37,8 @@
        'centralnotice-manage-templates' => 'Manage banners',
        'centralnotice-add' => 'Add',
        'centralnotice-add-notice' => 'Add a campaign',
+       'centralnotice-add-notice-button' => 'Create',
+       'centralnotice-add-notice-cancel-button' => 'Cancel',
        'centralnotice-edit-notice' => 'Edit campaign',
        'centralnotice-add-template' => 'Add a banner',
        'centralnotice-show-notices' => 'Show campaigns',
@@ -176,7 +178,7 @@
        'centralnotice-apply-filters' => 'Apply filters',
        'centralnotice-clear-filters' => 'Clear filters',
        'centralnotice-banner-messages' => 'Banner messages',
-       'centralnotice-filter-template-prompt' => 'Banner name contains:',
+       'centralnotice-filter-template-prompt' => 'Filter banners (e.g. Jimmy 
2008)',
        'centralnotice-filter-template-submit' => 'Apply filter',
        'centralnotice-filter-template-banner' => 'Filter banners',
        'centralnotice-priority-low' => 'low',
@@ -197,6 +199,8 @@
        'centralnotice-bucket-letter' => 'Bucket "$1"',
        'centralnotice-devicetype-desktop' => 'Desktop Computer',
        'centralnotice-all' => 'All',
+       'centralnotice-generic-error' => 'You do not have the correct 
permissions to perform the requested action or the action itself was invalid.',
+       'centralnotice-banner-name-error' => 'Invalid banner name provided. 
Valid names contain only alpha-numeric and underscore characters.'
        'centralnotice-all-except' => 'All except $1',
        'centralnotice-excluding-list' => '$1 (excluding $2)',
        'centralnotice-user-role' => 'User role',
diff --git a/CentralNotice.php b/CentralNotice.php
index 460551a..d6e5584 100644
--- a/CentralNotice.php
+++ b/CentralNotice.php
@@ -56,21 +56,35 @@
        'scripts'       => 'jquery.ui.multiselect/ui.multiselect.js',
        'styles'        => 'jquery.ui.multiselect/ui.multiselect.css',
 );
-$wgResourceModules[ 'ext.centralNotice.interface' ] = array(
+$wgResourceModules[ 'ext.centralNotice.adminUi' ] = array(
        'localBasePath' => $dir . '/modules',
        'remoteExtPath' => 'CentralNotice/modules',
        'dependencies' => array(
                'jquery.ui.datepicker',
                'jquery.ui.multiselect'
        ),
-       'scripts'       => 'ext.centralNotice.interface/centralnotice.js',
-       'styles'        => 'ext.centralNotice.interface/centralnotice.css',
+       'scripts'       => 'ext.centralNotice.adminUi/centralnotice.js',
+       'styles'        => 'ext.centralNotice.adminUi/centralnotice.css',
        'messages'      => array(
                'centralnotice-documentwrite-error',
                'centralnotice-close-title',
                'centralnotice-select-all',
                'centralnotice-remove-all',
                'centralnotice-items-selected'
+       )
+);
+$wgResourceModules[ 'ext.centralNotice.adminUi.bannerManager' ] = array(
+       'localBasePath' => $dir . '/modules',
+       'remoteExtPath' => 'CentralNotice/modules',
+       'dependencies' => array(
+               'ext.centralNotice.adminUi',
+               'jquery.ui.dialog'
+       ),
+       'scripts'       => 
'ext.centralNotice.adminUi.bannerManager/bannermanager.js',
+       'styles'        => 
'ext.centralNotice.adminUi.bannerManager/bannermanager.css',
+       'messages'      => array(
+               'centralnotice-add-notice-button',
+               'centralnotice-add-notice-cancel-button'
        )
 );
 $wgResourceModules[ 'ext.centralNotice.bannerStats' ] = array(
@@ -299,6 +313,8 @@
                $wgAutoloadClasses[ 'SpecialGlobalAllocation' ] = $specialDir . 
'SpecialGlobalAllocation.php';
                $wgAutoloadClasses[ 'SpecialBannerAllocation' ] = $specialDir . 
'SpecialBannerAllocation.php';
                $wgAutoloadClasses[ 'SpecialCentralNoticeLogs' ] = $specialDir 
. 'SpecialCentralNoticeLogs.php';
+               $wgAutoloadClasses[ 'SpecialCentralNoticeBanners' ] = 
$specialDir . 'SpecialCentralNoticeBanners.php';
+               $wgAutoloadClasses[ 'CN_BannerPager' ] = $includeDir . 
'CN_BannerPager.php';
                $wgAutoloadClasses[ 'CNDeviceTarget' ] = $includeDir . 
'CNDeviceTarget.php';
 
                if ( $wgNoticeUseTranslateExtension ) {
@@ -313,6 +329,7 @@
                $wgSpecialPages[ 'GlobalAllocation' ] = 
'SpecialGlobalAllocation';
                $wgSpecialPages[ 'BannerAllocation' ] = 
'SpecialBannerAllocation';
                $wgSpecialPages[ 'CentralNoticeLogs' ] = 
'SpecialCentralNoticeLogs';
+               $wgSpecialPages[ 'CentralNoticeBanners'] = 
'SpecialCentralNoticeBanners';
        }
 }
 
@@ -548,3 +565,4 @@
 }
 
 $wgHooks[ 'LoadExtensionSchemaUpdates' ][ ] = 
'CNDatabasePatcher::applyUpdates';
+$wgHooks[ 'SkinTemplateNavigation::SpecialPage' ][ ] = array( 
'CentralNotice::addNavigationTabs' );
diff --git a/includes/Banner.php b/includes/Banner.php
index 01d26c7..77b7736 100644
--- a/includes/Banner.php
+++ b/includes/Banner.php
@@ -702,4 +702,16 @@
                $row = $dbr->selectRow( 'cn_templates', 'tmp_landing_pages', 
array( 'tmp_name' => $eBannerName ) );
                return explode( ", ", $row->tmp_landing_pages );
        }
+
+       /**
+        * Validation function for banner names. Will return true iff the name 
fits
+        * the generic format of letters, numbers, and dashes.
+        *
+        * @param string $name The name to check
+        *
+        * @return bool True if valid
+        */
+       static function isValidBannerName( $name ) {
+               return preg_match( '/^[A-Za-z0-9_]+$/', $name );
+       }
 }
diff --git a/includes/CN_BannerPager.php b/includes/CN_BannerPager.php
new file mode 100644
index 0000000..f4e2863
--- /dev/null
+++ b/includes/CN_BannerPager.php
@@ -0,0 +1,314 @@
+<?php
+
+class CN_BannerPager extends IndexPager {
+
+       /** @var bool True if the form is to be created with editable elements 
*/
+       protected $editable = false;
+
+       /** @var string Space separated strings to filter banner titles on */
+       protected $filter = '';
+
+       /** @var bool If true; the pager will page off of the master database 
instead of the slave */
+       protected $useMasterDb = false;
+
+       /** @var array HTMLFormFields to add to the results before every banner 
entry */
+       protected $prependPrototypes = array();
+
+       /** @var array HTMLFormFields to add to the results after every banner 
entry */
+       protected $appendPrototypes = array();
+
+       /** @var string 'Section' attribute to apply to the banner elements 
generated */
+       protected $formSection = null;
+
+       /**
+        * @param IContextSource $hostTitle
+        * @param string         $formSection
+        * @param array          $prependPrototypes
+        * @param array          $appendPrototypes
+        * @param string         $bannerFilter
+        * @param bool           $editable
+        * @param bool           $useMaster
+        */
+       function __construct( $hostTitle, $formSection = null, 
$prependPrototypes = array(),
+               $appendPrototypes = array(), $bannerFilter = '', $editable = 
false, $useMaster = false
+       ) {
+               $this->editable = $editable;
+               $this->filter = $bannerFilter;
+               parent::__construct();
+
+               $this->useMasterDb = $useMaster;
+               $this->prependPrototypes = $prependPrototypes;
+               $this->appendPrototypes = $appendPrototypes;
+               $this->formSection = $formSection;
+
+               // Override paging defaults
+               list( $this->mLimit, /* $offset */ ) = 
$this->mRequest->getLimitOffset( 20, '' );
+               $this->mLimitsShown = array( 20, 50, 100 );
+               $this->viewPage = $hostTitle;
+               $this->mDefaultDirection = true;
+               $this->mIsBackwards = false;
+
+               // Use the master if required
+               if ( $useMaster ) {
+                       $this->mDb = wfGetDB( DB_MASTER );
+               }
+       }
+
+       function getNavigationBar() {
+               if ( !$this->isNavigationBarShown() ) {
+                       return none;
+               }
+
+               if ( isset( $this->mNavigationBar ) ) {
+                       return $this->mNavigationBar;
+               }
+
+               $linkTexts = array(
+                       'prev' => $this->msg( 'pager-newer-n' )->numParams( 
$this->mLimit )->escaped(),
+                       'next' => $this->msg( 'pager-older-n' )->numParams( 
$this->mLimit )->escaped(),
+                       'first' => $this->msg( 'histlast' )->escaped(),
+                       'last' => $this->msg( 'histfirst' )->escaped()
+               );
+
+               $pagingLinks = $this->getPagingLinks( $linkTexts );
+               $limitLinks = $this->getLimitLinks();
+               $limits = $this->getLanguage()->pipeList( $limitLinks );
+
+               $firstLastLinks = $this->msg( 'parentheses' )->rawParams(
+                       "{$pagingLinks['first']}" .
+                       $this->msg( 'pipe-separator' )->escaped() .
+                       "{$pagingLinks['last']}"
+               )->escaped();
+
+               $this->mNavigationBar = array(
+                       'class' => 'HTMLBannerPagerNavigation',
+                       'value' => $firstLastLinks . ' ' . $this->msg( 
'viewprevnext' )->rawParams(
+                                       $pagingLinks['prev'],
+                                       $pagingLinks['next'],
+                                       $limits
+                               )->escaped()
+               );
+
+               if ( $this->formSection ) {
+                       $this->mNavigationBar['section'] = $this->formSection;
+               }
+
+               return $this->mNavigationBar;
+       }
+
+       /**
+        * Set the database query to retrieve all the banners in the database
+        *
+        * @return array of query settings
+        */
+       function getQueryInfo() {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               // When the filter comes in it is space delimited, so break 
that...
+               $likeArray = preg_split( '/\s/', $this->filter );
+
+               // ...and then insert all the wildcards between search terms
+               if ( empty( $likeArray ) ) {
+                       $likeArray = $dbr->anyString();
+               } else {
+                       $anyStringToken = $dbr->anyString();
+                       $tempArray = array( $anyStringToken );
+                       foreach ( $likeArray as $likePart ) {
+                               $tempArray[ ] = $likePart;
+                               $tempArray[ ] = $anyStringToken;
+                       }
+                       $likeArray = $tempArray;
+               }
+
+               return array(
+                       'tables' => array( 'templates' => 'cn_templates'),
+                       'fields' => array( 'templates.tmp_name', 
'templates.tmp_id' ),
+                       'conds'  => array( 'templates.tmp_name' . 
$dbr->buildLike( $likeArray ) ),
+               );
+       }
+
+       /**
+        * Sort the banner list by tmp_id (generally equals reverse 
chronological)
+        *
+        * @return string
+        */
+       function getIndexField() {
+               return 'templates.tmp_id';
+       }
+
+       /**
+        * Generate the contents of the table pager; intended to be consumed by 
the HTMLForm
+        *
+        * @param $row object: database row
+        *
+        * @return array HTMLFormElement classes
+        */
+       function formatRow( $row ) {
+               $retval = array();
+
+               $bannerId = $row->tmp_id;
+               $bannerName = $row->tmp_name;
+
+               // Add the prepend prototypes
+               foreach( $this->prependPrototypes as $prototypeName => 
$prototypeValues ) {
+                       $retval[ "{$prototypeName}-{$bannerId}" ] = 
$prototypeValues;
+                       if ( array_key_exists( 'id', $prototypeValues ) ) {
+                               $retval[ "{$prototypeName}-{$bannerId}" ][ 'id' 
] .= "-$bannerId";
+                       }
+               }
+
+               // Now do the banner
+               $retval["cn-banner-list-element-$bannerId"] = array(
+                       'class' => 'HTMLCentralNoticeBanner',
+                       'banner' => $bannerName
+               );
+               if ( $this->formSection ) {
+                       $retval["cn-banner-list-element-$bannerId"]['section'] 
= $this->formSection;
+               }
+
+               // Append prototypes
+               foreach( $this->appendPrototypes as $prototypeName => 
$prototypeValues ) {
+                       $retval[ $prototypeName . "-$bannerId" ] = 
$prototypeValues;
+                       if ( array_key_exists( 'id', $prototypeValues ) ) {
+                               $retval[ $prototypeName . "-$bannerId" ][ 'id' 
] .= "-$bannerId";
+                       }
+               }
+
+               // Set the disabled attribute
+               if ( !$this->editable ) {
+                       foreach( $retval as $prototypeName => $prototypeValues 
) {
+                               $retval[ $prototypeName ][ 'disabled' ] = true;
+                       }
+               }
+
+               return $retval;
+       }
+
+       /**
+        * Get the formatted result list. Calls getStartBody(), formatRow() and
+        * getEndBody(), concatenates the results and returns them.
+        *
+        * @return array
+        */
+       public function getBody() {
+               if ( !$this->mQueryDone ) {
+                       $this->doQuery();
+               }
+
+               if ( $this->mResult->numRows() ) {
+                       # Do any special query batches before display
+                       $this->doBatchLookups();
+               }
+
+               # Don't use any extra rows returned by the query
+               $numRows = min( $this->mResult->numRows(), $this->mLimit );
+
+               $retval = array();
+
+               if ( $numRows ) {
+                       if ( $this->mIsBackwards ) {
+                               for ( $i = $numRows - 1; $i >= 0; $i-- ) {
+                                       $this->mResult->seek( $i );
+                                       $row = $this->mResult->fetchObject();
+                                       $retval += $this->formatRow( $row );
+                               }
+                       } else {
+                               $this->mResult->seek( 0 );
+                               for ( $i = 0; $i < $numRows; $i++ ) {
+                                       $row = $this->mResult->fetchObject();
+                                       $retval += $this->formatRow( $row );
+                               }
+                       }
+               } else {
+                       // TODO: empty value
+               }
+               return $retval;
+       }
+}
+
+class HTMLBannerPagerNavigation extends HTMLFormField {
+       /** Empty - no validation can be done on a navigation element */
+       function validate( $value, $alldata ) { return true; }
+
+       public function getInputHTML( $value ) {
+               return $value;
+       }
+}
+
+/**
+ * Produces a banner preview DIV that can be embedded in an HTMLForm.
+ *
+ * Expects the following options:
+ * - 'language' - ISO language code to render banner in
+ * - 'banner'   - Canonical name of banner
+ */
+class HTMLCentralNoticeBanner extends HTMLFormField {
+       /** Empty - no validation can be done on a banner */
+       function validate( $value, $alldata ) { return true; }
+
+       /** Get a preview of the banner */
+       public function getInputHTML( $value ) {
+               global $wgOut;
+
+               $renderer = new SpecialBannerLoader();
+               $renderer->language = array_key_exists( 'language', 
$this->mParams )
+                       ? $this->mParams['language']
+                       : $wgOut->getLanguage()->getCode();
+
+               $bannerName =  $this->mParams['banner'];
+               try {
+                       $preview = $renderer->getHtmlNotice( $bannerName );
+               } catch ( SpecialBannerLoaderException $e ) {
+                       $preview = $this->msg( 'centralnotice-nopreview' 
)->text();
+               }
+
+               return Xml::tags(
+                       'div',
+                       array(
+                                'id' => Sanitizer::escapeId( 
"cn-banner-preview-$bannerName" ),
+                                'class' => 'cn-banner-preview-div',
+                       ),
+                       $preview
+               );
+       }
+
+       public function getTableRow( $value ) {
+               throw new MWException( "getTableRow() is not implemented for 
HTMLCentralNoticeBanner" );
+       }
+
+       public function getRaw( $value ) {
+               throw new MWException( "getRaw() is not implemented for 
HTMLCentralNoticeBanner" );
+       }
+
+       public function getDiv( $value ) {
+               $html = Xml::openElement(
+                       'div',
+                       array(
+                                'id' =>  Sanitizer::escapeId( 
"cn-banner-list-element-{$this->mParams['banner']}" ),
+                                'class' => "cn-banner-list-element",
+                       )
+               );
+
+               // Make the label; this consists of a text link to the banner 
editor, and a series of status icons
+               $html .= Xml::openElement( 'div', array( 'class' => 
'cn-banner-list-element-label' ) );
+               $html .= Xml::tags(
+                       'div',
+                       array( 'class' => 'cn-banner-list-element-label-text' ),
+                       Linker::link(
+                               SpecialPage::getTitleFor( 
'CentralNoticeBanners', 'Edit' ),
+                               htmlspecialchars( $this->mParams['banner'] ),
+                               array(),
+                               array( 'banner' => $this->mParams['banner'] )
+                       )
+               );
+               // TODO: Output status icons
+               $html .= Xml::tags( 'div', array( 'class' => 
'cn-banner-list-element-label-icons' ), '' );
+               $html .= Xml::closeElement( 'div' );
+
+               // Add the banner preview
+               $html .= $this->getInputHTML( null );
+
+               $html .= Xml::closeElement( 'div' );
+               return $html;
+       }
+}
diff --git a/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.css 
b/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.css
new file mode 100644
index 0000000..fb477db
--- /dev/null
+++ b/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.css
@@ -0,0 +1,71 @@
+/* === Banner Manager List Form === */
+/* --- General Form Elements --- */
+/* Restyle the fieldsets to make them less annoying */
+#cn-banner-manager fieldset {
+       border: none;
+       margin: 0;
+       padding: 0;
+}
+#cn-banner-manager legend {
+       display: none;
+}
+
+/* --- Banner management header --- */
+/* General header */
+#cn-banner-manager #cn-formsection-header {
+       padding: 0.5em;
+       background-color: #F9F9F9;
+       border: 1px solid #2F6FAB;
+}
+
+/* Style the search sub form */
+#cn-formsection-header-banner-search,
+#mw-htmlform-banner-search .mw-htmlform-field-HTMLSubmitField {
+       float: right;
+}
+#mw-htmlform-banner-search .mw-htmlform-field-HTMLTextField {
+       float: left;
+}
+#mw-input-wpbannerNameLike {
+       width: 17em;
+}
+
+/* Bulk management options */
+#cn-formsection-header-banner-bulk-manage {
+       float: left;
+}
+#cn-formsection-header-banner-bulk-manage div {
+       display: inline;
+}
+
+/* One off actions */
+fieldset #cn-formsection-header-one-off {
+       padding-left: 1em;
+}
+
+/* --- Banner preview list --- */
+#mw-htmlform-banner-list {
+       margin-left: 0.9em;
+}
+.cn-banner-list-element {
+       margin: 0 0 0.5em 0.5em;
+}
+.cn-banner-preview-div {
+       border: 1px solid #A7D7F9;
+       padding: 1em;
+       margin-top: 0.5em;
+}
+#mw-htmlform-banner-list .mw-htmlform-field-HTMLCheckField {
+       float: left;
+}
+#mw-htmlform-banner-list .cn-banner-list-element {
+       display: inline;
+}
+
+/* --- Add banner dialog --- */
+#cn-formsection-addBanner {
+       display: none;
+}
+.ui-dialog-content legend {
+       display: none;
+}
\ No newline at end of file
diff --git a/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.js 
b/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.js
new file mode 100644
index 0000000..fdbe1e7
--- /dev/null
+++ b/modules/ext.centralNotice.adminUi.bannerManager/bannermanager.js
@@ -0,0 +1,35 @@
+/**
+ * CentralNotice Banner Management UI Functions
+ */
+( function ( $, mw ) {
+       mw.centralNotice.adminUi.bannerManagement = {
+               doAddBannerDialog: function() {
+                       var buttons = {},
+                               okButtonText = 
mw.message('centralnotice-add-notice-button').text(),
+                               cancelButtonText = 
mw.message('centralnotice-add-notice-cancel-button').text(),
+                               dialogObj = $('<form></form>');
+
+                       // Implement the functionality
+                       buttons[ cancelButtonText ] = function() { 
$(this).dialog("close"); };
+                       buttons[ okButtonText ] = function() {
+                               var formobj = $('#cn-banner-manager')[0];
+                               formobj.wpaction.value = 'create';
+                               formobj.wpnewBannerName.value = 
$(this)[0].wpnewBannerName.value;
+                               formobj.submit();
+                       };
+
+                       // Create the dialog by copying the textfield element 
into a new form
+                       dialogObj[0].name = 'addBannerDialog';
+                       dialogObj.append( $( '#cn-formsection-addBanner' 
).children( 'div' ).clone().show() )
+                               .dialog( {
+                                       title: 'Add a new banner',
+                                       modal: true,
+                                       buttons: buttons,
+                                       width: 375
+                               });
+
+                       // Do not submit the form... that's up to the ok button
+                       return false;
+               }
+       };
+} )( jQuery, mediaWiki );
diff --git a/modules/ext.centralNotice.interface/centralnotice.css 
b/modules/ext.centralNotice.adminUi/centralnotice.css
similarity index 94%
rename from modules/ext.centralNotice.interface/centralnotice.css
rename to modules/ext.centralNotice.adminUi/centralnotice.css
index 2675e89..c721e82 100644
--- a/modules/ext.centralNotice.interface/centralnotice.css
+++ b/modules/ext.centralNotice.adminUi/centralnotice.css
@@ -140,3 +140,12 @@
 body.skin-monobook #preferences fieldset.prefsection {
        border-color:#AAAAAA;
 }
+
+/* For HTMLForm generated forms */
+form .error {
+       color: #000000;
+       margin: 0 0 0.5em 0;
+       padding: 0.5em;
+       background-color: #FFCCCC;
+       border: 2px solid #FF0000;
+}
diff --git a/modules/ext.centralNotice.interface/centralnotice.js 
b/modules/ext.centralNotice.adminUi/centralnotice.js
similarity index 97%
rename from modules/ext.centralNotice.interface/centralnotice.js
rename to modules/ext.centralNotice.adminUi/centralnotice.js
index ee4df0b..d40367f 100644
--- a/modules/ext.centralNotice.interface/centralnotice.js
+++ b/modules/ext.centralNotice.adminUi/centralnotice.js
@@ -1,3 +1,8 @@
+/**
+ * CentralNotice Administrative UI - Common Functions
+ */
+window.mw.centralNotice.adminUi = {};
+
 // Collapse and uncollapse detailed view for an individual log entry
 window.toggleLogDisplay = function ( logId ) {
        var thisCollapsed = document.getElementById( 'cn-collapsed-' + logId );
diff --git a/modules/ext.centralNotice.interface/down-arrow-ltr.png 
b/modules/ext.centralNotice.adminUi/down-arrow-ltr.png
similarity index 100%
rename from modules/ext.centralNotice.interface/down-arrow-ltr.png
rename to modules/ext.centralNotice.adminUi/down-arrow-ltr.png
Binary files differ
diff --git a/modules/ext.centralNotice.interface/down-arrow-rtl.png 
b/modules/ext.centralNotice.adminUi/down-arrow-rtl.png
similarity index 100%
rename from modules/ext.centralNotice.interface/down-arrow-rtl.png
rename to modules/ext.centralNotice.adminUi/down-arrow-rtl.png
Binary files differ
diff --git a/modules/ext.centralNotice.interface/up-arrow-ltr.png 
b/modules/ext.centralNotice.adminUi/up-arrow-ltr.png
similarity index 100%
rename from modules/ext.centralNotice.interface/up-arrow-ltr.png
rename to modules/ext.centralNotice.adminUi/up-arrow-ltr.png
Binary files differ
diff --git a/modules/ext.centralNotice.interface/up-arrow-rtl.png 
b/modules/ext.centralNotice.adminUi/up-arrow-rtl.png
similarity index 100%
rename from modules/ext.centralNotice.interface/up-arrow-rtl.png
rename to modules/ext.centralNotice.adminUi/up-arrow-rtl.png
Binary files differ
diff --git a/special/SpecialBannerAllocation.php 
b/special/SpecialBannerAllocation.php
index 624fe7f..5cc6c79 100644
--- a/special/SpecialBannerAllocation.php
+++ b/special/SpecialBannerAllocation.php
@@ -84,7 +84,7 @@
 
                // Output ResourceLoader module for styling and javascript 
functions
                $out->addModules( array(
-                       'ext.centralNotice.interface',
+                       'ext.centralNotice.adminUi',
                        'ext.centralNotice.bannerStats'
                ) );
 
diff --git a/special/SpecialCentralNotice.php b/special/SpecialCentralNotice.php
index bb8d3e4..7466767 100644
--- a/special/SpecialCentralNotice.php
+++ b/special/SpecialCentralNotice.php
@@ -13,6 +13,8 @@
                parent::__construct( 'CentralNotice' );
        }
 
+       function __destruct() {}
+
        /**
         * Handle different types of page requests
         */
@@ -25,7 +27,7 @@
                $request = $this->getRequest();
 
                // Output ResourceLoader module for styling and javascript 
functions
-               $out->addModules( 'ext.centralNotice.interface' );
+               $out->addModules( 'ext.centralNotice.adminUi' );
 
                // Check permissions
                $this->editable = $this->getUser()->isAllowed( 
'centralnotice-admin' );
@@ -1386,7 +1388,7 @@
         *
         * @return string Space delimited string
         */
-       protected function sanitizeSearchTerms( $terms ) {
+       public static function sanitizeSearchTerms( $terms ) {
                $retval = ' '; // The space is important... it gets trimmed 
later
 
                foreach ( preg_split( '/\s/', $terms ) as $term ) {
@@ -1400,6 +1402,85 @@
                return trim( $retval );
        }
 
+       /**
+        * Adds CentralNotice specific navigation tabs to the UI.
+        * Implementation of SkinTemplateNavigation::SpecialPage hook.
+        *
+        * @param Skin  $skin Reference to the Skin object
+        * @param array $tabs Any current skin tabs
+        *
+        * @return boolean
+        */
+       public static function addNavigationTabs( Skin $skin, array &$tabs ) {
+               $title = $skin->getTitle();
+               list( $alias, $sub ) = SpecialPageFactory::resolveAlias( 
$title->getText() );
+
+               $centralNoticePages = array(
+                       'CentralNotice' => array(
+                               'type' => 'namespaces',
+                               'message' => 'centralnotice-notices',
+                       ),
+                       'CentralNoticeBanners' => array(
+                               'type' => 'namespaces',
+                               'message' => 'centralnotice-templates',
+                       ),
+
+                       'BannerAllocation' => array(
+                               'type' => 'views',
+                               'message' => 'centralnotice-allocation',
+                       ),
+                       'CentralNoticeLogs' => array(
+                               'type' => 'views',
+                               'message' => 'centralnotice-logs',
+                       ),
+               );
+
+               if ( !array_key_exists( $alias, $centralNoticePages ) ) {
+                       return true;
+               }
+
+               // Clear the special page tab that's there already
+               $tabs['namespaces'] = array();
+
+               // Now add our own
+               foreach ( $centralNoticePages as $page => $keys ) {
+                       $tabs[ $keys[ 'type' ] ][ $page ] = array(
+                               'text' => wfMessage( $keys[ 'message' ] ),
+                               'href' => SpecialPage::getTitleFor( $page 
)->getFullURL(),
+                               'class' => ( $alias === $page ) ? 'selected' : 
'',
+                       );
+               }
+
+               return true;
+       }
+
+       /**
+        * Loads a CentralNotice variable from session data.
+        *
+        * @param string $variable Name of the variable
+        * @param object $default Default value of the variable
+        *
+        * @return object Stored variable or default
+        */
+       public static function getCNSessionVar( $variable, $default = null ) {
+               if ( array_key_exists( $variable, $_SESSION ) ) {
+                       return $_SESSION[ "centralnotice-$variable" ];
+               } else {
+                       return $default;
+               }
+       }
+
+       /**
+        * Sets a CentralNotice session variable. Note that this will fail 
silently if a
+        * session does not exist for the user.
+        *
+        * @param string $variable Name of the variable
+        * @param object $value    Value for the variable
+        */
+       public static function setCNSessionVar( $variable, $value ) {
+               $_SESSION[ "centralnotice-$variable" ] = $value;
+       }
+       
        public function listProjects( $projects ) {
                global $wgNoticeProjects;
                return $this->makeShortList( $wgNoticeProjects, $projects );
diff --git a/special/SpecialCentralNoticeBanners.php 
b/special/SpecialCentralNoticeBanners.php
new file mode 100644
index 0000000..721f2d1
--- /dev/null
+++ b/special/SpecialCentralNoticeBanners.php
@@ -0,0 +1,239 @@
+<?php
+
+/**
+ * Special page for management of CentralNotice banners
+ */
+class SpecialCentralNoticeBanners extends CentralNotice {
+
+       /** @var bool True if edits have been made this sessions -- hint to use 
the DB_MASTER */
+       protected $inEditAction = false;
+
+       /** @var string Filter to apply to the banner search when generating 
the list */
+       protected $bannerFilterString = '';
+
+       function __construct() {
+               SpecialPage::__construct( 'CentralNoticeBanners' );
+               $this->getOutput()->addModules( 
'ext.centralNotice.adminUi.bannerManager' );
+
+               // Make sure we have a session
+               wfSetupSession();
+
+               // Load things that may have been serialized into the session
+               $this->bannerFilterString = $this->getCNSessionVar( 
'bannerFilterString', '' );
+       }
+
+       function __destruct() {
+               $this->setCNSessionVar( 'bannerFilterString', 
$this->bannerFilterString );
+       }
+
+       public function isListed() {
+               return false;
+       }
+
+       /**
+        * Handle all the different types of page requests determined by $action
+        *
+        * Valid actions are:
+        *    Null      - Display a list of banners
+        *    Edit      - Edits an existing banner
+        */
+       public function execute( $action ) {
+               // Do all the common setup
+               $this->setHeaders();
+               $this->getOutput()->addModules( 'ext.centralNotice.adminUi' );
+               $this->editable = $this->getUser()->isAllowed( 
'centralnotice-admin' );
+
+               switch ( strtolower( $action ) ) {
+                       case null:
+                               // Display the list of banners
+                               $this->showBannerList();
+                               break;
+
+                       case 'create':
+                       case 'edit':
+                       default:
+                               // Something went wrong; display error page
+                               $this->getOutput()->addHTML(
+                                       Xml::element( 'div', array( 'class' => 
'error' ), wfMessage( 'centralnotice-generic-error' ) )
+                               );
+                               break;
+               }
+       }
+
+       /**
+        * Process the 'banner list' form and display a new one.
+        */
+       protected function showBannerList() {
+               $out = $this->getOutput();
+               $out->setPageTitle( 'Manage Banners' );
+
+               // Process the form that we sent out
+               $formDescriptor = $this->generateBannerListForm();
+               $htmlForm = new CentralNoticeHtmlForm( $formDescriptor, 
$this->getContext() );
+               $htmlForm->setSubmitCallback( array( $this, 'processBannerList' 
) );
+               $htmlForm->loadData();
+               $formResult = $htmlForm->trySubmit();
+
+               // Re-generate the form in case they changed the filter string, 
archived something,
+               // deleted something, etc...
+               $formDescriptor = $this->generateBannerListForm( 
$this->bannerFilterString );
+               $htmlForm = new CentralNoticeHtmlForm( $formDescriptor, 
$this->getContext() );
+
+               $htmlForm->setId('cn-banner-manager')->
+                       suppressDefaultSubmit()->
+                       setDisplayFormat( 'div' )->
+                       setSubmitTextMsg( 
'centralnotice-filter-template-submit' )->
+                       setSubmitCallback( 
'SpecialCentralNoticeBanners::processBannerList' )->
+                       prepareForm()->
+                       displayForm( $formResult );
+       }
+
+       /**
+        * Generates the HTMLForm entities for the 'banner list' form.
+        *
+        * @param string $filter Filter to use for the banner list
+        *
+        * @return array of HTMLForm entities
+        */
+       protected function generateBannerListForm( $filter = '' ) {
+               // --- Create the banner search form --- //
+               $formDescriptor = array(
+                       'bannerNameLike' => array(
+                               'section' => 'header/banner-search',
+                               'class' => 'HTMLTextField',
+                               'placeholder' => wfMessage( 
'centralnotice-filter-template-prompt' ),
+                               'filter-callback' => array( $this, 
'sanitizeSearchTerms' )
+                       ),
+                       'filterSubmit' => array(
+                               'section' => 'header/banner-search',
+                               'class' => 'HTMLSubmitField',
+                               'default' => wfMessage( 
'centralnotice-filter-template-submit' )->text(),
+                       )
+               );
+
+               // --- Create the management options --- //
+               $formDescriptor += array(
+                       'selectAllBanners' => array(
+                               'section' => 'header/banner-bulk-manage',
+                               'class' => 'HTMLCheckField',
+                               'disabled' => !$this->editable,
+                       ),
+                       'archiveSelectedBanners' => array(
+                               'section' => 'header/banner-bulk-manage',
+                               'class' => 'HTMLSubmitField',
+                               'default' => 'Archive',
+                               'disabled' => !$this->editable,
+                       ),
+                       'deleteSelectedBanners' => array(
+                               'section' => 'header/banner-bulk-manage',
+                               'class' => 'HTMLSubmitField',
+                               'default' => wfMessage( 'centralnotice-remove' 
)->text(),
+                               'disabled' => !$this->editable,
+                       ),
+                       'addNewBanner' => array(
+                               'section' => 'header/one-off',
+                               'class' => 'HTMLButtonField',
+                               'default' => wfMessage( 
'centralnotice-add-template' )->text(),
+                               'disabled' => !$this->editable,
+                               'onclick' => 
'mw.centralNotice.adminUi.bannerManagement.doAddBannerDialog()',
+                       ),
+                       'newBannerName' => array(
+                               'section' => 'addBanner',
+                               'class' => 'HTMLTextField',
+                               'disabled' => !$this->editable,
+                               'label' => wfMessage( 
'centralnotice-banner-name' )->text(),
+                       ),
+                       'action' => array(
+                               'type' => 'hidden',
+                       )
+               );
+
+               // --- Add all the banners via the fancy pager object ---
+               $pager = new CN_BannerPager(
+                       $this->getTitle(),
+                       'banner-list',
+                       array(
+                                'applyTo' => array(
+                                        'section' => 'banner-list',
+                                        'class' => 'HTMLCheckField',
+                                )
+                       ),
+                       array(),
+                       $filter,
+                       $this->editable
+               );
+               $formDescriptor[ 'topPagerNav' ] = $pager->getNavigationBar();
+               $formDescriptor += $pager->getBody();
+               $formDescriptor[ 'bottomPagerNav' ] = 
$pager->getNavigationBar();
+
+               return $formDescriptor;
+       }
+
+       /**
+        * Callback function from the showBannerList() form that actually 
processes the
+        * response data.
+        *
+        * @param $formData
+        *
+        * @return null|string|array
+        */
+       public function processBannerList( $formData ) {
+               $this->bannerFilterString = $formData[ 'bannerNameLike' ];
+
+               if ( $formData[ 'action' ] && $this->editable ) {
+                       switch ( strtolower( $formData[ 'action' ] ) ) {
+                               case 'create':
+                                       // Attempt to create a new banner and 
redirect; we validate here because it's
+                                       // a hidden field and that doesn't work 
so well with the form
+                                       if ( !Banner::isValidBannerName( 
$formData[ 'newBannerName' ] ) ) {
+                                               return wfMessage( 
'centralnotice-banner-name-error' );
+                                       }
+
+                                       if ( Banner::bannerExists( $formData[ 
'newBannerName' ] ) ) {
+                                               return wfMessage( 
'centralnotice-template-exists' )->text();
+                                       } else {
+                                               $retval = Banner::addTemplate(
+                                                       $formData[ 
'newBannerName' ],
+                                                       "<!-- Empty banner -->",
+                                                       $this->getUser(),
+                                                       false,
+                                                       false
+                                               );
+
+                                               if ( $retval ) {
+                                                       // Something failed; 
display error to user
+                                                       return wfMessage( 
$retval )->text();
+                                               } else {
+                                                       
$this->getOutput()->redirect(
+                                                               
SpecialPage::getTitleFor( 'CentralNoticeBanners', 'Edit' )->
+                                                                       
getFullURL( "banner={$formData[ 'newBannerName' ]}" )
+                                                       );
+                                               }
+                                       }
+                                       break;
+
+                               case 'archive':
+                               case 'remove':
+                                       break;
+                       }
+               } elseif ( $formData[ 'action' ] ) {
+                       // Oh noes! The l33t hakorz are here...
+                       return wfMessage( 'centralnotice-generic-error' 
)->text();
+               }
+
+               return null;
+       }
+}
+
+/**
+ * Class CentralNoticeHtmlForm
+ */
+class CentralNoticeHtmlForm extends HTMLForm {
+       /**
+        * Get the whole body of the form.
+        * @return string
+        */
+       function getBody() {
+               return $this->displaySection( $this->mFieldTree, '', 
'cn-formsection-' );
+       }
+}
diff --git a/special/SpecialCentralNoticeLogs.php 
b/special/SpecialCentralNoticeLogs.php
index 2531ac1..50cd181 100644
--- a/special/SpecialCentralNoticeLogs.php
+++ b/special/SpecialCentralNoticeLogs.php
@@ -27,7 +27,7 @@
                $this->setHeaders();
 
                // Output ResourceLoader module for styling and javascript 
functions
-               $out->addModules( 'ext.centralNotice.interface' );
+               $out->addModules( 'ext.centralNotice.adminUi' );
 
                // Initialize error variable
                $this->centralNoticeError = false;
diff --git a/special/SpecialNoticeTemplate.php 
b/special/SpecialNoticeTemplate.php
index b7eb73e..90333a7 100644
--- a/special/SpecialNoticeTemplate.php
+++ b/special/SpecialNoticeTemplate.php
@@ -24,7 +24,7 @@
                $this->setHeaders();
 
                // Output ResourceLoader module for styling and javascript 
functions
-               $out->addModules( 'ext.centralNotice.interface' );
+               $out->addModules( 'ext.centralNotice.adminUi' );
 
                // Check permissions
                $this->editable = $user->isAllowed( 'centralnotice-admin' );

-- 
To view, visit https://gerrit.wikimedia.org/r/57090
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I02b1f7f5f5dff7e95528b7c55105f7447227b329
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CentralNotice
Gerrit-Branch: master
Gerrit-Owner: Mwalker <mwal...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to