Santhosh has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/333894 )
Change subject: WIP: Category adaptation and display ...................................................................... WIP: Category adaptation and display TODO: Listing of categories at end of columns. Bug: T152586 Change-Id: Icfc26460a2cb83301c5788614b5cc0e44a5e5ea0 --- M extension.json A modules/cache/mw.cx.CategoryCache.js M modules/dm/mw.cx.dm.SourcePage.js M modules/dm/mw.cx.dm.TargetPage.js M modules/dm/mw.cx.dm.Translation.js M modules/mw.cx.MwApiRequestManager.js A modules/ui/mw.cx.ui.Categories.js M modules/ui/mw.cx.ui.Columns.js M modules/ui/mw.cx.ui.SourceColumn.js M modules/ui/mw.cx.ui.ToolsColumn.js M modules/ui/mw.cx.ui.TranslationColumn.js M modules/ui/mw.cx.ui.TranslationView.js 12 files changed, 259 insertions(+), 65 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ContentTranslation refs/changes/94/333894/1 diff --git a/extension.json b/extension.json index c61ee27..6b86ced 100644 --- a/extension.json +++ b/extension.json @@ -1293,6 +1293,7 @@ "mw.cx.cache": { "scripts": [ "cache/mw.cx.ApiResponseCache.js", + "cache/mw.cx.CategoryCache.js", "cache/mw.cx.LinkCache.js", "cache/mw.cx.ImageInfoCache.js", "cache/mw.cx.TitlePairCache.js" @@ -1445,7 +1446,8 @@ ], "dependencies": [ "mw.cx.ui", - "ext.cx.widgets.spinner" + "ext.cx.widgets.spinner", + "mw.cx.ui.Categories" ] }, "mw.cx.ui.SourceColumn.legacy": { @@ -1520,6 +1522,17 @@ "mw.cx.tools.SearchTool" ] }, + "mw.cx.ui.Categories": { + "scripts": [ + "ui/mw.cx.ui.Categories.js" + ], + "dependencies": [ + "mw.cx.ui" + ], + "messages": [ + "cx-tools-categories-count-message" + ] + }, "mw.cx.ui.TranslationUnit": { "scripts": [ "ui/translationunits/mw.cx.ui.TranslationUnit.js", diff --git a/modules/cache/mw.cx.CategoryCache.js b/modules/cache/mw.cx.CategoryCache.js new file mode 100644 index 0000000..50f18e1 --- /dev/null +++ b/modules/cache/mw.cx.CategoryCache.js @@ -0,0 +1,51 @@ +/** + * ContentTranslation Category request cache + * + */ + +'use strict'; +/** + * Caches information about title pairs. + * + * @class + * @extends mw.cx.ApiResponseCache + * @constructor + * @param {Object} config Configuration + */ +mw.cx.CategoryCache = function CXCategoryCache( config ) { + // Call parent constructor + mw.cx.CategoryCache.super.call( this, config ); + this.language = config.language; + this.cacheValues = {}; +}; + +/* Inheritance */ + +OO.inheritClass( mw.cx.CategoryCache, mw.cx.ApiResponseCache ); + +/* Methods */ + +/** + * @inheritdoc + */ +mw.cx.CategoryCache.static.processPage = function ( page ) { + return { + categories: $.map( OO.getProp( page, 'categories' ), function( item ) { + return item.title; + } ) + }; +}; + +/** + * @inheritdoc + */ +mw.cx.CategoryCache.prototype.getRequestPromise = function ( subqueue ) { + return this.siteMapper.getApi( this.language ).get( { + formatversion: 2, + action: 'query', + prop: 'categories', + clshow: '!hidden', + cllimit: 100, + titles: subqueue.join( '|' ) + } ); +}; diff --git a/modules/dm/mw.cx.dm.SourcePage.js b/modules/dm/mw.cx.dm.SourcePage.js index ffbaf7b..ce567d2 100644 --- a/modules/dm/mw.cx.dm.SourcePage.js +++ b/modules/dm/mw.cx.dm.SourcePage.js @@ -26,6 +26,7 @@ this.direction = null; this.title = config.sourceTitle; this.sourceRevision = config.sourceRevision; + this.requestManager = config.requestManager; this.sections = []; this.categories = []; }; @@ -35,15 +36,9 @@ OO.mixinClass( mw.cx.dm.SourcePage, OO.EventEmitter ); mw.cx.dm.SourcePage.prototype.init = function () { - var self = this; - this.direction = $.uls.data.getDir( this.language ); - return this.fetchPage( this.title, this.language, this.sourceRevision ).then( function () { - return self.getCategories().then( function ( categories ) { - self.categories = categories; - } ); - } ); + return this.fetchPage( this.title, this.language, this.sourceRevision ); }; /** @@ -154,19 +149,9 @@ * @return {jQuery.Promise} */ mw.cx.dm.SourcePage.prototype.getCategories = function () { - return this.config.siteMapper.getApi( this.language ).get( { - action: 'query', - prop: 'categories', - clshow: '!hidden', - cllimit: 100, - indexpageids: true, - titles: this.title - } ).then( function ( response ) { - var pageId; - - if ( response.query ) { - pageId = response.query.pageids[ 0 ]; - return response.query.pages[ pageId ].categories || []; - } - } ); + return this.requestManager.getCategories( this.language, this.title ) + .then( function ( categoriesResult ) { + this.categories = categoriesResult.categories; + return this.categories; + }.bind( this ) ); }; diff --git a/modules/dm/mw.cx.dm.TargetPage.js b/modules/dm/mw.cx.dm.TargetPage.js index d48d730..f8e7a28 100644 --- a/modules/dm/mw.cx.dm.TargetPage.js +++ b/modules/dm/mw.cx.dm.TargetPage.js @@ -13,10 +13,11 @@ // Mixin constructor OO.EventEmitter.call( this ); this.config = config; - this.language = config.sourceLanguage; + this.language = config.targetLanguage; this.direction = null; this.title = config.sourceTitle; this.revisionId = config.revision; + this.requestManager = config.requestManager; this.section = []; this.categories = []; }; @@ -32,3 +33,55 @@ }; mw.cx.dm.TargetPage.prototype.publish = function () {}; + +/** + * Get categories for the target article. + * @return {string[]} Array of category titles with namespace prefix + */ +mw.cx.dm.TargetPage.prototype.getCategories = function () { + return this.categories; +}; + +/** + * Add a category to target article + * @param {string} categoryTitle Category title with namespace prefix + */ +mw.cx.dm.TargetPage.prototype.addCategory = function ( categoryTitle ) { + this.categories.push( categoryTitle ); +}; + +/** + * Remove a category to target article + * @param {string} categoryTitle Category title with namespace prefix + */ +mw.cx.dm.TargetPage.prototype.removeCategory = function ( categoryTitle ) { + var index = this.categories.indexOf( categoryTitle ); + if ( index > -1 ) { + this.categories.splice( index, 1 ); + } +}; + +/** + * Adapt and add categories from an array of categories + * @param {string} sourceLanguage Source language + * @param {string[]} sourceCategories Array of source category titles, with namespace prefix + * @return {jQuery.Promise} + */ +mw.cx.dm.TargetPage.prototype.adaptCategoriesFrom = function ( sourceLanguage, sourceCategories ) { + var i, category, + deferreds = []; + + for ( i = 0; i < sourceCategories.length; i++ ) { + category = sourceCategories[ i ]; + deferreds.push( this.requestManager.getTitlePair( sourceLanguage, category ) + .then( function ( pairInfo ) { + if ( pairInfo.targetTitle ) { + this.addCategory( pairInfo.targetTitle ); + } + }.bind( this ) ) + ); + } + // Note that requestManager will take care of combining all these categories + // to a single network request. + return $.when.apply( $, deferreds ); +}; diff --git a/modules/dm/mw.cx.dm.Translation.js b/modules/dm/mw.cx.dm.Translation.js index 0267e85..9f199bb 100644 --- a/modules/dm/mw.cx.dm.Translation.js +++ b/modules/dm/mw.cx.dm.Translation.js @@ -37,21 +37,26 @@ * @return {jQuery.Promise} */ mw.cx.dm.Translation.prototype.init = function () { - var self = this; this.sourcePage = new mw.cx.dm.SourcePage( this.config ); - return this.sourcePage.init().done( function () { - self.onSourcePageReady(); - } ); + return this.sourcePage.init().then( function () { + return this.onSourcePageReady().then( function() { + this.emit( 'sourcePageReady' ); + }.bind( this ) ); + }.bind( this ) ); }; /** * Handler for onSourcePageReady event. + * @return {jQuery.Promise} */ mw.cx.dm.Translation.prototype.onSourcePageReady = function () { mw.log( '[CX] Translation loaded', this ); this.setRevisionId( this.sourcePage.revisionId ); this.prepareTranslationUnits(); this.targetPage = new mw.cx.dm.TargetPage( this.config ); + return this.sourcePage.getCategories().then( function( sourceCategories ) { + return this.targetPage.adaptCategoriesFrom( this.sourceLanguage, sourceCategories ); + }.bind( this ) ); }; /** diff --git a/modules/mw.cx.MwApiRequestManager.js b/modules/mw.cx.MwApiRequestManager.js index 864a917..3fe41bf 100644 --- a/modules/mw.cx.MwApiRequestManager.js +++ b/modules/mw.cx.MwApiRequestManager.js @@ -25,6 +25,7 @@ this.linkCache = {}; this.imageCache = {}; this.titlePairCache = {}; + this.categoryCache = {}; }; /** @@ -56,6 +57,14 @@ this.titlePairCache[ this.targetLanguage ] = new mw.cx.TitlePairCache( { sourceLanguage: this.targetLanguage, targetLanguage: this.sourceLanguage, + siteMapper: this.siteMapper + } ); + this.categoryCache[ this.sourceLanguage ] = new mw.cx.CategoryCache( { + language: this.sourceLanguage, + siteMapper: this.siteMapper + } ); + this.categoryCache[ this.targetLanguage ] = new mw.cx.CategoryCache( { + language: this.targetLanguage, siteMapper: this.siteMapper } ); }; @@ -107,3 +116,17 @@ } return this.titlePairCache[ language ].get( title ); }; + +mw.cx.MwApiRequestManager.prototype.getTitlePairs = function ( language, title ) { + if ( !this.titlePairCache[ language ] ) { + throw Error( '[CX] TitlePairCache not initialized for ' + language ); + } + return this.titlePairCache[ language ].get( title ); +}; + +mw.cx.MwApiRequestManager.prototype.getCategories = function ( language, title ) { + if ( !this.categoryCache[ language ] ) { + throw Error( '[CX] CategoryCache not initialized for ' + language ); + } + return this.categoryCache[ language ].get( title ); +}; diff --git a/modules/ui/mw.cx.ui.Categories.js b/modules/ui/mw.cx.ui.Categories.js new file mode 100644 index 0000000..0564c47 --- /dev/null +++ b/modules/ui/mw.cx.ui.Categories.js @@ -0,0 +1,34 @@ +'use strict'; + +/** + * @class + * @param {Object} [config] Configuration object + */ +mw.cx.ui.Categories = function ( config ) { + this.categoryCount = null; + this.categoryListing = null; + this.page = config.page; +}; + +mw.cx.ui.Categories.prototype.getCategoryCount = function () { + var count; + + count = this.page.categories.length; + this.categoryCount = new OO.ui.ButtonWidget( { + label: mw.msg( 'cx-tools-categories-count-message', count ), + icon: 'tag', + framed: false + } ); + return this.categoryCount; +}; + +mw.cx.ui.Categories.prototype.getCategoryListing = function () { + return this.categoryListing; +}; + +mw.cx.ui.Categories.prototype.listen = function () { + this.categoryCount.on( 'click', function () { + // Scroll to categoryListing + this.categoryListing.scrollElementIntoView(); + } ); +}; diff --git a/modules/ui/mw.cx.ui.Columns.js b/modules/ui/mw.cx.ui.Columns.js index d164962..ecff3cd 100644 --- a/modules/ui/mw.cx.ui.Columns.js +++ b/modules/ui/mw.cx.ui.Columns.js @@ -3,13 +3,14 @@ /** * * @class + * @param {mw.cx.dm.Translation} translation * @param {Object} config */ -mw.cx.ui.Columns = function ( config ) { +mw.cx.ui.Columns = function ( translation, config ) { // Configuration initialization this.config = config || {}; - this.sourceColumn = new mw.cx.ui.SourceColumn( this.config ); - this.translationColumn = new mw.cx.ui.TranslationColumn( this.config ); + this.sourceColumn = new mw.cx.ui.SourceColumn( translation, this.config ); + this.translationColumn = new mw.cx.ui.TranslationColumn( translation, this.config ); this.ToolsColumn = new mw.cx.ui.ToolsColumn( this.config ); // Parent constructor diff --git a/modules/ui/mw.cx.ui.SourceColumn.js b/modules/ui/mw.cx.ui.SourceColumn.js index f4c2f25..ba793da 100644 --- a/modules/ui/mw.cx.ui.SourceColumn.js +++ b/modules/ui/mw.cx.ui.SourceColumn.js @@ -4,9 +4,10 @@ * Source article container * * @class + * @param {mw.cx.dm.Translation} translation * @param {Object} [config] Configuration object */ -mw.cx.ui.SourceColumn = function ( config ) { +mw.cx.ui.SourceColumn = function ( translation, config ) { // Configuration initialization this.config = $.extend( {}, config, { continuous: true, @@ -17,9 +18,13 @@ // Parent constructor mw.cx.ui.SourceColumn.parent.call( this, this.config ); this.siteMapper = config.siteMapper; + this.translation = translation; this.loading = true; this.$loadingIndicator = null; this.init(); + this.translation.connect( this, { + sourcePageReady: 'onSourcePageReady' + } ); }; /* Setup */ @@ -33,8 +38,8 @@ }; mw.cx.ui.SourceColumn.prototype.render = function () { - var sourceLanguageDir, - $languageLabel, $articleLink, userLanguage, $subHeading; + var sourceLanguageDir, $languageLabel, $articleLink, + userLanguage, $subHeading, categoryUI; sourceLanguageDir = $.uls.data.getDir( this.config.sourceLanguage ); this.$element.prop( { @@ -76,10 +81,25 @@ .addClass( 'cx-column__sub-heading' ) .append( $languageLabel, $articleLink ); + this.$content = $( '<div>' ) .addClass( 'cx-column__content' ); - this.$element.append( this.$title, $subHeading, this.$content ); + + this.$element.append( + this.$title, + $subHeading, + this.$content + ); this.showLoadingIndicator(); +}; + +mw.cx.ui.SourceColumn.prototype.onSourcePageReady = function() { + var categoryUI = new mw.cx.ui.Categories( { + page: this.translation.sourcePage + } ); + this.loading = false; + this.$loadingIndicator.remove(); + this.$content.before( categoryUI.getCategoryCount().$element ); }; /** @@ -87,10 +107,6 @@ * @param {integer} position */ mw.cx.ui.SourceColumn.prototype.add = function ( $translationUnit, position ) { - if ( this.loading ) { - this.loading = false; - this.$loadingIndicator.remove(); - } this.insertAt( position, $translationUnit ); }; diff --git a/modules/ui/mw.cx.ui.ToolsColumn.js b/modules/ui/mw.cx.ui.ToolsColumn.js index 8e2299b..a71d4b6 100644 --- a/modules/ui/mw.cx.ui.ToolsColumn.js +++ b/modules/ui/mw.cx.ui.ToolsColumn.js @@ -36,7 +36,8 @@ mw.cx.ui.ToolsColumn.prototype.init = function () { mw.loader.using( 'mw.cx.tools', function () { mw.log( '[CX] Initializing translation tool system' ); - } ); + this.showInstructions(); + }.bind( this ) ); this.listen(); }; @@ -44,6 +45,18 @@ $( window ).on( 'scroll resize', this.onWindowScroll.bind( this ) ); }; + +/** + * Show the instructions card when translation is loaded. + */ +mw.cx.ui.ToolsColumn.prototype.showInstructions = function () { + var instructions = mw.cx.tools.translationToolFactory.create( + 'instructions', null, this, this.config + ); + + this.showTool( instructions ); +}; + /** * Show the tools associated with the translationUnit. * diff --git a/modules/ui/mw.cx.ui.TranslationColumn.js b/modules/ui/mw.cx.ui.TranslationColumn.js index 88131ef..1af2d24 100644 --- a/modules/ui/mw.cx.ui.TranslationColumn.js +++ b/modules/ui/mw.cx.ui.TranslationColumn.js @@ -4,9 +4,10 @@ * Translation column * * @class + * @param {mw.cx.dm.Translation} translation * @param {Object} [config] Configuration object */ -mw.cx.ui.TranslationColumn = function ( config ) { +mw.cx.ui.TranslationColumn = function ( translation, config ) { // Configuration initialization this.config = $.extend( {}, config, { continuous: true, @@ -17,7 +18,11 @@ // Parent constructor mw.cx.ui.TranslationColumn.parent.call( this, this.config ); this.siteMapper = config.siteMapper; + this.translation = translation; this.init(); + this.translation.connect( this, { + sourcePageReady: 'onSourcePageReady' + } ); }; /* Setup */ @@ -90,6 +95,13 @@ mw.hook( 'mw.cx.translation.ready' ).fire(); }; +mw.cx.ui.TranslationColumn.prototype.onSourcePageReady = function() { + var categoryUI = new mw.cx.ui.Categories( { + page: this.translation.targetPage + } ); + this.$content.before( categoryUI.getCategoryCount().$element ); +}; + /** * @param {jQuery} $translationUnit * @param {integer} position diff --git a/modules/ui/mw.cx.ui.TranslationView.js b/modules/ui/mw.cx.ui.TranslationView.js index 5a7d8a3..2e6377f 100644 --- a/modules/ui/mw.cx.ui.TranslationView.js +++ b/modules/ui/mw.cx.ui.TranslationView.js @@ -7,13 +7,10 @@ */ mw.cx.ui.TranslationView = function ( config ) { - this.header = new mw.cx.ui.Header( config ); - this.columns = new mw.cx.ui.Columns( config ); // Configuration initialization this.config = $.extend( {}, config, { continuous: true, expanded: false, - items: [ this.header, this.columns ], classes: [ 'cx-widget' ], scrollable: false, padded: false @@ -21,9 +18,6 @@ // Parent constructor mw.cx.ui.TranslationView.parent.call( this, this.config ); this.publishButton = null; - this.connect( this, { - change: 'onChange' - } ); this.init(); }; @@ -33,8 +27,6 @@ OO.mixinClass( mw.cx.ui.TranslationUnit, OO.EventEmitter ); mw.cx.ui.TranslationView.prototype.init = function () { - var self = this; - if ( mw.user.isAnon() ) { mw.hook( 'mw.cx.error.anonuser' ).fire(); return; @@ -52,12 +44,19 @@ mw.hook( 'mw.cx.cta.accept' ).fire( this.config.campaign, this.config.sourceLanguage, this.config.targetLanguage ); } - this.translation = new mw.cx.dm.Translation( this.config ); - this.translation.init().then( function () { - self.loadTranslation(); - self.showInstructions(); - } ); + this.header = new mw.cx.ui.Header( this.config ); this.preparePublishButton(); + + this.translation = new mw.cx.dm.Translation( this.config ); + this.columns = new mw.cx.ui.Columns( this.translation, this.config ); + this.addItems( [ this.header, this.columns ] ); + + this.translation.init().then( function () { + this.loadTranslation(); + }.bind( this ) ); + this.connect( this, { + change: 'onChange' + } ); }; /** @@ -88,17 +87,6 @@ mw.cx.ui.TranslationView.prototype.preparePublishButton = function () { this.setupPublishButton(); this.attachPublishButton(); -}; - -/** - * Show the instructions card when translation is loaded. - */ -mw.cx.ui.TranslationView.prototype.showInstructions = function () { - var instructions = mw.cx.tools.translationToolFactory.create( - 'instructions', null, this, this.config - ); - - this.columns.ToolsColumn.showTool( instructions ); }; mw.cx.ui.TranslationView.prototype.setupPublishButton = function () { -- To view, visit https://gerrit.wikimedia.org/r/333894 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Icfc26460a2cb83301c5788614b5cc0e44a5e5ea0 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/ContentTranslation Gerrit-Branch: master Gerrit-Owner: Santhosh <santhosh.thottin...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits