MediaWiki
REL1_20
|
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 $allValues = $this->opts->getAllValues(); 00147 unset( $allValues['feed'] ); 00148 $out->setFeedAppendQuery( wfArrayToCGI( $allValues ) ); 00149 } 00150 00151 $pager = new NewPagesPager( $this, $this->opts ); 00152 $pager->mLimit = $this->opts->getValue( 'limit' ); 00153 $pager->mOffset = $this->opts->getValue( 'offset' ); 00154 00155 if( $pager->getNumRows() ) { 00156 $navigation = ''; 00157 if ( $this->showNavigation ) { 00158 $navigation = $pager->getNavigationBar(); 00159 } 00160 $out->addHTML( $navigation . $pager->getBody() . $navigation ); 00161 } else { 00162 $out->addWikiMsg( 'specialpage-empty' ); 00163 } 00164 } 00165 00166 protected function filterLinks() { 00167 global $wgGroupPermissions; 00168 00169 // show/hide links 00170 $showhide = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() ); 00171 00172 // Option value -> message mapping 00173 $filters = array( 00174 'hideliu' => 'rcshowhideliu', 00175 'hidepatrolled' => 'rcshowhidepatr', 00176 'hidebots' => 'rcshowhidebots', 00177 'hideredirs' => 'whatlinkshere-hideredirs' 00178 ); 00179 foreach ( $this->customFilters as $key => $params ) { 00180 $filters[$key] = $params['msg']; 00181 } 00182 00183 // Disable some if needed 00184 # @todo FIXME: Throws E_NOTICEs if not set; and doesn't obey hooks etc. 00185 if ( $wgGroupPermissions['*']['createpage'] !== true ) { 00186 unset( $filters['hideliu'] ); 00187 } 00188 if ( !$this->getUser()->useNPPatrol() ) { 00189 unset( $filters['hidepatrolled'] ); 00190 } 00191 00192 $links = array(); 00193 $changed = $this->opts->getChangedValues(); 00194 unset( $changed['offset'] ); // Reset offset if query type changes 00195 00196 $self = $this->getTitle(); 00197 foreach ( $filters as $key => $msg ) { 00198 $onoff = 1 - $this->opts->getValue( $key ); 00199 $link = Linker::link( $self, $showhide[$onoff], array(), 00200 array( $key => $onoff ) + $changed 00201 ); 00202 $links[$key] = $this->msg( $msg )->rawParams( $link )->escaped(); 00203 } 00204 00205 return $this->getLanguage()->pipeList( $links ); 00206 } 00207 00208 protected function form() { 00209 global $wgEnableNewpagesUserFilter, $wgScript; 00210 00211 // Consume values 00212 $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW 00213 $namespace = $this->opts->consumeValue( 'namespace' ); 00214 $username = $this->opts->consumeValue( 'username' ); 00215 $tagFilterVal = $this->opts->consumeValue( 'tagfilter' ); 00216 00217 // Check username input validity 00218 $ut = Title::makeTitleSafe( NS_USER, $username ); 00219 $userText = $ut ? $ut->getText() : ''; 00220 00221 // Store query values in hidden fields so that form submission doesn't lose them 00222 $hidden = array(); 00223 foreach ( $this->opts->getUnconsumedValues() as $key => $value ) { 00224 $hidden[] = Html::hidden( $key, $value ); 00225 } 00226 $hidden = implode( "\n", $hidden ); 00227 00228 $tagFilter = ChangeTags::buildTagFilterSelector( $tagFilterVal ); 00229 if ( $tagFilter ) { 00230 list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter; 00231 } 00232 00233 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) . 00234 Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . 00235 Xml::fieldset( $this->msg( 'newpages' )->text() ) . 00236 Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) . 00237 '<tr> 00238 <td class="mw-label">' . 00239 Xml::label( $this->msg( 'namespace' )->text(), 'namespace' ) . 00240 '</td> 00241 <td class="mw-input">' . 00242 Html::namespaceSelector( 00243 array( 00244 'selected' => $namespace, 00245 'all' => 'all', 00246 ), array( 00247 'name' => 'namespace', 00248 'id' => 'namespace', 00249 'class' => 'namespaceselector', 00250 ) 00251 ) . 00252 '</td> 00253 </tr>' . ( $tagFilter ? ( 00254 '<tr> 00255 <td class="mw-label">' . 00256 $tagFilterLabel . 00257 '</td> 00258 <td class="mw-input">' . 00259 $tagFilterSelector . 00260 '</td> 00261 </tr>' ) : '' ) . 00262 ( $wgEnableNewpagesUserFilter ? 00263 '<tr> 00264 <td class="mw-label">' . 00265 Xml::label( $this->msg( 'newpages-username' )->text(), 'mw-np-username' ) . 00266 '</td> 00267 <td class="mw-input">' . 00268 Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) . 00269 '</td> 00270 </tr>' : '' ) . 00271 '<tr> <td></td> 00272 <td class="mw-submit">' . 00273 Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . 00274 '</td> 00275 </tr>' . 00276 '<tr> 00277 <td></td> 00278 <td class="mw-input">' . 00279 $this->filterLinks() . 00280 '</td> 00281 </tr>' . 00282 Xml::closeElement( 'table' ) . 00283 Xml::closeElement( 'fieldset' ) . 00284 $hidden . 00285 Xml::closeElement( 'form' ); 00286 00287 $this->getOutput()->addHTML( $form ); 00288 } 00289 00296 public function formatRow( $result ) { 00297 $title = Title::newFromRow( $result ); 00298 00299 # Revision deletion works on revisions, so we should cast one 00300 $row = array( 00301 'comment' => $result->rc_comment, 00302 'deleted' => $result->rc_deleted, 00303 'user_text' => $result->rc_user_text, 00304 'user' => $result->rc_user, 00305 ); 00306 $rev = new Revision( $row ); 00307 $rev->setTitle( $title ); 00308 00309 $classes = array(); 00310 00311 $lang = $this->getLanguage(); 00312 $dm = $lang->getDirMark(); 00313 00314 $spanTime = Html::element( 'span', array( 'class' => 'mw-newpages-time' ), 00315 $lang->userTimeAndDate( $result->rc_timestamp, $this->getUser() ) 00316 ); 00317 $time = Linker::linkKnown( 00318 $title, 00319 $spanTime, 00320 array(), 00321 array( 'oldid' => $result->rc_this_oldid ), 00322 array() 00323 ); 00324 00325 $query = array( 'redirect' => 'no' ); 00326 00327 if( $this->patrollable( $result ) ) { 00328 $query['rcid'] = $result->rc_id; 00329 } 00330 00331 $plink = Linker::linkKnown( 00332 $title, 00333 null, 00334 array( 'class' => 'mw-newpages-pagename' ), 00335 $query, 00336 array( 'known' ) // Set explicitly to avoid the default of 'known','noclasses'. This breaks the colouration for stubs 00337 ); 00338 $histLink = Linker::linkKnown( 00339 $title, 00340 $this->msg( 'hist' )->escaped(), 00341 array(), 00342 array( 'action' => 'history' ) 00343 ); 00344 $hist = Html::rawElement( 'span', array( 'class' => 'mw-newpages-history' ), 00345 $this->msg( 'parentheses' )->rawParams( $histLink )->escaped() ); 00346 00347 $length = Html::element( 'span', array( 'class' => 'mw-newpages-length' ), 00348 $this->msg( 'brackets' )->params( $this->msg( 'nbytes' )->numParams( $result->length )->text() ) 00349 ); 00350 00351 $ulink = Linker::revUserTools( $rev ); 00352 $comment = Linker::revComment( $rev ); 00353 00354 if ( $this->patrollable( $result ) ) { 00355 $classes[] = 'not-patrolled'; 00356 } 00357 00358 # Add a class for zero byte pages 00359 if ( $result->length == 0 ) { 00360 $classes[] = 'mw-newpages-zero-byte-page'; 00361 } 00362 00363 # Tags, if any. 00364 if( isset( $result->ts_tags ) ) { 00365 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' ); 00366 $classes = array_merge( $classes, $newClasses ); 00367 } else { 00368 $tagDisplay = ''; 00369 } 00370 00371 $css = count( $classes ) ? ' class="' . implode( ' ', $classes ) . '"' : ''; 00372 00373 # Display the old title if the namespace/title has been changed 00374 $oldTitleText = ''; 00375 $oldTitle = Title::makeTitle( $result->rc_namespace, $result->rc_title ); 00376 if ( !$title->equals( $oldTitle ) ) { 00377 $oldTitleText = $this->msg( 'rc-old-title' )->params( $oldTitle->getPrefixedText() )->escaped(); 00378 } 00379 00380 return "<li{$css}>{$time} {$dm}{$plink} {$hist} {$dm}{$length} {$dm}{$ulink} {$comment} {$tagDisplay} {$oldTitleText}</li>\n"; 00381 } 00382 00389 protected function patrollable( $result ) { 00390 return ( $this->getUser()->useNPPatrol() && !$result->rc_patrolled ); 00391 } 00392 00398 protected function feed( $type ) { 00399 global $wgFeed, $wgFeedClasses, $wgFeedLimit; 00400 00401 if ( !$wgFeed ) { 00402 $this->getOutput()->addWikiMsg( 'feed-unavailable' ); 00403 return; 00404 } 00405 00406 if( !isset( $wgFeedClasses[$type] ) ) { 00407 $this->getOutput()->addWikiMsg( 'feed-invalid' ); 00408 return; 00409 } 00410 00411 $feed = new $wgFeedClasses[$type]( 00412 $this->feedTitle(), 00413 $this->msg( 'tagline' )->text(), 00414 $this->getTitle()->getFullUrl() 00415 ); 00416 00417 $pager = new NewPagesPager( $this, $this->opts ); 00418 $limit = $this->opts->getValue( 'limit' ); 00419 $pager->mLimit = min( $limit, $wgFeedLimit ); 00420 00421 $feed->outHeader(); 00422 if( $pager->getNumRows() > 0 ) { 00423 foreach ( $pager->mResult as $row ) { 00424 $feed->outItem( $this->feedItem( $row ) ); 00425 } 00426 } 00427 $feed->outFooter(); 00428 } 00429 00430 protected function feedTitle() { 00431 global $wgLanguageCode, $wgSitename; 00432 $desc = $this->getDescription(); 00433 return "$wgSitename - $desc [$wgLanguageCode]"; 00434 } 00435 00436 protected function feedItem( $row ) { 00437 $title = Title::makeTitle( intval( $row->rc_namespace ), $row->rc_title ); 00438 if( $title ) { 00439 $date = $row->rc_timestamp; 00440 $comments = $title->getTalkPage()->getFullURL(); 00441 00442 return new FeedItem( 00443 $title->getPrefixedText(), 00444 $this->feedItemDesc( $row ), 00445 $title->getFullURL(), 00446 $date, 00447 $this->feedItemAuthor( $row ), 00448 $comments 00449 ); 00450 } else { 00451 return null; 00452 } 00453 } 00454 00455 protected function feedItemAuthor( $row ) { 00456 return isset( $row->rc_user_text ) ? $row->rc_user_text : ''; 00457 } 00458 00459 protected function feedItemDesc( $row ) { 00460 $revision = Revision::newFromId( $row->rev_id ); 00461 if( $revision ) { 00462 return '<p>' . htmlspecialchars( $revision->getUserText() ) . 00463 $this->msg( 'colon-separator' )->inContentLanguage()->escaped() . 00464 htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . 00465 "</p>\n<hr />\n<div>" . 00466 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>"; 00467 } 00468 return ''; 00469 } 00470 } 00471 00475 class NewPagesPager extends ReverseChronologicalPager { 00476 // Stored opts 00477 protected $opts; 00478 00482 protected $mForm; 00483 00484 function __construct( $form, FormOptions $opts ) { 00485 parent::__construct( $form->getContext() ); 00486 $this->mForm = $form; 00487 $this->opts = $opts; 00488 } 00489 00490 function getQueryInfo() { 00491 global $wgEnableNewpagesUserFilter, $wgGroupPermissions; 00492 $conds = array(); 00493 $conds['rc_new'] = 1; 00494 00495 $namespace = $this->opts->getValue( 'namespace' ); 00496 $namespace = ( $namespace === 'all' ) ? false : intval( $namespace ); 00497 00498 $username = $this->opts->getValue( 'username' ); 00499 $user = Title::makeTitleSafe( NS_USER, $username ); 00500 00501 if( $namespace !== false ) { 00502 $conds['rc_namespace'] = $namespace; 00503 $rcIndexes = array( 'new_name_timestamp' ); 00504 } else { 00505 $rcIndexes = array( 'rc_timestamp' ); 00506 } 00507 00508 # $wgEnableNewpagesUserFilter - temp WMF hack 00509 if( $wgEnableNewpagesUserFilter && $user ) { 00510 $conds['rc_user_text'] = $user->getText(); 00511 $rcIndexes = 'rc_user_text'; 00512 # If anons cannot make new pages, don't "exclude logged in users"! 00513 } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) { 00514 $conds['rc_user'] = 0; 00515 } 00516 # If this user cannot see patrolled edits or they are off, don't do dumb queries! 00517 if( $this->opts->getValue( 'hidepatrolled' ) && $this->getUser()->useNPPatrol() ) { 00518 $conds['rc_patrolled'] = 0; 00519 } 00520 if( $this->opts->getValue( 'hidebots' ) ) { 00521 $conds['rc_bot'] = 0; 00522 } 00523 00524 if ( $this->opts->getValue( 'hideredirs' ) ) { 00525 $conds['page_is_redirect'] = 0; 00526 } 00527 00528 // Allow changes to the New Pages query 00529 $tables = array( 'recentchanges', 'page' ); 00530 $fields = array( 00531 'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_user', 'rc_user_text', 00532 'rc_comment', 'rc_timestamp', 'rc_patrolled','rc_id', 'rc_deleted', 00533 'length' => 'page_len', 'rev_id' => 'page_latest', 'rc_this_oldid', 00534 'page_namespace', 'page_title' 00535 ); 00536 $join_conds = array( 'page' => array( 'INNER JOIN', 'page_id=rc_cur_id' ) ); 00537 00538 wfRunHooks( 'SpecialNewpagesConditions', 00539 array( &$this, $this->opts, &$conds, &$tables, &$fields, &$join_conds ) ); 00540 00541 $info = array( 00542 'tables' => $tables, 00543 'fields' => $fields, 00544 'conds' => $conds, 00545 'options' => array( 'USE INDEX' => array( 'recentchanges' => $rcIndexes ) ), 00546 'join_conds' => $join_conds 00547 ); 00548 00549 // Modify query for tags 00550 ChangeTags::modifyDisplayQuery( 00551 $info['tables'], 00552 $info['fields'], 00553 $info['conds'], 00554 $info['join_conds'], 00555 $info['options'], 00556 $this->opts['tagfilter'] 00557 ); 00558 00559 return $info; 00560 } 00561 00562 function getIndexField() { 00563 return 'rc_timestamp'; 00564 } 00565 00566 function formatRow( $row ) { 00567 return $this->mForm->formatRow( $row ); 00568 } 00569 00570 function getStartBody() { 00571 # Do a batch existence check on pages 00572 $linkBatch = new LinkBatch(); 00573 foreach ( $this->mResult as $row ) { 00574 $linkBatch->add( NS_USER, $row->rc_user_text ); 00575 $linkBatch->add( NS_USER_TALK, $row->rc_user_text ); 00576 $linkBatch->add( $row->rc_namespace, $row->rc_title ); 00577 } 00578 $linkBatch->execute(); 00579 return '<ul>'; 00580 } 00581 00582 function getEndBody() { 00583 return '</ul>'; 00584 } 00585 }