diff --git a/VisualEditor.php b/VisualEditor.php index fcbffe2acc..e3b3398045 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -780,6 +780,7 @@ $wgResourceModules += array( 'visualeditor-dialog-meta-categories-category', 'visualeditor-dialog-meta-categories-data-label', 'visualeditor-dialog-meta-categories-defaultsort-label', + 'visualeditor-dialog-meta-categories-hidden', 'visualeditor-dialog-meta-categories-input-matchingcategorieslabel', 'visualeditor-dialog-meta-categories-input-movecategorylabel', 'visualeditor-dialog-meta-categories-input-newcategorylabel', diff --git a/modules/ve-mw/i18n/en.json b/modules/ve-mw/i18n/en.json index 8ec0d02b5c..847631fc7e 100644 --- a/modules/ve-mw/i18n/en.json +++ b/modules/ve-mw/i18n/en.json @@ -65,6 +65,7 @@ "visualeditor-dialog-meta-categories-category": "Category", "visualeditor-dialog-meta-categories-data-label": "Categories", "visualeditor-dialog-meta-categories-defaultsort-label": "Sort this page by default as", + "visualeditor-dialog-meta-categories-hidden": "This category is set not to show on pages to which it is added.", "visualeditor-dialog-meta-categories-input-matchingcategorieslabel": "Matching categories", "visualeditor-dialog-meta-categories-input-movecategorylabel": "Move this category here", "visualeditor-dialog-meta-categories-input-newcategorylabel": "New category", diff --git a/modules/ve-mw/i18n/qqq.json b/modules/ve-mw/i18n/qqq.json index a5495c5a88..c319c34cf5 100644 --- a/modules/ve-mw/i18n/qqq.json +++ b/modules/ve-mw/i18n/qqq.json @@ -70,6 +70,7 @@ "visualeditor-dialog-meta-categories-category": "Title of popup for editing category options.\n{{Identical|Category}}", "visualeditor-dialog-meta-categories-data-label": "Label for the categories sub-section.\n{{Identical|Category}}", "visualeditor-dialog-meta-categories-defaultsort-label": "Label for field setting the category default sort", + "visualeditor-dialog-meta-categories-hidden": "Text shown on the category popup if the category is hidden", "visualeditor-dialog-meta-categories-input-matchingcategorieslabel": "Label for matching suggested category or categories", "visualeditor-dialog-meta-categories-input-movecategorylabel": "Label for moving a given category or categories to end of list", "visualeditor-dialog-meta-categories-input-newcategorylabel": "Label for a suggested uncreated category", @@ -221,4 +222,4 @@ "visualeditor-wikitext-warning": "Contents of notification displayed when Wikitext has been detected.\n\nRefers to:\n* {{msg-mw|Visualeditor-wikitext-warning-link}}\n* {{msg-mw|Visualeditor-ca-editsource}}", "visualeditor-wikitext-warning-link": "Link to page describing what Wikitext is.\n\nUsed in:\n* {{msg-mw|Visualeditor-wikitext-warning}}.", "visualeditor-wikitext-warning-title": "Title of notification displayed when Wikitext has been detected" -} \ No newline at end of file +} diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js index 292b79aead..44693a5a57 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryItemWidget.js @@ -16,6 +16,7 @@ * @constructor * @param {Object} [config] Configuration options * @cfg {Object} [item] Category item + * @cfg {boolean} [hidden] Whether the category is hidden or not */ ve.ui.MWCategoryItemWidget = function VeUiMWCategoryItemWidget( config ) { // Config intialization @@ -32,6 +33,7 @@ ve.ui.MWCategoryItemWidget = function VeUiMWCategoryItemWidget( config ) { this.value = config.item.value; this.sortKey = config.item.sortKey || ''; this.metaItem = config.item.metaItem; + this.isHidden = config.hidden; this.menuOpen = false; this.$label = this.$( '' ); this.$categoryItem = this.$( '
' ); diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryPopupWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryPopupWidget.js index cef3fce882..3e409332f8 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryPopupWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryPopupWidget.js @@ -52,10 +52,12 @@ ve.ui.MWCategoryPopupWidget = function VeUiMWCategoryPopupWidget ( config ) { this.$title .addClass( 've-ui-mwCategoryPopupWidget-title oo-ui-icon-tag' ) .text( ve.msg( 'visualeditor-dialog-meta-categories-category' ) ); + this.$hiddenStatus = this.$( '
' ); this.$menu .addClass( 've-ui-mwCategoryPopupWidget-content' ) .append( this.$title, + this.$hiddenStatus, this.removeButton.$element.addClass( 've-ui-mwCategoryPopupWidget-removeButton' ), this.$sortKeyForm ); @@ -140,6 +142,11 @@ ve.ui.MWCategoryPopupWidget.prototype.onHide = function () { */ ve.ui.MWCategoryPopupWidget.prototype.loadCategoryIntoPopup = function ( item ) { this.origSortkey = item.sortkey; + if ( item.isHidden ) { + this.$hiddenStatus.text( ve.msg( 'visualeditor-dialog-meta-categories-hidden' ) ); + } else { + this.$hiddenStatus.empty(); + } this.sortKeyInput.$input.val( item.sortKey ); }; diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js index da9353541b..9d4d52e1c8 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryWidget.js @@ -28,6 +28,7 @@ ve.ui.MWCategoryWidget = function VeUiMWCategoryWidget( config ) { // Properties this.categories = {}; + this.categoryHiddenStatus = {}; this.popupState = false; this.savedPopupState = false; this.popup = new ve.ui.MWCategoryPopupWidget( { @@ -89,10 +90,11 @@ OO.mixinClass( ve.ui.MWCategoryWidget, OO.ui.GroupElement ); */ ve.ui.MWCategoryWidget.prototype.onLookupInputKeyDown = function ( e ) { if ( this.input.getValue() !== '' && e.which === 13 ) { - this.emit( - 'newCategory', - this.input.getCategoryItemFromValue( this.input.getValue() ) - ); + var item = this.input.getCategoryItemFromValue( this.input.getValue() ), + categoryWidget = this; + this.queryCategoryHiddenStatus( [item.name] ).done( function () { + categoryWidget.emit( 'newCategory', item ); + } ); this.input.setValue( '' ); } }; @@ -104,7 +106,9 @@ ve.ui.MWCategoryWidget.prototype.onLookupInputKeyDown = function ( e ) { * @param {OO.ui.MenuItemWidget} item Selected item */ ve.ui.MWCategoryWidget.prototype.onLookupMenuItemSelect = function ( item ) { - var value = item && item.getData(); + var categoryItem, + value = item && item.getData(), + categoryWidget = this; if ( value && value !== '' ) { // Remove existing items by value @@ -112,7 +116,11 @@ ve.ui.MWCategoryWidget.prototype.onLookupMenuItemSelect = function ( item ) { this.categories[value].metaItem.remove(); } // Add new item - this.emit( 'newCategory', this.input.getCategoryItemFromValue( value ) ); + categoryItem = this.input.getCategoryItemFromValue( value ); + this.queryCategoryHiddenStatus( [categoryItem.name] ).done( function () { + categoryWidget.emit( 'newCategory', categoryItem ); + } ); + // Reset input this.input.setValue( '' ); } @@ -183,44 +191,87 @@ ve.ui.MWCategoryWidget.prototype.getCategories = function () { return ve.getObjectKeys( this.categories ); }; +/** + * Starts a request to update categoryHiddenStatus for the given titles. + * The returned promise will be resolved with an API result if an API call was made, + * or no arguments if it was unnecessary. + * + * @param {string[]} categoryNames + * @return {jQuery.Promise} + */ +ve.ui.MWCategoryWidget.prototype.queryCategoryHiddenStatus = function ( categoryNames ) { + var categoryWidget = this, categoryNamesToQuery = []; + // Get rid of any we already know the hidden status of. + categoryNamesToQuery = $.grep( categoryNames, function ( categoryTitle ) { + return !Object.prototype.hasOwnProperty.call( categoryWidget.categoryHiddenStatus, categoryTitle ); + } ); + + if ( !categoryNamesToQuery.length ) { + return $.Deferred().resolve().promise(); + } + + /*global mw*/ + return new mw.Api().get( { + action: 'query', + prop: 'pageprops', + titles: categoryNamesToQuery.join( '|' ), + ppprop: 'hiddencat' + } ).then( function ( result ) { + if ( result && result.query && result.query.pages ) { + $.each( result.query.pages, function ( index, pageInfo ) { + var hiddenStatus = !!( pageInfo.pageprops && pageInfo.pageprops.hiddencat !== undefined ); + categoryWidget.categoryHiddenStatus[pageInfo.title] = hiddenStatus; + } ); + } + } ); +}; + /** * Adds category items. * * @method * @param {Object[]} items Items to add * @param {number} [index] Index to insert items after - * @chainable + * @return {jQuery.Promise} */ ve.ui.MWCategoryWidget.prototype.addItems = function ( items, index ) { var i, len, item, categoryItem, categoryItems = [], - existingCategoryItem = null; + existingCategoryItem = null, + categoryNames = $.map( items, function ( item ) { + return item.name; + } ), + categoryWidget = this; - for ( i = 0, len = items.length; i < len; i++ ) { - item = items[i]; + return this.queryCategoryHiddenStatus( categoryNames ).then( function () { + for ( i = 0, len = items.length; i < len; i++ ) { + item = items[i]; - // Create a widget using the item data - categoryItem = new ve.ui.MWCategoryItemWidget( { '$': this.$, 'item': item } ); - categoryItem.connect( this, { - 'savePopupState': 'onSavePopupState', - 'togglePopupMenu': 'onTogglePopupMenu' - } ); + // Create a widget using the item data + categoryItem = new ve.ui.MWCategoryItemWidget( { + '$': categoryWidget.$, + 'item': item, + 'hidden': categoryWidget.categoryHiddenStatus[item.name] + } ); + categoryItem.connect( categoryWidget, { + 'savePopupState': 'onSavePopupState', + 'togglePopupMenu': 'onTogglePopupMenu' + } ); - // Index item by value - this.categories[item.value] = categoryItem; - // Copy sortKey from old item when "moving" - if ( existingCategoryItem ) { - categoryItem.sortKey = existingCategoryItem.sortKey; + // Index item by value + categoryWidget.categories[item.value] = categoryItem; + // Copy sortKey from old item when "moving" + if ( existingCategoryItem ) { + categoryItem.sortKey = existingCategoryItem.sortKey; + } + + categoryItems.push( categoryItem ); } - categoryItems.push( categoryItem ); - } + OO.ui.GroupElement.prototype.addItems.call( categoryWidget, categoryItems, index ); - OO.ui.GroupElement.prototype.addItems.call( this, categoryItems, index ); - - this.fitInput(); - - return this; + categoryWidget.fitInput(); + } ); }; /**