* New helper class for dealing with forms
[lhc/web/wiklou.git] / includes / SpecialNewpages.php
1 <?php
2 /**
3 *
4 * @addtogroup SpecialPage
5 */
6
7
8 /**
9 * Start point
10 */
11 function wfSpecialNewPages( $par, $sp ) {
12 $page = new NewPagesForm();
13
14 $page->showList( $par, $sp->including() );
15 }
16
17 /**
18 * implements Special:Newpages
19 * @addtogroup SpecialPage
20 */
21 class NewPagesForm {
22 /**
23 * Show a form for filtering namespace and username
24 *
25 * @param string $par
26 * @param bool $including true if the page is being included with {{Special:Newpages}}
27 * @return string
28 */
29 public function showList( $par, $including ) {
30 global $wgScript, $wgLang, $wgGroupPermissions, $wgRequest, $wgUser, $wgOut;
31 $sk = $wgUser->getSkin();
32 $self = SpecialPage::getTitleFor( 'NewPages' );
33
34 // show/hide links
35 $showhide = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
36
37 $hidelinks = array();
38
39 if ( $wgGroupPermissions['*']['createpage'] === true ) {
40 $hidelinks['hideliu'] = 'rcshowhideliu';
41 }
42 if ( $wgUser->useNPPatrol() ) {
43 $hidelinks['hidepatrolled'] = 'rcshowhidepatr';
44 }
45 $hidelinks['hidebots'] = 'rcshowhidebots';
46
47 $defaults = array(
48 /* bool */ 'hideliu' => false,
49 /* bool */ 'hidepatrolled' => false,
50 /* bool */ 'hidebots' => false,
51 /* text */ 'namespace' => "0",
52 /* text */ 'username' => '',
53 /* int */ 'offset' => 0,
54 /* int */ 'limit' => 50,
55 );
56 $options = $defaults;
57
58 // Override all values from requests, if specified
59 foreach ( $defaults as $v => $t ) {
60 if ( is_bool($t) ) {
61 $options[$v] = $wgRequest->getBool( $v, $options[$v] );
62 } elseif( is_int($t) ) {
63 $options[$v] = $wgRequest->getInt( $v, $options[$v] );
64 } elseif( is_string($t) ) {
65 $options[$v] = $wgRequest->getText( $v, $options[$v] );
66 }
67 }
68
69 $shownav = !$including;
70 if ( $par ) {
71 $bits = preg_split( '/\s*,\s*/', trim( $par ) );
72 foreach ( $bits as $bit ) {
73 if ( 'shownav' == $bit )
74 $shownav = true;
75 if ( 'hideliu' === $bit )
76 $options['hideliu'] = true;
77 if ( 'hidepatrolled' == $bit )
78 $options['hidepatrolled'] = true;
79 if ( 'hidebots' == $bit )
80 $options['hidebots'] = true;
81 if ( is_numeric( $bit ) )
82 $options['limit'] = intval( $bit );
83
84 $m = array();
85 if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) )
86 $options['limit'] = intval($m[1]);
87 if ( preg_match( '/^offset=(\d+)$/', $bit, $m ) )
88 $options['offset'] = intval($m[1]);
89 if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
90 $ns = $wgLang->getNsIndex( $m[1] );
91 if( $ns !== false ) {
92 $options['namespace'] = $ns;
93 }
94 }
95 }
96 }
97
98 // hack disable
99 $options['username'] = '';
100
101 if( !$including ){
102 $wgOut->setSyndicated( true );
103 $wgOut->setFeedAppendQuery( "namespace={$options['namespace']}&username={$options['username']}" );
104
105 $feedType = $wgRequest->getVal( 'feed' );
106 if( $feedType ) {
107 wfProfileOut( __METHOD__ );
108 return $this->feed( $feedType, $options );
109 }
110
111 $nondefaults = array();
112 foreach ( $options as $v => $t ) {
113 if ( $v === 'offset' ) continue; # Reset offset if parameters change
114 wfAppendToArrayIfNotDefault( $v, $t, $defaults, $nondefaults );
115 }
116
117 $links = array();
118 foreach ( $hidelinks as $key => $msg ) {
119 $reversed = 1 - $options[$key];
120 $link = $sk->makeKnownLinkObj( $self, $showhide[$reversed],
121 wfArrayToCGI( array( $key => $reversed ), $nondefaults )
122 );
123 $links[$key] = wfMsgHtml( $msg, $link );
124 }
125
126 $hl = implode( ' | ', $links );
127
128 // Store query values in hidden fields so that form submission doesn't lose them
129 $hidden = array();
130 foreach ( $nondefaults as $key => $value ) {
131 if ( $key === 'namespace' ) continue;
132 if ( $key === 'username' ) continue;
133 $hidden[] = Xml::hidden( $key, $value );
134 }
135 $hidden = implode( "\n", $hidden );
136
137 $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
138 Xml::hidden( 'title', $self->getPrefixedDBkey() ) .
139 Xml::openElement( 'fieldset' ) .
140 Xml::element( 'legend', null, wfMsg( 'newpages' ) ) .
141 Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
142 "<tr>
143 <td class='mw-label'>" .
144 Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
145 "</td>
146 <td class='mw-input'>" .
147 Xml::namespaceSelector( $options['namespace'], 'all' ) .
148 "</td>
149 </tr>
150 <!--
151 <tr>
152 <td class='mw-label'>" .
153 Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) .
154 "</td>
155 <td class='mw-input'>" .
156 Xml::input( 'username', 30, $options['username'], array( 'id' => 'mw-np-username' ) ) .
157 "</td>
158 </tr>
159 -->
160 <tr> <td></td>
161 <td class='mw-submit'>" .
162 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
163 "</td>
164 </tr>" .
165 "<tr>
166 <td></td>
167 <td class='mw-input'>" .
168 $hl .
169 "</td>
170 </tr>" .
171 Xml::closeElement( 'table' ) .
172 Xml::closeElement( 'fieldset' ) .
173 $hidden .
174 Xml::closeElement( 'form' );
175
176 $wgOut->addHTML( $form );
177 }
178
179 $pager = new NewPagesPager( $this, array(), $options['namespace'], $options['hideliu'],
180 $options['hidepatrolled'], $options['hidebots'], $options['username'] );
181 $pager->mLimit = $options['limit'];
182 $pager->mOffset = $options['offset'];
183
184 if( $pager->getNumRows() ) {
185 $wgOut->addHTML(
186 ( $shownav ? $pager->getNavigationBar() : '' ) .
187 $pager->getBody() .
188 ( $shownav ? $pager->getNavigationBar() : '' )
189 );
190 } else {
191 $wgOut->addHTML( Xml::element( 'p', null, wfMsg( 'specialpage-empty' ) ) );
192 }
193 }
194
195 /**
196 * Format a row, providing the timestamp, links to the page/history, size, user links, and a comment
197 *
198 * @param $skin Skin to use
199 * @param $result Result row
200 * @return string
201 */
202 public function formatRow( $result ) {
203 global $wgLang, $wgContLang, $wgUser;
204 $dm = $wgContLang->getDirMark();
205
206 static $skin=null;
207
208 if( is_null( $skin ) )
209 $skin = $wgUser->getSkin();
210
211 $title = Title::makeTitleSafe( $result->rc_namespace, $result->rc_title );
212 $time = $wgLang->timeAndDate( $result->rc_timestamp, true );
213 $plink = $skin->makeKnownLinkObj( $title, '', $this->patrollable( $result ) ? 'rcid=' . $result->rc_id : '' );
214 $hist = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'hist' ), 'action=history' );
215 $length = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
216 $wgLang->formatNum( htmlspecialchars( $result->length ) ) );
217 $ulink = $skin->userLink( $result->rc_user, $result->rc_user_text ) . ' ' .
218 $skin->userToolLinks( $result->rc_user, $result->rc_user_text );
219 $comment = $skin->commentBlock( $result->rc_comment );
220 $css = $this->patrollable( $result ) ? 'not-patrolled' : '';
221
222 return "<li class='$css'>{$time} {$dm}{$plink} ({$hist}) {$dm}[{$length}] {$dm}{$ulink} {$comment}</li>\n";
223 }
224
225 /**
226 * Should a specific result row provide "patrollable" links?
227 *
228 * @param $result Result row
229 * @return bool
230 */
231 protected function patrollable( $result ) {
232 global $wgUser;
233 return ( $wgUser->useNPPatrol() && !$result->rc_patrolled );
234 }
235
236 /**
237 * Output a subscription feed listing recent edits to this page.
238 * @param string $type
239 */
240 protected function feed( $type, $options ) {
241 require_once 'SpecialRecentchanges.php';
242
243 global $wgFeed, $wgFeedClasses;
244
245 if ( !$wgFeed ) {
246 global $wgOut;
247 $wgOut->addWikiMsg( 'feed-unavailable' );
248 return;
249 }
250
251 if( !isset( $wgFeedClasses[$type] ) ) {
252 global $wgOut;
253 $wgOut->addWikiMsg( 'feed-invalid' );
254 return;
255 }
256
257 $self = SpecialPage::getTitleFor( 'NewPages' );
258 $feed = new $wgFeedClasses[$type](
259 $this->feedTitle(),
260 wfMsg( 'tagline' ),
261 $self->getFullUrl() );
262
263 $pager = new NewPagesPager( $this, array(), $options['namespace'], $options['hideliu'],
264 $options['hidepatrolled'], $options['hidebots'], $options['username'] );
265
266 $feed->outHeader();
267 if( $pager->getNumRows() > 0 ) {
268 while( $row = $pager->mResult->fetchObject() ) {
269 $feed->outItem( $this->feedItem( $row ) );
270 }
271 }
272 $feed->outFooter();
273 }
274
275 protected function feedTitle() {
276 global $wgContLanguageCode, $wgSitename;
277 $page = SpecialPage::getPage( 'Newpages' );
278 $desc = $page->getDescription();
279 return "$wgSitename - $desc [$wgContLanguageCode]";
280 }
281
282 protected function feedItem( $row ) {
283 $title = Title::MakeTitle( intval( $row->rc_namespace ), $row->rc_title );
284 if( $title ) {
285 $date = $row->rc_timestamp;
286 $comments = $this->stripComment( $row->rc_comment );
287
288 return new FeedItem(
289 $title->getPrefixedText(),
290 $this->feedItemDesc( $row ),
291 $title->getFullURL(),
292 $date,
293 $this->feedItemAuthor( $row ),
294 $comments);
295 } else {
296 return NULL;
297 }
298 }
299
300 /**
301 * Quickie hack... strip out wikilinks to more legible form from the comment.
302 */
303 function stripComment( $text ) {
304 return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
305 }
306
307 function feedItemAuthor( $row ) {
308 return isset( $row->rc_user_text ) ? $row->rc_user_text : '';
309 }
310
311 protected function feedItemDesc( $row ) {
312 $revision = Revision::newFromId( $row->rev_id );
313 if( $revision ) {
314 return '<p>' . htmlspecialchars( wfMsg( 'summary' ) ) . ': ' .
315 htmlspecialchars( $revision->getComment() ) . "</p>\n<hr />\n<div>" .
316 nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
317 }
318 return '';
319 }
320 }
321
322 /**
323 * @addtogroup Pager
324 */
325 class NewPagesPager extends ReverseChronologicalPager {
326 private $hideliu, $hidepatrolled, $hidebots, $namespace, $user, $spTitle;
327
328 function __construct( $form, $conds=array(), $namespace, $hliu=false, $hpatrolled=false, $hbots=1, $user='' ) {
329 parent::__construct();
330 $this->mForm = $form;
331 $this->mConds = $conds;
332
333 $this->namespace = ($namespace === "all") ? false : intval($namespace);
334 $this->user = $user;
335
336 $this->hideliu = (bool)$hliu;
337 $this->hidepatrolled = (bool)$hpatrolled;
338 $this->hidebots = (bool)$hbots;
339 }
340
341 function getTitle(){
342 if( !isset( $this->spTitle ) )
343 $this->spTitle = SpecialPage::getTitleFor( 'Newpages' );
344 return $this->spTitle;
345 }
346
347 function getQueryInfo() {
348 $conds = $this->mConds;
349 $conds['rc_new'] = 1;
350 if( $this->namespace !== false ) {
351 $conds['rc_namespace'] = $this->namespace;
352 $rcIndexes = array( 'new_name_timestamp' );
353 } else {
354 $rcIndexes = array( 'rc_timestamp' );
355 }
356 $conds[] = 'page_id = rc_cur_id';
357 $conds['page_is_redirect'] = 0;
358
359 global $wgGroupPermissions, $wgUser;
360 # If anons cannot make new pages, don't query for it!
361 if( $wgGroupPermissions['*']['createpage'] && $this->hideliu ) {
362 $conds['rc_user'] = 0;
363 } else {
364 $title = Title::makeTitleSafe( NS_USER, $this->user );
365 if( $title ) {
366 $conds['rc_user_text'] = $title->getText();
367 }
368 }
369 # If this user cannot see patrolled edits or they are off, don't do dumb queries!
370 if( $this->hidepatrolled && $wgUser->useNPPatrol() ) {
371 $conds['rc_patrolled'] = 0;
372 }
373 if( $this->hidebots ) {
374 $conds['rc_bot'] = 0;
375 }
376
377 if( $this->user ) {
378 $conds['rc_user_text'] = $this->user;
379 }
380
381 return array(
382 'tables' => array( 'recentchanges', 'page' ),
383 'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
384 rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id',
385 'conds' => $conds,
386 'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) )
387 );
388 }
389
390 function getIndexField() {
391 return 'rc_timestamp';
392 }
393
394 function formatRow( $row ) {
395 return $this->mForm->formatRow( $row );
396 }
397
398 function getStartBody() {
399 # Do a batch existence check on pages
400 $linkBatch = new LinkBatch();
401 while( $row = $this->mResult->fetchObject() ) {
402 $linkBatch->add( NS_USER, $row->rc_user_text );
403 $linkBatch->add( NS_USER_TALK, $row->rc_user_text );
404 $linkBatch->add( $row->rc_namespace, $row->rc_title );
405 }
406 $linkBatch->execute();
407 return "<ul>";
408 }
409
410 function getEndBody() {
411 return "</ul>";
412 }
413 }