MediaWiki
REL1_23
|
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 } 00058 00059 return; 00060 } 00061 00062 $batch = new LinkBatch; 00063 foreach ( $rows as $row ) { 00064 $batch->add( NS_USER, $row->rc_user_text ); 00065 $batch->add( NS_USER_TALK, $row->rc_user_text ); 00066 $batch->add( $row->rc_namespace, $row->rc_title ); 00067 } 00068 $batch->execute(); 00069 00070 $this->webOutput( $rows, $opts ); 00071 00072 $rows->free(); 00073 } 00074 00080 public function getRows() { 00081 $opts = $this->getOptions(); 00082 $conds = $this->buildMainQueryConds( $opts ); 00083 00084 return $this->doMainQuery( $conds, $opts ); 00085 } 00086 00092 public function getOptions() { 00093 if ( $this->rcOptions === null ) { 00094 $this->rcOptions = $this->setup( $this->rcSubpage ); 00095 } 00096 00097 return $this->rcOptions; 00098 } 00099 00107 public function setup( $parameters ) { 00108 $opts = $this->getDefaultOptions(); 00109 foreach ( $this->getCustomFilters() as $key => $params ) { 00110 $opts->add( $key, $params['default'] ); 00111 } 00112 00113 $opts = $this->fetchOptionsFromRequest( $opts ); 00114 00115 // Give precedence to subpage syntax 00116 if ( $parameters !== null ) { 00117 $this->parseParameters( $parameters, $opts ); 00118 } 00119 00120 $this->validateOptions( $opts ); 00121 00122 return $opts; 00123 } 00124 00131 public function getDefaultOptions() { 00132 $opts = new FormOptions(); 00133 00134 $opts->add( 'hideminor', false ); 00135 $opts->add( 'hidebots', false ); 00136 $opts->add( 'hideanons', false ); 00137 $opts->add( 'hideliu', false ); 00138 $opts->add( 'hidepatrolled', false ); 00139 $opts->add( 'hidemyself', false ); 00140 00141 $opts->add( 'namespace', '', FormOptions::INTNULL ); 00142 $opts->add( 'invert', false ); 00143 $opts->add( 'associated', false ); 00144 00145 return $opts; 00146 } 00147 00153 protected function getCustomFilters() { 00154 if ( $this->customFilters === null ) { 00155 $this->customFilters = array(); 00156 wfRunHooks( 'ChangesListSpecialPageFilters', array( $this, &$this->customFilters ) ); 00157 } 00158 00159 return $this->customFilters; 00160 } 00161 00170 protected function fetchOptionsFromRequest( $opts ) { 00171 $opts->fetchValuesFromRequest( $this->getRequest() ); 00172 00173 return $opts; 00174 } 00175 00182 public function parseParameters( $par, FormOptions $opts ) { 00183 // nothing by default 00184 } 00185 00191 public function validateOptions( FormOptions $opts ) { 00192 // nothing by default 00193 } 00194 00201 public function buildMainQueryConds( FormOptions $opts ) { 00202 $dbr = $this->getDB(); 00203 $user = $this->getUser(); 00204 $conds = array(); 00205 00206 // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on 00207 // what the user meant and either show only bots or force anons to be shown. 00208 $botsonly = false; 00209 $hideanons = $opts['hideanons']; 00210 if ( $opts['hideanons'] && $opts['hideliu'] ) { 00211 if ( $opts['hidebots'] ) { 00212 $hideanons = false; 00213 } else { 00214 $botsonly = true; 00215 } 00216 } 00217 00218 // Toggles 00219 if ( $opts['hideminor'] ) { 00220 $conds['rc_minor'] = 0; 00221 } 00222 if ( $opts['hidebots'] ) { 00223 $conds['rc_bot'] = 0; 00224 } 00225 if ( $user->useRCPatrol() && $opts['hidepatrolled'] ) { 00226 $conds['rc_patrolled'] = 0; 00227 } 00228 if ( $botsonly ) { 00229 $conds['rc_bot'] = 1; 00230 } else { 00231 if ( $opts['hideliu'] ) { 00232 $conds[] = 'rc_user = 0'; 00233 } 00234 if ( $hideanons ) { 00235 $conds[] = 'rc_user != 0'; 00236 } 00237 } 00238 if ( $opts['hidemyself'] ) { 00239 if ( $user->getId() ) { 00240 $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() ); 00241 } else { 00242 $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() ); 00243 } 00244 } 00245 00246 // Namespace filtering 00247 if ( $opts['namespace'] !== '' ) { 00248 $selectedNS = $dbr->addQuotes( $opts['namespace'] ); 00249 $operator = $opts['invert'] ? '!=' : '='; 00250 $boolean = $opts['invert'] ? 'AND' : 'OR'; 00251 00252 // Namespace association (bug 2429) 00253 if ( !$opts['associated'] ) { 00254 $condition = "rc_namespace $operator $selectedNS"; 00255 } else { 00256 // Also add the associated namespace 00257 $associatedNS = $dbr->addQuotes( 00258 MWNamespace::getAssociated( $opts['namespace'] ) 00259 ); 00260 $condition = "(rc_namespace $operator $selectedNS " 00261 . $boolean 00262 . " rc_namespace $operator $associatedNS)"; 00263 } 00264 00265 $conds[] = $condition; 00266 } 00267 00268 return $conds; 00269 } 00270 00278 public function doMainQuery( $conds, $opts ) { 00279 $tables = array( 'recentchanges' ); 00280 $fields = RecentChange::selectFields(); 00281 $query_options = array(); 00282 $join_conds = array(); 00283 00284 ChangeTags::modifyDisplayQuery( 00285 $tables, 00286 $fields, 00287 $conds, 00288 $join_conds, 00289 $query_options, 00290 '' 00291 ); 00292 00293 if ( !wfRunHooks( 'ChangesListSpecialPageQuery', 00294 array( $this->getName(), &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) ) 00295 ) { 00296 return false; 00297 } 00298 00299 $dbr = $this->getDB(); 00300 00301 return $dbr->select( 00302 $tables, 00303 $fields, 00304 $conds, 00305 __METHOD__, 00306 $query_options, 00307 $join_conds 00308 ); 00309 } 00310 00316 protected function getDB() { 00317 return wfGetDB( DB_SLAVE ); 00318 } 00319 00326 public function webOutput( $rows, $opts ) { 00327 if ( !$this->including() ) { 00328 $this->outputFeedLinks(); 00329 $this->doHeader( $opts, $rows->numRows() ); 00330 } 00331 00332 $this->outputChangesList( $rows, $opts ); 00333 } 00334 00338 public function outputFeedLinks() { 00339 // nothing by default 00340 } 00341 00348 abstract public function outputChangesList( $rows, $opts ); 00349 00356 public function doHeader( $opts, $numRows ) { 00357 $this->setTopText( $opts ); 00358 00359 // @todo Lots of stuff should be done here. 00360 00361 $this->setBottomText( $opts ); 00362 } 00363 00370 function setTopText( FormOptions $opts ) { 00371 // nothing by default 00372 } 00373 00380 function setBottomText( FormOptions $opts ) { 00381 // nothing by default 00382 } 00383 00392 function getExtraOptions( $opts ) { 00393 return array(); 00394 } 00395 00404 public static function makeLegend( IContextSource $context ) { 00405 global $wgRecentChangesFlags; 00406 $user = $context->getUser(); 00407 # The legend showing what the letters and stuff mean 00408 $legend = Xml::openElement( 'dl' ) . "\n"; 00409 # Iterates through them and gets the messages for both letter and tooltip 00410 $legendItems = $wgRecentChangesFlags; 00411 if ( !$user->useRCPatrol() ) { 00412 unset( $legendItems['unpatrolled'] ); 00413 } 00414 foreach ( $legendItems as $key => $legendInfo ) { # generate items of the legend 00415 $label = $legendInfo['title']; 00416 $letter = $legendInfo['letter']; 00417 $cssClass = isset( $legendInfo['class'] ) ? $legendInfo['class'] : $key; 00418 00419 $legend .= Xml::element( 'dt', 00420 array( 'class' => $cssClass ), $context->msg( $letter )->text() 00421 ) . "\n"; 00422 if ( $key === 'newpage' ) { 00423 $legend .= Xml::openElement( 'dd' ); 00424 $legend .= $context->msg( $label )->escaped(); 00425 $legend .= ' ' . $context->msg( 'recentchanges-legend-newpage' )->parse(); 00426 $legend .= Xml::closeElement( 'dd' ) . "\n"; 00427 } else { 00428 $legend .= Xml::element( 'dd', array(), 00429 $context->msg( $label )->text() 00430 ) . "\n"; 00431 } 00432 } 00433 # (+-123) 00434 $legend .= Xml::tags( 'dt', 00435 array( 'class' => 'mw-plusminus-pos' ), 00436 $context->msg( 'recentchanges-legend-plusminus' )->parse() 00437 ) . "\n"; 00438 $legend .= Xml::element( 00439 'dd', 00440 array( 'class' => 'mw-changeslist-legend-plusminus' ), 00441 $context->msg( 'recentchanges-label-plusminus' )->text() 00442 ) . "\n"; 00443 $legend .= Xml::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 }