MediaWiki
REL1_19
|
00001 <?php 00029 class SpecialAllpages extends IncludableSpecialPage { 00030 00034 protected $maxPerPage = 345; 00035 00039 protected $maxLineCount = 100; 00040 00044 protected $maxPageLength = 70; 00045 00049 protected $nsfromMsg = 'allpagesfrom'; 00050 00051 function __construct( $name = 'Allpages' ){ 00052 parent::__construct( $name ); 00053 } 00054 00060 function execute( $par ) { 00061 global $wgContLang; 00062 $request = $this->getRequest(); 00063 $out = $this->getOutput(); 00064 00065 $this->setHeaders(); 00066 $this->outputHeader(); 00067 $out->allowClickjacking(); 00068 00069 # GET values 00070 $from = $request->getVal( 'from', null ); 00071 $to = $request->getVal( 'to', null ); 00072 $namespace = $request->getInt( 'namespace' ); 00073 00074 $namespaces = $wgContLang->getNamespaces(); 00075 00076 $out->setPageTitle( 00077 ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ? 00078 $this->msg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) : 00079 $this->msg( 'allarticles' ) 00080 ); 00081 $out->addModuleStyles( 'mediawiki.special' ); 00082 00083 if( $par !== null ) { 00084 $this->showChunk( $namespace, $par, $to ); 00085 } elseif( $from !== null && $to === null ) { 00086 $this->showChunk( $namespace, $from, $to ); 00087 } else { 00088 $this->showToplevel( $namespace, $from, $to ); 00089 } 00090 } 00091 00099 function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '' ) { 00100 global $wgScript; 00101 $t = $this->getTitle(); 00102 00103 $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); 00104 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); 00105 $out .= Html::hidden( 'title', $t->getPrefixedText() ); 00106 $out .= Xml::openElement( 'fieldset' ); 00107 $out .= Xml::element( 'legend', null, $this->msg( 'allpages' )->text() ); 00108 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) ); 00109 $out .= "<tr> 00110 <td class='mw-label'>" . 00111 Xml::label( $this->msg( 'allpagesfrom' )->text(), 'nsfrom' ) . 00112 " </td> 00113 <td class='mw-input'>" . 00114 Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) . 00115 " </td> 00116 </tr> 00117 <tr> 00118 <td class='mw-label'>" . 00119 Xml::label( $this->msg( 'allpagesto' )->text(), 'nsto' ) . 00120 " </td> 00121 <td class='mw-input'>" . 00122 Xml::input( 'to', 30, str_replace('_',' ',$to), array( 'id' => 'nsto' ) ) . 00123 " </td> 00124 </tr> 00125 <tr> 00126 <td class='mw-label'>" . 00127 Xml::label( $this->msg( 'namespace' )->text(), 'namespace' ) . 00128 " </td> 00129 <td class='mw-input'>" . 00130 Html::namespaceSelector( 00131 array( 'selected' => $namespace ), 00132 array( 'name' => 'namespace', 'id' => 'namespace' ) 00133 ) . ' ' . 00134 Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . 00135 " </td> 00136 </tr>"; 00137 $out .= Xml::closeElement( 'table' ); 00138 $out .= Xml::closeElement( 'fieldset' ); 00139 $out .= Xml::closeElement( 'form' ); 00140 $out .= Xml::closeElement( 'div' ); 00141 return $out; 00142 } 00143 00149 function showToplevel( $namespace = NS_MAIN, $from = '', $to = '' ) { 00150 $output = $this->getOutput(); 00151 00152 # TODO: Either make this *much* faster or cache the title index points 00153 # in the querycache table. 00154 00155 $dbr = wfGetDB( DB_SLAVE ); 00156 $out = ""; 00157 $where = array( 'page_namespace' => $namespace ); 00158 00159 $from = Title::makeTitleSafe( $namespace, $from ); 00160 $to = Title::makeTitleSafe( $namespace, $to ); 00161 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null; 00162 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null; 00163 00164 if( isset($from) ) 00165 $where[] = 'page_title >= '.$dbr->addQuotes( $from ); 00166 if( isset($to) ) 00167 $where[] = 'page_title <= '.$dbr->addQuotes( $to ); 00168 00169 global $wgMemc; 00170 $key = wfMemcKey( 'allpages', 'ns', $namespace, $from, $to ); 00171 $lines = $wgMemc->get( $key ); 00172 00173 $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ ); 00174 $maxPerSubpage = intval($count/$this->maxLineCount); 00175 $maxPerSubpage = max($maxPerSubpage,$this->maxPerPage); 00176 00177 if( !is_array( $lines ) ) { 00178 $options = array( 'LIMIT' => 1 ); 00179 $options['ORDER BY'] = 'page_title ASC'; 00180 $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options ); 00181 $lastTitle = $firstTitle; 00182 # This array is going to hold the page_titles in order. 00183 $lines = array( $firstTitle ); 00184 # If we are going to show n rows, we need n+1 queries to find the relevant titles. 00185 $done = false; 00186 while( !$done ) { 00187 // Fetch the last title of this chunk and the first of the next 00188 $chunk = ( $lastTitle === false ) 00189 ? array() 00190 : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) ); 00191 $res = $dbr->select( 'page', /* FROM */ 00192 'page_title', /* WHAT */ 00193 array_merge($where,$chunk), 00194 __METHOD__, 00195 array ('LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC') 00196 ); 00197 00198 $s = $dbr->fetchObject( $res ); 00199 if( $s ) { 00200 array_push( $lines, $s->page_title ); 00201 } else { 00202 // Final chunk, but ended prematurely. Go back and find the end. 00203 $endTitle = $dbr->selectField( 'page', 'MAX(page_title)', 00204 array_merge($where,$chunk), 00205 __METHOD__ ); 00206 array_push( $lines, $endTitle ); 00207 $done = true; 00208 } 00209 $s = $res->fetchObject(); 00210 if( $s ) { 00211 array_push( $lines, $s->page_title ); 00212 $lastTitle = $s->page_title; 00213 } else { 00214 // This was a final chunk and ended exactly at the limit. 00215 // Rare but convenient! 00216 $done = true; 00217 } 00218 $res->free(); 00219 } 00220 $wgMemc->add( $key, $lines, 3600 ); 00221 } 00222 00223 // If there are only two or less sections, don't even display them. 00224 // Instead, display the first section directly. 00225 if( count( $lines ) <= 2 ) { 00226 if( !empty($lines) ) { 00227 $this->showChunk( $namespace, $from, $to ); 00228 } else { 00229 $output->addHTML( $this->namespaceForm( $namespace, $from, $to ) ); 00230 } 00231 return; 00232 } 00233 00234 # At this point, $lines should contain an even number of elements. 00235 $out .= Xml::openElement( 'table', array( 'class' => 'allpageslist' ) ); 00236 while( count ( $lines ) > 0 ) { 00237 $inpoint = array_shift( $lines ); 00238 $outpoint = array_shift( $lines ); 00239 $out .= $this->showline( $inpoint, $outpoint, $namespace ); 00240 } 00241 $out .= Xml::closeElement( 'table' ); 00242 $nsForm = $this->namespaceForm( $namespace, $from, $to ); 00243 00244 # Is there more? 00245 if( $this->including() ) { 00246 $out2 = ''; 00247 } else { 00248 if( isset($from) || isset($to) ) { 00249 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ). 00250 '<tr> 00251 <td>' . 00252 $nsForm . 00253 '</td> 00254 <td class="mw-allpages-nav">' . 00255 Linker::link( $this->getTitle(), $this->msg( 'allpages' )->escaped(), 00256 array(), array(), 'known' ) . 00257 "</td> 00258 </tr>" . 00259 Xml::closeElement( 'table' ); 00260 } else { 00261 $out2 = $nsForm; 00262 } 00263 } 00264 $output->addHTML( $out2 . $out ); 00265 } 00266 00274 function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) { 00275 global $wgContLang; 00276 $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) ); 00277 $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) ); 00278 // Don't let the length runaway 00279 $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength ); 00280 $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength ); 00281 00282 $queryparams = $namespace ? "namespace=$namespace&" : ''; 00283 $special = $this->getTitle(); 00284 $link = htmlspecialchars( $special->getLocalUrl( $queryparams . 'from=' . urlencode($inpoint) . '&to=' . urlencode($outpoint) ) ); 00285 00286 $out = $this->msg( 'alphaindexline' )->rawParams( 00287 "<a href=\"$link\">$inpointf</a></td><td>", 00288 "</td><td><a href=\"$link\">$outpointf</a>" 00289 )->escaped(); 00290 return '<tr><td class="mw-allpages-alphaindexline">' . $out . '</td></tr>'; 00291 } 00292 00298 function showChunk( $namespace = NS_MAIN, $from = false, $to = false ) { 00299 global $wgContLang; 00300 $output = $this->getOutput(); 00301 00302 $fromList = $this->getNamespaceKeyAndText($namespace, $from); 00303 $toList = $this->getNamespaceKeyAndText( $namespace, $to ); 00304 $namespaces = $wgContLang->getNamespaces(); 00305 $n = 0; 00306 00307 if ( !$fromList || !$toList ) { 00308 $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock(); 00309 } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) { 00310 // Show errormessage and reset to NS_MAIN 00311 $out = $this->msg( 'allpages-bad-ns', $namespace )->parse(); 00312 $namespace = NS_MAIN; 00313 } else { 00314 list( $namespace, $fromKey, $from ) = $fromList; 00315 list( , $toKey, $to ) = $toList; 00316 00317 $dbr = wfGetDB( DB_SLAVE ); 00318 $conds = array( 00319 'page_namespace' => $namespace, 00320 'page_title >= ' . $dbr->addQuotes( $fromKey ) 00321 ); 00322 if( $toKey !== "" ) { 00323 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey ); 00324 } 00325 00326 $res = $dbr->select( 'page', 00327 array( 'page_namespace', 'page_title', 'page_is_redirect', 'page_id' ), 00328 $conds, 00329 __METHOD__, 00330 array( 00331 'ORDER BY' => 'page_title', 00332 'LIMIT' => $this->maxPerPage + 1, 00333 'USE INDEX' => 'name_title', 00334 ) 00335 ); 00336 00337 if( $res->numRows() > 0 ) { 00338 $out = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-chunk' ) ); 00339 while( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) { 00340 $t = Title::newFromRow( $s ); 00341 if( $t ) { 00342 $link = ( $s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) . 00343 Linker::link( $t ) . 00344 ($s->page_is_redirect ? '</div>' : '' ); 00345 } else { 00346 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]'; 00347 } 00348 if( $n % 3 == 0 ) { 00349 $out .= '<tr>'; 00350 } 00351 $out .= "<td style=\"width:33%\">$link</td>"; 00352 $n++; 00353 if( $n % 3 == 0 ) { 00354 $out .= "</tr>\n"; 00355 } 00356 } 00357 if( ($n % 3) != 0 ) { 00358 $out .= "</tr>\n"; 00359 } 00360 $out .= Xml::closeElement( 'table' ); 00361 } else { 00362 $out = ''; 00363 } 00364 } 00365 00366 if ( $this->including() ) { 00367 $out2 = ''; 00368 } else { 00369 if( $from == '' ) { 00370 // First chunk; no previous link. 00371 $prevTitle = null; 00372 } else { 00373 # Get the last title from previous chunk 00374 $dbr = wfGetDB( DB_SLAVE ); 00375 $res_prev = $dbr->select( 00376 'page', 00377 'page_title', 00378 array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ), 00379 __METHOD__, 00380 array( 'ORDER BY' => 'page_title DESC', 00381 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) 00382 ) 00383 ); 00384 00385 # Get first title of previous complete chunk 00386 if( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) { 00387 $pt = $dbr->fetchObject( $res_prev ); 00388 $prevTitle = Title::makeTitle( $namespace, $pt->page_title ); 00389 } else { 00390 # The previous chunk is not complete, need to link to the very first title 00391 # available in the database 00392 $options = array( 'LIMIT' => 1 ); 00393 if ( ! $dbr->implicitOrderby() ) { 00394 $options['ORDER BY'] = 'page_title'; 00395 } 00396 $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', 00397 array( 'page_namespace' => $namespace ), __METHOD__, $options ); 00398 # Show the previous link if it s not the current requested chunk 00399 if( $from != $reallyFirstPage_title ) { 00400 $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title ); 00401 } else { 00402 $prevTitle = null; 00403 } 00404 } 00405 } 00406 00407 $self = $this->getTitle(); 00408 00409 $nsForm = $this->namespaceForm( $namespace, $from, $to ); 00410 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ). 00411 '<tr> 00412 <td>' . 00413 $nsForm . 00414 '</td> 00415 <td class="mw-allpages-nav">' . 00416 Linker::link( $self, $this->msg( 'allpages' )->escaped() ); 00417 00418 # Do we put a previous link ? 00419 if( isset( $prevTitle ) && $pt = $prevTitle->getText() ) { 00420 $query = array( 'from' => $prevTitle->getText() ); 00421 00422 if( $namespace ) 00423 $query['namespace'] = $namespace; 00424 00425 $prevLink = Linker::linkKnown( 00426 $self, 00427 $this->msg( 'prevpage', $pt )->escaped(), 00428 array(), 00429 $query 00430 ); 00431 $out2 = $this->getLanguage()->pipeList( array( $out2, $prevLink ) ); 00432 } 00433 00434 if( $n == $this->maxPerPage && $s = $res->fetchObject() ) { 00435 # $s is the first link of the next chunk 00436 $t = Title::MakeTitle($namespace, $s->page_title); 00437 $query = array( 'from' => $t->getText() ); 00438 00439 if( $namespace ) 00440 $query['namespace'] = $namespace; 00441 00442 $nextLink = Linker::linkKnown( 00443 $self, 00444 $this->msg( 'nextpage', $t->getText() )->escaped(), 00445 array(), 00446 $query 00447 ); 00448 $out2 = $this->getLanguage()->pipeList( array( $out2, $nextLink ) ); 00449 } 00450 $out2 .= "</td></tr></table>"; 00451 } 00452 00453 $output->addHTML( $out2 . $out ); 00454 00455 $links = array(); 00456 if ( isset( $prevLink ) ) $links[] = $prevLink; 00457 if ( isset( $nextLink ) ) $links[] = $nextLink; 00458 00459 if ( count( $links ) ) { 00460 $output->addHTML( 00461 Html::element( 'hr' ) . 00462 Html::rawElement( 'div', array( 'class' => 'mw-allpages-nav' ), 00463 $this->getLanguage()->pipeList( $links ) 00464 ) ); 00465 } 00466 00467 } 00468 00474 protected function getNamespaceKeyAndText($ns, $text) { 00475 if ( $text == '' ) 00476 return array( $ns, '', '' ); # shortcut for common case 00477 00478 $t = Title::makeTitleSafe($ns, $text); 00479 if ( $t && $t->isLocal() ) { 00480 return array( $t->getNamespace(), $t->getDBkey(), $t->getText() ); 00481 } elseif ( $t ) { 00482 return null; 00483 } 00484 00485 # try again, in case the problem was an empty pagename 00486 $text = preg_replace('/(#|$)/', 'X$1', $text); 00487 $t = Title::makeTitleSafe($ns, $text); 00488 if ( $t && $t->isLocal() ) { 00489 return array( $t->getNamespace(), '', '' ); 00490 } else { 00491 return null; 00492 } 00493 } 00494 }