ChangesListSpecialPage and subclasses: Reorder functions
authorBartosz Dziewoński <matma.rex@gmail.com>
Sun, 22 Dec 2013 02:17:34 +0000 (03:17 +0100)
committerBartosz Dziewoński <matma.rex@gmail.com>
Thu, 16 Jan 2014 18:30:15 +0000 (19:30 +0100)
No functional nor cleanup changes, just reordering. This should
make further refactoring (and reviewing it) easier.

In ChangesListSpecialPage placed them in "call-graph" order
instead of somewhat randomly.

In subclasses placed ChangesListSpecialPage functions first in the
same order, feed-related things later, and internal functions last.

Change-Id: I0cccfdb7c5ecd99a92d09d835211153279d71846

includes/specialpage/ChangesListSpecialPage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialWatchlist.php

index 7814359..d5ec18c 100644 (file)
@@ -32,19 +32,28 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        protected $customFilters;
 
        /**
-        * Get a FormOptions object containing the default options. By default returns some basic options,
-        * you might not call parent method and discard them.
+        * Main execution point
+        * @todo This should totally do things
         *
-        * @return FormOptions
+        * @param string $subpage
         */
-       public function getDefaultOptions() {
-               $opts = new FormOptions();
+       public function execute( $subpage ) {
+               $this->rcSubpage = $subpage;
+               throw new MWException( "Not implemented" );
+       }
 
-               $opts->add( 'namespace', '', FormOptions::INTNULL );
-               $opts->add( 'invert', false );
-               $opts->add( 'associated', false );
+       /**
+        * Get the current FormOptions for this request
+        * @todo Not called by anything, should be called by execute()
+        *
+        * @return FormOptions
+        */
+       public function getOptions() {
+               if ( $this->rcOptions === null ) {
+                       $this->rcOptions = $this->setup( $this->rcSubpage );
+               }
 
-               return $opts;
+               return $this->rcOptions;
        }
 
        /**
@@ -73,12 +82,29 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Validate a FormOptions object generated by getDefaultOptions() with values already populated.
+        * Get a FormOptions object containing the default options. By default returns some basic options,
+        * you might not call parent method and discard them.
         *
-        * @param FormOptions $opts
+        * @return FormOptions
         */
-       public function validateOptions( FormOptions $opts ) {
-               // nothing by default
+       public function getDefaultOptions() {
+               $opts = new FormOptions();
+
+               $opts->add( 'namespace', '', FormOptions::INTNULL );
+               $opts->add( 'invert', false );
+               $opts->add( 'associated', false );
+
+               return $opts;
+       }
+
+       /**
+        * Get custom show/hide filters
+        *
+        * @return array Map of filter URL param names to properties (msg/default)
+        */
+       protected function getCustomFilters() {
+               // @todo Fire a Special{$this->getName()}Filters hook here
+               return array();
        }
 
        /**
@@ -94,16 +120,6 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                return $opts;
        }
 
-       /**
-        * Get custom show/hide filters
-        *
-        * @return array Map of filter URL param names to properties (msg/default)
-        */
-       protected function getCustomFilters() {
-               // @todo Fire a Special{$this->getName()}Filters hook here
-               return array();
-       }
-
        /**
         * Process $par and put options found in $opts. Used when including the page.
         *
@@ -115,28 +131,12 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Get the current FormOptions for this request
-        * @todo Not called by anything, should be called by execute()
-        *
-        * @return FormOptions
-        */
-       public function getOptions() {
-               if ( $this->rcOptions === null ) {
-                       $this->rcOptions = $this->setup( $this->rcSubpage );
-               }
-
-               return $this->rcOptions;
-       }
-
-       /**
-        * Main execution point
-        * @todo This should totally do things
+        * Validate a FormOptions object generated by getDefaultOptions() with values already populated.
         *
-        * @param string $subpage
+        * @param FormOptions $opts
         */
-       public function execute( $subpage ) {
-               $this->rcSubpage = $subpage;
-               throw new MWException( "Not implemented" );
+       public function validateOptions( FormOptions $opts ) {
+               // nothing by default
        }
 
        /**
@@ -185,6 +185,26 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                $this->setBottomText( $opts );
        }
 
+       /**
+        * Send the text to be displayed before the options. Should use $this->getOutput()->addWikiText()
+        * or similar methods to print the text.
+        *
+        * @param FormOptions $opts
+        */
+       function setTopText( FormOptions $opts ) {
+               // nothing by default
+       }
+
+       /**
+        * Send the text to be displayed after the options. Should use $this->getOutput()->addWikiText()
+        * or similar methods to print the text.
+        *
+        * @param FormOptions $opts
+        */
+       function setBottomText( FormOptions $opts ) {
+               // nothing by default
+       }
+
        /**
         * Get options to be displayed in a form
         * @todo This should handle options returned by getDefaultOptions().
@@ -256,26 +276,6 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                return $legend;
        }
 
-       /**
-        * Send the text to be displayed before the options. Should use $this->getOutput()->addWikiText()
-        * or similar methods to print the text.
-        *
-        * @param FormOptions $opts
-        */
-       function setTopText( FormOptions $opts ) {
-               // nothing by default
-       }
-
-       /**
-        * Send the text to be displayed after the options. Should use $this->getOutput()->addWikiText()
-        * or similar methods to print the text.
-        *
-        * @param FormOptions $opts
-        */
-       function setBottomText( FormOptions $opts ) {
-               // nothing by default
-       }
-
        /**
         * Add page-specific modules.
         * @todo Not called by anything, should be called by execute()
index 37fa32c..4f89c00 100644 (file)
@@ -39,56 +39,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                parent::__construct( $name, $restriction );
        }
 
-       public function isIncludable() {
-               return true;
-       }
-
-       /**
-        * Get a FormOptions object containing the default options
-        *
-        * @return FormOptions
-        */
-       public function getDefaultOptions() {
-               $opts = parent::getDefaultOptions();
-               $user = $this->getUser();
-
-               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
-               $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
-               $opts->add( 'from', '' );
-
-               $opts->add( 'hideminor', $user->getBoolOption( 'hideminor' ) );
-               $opts->add( 'hidebots', true );
-               $opts->add( 'hideanons', false );
-               $opts->add( 'hideliu', false );
-               $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
-               $opts->add( 'hidemyself', false );
-
-               $opts->add( 'categories', '' );
-               $opts->add( 'categories_any', false );
-               $opts->add( 'tagfilter', '' );
-
-               return $opts;
-       }
-
-       public function validateOptions( FormOptions $opts ) {
-               global $wgFeedLimit;
-               $opts->validateIntBounds( 'limit', 0, $this->feedFormat ? $wgFeedLimit : 5000 );
-       }
-
-       /**
-        * Get custom show/hide filters
-        *
-        * @return array Map of filter URL param names to properties (msg/default)
-        */
-       protected function getCustomFilters() {
-               if ( $this->customFilters === null ) {
-                       $this->customFilters = array();
-                       wfRunHooks( 'SpecialRecentChangesFilters', array( $this, &$this->customFilters ) );
-               }
-
-               return $this->customFilters;
-       }
-
        /**
         * Main execution point
         *
@@ -143,20 +93,44 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        /**
-        * Return an array with a ChangesFeed object and ChannelFeed object
+        * Get a FormOptions object containing the default options
         *
-        * @param string $feedFormat Feed's format (either 'rss' or 'atom')
-        * @return array
+        * @return FormOptions
         */
-       public function getFeedObject( $feedFormat ) {
-               $changesFeed = new ChangesFeed( $feedFormat, 'rcfeed' );
-               $formatter = $changesFeed->getFeedObject(
-                       $this->msg( 'recentchanges' )->inContentLanguage()->text(),
-                       $this->msg( 'recentchanges-feed-description' )->inContentLanguage()->text(),
-                       $this->getPageTitle()->getFullURL()
-               );
+       public function getDefaultOptions() {
+               $opts = parent::getDefaultOptions();
+               $user = $this->getUser();
 
-               return array( $changesFeed, $formatter );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
+               $opts->add( 'from', '' );
+
+               $opts->add( 'hideminor', $user->getBoolOption( 'hideminor' ) );
+               $opts->add( 'hidebots', true );
+               $opts->add( 'hideanons', false );
+               $opts->add( 'hideliu', false );
+               $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
+               $opts->add( 'hidemyself', false );
+
+               $opts->add( 'categories', '' );
+               $opts->add( 'categories_any', false );
+               $opts->add( 'tagfilter', '' );
+
+               return $opts;
+       }
+
+       /**
+        * Get custom show/hide filters
+        *
+        * @return array Map of filter URL param names to properties (msg/default)
+        */
+       protected function getCustomFilters() {
+               if ( $this->customFilters === null ) {
+                       $this->customFilters = array();
+                       wfRunHooks( 'SpecialRecentChangesFilters', array( $this, &$this->customFilters ) );
+               }
+
+               return $this->customFilters;
        }
 
        /**
@@ -210,25 +184,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
        }
 
-       /**
-        * Get last modified date, for client caching
-        * Don't use this if we are using the patrol feature, patrol changes don't
-        * update the timestamp
-        *
-        * @param string $feedFormat
-        * @return string|bool
-        */
-       public function checkLastModified( $feedFormat ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __METHOD__ );
-               if ( $feedFormat || !$this->getUser()->useRCPatrol() ) {
-                       if ( $lastmod && $this->getOutput()->checkLastModified( $lastmod ) ) {
-                               # Client cache fresh and headers sent, nothing more to do.
-                               return false;
-                       }
-               }
-
-               return $lastmod;
+       public function validateOptions( FormOptions $opts ) {
+               global $wgFeedLimit;
+               $opts->validateIntBounds( 'limit', 0, $this->feedFormat ? $wgFeedLimit : 5000 );
        }
 
        /**
@@ -476,28 +434,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
        }
 
-       /**
-        * Get the query string to append to feed link URLs.
-        *
-        * @return string
-        */
-       public function getFeedQuery() {
-               global $wgFeedLimit;
-
-               $this->getOptions()->validateIntBounds( 'limit', 0, $wgFeedLimit );
-               $options = $this->getOptions()->getChangedValues();
-
-               // wfArrayToCgi() omits options set to null or false
-               foreach ( $options as &$value ) {
-                       if ( $value === false ) {
-                               $value = '0';
-                       }
-               }
-               unset( $value );
-
-               return wfArrayToCgi( $options );
-       }
-
        /**
         * Return the text to be displayed above the changes
         *
@@ -573,6 +509,27 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $this->setBottomText( $opts );
        }
 
+       /**
+        * Send the text to be displayed above the options
+        *
+        * @param FormOptions $opts Unused
+        */
+       function setTopText( FormOptions $opts ) {
+               global $wgContLang;
+
+               $message = $this->msg( 'recentchangestext' )->inContentLanguage();
+               if ( !$message->isDisabled() ) {
+                       $this->getOutput()->addWikiText(
+                               Html::rawElement( 'p',
+                                       array( 'lang' => $wgContLang->getCode(), 'dir' => $wgContLang->getDir() ),
+                                       "\n" . $message->plain() . "\n"
+                               ),
+                               /* $lineStart */ false,
+                               /* $interface */ false
+                       );
+               }
+       }
+
        /**
         * Get options to be displayed in a form
         *
@@ -606,24 +563,72 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        /**
-        * Send the text to be displayed above the options
+        * Add page-specific modules.
+        */
+       protected function addModules() {
+               parent::addModules();
+               $out = $this->getOutput();
+               $out->addModules( 'mediawiki.special.recentchanges' );
+       }
+
+       /**
+        * Get last modified date, for client caching
+        * Don't use this if we are using the patrol feature, patrol changes don't
+        * update the timestamp
         *
-        * @param FormOptions $opts Unused
+        * @param string $feedFormat
+        * @return string|bool
         */
-       function setTopText( FormOptions $opts ) {
-               global $wgContLang;
+       public function checkLastModified( $feedFormat ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __METHOD__ );
+               if ( $feedFormat || !$this->getUser()->useRCPatrol() ) {
+                       if ( $lastmod && $this->getOutput()->checkLastModified( $lastmod ) ) {
+                               # Client cache fresh and headers sent, nothing more to do.
+                               return false;
+                       }
+               }
 
-               $message = $this->msg( 'recentchangestext' )->inContentLanguage();
-               if ( !$message->isDisabled() ) {
-                       $this->getOutput()->addWikiText(
-                               Html::rawElement( 'p',
-                                       array( 'lang' => $wgContLang->getCode(), 'dir' => $wgContLang->getDir() ),
-                                       "\n" . $message->plain() . "\n"
-                               ),
-                               /* $lineStart */ false,
-                               /* $interface */ false
-                       );
+               return $lastmod;
+       }
+
+       /**
+        * Return an array with a ChangesFeed object and ChannelFeed object
+        *
+        * @param string $feedFormat Feed's format (either 'rss' or 'atom')
+        * @return array
+        */
+       public function getFeedObject( $feedFormat ) {
+               $changesFeed = new ChangesFeed( $feedFormat, 'rcfeed' );
+               $formatter = $changesFeed->getFeedObject(
+                       $this->msg( 'recentchanges' )->inContentLanguage()->text(),
+                       $this->msg( 'recentchanges-feed-description' )->inContentLanguage()->text(),
+                       $this->getPageTitle()->getFullURL()
+               );
+
+               return array( $changesFeed, $formatter );
+       }
+
+       /**
+        * Get the query string to append to feed link URLs.
+        *
+        * @return string
+        */
+       public function getFeedQuery() {
+               global $wgFeedLimit;
+
+               $this->getOptions()->validateIntBounds( 'limit', 0, $wgFeedLimit );
+               $options = $this->getOptions()->getChangedValues();
+
+               // wfArrayToCgi() omits options set to null or false
+               foreach ( $options as &$value ) {
+                       if ( $value === false ) {
+                               $value = '0';
+                       }
                }
+               unset( $value );
+
+               return wfArrayToCgi( $options );
        }
 
        /**
@@ -854,12 +859,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                return "{$note}$rclinks<br />$rclistfrom";
        }
 
-       /**
-        * Add page-specific modules.
-        */
-       protected function addModules() {
-               parent::addModules();
-               $out = $this->getOutput();
-               $out->addModules( 'mediawiki.special.recentchanges' );
+       public function isIncludable() {
+               return true;
        }
 }
index 9a9cd13..7cc8d30 100644 (file)
@@ -44,17 +44,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                $opts['target'] = $par;
        }
 
-       public function getFeedObject( $feedFormat ) {
-               $feed = new ChangesFeed( $feedFormat, false );
-               $feedObj = $feed->getFeedObject(
-                       $this->msg( 'recentchangeslinked-title', $this->getTargetTitle()->getPrefixedText() )
-                               ->inContentLanguage()->text(),
-                       $this->msg( 'recentchangeslinked-feed' )->inContentLanguage()->text(),
-                       $this->getPageTitle()->getFullURL()
-               );
-               return array( $feed, $feedObj );
-       }
-
        public function doMainQuery( $conds, $opts ) {
                $target = $opts['target'];
                $showlinkedto = $opts['showlinkedto'];
@@ -219,6 +208,13 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                return $res;
        }
 
+       function setTopText( FormOptions $opts ) {
+               $target = $this->getTargetTitle();
+               if ( $target ) {
+                       $this->getOutput()->addBacklinkSubtitle( $target );
+               }
+       }
+
        /**
         * Get options to be displayed in a form
         *
@@ -238,6 +234,17 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                return $extraOpts;
        }
 
+       public function getFeedObject( $feedFormat ) {
+               $feed = new ChangesFeed( $feedFormat, false );
+               $feedObj = $feed->getFeedObject(
+                       $this->msg( 'recentchangeslinked-title', $this->getTargetTitle()->getPrefixedText() )
+                               ->inContentLanguage()->text(),
+                       $this->msg( 'recentchangeslinked-feed' )->inContentLanguage()->text(),
+                       $this->getPageTitle()->getFullURL()
+               );
+               return array( $feed, $feedObj );
+       }
+
        /**
         * @return Title
         */
@@ -252,11 +259,4 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                }
                return $this->rclTargetTitle;
        }
-
-       function setTopText( FormOptions $opts ) {
-               $target = $this->getTargetTitle();
-               if ( $target ) {
-                       $this->getOutput()->addBacklinkSubtitle( $target );
-               }
-       }
 }
index 7432a40..fc48421 100644 (file)
@@ -28,77 +28,6 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                parent::__construct( $page, $restriction );
        }
 
-       /**
-        * Get a FormOptions object containing the default options
-        *
-        * @return FormOptions
-        */
-       public function getDefaultOptions() {
-               $opts = parent::getDefaultOptions();
-               $user = $this->getUser();
-
-               $opts->add( 'days', $user->getOption( 'watchlistdays' ), FormOptions::FLOAT );
-
-               $opts->add( 'hideminor', $user->getBoolOption( 'watchlisthideminor' ) );
-               $opts->add( 'hidebots', $user->getBoolOption( 'watchlisthidebots' ) );
-               $opts->add( 'hideanons', $user->getBoolOption( 'watchlisthideanons' ) );
-               $opts->add( 'hideliu', $user->getBoolOption( 'watchlisthideliu' ) );
-               $opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
-               $opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
-
-               $opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) );
-
-               return $opts;
-       }
-
-       /**
-        * Fetch values for a FormOptions object from the WebRequest associated with this instance.
-        *
-        * Maps old pre-1.23 request parameters Watchlist used to use (different from Recentchanges' ones)
-        * to the current ones.
-        *
-        * @param FormOptions $parameters
-        * @return FormOptions
-        */
-       protected function fetchOptionsFromRequest( $opts ) {
-               static $compatibilityMap = array(
-                       'hideMinor' => 'hideminor',
-                       'hideBots' => 'hidebots',
-                       'hideAnons' => 'hideanons',
-                       'hideLiu' => 'hideliu',
-                       'hidePatrolled' => 'hidepatrolled',
-                       'hideOwn' => 'hidemyself',
-               );
-
-               $params = $this->getRequest()->getValues();
-               foreach ( $compatibilityMap as $from => $to ) {
-                       if ( isset( $params[$from] ) ) {
-                               $params[$to] = $params[$from];
-                               unset( $params[$from] );
-                       }
-               }
-
-               // Not the prettiest way to achieve this… FormOptions internally depends on data sanitization
-               // methods defined on WebRequest and removing this dependency would cause some code duplication.
-               $request = new DerivativeRequest( $this->getRequest(), $params );
-               $opts->fetchValuesFromRequest( $request );
-               return $opts;
-       }
-
-       /**
-        * Get custom show/hide filters
-        *
-        * @return array Map of filter URL param names to properties (msg/default)
-        */
-       protected function getCustomFilters() {
-               if ( $this->customFilters === null ) {
-                       $this->customFilters = array();
-                       wfRunHooks( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ) );
-               }
-
-               return $this->customFilters;
-       }
-
        /**
         * Execute
         * @param $par Parameter passed to the page
@@ -181,6 +110,77 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $rows->free();
        }
 
+       /**
+        * Get a FormOptions object containing the default options
+        *
+        * @return FormOptions
+        */
+       public function getDefaultOptions() {
+               $opts = parent::getDefaultOptions();
+               $user = $this->getUser();
+
+               $opts->add( 'days', $user->getOption( 'watchlistdays' ), FormOptions::FLOAT );
+
+               $opts->add( 'hideminor', $user->getBoolOption( 'watchlisthideminor' ) );
+               $opts->add( 'hidebots', $user->getBoolOption( 'watchlisthidebots' ) );
+               $opts->add( 'hideanons', $user->getBoolOption( 'watchlisthideanons' ) );
+               $opts->add( 'hideliu', $user->getBoolOption( 'watchlisthideliu' ) );
+               $opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
+               $opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
+
+               $opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) );
+
+               return $opts;
+       }
+
+       /**
+        * Get custom show/hide filters
+        *
+        * @return array Map of filter URL param names to properties (msg/default)
+        */
+       protected function getCustomFilters() {
+               if ( $this->customFilters === null ) {
+                       $this->customFilters = array();
+                       wfRunHooks( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ) );
+               }
+
+               return $this->customFilters;
+       }
+
+       /**
+        * Fetch values for a FormOptions object from the WebRequest associated with this instance.
+        *
+        * Maps old pre-1.23 request parameters Watchlist used to use (different from Recentchanges' ones)
+        * to the current ones.
+        *
+        * @param FormOptions $parameters
+        * @return FormOptions
+        */
+       protected function fetchOptionsFromRequest( $opts ) {
+               static $compatibilityMap = array(
+                       'hideMinor' => 'hideminor',
+                       'hideBots' => 'hidebots',
+                       'hideAnons' => 'hideanons',
+                       'hideLiu' => 'hideliu',
+                       'hidePatrolled' => 'hidepatrolled',
+                       'hideOwn' => 'hidemyself',
+               );
+
+               $params = $this->getRequest()->getValues();
+               foreach ( $compatibilityMap as $from => $to ) {
+                       if ( isset( $params[$from] ) ) {
+                               $params[$to] = $params[$from];
+                               unset( $params[$from] );
+                       }
+               }
+
+               // Not the prettiest way to achieve this… FormOptions internally depends on data sanitization
+               // methods defined on WebRequest and removing this dependency would cause some code duplication.
+               $request = new DerivativeRequest( $this->getRequest(), $params );
+               $opts->fetchValuesFromRequest( $request );
+               return $opts;
+       }
+
        /**
         * Return an array of conditions depending of options set in $opts
         *
@@ -414,52 +414,6 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                }
        }
 
-       function setTopText( FormOptions $opts ) {
-               global $wgEnotifWatchlist, $wgShowUpdatedMarker;
-
-               $nondefaults = $opts->getChangedValues();
-               $form = "";
-               $user = $this->getUser();
-
-               # Show watchlist header
-               $form .= "<p>";
-               $form .= $this->msg( 'watchlist-details' )->numParams( $this->numItems )->parse() . "\n";
-               if ( $wgEnotifWatchlist && $user->getOption( 'enotifwatchlistpages' ) ) {
-                       $form .= $this->msg( 'wlheader-enotif' )->parse() . "\n";
-               }
-               if ( $wgShowUpdatedMarker ) {
-                       $form .= $this->msg( 'wlheader-showupdated' )->parse() . "\n";
-               }
-               $form .= "</p>";
-
-               if ( $wgShowUpdatedMarker ) {
-                       $form .= Xml::openElement( 'form', array( 'method' => 'post',
-                               'action' => $this->getPageTitle()->getLocalURL(),
-                               'id' => 'mw-watchlist-resetbutton' ) ) . "\n" .
-                       Xml::submitButton( $this->msg( 'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . "\n" .
-                       Html::hidden( 'reset', 'all' ) . "\n";
-                       foreach ( $nondefaults as $key => $value ) {
-                               $form .= Html::hidden( $key, $value ) . "\n";
-                       }
-                       $form .= Xml::closeElement( 'form' ) . "\n";
-               }
-
-               $form .= Xml::openElement( 'form', array(
-                       'method' => 'post',
-                       'action' => $this->getPageTitle()->getLocalURL(),
-                       'id' => 'mw-watchlist-form'
-               ) );
-               $form .= Xml::fieldset(
-                       $this->msg( 'watchlist-options' )->text(),
-                       false,
-                       array( 'id' => 'mw-watchlist-options' )
-               );
-
-               $form .= SpecialRecentChanges::makeLegend( $this->getContext() );
-
-               $this->getOutput()->addHTML( $form );
-       }
-
        /**
         * Return the text to be displayed above the changes
         *
@@ -555,6 +509,52 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $this->setBottomText( $opts );
        }
 
+       function setTopText( FormOptions $opts ) {
+               global $wgEnotifWatchlist, $wgShowUpdatedMarker;
+
+               $nondefaults = $opts->getChangedValues();
+               $form = "";
+               $user = $this->getUser();
+
+               # Show watchlist header
+               $form .= "<p>";
+               $form .= $this->msg( 'watchlist-details' )->numParams( $this->numItems )->parse() . "\n";
+               if ( $wgEnotifWatchlist && $user->getOption( 'enotifwatchlistpages' ) ) {
+                       $form .= $this->msg( 'wlheader-enotif' )->parse() . "\n";
+               }
+               if ( $wgShowUpdatedMarker ) {
+                       $form .= $this->msg( 'wlheader-showupdated' )->parse() . "\n";
+               }
+               $form .= "</p>";
+
+               if ( $wgShowUpdatedMarker ) {
+                       $form .= Xml::openElement( 'form', array( 'method' => 'post',
+                               'action' => $this->getPageTitle()->getLocalURL(),
+                               'id' => 'mw-watchlist-resetbutton' ) ) . "\n" .
+                       Xml::submitButton( $this->msg( 'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . "\n" .
+                       Html::hidden( 'reset', 'all' ) . "\n";
+                       foreach ( $nondefaults as $key => $value ) {
+                               $form .= Html::hidden( $key, $value ) . "\n";
+                       }
+                       $form .= Xml::closeElement( 'form' ) . "\n";
+               }
+
+               $form .= Xml::openElement( 'form', array(
+                       'method' => 'post',
+                       'action' => $this->getPageTitle()->getLocalURL(),
+                       'id' => 'mw-watchlist-form'
+               ) );
+               $form .= Xml::fieldset(
+                       $this->msg( 'watchlist-options' )->text(),
+                       false,
+                       array( 'id' => 'mw-watchlist-options' )
+               );
+
+               $form .= SpecialRecentChanges::makeLegend( $this->getContext() );
+
+               $this->getOutput()->addHTML( $form );
+       }
+
        protected function showHideLink( $options, $message, $name, $value ) {
                $label = $this->msg( $value ? 'show' : 'hide' )->escaped();
                $options[$name] = 1 - (int)$value;