Index: trunk/phase3/maintenance/language/messages.inc |
— | — | @@ -117,8 +117,6 @@ |
118 | 118 | 'categoriespagetext', |
119 | 119 | 'special-categories-sort-count', |
120 | 120 | 'special-categories-sort-abc', |
121 | | - 'special-categories-sort-asc', |
122 | | - 'special-categories-sort-desc', |
123 | 121 | 'pagecategories', |
124 | 122 | 'pagecategorieslink', |
125 | 123 | 'category_header', |
— | — | @@ -1135,6 +1133,8 @@ |
1136 | 1134 | 'notargettext', |
1137 | 1135 | 'pager-newer-n', |
1138 | 1136 | 'pager-older-n', |
| 1137 | + 'pager-sort-asc', |
| 1138 | + 'pager-sort-desc', |
1139 | 1139 | ), |
1140 | 1140 | 'booksources' => array( |
1141 | 1141 | 'booksources', |
Index: trunk/phase3/includes/SpecialCategories.php |
— | — | @@ -21,21 +21,6 @@ |
22 | 22 | * @addtogroup Pager |
23 | 23 | */ |
24 | 24 | class CategoryPager extends AlphabeticPager { |
25 | | - private $mOrderType = 'abc'; |
26 | | - |
27 | | - public function __construct() { |
28 | | - parent::__construct(); |
29 | | - if( $this->mRequest->getText( 'order' ) == 'count' ) { |
30 | | - $this->mOrderType = 'count'; |
31 | | - } |
32 | | - if( $this->mRequest->getText( 'direction' ) == 'asc' ) { |
33 | | - $this->mDefaultDirection = false; |
34 | | - } elseif( $this->mRequest->getText( 'direction' ) == 'desc' |
35 | | - || $this->mOrderType == 'count' ) { |
36 | | - $this->mDefaultDirection = true; |
37 | | - } |
38 | | - } |
39 | | - |
40 | 25 | function getQueryInfo() { |
41 | 26 | global $wgRequest; |
42 | 27 | return array( |
— | — | @@ -46,14 +31,17 @@ |
47 | 32 | } |
48 | 33 | |
49 | 34 | function getIndexField() { |
50 | | - # We can't use mOrderType here, since this is called from the parent |
51 | | - # constructor. Hmm. |
52 | | - if( $this->mRequest->getText( 'order' ) == 'count' ) { |
53 | | - return 'cat_pages'; |
54 | | - } else { |
55 | | - return "cat_title"; |
56 | | - } |
| 35 | + return array( 'abc' => 'cat_title', 'count' => 'cat_pages' ); |
57 | 36 | } |
| 37 | + |
| 38 | + protected function getOrderTypeMessages() { |
| 39 | + return array( 'abc' => 'special-categories-sort-abc', |
| 40 | + 'count' => 'special-categories-sort-count' ); |
| 41 | + } |
| 42 | + |
| 43 | + protected function getDefaultDirection() { |
| 44 | + return array( 'abc' => false, 'count' => true ); |
| 45 | + } |
58 | 46 | |
59 | 47 | /* Override getBody to apply LinksBatch on resultset before actually outputting anything. */ |
60 | 48 | public function getBody() { |
— | — | @@ -80,39 +68,6 @@ |
81 | 69 | $wgLang->formatNum( $result->cat_pages ) ); |
82 | 70 | return Xml::tags('li', null, "$titleText ($count)" ) . "\n"; |
83 | 71 | } |
84 | | - |
85 | | - /** Override this to order by count */ |
86 | | - public function getNavigationBar() { |
87 | | - $nav = parent::getNavigationBar() . ' ('; |
88 | | - if( $this->mOrderType == 'abc' ) { |
89 | | - $nav .= $this->makeLink( |
90 | | - wfMsgHTML( 'special-categories-sort-count' ), |
91 | | - array( 'order' => 'count' ) |
92 | | - ); |
93 | | - } else { |
94 | | - $nav .= $this->makeLink( |
95 | | - wfMsgHTML( 'special-categories-sort-abc' ), |
96 | | - array( 'order' => 'abc' ) |
97 | | - ); |
98 | | - } |
99 | | - $nav .= ') ('; |
100 | | - # FIXME, these are stupid query names. "order" and "dir" are already |
101 | | - # used. |
102 | | - if( $this->mDefaultDirection ) { |
103 | | - # Descending |
104 | | - $nav .= $this->makeLink( |
105 | | - wfMsgHTML( 'special-categories-sort-asc' ), |
106 | | - array( 'direction' => 'asc' ) |
107 | | - ); |
108 | | - } else { |
109 | | - $nav .= $this->makeLink( |
110 | | - wfMsgHTML( 'special-categories-sort-desc' ), |
111 | | - array( 'direction' => 'desc' ) |
112 | | - ); |
113 | | - } |
114 | | - $nav .= ')'; |
115 | | - return $nav; |
116 | | - } |
117 | 72 | } |
118 | 73 | |
119 | 74 | |
Index: trunk/phase3/includes/Pager.php |
— | — | @@ -60,19 +60,34 @@ |
61 | 61 | public $mDb; |
62 | 62 | public $mPastTheEndRow; |
63 | 63 | |
| 64 | + /** |
| 65 | + * The index to actually be used for ordering. This is a single string e- |
| 66 | + * ven if multiple orderings are supported. |
| 67 | + */ |
64 | 68 | protected $mIndexField; |
65 | | - |
| 69 | + /** For pages that support multiple types of ordering, which one to use. */ |
| 70 | + protected $mOrderType; |
66 | 71 | /** |
67 | | - * Default query direction. false for ascending, true for descending |
| 72 | + * $mDefaultDirection gives the direction to use when sorting results: |
| 73 | + * false for ascending, true for descending. If $mIsBackwards is set, we |
| 74 | + * start from the opposite end, but we still sort the page itself according |
| 75 | + * to $mDefaultDirection. E.g., if $mDefaultDirection is false but we're |
| 76 | + * going backwards, we'll display the last page of results, but the last |
| 77 | + * result will be at the bottom, not the top. |
| 78 | + * |
| 79 | + * "Default" is really a misnomer here, since now it's possible for the |
| 80 | + * user to directly modify this with query parameters. TODO: rename that |
| 81 | + * to $mDescending, maybe set this to that by reference for compatibility. |
68 | 82 | */ |
69 | | - public $mDefaultDirection = false; |
| 83 | + public $mDefaultDirection; |
| 84 | + public $mIsBackwards; |
70 | 85 | |
71 | 86 | /** |
72 | 87 | * Result object for the query. Warning: seek before use. |
73 | 88 | */ |
74 | 89 | public $mResult; |
75 | 90 | |
76 | | - function __construct() { |
| 91 | + public function __construct() { |
77 | 92 | global $wgRequest, $wgUser; |
78 | 93 | $this->mRequest = $wgRequest; |
79 | 94 | |
— | — | @@ -80,14 +95,44 @@ |
81 | 96 | # arbitrary string to support the widest variety of index types. Be |
82 | 97 | # careful outputting it into HTML! |
83 | 98 | $this->mOffset = $this->mRequest->getText( 'offset' ); |
84 | | - |
| 99 | + |
85 | 100 | # Use consistent behavior for the limit options |
86 | 101 | $this->mDefaultLimit = intval( $wgUser->getOption( 'rclimit' ) ); |
87 | 102 | list( $this->mLimit, /* $offset */ ) = $this->mRequest->getLimitOffset(); |
88 | | - |
| 103 | + |
89 | 104 | $this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' ); |
90 | | - $this->mIndexField = $this->getIndexField(); |
91 | 105 | $this->mDb = wfGetDB( DB_SLAVE ); |
| 106 | + |
| 107 | + $index = $this->getIndexField(); |
| 108 | + $order = $this->mRequest->getVal( 'order' ); |
| 109 | + if( is_array( $index ) && isset( $index[$order] ) ) { |
| 110 | + $this->mOrderType = $order; |
| 111 | + $this->mIndexField = $index[$order]; |
| 112 | + } elseif( is_array( $index ) ) { |
| 113 | + # First element is the default |
| 114 | + reset( $index ); |
| 115 | + list( $this->mOrderType, $this->mIndexField ) = each( $index ); |
| 116 | + } else { |
| 117 | + # $index is not an array |
| 118 | + $this->mOrderType = null; |
| 119 | + $this->mIndexField = $index; |
| 120 | + } |
| 121 | + |
| 122 | + if( !isset( $this->mDefaultDirection ) ) { |
| 123 | + $dir = $this->getDefaultDirection(); |
| 124 | + $this->mDefaultDirection = is_array( $dir ) |
| 125 | + ? $dir[$this->mOrderType] |
| 126 | + : $dir; |
| 127 | + } |
| 128 | + |
| 129 | + # FIXME: Can we figure out a less confusing convention than using a |
| 130 | + # "direction" parameter when we already have "dir" and "order"? |
| 131 | + if( $this->mRequest->getVal( 'direction' ) == 'asc' ) { |
| 132 | + $this->mDefaultDirection = false; |
| 133 | + } |
| 134 | + if( $this->mRequest->getVal( 'direction' ) == 'desc' ) { |
| 135 | + $this->mDefaultDirection = true; |
| 136 | + } |
92 | 137 | } |
93 | 138 | |
94 | 139 | /** |
— | — | @@ -300,6 +345,8 @@ |
301 | 346 | unset( $this->mDefaultQuery['dir'] ); |
302 | 347 | unset( $this->mDefaultQuery['offset'] ); |
303 | 348 | unset( $this->mDefaultQuery['limit'] ); |
| 349 | + unset( $this->mDefaultQuery['direction'] ); |
| 350 | + unset( $this->mDefaultQuery['order'] ); |
304 | 351 | } |
305 | 352 | return $this->mDefaultQuery; |
306 | 353 | } |
— | — | @@ -397,10 +444,32 @@ |
398 | 445 | abstract function getQueryInfo(); |
399 | 446 | |
400 | 447 | /** |
401 | | - * This function should be overridden to return the name of the |
402 | | - * index field. |
| 448 | + * This function should be overridden to return the name of the index fi- |
| 449 | + * eld. If the pager supports multiple orders, it may return an array of |
| 450 | + * 'querykey' => 'indexfield' pairs, so that a request with &count=querykey |
| 451 | + * will use indexfield to sort. In this case, the first returned key is |
| 452 | + * the default. |
403 | 453 | */ |
404 | 454 | abstract function getIndexField(); |
| 455 | + |
| 456 | + /** |
| 457 | + * Return the default sorting direction: false for ascending, true for de- |
| 458 | + * scending. You can also have an associative array of ordertype => dir, |
| 459 | + * if multiple order types are supported. In this case getIndexField() |
| 460 | + * must return an array, and the keys of that must exactly match the keys |
| 461 | + * of this. |
| 462 | + * |
| 463 | + * For backward compatibility, this method's return value will be ignored |
| 464 | + * if $this->mDefaultDirection is already set when the constructor is |
| 465 | + * called, for instance if it's statically initialized. In that case the |
| 466 | + * value of that variable (which must be a boolean) will be used. |
| 467 | + * |
| 468 | + * Note that despite its name, this does not return the value of the now- |
| 469 | + * misnamed $this->mDefaultDirection member variable. That is not a |
| 470 | + * default, it's the actual direction used. This is just the default and |
| 471 | + * can be overridden by user input. |
| 472 | + */ |
| 473 | + protected function getDefaultDirection() { return false; } |
405 | 474 | } |
406 | 475 | |
407 | 476 | |
— | — | @@ -409,8 +478,6 @@ |
410 | 479 | * @addtogroup Pager |
411 | 480 | */ |
412 | 481 | abstract class AlphabeticPager extends IndexPager { |
413 | | - public $mDefaultDirection = false; |
414 | | - |
415 | 482 | function __construct() { |
416 | 483 | parent::__construct(); |
417 | 484 | } |
— | — | @@ -422,20 +489,76 @@ |
423 | 490 | function getNavigationBar() { |
424 | 491 | global $wgLang; |
425 | 492 | |
| 493 | + if( isset( $this->mNavigationBar ) ) { |
| 494 | + return $this->mNavigationBar; |
| 495 | + } |
| 496 | + |
426 | 497 | $linkTexts = array( |
427 | 498 | 'prev' => wfMsgHtml( 'prevn', $wgLang->formatNum( $this->mLimit ) ), |
428 | 499 | 'next' => wfMsgHtml( 'nextn', $wgLang->formatNum($this->mLimit ) ), |
429 | | - 'first' => wfMsgHtml( 'page_first' ), /* Introduced the message */ |
430 | | - 'last' => wfMsgHtml( 'page_last' ) /* Introduced the message */ |
| 500 | + 'first' => wfMsgHtml( 'page_first' ), |
| 501 | + 'last' => wfMsgHtml( 'page_last' ) |
431 | 502 | ); |
432 | 503 | |
433 | 504 | $pagingLinks = $this->getPagingLinks( $linkTexts ); |
434 | 505 | $limitLinks = $this->getLimitLinks(); |
435 | 506 | $limits = implode( ' | ', $limitLinks ); |
436 | 507 | |
437 | | - $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " . wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits); |
| 508 | + $this->mNavigationBar = |
| 509 | + "({$pagingLinks['first']} | {$pagingLinks['last']}) " . |
| 510 | + wfMsgHtml( 'viewprevnext', $pagingLinks['prev'], |
| 511 | + $pagingLinks['next'], $limits ); |
| 512 | + |
| 513 | + # Which direction should the link go? Opposite of the current. |
| 514 | + $dir = $this->mDefaultDirection ? 'asc' : 'desc'; |
| 515 | + $query = array( 'direction' => $dir ); |
| 516 | + if( $this->mOrderType !== null ) { |
| 517 | + $query['order'] = $this->mOrderType; |
| 518 | + } |
| 519 | + # Note for grep: uses pager-sort-asc, pager-sort-desc |
| 520 | + $this->mNavigationBar .= ' (' . $this->makeLink( |
| 521 | + wfMsgHTML( "pager-sort-$dir" ), |
| 522 | + $query |
| 523 | + ) . ')'; |
| 524 | + |
| 525 | + if( !is_array( $this->getIndexField() ) ) { |
| 526 | + # Early return to avoid undue nesting |
| 527 | + return $this->mNavigationBar; |
| 528 | + } |
| 529 | + |
| 530 | + $extra = ''; |
| 531 | + $first = true; |
| 532 | + $msgs = $this->getOrderTypeMessages(); |
| 533 | + foreach( array_keys( $msgs ) as $order ) { |
| 534 | + if( $order == $this->mOrderType ) { |
| 535 | + continue; |
| 536 | + } |
| 537 | + if( !$first ) { |
| 538 | + $extra .= ' | '; |
| 539 | + $first = false; |
| 540 | + } |
| 541 | + $extra .= $this->makeLink( |
| 542 | + wfMsgHTML( $msgs[$order] ), |
| 543 | + array( 'order' => $order ) |
| 544 | + ); |
| 545 | + } |
| 546 | + |
| 547 | + if( $extra !== '' ) { |
| 548 | + $this->mNavigationBar .= " ($extra)"; |
| 549 | + } |
| 550 | + |
438 | 551 | return $this->mNavigationBar; |
| 552 | + } |
439 | 553 | |
| 554 | + /** |
| 555 | + * If this supports multiple order type messages, give the message key for |
| 556 | + * enabling each one in getNavigationBar. The return type is an associa- |
| 557 | + * tive array whose keys must exactly match the keys of the array returned |
| 558 | + * by getIndexField(), and whose values are message keys. |
| 559 | + * @return array |
| 560 | + */ |
| 561 | + protected function getOrderTypeMessages() { |
| 562 | + return null; |
440 | 563 | } |
441 | 564 | } |
442 | 565 | |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -567,8 +567,6 @@ |
568 | 568 | 'categoriespagetext' => 'The following categories contain pages or media.', |
569 | 569 | 'special-categories-sort-count' => 'sort by count', |
570 | 570 | 'special-categories-sort-abc' => 'sort alphabetically', |
571 | | -'special-categories-sort-asc' => 'ascending', |
572 | | -'special-categories-sort-desc' => 'descending', |
573 | 571 | 'pagecategories' => '{{PLURAL:$1|Category|Categories}}', |
574 | 572 | 'pagecategorieslink' => 'Special:Categories', # don't translate or duplicate this message to other languages |
575 | 573 | 'category_header' => 'Pages in category "$1"', |
— | — | @@ -1731,6 +1729,8 @@ |
1732 | 1730 | 'notargettext' => 'You have not specified a target page or user to perform this function on.', |
1733 | 1731 | 'pager-newer-n' => '{{PLURAL:$1|newer 1|newer $1}}', |
1734 | 1732 | 'pager-older-n' => '{{PLURAL:$1|older 1|older $1}}', |
| 1733 | +'pager-sort-asc' => 'ascending', |
| 1734 | +'pager-sort-desc' => 'descending', |
1735 | 1735 | |
1736 | 1736 | # Book sources |
1737 | 1737 | 'booksources' => 'Book sources', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -49,9 +49,11 @@ |
50 | 50 | * Add category table to allow better tracking of category membership counts |
51 | 51 | ** (bug 1212) Give correct membership counts on the pages of large categories |
52 | 52 | ** Use category table for more efficient display of Special:Categories |
53 | | -** Allow sorting by number of members on Special:Categories, and also allow |
54 | | - descending sorts |
| 53 | +** Allow sorting by number of members on Special:Categories |
55 | 54 | * (bug 1459) Search for duplicate files by hash: Special:FileDuplicateSearch |
| 55 | +* Allow the user to choose whether to use a descending or ascending sort in |
| 56 | + AlphabeticPager-based special pages (Categories, Listusers, Protectedpages/ |
| 57 | + titles). |
56 | 58 | |
57 | 59 | === Bug fixes in 1.13 === |
58 | 60 | |