MediaWiki
REL1_23
|
00001 <?php 00029 class SpecialAllpages extends IncludableSpecialPage { 00030 00036 protected $maxPerPage = 345; 00037 00043 protected $maxLineCount = 100; 00044 00050 protected $maxPageLength = 70; 00051 00060 protected $maxTopLevelPages = 50000; 00061 00067 protected $nsfromMsg = 'allpagesfrom'; 00068 00074 function __construct( $name = 'Allpages' ) { 00075 parent::__construct( $name ); 00076 } 00077 00083 function execute( $par ) { 00084 $request = $this->getRequest(); 00085 $out = $this->getOutput(); 00086 00087 $this->setHeaders(); 00088 $this->outputHeader(); 00089 $out->allowClickjacking(); 00090 00091 # GET values 00092 $from = $request->getVal( 'from', null ); 00093 $to = $request->getVal( 'to', null ); 00094 $namespace = $request->getInt( 'namespace' ); 00095 $hideredirects = $request->getBool( 'hideredirects', false ); 00096 00097 $namespaces = $this->getContext()->getLanguage()->getNamespaces(); 00098 00099 $out->setPageTitle( 00100 ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) ) ? 00101 $this->msg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) : 00102 $this->msg( 'allarticles' ) 00103 ); 00104 $out->addModuleStyles( 'mediawiki.special' ); 00105 00106 if ( $par !== null ) { 00107 $this->showChunk( $namespace, $par, $to, $hideredirects ); 00108 } elseif ( $from !== null && $to === null ) { 00109 $this->showChunk( $namespace, $from, $to, $hideredirects ); 00110 } else { 00111 $this->showToplevel( $namespace, $from, $to, $hideredirects ); 00112 } 00113 } 00114 00124 function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) { 00125 global $wgScript; 00126 $t = $this->getPageTitle(); 00127 00128 $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); 00129 $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); 00130 $out .= Html::hidden( 'title', $t->getPrefixedText() ); 00131 $out .= Xml::openElement( 'fieldset' ); 00132 $out .= Xml::element( 'legend', null, $this->msg( 'allpages' )->text() ); 00133 $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) ); 00134 $out .= "<tr> 00135 <td class='mw-label'>" . 00136 Xml::label( $this->msg( 'allpagesfrom' )->text(), 'nsfrom' ) . 00137 " </td> 00138 <td class='mw-input'>" . 00139 Xml::input( 'from', 30, str_replace( '_', ' ', $from ), array( 'id' => 'nsfrom' ) ) . 00140 " </td> 00141 </tr> 00142 <tr> 00143 <td class='mw-label'>" . 00144 Xml::label( $this->msg( 'allpagesto' )->text(), 'nsto' ) . 00145 " </td> 00146 <td class='mw-input'>" . 00147 Xml::input( 'to', 30, str_replace( '_', ' ', $to ), array( 'id' => 'nsto' ) ) . 00148 " </td> 00149 </tr> 00150 <tr> 00151 <td class='mw-label'>" . 00152 Xml::label( $this->msg( 'namespace' )->text(), 'namespace' ) . 00153 " </td> 00154 <td class='mw-input'>" . 00155 Html::namespaceSelector( 00156 array( 'selected' => $namespace ), 00157 array( 'name' => 'namespace', 'id' => 'namespace' ) 00158 ) . ' ' . 00159 Xml::checkLabel( 00160 $this->msg( 'allpages-hide-redirects' )->text(), 00161 'hideredirects', 00162 'hideredirects', 00163 $hideredirects 00164 ) . ' ' . 00165 Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . 00166 " </td> 00167 </tr>"; 00168 $out .= Xml::closeElement( 'table' ); 00169 $out .= Xml::closeElement( 'fieldset' ); 00170 $out .= Xml::closeElement( 'form' ); 00171 $out .= Xml::closeElement( 'div' ); 00172 00173 return $out; 00174 } 00175 00182 function showToplevel( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) { 00183 $output = $this->getOutput(); 00184 00185 # TODO: Either make this *much* faster or cache the title index points 00186 # in the querycache table. 00187 00188 $dbr = wfGetDB( DB_SLAVE ); 00189 $out = ""; 00190 $where = array( 'page_namespace' => $namespace ); 00191 00192 if ( $hideredirects ) { 00193 $where['page_is_redirect'] = 0; 00194 } 00195 00196 $from = Title::makeTitleSafe( $namespace, $from ); 00197 $to = Title::makeTitleSafe( $namespace, $to ); 00198 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null; 00199 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null; 00200 00201 if ( isset( $from ) ) { 00202 $where[] = 'page_title >= ' . $dbr->addQuotes( $from ); 00203 } 00204 00205 if ( isset( $to ) ) { 00206 $where[] = 'page_title <= ' . $dbr->addQuotes( $to ); 00207 } 00208 00209 global $wgMemc; 00210 $key = wfMemcKey( 'allpages', 'ns', $namespace, sha1( $from ), sha1( $to ) ); 00211 $lines = $wgMemc->get( $key ); 00212 00213 $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ ); 00214 00215 // Don't show a hierarchical list if the number of pages is very large, 00216 // since generating it will cause a lot of scanning 00217 if ( $count > $this->maxTopLevelPages ) { 00218 $this->showChunk( $namespace, $from, $to, $hideredirects ); 00219 00220 return; 00221 } 00222 00223 $maxPerSubpage = intval( $count / $this->maxLineCount ); 00224 $maxPerSubpage = max( $maxPerSubpage, $this->maxPerPage ); 00225 00226 if ( !is_array( $lines ) ) { 00227 $options = array( 'LIMIT' => 1 ); 00228 $options['ORDER BY'] = 'page_title ASC'; 00229 $firstTitle = $dbr->selectField( 'page', 'page_title', $where, __METHOD__, $options ); 00230 $lastTitle = $firstTitle; 00231 # This array is going to hold the page_titles in order. 00232 $lines = array( $firstTitle ); 00233 # If we are going to show n rows, we need n+1 queries to find the relevant titles. 00234 $done = false; 00235 while ( !$done ) { 00236 // Fetch the last title of this chunk and the first of the next 00237 $chunk = ( $lastTitle === false ) 00238 ? array() 00239 : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) ); 00240 $res = $dbr->select( 'page', /* FROM */ 00241 'page_title', /* WHAT */ 00242 array_merge( $where, $chunk ), 00243 __METHOD__, 00244 array( 'LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC' ) 00245 ); 00246 00247 $s = $dbr->fetchObject( $res ); 00248 if ( $s ) { 00249 array_push( $lines, $s->page_title ); 00250 } else { 00251 // Final chunk, but ended prematurely. Go back and find the end. 00252 $endTitle = $dbr->selectField( 'page', 'MAX(page_title)', 00253 array_merge( $where, $chunk ), 00254 __METHOD__ ); 00255 array_push( $lines, $endTitle ); 00256 $done = true; 00257 } 00258 00259 $s = $res->fetchObject(); 00260 if ( $s ) { 00261 array_push( $lines, $s->page_title ); 00262 $lastTitle = $s->page_title; 00263 } else { 00264 // This was a final chunk and ended exactly at the limit. 00265 // Rare but convenient! 00266 $done = true; 00267 } 00268 $res->free(); 00269 } 00270 $wgMemc->add( $key, $lines, 3600 ); 00271 } 00272 00273 // If there are only two or less sections, don't even display them. 00274 // Instead, display the first section directly. 00275 if ( count( $lines ) <= 2 ) { 00276 if ( !empty( $lines ) ) { 00277 $this->showChunk( $namespace, $from, $to, $hideredirects ); 00278 } else { 00279 $output->addHTML( $this->namespaceForm( $namespace, $from, $to, $hideredirects ) ); 00280 } 00281 00282 return; 00283 } 00284 00285 # At this point, $lines should contain an even number of elements. 00286 $out .= Xml::openElement( 'table', array( 'class' => 'allpageslist' ) ); 00287 while ( count( $lines ) > 0 ) { 00288 $inpoint = array_shift( $lines ); 00289 $outpoint = array_shift( $lines ); 00290 $out .= $this->showline( $inpoint, $outpoint, $namespace, $hideredirects ); 00291 } 00292 $out .= Xml::closeElement( 'table' ); 00293 $nsForm = $this->namespaceForm( $namespace, $from, $to, $hideredirects ); 00294 00295 # Is there more? 00296 if ( $this->including() ) { 00297 $out2 = ''; 00298 } else { 00299 if ( isset( $from ) || isset( $to ) ) { 00300 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ) . 00301 '<tr> 00302 <td>' . 00303 $nsForm . 00304 '</td> 00305 <td class="mw-allpages-nav">' . 00306 Linker::link( $this->getPageTitle(), $this->msg( 'allpages' )->escaped(), 00307 array(), array(), 'known' ) . 00308 "</td> 00309 </tr>" . 00310 Xml::closeElement( 'table' ); 00311 } else { 00312 $out2 = $nsForm; 00313 } 00314 } 00315 $output->addHTML( $out2 . $out ); 00316 } 00317 00327 function showline( $inpoint, $outpoint, $namespace = NS_MAIN, $hideRedirects = false ) { 00328 // Use content language since page titles are considered to use content language 00329 global $wgContLang; 00330 00331 $inpointf = str_replace( '_', ' ', $inpoint ); 00332 $outpointf = str_replace( '_', ' ', $outpoint ); 00333 00334 // Don't let the length runaway 00335 $inpointf = $wgContLang->truncate( $inpointf, $this->maxPageLength ); 00336 $outpointf = $wgContLang->truncate( $outpointf, $this->maxPageLength ); 00337 00338 $queryParams = array( 00339 'from' => $inpoint, 00340 'to' => $outpoint, 00341 ); 00342 00343 if ( $namespace ) { 00344 $queryParams['namespace'] = $namespace; 00345 } 00346 if ( $hideRedirects ) { 00347 $queryParams['hideredirects'] = 1; 00348 } 00349 00350 $url = $this->getPageTitle()->getLocalURL( $queryParams ); 00351 $inlink = Html::element( 'a', array( 'href' => $url ), $inpointf ); 00352 $outlink = Html::element( 'a', array( 'href' => $url ), $outpointf ); 00353 00354 $out = $this->msg( 'alphaindexline' )->rawParams( 00355 "$inlink</td><td>", 00356 "</td><td>$outlink" 00357 )->escaped(); 00358 00359 return '<tr><td class="mw-allpages-alphaindexline">' . $out . '</td></tr>'; 00360 } 00361 00368 function showChunk( $namespace = NS_MAIN, $from = false, $to = false, $hideredirects = false ) { 00369 $output = $this->getOutput(); 00370 00371 $fromList = $this->getNamespaceKeyAndText( $namespace, $from ); 00372 $toList = $this->getNamespaceKeyAndText( $namespace, $to ); 00373 $namespaces = $this->getContext()->getLanguage()->getNamespaces(); 00374 $n = 0; 00375 00376 if ( !$fromList || !$toList ) { 00377 $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock(); 00378 } elseif ( !array_key_exists( $namespace, $namespaces ) ) { 00379 // Show errormessage and reset to NS_MAIN 00380 $out = $this->msg( 'allpages-bad-ns', $namespace )->parse(); 00381 $namespace = NS_MAIN; 00382 } else { 00383 list( $namespace, $fromKey, $from ) = $fromList; 00384 list( , $toKey, $to ) = $toList; 00385 00386 $dbr = wfGetDB( DB_SLAVE ); 00387 $conds = array( 00388 'page_namespace' => $namespace, 00389 'page_title >= ' . $dbr->addQuotes( $fromKey ) 00390 ); 00391 00392 if ( $hideredirects ) { 00393 $conds['page_is_redirect'] = 0; 00394 } 00395 00396 if ( $toKey !== "" ) { 00397 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey ); 00398 } 00399 00400 $res = $dbr->select( 'page', 00401 array( 'page_namespace', 'page_title', 'page_is_redirect', 'page_id' ), 00402 $conds, 00403 __METHOD__, 00404 array( 00405 'ORDER BY' => 'page_title', 00406 'LIMIT' => $this->maxPerPage + 1, 00407 'USE INDEX' => 'name_title', 00408 ) 00409 ); 00410 00411 if ( $res->numRows() > 0 ) { 00412 $out = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-chunk' ) ); 00413 while ( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) { 00414 $t = Title::newFromRow( $s ); 00415 if ( $t ) { 00416 $link = ( $s->page_is_redirect ? '<div class="allpagesredirect">' : '' ) . 00417 Linker::link( $t ) . 00418 ( $s->page_is_redirect ? '</div>' : '' ); 00419 } else { 00420 $link = '[[' . htmlspecialchars( $s->page_title ) . ']]'; 00421 } 00422 00423 if ( $n % 3 == 0 ) { 00424 $out .= '<tr>'; 00425 } 00426 00427 $out .= "<td style=\"width:33%\">$link</td>"; 00428 $n++; 00429 if ( $n % 3 == 0 ) { 00430 $out .= "</tr>\n"; 00431 } 00432 } 00433 00434 if ( ( $n % 3 ) != 0 ) { 00435 $out .= "</tr>\n"; 00436 } 00437 $out .= Xml::closeElement( 'table' ); 00438 } else { 00439 $out = ''; 00440 } 00441 } 00442 00443 if ( $this->including() ) { 00444 $out2 = ''; 00445 } else { 00446 if ( $from == '' ) { 00447 // First chunk; no previous link. 00448 $prevTitle = null; 00449 } else { 00450 # Get the last title from previous chunk 00451 $dbr = wfGetDB( DB_SLAVE ); 00452 $res_prev = $dbr->select( 00453 'page', 00454 'page_title', 00455 array( 'page_namespace' => $namespace, 'page_title < ' . $dbr->addQuotes( $from ) ), 00456 __METHOD__, 00457 array( 'ORDER BY' => 'page_title DESC', 00458 'LIMIT' => $this->maxPerPage, 'OFFSET' => ( $this->maxPerPage - 1 ) 00459 ) 00460 ); 00461 00462 # Get first title of previous complete chunk 00463 if ( $dbr->numrows( $res_prev ) >= $this->maxPerPage ) { 00464 $pt = $dbr->fetchObject( $res_prev ); 00465 $prevTitle = Title::makeTitle( $namespace, $pt->page_title ); 00466 } else { 00467 # The previous chunk is not complete, need to link to the very first title 00468 # available in the database 00469 $options = array( 'LIMIT' => 1 ); 00470 if ( !$dbr->implicitOrderby() ) { 00471 $options['ORDER BY'] = 'page_title'; 00472 } 00473 $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', 00474 array( 'page_namespace' => $namespace ), __METHOD__, $options ); 00475 # Show the previous link if it s not the current requested chunk 00476 if ( $from != $reallyFirstPage_title ) { 00477 $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title ); 00478 } else { 00479 $prevTitle = null; 00480 } 00481 } 00482 } 00483 00484 $self = $this->getPageTitle(); 00485 00486 $nsForm = $this->namespaceForm( $namespace, $from, $to, $hideredirects ); 00487 $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ) . 00488 '<tr> 00489 <td>' . 00490 $nsForm . 00491 '</td> 00492 <td class="mw-allpages-nav">' . 00493 Linker::link( $self, $this->msg( 'allpages' )->escaped() ); 00494 00495 # Do we put a previous link ? 00496 if ( isset( $prevTitle ) && $pt = $prevTitle->getText() ) { 00497 $query = array( 'from' => $prevTitle->getText() ); 00498 00499 if ( $namespace ) { 00500 $query['namespace'] = $namespace; 00501 } 00502 00503 if ( $hideredirects ) { 00504 $query['hideredirects'] = $hideredirects; 00505 } 00506 00507 $prevLink = Linker::linkKnown( 00508 $self, 00509 $this->msg( 'prevpage', $pt )->escaped(), 00510 array(), 00511 $query 00512 ); 00513 $out2 = $this->getLanguage()->pipeList( array( $out2, $prevLink ) ); 00514 } 00515 00516 if ( $n == $this->maxPerPage && $s = $res->fetchObject() ) { 00517 # $s is the first link of the next chunk 00518 $t = Title::makeTitle( $namespace, $s->page_title ); 00519 $query = array( 'from' => $t->getText() ); 00520 00521 if ( $namespace ) { 00522 $query['namespace'] = $namespace; 00523 } 00524 00525 if ( $hideredirects ) { 00526 $query['hideredirects'] = $hideredirects; 00527 } 00528 00529 $nextLink = Linker::linkKnown( 00530 $self, 00531 $this->msg( 'nextpage', $t->getText() )->escaped(), 00532 array(), 00533 $query 00534 ); 00535 $out2 = $this->getLanguage()->pipeList( array( $out2, $nextLink ) ); 00536 } 00537 $out2 .= "</td></tr></table>"; 00538 } 00539 00540 $output->addHTML( $out2 . $out ); 00541 00542 $links = array(); 00543 if ( isset( $prevLink ) ) { 00544 $links[] = $prevLink; 00545 } 00546 00547 if ( isset( $nextLink ) ) { 00548 $links[] = $nextLink; 00549 } 00550 00551 if ( count( $links ) ) { 00552 $output->addHTML( 00553 Html::element( 'hr' ) . 00554 Html::rawElement( 'div', array( 'class' => 'mw-allpages-nav' ), 00555 $this->getLanguage()->pipeList( $links ) 00556 ) 00557 ); 00558 } 00559 } 00560 00566 protected function getNamespaceKeyAndText( $ns, $text ) { 00567 if ( $text == '' ) { 00568 # shortcut for common case 00569 return array( $ns, '', '' ); 00570 } 00571 00572 $t = Title::makeTitleSafe( $ns, $text ); 00573 if ( $t && $t->isLocal() ) { 00574 return array( $t->getNamespace(), $t->getDBkey(), $t->getText() ); 00575 } elseif ( $t ) { 00576 return null; 00577 } 00578 00579 # try again, in case the problem was an empty pagename 00580 $text = preg_replace( '/(#|$)/', 'X$1', $text ); 00581 $t = Title::makeTitleSafe( $ns, $text ); 00582 if ( $t && $t->isLocal() ) { 00583 return array( $t->getNamespace(), '', '' ); 00584 } else { 00585 return null; 00586 } 00587 } 00588 00589 protected function getGroupName() { 00590 return 'pages'; 00591 } 00592 }