]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialNewpages.php
MediaWiki 1.16.1-scripts
[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, $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                 $tagFilterVal = $this->opts->consumeValue( 'tagfilter' );
169
170                 // Check username input validity
171                 $ut = Title::makeTitleSafe( NS_USER, $username );
172                 $userText = $ut ? $ut->getText() : '';
173
174                 // Store query values in hidden fields so that form submission doesn't lose them
175                 $hidden = array();
176                 foreach ( $this->opts->getUnconsumedValues() as $key => $value ) {
177                         $hidden[] = Xml::hidden( $key, $value );
178                 }
179                 $hidden = implode( "\n", $hidden );
180
181                 $tagFilter = ChangeTags::buildTagFilterSelector( $tagFilterVal );
182                 if ($tagFilter)
183                         list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter;
184
185                 $form = Xml::openElement( 'form', array( 'action' => $wgScript ) ) .
186                         Xml::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
187                         Xml::fieldset( wfMsg( 'newpages' ) ) .
188                         Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
189                         "<tr>
190                                 <td class='mw-label'>" .
191                                         Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
192                                 "</td>
193                                 <td class='mw-input'>" .
194                                         Xml::namespaceSelector( $namespace, 'all' ) .
195                                 "</td>
196                         </tr>" . ( $tagFilter ? (
197                         "<tr>
198                                 <td class='mw-label'>" .
199                                         $tagFilterLabel .
200                                 "</td>
201                                 <td class='mw-input'>" .
202                                         $tagFilterSelector .
203                                 "</td>
204                         </tr>" ) : '' ) .
205                         ($wgEnableNewpagesUserFilter ?
206                         "<tr>
207                                 <td class='mw-label'>" .
208                                         Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
209                                 "</td>
210                                 <td class='mw-input'>" .
211                                         Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
212                                 "</td>
213                         </tr>" : "" ) .
214                         "<tr> <td></td>
215                                 <td class='mw-submit'>" .
216                                         Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
217                                 "</td>
218                         </tr>" .
219                         "<tr>
220                                 <td></td>
221                                 <td class='mw-input'>" .
222                                         $this->filterLinks() .
223                                 "</td>
224                         </tr>" .
225                         Xml::closeElement( 'table' ) .
226                         Xml::closeElement( 'fieldset' ) .
227                         $hidden .
228                         Xml::closeElement( 'form' );
229
230                 $wgOut->addHTML( $form );
231         }
232
233         protected function setSyndicated() {
234                 global $wgOut;
235                 $wgOut->setSyndicated( true );
236                 $wgOut->setFeedAppendQuery( wfArrayToCGI( $this->opts->getAllValues() ) );
237         }
238
239         /**
240          * Format a row, providing the timestamp, links to the page/history, size, user links, and a comment
241          *
242          * @param $skin Skin to use
243          * @param $result Result row
244          * @return string
245          */
246         public function formatRow( $result ) {
247                 global $wgLang, $wgContLang;
248
249                 $classes = array();
250                 
251                 $dm = $wgContLang->getDirMark();
252
253                 $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
254                 $time = htmlspecialchars( $wgLang->timeAndDate( $result->rc_timestamp, true ) );
255
256                 $query = array( 'redirect' => 'no' );
257
258                 if( $this->patrollable( $result ) )
259                         $query['rcid'] = $result->rc_id;
260
261                 $plink = $this->skin->linkKnown(
262                         $title,
263                         null,
264                         array(),
265                         $query
266                 );
267                 $hist = $this->skin->linkKnown(
268                         $title,
269                         wfMsgHtml( 'hist' ),
270                         array(),
271                         array( 'action' => 'history' )
272                 );
273                 $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
274                         $wgLang->formatNum( $result->length ) );
275                 $ulink = $this->skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
276                         $this->skin->userToolLinks( $result->rc_user, $result->rc_user_text );
277                 $comment = $this->skin->commentBlock( $result->rc_comment );
278                 
279                 if ( $this->patrollable( $result ) )
280                         $classes[] = 'not-patrolled';
281
282                 # Tags, if any.
283                 list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $result->ts_tags, 'newpages' );
284                 $classes = array_merge( $classes, $newClasses );
285
286                 $css = count($classes) ? ' class="'.implode( " ", $classes).'"' : '';
287
288                 return "<li{$css}>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment} {$tagDisplay}</li>\n";
289         }
290
291         /**
292          * Should a specific result row provide "patrollable" links?
293          *
294          * @param $result Result row
295          * @return bool
296          */
297         protected function patrollable( $result ) {
298                 global $wgUser;
299                 return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
300         }
301
302         /**
303          * Output a subscription feed listing recent edits to this page.
304          * @param string $type
305          */
306         protected function feed( $type ) {
307                 global $wgFeed, $wgFeedClasses, $wgFeedLimit;
308
309                 if ( !$wgFeed ) {
310                         global $wgOut;
311                         $wgOut->addWikiMsg( 'feed-unavailable' );
312                         return;
313                 }
314
315                 if( !isset( $wgFeedClasses[$type] ) ) {
316                         global $wgOut;
317                         $wgOut->addWikiMsg( 'feed-invalid' );
318                         return;
319                 }
320
321                 $feed = new $wgFeedClasses[$type](
322                         $this->feedTitle(),
323                         wfMsgExt( 'tagline', 'parsemag' ),
324                         $this->getTitle()->getFullUrl() );
325
326                 $pager = new NewPagesPager( $this, $this->opts );
327                 $limit = $this->opts->getValue( 'limit' );
328                 $pager->mLimit = min( $limit, $wgFeedLimit );
329
330                 $feed->outHeader();
331                 if( $pager->getNumRows() > 0 ) {
332                         while( $row = $pager->mResult->fetchObject() ) {
333                                 $feed->outItem( $this->feedItem( $row ) );
334                         }
335                 }
336                 $feed->outFooter();
337         }
338
339         protected function feedTitle() {
340                 global $wgContLanguageCode, $wgSitename;
341                 $page = SpecialPage::getPage( 'Newpages' );
342                 $desc = $page->getDescription();
343                 return "$wgSitename - $desc [$wgContLanguageCode]";
344         }
345
346         protected function feedItem( $row ) {
347                 $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
348                 if( $title ) {
349                         $date = $row->rc_timestamp;
350                         $comments = $title->getTalkPage()->getFullURL();
351
352                         return new FeedItem(
353                                 $title->getPrefixedText(),
354                                 $this->feedItemDesc( $row ),
355                                 $title->getFullURL(),
356                                 $date,
357                                 $this->feedItemAuthor( $row ),
358                                 $comments);
359                 } else {
360                         return null;
361                 }
362         }
363
364         protected function feedItemAuthor( $row ) {
365                 return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
366         }
367
368         protected function feedItemDesc( $row ) {
369                 $revision = Revision::newFromId( $row->rev_id );
370                 if( $revision ) {
371                         return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) .
372                                 htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . 
373                                 "</p>\n<hr />\n<div>" .
374                                 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
375                 }
376                 return '';
377         }
378 }
379
380 /**
381  * @ingroup SpecialPage Pager
382  */
383 class NewPagesPager extends ReverseChronologicalPager {
384         // Stored opts
385         protected $opts, $mForm;
386
387         function __construct( $form, FormOptions $opts ) {
388                 parent::__construct();
389                 $this->mForm = $form;
390                 $this->opts = $opts;
391         }
392
393         function getTitle() {
394                 static $title = null;
395                 if ( $title === null )
396                         $title = $this->mForm->getTitle();
397                 return $title;
398         }
399
400         function getQueryInfo() {
401                 global $wgEnableNewpagesUserFilter, $wgGroupPermissions, $wgUser;
402                 $conds = array();
403                 $conds['rc_new'] = 1;
404
405                 $namespace = $this->opts->getValue( 'namespace' );
406                 $namespace = ( $namespace === 'all' ) ? false : intval( $namespace );
407
408                 $username = $this->opts->getValue( 'username' );
409                 $user = Title::makeTitleSafe( NS_USER, $username );
410
411                 if( $namespace !== false ) {
412                         $conds['rc_namespace'] = $namespace;
413                         $rcIndexes = array( 'new_name_timestamp' );
414                 } else {
415                         $rcIndexes = array( 'rc_timestamp' );
416                 }
417
418                 # $wgEnableNewpagesUserFilter - temp WMF hack
419                 if( $wgEnableNewpagesUserFilter && $user ) {
420                         $conds['rc_user_text'] = $user->getText();
421                         $rcIndexes = 'rc_user_text';
422                 # If anons cannot make new pages, don't "exclude logged in users"!
423                 } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) {
424                         $conds['rc_user'] = 0;
425                 }
426                 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
427                 if( $this->opts->getValue( 'hidepatrolled' ) && $wgUser->useNPPatrol() ) {
428                         $conds['rc_patrolled'] = 0;
429                 }
430                 if( $this->opts->getValue( 'hidebots' ) ) {
431                         $conds['rc_bot'] = 0;
432                 }
433
434                 if ( $this->opts->getValue( 'hideredirs' ) ) {
435                         $conds['page_is_redirect'] = 0;
436                 }
437
438                 $info = array(
439                         'tables' => array( 'recentchanges', 'page' ),
440                         'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
441                                 rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags',
442                         'conds' => $conds,
443                         'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ),
444                         'join_conds' => array(
445                                 'page' => array('INNER JOIN', 'page_id=rc_cur_id'),
446                         ),
447                 );
448
449                 ## Empty array for fields, it'll be set by us anyway.
450                 $fields = array();
451
452                 ## Modify query for tags
453                 ChangeTags::modifyDisplayQuery( $info['tables'],
454                                                                                 $fields,
455                                                                                 $info['conds'],
456                                                                                 $info['join_conds'],
457                                                                                 $info['options'],
458                                                                                 $this->opts['tagfilter'] );
459
460                 return $info;
461         }
462
463         function getIndexField() {
464                 return 'rc_timestamp';
465         }
466
467         function formatRow( $row ) {
468                 return $this->mForm->formatRow( $row );
469         }
470
471         function getStartBody() {
472                 # Do a batch existence check on pages
473                 $linkBatch = new LinkBatch();
474                 while( $row = $this->mResult->fetchObject() ) {
475                         $linkBatch->add( NS_USER, $row->rc_user_text );
476                         $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
477                         $linkBatch->add( $row->rc_namespace, $row->rc_title );
478                 }
479                 $linkBatch->execute();
480                 return "<ul>";
481         }
482
483         function getEndBody() {
484                 return "</ul>";
485         }
486 }