Update to r32375 / bug 11874 -- !important may have whitespace between ! and important
[lhc/web/wiklou.git] / includes / SpecialAllpages.php
1 <?php
2 /**
3 * @addtogroup SpecialPage
4 */
5
6 /**
7 * Entry point : initialise variables and call subfunctions.
8 * @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL)
9 * @param $specialPage See the SpecialPage object.
10 */
11 function wfSpecialAllpages( $par=NULL, $specialPage ) {
12 global $wgRequest, $wgOut, $wgContLang;
13
14 # GET values
15 $from = $wgRequest->getVal( 'from' );
16 $namespace = $wgRequest->getInt( 'namespace' );
17
18 $namespaces = $wgContLang->getNamespaces();
19
20 $indexPage = new SpecialAllpages();
21
22 $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ?
23 wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
24 wfMsg( 'allarticles' )
25 );
26
27 if ( isset($par) ) {
28 $indexPage->showChunk( $namespace, $par, $specialPage->including() );
29 } elseif ( isset($from) ) {
30 $indexPage->showChunk( $namespace, $from, $specialPage->including() );
31 } else {
32 $indexPage->showToplevel ( $namespace, $specialPage->including() );
33 }
34 }
35
36 /**
37 * Implements Special:Allpages
38 * @addtogroup SpecialPage
39 */
40 class SpecialAllpages {
41 /**
42 * Maximum number of pages to show on single subpage.
43 */
44 protected $maxPerPage = 960;
45
46 /**
47 * Name of this special page. Used to make title objects that reference back
48 * to this page.
49 */
50 protected $name = 'Allpages';
51
52 /**
53 * Determines, which message describes the input field 'nsfrom'.
54 */
55 protected $nsfromMsg = 'allpagesfrom';
56
57 /**
58 * HTML for the top form
59 * @param integer $namespace A namespace constant (default NS_MAIN).
60 * @param string $from Article name we are starting listing at.
61 */
62 function namespaceForm ( $namespace = NS_MAIN, $from = '' ) {
63 global $wgScript;
64 $t = SpecialPage::getTitleFor( $this->name );
65
66 $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
67 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
68 $out .= Xml::hidden( 'title', $t->getPrefixedText() );
69 $out .= Xml::openElement( 'fieldset' );
70 $out .= Xml::element( 'legend', null, wfMsg( 'allpages' ) );
71 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
72 $out .= "<tr>
73 <td class='mw-label'>" .
74 Xml::label( wfMsg( $this->nsfromMsg ), 'nsfrom' ) .
75 "</td>
76 <td class='mw-input'>" .
77 Xml::input( 'from', 20, $from, array( 'id' => 'nsfrom' ) ) .
78 "</td>
79 </tr>
80 <tr>
81 <td class='mw-label'>" .
82 Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
83 "</td>
84 <td class='mw-input'>" .
85 Xml::namespaceSelector( $namespace, null ) . ' ' .
86 Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
87 "</td>
88 </tr>";
89 $out .= Xml::closeElement( 'table' );
90 $out .= Xml::closeElement( 'fieldset' );
91 $out .= Xml::closeElement( 'form' );
92 $out .= Xml::closeElement( 'div' );
93 return $out;
94 }
95
96 /**
97 * @param integer $namespace (default NS_MAIN)
98 */
99 function showToplevel ( $namespace = NS_MAIN, $including = false ) {
100 global $wgOut, $wgContLang;
101 $align = $wgContLang->isRtl() ? 'left' : 'right';
102
103 # TODO: Either make this *much* faster or cache the title index points
104 # in the querycache table.
105
106 $dbr = wfGetDB( DB_SLAVE );
107 $out = "";
108 $where = array( 'page_namespace' => $namespace );
109
110 global $wgMemc;
111 $key = wfMemcKey( 'allpages', 'ns', $namespace );
112 $lines = $wgMemc->get( $key );
113
114 if( !is_array( $lines ) ) {
115 $options = array( 'LIMIT' => 1 );
116 if ( ! $dbr->implicitOrderby() ) {
117 $options['ORDER BY'] = 'page_title';
118 }
119 $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options );
120 $lastTitle = $firstTitle;
121
122 # This array is going to hold the page_titles in order.
123 $lines = array( $firstTitle );
124
125 # If we are going to show n rows, we need n+1 queries to find the relevant titles.
126 $done = false;
127 for( $i = 0; !$done; ++$i ) {
128 // Fetch the last title of this chunk and the first of the next
129 $chunk = is_null( $lastTitle )
130 ? ''
131 : 'page_title >= ' . $dbr->addQuotes( $lastTitle );
132 $res = $dbr->select(
133 'page', /* FROM */
134 'page_title', /* WHAT */
135 $where + array($chunk),
136 __METHOD__,
137 array ('LIMIT' => 2, 'OFFSET' => $this->maxPerPage - 1, 'ORDER BY' => 'page_title') );
138
139 if ( $s = $dbr->fetchObject( $res ) ) {
140 array_push( $lines, $s->page_title );
141 } else {
142 // Final chunk, but ended prematurely. Go back and find the end.
143 $endTitle = $dbr->selectField( 'page', 'MAX(page_title)',
144 array(
145 'page_namespace' => $namespace,
146 $chunk
147 ), __METHOD__ );
148 array_push( $lines, $endTitle );
149 $done = true;
150 }
151 if( $s = $dbr->fetchObject( $res ) ) {
152 array_push( $lines, $s->page_title );
153 $lastTitle = $s->page_title;
154 } else {
155 // This was a final chunk and ended exactly at the limit.
156 // Rare but convenient!
157 $done = true;
158 }
159 $dbr->freeResult( $res );
160 }
161 $wgMemc->add( $key, $lines, 3600 );
162 }
163
164 // If there are only two or less sections, don't even display them.
165 // Instead, display the first section directly.
166 if( count( $lines ) <= 2 ) {
167 $this->showChunk( $namespace, '', $including );
168 return;
169 }
170
171 # At this point, $lines should contain an even number of elements.
172 $out .= "<table class='allpageslist' style='background: inherit;'>";
173 while ( count ( $lines ) > 0 ) {
174 $inpoint = array_shift ( $lines );
175 $outpoint = array_shift ( $lines );
176 $out .= $this->showline ( $inpoint, $outpoint, $namespace, false );
177 }
178 $out .= '</table>';
179 $nsForm = $this->namespaceForm( $namespace, '', false );
180
181 # Is there more?
182 if ( $including ) {
183 $out2 = '';
184 } else {
185 $morelinks = '';
186 if ( $morelinks != '' ) {
187 $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
188 $out2 .= '<tr valign="top"><td>' . $nsForm;
189 $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">';
190 $out2 .= $morelinks . '</td></tr></table><hr />';
191 } else {
192 $out2 = $nsForm . '<hr />';
193 }
194 }
195
196 $wgOut->addHtml( $out2 . $out );
197 }
198
199 /**
200 * @todo Document
201 * @param string $from
202 * @param integer $namespace (Default NS_MAIN)
203 */
204 function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
205 global $wgContLang;
206 $align = $wgContLang->isRtl() ? 'left' : 'right';
207 $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
208 $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
209 $queryparams = ($namespace ? "namespace=$namespace" : '');
210 $special = SpecialPage::getTitleFor( $this->name, $inpoint );
211 $link = $special->escapeLocalUrl( $queryparams );
212
213 $out = wfMsgHtml(
214 'alphaindexline',
215 "<a href=\"$link\">$inpointf</a></td><td><a href=\"$link\">",
216 "</a></td><td><a href=\"$link\">$outpointf</a>"
217 );
218 return '<tr><td align="' . $align . '">'.$out.'</td></tr>';
219 }
220
221 /**
222 * @param integer $namespace (Default NS_MAIN)
223 * @param string $from list all pages from this name (default FALSE)
224 */
225 function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
226 global $wgOut, $wgUser, $wgContLang;
227
228 $sk = $wgUser->getSkin();
229
230 $fromList = $this->getNamespaceKeyAndText($namespace, $from);
231 $namespaces = $wgContLang->getNamespaces();
232 $align = $wgContLang->isRtl() ? 'left' : 'right';
233
234 $n = 0;
235
236 if ( !$fromList ) {
237 $out = wfMsgWikiHtml( 'allpagesbadtitle' );
238 } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
239 // Show errormessage and reset to NS_MAIN
240 $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
241 $namespace = NS_MAIN;
242 } else {
243 list( $namespace, $fromKey, $from ) = $fromList;
244
245 $dbr = wfGetDB( DB_SLAVE );
246 $res = $dbr->select( 'page',
247 array( 'page_namespace', 'page_title', 'page_is_redirect' ),
248 array(
249 'page_namespace' => $namespace,
250 'page_title >= ' . $dbr->addQuotes( $fromKey )
251 ),
252 __METHOD__,
253 array(
254 'ORDER BY' => 'page_title',
255 'LIMIT' => $this->maxPerPage + 1,
256 'USE INDEX' => 'name_title',
257 )
258 );
259
260 $out = '<table style="background: inherit;" border="0" width="100%">';
261
262 while( ($n < $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
263 $t = Title::makeTitle( $s->page_namespace, $s->page_title );
264 if( $t ) {
265 $link = ($s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) .
266 $sk->makeKnownLinkObj( $t, htmlspecialchars( $t->getText() ), false, false ) .
267 ($s->page_is_redirect ? '</div>' : '' );
268 } else {
269 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
270 }
271 if( $n % 3 == 0 ) {
272 $out .= '<tr>';
273 }
274 $out .= "<td width=\"33%\">$link</td>";
275 $n++;
276 if( $n % 3 == 0 ) {
277 $out .= '</tr>';
278 }
279 }
280 if( ($n % 3) != 0 ) {
281 $out .= '</tr>';
282 }
283 $out .= '</table>';
284 }
285
286 if ( $including ) {
287 $out2 = '';
288 } else {
289 if( $from == '' ) {
290 // First chunk; no previous link.
291 $prevTitle = null;
292 } else {
293 # Get the last title from previous chunk
294 $dbr = wfGetDB( DB_SLAVE );
295 $res_prev = $dbr->select(
296 'page',
297 'page_title',
298 array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ),
299 __METHOD__,
300 array( 'ORDER BY' => 'page_title DESC', 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) )
301 );
302
303 # Get first title of previous complete chunk
304 if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) {
305 $pt = $dbr->fetchObject( $res_prev );
306 $prevTitle = Title::makeTitle( $namespace, $pt->page_title );
307 } else {
308 # The previous chunk is not complete, need to link to the very first title
309 # available in the database
310 $options = array( 'LIMIT' => 1 );
311 if ( ! $dbr->implicitOrderby() ) {
312 $options['ORDER BY'] = 'page_title';
313 }
314 $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), __METHOD__, $options );
315 # Show the previous link if it s not the current requested chunk
316 if( $from != $reallyFirstPage_title ) {
317 $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title );
318 } else {
319 $prevTitle = null;
320 }
321 }
322 }
323
324 $nsForm = $this->namespaceForm( $namespace, $from );
325 $out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
326 $out2 .= '<tr valign="top"><td>' . $nsForm;
327 $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
328 $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
329 wfMsgHtml ( 'allpages' ) );
330
331 $self = SpecialPage::getTitleFor( 'Allpages' );
332
333 # Do we put a previous link ?
334 if( isset( $prevTitle ) && $pt = $prevTitle->getText() ) {
335 $q = 'from=' . $prevTitle->getPartialUrl() . ( $namespace ? '&namespace=' . $namespace : '' );
336 $prevLink = $sk->makeKnownLinkObj( $self, wfMsgHTML( 'prevpage', $pt ), $q );
337 $out2 .= ' | ' . $prevLink;
338 }
339
340 if( $n == $this->maxPerPage && $s = $dbr->fetchObject($res) ) {
341 # $s is the first link of the next chunk
342 $t = Title::MakeTitle($namespace, $s->page_title);
343 $q = 'from=' . $t->getPartialUrl() . ( $namespace ? '&namespace=' . $namespace : '' );
344 $nextLink = $sk->makeKnownLinkObj( $self, wfMsgHtml( 'nextpage', $t->getText() ), $q );
345 $out2 .= ' | ' . $nextLink;
346 }
347 $out2 .= "</td></tr></table><hr />";
348 }
349
350 $wgOut->addHtml( $out2 . $out );
351 if( isset($prevLink) or isset($nextLink) ) {
352 $wgOut->addHtml( '<hr /><p style="font-size: smaller; float: ' . $align . '">' );
353 if( isset( $prevLink ) ) {
354 $wgOut->addHTML( $prevLink );
355 }
356 if( isset( $prevLink ) && isset( $nextLink ) ) {
357 $wgOut->addHTML( ' | ' );
358 }
359 if( isset( $nextLink ) ) {
360 $wgOut->addHTML( $nextLink );
361 }
362 $wgOut->addHTML( '</p>' );
363
364 }
365
366 }
367
368 /**
369 * @param int $ns the namespace of the article
370 * @param string $text the name of the article
371 * @return array( int namespace, string dbkey, string pagename ) or NULL on error
372 * @static (sort of)
373 * @access private
374 */
375 function getNamespaceKeyAndText ($ns, $text) {
376 if ( $text == '' )
377 return array( $ns, '', '' ); # shortcut for common case
378
379 $t = Title::makeTitleSafe($ns, $text);
380 if ( $t && $t->isLocal() ) {
381 return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
382 } else if ( $t ) {
383 return NULL;
384 }
385
386 # try again, in case the problem was an empty pagename
387 $text = preg_replace('/(#|$)/', 'X$1', $text);
388 $t = Title::makeTitleSafe($ns, $text);
389 if ( $t && $t->isLocal() ) {
390 return array( $t->getNamespace(), '', '' );
391 } else {
392 return NULL;
393 }
394 }
395 }
396
397 ?>