[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/specialpage/ -> ChangesListSpecialPage.php (source)

   1  <?php
   2  /**
   3   * Special page which uses a ChangesList to show query results.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup SpecialPage
  22   */
  23  
  24  /**
  25   * Special page which uses a ChangesList to show query results.
  26   * @todo Way too many public functions, most of them should be protected
  27   *
  28   * @ingroup SpecialPage
  29   */
  30  abstract class ChangesListSpecialPage extends SpecialPage {
  31      /** @var string */
  32      protected $rcSubpage;
  33  
  34      /** @var FormOptions */
  35      protected $rcOptions;
  36  
  37      /** @var array */
  38      protected $customFilters;
  39  
  40      /**
  41       * Main execution point
  42       *
  43       * @param string $subpage
  44       */
  45  	public function execute( $subpage ) {
  46          $this->rcSubpage = $subpage;
  47  
  48          $this->setHeaders();
  49          $this->outputHeader();
  50          $this->addModules();
  51  
  52          $rows = $this->getRows();
  53          $opts = $this->getOptions();
  54          if ( $rows === false ) {
  55              if ( !$this->including() ) {
  56                  $this->doHeader( $opts, 0 );
  57                  $this->getOutput()->setStatusCode( 404 );
  58              }
  59  
  60              return;
  61          }
  62  
  63          $batch = new LinkBatch;
  64          foreach ( $rows as $row ) {
  65              $batch->add( NS_USER, $row->rc_user_text );
  66              $batch->add( NS_USER_TALK, $row->rc_user_text );
  67              $batch->add( $row->rc_namespace, $row->rc_title );
  68          }
  69          $batch->execute();
  70  
  71          $this->webOutput( $rows, $opts );
  72  
  73          $rows->free();
  74      }
  75  
  76      /**
  77       * Get the database result for this special page instance. Used by ApiFeedRecentChanges.
  78       *
  79       * @return bool|ResultWrapper Result or false
  80       */
  81  	public function getRows() {
  82          $opts = $this->getOptions();
  83          $conds = $this->buildMainQueryConds( $opts );
  84  
  85          return $this->doMainQuery( $conds, $opts );
  86      }
  87  
  88      /**
  89       * Get the current FormOptions for this request
  90       *
  91       * @return FormOptions
  92       */
  93  	public function getOptions() {
  94          if ( $this->rcOptions === null ) {
  95              $this->rcOptions = $this->setup( $this->rcSubpage );
  96          }
  97  
  98          return $this->rcOptions;
  99      }
 100  
 101      /**
 102       * Create a FormOptions object with options as specified by the user
 103       *
 104       * @param array $parameters
 105       *
 106       * @return FormOptions
 107       */
 108  	public function setup( $parameters ) {
 109          $opts = $this->getDefaultOptions();
 110          foreach ( $this->getCustomFilters() as $key => $params ) {
 111              $opts->add( $key, $params['default'] );
 112          }
 113  
 114          $opts = $this->fetchOptionsFromRequest( $opts );
 115  
 116          // Give precedence to subpage syntax
 117          if ( $parameters !== null ) {
 118              $this->parseParameters( $parameters, $opts );
 119          }
 120  
 121          $this->validateOptions( $opts );
 122  
 123          return $opts;
 124      }
 125  
 126      /**
 127       * Get a FormOptions object containing the default options. By default returns some basic options,
 128       * you might want to not call parent method and discard them, or to override default values.
 129       *
 130       * @return FormOptions
 131       */
 132  	public function getDefaultOptions() {
 133          $opts = new FormOptions();
 134  
 135          $opts->add( 'hideminor', false );
 136          $opts->add( 'hidebots', false );
 137          $opts->add( 'hideanons', false );
 138          $opts->add( 'hideliu', false );
 139          $opts->add( 'hidepatrolled', false );
 140          $opts->add( 'hidemyself', false );
 141  
 142          $opts->add( 'namespace', '', FormOptions::INTNULL );
 143          $opts->add( 'invert', false );
 144          $opts->add( 'associated', false );
 145  
 146          return $opts;
 147      }
 148  
 149      /**
 150       * Get custom show/hide filters
 151       *
 152       * @return array Map of filter URL param names to properties (msg/default)
 153       */
 154  	protected function getCustomFilters() {
 155          if ( $this->customFilters === null ) {
 156              $this->customFilters = array();
 157              wfRunHooks( 'ChangesListSpecialPageFilters', array( $this, &$this->customFilters ) );
 158          }
 159  
 160          return $this->customFilters;
 161      }
 162  
 163      /**
 164       * Fetch values for a FormOptions object from the WebRequest associated with this instance.
 165       *
 166       * Intended for subclassing, e.g. to add a backwards-compatibility layer.
 167       *
 168       * @param FormOptions $opts
 169       * @return FormOptions
 170       */
 171  	protected function fetchOptionsFromRequest( $opts ) {
 172          $opts->fetchValuesFromRequest( $this->getRequest() );
 173  
 174          return $opts;
 175      }
 176  
 177      /**
 178       * Process $par and put options found in $opts. Used when including the page.
 179       *
 180       * @param string $par
 181       * @param FormOptions $opts
 182       */
 183  	public function parseParameters( $par, FormOptions $opts ) {
 184          // nothing by default
 185      }
 186  
 187      /**
 188       * Validate a FormOptions object generated by getDefaultOptions() with values already populated.
 189       *
 190       * @param FormOptions $opts
 191       */
 192  	public function validateOptions( FormOptions $opts ) {
 193          // nothing by default
 194      }
 195  
 196      /**
 197       * Return an array of conditions depending of options set in $opts
 198       *
 199       * @param FormOptions $opts
 200       * @return array
 201       */
 202  	public function buildMainQueryConds( FormOptions $opts ) {
 203          $dbr = $this->getDB();
 204          $user = $this->getUser();
 205          $conds = array();
 206  
 207          // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on
 208          // what the user meant and either show only bots or force anons to be shown.
 209          $botsonly = false;
 210          $hideanons = $opts['hideanons'];
 211          if ( $opts['hideanons'] && $opts['hideliu'] ) {
 212              if ( $opts['hidebots'] ) {
 213                  $hideanons = false;
 214              } else {
 215                  $botsonly = true;
 216              }
 217          }
 218  
 219          // Toggles
 220          if ( $opts['hideminor'] ) {
 221              $conds['rc_minor'] = 0;
 222          }
 223          if ( $opts['hidebots'] ) {
 224              $conds['rc_bot'] = 0;
 225          }
 226          if ( $user->useRCPatrol() && $opts['hidepatrolled'] ) {
 227              $conds['rc_patrolled'] = 0;
 228          }
 229          if ( $botsonly ) {
 230              $conds['rc_bot'] = 1;
 231          } else {
 232              if ( $opts['hideliu'] ) {
 233                  $conds[] = 'rc_user = 0';
 234              }
 235              if ( $hideanons ) {
 236                  $conds[] = 'rc_user != 0';
 237              }
 238          }
 239          if ( $opts['hidemyself'] ) {
 240              if ( $user->getId() ) {
 241                  $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() );
 242              } else {
 243                  $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
 244              }
 245          }
 246  
 247          // Namespace filtering
 248          if ( $opts['namespace'] !== '' ) {
 249              $selectedNS = $dbr->addQuotes( $opts['namespace'] );
 250              $operator = $opts['invert'] ? '!=' : '=';
 251              $boolean = $opts['invert'] ? 'AND' : 'OR';
 252  
 253              // Namespace association (bug 2429)
 254              if ( !$opts['associated'] ) {
 255                  $condition = "rc_namespace $operator $selectedNS";
 256              } else {
 257                  // Also add the associated namespace
 258                  $associatedNS = $dbr->addQuotes(
 259                      MWNamespace::getAssociated( $opts['namespace'] )
 260                  );
 261                  $condition = "(rc_namespace $operator $selectedNS "
 262                      . $boolean
 263                      . " rc_namespace $operator $associatedNS)";
 264              }
 265  
 266              $conds[] = $condition;
 267          }
 268  
 269          return $conds;
 270      }
 271  
 272      /**
 273       * Process the query
 274       *
 275       * @param array $conds
 276       * @param FormOptions $opts
 277       * @return bool|ResultWrapper Result or false
 278       */
 279  	public function doMainQuery( $conds, $opts ) {
 280          $tables = array( 'recentchanges' );
 281          $fields = RecentChange::selectFields();
 282          $query_options = array();
 283          $join_conds = array();
 284  
 285          ChangeTags::modifyDisplayQuery(
 286              $tables,
 287              $fields,
 288              $conds,
 289              $join_conds,
 290              $query_options,
 291              ''
 292          );
 293  
 294          if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
 295              $opts )
 296          ) {
 297              return false;
 298          }
 299  
 300          $dbr = $this->getDB();
 301  
 302          return $dbr->select(
 303              $tables,
 304              $fields,
 305              $conds,
 306              __METHOD__,
 307              $query_options,
 308              $join_conds
 309          );
 310      }
 311  
 312  	protected function runMainQueryHook( &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) {
 313          return wfRunHooks(
 314              'ChangesListSpecialPageQuery',
 315              array( $this->getName(), &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts )
 316          );
 317      }
 318  
 319      /**
 320       * Return a DatabaseBase object for reading
 321       *
 322       * @return DatabaseBase
 323       */
 324  	protected function getDB() {
 325          return wfGetDB( DB_SLAVE );
 326      }
 327  
 328      /**
 329       * Send output to the OutputPage object, only called if not used feeds
 330       *
 331       * @param ResultWrapper $rows Database rows
 332       * @param FormOptions $opts
 333       */
 334  	public function webOutput( $rows, $opts ) {
 335          if ( !$this->including() ) {
 336              $this->outputFeedLinks();
 337              $this->doHeader( $opts, $rows->numRows() );
 338          }
 339  
 340          $this->outputChangesList( $rows, $opts );
 341      }
 342  
 343      /**
 344       * Output feed links.
 345       */
 346  	public function outputFeedLinks() {
 347          // nothing by default
 348      }
 349  
 350      /**
 351       * Build and output the actual changes list.
 352       *
 353       * @param array $rows Database rows
 354       * @param FormOptions $opts
 355       */
 356      abstract public function outputChangesList( $rows, $opts );
 357  
 358      /**
 359       * Set the text to be displayed above the changes
 360       *
 361       * @param FormOptions $opts
 362       * @param int $numRows Number of rows in the result to show after this header
 363       */
 364  	public function doHeader( $opts, $numRows ) {
 365          $this->setTopText( $opts );
 366  
 367          // @todo Lots of stuff should be done here.
 368  
 369          $this->setBottomText( $opts );
 370      }
 371  
 372      /**
 373       * Send the text to be displayed before the options. Should use $this->getOutput()->addWikiText()
 374       * or similar methods to print the text.
 375       *
 376       * @param FormOptions $opts
 377       */
 378  	function setTopText( FormOptions $opts ) {
 379          // nothing by default
 380      }
 381  
 382      /**
 383       * Send the text to be displayed after the options. Should use $this->getOutput()->addWikiText()
 384       * or similar methods to print the text.
 385       *
 386       * @param FormOptions $opts
 387       */
 388  	function setBottomText( FormOptions $opts ) {
 389          // nothing by default
 390      }
 391  
 392      /**
 393       * Get options to be displayed in a form
 394       * @todo This should handle options returned by getDefaultOptions().
 395       * @todo Not called by anything, should be called by something… doHeader() maybe?
 396       *
 397       * @param FormOptions $opts
 398       * @return array
 399       */
 400  	function getExtraOptions( $opts ) {
 401          return array();
 402      }
 403  
 404      /**
 405       * Return the legend displayed within the fieldset
 406       * @todo This should not be static, then we can drop the parameter
 407       * @todo Not called by anything, should be called by doHeader()
 408       *
 409       * @param IContextSource $context The object available as $this in non-static functions
 410       * @return string
 411       */
 412  	public static function makeLegend( IContextSource $context ) {
 413          $user = $context->getUser();
 414          # The legend showing what the letters and stuff mean
 415          $legend = Html::openElement( 'dl' ) . "\n";
 416          # Iterates through them and gets the messages for both letter and tooltip
 417          $legendItems = $context->getConfig()->get( 'RecentChangesFlags' );
 418          if ( !( $user->useRCPatrol() || $user->useNPPatrol() ) ) {
 419              unset( $legendItems['unpatrolled'] );
 420          }
 421          foreach ( $legendItems as $key => $item ) { # generate items of the legend
 422              $label = isset( $item['legend'] ) ? $item['legend'] : $item['title'];
 423              $letter = $item['letter'];
 424              $cssClass = isset( $item['class'] ) ? $item['class'] : $key;
 425  
 426              $legend .= Html::element( 'dt',
 427                  array( 'class' => $cssClass ), $context->msg( $letter )->text()
 428              ) . "\n" .
 429              Html::rawElement( 'dd', array(),
 430                  $context->msg( $label )->parse()
 431              ) . "\n";
 432          }
 433          # (+-123)
 434          $legend .= Html::rawElement( 'dt',
 435              array( 'class' => 'mw-plusminus-pos' ),
 436              $context->msg( 'recentchanges-legend-plusminus' )->parse()
 437          ) . "\n";
 438          $legend .= Html::element(
 439              'dd',
 440              array( 'class' => 'mw-changeslist-legend-plusminus' ),
 441              $context->msg( 'recentchanges-label-plusminus' )->text()
 442          ) . "\n";
 443          $legend .= Html::closeElement( 'dl' ) . "\n";
 444  
 445          # Collapsibility
 446          $legend =
 447              '<div class="mw-changeslist-legend">' .
 448                  $context->msg( 'recentchanges-legend-heading' )->parse() .
 449                  '<div class="mw-collapsible-content">' . $legend . '</div>' .
 450              '</div>';
 451  
 452          return $legend;
 453      }
 454  
 455      /**
 456       * Add page-specific modules.
 457       */
 458  	protected function addModules() {
 459          $out = $this->getOutput();
 460          // Styles and behavior for the legend box (see makeLegend())
 461          $out->addModuleStyles( 'mediawiki.special.changeslist.legend' );
 462          $out->addModules( 'mediawiki.special.changeslist.legend.js' );
 463      }
 464  
 465  	protected function getGroupName() {
 466          return 'changes';
 467      }
 468  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1