]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialNewpages.php
MediaWiki 1.15.0
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialNewpages.php
1 <?php
2
3 /**
4  * implements Special:Newpages
5  * @ingroup SpecialPage
6  */
7 class SpecialNewpages extends SpecialPage {
8
9         // Stored objects
10         protected $opts, $skin;
11
12         // Some internal settings
13         protected $showNavigation = false;
14
15         public function __construct() {
16                 parent::__construct( 'Newpages' );
17                 $this->includable( true );      
18         }
19
20         protected function setup( $par ) {
21                 global $wgRequest, $wgUser, $wgEnableNewpagesUserFilter;
22
23                 // Options
24                 $opts = new FormOptions();
25                 $this->opts = $opts; // bind
26                 $opts->add( 'hideliu', false );
27                 $opts->add( 'hidepatrolled', $wgUser->getBoolOption( 'newpageshidepatrolled' ) );
28                 $opts->add( 'hidebots', false );
29                 $opts->add( 'hideredirs', true );
30                 $opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) );
31                 $opts->add( 'offset', '' );
32                 $opts->add( 'namespace', '0' );
33                 $opts->add( 'username', '' );
34                 $opts->add( 'feed', '' );
35                 $opts->add( 'tagfilter', '' );
36
37                 // Set values
38                 $opts->fetchValuesFromRequest( $wgRequest );
39                 if ( $par ) $this->parseParams( $par );
40
41                 // Validate
42                 $opts->validateIntBounds( 'limit', 0, 5000 );
43                 if( !$wgEnableNewpagesUserFilter ) {
44                         $opts->setValue( 'username', '' );
45                 }
46
47                 // Store some objects
48                 $this->skin = $wgUser->getSkin();
49         }
50
51         protected function parseParams( $par ) {
52                 global $wgLang;
53                 $bits = preg_split( '/\s*,\s*/', trim( $par ) );
54                 foreach ( $bits as $bit ) {
55                         if ( 'shownav' == $bit )
56                                 $this->showNavigation = true;
57                         if ( 'hideliu' === $bit )
58                                 $this->opts->setValue( 'hideliu', true );
59                         if ( 'hidepatrolled' == $bit )
60                                 $this->opts->setValue( 'hidepatrolled', true );
61                         if ( 'hidebots' == $bit )
62                                 $this->opts->setValue( 'hidebots', true );
63                         if ( 'showredirs' == $bit )
64                                 $this->opts->setValue( 'hideredirs', false );
65                         if ( is_numeric( $bit ) )
66                                 $this->opts->setValue( 'limit', intval( $bit ) );
67
68                         $m = array();
69                         if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) )
70                                 $this->opts->setValue( 'limit', intval($m[1]) );
71                         // PG offsets not just digits!
72                         if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) )
73                                 $this->opts->setValue( 'offset',  intval($m[1]) );
74                         if ( preg_match( '/^username=(.*)$/', $bit, $m ) )
75                                 $this->opts->setValue( 'username', $m[1] );
76                         if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
77                                 $ns = $wgLang->getNsIndex( $m[1] );
78                                 if( $ns !== false ) {
79                                         $this->opts->setValue( 'namespace',  $ns );
80                                 }
81                         }
82                 }
83         }
84
85         /**
86          * Show a form for filtering namespace and username
87          *
88          * @param string $par
89          * @return string
90          */
91         public function execute( $par ) {
92                 global $wgLang, $wgUser, $wgOut;
93
94                 $this->setHeaders();
95                 $this->outputHeader();
96
97                 $this->showNavigation = !$this->including(); // Maybe changed in setup
98                 $this->setup( $par );
99
100                 if( !$this->including() ) {
101                         // Settings
102                         $this->form();
103
104                         $this->setSyndicated();
105                         $feedType = $this->opts->getValue( 'feed' );
106                         if( $feedType ) {
107                                 return $this->feed( $feedType );
108                         }
109                 }
110
111                 $pager = new NewPagesPager( $this, $this->opts );
112                 $pager->mLimit = $this->opts->getValue( 'limit' );
113                 $pager->mOffset = $this->opts->getValue( 'offset' );
114
115                 if( $pager->getNumRows() ) {
116                         $navigation = '';
117                         if ( $this->showNavigation ) $navigation = $pager->getNavigationBar();
118                         $wgOut->addHTML( $navigation . $pager->getBody() . $navigation );
119                 } else {
120                         $wgOut->addWikiMsg( 'specialpage-empty' );
121                 }
122         }
123
124         protected function filterLinks() {
125                 global $wgGroupPermissions, $wgUser, $wgLang;
126
127                 // show/hide links
128                 $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
129
130                 // Option value -> message mapping
131                 $filters = array(
132                         'hideliu' => 'rcshowhideliu',
133                         'hidepatrolled' => 'rcshowhidepatr',
134                         'hidebots' => 'rcshowhidebots',
135                         'hideredirs' => 'whatlinkshere-hideredirs'
136                 );
137
138                 // Disable some if needed
139                 if ( $wgGroupPermissions['*']['createpage'] !== true )
140                         unset($filters['hideliu']);
141
142                 if ( !$wgUser->useNPPatrol() )
143                         unset($filters['hidepatrolled']);
144
145                 $links = array();
146                 $changed = $this->opts->getChangedValues();
147                 unset($changed['offset']); // Reset offset if query type changes
148
149                 $self = $this->getTitle();
150                 foreach ( $filters as $key => $msg ) {
151                         $onoff = 1 - $this->opts->getValue($key);
152                         $link = $this->skin->link( $self, $showhide[$onoff], array(),
153                                  array( $key => $onoff ) + $changed
154                         );
155                         $links[$key] = wfMsgHtml( $msg, $link );
156                 }
157
158                 return $wgLang->pipeList( $links );
159         }
160
161         protected function form() {
162                 global $wgOut, $wgEnableNewpagesUserFilter, $wgScript;
163
164                 // Consume values
165                 $this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
166                 $namespace = $this->opts->consumeValue( 'namespace' );
167                 $username = $this->opts->consumeValue( 'username' );
168
169                 // Check username input validity
170                 $ut = Title::makeTitleSafe( NS_USER, $username );
171                 $userText = $ut ? $ut->getText() : '';
172
173                 // Store query values in hidden fields so that form submission doesn't lose them
174                 $hidden = array();
175                 foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
176                         $hidden[] = Xml::hidden( $key, $value );
177                 }
178                 $hidden = implode( "\n", $hidden );
179
180                 $tagFilter = ChangeTags::buildTagFilterSelector( $this->opts['tagfilter'] );
181                 if ($tagFilter)
182                         list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter;
183
184                 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
185                         Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
186                         Xml::fieldset( wfMsg( 'newpages' ) ) .
187                         Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
188                         "<tr>
189                                 <td class='mw-label'>" .
190                                         Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
191                                 "</td>
192                                 <td class='mw-input'>" .
193                                         Xml::namespaceSelector( $namespace, 'all' ) .
194                                 "</td>
195                         </tr>" . ( $tagFilter ? (
196                         "<tr>
197                                 <td class='mw-label'>" .
198                                         $tagFilterLabel .
199                                 "</td>
200                                 <td class='mw-input'>" .
201                                         $tagFilterSelector .
202                                 "</td>
203                         </tr>" ) : '' ) .
204                         ($wgEnableNewpagesUserFilter ?
205                         "<tr>
206                                 <td class='mw-label'>" .
207                                         Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
208                                 "</td>
209                                 <td class='mw-input'>" .
210                                         Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
211                                 "</td>
212                         </tr>" : "" ) .
213                         "<tr> <td></td>
214                                 <td class='mw-submit'>" .
215                                         Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
216                                 "</td>
217                         </tr>" .
218                         "<tr>
219                                 <td></td>
220                                 <td class='mw-input'>" .
221                                         $this->filterLinks() .
222                                 "</td>
223                         </tr>" .
224                         Xml::closeElement( 'table' ) .
225                         Xml::closeElement( 'fieldset' ) .
226                         $hidden .
227                         Xml::closeElement( 'form' );
228
229                 $wgOut->addHTML( $form );
230         }
231
232         protected function setSyndicated() {
233                 global $wgOut;
234                 $queryParams = array(
235                         'namespace' => $this->opts->getValue( 'namespace' ),
236                         'username' => $this->opts->getValue( 'username' )
237                 );
238                 $wgOut->setSyndicated( true );
239                 $wgOut->setFeedAppendQuery( wfArrayToCGI( $queryParams ) );
240         }
241
242         /**
243          * Format a row, providing the timestamp, links to the page/history, size, user links, and a comment
244          *
245          * @param $skin Skin to use
246          * @param $result Result row
247          * @return string
248          */
249         public function formatRow( $result ) {
250                 global $wgLang, $wgContLang, $wgUser;
251
252                 $classes = array();
253                 
254                 $dm = $wgContLang->getDirMark();
255
256                 $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
257                 $time = $wgLang->timeAndDate( $result->rc_timestamp, true );
258                 $query = $this->patrollable( $result ) ? "rcid={$result->rc_id}&redirect=no" : 'redirect=no';
259                 $plink = $this->skin->makeKnownLinkObj( $title, '', $query );
260                 $hist = $this->skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
261                 $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
262                         $wgLang->formatNum( $result->length ) );
263                 $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
264                         $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
265                 $comment = $this->skin->commentBlock( $result->rc_comment );
266                 
267                 if ( $this->patrollable( $result ) )
268                         $classes[] = 'not-patrolled';
269
270                 # Tags, if any.
271                 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' );
272                 $classes = array_merge( $classes, $newClasses );
273
274                 $css = count($classes) ? ' class="'.implode( " ", $classes).'"' : '';
275
276                 return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment} {$tagDisplay}</li>\n";
277         }
278
279         /**
280          * Should a specific result row provide "patrollable" links?
281          *
282          * @param $result Result row
283          * @return bool
284          */
285         protected function patrollable( $result ) {
286                 global $wgUser;
287                 return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
288         }
289
290         /**
291          * Output a subscription feed listing recent edits to this page.
292          * @param string $type
293          */
294         protected function feed( $type ) {
295                 global $wgFeed, $wgFeedClasses, $wgFeedLimit;
296
297                 if ( !$wgFeed ) {
298                         global $wgOut;
299                         $wgOut->addWikiMsg( 'feed-unavailable' );
300                         return;
301                 }
302
303                 if( !isset( $wgFeedClasses[$type] ) ) {
304                         global $wgOut;
305                         $wgOut->addWikiMsg( 'feed-invalid' );
306                         return;
307                 }
308
309                 $feed = new $wgFeedClasses[$type](
310                         $this->feedTitle(),
311                         wfMsgExt( 'tagline', 'parsemag' ),
312                         $this->getTitle()->getFullUrl() );
313
314                 $pager = new NewPagesPager( $this, $this->opts );
315                 $limit = $this->opts->getValue( 'limit' );
316                 $pager->mLimit = min( $limit, $wgFeedLimit );
317
318                 $feed->outHeader();
319                 if( $pager->getNumRows() > 0 ) {
320                         while( $row = $pager->mResult->fetchObject() ) {
321                                 $feed->outItem( $this->feedItem( $row ) );
322                         }
323                 }
324                 $feed->outFooter();
325         }
326
327         protected function feedTitle() {
328                 global $wgContLanguageCode, $wgSitename;
329                 $page = SpecialPage::getPage( 'Newpages' );
330                 $desc = $page->getDescription();
331                 return "$wgSitename - $desc [$wgContLanguageCode]";
332         }
333
334         protected function feedItem( $row ) {
335                 $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
336                 if( $title ) {
337                         $date = $row->rc_timestamp;
338                         $comments = $title->getTalkPage()->getFullURL();
339
340                         return new FeedItem(
341                                 $title->getPrefixedText(),
342                                 $this->feedItemDesc( $row ),
343                                 $title->getFullURL(),
344                                 $date,
345                                 $this->feedItemAuthor( $row ),
346                                 $comments);
347                 } else {
348                         return NULL;
349                 }
350         }
351
352         protected function feedItemAuthor( $row ) {
353                 return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
354         }
355
356         protected function feedItemDesc( $row ) {
357                 $revision = Revision::newFromId( $row->rev_id );
358                 if( $revision ) {
359                         return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) .
360                                 htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . 
361                                 "</p>\n<hr />\n<div>" .
362                                 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
363                 }
364                 return '';
365         }
366 }
367
368 /**
369  * @ingroup SpecialPage Pager
370  */
371 class NewPagesPager extends ReverseChronologicalPager {
372         // Stored opts
373         protected $opts, $mForm;
374
375         function __construct( $form, FormOptions $opts ) {
376                 parent::__construct();
377                 $this->mForm = $form;
378                 $this->opts = $opts;
379         }
380
381         function getTitle() {
382                 static $title = null;
383                 if ( $title === null )
384                         $title = $this->mForm->getTitle();
385                 return $title;
386         }
387
388         function getQueryInfo() {
389                 global $wgEnableNewpagesUserFilter, $wgGroupPermissions, $wgUser;
390                 $conds = array();
391                 $conds['rc_new'] = 1;
392
393                 $namespace = $this->opts->getValue( 'namespace' );
394                 $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
395
396                 $username = $this->opts->getValue( 'username' );
397                 $user = Title::makeTitleSafe( NS_USER, $username );
398
399                 if( $namespace !== false ) {
400                         $conds['rc_namespace'] = $namespace;
401                         $rcIndexes = array( 'new_name_timestamp' );
402                 } else {
403                         $rcIndexes = array( 'rc_timestamp' );
404                 }
405
406                 # $wgEnableNewpagesUserFilter - temp WMF hack
407                 if( $wgEnableNewpagesUserFilter && $user ) {
408                         $conds['rc_user_text'] = $user->getText();
409                         $rcIndexes = 'rc_user_text';
410                 # If anons cannot make new pages, don't "exclude logged in users"!
411                 } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
412                         $conds['rc_user'] = 0;
413                 }
414                 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
415                 if( $this->opts->getValue( 'hidepatrolled' ) && $wgUser->useNPPatrol() ) {
416                         $conds['rc_patrolled'] = 0;
417                 }
418                 if( $this->opts->getValue( 'hidebots' ) ) {
419                         $conds['rc_bot'] = 0;
420                 }
421
422                 if ( $this->opts->getValue( 'hideredirs' ) ) {
423                         $conds['page_is_redirect'] = 0;
424                 }
425
426                 $info = array(
427                         'tables' => array( 'recentchanges', 'page' ),
428                         'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
429                                 rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags',
430                         'conds' => $conds,
431                         'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ),
432                         'join_conds' => array(
433                                 'page' => array('INNER JOIN', 'page_id=rc_cur_id'),
434                         ),
435                 );
436
437                 ## Empty array for fields, it'll be set by us anyway.
438                 $fields = array();
439
440                 ## Modify query for tags
441                 ChangeTags::modifyDisplayQuery( $info['tables'],
442                                                                                 $fields,
443                                                                                 $info['conds'],
444                                                                                 $info['join_conds'],
445                                                                                 $info['options'],
446                                                                                 $this->opts['tagfilter'] );
447
448                 return $info;
449         }
450
451         function getIndexField() {
452                 return 'rc_timestamp';
453         }
454
455         function formatRow( $row ) {
456                 return $this->mForm->formatRow( $row );
457         }
458
459         function getStartBody() {
460                 # Do a batch existence check on pages
461                 $linkBatch = new LinkBatch();
462                 while( $row = $this->mResult->fetchObject() ) {
463                         $linkBatch->add( NS_USER, $row->rc_user_text );
464                         $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
465                         $linkBatch->add( $row->rc_namespace, $row->rc_title );
466                 }
467                 $linkBatch->execute();
468                 return "<ul>";
469         }
470
471         function getEndBody() {
472                 return "</ul>";
473         }
474 }