MediaWiki
REL1_24
|
00001 <?php 00030 abstract class ChangesListSpecialPage extends SpecialPage { 00032 protected $rcSubpage; 00033 00035 protected $rcOptions; 00036 00038 protected $customFilters; 00039 00045 public function execute( $subpage ) { 00046 $this->rcSubpage = $subpage; 00047 00048 $this->setHeaders(); 00049 $this->outputHeader(); 00050 $this->addModules(); 00051 00052 $rows = $this->getRows(); 00053 $opts = $this->getOptions(); 00054 if ( $rows === false ) { 00055 if ( !$this->including() ) { 00056 $this->doHeader( $opts, 0 ); 00057 $this->getOutput()->setStatusCode( 404 ); 00058 } 00059 00060 return; 00061 } 00062 00063 $batch = new LinkBatch; 00064 foreach ( $rows as $row ) { 00065 $batch->add( NS_USER, $row->rc_user_text ); 00066 $batch->add( NS_USER_TALK, $row->rc_user_text ); 00067 $batch->add( $row->rc_namespace, $row->rc_title ); 00068 } 00069 $batch->execute(); 00070 00071 $this->webOutput( $rows, $opts ); 00072 00073 $rows->free(); 00074 } 00075 00081 public function getRows() { 00082 $opts = $this->getOptions(); 00083 $conds = $this->buildMainQueryConds( $opts ); 00084 00085 return $this->doMainQuery( $conds, $opts ); 00086 } 00087 00093 public function getOptions() { 00094 if ( $this->rcOptions === null ) { 00095 $this->rcOptions = $this->setup( $this->rcSubpage ); 00096 } 00097 00098 return $this->rcOptions; 00099 } 00100 00108 public function setup( $parameters ) { 00109 $opts = $this->getDefaultOptions(); 00110 foreach ( $this->getCustomFilters() as $key => $params ) { 00111 $opts->add( $key, $params['default'] ); 00112 } 00113 00114 $opts = $this->fetchOptionsFromRequest( $opts ); 00115 00116 // Give precedence to subpage syntax 00117 if ( $parameters !== null ) { 00118 $this->parseParameters( $parameters, $opts ); 00119 } 00120 00121 $this->validateOptions( $opts ); 00122 00123 return $opts; 00124 } 00125 00132 public function getDefaultOptions() { 00133 $opts = new FormOptions(); 00134 00135 $opts->add( 'hideminor', false ); 00136 $opts->add( 'hidebots', false ); 00137 $opts->add( 'hideanons', false ); 00138 $opts->add( 'hideliu', false ); 00139 $opts->add( 'hidepatrolled', false ); 00140 $opts->add( 'hidemyself', false ); 00141 00142 $opts->add( 'namespace', '', FormOptions::INTNULL ); 00143 $opts->add( 'invert', false ); 00144 $opts->add( 'associated', false ); 00145 00146 return $opts; 00147 } 00148 00154 protected function getCustomFilters() { 00155 if ( $this->customFilters === null ) { 00156 $this->customFilters = array(); 00157 wfRunHooks( 'ChangesListSpecialPageFilters', array( $this, &$this->customFilters ) ); 00158 } 00159 00160 return $this->customFilters; 00161 } 00162 00171 protected function fetchOptionsFromRequest( $opts ) { 00172 $opts->fetchValuesFromRequest( $this->getRequest() ); 00173 00174 return $opts; 00175 } 00176 00183 public function parseParameters( $par, FormOptions $opts ) { 00184 // nothing by default 00185 } 00186 00192 public function validateOptions( FormOptions $opts ) { 00193 // nothing by default 00194 } 00195 00202 public function buildMainQueryConds( FormOptions $opts ) { 00203 $dbr = $this->getDB(); 00204 $user = $this->getUser(); 00205 $conds = array(); 00206 00207 // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on 00208 // what the user meant and either show only bots or force anons to be shown. 00209 $botsonly = false; 00210 $hideanons = $opts['hideanons']; 00211 if ( $opts['hideanons'] && $opts['hideliu'] ) { 00212 if ( $opts['hidebots'] ) { 00213 $hideanons = false; 00214 } else { 00215 $botsonly = true; 00216 } 00217 } 00218 00219 // Toggles 00220 if ( $opts['hideminor'] ) { 00221 $conds['rc_minor'] = 0; 00222 } 00223 if ( $opts['hidebots'] ) { 00224 $conds['rc_bot'] = 0; 00225 } 00226 if ( $user->useRCPatrol() && $opts['hidepatrolled'] ) { 00227 $conds['rc_patrolled'] = 0; 00228 } 00229 if ( $botsonly ) { 00230 $conds['rc_bot'] = 1; 00231 } else { 00232 if ( $opts['hideliu'] ) { 00233 $conds[] = 'rc_user = 0'; 00234 } 00235 if ( $hideanons ) { 00236 $conds[] = 'rc_user != 0'; 00237 } 00238 } 00239 if ( $opts['hidemyself'] ) { 00240 if ( $user->getId() ) { 00241 $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() ); 00242 } else { 00243 $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() ); 00244 } 00245 } 00246 00247 // Namespace filtering 00248 if ( $opts['namespace'] !== '' ) { 00249 $selectedNS = $dbr->addQuotes( $opts['namespace'] ); 00250 $operator = $opts['invert'] ? '!=' : '='; 00251 $boolean = $opts['invert'] ? 'AND' : 'OR'; 00252 00253 // Namespace association (bug 2429) 00254 if ( !$opts['associated'] ) { 00255 $condition = "rc_namespace $operator $selectedNS"; 00256 } else { 00257 // Also add the associated namespace 00258 $associatedNS = $dbr->addQuotes( 00259 MWNamespace::getAssociated( $opts['namespace'] ) 00260 ); 00261 $condition = "(rc_namespace $operator $selectedNS " 00262 . $boolean 00263 . " rc_namespace $operator $associatedNS)"; 00264 } 00265 00266 $conds[] = $condition; 00267 } 00268 00269 return $conds; 00270 } 00271 00279 public function doMainQuery( $conds, $opts ) { 00280 $tables = array( 'recentchanges' ); 00281 $fields = RecentChange::selectFields(); 00282 $query_options = array(); 00283 $join_conds = array(); 00284 00285 ChangeTags::modifyDisplayQuery( 00286 $tables, 00287 $fields, 00288 $conds, 00289 $join_conds, 00290 $query_options, 00291 '' 00292 ); 00293 00294 if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds, 00295 $opts ) 00296 ) { 00297 return false; 00298 } 00299 00300 $dbr = $this->getDB(); 00301 00302 return $dbr->select( 00303 $tables, 00304 $fields, 00305 $conds, 00306 __METHOD__, 00307 $query_options, 00308 $join_conds 00309 ); 00310 } 00311 00312 protected function runMainQueryHook( &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) { 00313 return wfRunHooks( 00314 'ChangesListSpecialPageQuery', 00315 array( $this->getName(), &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) 00316 ); 00317 } 00318 00324 protected function getDB() { 00325 return wfGetDB( DB_SLAVE ); 00326 } 00327 00334 public function webOutput( $rows, $opts ) { 00335 if ( !$this->including() ) { 00336 $this->outputFeedLinks(); 00337 $this->doHeader( $opts, $rows->numRows() ); 00338 } 00339 00340 $this->outputChangesList( $rows, $opts ); 00341 } 00342 00346 public function outputFeedLinks() { 00347 // nothing by default 00348 } 00349 00356 abstract public function outputChangesList( $rows, $opts ); 00357 00364 public function doHeader( $opts, $numRows ) { 00365 $this->setTopText( $opts ); 00366 00367 // @todo Lots of stuff should be done here. 00368 00369 $this->setBottomText( $opts ); 00370 } 00371 00378 function setTopText( FormOptions $opts ) { 00379 // nothing by default 00380 } 00381 00388 function setBottomText( FormOptions $opts ) { 00389 // nothing by default 00390 } 00391 00400 function getExtraOptions( $opts ) { 00401 return array(); 00402 } 00403 00412 public static function makeLegend( IContextSource $context ) { 00413 $user = $context->getUser(); 00414 # The legend showing what the letters and stuff mean 00415 $legend = Html::openElement( 'dl' ) . "\n"; 00416 # Iterates through them and gets the messages for both letter and tooltip 00417 $legendItems = $context->getConfig()->get( 'RecentChangesFlags' ); 00418 if ( !( $user->useRCPatrol() || $user->useNPPatrol() ) ) { 00419 unset( $legendItems['unpatrolled'] ); 00420 } 00421 foreach ( $legendItems as $key => $item ) { # generate items of the legend 00422 $label = isset( $item['legend'] ) ? $item['legend'] : $item['title']; 00423 $letter = $item['letter']; 00424 $cssClass = isset( $item['class'] ) ? $item['class'] : $key; 00425 00426 $legend .= Html::element( 'dt', 00427 array( 'class' => $cssClass ), $context->msg( $letter )->text() 00428 ) . "\n" . 00429 Html::rawElement( 'dd', array(), 00430 $context->msg( $label )->parse() 00431 ) . "\n"; 00432 } 00433 # (+-123) 00434 $legend .= Html::rawElement( 'dt', 00435 array( 'class' => 'mw-plusminus-pos' ), 00436 $context->msg( 'recentchanges-legend-plusminus' )->parse() 00437 ) . "\n"; 00438 $legend .= Html::element( 00439 'dd', 00440 array( 'class' => 'mw-changeslist-legend-plusminus' ), 00441 $context->msg( 'recentchanges-label-plusminus' )->text() 00442 ) . "\n"; 00443 $legend .= Html::closeElement( 'dl' ) . "\n"; 00444 00445 # Collapsibility 00446 $legend = 00447 '<div class="mw-changeslist-legend">' . 00448 $context->msg( 'recentchanges-legend-heading' )->parse() . 00449 '<div class="mw-collapsible-content">' . $legend . '</div>' . 00450 '</div>'; 00451 00452 return $legend; 00453 } 00454 00458 protected function addModules() { 00459 $out = $this->getOutput(); 00460 // Styles and behavior for the legend box (see makeLegend()) 00461 $out->addModuleStyles( 'mediawiki.special.changeslist.legend' ); 00462 $out->addModules( 'mediawiki.special.changeslist.legend.js' ); 00463 } 00464 00465 protected function getGroupName() { 00466 return 'changes'; 00467 } 00468 }