From eabfeda3bec5d8108bddf143cf677aa301d8073b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bartosz=20Dziewo=C5=84ski?= Date: Mon, 9 Dec 2013 12:11:14 +0100 Subject: [PATCH] Create ChangesListSpecialPage as a base class for Watchlist and RC Most of the code has yet to be actually moved there, but it's a start! It's full of @todo comments, I'm going to go through them later. This should introduce no functional changes and no backwards incompatibilities. Verified that all special pages being subclasses work as they did before (SpecialRecentChanges, SpecialWatchlist, SpecialRecentChangesLinked). Change-Id: Icb7671e92a9255619e047ccbe5f457aa22581479 --- includes/AutoLoader.php | 1 + .../specialpage/ChangesListSpecialPage.php | 293 ++++++++++++++++++ includes/specials/SpecialRecentchanges.php | 136 +------- includes/specials/SpecialWatchlist.php | 32 +- 4 files changed, 301 insertions(+), 161 deletions(-) create mode 100644 includes/specialpage/ChangesListSpecialPage.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f179e78852..dde07b415b 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -47,6 +47,7 @@ $wgAutoloadLocalClasses = array( 'CategoryPage' => 'includes/CategoryPage.php', 'CategoryViewer' => 'includes/CategoryViewer.php', 'ChangesFeed' => 'includes/ChangesFeed.php', + 'ChangesListSpecialPage' => 'includes/specialpage/ChangesListSpecialPage.php', 'ChangeTags' => 'includes/ChangeTags.php', 'ChannelFeed' => 'includes/Feed.php', 'Collation' => 'includes/Collation.php', diff --git a/includes/specialpage/ChangesListSpecialPage.php b/includes/specialpage/ChangesListSpecialPage.php new file mode 100644 index 0000000000..cedd49c0e7 --- /dev/null +++ b/includes/specialpage/ChangesListSpecialPage.php @@ -0,0 +1,293 @@ +add( 'namespace', '', FormOptions::INTNULL ); + $opts->add( 'invert', false ); + $opts->add( 'associated', false ); + + return $opts; + } + + /** + * Create a FormOptions object with options as specified by the user + * + * @param array $parameters + * + * @return FormOptions + */ + public function setup( $parameters ) { + $opts = $this->getDefaultOptions(); + foreach ( $this->getCustomFilters() as $key => $params ) { + $opts->add( $key, $params['default'] ); + } + + $opts = $this->fetchOptionsFromRequest( $opts ); + + // Give precedence to subpage syntax + if ( $parameters !== null ) { + $this->parseParameters( $parameters, $opts ); + } + + $this->validateOptions( $opts ); + + return $opts; + } + + /** + * Validate a FormOptions object generated by getDefaultOptions() with values already populated. + * + * @param FormOptions $opts + */ + public function validateOptions( FormOptions $opts ) { + // nothing by default + } + + /** + * Fetch values for a FormOptions object from the WebRequest associated with this instance. + * + * Intended for subclassing, e.g. to add a backwards-compatibility layer. + * + * @param FormOptions $parameters + * @return FormOptions + */ + protected function fetchOptionsFromRequest( $opts ) { + $opts->fetchValuesFromRequest( $this->getRequest() ); + 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. + * + * @param string $par + * @param FormOptions $opts + */ + public function parseParameters( $par, FormOptions $opts ) { + // nothing by default + } + + /** + * 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 + * + * @param string $subpage + */ + public function execute( $subpage ) { + $this->rcSubpage = $subpage; + throw new MWException( "Not implemented" ); + } + + /** + * Return an array of conditions depending of options set in $opts + * @todo This should build some basic conditions here… + * @todo Not called by anything, should be called by execute() + * + * @param FormOptions $opts + * @return array + */ + abstract public function buildMainQueryConds( FormOptions $opts ); + + /** + * Process the query + * @todo This should build some basic processing here… + * @todo Not called by anything, should be called by execute() + * + * @param array $conds + * @param FormOptions $opts + * @return bool|ResultWrapper Result or false (for Recentchangeslinked only) + */ + abstract public function doMainQuery( $conds, $opts ); + + /** + * Send output to the OutputPage object, only called if not used feeds + * @todo This should do most, if not all, of the outputting now done by subclasses + * @todo Not called by anything, should be called by execute() + * + * @param array $rows Database rows + * @param FormOptions $opts + */ + abstract public function webOutput( $rows, $opts ); + + /** + * Return the text to be displayed above the changes + * @todo Not called by anything, should be called by webOutput() + * + * @param FormOptions $opts + * @return string XHTML + */ + public function doHeader( $opts ) { + $this->setTopText( $opts ); + + // @todo Lots of stuff should be done here. + + $this->setBottomText( $opts ); + } + + /** + * Get options to be displayed in a form + * @todo This should handle options returned by getDefaultOptions(). + * @todo Not called by anything, should be called by something… doHeader() maybe? + * + * @param FormOptions $opts + * @return array + */ + function getExtraOptions( $opts ) { + return array(); + } + + /** + * Return the legend displayed within the fieldset + * @todo This should not be static, then we can drop the parameter + * @todo Not called by anything, should be called by doHeader() + * + * @param $context the object available as $this in non-static functions + * @return string + */ + public static function makeLegend( IContextSource $context ) { + global $wgRecentChangesFlags; + $user = $context->getUser(); + # The legend showing what the letters and stuff mean + $legend = Xml::openElement( 'dl' ) . "\n"; + # Iterates through them and gets the messages for both letter and tooltip + $legendItems = $wgRecentChangesFlags; + if ( !$user->useRCPatrol() ) { + unset( $legendItems['unpatrolled'] ); + } + foreach ( $legendItems as $key => $legendInfo ) { # generate items of the legend + $label = $legendInfo['title']; + $letter = $legendInfo['letter']; + $cssClass = isset( $legendInfo['class'] ) ? $legendInfo['class'] : $key; + + $legend .= Xml::element( 'dt', + array( 'class' => $cssClass ), $context->msg( $letter )->text() + ) . "\n"; + if ( $key === 'newpage' ) { + $legend .= Xml::openElement( 'dd' ); + $legend .= $context->msg( $label )->escaped(); + $legend .= ' ' . $context->msg( 'recentchanges-legend-newpage' )->parse(); + $legend .= Xml::closeElement( 'dd' ) . "\n"; + } else { + $legend .= Xml::element( 'dd', array(), + $context->msg( $label )->text() + ) . "\n"; + } + } + # (+-123) + $legend .= Xml::tags( 'dt', + array( 'class' => 'mw-plusminus-pos' ), + $context->msg( 'recentchanges-legend-plusminus' )->parse() + ) . "\n"; + $legend .= Xml::element( + 'dd', + array( 'class' => 'mw-changeslist-legend-plusminus' ), + $context->msg( 'recentchanges-label-plusminus' )->text() + ) . "\n"; + $legend .= Xml::closeElement( 'dl' ) . "\n"; + + # Collapsibility + $legend = + '
' . + $context->msg( 'recentchanges-legend-heading' )->parse() . + '
' . $legend . '
' . + '
'; + + 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() + */ + protected function addModules() { + $out = $this->getOutput(); + // These modules include styles and behavior for the legend box, load them unconditionally + $out->addModuleStyles( 'mediawiki.special.changeslist' ); + $out->addModules( 'mediawiki.special.changeslist.js' ); + } + + protected function getGroupName() { + return 'changes'; + } +} diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php index 90abe1ca3f..37fa32c4fd 100644 --- a/includes/specials/SpecialRecentchanges.php +++ b/includes/specials/SpecialRecentchanges.php @@ -26,10 +26,7 @@ * * @ingroup SpecialPage */ -class SpecialRecentChanges extends SpecialPage { - var $rcOptions, $rcSubpage; - protected $customFilters; - +class SpecialRecentChanges extends ChangesListSpecialPage { /** * The feed format to output as (either 'rss' or 'atom'), or null if no * feed output was requested @@ -52,7 +49,7 @@ class SpecialRecentChanges extends SpecialPage { * @return FormOptions */ public function getDefaultOptions() { - $opts = new FormOptions(); + $opts = parent::getDefaultOptions(); $user = $this->getUser(); $opts->add( 'days', $user->getIntOption( 'rcdays' ) ); @@ -66,10 +63,6 @@ class SpecialRecentChanges extends SpecialPage { $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) ); $opts->add( 'hidemyself', false ); - $opts->add( 'namespace', '', FormOptions::INTNULL ); - $opts->add( 'invert', false ); - $opts->add( 'associated', false ); - $opts->add( 'categories', '' ); $opts->add( 'categories_any', false ); $opts->add( 'tagfilter', '' ); @@ -77,43 +70,9 @@ class SpecialRecentChanges extends SpecialPage { return $opts; } - /** - * Create a FormOptions object with options as specified by the user - * - * @param array $parameters - * @return FormOptions - */ - public function setup( $parameters ) { + public function validateOptions( FormOptions $opts ) { global $wgFeedLimit; - - $opts = $this->getDefaultOptions(); - foreach ( $this->getCustomFilters() as $key => $params ) { - $opts->add( $key, $params['default'] ); - } - - $opts = $this->fetchOptionsFromRequest( $opts ); - - // Give precedence to subpage syntax - if ( $parameters !== null ) { - $this->parseParameters( $parameters, $opts ); - } - $opts->validateIntBounds( 'limit', 0, $this->feedFormat ? $wgFeedLimit : 5000 ); - - return $opts; - } - - /** - * Fetch values for a FormOptions object from the WebRequest associated with this instance. - * - * Intended for subclassing, e.g. to add a backwards-compatibility layer. - * - * @param FormOptions $parameters - * @return FormOptions - */ - protected function fetchOptionsFromRequest( $opts ) { - $opts->fetchValuesFromRequest( $this->getRequest() ); - return $opts; } /** @@ -130,17 +89,6 @@ class SpecialRecentChanges extends SpecialPage { return $this->customFilters; } - /** - * Get the current FormOptions for this request - */ - public function getOptions() { - if ( $this->rcOptions === null ) { - $this->rcOptions = $this->setup( $this->rcSubpage ); - } - - return $this->rcOptions; - } - /** * Main execution point * @@ -212,8 +160,7 @@ class SpecialRecentChanges extends SpecialPage { } /** - * Process $par and put options found if $opts - * Mainly used when including the page + * Process $par and put options found in $opts. Used when including the page. * * @param string $par * @param FormOptions $opts @@ -658,65 +605,6 @@ class SpecialRecentChanges extends SpecialPage { return $extraOpts; } - /** - * Return the legend displayed within the fieldset. - * - * This method is also called from SpecialWatchlist. - * - * @param $context the object available as $this in non-static functions - * @return string - */ - public static function makeLegend( IContextSource $context ) { - global $wgRecentChangesFlags; - $user = $context->getUser(); - # The legend showing what the letters and stuff mean - $legend = Xml::openElement( 'dl' ) . "\n"; - # Iterates through them and gets the messages for both letter and tooltip - $legendItems = $wgRecentChangesFlags; - if ( !$user->useRCPatrol() ) { - unset( $legendItems['unpatrolled'] ); - } - foreach ( $legendItems as $key => $legendInfo ) { # generate items of the legend - $label = $legendInfo['title']; - $letter = $legendInfo['letter']; - $cssClass = isset( $legendInfo['class'] ) ? $legendInfo['class'] : $key; - - $legend .= Xml::element( 'dt', - array( 'class' => $cssClass ), $context->msg( $letter )->text() - ) . "\n"; - if ( $key === 'newpage' ) { - $legend .= Xml::openElement( 'dd' ); - $legend .= $context->msg( $label )->escaped(); - $legend .= ' ' . $context->msg( 'recentchanges-legend-newpage' )->parse(); - $legend .= Xml::closeElement( 'dd' ) . "\n"; - } else { - $legend .= Xml::element( 'dd', array(), - $context->msg( $label )->text() - ) . "\n"; - } - } - # (+-123) - $legend .= Xml::tags( 'dt', - array( 'class' => 'mw-plusminus-pos' ), - $context->msg( 'recentchanges-legend-plusminus' )->parse() - ) . "\n"; - $legend .= Xml::element( - 'dd', - array( 'class' => 'mw-changeslist-legend-plusminus' ), - $context->msg( 'recentchanges-label-plusminus' )->text() - ) . "\n"; - $legend .= Xml::closeElement( 'dl' ) . "\n"; - - # Collapsibility - $legend = - '
' . - $context->msg( 'recentchanges-legend-heading' )->parse() . - '
' . $legend . '
' . - '
'; - - return $legend; - } - /** * Send the text to be displayed above the options * @@ -738,14 +626,6 @@ class SpecialRecentChanges extends SpecialPage { } } - /** - * Send the text to be displayed after the options, for use in subclasses. - * - * @param FormOptions $opts - */ - function setBottomText( FormOptions $opts ) { - } - /** * Creates the choose namespace selection * @@ -978,14 +858,8 @@ class SpecialRecentChanges extends SpecialPage { * Add page-specific modules. */ protected function addModules() { + parent::addModules(); $out = $this->getOutput(); $out->addModules( 'mediawiki.special.recentchanges' ); - // This modules include styles and behavior for the legend box, load it unconditionally - $out->addModuleStyles( 'mediawiki.special.changeslist' ); - $out->addModules( 'mediawiki.special.changeslist.js' ); - } - - protected function getGroupName() { - return 'changes'; } } diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index ac41340501..7432a40a3b 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -20,9 +20,7 @@ * @file * @ingroup SpecialPage Watchlist */ -class SpecialWatchlist extends SpecialRecentChanges { - protected $customFilters; - +class SpecialWatchlist extends ChangesListSpecialPage { /** * Constructor */ @@ -30,10 +28,6 @@ class SpecialWatchlist extends SpecialRecentChanges { parent::__construct( $page, $restriction ); } - public function isIncludable() { - return false; - } - /** * Get a FormOptions object containing the default options * @@ -43,9 +37,8 @@ class SpecialWatchlist extends SpecialRecentChanges { $opts = parent::getDefaultOptions(); $user = $this->getUser(); - // Overwrite RC options with Watchlist options - // (calling #add() again is okay) $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' ) ); @@ -53,7 +46,6 @@ class SpecialWatchlist extends SpecialRecentChanges { $opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) ); $opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) ); - // Add new ones $opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) ); return $opts; @@ -107,26 +99,6 @@ class SpecialWatchlist extends SpecialRecentChanges { return $this->customFilters; } - /** - * Process $par and put options found if $opts. Not used for Watchlist. - * - * @param string $par - * @param FormOptions $opts - */ - public function parseParameters( $par, FormOptions $opts ) { - } - - /** - * Get the current FormOptions for this request - */ - public function getOptions() { - if ( $this->rcOptions === null ) { - $this->rcOptions = $this->setup( null ); - } - - return $this->rcOptions; - } - /** * Execute * @param $par Parameter passed to the page -- 2.20.1