r99895 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r99894‎ | r99895 | r99896 >
Date:17:37, 15 October 2011
Author:reedy
Status:ok
Tags:
Comment:
Move CategoryViewer to it's own file
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/CategoryPage.php (modified) (history)
  • /trunk/phase3/includes/CategoryViewer.php (added) (history)

Diff [purge]

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

Status & tagging log