MediaWiki  REL1_23
ChangesListSpecialPage.php
Go to the documentation of this file.
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 }