r83524 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r83523‎ | r83524 | r83525 >
Date:15:14, 8 March 2011
Author:catrope
Status:ok
Tags:
Comment:
1.17wmf1: Revert r81671 (Back out category collation changes) and followup r81686, so as to reintroduce the category collation changes
Modified paths:
  • /branches/wmf/1.17wmf1/extensions/CategoryTree/CategoryTreeFunctions.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/Collection/Collection.body.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/FlaggedRevs/specialpages/UnreviewedPages_body.php (modified) (history)
  • /branches/wmf/1.17wmf1/extensions/intersection/DynamicPageList.php (modified) (history)
  • /branches/wmf/1.17wmf1/includes/CategoryPage.php (modified) (history)
  • /branches/wmf/1.17wmf1/includes/LinksUpdate.php (modified) (history)
  • /branches/wmf/1.17wmf1/includes/Title.php (modified) (history)
  • /branches/wmf/1.17wmf1/includes/api/ApiQueryCategoryMembers.php (modified) (history)
  • /branches/wmf/1.17wmf1/includes/parser/Parser.php (modified) (history)

Diff [purge]

Index: branches/wmf/1.17wmf1/extensions/FlaggedRevs/specialpages/UnreviewedPages_body.php
@@ -272,6 +272,14 @@
273273 $fields[] = 'cl_sortkey';
274274 $conds['cl_to'] = $this->category;
275275 $conds[] = 'cl_from = page_id';
 276+ # Note: single NS always specified
 277+ if( $this->namespace == NS_FILE ) {
 278+ $conds['cl_type'] = 'file';
 279+ } elseif( $this->namespace == NS_CATEGORY ) {
 280+ $conds['cl_type'] = 'subcat';
 281+ } else {
 282+ $conds['cl_type'] = 'page';
 283+ }
276284 $this->mIndexField = 'cl_sortkey';
277285 $useIndex = array( 'categorylinks' => 'cl_sortkey' );
278286 $groupBy = 'cl_sortkey,cl_from';
Index: branches/wmf/1.17wmf1/extensions/Collection/Collection.body.php
@@ -631,10 +631,10 @@
632632 }
633633 $db = wfGetDB( DB_SLAVE );
634634 $tables = array( 'page', 'categorylinks' );
635 - $fields = array( 'cl_from', 'cl_sortkey', 'page_namespace', 'page_title' );
 635+ $fields = array( 'page_namespace', 'page_title' );
636636 $options = array(
637637 'USE INDEX' => 'cl_sortkey',
638 - 'ORDER BY' => 'cl_sortkey',
 638+ 'ORDER BY' => 'cl_type, cl_sortkey',
639639 'LIMIT' => $limit + 1,
640640 );
641641 $where = array(
Index: branches/wmf/1.17wmf1/extensions/intersection/DynamicPageList.php
@@ -497,7 +497,7 @@
498498 $sSqlSort = 'page_id'; # Since they're never reused and increasing
499499 break;
500500 case 'categorysortkey':
501 - $sSqlSort = 'c1.cl_sortkey';
 501+ $sSqlSort = "c1.cl_type $sSqlOrder, c1.cl_sortkey";
502502 break;
503503 case 'popularity':
504504 $sSqlSort = 'page_counter';
Index: branches/wmf/1.17wmf1/extensions/CategoryTree/CategoryTreeFunctions.php
@@ -427,7 +427,7 @@
428428 'cl_from' );
429429 $where = array();
430430 $joins = array();
431 - $options = array( 'ORDER BY' => 'cl_sortkey', 'LIMIT' => $wgCategoryTreeMaxChildren );
 431+ $options = array( 'ORDER BY' => 'cl_type, cl_sortkey', 'LIMIT' => $wgCategoryTreeMaxChildren );
432432
433433 if ( $inverse ) {
434434 $joins['categorylinks'] = array( 'RIGHT JOIN', 'cl_to = page_title AND page_namespace = ' . NS_CATEGORY );
@@ -443,9 +443,9 @@
444444 $where['page_namespace'] = $namespaces;
445445 } elseif ( $mode != CT_MODE_ALL ) {
446446 if ( $mode == CT_MODE_PAGES ) {
447 - $where = array_merge( $where, array( 'page_namespace != ' . NS_IMAGE ) );
 447+ $where['cl_type'] = array( 'page', 'subcat' );
448448 } else {
449 - $where['page_namespace'] = NS_CATEGORY;
 449+ $where['cl_type'] = 'subcat';
450450 }
451451 }
452452 }
Index: branches/wmf/1.17wmf1/includes/CategoryPage.php
@@ -1,33 +1,41 @@
22 <?php
33 /**
4 - * Special handling for category description pages
5 - * Modelled after ImagePage.php
 4+ * Special handling for category description pages.
 5+ * Modelled after ImagePage.php.
66 *
 7+ * @file
78 */
89
910 if ( !defined( 'MEDIAWIKI' ) )
1011 die( 1 );
1112
1213 /**
 14+ * Special handling for category description pages, showing pages,
 15+ * subcategories and file that belong to the category
1316 */
1417 class CategoryPage extends Article {
 18+ # Subclasses can change this to override the viewer class.
 19+ protected $mCategoryViewerClass = 'CategoryViewer';
 20+
1521 function view() {
1622 global $wgRequest, $wgUser;
1723
1824 $diff = $wgRequest->getVal( 'diff' );
1925 $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
2026
21 - if ( isset( $diff ) && $diffOnly )
22 - return Article::view();
 27+ if ( isset( $diff ) && $diffOnly ) {
 28+ return parent::view();
 29+ }
2330
24 - if ( !wfRunHooks( 'CategoryPageView', array( &$this ) ) )
 31+ if ( !wfRunHooks( 'CategoryPageView', array( &$this ) ) ) {
2532 return;
 33+ }
2634
2735 if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
2836 $this->openShowCategory();
2937 }
3038
31 - Article::view();
 39+ parent::view();
3240
3341 if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
3442 $this->closeShowCategory();
@@ -52,10 +60,14 @@
5361
5462 function closeShowCategory() {
5563 global $wgOut, $wgRequest;
56 - $from = $wgRequest->getVal( 'from' );
57 - $until = $wgRequest->getVal( 'until' );
5864
59 - $viewer = new CategoryViewer( $this->mTitle, $from, $until );
 65+ $from = $until = array();
 66+ foreach ( array( 'page', 'subcat', 'file' ) as $type ) {
 67+ $from[$type] = $wgRequest->getVal( "{$type}from" );
 68+ $until[$type] = $wgRequest->getVal( "{$type}until" );
 69+ }
 70+
 71+ $viewer = new $this->mCategoryViewerClass( $this->mTitle, $from, $until, $wgRequest->getValues() );
6072 $wgOut->addHTML( $viewer->getHTML() );
6173 }
6274 }
@@ -65,27 +77,32 @@
6678 $articles, $articles_start_char,
6779 $children, $children_start_char,
6880 $showGallery, $gallery,
69 - $skin;
70 - /** Category object for this page */
 81+ $imgsNoGalley, $imgsNoGallery_start_char,
 82+ $skin, $collation;
 83+ # Category object for this page
7184 private $cat;
 85+ # The original query array, to be used in generating paging links.
 86+ private $query;
7287
73 - function __construct( $title, $from = '', $until = '' ) {
 88+ function __construct( $title, $from = '', $until = '', $query = array() ) {
7489 global $wgCategoryPagingLimit;
7590 $this->title = $title;
7691 $this->from = $from;
7792 $this->until = $until;
7893 $this->limit = $wgCategoryPagingLimit;
7994 $this->cat = Category::newFromTitle( $title );
 95+ $this->query = $query;
 96+ $this->collation = Collation::singleton();
 97+ unset( $this->query['title'] );
8098 }
8199
82100 /**
83101 * Format the category data list.
84102 *
85103 * @return string HTML output
86 - * @private
87104 */
88 - function getHTML() {
89 - global $wgOut, $wgCategoryMagicGallery, $wgCategoryPagingLimit, $wgContLang;
 105+ public function getHTML() {
 106+ global $wgOut, $wgCategoryMagicGallery, $wgContLang;
90107 wfProfileIn( __METHOD__ );
91108
92109 $this->showGallery = $wgCategoryMagicGallery && !$wgOut->mNoGallery;
@@ -128,6 +145,9 @@
129146 if ( $this->showGallery ) {
130147 $this->gallery = new ImageGallery();
131148 $this->gallery->setHideBadImages();
 149+ } else {
 150+ $this->imgsNoGallery = array();
 151+ $this->imgsNoGallery_start_char = array();
132152 }
133153 }
134154
@@ -142,26 +162,29 @@
143163 /**
144164 * Add a subcategory to the internal lists, using a Category object
145165 */
146 - function addSubcategoryObject( $cat, $sortkey, $pageLength ) {
 166+ function addSubcategoryObject( Category $cat, $sortkey, $pageLength ) {
 167+ // Subcategory; strip the 'Category' namespace from the link text.
147168 $title = $cat->getTitle();
148 - $this->addSubcategory( $title, $sortkey, $pageLength );
 169+
 170+ $link = $this->getSkin()->link( $title, $title->getText() );
 171+ if ( $title->isRedirect() ) {
 172+ // This didn't used to add redirect-in-category, but might
 173+ // as well be consistent with the rest of the sections
 174+ // on a category page.
 175+ $link = '<span class="redirect-in-category">' . $link . '</span>';
 176+ }
 177+ $this->children[] = $link;
 178+
 179+ $this->children_start_char[] =
 180+ $this->getSubcategorySortChar( $cat->getTitle(), $sortkey );
149181 }
150182
151183 /**
152184 * Add a subcategory to the internal lists, using a title object
153185 * @deprecated kept for compatibility, please use addSubcategoryObject instead
154186 */
155 - function addSubcategory( $title, $sortkey, $pageLength ) {
156 - // Subcategory; strip the 'Category' namespace from the link text.
157 - $this->children[] = $this->getSkin()->link(
158 - $title,
159 - null,
160 - array(),
161 - array(),
162 - array( 'known', 'noclasses' )
163 - );
164 -
165 - $this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey );
 187+ function addSubcategory( Title $title, $sortkey, $pageLength ) {
 188+ $this->addSubcategoryObject( Category::newFromTitle( $title ), $sortkey, $pageLength );
166189 }
167190
168191 /**
@@ -175,11 +198,13 @@
176199 global $wgContLang;
177200
178201 if ( $title->getPrefixedText() == $sortkey ) {
179 - $firstChar = $wgContLang->firstChar( $title->getDBkey() );
 202+ $word = $title->getDBkey();
180203 } else {
181 - $firstChar = $wgContLang->firstChar( $sortkey );
 204+ $word = $sortkey;
182205 }
183206
 207+ $firstChar = $this->collation->getFirstLetter( $word );
 208+
184209 return $wgContLang->convert( $firstChar );
185210 }
186211
@@ -187,14 +212,25 @@
188213 * Add a page in the image namespace
189214 */
190215 function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
 216+ global $wgContLang;
191217 if ( $this->showGallery ) {
192 - if ( $this->flip ) {
 218+ $flip = $this->flip['file'];
 219+ if ( $flip ) {
193220 $this->gallery->insert( $title );
194221 } else {
195222 $this->gallery->add( $title );
196223 }
197224 } else {
198 - $this->addPage( $title, $sortkey, $pageLength, $isRedirect );
 225+ $link = $this->getSkin()->link( $title );
 226+ if ( $isRedirect ) {
 227+ // This seems kind of pointless given 'mw-redirect' class,
 228+ // but keeping for back-compatibility with user css.
 229+ $link = '<span class="redirect-in-category">' . $link . '</span>';
 230+ }
 231+ $this->imgsNoGallery[] = $link;
 232+
 233+ $this->imgsNoGallery_start_char[] = $wgContLang->convert(
 234+ $this->collation->getFirstLetter( $sortkey ) );
199235 }
200236 }
201237
@@ -203,74 +239,96 @@
204240 */
205241 function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
206242 global $wgContLang;
207 - $this->articles[] = $isRedirect
208 - ? '<span class="redirect-in-category">' .
209 - $this->getSkin()->link(
210 - $title,
211 - null,
212 - array(),
213 - array(),
214 - array( 'known', 'noclasses' )
215 - ) . '</span>'
216 - : $this->getSkin()->makeSizeLinkObj( $pageLength, $title );
217 - $this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
 243+
 244+ $link = $this->getSkin()->link( $title );
 245+ if ( $isRedirect ) {
 246+ // This seems kind of pointless given 'mw-redirect' class,
 247+ // but keeping for back-compatiability with user css.
 248+ $link = '<span class="redirect-in-category">' . $link . '</span>';
 249+ }
 250+ $this->articles[] = $link;
 251+
 252+ $this->articles_start_char[] = $wgContLang->convert(
 253+ $this->collation->getFirstLetter( $sortkey ) );
218254 }
219255
220256 function finaliseCategoryState() {
221 - if ( $this->flip ) {
 257+ if ( $this->flip['subcat'] ) {
222258 $this->children = array_reverse( $this->children );
223259 $this->children_start_char = array_reverse( $this->children_start_char );
 260+ }
 261+ if ( $this->flip['page'] ) {
224262 $this->articles = array_reverse( $this->articles );
225263 $this->articles_start_char = array_reverse( $this->articles_start_char );
226264 }
 265+ if ( !$this->showGallery && $this->flip['file'] ) {
 266+ $this->imgsNoGallery = array_reverse( $this->imgsNoGallery );
 267+ $this->imgsNoGallery_start_char = array_reverse( $this->imgsNoGallery_start_char );
 268+ }
227269 }
228270
229271 function doCategoryQuery() {
230272 $dbr = wfGetDB( DB_SLAVE, 'category' );
231 - if ( $this->from != '' ) {
232 - $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
233 - $this->flip = false;
234 - } elseif ( $this->until != '' ) {
235 - $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $this->until );
236 - $this->flip = true;
237 - } else {
238 - $pageCondition = '1 = 1';
239 - $this->flip = false;
240 - }
241273
242 - $res = $dbr->select(
243 - array( 'page', 'categorylinks', 'category' ),
244 - array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey',
245 - 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files' ),
246 - array( $pageCondition, 'cl_to' => $this->title->getDBkey() ),
247 - __METHOD__,
248 - array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
249 - 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
250 - 'LIMIT' => $this->limit + 1 ),
251 - array( 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
252 - 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY ) )
 274+ $this->nextPage = array(
 275+ 'page' => null,
 276+ 'subcat' => null,
 277+ 'file' => null,
253278 );
 279+ $this->flip = array( 'page' => false, 'subcat' => false, 'file' => false );
254280
255 - $count = 0;
256 - $this->nextPage = null;
257 -
258 - while ( $x = $dbr->fetchObject ( $res ) ) {
259 - if ( ++$count > $this->limit ) {
260 - // We've reached the one extra which shows that there are
261 - // additional pages to be had. Stop here...
262 - $this->nextPage = $x->cl_sortkey;
263 - break;
 281+ foreach ( array( 'page', 'subcat', 'file' ) as $type ) {
 282+ # Get the sortkeys for start/end, if applicable. Note that if
 283+ # the collation in the database differs from the one
 284+ # set in $wgCategoryCollation, pagination might go totally haywire.
 285+ $extraConds = array( 'cl_type' => $type );
 286+ if ( $this->from[$type] !== null ) {
 287+ $extraConds[] = 'cl_sortkey >= '
 288+ . $dbr->addQuotes( $this->collation->getSortKey( $this->from[$type] ) );
 289+ } elseif ( $this->until[$type] !== null ) {
 290+ $extraConds[] = 'cl_sortkey < '
 291+ . $dbr->addQuotes( $this->collation->getSortKey( $this->until[$type] ) );
 292+ $this->flip[$type] = true;
264293 }
265294
266 - $title = Title::makeTitle( $x->page_namespace, $x->page_title );
 295+ $res = $dbr->select(
 296+ array( 'page', 'categorylinks', 'category' ),
 297+ array( 'page_id', 'page_title', 'page_namespace', 'page_len',
 298+ 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title',
 299+ 'cat_subcats', 'cat_pages', 'cat_files', 'cl_sortkey_prefix' ),
 300+ array( 'cl_to' => $this->title->getDBkey() ) + $extraConds,
 301+ __METHOD__,
 302+ array(
 303+ 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
 304+ 'LIMIT' => $this->limit + 1,
 305+ 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey',
 306+ ),
 307+ array(
 308+ 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
 309+ 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY )
 310+ )
 311+ );
267312
268 - if ( $title->getNamespace() == NS_CATEGORY ) {
269 - $cat = Category::newFromRow( $x, $title );
270 - $this->addSubcategoryObject( $cat, $x->cl_sortkey, $x->page_len );
271 - } elseif ( $this->showGallery && $title->getNamespace() == NS_FILE ) {
272 - $this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
273 - } else {
274 - $this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
 313+ $count = 0;
 314+ foreach ( $res as $row ) {
 315+ $title = Title::newFromRow( $row );
 316+ $rawSortkey = $title->getCategorySortkey( $row->cl_sortkey_prefix );
 317+
 318+ if ( ++$count > $this->limit ) {
 319+ # We've reached the one extra which shows that there
 320+ # are additional pages to be had. Stop here...
 321+ $this->nextPage[$type] = $rawSortkey;
 322+ break;
 323+ }
 324+
 325+ if ( $title->getNamespace() == NS_CATEGORY ) {
 326+ $cat = Category::newFromRow( $row, $title );
 327+ $this->addSubcategoryObject( $cat, $rawSortkey, $row->page_len );
 328+ } elseif ( $title->getNamespace() == NS_FILE ) {
 329+ $this->addImage( $title, $rawSortkey, $row->page_len, $row->page_is_redirect );
 330+ } else {
 331+ $this->addPage( $title, $rawSortkey, $row->page_len, $row->page_is_redirect );
 332+ }
275333 }
276334 }
277335 }
@@ -294,7 +352,9 @@
295353 $r .= "<div id=\"mw-subcategories\">\n";
296354 $r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
297355 $r .= $countmsg;
 356+ $r .= $this->getSectionPagingLinks( 'subcat' );
298357 $r .= $this->formatList( $this->children, $this->children_start_char );
 358+ $r .= $this->getSectionPagingLinks( 'subcat' );
299359 $r .= "\n</div>";
300360 }
301361 return $r;
@@ -318,36 +378,57 @@
319379 $r = "<div id=\"mw-pages\">\n";
320380 $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
321381 $r .= $countmsg;
 382+ $r .= $this->getSectionPagingLinks( 'page' );
322383 $r .= $this->formatList( $this->articles, $this->articles_start_char );
 384+ $r .= $this->getSectionPagingLinks( 'page' );
323385 $r .= "\n</div>";
324386 }
325387 return $r;
326388 }
327389
328390 function getImageSection() {
329 - if ( $this->showGallery && ! $this->gallery->isEmpty() ) {
 391+ $r = '';
 392+ $rescnt = $this->showGallery ? $this->gallery->count() : count( $this->imgsNoGallery );
 393+ if ( $rescnt > 0 ) {
330394 $dbcnt = $this->cat->getFileCount();
331 - $rescnt = $this->gallery->count();
332395 $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' );
333396
334 - return "<div id=\"mw-category-media\">\n" .
335 - '<h2>' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "</h2>\n" .
336 - $countmsg . $this->gallery->toHTML() . "\n</div>";
337 - } else {
338 - return '';
 397+ $r .= "<div id=\"mw-category-media\">\n";
 398+ $r .= '<h2>' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "</h2>\n";
 399+ $r .= $countmsg;
 400+ $r .= $this->getSectionPagingLinks( 'file' );
 401+ if ( $this->showGallery ) {
 402+ $r .= $this->gallery->toHTML();
 403+ } else {
 404+ $r .= $this->formatList( $this->imgsNoGallery, $this->imgsNoGallery_start_char );
 405+ }
 406+ $r .= $this->getSectionPagingLinks( 'file' );
 407+ $r .= "\n</div>";
339408 }
 409+ return $r;
340410 }
341411
342 - function getCategoryBottom() {
343 - if ( $this->until != '' ) {
344 - return $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
345 - } elseif ( $this->nextPage != '' || $this->from != '' ) {
346 - return $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
 412+ /**
 413+ * Get the paging links for a section (subcats/pages/files), to go at the top and bottom
 414+ * of the output.
 415+ *
 416+ * @param $type String: 'page', 'subcat', or 'file'
 417+ * @return String: HTML output, possibly empty if there are no other pages
 418+ */
 419+ private function getSectionPagingLinks( $type ) {
 420+ if ( $this->until[$type] !== null ) {
 421+ return $this->pagingLinks( $this->nextPage[$type], $this->until[$type], $type );
 422+ } elseif ( $this->nextPage[$type] !== null || $this->from[$type] !== null ) {
 423+ return $this->pagingLinks( $this->from[$type], $this->nextPage[$type], $type );
347424 } else {
348425 return '';
349426 }
350427 }
351428
 429+ function getCategoryBottom() {
 430+ return '';
 431+ }
 432+
352433 /**
353434 * Format a list of articles chunked by letter, either as a
354435 * bullet list or a columnar format, depending on the length.
@@ -360,10 +441,10 @@
361442 */
362443 function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
363444 if ( count ( $articles ) > $cutoff ) {
364 - return $this->columnList( $articles, $articles_start_char );
 445+ return self::columnList( $articles, $articles_start_char );
365446 } elseif ( count( $articles ) > 0 ) {
366447 // for short lists of articles in categories.
367 - return $this->shortList( $articles, $articles_start_char );
 448+ return self::shortList( $articles, $articles_start_char );
368449 }
369450 return '';
370451 }
@@ -383,7 +464,7 @@
384465 * @return String
385466 * @private
386467 */
387 - function columnList( $articles, $articles_start_char ) {
 468+ static function columnList( $articles, $articles_start_char ) {
388469 $columns = array_combine( $articles, $articles_start_char );
389470 # Split into three columns
390471 $columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ );
@@ -435,7 +516,7 @@
436517 * @return String
437518 * @private
438519 */
439 - function shortList( $articles, $articles_start_char ) {
 520+ static function shortList( $articles, $articles_start_char ) {
440521 $r = '<h3>' . htmlspecialchars( $articles_start_char[0] ) . "</h3>\n";
441522 $r .= '<ul><li>' . $articles[0] . '</li>';
442523 for ( $index = 1; $index < count( $articles ); $index++ )
@@ -452,26 +533,27 @@
453534 }
454535
455536 /**
456 - * @param $title Title object
457 - * @param $first String
458 - * @param $last String
459 - * @param $limit Int
460 - * @param $query Array: additional query options to pass
461 - * @return String
462 - * @private
 537+ * Create paging links, as a helper method to getSectionPagingLinks().
 538+ *
 539+ * @param $first String The 'until' parameter for the generated URL
 540+ * @param $last String The 'from' parameter for the genererated URL
 541+ * @param $type String A prefix for parameters, 'page' or 'subcat' or
 542+ * 'file'
 543+ * @return String HTML
463544 */
464 - function pagingLinks( $title, $first, $last, $limit, $query = array() ) {
 545+ private function pagingLinks( $first, $last, $type = '' ) {
465546 global $wgLang;
466547 $sk = $this->getSkin();
467 - $limitText = $wgLang->formatNum( $limit );
 548+ $limitText = $wgLang->formatNum( $this->limit );
468549
469550 $prevLink = wfMsgExt( 'prevn', array( 'escape', 'parsemag' ), $limitText );
470551
471552 if ( $first != '' ) {
472 - $prevQuery = $query;
473 - $prevQuery['until'] = $first;
 553+ $prevQuery = $this->query;
 554+ $prevQuery["{$type}until"] = $first;
 555+ unset( $prevQuery["{$type}from"] );
474556 $prevLink = $sk->linkKnown(
475 - $title,
 557+ $this->title,
476558 $prevLink,
477559 array(),
478560 $prevQuery
@@ -481,10 +563,11 @@
482564 $nextLink = wfMsgExt( 'nextn', array( 'escape', 'parsemag' ), $limitText );
483565
484566 if ( $last != '' ) {
485 - $lastQuery = $query;
486 - $lastQuery['from'] = $last;
 567+ $lastQuery = $this->query;
 568+ $lastQuery["{$type}from"] = $last;
 569+ unset( $lastQuery["{$type}until"] );
487570 $nextLink = $sk->linkKnown(
488 - $title,
 571+ $this->title,
489572 $nextLink,
490573 array(),
491574 $lastQuery
@@ -496,8 +579,8 @@
497580
498581 /**
499582 * What to do if the category table conflicts with the number of results
500 - * returned? This function says what. It works the same whether the
501 - * things being counted are articles, subcategories, or files.
 583+ * returned? This function says what. Each type is considered independantly
 584+ * of the other types.
502585 *
503586 * Note for grepping: uses the messages category-article-count,
504587 * category-article-count-limited, category-subcat-count,
@@ -520,15 +603,28 @@
521604 # than $this->limit and there's no offset. In this case we still
522605 # know the right figure.
523606 # 3) We have no idea.
524 - $totalrescnt = count( $this->articles ) + count( $this->children ) +
525 - ( $this->showGallery ? $this->gallery->count() : 0 );
526607
527 - if ( $dbcnt == $rescnt || ( ( $totalrescnt == $this->limit || $this->from
528 - || $this->until ) && $dbcnt > $rescnt ) )
 608+ # Check if there's a "from" or "until" for anything
 609+
 610+ // This is a little ugly, but we seem to use different names
 611+ // for the paging types then for the messages.
 612+ if ( $type === 'article' ) {
 613+ $pagingType = 'page';
 614+ } else {
 615+ $pagingType = $type;
 616+ }
 617+
 618+ $fromOrUntil = false;
 619+ if ( $this->from[$pagingType] !== null || $this->until[$pagingType] !== null ) {
 620+ $fromOrUntil = true;
 621+ }
 622+
 623+ if ( $dbcnt == $rescnt || ( ( $rescnt == $this->limit || $fromOrUntil )
 624+ && $dbcnt > $rescnt ) )
529625 {
530626 # Case 1: seems sane.
531627 $totalcnt = $dbcnt;
532 - } elseif ( $totalrescnt < $this->limit && !$this->from && !$this->until ) {
 628+ } elseif ( $rescnt < $this->limit && !$fromOrUntil ) {
533629 # Case 2: not sane, but salvageable. Use the number of results.
534630 # Since there are fewer than 200, we can also take this opportunity
535631 # to refresh the incorrect category table entry -- which should be
Index: branches/wmf/1.17wmf1/includes/parser/Parser.php
@@ -5001,15 +5001,19 @@
50025002
50035003 /**
50045004 * Accessor for $mDefaultSort
5005 - * Will use the title/prefixed title if none is set
 5005+ * Will use the empty string if none is set.
50065006 *
 5007+ * This value is treated as a prefix, so the
 5008+ * empty string is equivalent to sorting by
 5009+ * page name.
 5010+ *
50075011 * @return string
50085012 */
50095013 public function getDefaultSort() {
50105014 if ( $this->mDefaultSort !== false ) {
50115015 return $this->mDefaultSort;
50125016 } else {
5013 - return $this->mTitle->getCategorySortkey();
 5017+ return '';
50145018 }
50155019 }
50165020
Property changes on: branches/wmf/1.17wmf1/includes/parser/Parser.php
___________________________________________________________________
Modified: svn:mergeinfo
50175021 Merged /trunk/phase3/includes/parser/Parser.php:r81554
Index: branches/wmf/1.17wmf1/includes/api/ApiQueryCategoryMembers.php
@@ -65,28 +65,28 @@
6666 $fld_ids = isset( $prop['ids'] );
6767 $fld_title = isset( $prop['title'] );
6868 $fld_sortkey = isset( $prop['sortkey'] );
 69+ $fld_sortkeyprefix = isset( $prop['sortkeyprefix'] );
6970 $fld_timestamp = isset( $prop['timestamp'] );
 71+ $fld_type = isset( $prop['type'] );
7072
7173 if ( is_null( $resultPageSet ) ) {
72 - $this->addFields( array( 'cl_from', 'cl_sortkey', 'page_namespace', 'page_title' ) );
 74+ $this->addFields( array( 'cl_from', 'page_namespace', 'page_title' ) );
7375 $this->addFieldsIf( 'page_id', $fld_ids );
 76+ $this->addFieldsIf( 'cl_sortkey_prefix', $fld_sortkeyprefix );
 77+ $this->addFieldsIf( 'cl_sortkey', $fld_sortkey );
7478 } else {
7579 $this->addFields( $resultPageSet->getPageTableFields() ); // will include page_ id, ns, title
7680 $this->addFields( array( 'cl_from', 'cl_sortkey' ) );
7781 }
7882
7983 $this->addFieldsIf( 'cl_timestamp', $fld_timestamp || $params['sort'] == 'timestamp' );
 84+ $this->addFieldsIf( 'cl_type', $fld_type );
 85+
8086 $this->addTables( array( 'page', 'categorylinks' ) ); // must be in this order for 'USE INDEX'
81 - // Not needed after bug 10280 is applied to servers
82 - if ( $params['sort'] == 'timestamp' ) {
83 - $this->addOption( 'USE INDEX', 'cl_timestamp' );
84 - } else {
85 - $this->addOption( 'USE INDEX', 'cl_sortkey' );
86 - }
8787
88 - $this->addWhere( 'cl_from=page_id' );
89 - $this->setContinuation( $params['continue'], $params['dir'] );
9088 $this->addWhereFld( 'cl_to', $categoryTitle->getDBkey() );
 89+ $this->addWhereFld( 'cl_type', $params['type'] );
 90+
9191 // Scanning large datasets for rare categories sucks, and I already told
9292 // how to have efficient subcategory access :-) ~~~~ (oh well, domas)
9393 global $wgMiserMode;
@@ -96,18 +96,35 @@
9797 } else {
9898 $this->addWhereFld( 'page_namespace', $params['namespace'] );
9999 }
 100+
 101+ $dir = $params['dir'] == 'asc' ? 'newer' : 'older';
 102+
100103 if ( $params['sort'] == 'timestamp' ) {
101 - $this->addWhereRange( 'cl_timestamp', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), $params['start'], $params['end'] );
 104+ $this->addWhereRange( 'cl_timestamp',
 105+ $dir,
 106+ $params['start'],
 107+ $params['end'] );
 108+
 109+ $this->addOption( 'USE INDEX', 'cl_timestamp' );
102110 } else {
103 - $this->addWhereRange( 'cl_sortkey', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), $params['startsortkey'], $params['endsortkey'] );
104 - $this->addWhereRange( 'cl_from', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), null, null );
 111+ // The below produces ORDER BY cl_type, cl_sortkey, cl_from, possibly with DESC added to each of them
 112+ $this->addWhereRange( 'cl_type', $dir, null, null );
 113+ $this->addWhereRange( 'cl_sortkey',
 114+ $dir,
 115+ $params['startsortkey'],
 116+ $params['endsortkey'] );
 117+ $this->addWhereRange( 'cl_from', $dir, null, null );
 118+ $this->addOption( 'USE INDEX', 'cl_sortkey' );
105119 }
106120
 121+ $this->setContinuation( $params['continue'], $params['dir'] );
 122+
 123+ $this->addWhere( 'cl_from=page_id' );
 124+
107125 $limit = $params['limit'];
108126 $this->addOption( 'LIMIT', $limit + 1 );
109127
110128 $count = 0;
111 - $lastSortKey = null;
112129 $res = $this->select( __METHOD__ );
113130 foreach ( $res as $row ) {
114131 if ( ++ $count > $limit ) {
@@ -116,7 +133,7 @@
117134 if ( $params['sort'] == 'timestamp' ) {
118135 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) );
119136 } else {
120 - $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) );
 137+ $this->setContinueEnumParameter( 'continue', $row->cl_from );
121138 }
122139 break;
123140 }
@@ -141,6 +158,12 @@
142159 if ( $fld_sortkey ) {
143160 $vals['sortkey'] = $row->cl_sortkey;
144161 }
 162+ if ( $fld_sortkeyprefix ) {
 163+ $vals['sortkeyprefix'] = $row->cl_sortkey_prefix;
 164+ }
 165+ if ( $fld_type ) {
 166+ $vals['type'] = $row->cl_type;
 167+ }
145168 if ( $fld_timestamp ) {
146169 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->cl_timestamp );
147170 }
@@ -150,14 +173,13 @@
151174 if ( $params['sort'] == 'timestamp' ) {
152175 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) );
153176 } else {
154 - $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) );
 177+ $this->setContinueEnumParameter( 'continue', $row->cl_from );
155178 }
156179 break;
157180 }
158181 } else {
159182 $resultPageSet->processDbRow( $row );
160183 }
161 - $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
162184 }
163185
164186 if ( is_null( $resultPageSet ) ) {
@@ -166,14 +188,6 @@
167189 }
168190 }
169191
170 - private function getContinueStr( $row, $lastSortKey ) {
171 - $ret = $row->cl_sortkey . '|';
172 - if ( $row->cl_sortkey == $lastSortKey ) { // duplicate sort key, add cl_from
173 - $ret .= $row->cl_from;
174 - }
175 - return $ret;
176 - }
177 -
178192 /**
179193 * Add DB WHERE clause to continue previous query based on 'continue' parameter
180194 */
@@ -182,26 +196,11 @@
183197 return; // This is not a continuation request
184198 }
185199
186 - $pos = strrpos( $continue, '|' );
187 - $sortkey = substr( $continue, 0, $pos );
188 - $fromstr = substr( $continue, $pos + 1 );
189 - $from = intval( $fromstr );
 200+ $encFrom = $this->getDB()->addQuotes( intval( $continue ) );
190201
191 - if ( $from == 0 && strlen( $fromstr ) > 0 ) {
192 - $this->dieUsage( 'Invalid continue param. You should pass the original value returned by the previous query', 'badcontinue' );
193 - }
 202+ $op = ( $dir == 'desc' ? '<=' : '>=' );
194203
195 - $encSortKey = $this->getDB()->addQuotes( $sortkey );
196 - $encFrom = $this->getDB()->addQuotes( $from );
197 -
198 - $op = ( $dir == 'desc' ? '<' : '>' );
199 -
200 - if ( $from != 0 ) {
201 - // Duplicate sort key continue
202 - $this->addWhere( "cl_sortkey$op$encSortKey OR (cl_sortkey=$encSortKey AND cl_from$op=$encFrom)" );
203 - } else {
204 - $this->addWhere( "cl_sortkey$op=$encSortKey" );
205 - }
 204+ $this->addWhere( "cl_from $op $encFrom" );
206205 }
207206
208207 public function getAllowedParams() {
@@ -217,6 +216,8 @@
218217 'ids',
219218 'title',
220219 'sortkey',
 220+ 'sortkeyprefix',
 221+ 'type',
221222 'timestamp',
222223 )
223224 ),
@@ -224,6 +225,15 @@
225226 ApiBase::PARAM_ISMULTI => true,
226227 ApiBase::PARAM_TYPE => 'namespace',
227228 ),
 229+ 'type' => array(
 230+ ApiBase::PARAM_ISMULTI => true,
 231+ ApiBase::PARAM_DFLT => 'page|subcat|file',
 232+ ApiBase::PARAM_TYPE => array(
 233+ 'page',
 234+ 'subcat',
 235+ 'file'
 236+ )
 237+ ),
228238 'continue' => null,
229239 'limit' => array(
230240 ApiBase::PARAM_TYPE => 'limit',
@@ -264,12 +274,15 @@
265275 'title' => 'Which category to enumerate (required). Must include Category: prefix',
266276 'prop' => array(
267277 'What pieces of information to include',
268 - ' ids - Adds the page id',
269 - ' title - Adds the title and namespace id of the page',
270 - ' sortkey - Adds the sortkey used for the category',
271 - ' timestamp - Adds the timestamp of when the page was included',
 278+ ' ids - Adds the page ID',
 279+ ' title - Adds the title and namespace ID of the page',
 280+ ' sortkey - Adds the sortkey used for sorting in the category (may not be human-readble)',
 281+ ' sortkeyprefix - Adds the sortkey prefix used for sorting in the category (human-readable part of the sortkey)',
 282+ ' type - Adds the type that the page has been categorised as (page, subcat or file)',
 283+ ' timestamp - Adds the timestamp of when the page was included',
272284 ),
273285 'namespace' => 'Only include pages in these namespaces',
 286+ 'type' => 'What type of category members to include',
274287 'sort' => 'Property to sort by',
275288 'dir' => 'In which direction to sort',
276289 'start' => "Timestamp to start listing from. Can only be used with {$p}sort=timestamp",
Index: branches/wmf/1.17wmf1/includes/LinksUpdate.php
@@ -72,7 +72,10 @@
7373 # it truncated by DB, and then doesn't get
7474 # matched when comparing existing vs current
7575 # categories, causing bug 25254.
76 - $sortkey = substr( $sortkey, 0, 255 );
 76+ # Also. substr behaves weird when given "".
 77+ if ( $sortkey !== '' ) {
 78+ $sortkey = substr( $sortkey, 0, 255 );
 79+ }
7780 }
7881
7982 $this->mRecursive = $recursive;
@@ -432,20 +435,39 @@
433436 * @private
434437 */
435438 function getCategoryInsertions( $existing = array() ) {
436 - global $wgContLang;
 439+ global $wgContLang, $wgCategoryCollation;
437440 $diffs = array_diff_assoc( $this->mCategories, $existing );
438441 $arr = array();
439 - foreach ( $diffs as $name => $sortkey ) {
 442+ foreach ( $diffs as $name => $prefix ) {
440443 $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
441444 $wgContLang->findVariantLink( $name, $nt, true );
 445+
 446+ if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
 447+ $type = 'subcat';
 448+ } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
 449+ $type = 'file';
 450+ } else {
 451+ $type = 'page';
 452+ }
 453+
 454+ # Treat custom sortkeys as a prefix, so that if multiple
 455+ # things are forced to sort as '*' or something, they'll
 456+ # sort properly in the category rather than in page_id
 457+ # order or such.
 458+ $sortkey = Collation::singleton()->getSortKey(
 459+ $this->mTitle->getCategorySortkey( $prefix ) );
 460+
442461 $arr[] = array(
443462 'cl_from' => $this->mId,
444463 'cl_to' => $name,
445464 'cl_sortkey' => $sortkey,
446 - 'cl_timestamp' => $this->mDb->timestamp()
 465+ 'cl_timestamp' => $this->mDb->timestamp(),
 466+ 'cl_sortkey_prefix' => $prefix,
 467+ 'cl_collation' => $wgCategoryCollation,
 468+ 'cl_type' => $type,
447469 );
448470 }
449 - return $arr;
 471+ return $arr;
450472 }
451473
452474 /**
@@ -665,14 +687,13 @@
666688 * @private
667689 */
668690 function getExistingCategories() {
669 - $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey' ),
 691+ $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
670692 array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
671693 $arr = array();
672 - while ( $row = $this->mDb->fetchObject( $res ) ) {
673 - $arr[$row->cl_to] = $row->cl_sortkey;
 694+ foreach ( $res as $row ) {
 695+ $arr[$row->cl_to] = $row->cl_sortkey_prefix;
674696 }
675 - $this->mDb->freeResult( $res );
676 - return $arr;
 697+ return $arr;
677698 }
678699
679700 /**
Index: branches/wmf/1.17wmf1/includes/Title.php
@@ -3096,27 +3096,22 @@
30973097 }
30983098 $redirid = $this->getArticleID();
30993099
3100 - // Category memberships include a sort key which may be customized.
3101 - // If it's left as the default (the page title), we need to update
3102 - // the sort key to match the new title.
3103 - //
3104 - // Be careful to avoid resetting cl_timestamp, which may disturb
3105 - // time-based lists on some sites.
3106 - //
3107 - // Warning -- if the sort key is *explicitly* set to the old title,
3108 - // we can't actually distinguish it from a default here, and it'll
3109 - // be set to the new title even though it really shouldn't.
3110 - // It'll get corrected on the next edit, but resetting cl_timestamp.
 3100+ // Refresh the sortkey for this row. Be careful to avoid resetting
 3101+ // cl_timestamp, which may disturb time-based lists on some sites.
 3102+ $prefix = $dbw->selectField(
 3103+ 'categorylinks',
 3104+ 'cl_sortkey_prefix',
 3105+ array( 'cl_from' => $pageid ),
 3106+ __METHOD__
 3107+ );
31113108 $dbw->update( 'categorylinks',
31123109 array(
3113 - 'cl_sortkey' => $nt->getPrefixedText(),
 3110+ 'cl_sortkey' => Collation::singleton()->getSortKey(
 3111+ $nt->getCategorySortkey( $prefix ) ),
31143112 'cl_timestamp=cl_timestamp' ),
3115 - array(
3116 - 'cl_from' => $pageid,
3117 - 'cl_sortkey' => $this->getPrefixedText() ),
 3113+ array( 'cl_from' => $pageid ),
31183114 __METHOD__ );
31193115
3120 -
31213116 if ( $protected ) {
31223117 # Protect the redirect title as the title used to be...
31233118 $dbw->insertSelect( 'page_restrictions', 'page_restrictions',

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r81671Back out the changes which depend on the categorylinks schema change, so that...tstarling00:57, 8 February 2011
r81686rm var_dump from r81671tstarling03:23, 8 February 2011

Status & tagging log