[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * List and paging of category members. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 */ 22 23 class CategoryViewer extends ContextSource { 24 /** @var int */ 25 public $limit; 26 27 /** @var array */ 28 public $from; 29 30 /** @var array */ 31 public $until; 32 33 /** @var string[] */ 34 public $articles; 35 36 /** @var array */ 37 public $articles_start_char; 38 39 /** @var array */ 40 public $children; 41 42 /** @var array */ 43 public $children_start_char; 44 45 /** @var bool */ 46 public $showGallery; 47 48 /** @var array */ 49 public $imgsNoGallery_start_char; 50 51 /** @var array */ 52 public $imgsNoGallery; 53 54 /** @var array */ 55 public $nextPage; 56 57 /** @var array */ 58 protected $prevPage; 59 60 /** @var array */ 61 public $flip; 62 63 /** @var Title */ 64 public $title; 65 66 /** @var Collation */ 67 public $collation; 68 69 /** @var ImageGallery */ 70 public $gallery; 71 72 /** @var Category Category object for this page. */ 73 private $cat; 74 75 /** @var array The original query array, to be used in generating paging links. */ 76 private $query; 77 78 /** 79 * @since 1.19 $context is a second, required parameter 80 * @param Title $title 81 * @param IContextSource $context 82 * @param array $from An array with keys page, subcat, 83 * and file for offset of results of each section (since 1.17) 84 * @param array $until An array with 3 keys for until of each section (since 1.17) 85 * @param array $query 86 */ 87 function __construct( $title, IContextSource $context, $from = array(), 88 $until = array(), $query = array() 89 ) { 90 $this->title = $title; 91 $this->setContext( $context ); 92 $this->from = $from; 93 $this->until = $until; 94 $this->limit = $context->getConfig()->get( 'CategoryPagingLimit' ); 95 $this->cat = Category::newFromTitle( $title ); 96 $this->query = $query; 97 $this->collation = Collation::singleton(); 98 unset( $this->query['title'] ); 99 } 100 101 /** 102 * Format the category data list. 103 * 104 * @return string HTML output 105 */ 106 public function getHTML() { 107 wfProfileIn( __METHOD__ ); 108 109 $this->showGallery = $this->getConfig()->get( 'CategoryMagicGallery' ) 110 && !$this->getOutput()->mNoGallery; 111 112 $this->clearCategoryState(); 113 $this->doCategoryQuery(); 114 $this->finaliseCategoryState(); 115 116 $r = $this->getSubcategorySection() . 117 $this->getPagesSection() . 118 $this->getImageSection(); 119 120 if ( $r == '' ) { 121 // If there is no category content to display, only 122 // show the top part of the navigation links. 123 // @todo FIXME: Cannot be completely suppressed because it 124 // is unknown if 'until' or 'from' makes this 125 // give 0 results. 126 $r = $r . $this->getCategoryTop(); 127 } else { 128 $r = $this->getCategoryTop() . 129 $r . 130 $this->getCategoryBottom(); 131 } 132 133 // Give a proper message if category is empty 134 if ( $r == '' ) { 135 $r = $this->msg( 'category-empty' )->parseAsBlock(); 136 } 137 138 $lang = $this->getLanguage(); 139 $langAttribs = array( 'lang' => $lang->getCode(), 'dir' => $lang->getDir() ); 140 # put a div around the headings which are in the user language 141 $r = Html::openElement( 'div', $langAttribs ) . $r . '</div>'; 142 143 wfProfileOut( __METHOD__ ); 144 return $r; 145 } 146 147 function clearCategoryState() { 148 $this->articles = array(); 149 $this->articles_start_char = array(); 150 $this->children = array(); 151 $this->children_start_char = array(); 152 if ( $this->showGallery ) { 153 // Note that null for mode is taken to mean use default. 154 $mode = $this->getRequest()->getVal( 'gallerymode', null ); 155 try { 156 $this->gallery = ImageGalleryBase::factory( $mode, $this->getContext() ); 157 } catch ( MWException $e ) { 158 // User specified something invalid, fallback to default. 159 $this->gallery = ImageGalleryBase::factory( false, $this->getContext() ); 160 } 161 162 $this->gallery->setHideBadImages(); 163 } else { 164 $this->imgsNoGallery = array(); 165 $this->imgsNoGallery_start_char = array(); 166 } 167 } 168 169 /** 170 * Add a subcategory to the internal lists, using a Category object 171 * @param Category $cat 172 * @param string $sortkey 173 * @param int $pageLength 174 */ 175 function addSubcategoryObject( Category $cat, $sortkey, $pageLength ) { 176 // Subcategory; strip the 'Category' namespace from the link text. 177 $title = $cat->getTitle(); 178 179 $link = Linker::link( $title, htmlspecialchars( $title->getText() ) ); 180 if ( $title->isRedirect() ) { 181 // This didn't used to add redirect-in-category, but might 182 // as well be consistent with the rest of the sections 183 // on a category page. 184 $link = '<span class="redirect-in-category">' . $link . '</span>'; 185 } 186 $this->children[] = $link; 187 188 $this->children_start_char[] = 189 $this->getSubcategorySortChar( $cat->getTitle(), $sortkey ); 190 } 191 192 /** 193 * Get the character to be used for sorting subcategories. 194 * If there's a link from Category:A to Category:B, the sortkey of the resulting 195 * entry in the categorylinks table is Category:A, not A, which it SHOULD be. 196 * Workaround: If sortkey == "Category:".$title, than use $title for sorting, 197 * else use sortkey... 198 * 199 * @param Title $title 200 * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever). 201 * @return string 202 */ 203 function getSubcategorySortChar( $title, $sortkey ) { 204 global $wgContLang; 205 206 if ( $title->getPrefixedText() == $sortkey ) { 207 $word = $title->getDBkey(); 208 } else { 209 $word = $sortkey; 210 } 211 212 $firstChar = $this->collation->getFirstLetter( $word ); 213 214 return $wgContLang->convert( $firstChar ); 215 } 216 217 /** 218 * Add a page in the image namespace 219 * @param Title $title 220 * @param string $sortkey 221 * @param int $pageLength 222 * @param bool $isRedirect 223 */ 224 function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) { 225 global $wgContLang; 226 if ( $this->showGallery ) { 227 $flip = $this->flip['file']; 228 if ( $flip ) { 229 $this->gallery->insert( $title ); 230 } else { 231 $this->gallery->add( $title ); 232 } 233 } else { 234 $link = Linker::link( $title ); 235 if ( $isRedirect ) { 236 // This seems kind of pointless given 'mw-redirect' class, 237 // but keeping for back-compatibility with user css. 238 $link = '<span class="redirect-in-category">' . $link . '</span>'; 239 } 240 $this->imgsNoGallery[] = $link; 241 242 $this->imgsNoGallery_start_char[] = $wgContLang->convert( 243 $this->collation->getFirstLetter( $sortkey ) ); 244 } 245 } 246 247 /** 248 * Add a miscellaneous page 249 * @param Title $title 250 * @param string $sortkey 251 * @param int $pageLength 252 * @param bool $isRedirect 253 */ 254 function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) { 255 global $wgContLang; 256 257 $link = Linker::link( $title ); 258 if ( $isRedirect ) { 259 // This seems kind of pointless given 'mw-redirect' class, 260 // but keeping for back-compatibility with user css. 261 $link = '<span class="redirect-in-category">' . $link . '</span>'; 262 } 263 $this->articles[] = $link; 264 265 $this->articles_start_char[] = $wgContLang->convert( 266 $this->collation->getFirstLetter( $sortkey ) ); 267 } 268 269 function finaliseCategoryState() { 270 if ( $this->flip['subcat'] ) { 271 $this->children = array_reverse( $this->children ); 272 $this->children_start_char = array_reverse( $this->children_start_char ); 273 } 274 if ( $this->flip['page'] ) { 275 $this->articles = array_reverse( $this->articles ); 276 $this->articles_start_char = array_reverse( $this->articles_start_char ); 277 } 278 if ( !$this->showGallery && $this->flip['file'] ) { 279 $this->imgsNoGallery = array_reverse( $this->imgsNoGallery ); 280 $this->imgsNoGallery_start_char = array_reverse( $this->imgsNoGallery_start_char ); 281 } 282 } 283 284 function doCategoryQuery() { 285 $dbr = wfGetDB( DB_SLAVE, 'category' ); 286 287 $this->nextPage = array( 288 'page' => null, 289 'subcat' => null, 290 'file' => null, 291 ); 292 $this->prevPage = array( 293 'page' => null, 294 'subcat' => null, 295 'file' => null, 296 ); 297 298 $this->flip = array( 'page' => false, 'subcat' => false, 'file' => false ); 299 300 foreach ( array( 'page', 'subcat', 'file' ) as $type ) { 301 # Get the sortkeys for start/end, if applicable. Note that if 302 # the collation in the database differs from the one 303 # set in $wgCategoryCollation, pagination might go totally haywire. 304 $extraConds = array( 'cl_type' => $type ); 305 if ( isset( $this->from[$type] ) && $this->from[$type] !== null ) { 306 $extraConds[] = 'cl_sortkey >= ' 307 . $dbr->addQuotes( $this->collation->getSortKey( $this->from[$type] ) ); 308 } elseif ( isset( $this->until[$type] ) && $this->until[$type] !== null ) { 309 $extraConds[] = 'cl_sortkey < ' 310 . $dbr->addQuotes( $this->collation->getSortKey( $this->until[$type] ) ); 311 $this->flip[$type] = true; 312 } 313 314 $res = $dbr->select( 315 array( 'page', 'categorylinks', 'category' ), 316 array( 'page_id', 'page_title', 'page_namespace', 'page_len', 317 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', 318 'cat_subcats', 'cat_pages', 'cat_files', 319 'cl_sortkey_prefix', 'cl_collation' ), 320 array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ), 321 __METHOD__, 322 array( 323 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ), 324 'LIMIT' => $this->limit + 1, 325 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey', 326 ), 327 array( 328 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ), 329 'category' => array( 'LEFT JOIN', array( 330 'cat_title = page_title', 331 'page_namespace' => NS_CATEGORY 332 )) 333 ) 334 ); 335 336 $count = 0; 337 foreach ( $res as $row ) { 338 $title = Title::newFromRow( $row ); 339 if ( $row->cl_collation === '' ) { 340 // Hack to make sure that while updating from 1.16 schema 341 // and db is inconsistent, that the sky doesn't fall. 342 // See r83544. Could perhaps be removed in a couple decades... 343 $humanSortkey = $row->cl_sortkey; 344 } else { 345 $humanSortkey = $title->getCategorySortkey( $row->cl_sortkey_prefix ); 346 } 347 348 if ( ++$count > $this->limit ) { 349 # We've reached the one extra which shows that there 350 # are additional pages to be had. Stop here... 351 $this->nextPage[$type] = $humanSortkey; 352 break; 353 } 354 if ( $count == $this->limit ) { 355 $this->prevPage[$type] = $humanSortkey; 356 } 357 358 if ( $title->getNamespace() == NS_CATEGORY ) { 359 $cat = Category::newFromRow( $row, $title ); 360 $this->addSubcategoryObject( $cat, $humanSortkey, $row->page_len ); 361 } elseif ( $title->getNamespace() == NS_FILE ) { 362 $this->addImage( $title, $humanSortkey, $row->page_len, $row->page_is_redirect ); 363 } else { 364 $this->addPage( $title, $humanSortkey, $row->page_len, $row->page_is_redirect ); 365 } 366 } 367 } 368 } 369 370 /** 371 * @return string 372 */ 373 function getCategoryTop() { 374 $r = $this->getCategoryBottom(); 375 return $r === '' 376 ? $r 377 : "<br style=\"clear:both;\"/>\n" . $r; 378 } 379 380 /** 381 * @return string 382 */ 383 function getSubcategorySection() { 384 # Don't show subcategories section if there are none. 385 $r = ''; 386 $rescnt = count( $this->children ); 387 $dbcnt = $this->cat->getSubcatCount(); 388 $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'subcat' ); 389 390 if ( $rescnt > 0 ) { 391 # Showing subcategories 392 $r .= "<div id=\"mw-subcategories\">\n"; 393 $r .= '<h2>' . $this->msg( 'subcategories' )->text() . "</h2>\n"; 394 $r .= $countmsg; 395 $r .= $this->getSectionPagingLinks( 'subcat' ); 396 $r .= $this->formatList( $this->children, $this->children_start_char ); 397 $r .= $this->getSectionPagingLinks( 'subcat' ); 398 $r .= "\n</div>"; 399 } 400 return $r; 401 } 402 403 /** 404 * @return string 405 */ 406 function getPagesSection() { 407 $ti = wfEscapeWikiText( $this->title->getText() ); 408 # Don't show articles section if there are none. 409 $r = ''; 410 411 # @todo FIXME: Here and in the other two sections: we don't need to bother 412 # with this rigmarole if the entire category contents fit on one page 413 # and have already been retrieved. We can just use $rescnt in that 414 # case and save a query and some logic. 415 $dbcnt = $this->cat->getPageCount() - $this->cat->getSubcatCount() 416 - $this->cat->getFileCount(); 417 $rescnt = count( $this->articles ); 418 $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'article' ); 419 420 if ( $rescnt > 0 ) { 421 $r = "<div id=\"mw-pages\">\n"; 422 $r .= '<h2>' . $this->msg( 'category_header', $ti )->text() . "</h2>\n"; 423 $r .= $countmsg; 424 $r .= $this->getSectionPagingLinks( 'page' ); 425 $r .= $this->formatList( $this->articles, $this->articles_start_char ); 426 $r .= $this->getSectionPagingLinks( 'page' ); 427 $r .= "\n</div>"; 428 } 429 return $r; 430 } 431 432 /** 433 * @return string 434 */ 435 function getImageSection() { 436 $r = ''; 437 $rescnt = $this->showGallery ? $this->gallery->count() : count( $this->imgsNoGallery ); 438 if ( $rescnt > 0 ) { 439 $dbcnt = $this->cat->getFileCount(); 440 $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' ); 441 442 $r .= "<div id=\"mw-category-media\">\n"; 443 $r .= '<h2>' . 444 $this->msg( 445 'category-media-header', 446 wfEscapeWikiText( $this->title->getText() ) 447 )->text() . 448 "</h2>\n"; 449 $r .= $countmsg; 450 $r .= $this->getSectionPagingLinks( 'file' ); 451 if ( $this->showGallery ) { 452 $r .= $this->gallery->toHTML(); 453 } else { 454 $r .= $this->formatList( $this->imgsNoGallery, $this->imgsNoGallery_start_char ); 455 } 456 $r .= $this->getSectionPagingLinks( 'file' ); 457 $r .= "\n</div>"; 458 } 459 return $r; 460 } 461 462 /** 463 * Get the paging links for a section (subcats/pages/files), to go at the top and bottom 464 * of the output. 465 * 466 * @param string $type 'page', 'subcat', or 'file' 467 * @return string HTML output, possibly empty if there are no other pages 468 */ 469 private function getSectionPagingLinks( $type ) { 470 if ( isset( $this->until[$type] ) && $this->until[$type] !== null ) { 471 // The new value for the until parameter should be pointing to the first 472 // result displayed on the page which is the second last result retrieved 473 // from the database.The next link should have a from parameter pointing 474 // to the until parameter of the current page. 475 if ( $this->nextPage[$type] !== null ) { 476 return $this->pagingLinks( $this->prevPage[$type], $this->until[$type], $type ); 477 } else { 478 // If the nextPage variable is null, it means that we have reached the first page 479 // and therefore the previous link should be disabled. 480 return $this->pagingLinks( null, $this->until[$type], $type ); 481 } 482 } elseif ( $this->nextPage[$type] !== null 483 || ( isset( $this->from[$type] ) && $this->from[$type] !== null ) 484 ) { 485 return $this->pagingLinks( $this->from[$type], $this->nextPage[$type], $type ); 486 } else { 487 return ''; 488 } 489 } 490 491 /** 492 * @return string 493 */ 494 function getCategoryBottom() { 495 return ''; 496 } 497 498 /** 499 * Format a list of articles chunked by letter, either as a 500 * bullet list or a columnar format, depending on the length. 501 * 502 * @param array $articles 503 * @param array $articles_start_char 504 * @param int $cutoff 505 * @return string 506 * @private 507 */ 508 function formatList( $articles, $articles_start_char, $cutoff = 6 ) { 509 $list = ''; 510 if ( count( $articles ) > $cutoff ) { 511 $list = self::columnList( $articles, $articles_start_char ); 512 } elseif ( count( $articles ) > 0 ) { 513 // for short lists of articles in categories. 514 $list = self::shortList( $articles, $articles_start_char ); 515 } 516 517 $pageLang = $this->title->getPageLanguage(); 518 $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(), 519 'class' => 'mw-content-' . $pageLang->getDir() ); 520 $list = Html::rawElement( 'div', $attribs, $list ); 521 522 return $list; 523 } 524 525 /** 526 * Format a list of articles chunked by letter in a three-column 527 * list, ordered vertically. 528 * 529 * TODO: Take the headers into account when creating columns, so they're 530 * more visually equal. 531 * 532 * More distant TODO: Scrap this and use CSS columns, whenever IE finally 533 * supports those. 534 * 535 * @param array $articles 536 * @param string[] $articles_start_char 537 * @return string 538 * @private 539 */ 540 static function columnList( $articles, $articles_start_char ) { 541 $columns = array_combine( $articles, $articles_start_char ); 542 # Split into three columns 543 $columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ ); 544 545 $ret = '<table style="width: 100%;"><tr style="vertical-align: top;">'; 546 $prevchar = null; 547 548 foreach ( $columns as $column ) { 549 $ret .= '<td style="width: 33.3%;">'; 550 $colContents = array(); 551 552 # Kind of like array_flip() here, but we keep duplicates in an 553 # array instead of dropping them. 554 foreach ( $column as $article => $char ) { 555 if ( !isset( $colContents[$char] ) ) { 556 $colContents[$char] = array(); 557 } 558 $colContents[$char][] = $article; 559 } 560 561 $first = true; 562 foreach ( $colContents as $char => $articles ) { 563 # Change space to non-breaking space to keep headers aligned 564 $h3char = $char === ' ' ? ' ' : htmlspecialchars( $char ); 565 566 $ret .= '<h3>' . $h3char; 567 if ( $first && $char === $prevchar ) { 568 # We're continuing a previous chunk at the top of a new 569 # column, so add " cont." after the letter. 570 $ret .= ' ' . wfMessage( 'listingcontinuesabbrev' )->escaped(); 571 } 572 $ret .= "</h3>\n"; 573 574 $ret .= '<ul><li>'; 575 $ret .= implode( "</li>\n<li>", $articles ); 576 $ret .= '</li></ul>'; 577 578 $first = false; 579 $prevchar = $char; 580 } 581 582 $ret .= "</td>\n"; 583 } 584 585 $ret .= '</tr></table>'; 586 return $ret; 587 } 588 589 /** 590 * Format a list of articles chunked by letter in a bullet list. 591 * @param array $articles 592 * @param string[] $articles_start_char 593 * @return string 594 * @private 595 */ 596 static function shortList( $articles, $articles_start_char ) { 597 $r = '<h3>' . htmlspecialchars( $articles_start_char[0] ) . "</h3>\n"; 598 $r .= '<ul><li>' . $articles[0] . '</li>'; 599 $articleCount = count( $articles ); 600 for ( $index = 1; $index < $articleCount; $index++ ) { 601 if ( $articles_start_char[$index] != $articles_start_char[$index - 1] ) { 602 $r .= "</ul><h3>" . htmlspecialchars( $articles_start_char[$index] ) . "</h3>\n<ul>"; 603 } 604 605 $r .= "<li>{$articles[$index]}</li>"; 606 } 607 $r .= '</ul>'; 608 return $r; 609 } 610 611 /** 612 * Create paging links, as a helper method to getSectionPagingLinks(). 613 * 614 * @param string $first The 'until' parameter for the generated URL 615 * @param string $last The 'from' parameter for the generated URL 616 * @param string $type A prefix for parameters, 'page' or 'subcat' or 617 * 'file' 618 * @return string HTML 619 */ 620 private function pagingLinks( $first, $last, $type = '' ) { 621 $prevLink = $this->msg( 'prevn' )->numParams( $this->limit )->escaped(); 622 623 if ( $first != '' ) { 624 $prevQuery = $this->query; 625 $prevQuery["{$type}until"] = $first; 626 unset( $prevQuery["{$type}from"] ); 627 $prevLink = Linker::linkKnown( 628 $this->addFragmentToTitle( $this->title, $type ), 629 $prevLink, 630 array(), 631 $prevQuery 632 ); 633 } 634 635 $nextLink = $this->msg( 'nextn' )->numParams( $this->limit )->escaped(); 636 637 if ( $last != '' ) { 638 $lastQuery = $this->query; 639 $lastQuery["{$type}from"] = $last; 640 unset( $lastQuery["{$type}until"] ); 641 $nextLink = Linker::linkKnown( 642 $this->addFragmentToTitle( $this->title, $type ), 643 $nextLink, 644 array(), 645 $lastQuery 646 ); 647 } 648 649 return $this->msg( 'categoryviewer-pagedlinks' )->rawParams( $prevLink, $nextLink )->escaped(); 650 } 651 652 /** 653 * Takes a title, and adds the fragment identifier that 654 * corresponds to the correct segment of the category. 655 * 656 * @param Title $title The title (usually $this->title) 657 * @param string $section Which section 658 * @throws MWException 659 * @return Title 660 */ 661 private function addFragmentToTitle( $title, $section ) { 662 switch ( $section ) { 663 case 'page': 664 $fragment = 'mw-pages'; 665 break; 666 case 'subcat': 667 $fragment = 'mw-subcategories'; 668 break; 669 case 'file': 670 $fragment = 'mw-category-media'; 671 break; 672 default: 673 throw new MWException( __METHOD__ . 674 " Invalid section $section." ); 675 } 676 677 return Title::makeTitle( $title->getNamespace(), 678 $title->getDBkey(), $fragment ); 679 } 680 681 /** 682 * What to do if the category table conflicts with the number of results 683 * returned? This function says what. Each type is considered independently 684 * of the other types. 685 * 686 * @param int $rescnt The number of items returned by our database query. 687 * @param int $dbcnt The number of items according to the category table. 688 * @param string $type 'subcat', 'article', or 'file' 689 * @return string A message giving the number of items, to output to HTML. 690 */ 691 private function getCountMessage( $rescnt, $dbcnt, $type ) { 692 // There are three cases: 693 // 1) The category table figure seems sane. It might be wrong, but 694 // we can't do anything about it if we don't recalculate it on ev- 695 // ery category view. 696 // 2) The category table figure isn't sane, like it's smaller than the 697 // number of actual results, *but* the number of results is less 698 // than $this->limit and there's no offset. In this case we still 699 // know the right figure. 700 // 3) We have no idea. 701 702 // Check if there's a "from" or "until" for anything 703 704 // This is a little ugly, but we seem to use different names 705 // for the paging types then for the messages. 706 if ( $type === 'article' ) { 707 $pagingType = 'page'; 708 } else { 709 $pagingType = $type; 710 } 711 712 $fromOrUntil = false; 713 if ( ( isset( $this->from[$pagingType] ) && $this->from[$pagingType] !== null ) || 714 ( isset( $this->until[$pagingType] ) && $this->until[$pagingType] !== null ) 715 ) { 716 $fromOrUntil = true; 717 } 718 719 if ( $dbcnt == $rescnt || 720 ( ( $rescnt == $this->limit || $fromOrUntil ) && $dbcnt > $rescnt ) 721 ) { 722 // Case 1: seems sane. 723 $totalcnt = $dbcnt; 724 } elseif ( $rescnt < $this->limit && !$fromOrUntil ) { 725 // Case 2: not sane, but salvageable. Use the number of results. 726 // Since there are fewer than 200, we can also take this opportunity 727 // to refresh the incorrect category table entry -- which should be 728 // quick due to the small number of entries. 729 $totalcnt = $rescnt; 730 $category = $this->cat; 731 wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $category ) { 732 $category->refreshCounts(); 733 } ); 734 } else { 735 // Case 3: hopeless. Don't give a total count at all. 736 // Messages: category-subcat-count-limited, category-article-count-limited, 737 // category-file-count-limited 738 return $this->msg( "category-$type-count-limited" )->numParams( $rescnt )->parseAsBlock(); 739 } 740 // Messages: category-subcat-count, category-article-count, category-file-count 741 return $this->msg( "category-$type-count" )->numParams( $rescnt, $totalcnt )->parseAsBlock(); 742 } 743 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |