[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> CategoryViewer.php (source)

   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 === ' ' ? '&#160;' : 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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1