MediaWiki  REL1_19
SpecialNewpages.php
Go to the documentation of this file.
00001 <?php
00029 class SpecialNewpages extends IncludableSpecialPage {
00030 
00031         // Stored objects
00032 
00036         protected $opts;
00037         protected $customFilters;
00038 
00039         // Some internal settings
00040         protected $showNavigation = false;
00041 
00042         public function __construct() {
00043                 parent::__construct( 'Newpages' );
00044         }
00045 
00046         protected function setup( $par ) {
00047                 global $wgEnableNewpagesUserFilter;
00048 
00049                 // Options
00050                 $opts = new FormOptions();
00051                 $this->opts = $opts; // bind
00052                 $opts->add( 'hideliu', false );
00053                 $opts->add( 'hidepatrolled', $this->getUser()->getBoolOption( 'newpageshidepatrolled' ) );
00054                 $opts->add( 'hidebots', false );
00055                 $opts->add( 'hideredirs', true );
00056                 $opts->add( 'limit', (int)$this->getUser()->getOption( 'rclimit' ) );
00057                 $opts->add( 'offset', '' );
00058                 $opts->add( 'namespace', '0' );
00059                 $opts->add( 'username', '' );
00060                 $opts->add( 'feed', '' );
00061                 $opts->add( 'tagfilter', '' );
00062 
00063                 $this->customFilters = array();
00064                 wfRunHooks( 'SpecialNewPagesFilters', array( $this, &$this->customFilters ) );
00065                 foreach( $this->customFilters as $key => $params ) {
00066                         $opts->add( $key, $params['default'] );
00067                 }
00068 
00069                 // Set values
00070                 $opts->fetchValuesFromRequest( $this->getRequest() );
00071                 if ( $par ) $this->parseParams( $par );
00072 
00073                 // Validate
00074                 $opts->validateIntBounds( 'limit', 0, 5000 );
00075                 if( !$wgEnableNewpagesUserFilter ) {
00076                         $opts->setValue( 'username', '' );
00077                 }
00078         }
00079 
00080         protected function parseParams( $par ) {
00081                 $bits = preg_split( '/\s*,\s*/', trim( $par ) );
00082                 foreach ( $bits as $bit ) {
00083                         if ( 'shownav' == $bit ) {
00084                                 $this->showNavigation = true;
00085                         }
00086                         if ( 'hideliu' === $bit ) {
00087                                 $this->opts->setValue( 'hideliu', true );
00088                         }
00089                         if ( 'hidepatrolled' == $bit ) {
00090                                 $this->opts->setValue( 'hidepatrolled', true );
00091                         }
00092                         if ( 'hidebots' == $bit ) {
00093                                 $this->opts->setValue( 'hidebots', true );
00094                         }
00095                         if ( 'showredirs' == $bit ) {
00096                                 $this->opts->setValue( 'hideredirs', false );
00097                         }
00098                         if ( is_numeric( $bit ) ) {
00099                                 $this->opts->setValue( 'limit', intval( $bit ) );
00100                         }
00101 
00102                         $m = array();
00103                         if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
00104                                 $this->opts->setValue( 'limit', intval( $m[1] ) );
00105                         }
00106                         // PG offsets not just digits!
00107                         if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) ) {
00108                                 $this->opts->setValue( 'offset',  intval( $m[1] ) );
00109                         }
00110                         if ( preg_match( '/^username=(.*)$/', $bit, $m ) ) {
00111                                 $this->opts->setValue( 'username', $m[1] );
00112                         }
00113                         if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
00114                                 $ns = $this->getLanguage()->getNsIndex( $m[1] );
00115                                 if( $ns !== false ) {
00116                                         $this->opts->setValue( 'namespace',  $ns );
00117                                 }
00118                         }
00119                 }
00120         }
00121 
00128         public function execute( $par ) {
00129                 $out = $this->getOutput();
00130 
00131                 $this->setHeaders();
00132                 $this->outputHeader();
00133 
00134                 $this->showNavigation = !$this->including(); // Maybe changed in setup
00135                 $this->setup( $par );
00136 
00137                 if( !$this->including() ) {
00138                         // Settings
00139                         $this->form();
00140 
00141                         $feedType = $this->opts->getValue( 'feed' );
00142                         if( $feedType ) {
00143                                 return $this->feed( $feedType );
00144                         }
00145 
00146                         $out->setFeedAppendQuery( wfArrayToCGI( $this->opts->getAllValues() ) );
00147                 }
00148 
00149                 $pager = new NewPagesPager( $this, $this->opts );
00150                 $pager->mLimit = $this->opts->getValue( 'limit' );
00151                 $pager->mOffset = $this->opts->getValue( 'offset' );
00152 
00153                 if( $pager->getNumRows() ) {
00154                         $navigation = '';
00155                         if ( $this->showNavigation ) {
00156                                 $navigation = $pager->getNavigationBar();
00157                         }
00158                         $out->addHTML( $navigation . $pager->getBody() . $navigation );
00159                 } else {
00160                         $out->addWikiMsg( 'specialpage-empty' );
00161                 }
00162         }
00163 
00164         protected function filterLinks() {
00165                 global $wgGroupPermissions;
00166 
00167                 // show/hide links
00168                 $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
00169 
00170                 // Option value -> message mapping
00171                 $filters = array(
00172                         'hideliu' => 'rcshowhideliu',
00173                         'hidepatrolled' => 'rcshowhidepatr',
00174                         'hidebots' => 'rcshowhidebots',
00175                         'hideredirs' => 'whatlinkshere-hideredirs'
00176                 );
00177                 foreach ( $this->customFilters as $key => $params ) {
00178                         $filters[$key] = $params['msg'];
00179                 }
00180 
00181                 // Disable some if needed
00182                 # @todo FIXME: Throws E_NOTICEs if not set; and doesn't obey hooks etc.
00183                 if ( $wgGroupPermissions['*']['createpage'] !== true ) {
00184                         unset( $filters['hideliu'] );
00185                 }
00186                 if ( !$this->getUser()->useNPPatrol() ) {
00187                         unset( $filters['hidepatrolled'] );
00188                 }
00189 
00190                 $links = array();
00191                 $changed = $this->opts->getChangedValues();
00192                 unset( $changed['offset'] ); // Reset offset if query type changes
00193 
00194                 $self = $this->getTitle();
00195                 foreach ( $filters as $key => $msg ) {
00196                         $onoff = 1 - $this->opts->getValue( $key );
00197                         $link = Linker::link( $self, $showhide[$onoff], array(),
00198                                         array( $key => $onoff ) + $changed
00199                         );
00200                         $links[$key] = wfMsgHtml( $msg, $link );
00201                 }
00202 
00203                 return $this->getLanguage()->pipeList( $links );
00204         }
00205 
00206         protected function form() {
00207                 global $wgEnableNewpagesUserFilter, $wgScript;
00208 
00209                 // Consume values
00210                 $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
00211                 $namespace = $this->opts->consumeValue( 'namespace' );
00212                 $username = $this->opts->consumeValue( 'username' );
00213                 $tagFilterVal = $this->opts->consumeValue( 'tagfilter' );
00214 
00215                 // Check username input validity
00216                 $ut = Title::makeTitleSafe( NS_USER, $username );
00217                 $userText = $ut ? $ut->getText() : '';
00218 
00219                 // Store query values in hidden fields so that form submission doesn't lose them
00220                 $hidden = array();
00221                 foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
00222                         $hidden[] = Html::hidden( $key, $value );
00223                 }
00224                 $hidden = implode( "\n", $hidden );
00225 
00226                 $tagFilter = ChangeTags::buildTagFilterSelector( $tagFilterVal );
00227                 if ( $tagFilter ) {
00228                         list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter;
00229                 }
00230 
00231                 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
00232                         Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
00233                         Xml::fieldset( wfMsg( 'newpages' ) ) .
00234                         Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
00235                         '<tr>
00236                                 <td class="mw-label">' .
00237                                         Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
00238                                 '</td>
00239                                 <td class="mw-input">' .
00240                                         Xml::namespaceSelector( $namespace, 'all' ) .
00241                                 '</td>
00242                         </tr>' . ( $tagFilter ? (
00243                         '<tr>
00244                                 <td class="mw-label">' .
00245                                         $tagFilterLabel .
00246                                 '</td>
00247                                 <td class="mw-input">' .
00248                                         $tagFilterSelector .
00249                                 '</td>
00250                         </tr>' ) : '' ) .
00251                         ( $wgEnableNewpagesUserFilter ?
00252                         '<tr>
00253                                 <td class="mw-label">' .
00254                                         Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
00255                                 '</td>
00256                                 <td class="mw-input">' .
00257                                         Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
00258                                 '</td>
00259                         </tr>' : '' ) .
00260                         '<tr> <td></td>
00261                                 <td class="mw-submit">' .
00262                                         Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
00263                                 '</td>
00264                         </tr>' .
00265                         '<tr>
00266                                 <td></td>
00267                                 <td class="mw-input">' .
00268                                         $this->filterLinks() .
00269                                 '</td>
00270                         </tr>' .
00271                         Xml::closeElement( 'table' ) .
00272                         Xml::closeElement( 'fieldset' ) .
00273                         $hidden .
00274                         Xml::closeElement( 'form' );
00275 
00276                 $this->getOutput()->addHTML( $form );
00277         }
00278 
00285         public function formatRow( $result ) {
00286                 # Revision deletion works on revisions, so we should cast one
00287                 $row = array(
00288                                           'comment' => $result->rc_comment,
00289                                           'deleted' => $result->rc_deleted,
00290                                           'user_text' => $result->rc_user_text,
00291                                           'user' => $result->rc_user,
00292                                         );
00293                 $rev = new Revision( $row );
00294 
00295                 $classes = array();
00296 
00297                 $lang = $this->getLanguage();
00298                 $dm = $lang->getDirMark();
00299 
00300                 $title = Title::newFromRow( $result );
00301                 $spanTime = Html::element( 'span', array( 'class' => 'mw-newpages-time' ),
00302                         $lang->timeanddate( $result->rc_timestamp, true )
00303                 );
00304                 $time = Linker::linkKnown(
00305                         $title,
00306                         $spanTime,
00307                         array(),
00308                         array( 'oldid' => $result->rc_this_oldid ),
00309                         array()
00310                 );
00311 
00312                 $query = array( 'redirect' => 'no' );
00313 
00314                 if( $this->patrollable( $result ) ) {
00315                         $query['rcid'] = $result->rc_id;
00316                 }
00317 
00318                 $plink = Linker::linkKnown(
00319                         $title,
00320                         null,
00321                         array( 'class' => 'mw-newpages-pagename' ),
00322                         $query,
00323                         array( 'known' ) // Set explicitly to avoid the default of 'known','noclasses'. This breaks the colouration for stubs
00324                 );
00325                 $histLink = Linker::linkKnown(
00326                         $title,
00327                         wfMsgHtml( 'hist' ),
00328                         array(),
00329                         array( 'action' => 'history' )
00330                 );
00331                 $hist = Html::rawElement( 'span', array( 'class' => 'mw-newpages-history' ), wfMsg( 'parentheses', $histLink ) );
00332 
00333                 $length = Html::element( 'span', array( 'class' => 'mw-newpages-length' ),
00334                                 '[' . $this->msg( 'nbytes' )->numParams( $result->length )->text() . ']'
00335                 );
00336 
00337                 $ulink = Linker::revUserTools( $rev );
00338                 $comment = Linker::revComment( $rev );
00339 
00340                 if ( $this->patrollable( $result ) ) {
00341                         $classes[] = 'not-patrolled';
00342                 }
00343 
00344                 # Add a class for zero byte pages
00345                 if ( $result->length == 0 ) {
00346                         $classes[] = 'mw-newpages-zero-byte-page';
00347                 }
00348 
00349                 # Tags, if any. check for including due to bug 23293
00350                 if ( !$this->including() ) {
00351                         list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' );
00352                         $classes = array_merge( $classes, $newClasses );
00353                 } else {
00354                         $tagDisplay = '';
00355                 }
00356 
00357                 $css = count( $classes ) ? ' class="' . implode( ' ', $classes ) . '"' : '';
00358 
00359                 # Display the old title if the namespace has been changed
00360                 $oldTitleText = '';
00361                 if ( $result->page_namespace !== $result->rc_namespace ) {
00362                         $oldTitleText = wfMessage( 'rc-old-title' )->params( Title::makeTitle( $result->rc_namespace, $result->rc_title )
00363                                                                    ->getPrefixedText() )->escaped();    
00364                 }
00365 
00366                 return "<li{$css}>{$time} {$dm}{$plink} {$hist} {$dm}{$length} {$dm}{$ulink} {$comment} {$tagDisplay} {$oldTitleText}</li>\n";
00367         }
00368 
00375         protected function patrollable( $result ) {
00376                 return ( $this->getUser()->useNPPatrol() && !$result->rc_patrolled );
00377         }
00378 
00384         protected function feed( $type ) {
00385                 global $wgFeed, $wgFeedClasses, $wgFeedLimit;
00386 
00387                 if ( !$wgFeed ) {
00388                         $this->getOutput()->addWikiMsg( 'feed-unavailable' );
00389                         return;
00390                 }
00391 
00392                 if( !isset( $wgFeedClasses[$type] ) ) {
00393                         $this->getOutput()->addWikiMsg( 'feed-invalid' );
00394                         return;
00395                 }
00396 
00397                 $feed = new $wgFeedClasses[$type](
00398                         $this->feedTitle(),
00399                         wfMsgExt( 'tagline', 'parsemag' ),
00400                         $this->getTitle()->getFullUrl()
00401                 );
00402 
00403                 $pager = new NewPagesPager( $this, $this->opts );
00404                 $limit = $this->opts->getValue( 'limit' );
00405                 $pager->mLimit = min( $limit, $wgFeedLimit );
00406 
00407                 $feed->outHeader();
00408                 if( $pager->getNumRows() > 0 ) {
00409                         foreach ( $pager->mResult as $row ) {
00410                                 $feed->outItem( $this->feedItem( $row ) );
00411                         }
00412                 }
00413                 $feed->outFooter();
00414         }
00415 
00416         protected function feedTitle() {
00417                 global $wgLanguageCode, $wgSitename;
00418                 $desc = $this->getDescription();
00419                 return "$wgSitename - $desc [$wgLanguageCode]";
00420         }
00421 
00422         protected function feedItem( $row ) {
00423                 $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
00424                 if( $title ) {
00425                         $date = $row->rc_timestamp;
00426                         $comments = $title->getTalkPage()->getFullURL();
00427 
00428                         return new FeedItem(
00429                                 $title->getPrefixedText(),
00430                                 $this->feedItemDesc( $row ),
00431                                 $title->getFullURL(),
00432                                 $date,
00433                                 $this->feedItemAuthor( $row ),
00434                                 $comments
00435                         );
00436                 } else {
00437                         return null;
00438                 }
00439         }
00440 
00441         protected function feedItemAuthor( $row ) {
00442                 return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
00443         }
00444 
00445         protected function feedItemDesc( $row ) {
00446                 $revision = Revision::newFromId( $row->rev_id );
00447                 if( $revision ) {
00448                         return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) .
00449                                 htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) .
00450                                 "</p>\n<hr />\n<div>" .
00451                                 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
00452                 }
00453                 return '';
00454         }
00455 }
00456 
00460 class NewPagesPager extends ReverseChronologicalPager {
00461         // Stored opts
00462         protected $opts;
00463 
00467         protected $mForm;
00468 
00469         function __construct( $form, FormOptions $opts ) {
00470                 parent::__construct( $form->getContext() );
00471                 $this->mForm = $form;
00472                 $this->opts = $opts;
00473         }
00474 
00475         function getQueryInfo() {
00476                 global $wgEnableNewpagesUserFilter, $wgGroupPermissions;
00477                 $conds = array();
00478                 $conds['rc_new'] = 1;
00479 
00480                 $namespace = $this->opts->getValue( 'namespace' );
00481                 $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
00482 
00483                 $username = $this->opts->getValue( 'username' );
00484                 $user = Title::makeTitleSafe( NS_USER, $username );
00485 
00486                 if( $namespace !== false ) {
00487                         $conds['rc_namespace'] = $namespace;
00488                         $rcIndexes = array( 'new_name_timestamp' );
00489                 } else {
00490                         $rcIndexes = array( 'rc_timestamp' );
00491                 }
00492 
00493                 # $wgEnableNewpagesUserFilter - temp WMF hack
00494                 if( $wgEnableNewpagesUserFilter && $user ) {
00495                         $conds['rc_user_text'] = $user->getText();
00496                         $rcIndexes = 'rc_user_text';
00497                 # If anons cannot make new pages, don't "exclude logged in users"!
00498                 } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
00499                         $conds['rc_user'] = 0;
00500                 }
00501                 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
00502                 if( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) {
00503                         $conds['rc_patrolled'] = 0;
00504                 }
00505                 if( $this->opts->getValue( 'hidebots' ) ) {
00506                         $conds['rc_bot'] = 0;
00507                 }
00508 
00509                 if ( $this->opts->getValue( 'hideredirs' ) ) {
00510                         $conds['page_is_redirect'] = 0;
00511                 }
00512 
00513                 // Allow changes to the New Pages query
00514                 $tables = array( 'recentchanges', 'page' );
00515                 $fields = array(
00516                         'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_user', 'rc_user_text',
00517                         'rc_comment', 'rc_timestamp', 'rc_patrolled','rc_id', 'rc_deleted',
00518                         'page_len AS length', 'page_latest AS rev_id', 'ts_tags', 'rc_this_oldid',
00519                         'page_namespace', 'page_title'
00520                 );
00521                 $join_conds = array( 'page' => array( 'INNER JOIN', 'page_id=rc_cur_id' ) );
00522 
00523                 wfRunHooks( 'SpecialNewpagesConditions',
00524                         array( &$this, $this->opts, &$conds, &$tables, &$fields, &$join_conds ) );
00525 
00526                 $info = array(
00527                         'tables'         => $tables,
00528                         'fields'         => $fields,
00529                         'conds'          => $conds,
00530                         'options'        => array( 'USE INDEX' => array( 'recentchanges' => $rcIndexes ) ),
00531                         'join_conds' => $join_conds
00532                 );
00533 
00534                 // Empty array for fields, it'll be set by us anyway.
00535                 $fields = array();
00536 
00537                 // Modify query for tags
00538                 ChangeTags::modifyDisplayQuery(
00539                         $info['tables'],
00540                         $fields,
00541                         $info['conds'],
00542                         $info['join_conds'],
00543                         $info['options'],
00544                         $this->opts['tagfilter']
00545                 );
00546 
00547                 return $info;
00548         }
00549 
00550         function getIndexField() {
00551                 return 'rc_timestamp';
00552         }
00553 
00554         function formatRow( $row ) {
00555                 return $this->mForm->formatRow( $row );
00556         }
00557 
00558         function getStartBody() {
00559                 # Do a batch existence check on pages
00560                 $linkBatch = new LinkBatch();
00561                 foreach ( $this->mResult as $row ) {
00562                         $linkBatch->add( NS_USER, $row->rc_user_text );
00563                         $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
00564                         $linkBatch->add( $row->rc_namespace, $row->rc_title );
00565                 }
00566                 $linkBatch->execute();
00567                 return '<ul>';
00568         }
00569 
00570         function getEndBody() {
00571                 return '</ul>';
00572         }
00573 }