[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |