--- /dev/null
+<?php
+/**
+ * Special page which uses a ChangesList to show query results.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page which uses a ChangesList to show query results.
+ * @todo Way too many public functions, most of them should be protected
+ *
+ * @ingroup SpecialPage
+ */
+abstract class ChangesListSpecialPage extends SpecialPage {
+ var $rcSubpage, $rcOptions; // @todo Rename these, make protected
+ 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.
+ *
+ * @return FormOptions
+ */
+ public function getDefaultOptions() {
+ $opts = new FormOptions();
+
+ $opts->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 =
+ '<div class="mw-changeslist-legend">' .
+ $context->msg( 'recentchanges-legend-heading' )->parse() .
+ '<div class="mw-collapsible-content">' . $legend . '</div>' .
+ '</div>';
+
+ 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';
+ }
+}
*
* @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
* @return FormOptions
*/
public function getDefaultOptions() {
- $opts = new FormOptions();
+ $opts = parent::getDefaultOptions();
$user = $this->getUser();
$opts->add( 'days', $user->getIntOption( 'rcdays' ) );
$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', '' );
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;
}
/**
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
*
}
/**
- * 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
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 =
- '<div class="mw-changeslist-legend">' .
- $context->msg( 'recentchanges-legend-heading' )->parse() .
- '<div class="mw-collapsible-content">' . $legend . '</div>' .
- '</div>';
-
- return $legend;
- }
-
/**
* Send the text to be displayed above the options
*
}
}
- /**
- * 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
*
* 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';
}
}
* @file
* @ingroup SpecialPage Watchlist
*/
-class SpecialWatchlist extends SpecialRecentChanges {
- protected $customFilters;
-
+class SpecialWatchlist extends ChangesListSpecialPage {
/**
* Constructor
*/
parent::__construct( $page, $restriction );
}
- public function isIncludable() {
- return false;
- }
-
/**
* Get a FormOptions object containing the default options
*
$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' ) );
$opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
$opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
- // Add new ones
$opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) );
return $opts;
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