Index: trunk/phase3/includes/CategoryPage.php |
— | — | @@ -51,11 +51,20 @@ |
52 | 52 | } |
53 | 53 | |
54 | 54 | function closeShowCategory() { |
55 | | - global $wgOut, $wgRequest; |
56 | | - $from = $wgRequest->getVal( 'from' ); |
57 | | - $until = $wgRequest->getVal( 'until' ); |
| 55 | + global $wgOut, $wgRequest, $wgExperimentalCategorySort; |
58 | 56 | |
59 | | - $viewer = new CategoryViewer( $this->mTitle, $from, $until ); |
| 57 | + if ( $wgExperimentalCategorySort ) { |
| 58 | + $from = $until = array(); |
| 59 | + foreach ( array( 'page', 'subcat', 'file' ) as $type ) { |
| 60 | + $from[$type] = $wgRequest->getVal( "{$type}from" ); |
| 61 | + $until[$type] = $wgRequest->getVal( "{$type}until" ); |
| 62 | + } |
| 63 | + } else { |
| 64 | + $from = $wgRequest->getVal( 'from' ); |
| 65 | + $until = $wgRequest->getVal( 'until' ); |
| 66 | + } |
| 67 | + |
| 68 | + $viewer = new CategoryViewer( $this->mTitle, $from, $until, $wgRequest->getValues() ); |
60 | 69 | $wgOut->addHTML( $viewer->getHTML() ); |
61 | 70 | } |
62 | 71 | } |
— | — | @@ -66,16 +75,19 @@ |
67 | 76 | $children, $children_start_char, |
68 | 77 | $showGallery, $gallery, |
69 | 78 | $skin; |
70 | | - /** Category object for this page */ |
| 79 | + # Category object for this page |
71 | 80 | private $cat; |
| 81 | + # The original query array, to be used in generating paging links. |
| 82 | + private $query; |
72 | 83 | |
73 | | - function __construct( $title, $from = '', $until = '' ) { |
| 84 | + function __construct( $title, $from = '', $until = '', $query = array() ) { |
74 | 85 | global $wgCategoryPagingLimit; |
75 | 86 | $this->title = $title; |
76 | 87 | $this->from = $from; |
77 | 88 | $this->until = $until; |
78 | 89 | $this->limit = $wgCategoryPagingLimit; |
79 | 90 | $this->cat = Category::newFromTitle( $title ); |
| 91 | + $this->query = $query; |
80 | 92 | } |
81 | 93 | |
82 | 94 | /** |
— | — | @@ -194,7 +206,13 @@ |
195 | 207 | */ |
196 | 208 | function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) { |
197 | 209 | if ( $this->showGallery ) { |
198 | | - if ( $this->flip ) { |
| 210 | + global $wgExperimentalCategorySort; |
| 211 | + if ( $wgExperimentalCategorySort ) { |
| 212 | + $flip = $this->flip['file']; |
| 213 | + } else { |
| 214 | + $flip = $this->flip; |
| 215 | + } |
| 216 | + if ( $flip ) { |
199 | 217 | $this->gallery->insert( $title ); |
200 | 218 | } else { |
201 | 219 | $this->gallery->add( $title ); |
— | — | @@ -228,66 +246,81 @@ |
229 | 247 | } |
230 | 248 | |
231 | 249 | function finaliseCategoryState() { |
232 | | - if ( $this->flip ) { |
| 250 | + global $wgExperimentalCategorySort; |
| 251 | + if ( ( !$wgExperimentalCategorySort && $this->flip ) |
| 252 | + || ( $wgExperimentalCategorySort && $this->flip['subcat'] ) ) { |
233 | 253 | $this->children = array_reverse( $this->children ); |
234 | 254 | $this->children_start_char = array_reverse( $this->children_start_char ); |
| 255 | + } |
| 256 | + if ( ( !$wgExperimentalCategorySort && $this->flip ) |
| 257 | + || ( $wgExperimentalCategorySort && $this->flip['page'] ) ) { |
235 | 258 | $this->articles = array_reverse( $this->articles ); |
236 | 259 | $this->articles_start_char = array_reverse( $this->articles_start_char ); |
237 | 260 | } |
238 | 261 | } |
239 | 262 | |
240 | 263 | function doCategoryQuery() { |
241 | | - global $wgExperimentalCategorySort; |
| 264 | + global $wgExperimentalCategorySort, $wgRequest, $wgContLang; |
242 | 265 | |
243 | 266 | $dbr = wfGetDB( DB_SLAVE, 'category' ); |
244 | | - if ( $this->from != '' ) { |
245 | | - $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from ); |
246 | | - $this->flip = false; |
247 | | - } elseif ( $this->until != '' ) { |
248 | | - $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $this->until ); |
249 | | - $this->flip = true; |
250 | | - } else { |
251 | | - $pageCondition = '1 = 1'; |
252 | | - $this->flip = false; |
253 | | - } |
254 | 267 | |
255 | 268 | $tables = array( 'page', 'categorylinks', 'category' ); |
256 | 269 | $fields = array( 'page_title', 'page_namespace', 'page_len', |
257 | 270 | 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', |
258 | 271 | 'cat_subcats', 'cat_pages', 'cat_files' ); |
259 | 272 | $conds = array( 'cl_to' => $this->title->getDBkey() ); |
260 | | - $opts = array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : |
261 | | - 'cl_sortkey', 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ) ); |
| 273 | + $opts = array( |
| 274 | + 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ), |
| 275 | + 'LIMIT' => $this->limit + 1, |
| 276 | + ); |
262 | 277 | $joins = array( 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ), |
263 | 278 | 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY ) ); |
264 | 279 | |
265 | 280 | if ( $wgExperimentalCategorySort ) { |
266 | 281 | # Copy-pasted from below, but that's okay, because the stuff below |
267 | 282 | # will be deleted when this becomes the default. |
268 | | - $count = 0; |
269 | | - $this->nextPage = null; |
| 283 | + $this->nextPage = array( |
| 284 | + 'page' => null, |
| 285 | + 'subcat' => null, |
| 286 | + 'file' => null, |
| 287 | + ); |
| 288 | + $this->flip = array( 'page' => false, 'subcat' => false, 'file' => false ); |
270 | 289 | |
271 | 290 | foreach ( array( 'page', 'subcat', 'file' ) as $type ) { |
| 291 | + # Get the sortkeys for start/end, if applicable. Note that if |
| 292 | + # the collation in the database differs from the one |
| 293 | + # $wgContLang is using, pagination might go totally haywire. |
| 294 | + $extraConds = array( 'cl_type' => $type ); |
| 295 | + if ( $this->from[$type] !== null ) { |
| 296 | + $extraConds[] = 'cl_sortkey >= ' |
| 297 | + . $dbr->addQuotes( $wgContLang->convertToSortkey( $this->from[$type] ) ); |
| 298 | + } elseif ( $this->until[$type] !== null ) { |
| 299 | + $extraConds[] = 'cl_sortkey < ' |
| 300 | + . $dbr->addQuotes( $wgContLang->convertToSortkey( $this->until[$type] ) ); |
| 301 | + $this->flip[$type] = true; |
| 302 | + } |
| 303 | + |
272 | 304 | $res = $dbr->select( |
273 | 305 | $tables, |
274 | 306 | array_merge( $fields, array( 'cl_sortkey_prefix' ) ), |
275 | | - $conds + array( 'cl_type' => $type ) + ( $type == 'page' ? array( $pageCondition ) : array() ), |
| 307 | + $conds + $extraConds, |
276 | 308 | __METHOD__, |
277 | | - $opts + ( $type == 'page' ? array( 'LIMIT' => $this->limit + 1 ) : array() ), |
| 309 | + $opts + array( 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey' ), |
278 | 310 | $joins |
279 | 311 | ); |
280 | 312 | |
| 313 | + $count = 0; |
281 | 314 | foreach ( $res as $row ) { |
282 | | - if ( $type == 'page' && ++$count > $this->limit ) { |
| 315 | + $title = Title::newFromRow( $row ); |
| 316 | + $rawSortkey = $row->cl_sortkey_prefix . $title->getCategorySortkey(); |
| 317 | + |
| 318 | + if ( ++$count > $this->limit ) { |
283 | 319 | # We've reached the one extra which shows that there |
284 | 320 | # are additional pages to be had. Stop here... |
285 | | - $this->nextPage = $row->cl_sortkey; |
| 321 | + $this->nextPage[$type] = $rawSortkey; |
286 | 322 | break; |
287 | 323 | } |
288 | 324 | |
289 | | - $title = Title::newFromRow( $row ); |
290 | | - $rawSortkey = $row->cl_sortkey_prefix . $title->getCategorySortkey(); |
291 | | - |
292 | 325 | if ( $title->getNamespace() == NS_CATEGORY ) { |
293 | 326 | $cat = Category::newFromRow( $row, $title ); |
294 | 327 | $this->addSubcategoryObject( $cat, $rawSortkey, $row->page_len ); |
— | — | @@ -304,12 +337,23 @@ |
305 | 338 | |
306 | 339 | # Non-$wgExperimentalCategorySort stuff |
307 | 340 | |
| 341 | + if ( $this->from != '' ) { |
| 342 | + $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from ); |
| 343 | + $this->flip = false; |
| 344 | + } elseif ( $this->until != '' ) { |
| 345 | + $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $this->until ); |
| 346 | + $this->flip = true; |
| 347 | + } else { |
| 348 | + $pageCondition = '1 = 1'; |
| 349 | + $this->flip = false; |
| 350 | + } |
| 351 | + |
308 | 352 | $res = $dbr->select( |
309 | 353 | $tables, |
310 | 354 | $fields, |
311 | 355 | $conds + array( $pageCondition ), |
312 | 356 | __METHOD__, |
313 | | - $opts + array( 'LIMIT' => $this->limit + 1 ), |
| 357 | + $opts + array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey' ), |
314 | 358 | $joins |
315 | 359 | ); |
316 | 360 | |
— | — | @@ -356,7 +400,9 @@ |
357 | 401 | $r .= "<div id=\"mw-subcategories\">\n"; |
358 | 402 | $r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n"; |
359 | 403 | $r .= $countmsg; |
| 404 | + $r .= $this->getSectionPagingLinks( 'subcat' ); |
360 | 405 | $r .= $this->formatList( $this->children, $this->children_start_char ); |
| 406 | + $r .= $this->getSectionPagingLinks( 'subcat' ); |
361 | 407 | $r .= "\n</div>"; |
362 | 408 | } |
363 | 409 | return $r; |
— | — | @@ -380,31 +426,63 @@ |
381 | 427 | $r = "<div id=\"mw-pages\">\n"; |
382 | 428 | $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n"; |
383 | 429 | $r .= $countmsg; |
| 430 | + $r .= $this->getSectionPagingLinks( 'page' ); |
384 | 431 | $r .= $this->formatList( $this->articles, $this->articles_start_char ); |
| 432 | + $r .= $this->getSectionPagingLinks( 'page' ); |
385 | 433 | $r .= "\n</div>"; |
386 | 434 | } |
387 | 435 | return $r; |
388 | 436 | } |
389 | 437 | |
390 | 438 | function getImageSection() { |
| 439 | + $r = ''; |
391 | 440 | if ( $this->showGallery && ! $this->gallery->isEmpty() ) { |
392 | 441 | $dbcnt = $this->cat->getFileCount(); |
393 | 442 | $rescnt = $this->gallery->count(); |
394 | 443 | $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' ); |
395 | 444 | |
396 | | - return "<div id=\"mw-category-media\">\n" . |
397 | | - '<h2>' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "</h2>\n" . |
398 | | - $countmsg . $this->gallery->toHTML() . "\n</div>"; |
| 445 | + $r .= "<div id=\"mw-category-media\">\n"; |
| 446 | + $r .= '<h2>' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "</h2>\n"; |
| 447 | + $r .= $countmsg; |
| 448 | + $r .= $this->getSectionPagingLinks( 'file' ); |
| 449 | + $r .= $this->gallery->toHTML(); |
| 450 | + $r .= $this->getSectionPagingLinks( 'file' ); |
| 451 | + $r .= "\n</div>"; |
| 452 | + } |
| 453 | + return $r; |
| 454 | + } |
| 455 | + |
| 456 | + /** |
| 457 | + * Get the paging links for a section (subcats/pages/files), to go at the top and bottom |
| 458 | + * of the output. |
| 459 | + * |
| 460 | + * @param string $type 'page', 'subcat', or 'file' |
| 461 | + * @return string HTML output, possibly empty if there are no other pages |
| 462 | + */ |
| 463 | + private function getSectionPagingLinks( $type ) { |
| 464 | + global $wgExperimentalCategorySort; |
| 465 | + if ( !$wgExperimentalCategorySort ) { |
| 466 | + return ''; |
| 467 | + } |
| 468 | + if ( $this->until[$type] !== null ) { |
| 469 | + return $this->pagingLinks( $this->nextPage[$type], $this->until[$type], $type ); |
| 470 | + } elseif ( $this->nextPage[$type] !== null || $this->from[$type] !== null ) { |
| 471 | + return $this->pagingLinks( $this->from[$type], $this->nextPage[$type], $type ); |
399 | 472 | } else { |
400 | 473 | return ''; |
401 | 474 | } |
402 | 475 | } |
403 | 476 | |
404 | 477 | function getCategoryBottom() { |
| 478 | + global $wgExperimentalCategorySort; |
| 479 | + if ( $wgExperimentalCategorySort ) { |
| 480 | + # We have per-section paging links, no global ones. |
| 481 | + return ''; |
| 482 | + } |
405 | 483 | if ( $this->until != '' ) { |
406 | | - return $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit ); |
| 484 | + return $this->pagingLinks( $this->nextPage, $this->until ); |
407 | 485 | } elseif ( $this->nextPage != '' || $this->from != '' ) { |
408 | | - return $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit ); |
| 486 | + return $this->pagingLinks( $this->from, $this->nextPage ); |
409 | 487 | } else { |
410 | 488 | return ''; |
411 | 489 | } |
— | — | @@ -514,26 +592,27 @@ |
515 | 593 | } |
516 | 594 | |
517 | 595 | /** |
518 | | - * @param $title Title object |
519 | | - * @param $first String |
520 | | - * @param $last String |
521 | | - * @param $limit Int |
522 | | - * @param $query Array: additional query options to pass |
523 | | - * @return String |
524 | | - * @private |
| 596 | + * Create paging links, as a helper method to getSectionPagingLinks(). |
| 597 | + * |
| 598 | + * @param $until String The 'until' parameter for the generated URL |
| 599 | + * @param $from String The 'from' parameter for the genererated URL |
| 600 | + * @param $type String A prefix for parameters, 'page' or 'subcat' or |
| 601 | + * 'file' |
| 602 | + * @return String HTML |
525 | 603 | */ |
526 | | - function pagingLinks( $title, $first, $last, $limit, $query = array() ) { |
| 604 | + private function pagingLinks( $first, $last, $type = '' ) { |
527 | 605 | global $wgLang; |
528 | 606 | $sk = $this->getSkin(); |
529 | | - $limitText = $wgLang->formatNum( $limit ); |
| 607 | + $limitText = $wgLang->formatNum( $this->limit ); |
530 | 608 | |
531 | 609 | $prevLink = wfMsgExt( 'prevn', array( 'escape', 'parsemag' ), $limitText ); |
532 | 610 | |
533 | 611 | if ( $first != '' ) { |
534 | | - $prevQuery = $query; |
535 | | - $prevQuery['until'] = $first; |
| 612 | + $prevQuery = $this->query; |
| 613 | + $prevQuery["{$type}until"] = $first; |
| 614 | + unset( $prevQuery["{$type}from"] ); |
536 | 615 | $prevLink = $sk->linkKnown( |
537 | | - $title, |
| 616 | + $this->title, |
538 | 617 | $prevLink, |
539 | 618 | array(), |
540 | 619 | $prevQuery |
— | — | @@ -543,10 +622,11 @@ |
544 | 623 | $nextLink = wfMsgExt( 'nextn', array( 'escape', 'parsemag' ), $limitText ); |
545 | 624 | |
546 | 625 | if ( $last != '' ) { |
547 | | - $lastQuery = $query; |
548 | | - $lastQuery['from'] = $last; |
| 626 | + $lastQuery = $this->query; |
| 627 | + $lastQuery["{$type}from"] = $last; |
| 628 | + unset( $lastQuery["{$type}until"] ); |
549 | 629 | $nextLink = $sk->linkKnown( |
550 | | - $title, |
| 630 | + $this->title, |
551 | 631 | $nextLink, |
552 | 632 | array(), |
553 | 633 | $lastQuery |