From 9c2e1d804842b93d215d761586bf0ecc6ccd821d Mon Sep 17 00:00:00 2001 From: Krenair Date: Tue, 11 Mar 2014 19:02:16 +0000 Subject: [PATCH] Follow redirects in the category widgets Display the redirects nicely in the UI, etc. Bug: 52414 Change-Id: Ib62144cb90899d0c525d6f08f6b5f1159b7d2a86 --- .../ui/widgets/ve.ui.MWCategoryInputWidget.js | 92 +++++++++++++++++-- .../ui/widgets/ve.ui.MWCategoryItemWidget.js | 3 +- .../ui/widgets/ve.ui.MWCategoryWidget.js | 22 ++++- 3 files changed, 103 insertions(+), 14 deletions(-) diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js index fec682d735..380123f181 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js @@ -38,6 +38,7 @@ ve.ui.MWCategoryInputWidget = function VeUiMWCategoryInputWidget( categoryWidget // Initialization this.$element.addClass( 've-ui-mwCategoryInputWidget' ); this.lookupMenu.$element.addClass( 've-ui-mwCategoryInputWidget-menu' ); + this.categoryRedirects = []; }; /* Inheritance */ @@ -67,7 +68,8 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupRequest = function () { 'action': 'query', 'prop': 'pageprops', 'titles': ( data[1] || [] ).join( '|' ), - 'ppprop': 'hiddencat' + 'ppprop': 'hiddencat', + 'redirects': '' } ); return propsJqXhr; } ).promise( { abort: function () { @@ -86,18 +88,31 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupRequest = function () { * @param {Mixed} data Response from server */ ve.ui.MWCategoryInputWidget.prototype.getLookupCacheItemFromData = function ( data ) { - var categoryWidget = this.categoryWidget, result = {}; + var categoryInputWidget = this, result = {}; if ( data.query && data.query.pages ) { $.each( data.query.pages, function ( pageId, pageInfo ) { var title = mw.Title.newFromText( pageInfo.title ); if ( title ) { result[title.getMainText()] = !!( pageInfo.pageprops && pageInfo.pageprops.hiddencat !== undefined ); - categoryWidget.categoryHiddenStatus[pageInfo.title] = result[title.getMainText()]; + categoryInputWidget.categoryWidget.categoryHiddenStatus[pageInfo.title] = result[title.getMainText()]; } else { mw.log.warning( '"' + pageInfo.title + '" is an invalid title!' ); } } ); } + if ( data.query && data.query.redirects ) { + $.each( data.query.redirects, function ( index, redirectInfo ) { + var foundIdentical = false; + $.each( categoryInputWidget.categoryRedirects, function ( index, existingRedirectInfo ) { + if ( existingRedirectInfo.from === redirectInfo.from && existingRedirectInfo.to === redirectInfo.to ) { + foundIdentical = true; + } + } ); + if ( !foundIdentical ) { + categoryInputWidget.categoryRedirects.push( redirectInfo ); + } + } ); + } return result; }; @@ -119,7 +134,20 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( da category = this.getCategoryItemFromValue( this.value ), existingCategories = this.categoryWidget.getCategories(), matchingCategories = [], - hiddenCategories = []; + hiddenCategories = [], + redirectStorage = {}, + itemTitle, + searchForQueryWithinRedirectInfo = function ( element ) { + return element.lastIndexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText(), 0 ) === 0; + }; + + $.each( this.categoryRedirects, function () { + if ( redirectStorage.hasOwnProperty( this.to ) && redirectStorage[this.to].indexOf( this.from ) === -1 ) { + redirectStorage[this.to].push( this.from ); + } else { + redirectStorage[this.to] = [this.from]; + } + } ); $.each( data, function ( title, hiddenStatus ) { if ( hiddenStatus ) { @@ -140,14 +168,22 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( da existingCategoryItems.push( item ); } } + // Matching categories for ( i = 0, len = matchingCategories.length; i < len; i++ ) { item = matchingCategories[i]; + itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(); if ( ve.indexOf( item, existingCategories ) === -1 && - item.lastIndexOf( category.value, 0 ) === 0 + item.lastIndexOf( category.value, 0 ) === 0 || ( + redirectStorage[itemTitle] !== undefined && + $.grep( redirectStorage[itemTitle], searchForQueryWithinRedirectInfo ).length + ) ) { - if ( item === category.value ) { + if ( ( item === category.value ) || ( + redirectStorage[itemTitle] !== undefined && + redirectStorage[itemTitle].indexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText() ) !== -1 + ) ) { exactMatch = true; } matchingCategoryItems.push( item ); @@ -156,16 +192,24 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( da // Hidden categories for ( i = 0, len = hiddenCategories.length; i < len; i++ ) { item = hiddenCategories[i]; + itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(); if ( ve.indexOf( item, existingCategories ) === -1 && - item.lastIndexOf( category.value, 0 ) === 0 + item.lastIndexOf( category.value, 0 ) === 0 || ( + redirectStorage[itemTitle] !== undefined && + $.grep( redirectStorage[itemTitle], searchForQueryWithinRedirectInfo ).length + ) ) { - if ( item === category.value ) { + if ( ( item === category.value ) || ( + redirectStorage[itemTitle] !== undefined && + redirectStorage[itemTitle].indexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText() ) !== -1 + ) ) { exactMatch = true; } hiddenCategoryItems.push( item ); } } + // New category if ( !exactMatch ) { newCategoryItems.push( category.value ); @@ -196,7 +240,7 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( da ) ); for ( i = 0, len = matchingCategoryItems.length; i < len; i++ ) { item = matchingCategoryItems[i]; - items.push( new OO.ui.MenuItemWidget( item, { '$': menu$, 'label': item } ) ); + items.push( this.getMenuItemWidgetFromCategoryName( item, menu$ ) ); } } if ( hiddenCategoryItems.length ) { @@ -205,13 +249,41 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( da ) ); for ( i = 0, len = hiddenCategoryItems.length; i < len; i++ ) { item = hiddenCategoryItems[i]; - items.push( new OO.ui.MenuItemWidget( item, { '$': menu$, 'label': item } ) ); + items.push( this.getMenuItemWidgetFromCategoryName( item, menu$ ) ); } } return items; }; +/** + * Get a OO.ui.MenuSectionItemWidget object for a given category name. + * Deals with redirects. + * + * @method + * @param {string} item Category name + * @param {jQuery} menu$ Lookup menu jQuery + * @returns {OO.ui.MenuSectionItemWidget} Menu item + */ +ve.ui.MWCategoryInputWidget.prototype.getMenuItemWidgetFromCategoryName = function ( item, menu$ ) { + var itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(), + redirectInfo = $.grep( this.categoryRedirects, function ( redirectInfo ) { + return redirectInfo.to === itemTitle; + } ); + if ( redirectInfo.length ) { + return new OO.ui.MenuItemWidget( item, { + '$': menu$, + 'autoFitLabel': false, + 'label': this.$( '' ) + .text( new mw.Title( redirectInfo[0].from ).getMainText() ) + .append( '
↳ ' ) + .append( $( '' ).text( new mw.Title( item ).getMainText() ) ) + } ); + } else { + return new OO.ui.MenuItemWidget( item, { '$': menu$, 'label': item } ); + } +}; + /** * Get a category item. * diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js index 44693a5a57..9a565cdfc7 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js @@ -17,6 +17,7 @@ * @param {Object} [config] Configuration options * @cfg {Object} [item] Category item * @cfg {boolean} [hidden] Whether the category is hidden or not + * @cfg {string} [redirectTo] The name of the category this category's page redirects to. */ ve.ui.MWCategoryItemWidget = function VeUiMWCategoryItemWidget( config ) { // Config intialization @@ -47,7 +48,7 @@ ve.ui.MWCategoryItemWidget = function VeUiMWCategoryItemWidget( config ) { // Initialization this.$label .addClass( 've-ui-mwCategoryItemWidget-label' ) - .text( this.value ); + .text( config.redirectTo || this.value ); this.$categoryItem .addClass( 've-ui-mwCategoryItemWidget-button' ) .append( this.$label, this.$indicator ); diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js index ab69a2474d..6e47809bd2 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js @@ -29,6 +29,7 @@ ve.ui.MWCategoryWidget = function VeUiMWCategoryWidget( config ) { // Properties this.categories = {}; this.categoryHiddenStatus = {}; + this.categoryRedirects = {}; // Source -> target this.popupState = false; this.savedPopupState = false; this.popup = new ve.ui.MWCategoryPopupWidget( { @@ -215,7 +216,8 @@ ve.ui.MWCategoryWidget.prototype.queryCategoryHiddenStatus = function ( category action: 'query', prop: 'pageprops', titles: categoryNamesToQuery.join( '|' ), - ppprop: 'hiddencat' + ppprop: 'hiddencat', + redirects: '' } ).then( function ( result ) { if ( result && result.query && result.query.pages ) { $.each( result.query.pages, function ( index, pageInfo ) { @@ -223,6 +225,11 @@ ve.ui.MWCategoryWidget.prototype.queryCategoryHiddenStatus = function ( category categoryWidget.categoryHiddenStatus[pageInfo.title] = hiddenStatus; } ); } + if ( result && result.query && result.query.redirects ) { + $.each( result.query.redirects, function ( index, redirectInfo ) { + categoryWidget.categoryRedirects[redirectInfo.from] = redirectInfo.to; + } ); + } } ); }; @@ -244,15 +251,24 @@ ve.ui.MWCategoryWidget.prototype.addItems = function ( items, index ) { categoryWidget = this; return this.queryCategoryHiddenStatus( categoryNames ).then( function () { + var itemTitle, config; for ( i = 0, len = items.length; i < len; i++ ) { item = items[i]; + itemTitle = new mw.Title( item.name, mw.config.get( 'wgNamespaceIds' ).category ).getPrefixedText(); // Create a widget using the item data - categoryItem = new ve.ui.MWCategoryItemWidget( { + config = { '$': categoryWidget.$, 'item': item, 'hidden': categoryWidget.categoryHiddenStatus[item.name] - } ); + }; + if ( Object.prototype.hasOwnProperty.call( categoryWidget.categoryRedirects, itemTitle ) ) { + config.redirectTo = new mw.Title( + categoryWidget.categoryRedirects[itemTitle], + mw.config.get( 'wgNamespaceIds' ).category + ).getMainText(); + } + categoryItem = new ve.ui.MWCategoryItemWidget( config ); categoryItem.connect( categoryWidget, { 'savePopupState': 'onSavePopupState', 'togglePopupMenu': 'onTogglePopupMenu'