00001 <?php
00002
00007 class SpecialNewpages extends SpecialPage {
00008
00009
00010 protected $opts, $skin;
00011
00012
00013 protected $showNavigation = false;
00014
00015 public function __construct() {
00016 parent::__construct( 'Newpages' );
00017 $this->includable( true );
00018 }
00019
00020 protected function setup( $par ) {
00021 global $wgRequest, $wgUser, $wgEnableNewpagesUserFilter;
00022
00023
00024 $opts = new FormOptions();
00025 $this->opts = $opts;
00026 $opts->add( 'hideliu', false );
00027 $opts->add( 'hidepatrolled', $wgUser->getBoolOption( 'newpageshidepatrolled' ) );
00028 $opts->add( 'hidebots', false );
00029 $opts->add( 'hideredirs', true );
00030 $opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) );
00031 $opts->add( 'offset', '' );
00032 $opts->add( 'namespace', '0' );
00033 $opts->add( 'username', '' );
00034 $opts->add( 'feed', '' );
00035 $opts->add( 'tagfilter', '' );
00036
00037
00038 $opts->fetchValuesFromRequest( $wgRequest );
00039 if ( $par ) $this->parseParams( $par );
00040
00041
00042 $opts->validateIntBounds( 'limit', 0, 5000 );
00043 if( !$wgEnableNewpagesUserFilter ) {
00044 $opts->setValue( 'username', '' );
00045 }
00046
00047
00048 $this->skin = $wgUser->getSkin();
00049 }
00050
00051 protected function parseParams( $par ) {
00052 global $wgLang;
00053 $bits = preg_split( '/\s*,\s*/', trim( $par ) );
00054 foreach ( $bits as $bit ) {
00055 if ( 'shownav' == $bit )
00056 $this->showNavigation = true;
00057 if ( 'hideliu' === $bit )
00058 $this->opts->setValue( 'hideliu', true );
00059 if ( 'hidepatrolled' == $bit )
00060 $this->opts->setValue( 'hidepatrolled', true );
00061 if ( 'hidebots' == $bit )
00062 $this->opts->setValue( 'hidebots', true );
00063 if ( 'showredirs' == $bit )
00064 $this->opts->setValue( 'hideredirs', false );
00065 if ( is_numeric( $bit ) )
00066 $this->opts->setValue( 'limit', intval( $bit ) );
00067
00068 $m = array();
00069 if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) )
00070 $this->opts->setValue( 'limit', intval($m[1]) );
00071
00072 if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) )
00073 $this->opts->setValue( 'offset', intval($m[1]) );
00074 if ( preg_match( '/^username=(.*)$/', $bit, $m ) )
00075 $this->opts->setValue( 'username', $m[1] );
00076 if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
00077 $ns = $wgLang->getNsIndex( $m[1] );
00078 if( $ns !== false ) {
00079 $this->opts->setValue( 'namespace', $ns );
00080 }
00081 }
00082 }
00083 }
00084
00091 public function execute( $par ) {
00092 global $wgLang, $wgUser, $wgOut;
00093
00094 $this->setHeaders();
00095 $this->outputHeader();
00096
00097 $this->showNavigation = !$this->including();
00098 $this->setup( $par );
00099
00100 if( !$this->including() ) {
00101
00102 $this->form();
00103
00104 $this->setSyndicated();
00105 $feedType = $this->opts->getValue( 'feed' );
00106 if( $feedType ) {
00107 return $this->feed( $feedType );
00108 }
00109 }
00110
00111 $pager = new NewPagesPager( $this, $this->opts );
00112 $pager->mLimit = $this->opts->getValue( 'limit' );
00113 $pager->mOffset = $this->opts->getValue( 'offset' );
00114
00115 if( $pager->getNumRows() ) {
00116 $navigation = '';
00117 if ( $this->showNavigation ) $navigation = $pager->getNavigationBar();
00118 $wgOut->addHTML( $navigation . $pager->getBody() . $navigation );
00119 } else {
00120 $wgOut->addWikiMsg( 'specialpage-empty' );
00121 }
00122 }
00123
00124 protected function filterLinks() {
00125 global $wgGroupPermissions, $wgUser, $wgLang;
00126
00127
00128 $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
00129
00130
00131 $filters = array(
00132 'hideliu' => 'rcshowhideliu',
00133 'hidepatrolled' => 'rcshowhidepatr',
00134 'hidebots' => 'rcshowhidebots',
00135 'hideredirs' => 'whatlinkshere-hideredirs'
00136 );
00137
00138
00139 if ( $wgGroupPermissions['*']['createpage'] !== true )
00140 unset($filters['hideliu']);
00141
00142 if ( !$wgUser->useNPPatrol() )
00143 unset($filters['hidepatrolled']);
00144
00145 $links = array();
00146 $changed = $this->opts->getChangedValues();
00147 unset($changed['offset']);
00148
00149 $self = $this->getTitle();
00150 foreach ( $filters as $key => $msg ) {
00151 $onoff = 1 - $this->opts->getValue($key);
00152 $link = $this->skin->link( $self, $showhide[$onoff], array(),
00153 array( $key => $onoff ) + $changed
00154 );
00155 $links[$key] = wfMsgHtml( $msg, $link );
00156 }
00157
00158 return $wgLang->pipeList( $links );
00159 }
00160
00161 protected function form() {
00162 global $wgOut, $wgEnableNewpagesUserFilter, $wgScript;
00163
00164
00165 $this->opts->consumeValue( 'offset' );
00166 $namespace = $this->opts->consumeValue( 'namespace' );
00167 $username = $this->opts->consumeValue( 'username' );
00168
00169
00170 $ut = Title::makeTitleSafe( NS_USER, $username );
00171 $userText = $ut ? $ut->getText() : '';
00172
00173
00174 $hidden = array();
00175 foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
00176 $hidden[] = Xml::hidden( $key, $value );
00177 }
00178 $hidden = implode( "\n", $hidden );
00179
00180 $tagFilter = ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] );
00181 if ($tagFilter)
00182 list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter;
00183
00184 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
00185 Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
00186 Xml::fieldset( wfMsg( 'newpages' ) ) .
00187 Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
00188 "<tr>
00189 <td class='mw-label'>" .
00190 Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
00191 "</td>
00192 <td class='mw-input'>" .
00193 Xml::namespaceSelector( $namespace, 'all' ) .
00194 "</td>
00195 </tr>" . ( $tagFilter ? (
00196 "<tr>
00197 <td class='mw-label'>" .
00198 $tagFilterLabel .
00199 "</td>
00200 <td class='mw-input'>" .
00201 $tagFilterSelector .
00202 "</td>
00203 </tr>" ) : '' ) .
00204 ($wgEnableNewpagesUserFilter ?
00205 "<tr>
00206 <td class='mw-label'>" .
00207 Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
00208 "</td>
00209 <td class='mw-input'>" .
00210 Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
00211 "</td>
00212 </tr>" : "" ) .
00213 "<tr> <td></td>
00214 <td class='mw-submit'>" .
00215 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
00216 "</td>
00217 </tr>" .
00218 "<tr>
00219 <td></td>
00220 <td class='mw-input'>" .
00221 $this->filterLinks() .
00222 "</td>
00223 </tr>" .
00224 Xml::closeElement( 'table' ) .
00225 Xml::closeElement( 'fieldset' ) .
00226 $hidden .
00227 Xml::closeElement( 'form' );
00228
00229 $wgOut->addHTML( $form );
00230 }
00231
00232 protected function setSyndicated() {
00233 global $wgOut;
00234 $queryParams = array(
00235 'namespace' => $this->opts->getValue( 'namespace' ),
00236 'username' => $this->opts->getValue( 'username' )
00237 );
00238 $wgOut->setSyndicated( true );
00239 $wgOut->setFeedAppendQuery( wfArrayToCGI( $queryParams ) );
00240 }
00241
00249 public function formatRow( $result ) {
00250 global $wgLang, $wgContLang, $wgUser;
00251
00252 $classes = array();
00253
00254 $dm = $wgContLang->getDirMark();
00255
00256 $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
00257 $time = $wgLang->timeAndDate( $result->rc_timestamp, true );
00258 $query = $this->patrollable( $result ) ? "rcid={$result->rc_id}&redirect=no" : 'redirect=no';
00259 $plink = $this->skin->makeKnownLinkObj( $title, '', $query );
00260 $hist = $this->skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
00261 $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
00262 $wgLang->formatNum( $result->length ) );
00263 $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
00264 $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
00265 $comment = $this->skin->commentBlock( $result->rc_comment );
00266
00267 if ( $this->patrollable( $result ) )
00268 $classes[] = 'not-patrolled';
00269
00270 # Tags, if any.
00271 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' );
00272 $classes = array_merge( $classes, $newClasses );
00273
00274 $css = count($classes) ? ' class="'.implode( " ", $classes).'"' : '';
00275
00276 return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment} {$tagDisplay}</li>\n";
00277 }
00278
00285 protected function patrollable( $result ) {
00286 global $wgUser;
00287 return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
00288 }
00289
00294 protected function feed( $type ) {
00295 global $wgFeed, $wgFeedClasses, $wgFeedLimit;
00296
00297 if ( !$wgFeed ) {
00298 global $wgOut;
00299 $wgOut->addWikiMsg( 'feed-unavailable' );
00300 return;
00301 }
00302
00303 if( !isset( $wgFeedClasses[$type] ) ) {
00304 global $wgOut;
00305 $wgOut->addWikiMsg( 'feed-invalid' );
00306 return;
00307 }
00308
00309 $feed = new $wgFeedClasses[$type](
00310 $this->feedTitle(),
00311 wfMsgExt( 'tagline', 'parsemag' ),
00312 $this->getTitle()->getFullUrl() );
00313
00314 $pager = new NewPagesPager( $this, $this->opts );
00315 $limit = $this->opts->getValue( 'limit' );
00316 $pager->mLimit = min( $limit, $wgFeedLimit );
00317
00318 $feed->outHeader();
00319 if( $pager->getNumRows() > 0 ) {
00320 while( $row = $pager->mResult->fetchObject() ) {
00321 $feed->outItem( $this->feedItem( $row ) );
00322 }
00323 }
00324 $feed->outFooter();
00325 }
00326
00327 protected function feedTitle() {
00328 global $wgContLanguageCode, $wgSitename;
00329 $page = SpecialPage::getPage( 'Newpages' );
00330 $desc = $page->getDescription();
00331 return "$wgSitename - $desc [$wgContLanguageCode]";
00332 }
00333
00334 protected function feedItem( $row ) {
00335 $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
00336 if( $title ) {
00337 $date = $row->rc_timestamp;
00338 $comments = $title->getTalkPage()->getFullURL();
00339
00340 return new FeedItem(
00341 $title->getPrefixedText(),
00342 $this->feedItemDesc( $row ),
00343 $title->getFullURL(),
00344 $date,
00345 $this->feedItemAuthor( $row ),
00346 $comments);
00347 } else {
00348 return NULL;
00349 }
00350 }
00351
00352 protected function feedItemAuthor( $row ) {
00353 return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
00354 }
00355
00356 protected function feedItemDesc( $row ) {
00357 $revision = Revision::newFromId( $row->rev_id );
00358 if( $revision ) {
00359 return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) .
00360 htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) .
00361 "</p>\n<hr />\n<div>" .
00362 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
00363 }
00364 return '';
00365 }
00366 }
00367
00371 class NewPagesPager extends ReverseChronologicalPager {
00372
00373 protected $opts, $mForm;
00374
00375 function __construct( $form, FormOptions $opts ) {
00376 parent::__construct();
00377 $this->mForm = $form;
00378 $this->opts = $opts;
00379 }
00380
00381 function getTitle() {
00382 static $title = null;
00383 if ( $title === null )
00384 $title = $this->mForm->getTitle();
00385 return $title;
00386 }
00387
00388 function getQueryInfo() {
00389 global $wgEnableNewpagesUserFilter, $wgGroupPermissions, $wgUser;
00390 $conds = array();
00391 $conds['rc_new'] = 1;
00392
00393 $namespace = $this->opts->getValue( 'namespace' );
00394 $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
00395
00396 $username = $this->opts->getValue( 'username' );
00397 $user = Title::makeTitleSafe( NS_USER, $username );
00398
00399 if( $namespace !== false ) {
00400 $conds['rc_namespace'] = $namespace;
00401 $rcIndexes = array( 'new_name_timestamp' );
00402 } else {
00403 $rcIndexes = array( 'rc_timestamp' );
00404 }
00405
00406 # $wgEnableNewpagesUserFilter - temp WMF hack
00407 if( $wgEnableNewpagesUserFilter && $user ) {
00408 $conds['rc_user_text'] = $user->getText();
00409 $rcIndexes = 'rc_user_text';
00410 # If anons cannot make new pages, don't "exclude logged in users"!
00411 } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
00412 $conds['rc_user'] = 0;
00413 }
00414 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
00415 if( $this->opts->getValue( 'hidepatrolled' ) && $wgUser->useNPPatrol() ) {
00416 $conds['rc_patrolled'] = 0;
00417 }
00418 if( $this->opts->getValue( 'hidebots' ) ) {
00419 $conds['rc_bot'] = 0;
00420 }
00421
00422 if ( $this->opts->getValue( 'hideredirs' ) ) {
00423 $conds['page_is_redirect'] = 0;
00424 }
00425
00426 $info = array(
00427 'tables' => array( 'recentchanges', 'page' ),
00428 'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
00429 rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags',
00430 'conds' => $conds,
00431 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ),
00432 'join_conds' => array(
00433 'page' => array('INNER JOIN', 'page_id=rc_cur_id'),
00434 ),
00435 );
00436
00437 ## Empty array for fields, it'll be set by us anyway.
00438 $fields = array();
00439
00440 ## Modify query for tags
00441 ChangeTags::modifyDisplayQuery( $info['tables'],
00442 $fields,
00443 $info['conds'],
00444 $info['join_conds'],
00445 $info['options'],
00446 $this->opts['tagfilter'] );
00447
00448 return $info;
00449 }
00450
00451 function getIndexField() {
00452 return 'rc_timestamp';
00453 }
00454
00455 function formatRow( $row ) {
00456 return $this->mForm->formatRow( $row );
00457 }
00458
00459 function getStartBody() {
00460 # Do a batch existence check on pages
00461 $linkBatch = new LinkBatch();
00462 while( $row = $this->mResult->fetchObject() ) {
00463 $linkBatch->add( NS_USER, $row->rc_user_text );
00464 $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
00465 $linkBatch->add( $row->rc_namespace, $row->rc_title );
00466 }
00467 $linkBatch->execute();
00468 return "<ul>";
00469 }
00470
00471 function getEndBody() {
00472 return "</ul>";
00473 }
00474 }