Santhosh has uploaded a new change for review.
https://gerrit.wikimedia.org/r/232019
Change subject: [WIP] Dashboard: Show translation suggestions
......................................................................
[WIP] Dashboard: Show translation suggestions
Change-Id: I503310492a7bac3f4072042bb1e64b9e2ed4891b
---
M extension.json
M modules/dashboard/ext.cx.dashboard.js
A modules/dashboard/ext.cx.suggestionlist.js
A modules/dashboard/styles/ext.cx.suggestionlist.less
4 files changed, 348 insertions(+), 5 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ContentTranslation
refs/changes/19/232019/1
diff --git a/extension.json b/extension.json
index c1604cf..eabfd35 100644
--- a/extension.json
+++ b/extension.json
@@ -197,6 +197,7 @@
"ext.cx.sitemapper",
"ext.cx.source.selector",
"ext.cx.translationlist",
+ "ext.cx.suggestionlist",
"mediawiki.Uri",
"mediawiki.ui.button"
],
@@ -768,6 +769,18 @@
"cx-translationlist-empty-desc"
]
},
+ "ext.cx.suggestionlist": {
+ "scripts": [
+ "dashboard/ext.cx.suggestionlist.js"
+ ],
+ "styles": [
+ "dashboard/styles/ext.cx.suggestionlist.less"
+ ],
+ "dependencies": [
+ "ext.cx.util",
+ "jquery.uls.data"
+ ]
+ },
"ext.cx.translation.conflict": {
"scripts": [
"translation/ext.cx.translation.conflict.js"
diff --git a/modules/dashboard/ext.cx.dashboard.js
b/modules/dashboard/ext.cx.dashboard.js
index ac36202..91b2d67 100644
--- a/modules/dashboard/ext.cx.dashboard.js
+++ b/modules/dashboard/ext.cx.dashboard.js
@@ -39,6 +39,27 @@
};
/**
+ * Get all the translation suggestion lists of given user.
+ *
+ * @return {jQuery.Promise}
+ */
+ CXDashboard.prototype.getSuggestionLists = function () {
+ var storedSourceLanguage, storedTargetLanguage, api = new
mw.Api();
+ if ( window.localStorage ) {
+ storedTargetLanguage = localStorage.getItem(
'cxTargetLanguage' );
+ storedSourceLanguage = localStorage.getItem(
'cxSourceLanguage' );
+ }
+
+ return api.get( {
+ action: 'query',
+ list: 'contenttranslationlist',
+ from: storedSourceLanguage || 'en', // FIXME do
something smart here
+ to: storedTargetLanguage || mw.config.get(
'wgContentLanguage' ),
+ format: 'json'
+ } );
+ };
+
+ /**
* Get all the translations of given user.
*
* @return {jQuery.Promise}
@@ -64,7 +85,11 @@
response.query.contenttranslation.translations
);
} );
- // TODO: Get suggestions
+ this.getSuggestionLists().done( function ( response ) {
+ self.renderTranslationSuggestions(
+
response.query.contenttranslationlist.lists.missingfeaturedarticles
+ );
+ } );
};
/**
@@ -84,6 +109,15 @@
);
}
};
+
+ /**
+ * Populates various UI components with data in the given translation
suggestions.
+ */
+ CXDashboard.prototype.renderTranslationSuggestions = function (
suggestions ) {
+ this.suggestionList = new mw.cx.CXSuggestionList(
this.$translationListContainer, suggestions, this.siteMapper );
+ this.suggestionList.init();
+ };
+
/**
* Populates various UI components with data in the given translations.
*/
@@ -213,11 +247,14 @@
.addClass( 'cx-statusfilter' )
.append(
$( '<span>' )
- .addClass( 'cx-status cx-status--draft
cx-status--selected mw-ui-input' )
- .text( mw.msg(
'cx-translation-filter-draft-translations' ) ),
+ .addClass( 'cx-status
cx-status--suggestions mw-ui-input' )
+ .text( 'Suggestions' ),
$( '<span>' )
- .addClass( 'cx-status cx-status--published
mw-ui-input' )
- .text( mw.msg(
'cx-translation-filter-published-translations' ) )
+ .addClass( 'cx-status cx-status--draft
cx-status--selected mw-ui-input' )
+ .text( mw.msg(
'cx-translation-filter-draft-translations' ) ),
+ $( '<span>' )
+ .addClass( 'cx-status
cx-status--published mw-ui-input' )
+ .text( mw.msg(
'cx-translation-filter-published-translations' ) )
);
this.$sourceLanguageFilter = createSelect(
diff --git a/modules/dashboard/ext.cx.suggestionlist.js
b/modules/dashboard/ext.cx.suggestionlist.js
new file mode 100644
index 0000000..b7f83eb
--- /dev/null
+++ b/modules/dashboard/ext.cx.suggestionlist.js
@@ -0,0 +1,291 @@
+/**
+ * ContentTranslation extension - Translation listing in dashboard.
+ *
+ * @file
+ * @ingroup Extensions
+ * @copyright See AUTHORS.txt
+ * @license GPL-2.0+
+ */
+( function ( $, mw ) {
+ 'use strict';
+
+ /**
+ * CXTranslationList
+ *
+ * @class
+ */
+ function CXSuggestionList( $container, translations, siteMapper ) {
+ this.$container = $container;
+ this.siteMapper = siteMapper;
+ this.translations = translations;
+ this.$sourceLanguageFilter = null;
+ this.$targetLanguageFilter = null;
+ this.$header = null;
+ this.$confirmationDialog = null;
+ this.$overlay = null;
+
+ this.listen();
+ }
+
+ CXSuggestionList.prototype.init = function () {
+ this.listTranslations();
+ };
+
+ /**
+ * Get the thumbnail image of the given link.
+ * @param {string} id translation id
+ * @param {string} language
+ * @param {string} title Title
+ * @return {jQuery.Promise}
+ */
+ CXSuggestionList.prototype.getLinkImage = function ( language, title ) {
+ return this.siteMapper.getApi( language ).get( {
+ action: 'query',
+ titles: title,
+ prop: 'pageimages',
+ piprop: 'thumbnail',
+ pithumbsize: 150,
+ redirects: true,
+ format: 'json'
+ }, {
+ dataType: 'jsonp',
+ // This prevents warnings about the unrecognized
parameter "_"
+ cache: true
+ } );
+ };
+
+ /**
+ * Show a title image of the translation based on source title.
+ * @param {Object} translation
+ */
+ CXSuggestionList.prototype.showTitleImage = function ( translation,
type ) {
+ this.getLinkImage( translation.sourceLanguage,
translation.sourceTitle )
+ .done( function ( response ) {
+ var pageId, page, imgSrc;
+
+ pageId = Object.keys( response.query.pages )[ 0
];
+ page = response.query.pages[ pageId ];
+ if ( page.thumbnail ) {
+ imgSrc = page.thumbnail.source;
+ $( '#' + type + translation.id ).find(
'.image' ).attr( 'src', imgSrc );
+ }
+ } );
+ };
+
+ /**
+ * List all translations.
+ */
+ CXSuggestionList.prototype.listTranslations = function () {
+ var i, translation, progress, $translation,
+ $lastUpdated, $imageBlock, $image, $progressbar,
+ sourceDir, targetDir,
+ translationLinkUrl, $translationLink,
+ $sourceLanguage, $targetLanguage, $languageContainer,
$status,
+ $actionsTrigger, $deleteTranslation, $menu,
$menuContainer,
+ $titleLanguageBlock,
+ $translations = [];
+
+ for ( i = 0; i < this.translations.length; i++ ) {
+ translation = this.translations[ i ];
+
+ try {
+ progress = JSON.parse( translation.progress );
+ } catch ( e ) {
+ progress = {};
+ }
+
+ $translation = $( '<div>' )
+ .addClass( 'cx-tlitem' )
+ .attr( 'id', 'translation' + translation.id );
+ $lastUpdated = $( '<div>' )
+ .addClass( 'last-updated' )
+ .text( moment( translation.lastUpdateTimeStamp,
'YYYYMMDDHHmmss Z' ).fromNow() );
+ $imageBlock = $( '<div>' )
+ .addClass( 'cx-tlitem__image' );
+ $image = $( '<img>' )
+ .addClass( 'image' );
+ $progressbar = $( '<div>' )
+ .addClass( 'progressbar' )
+ .cxProgressBar( {
+ weights: progress
+ } );
+ $imageBlock.append( $image );
+ this.showTitleImage( translation, 'translation' );
+
+ sourceDir = $.uls.data.getDir(
translation.sourceLanguage );
+ targetDir = $.uls.data.getDir(
translation.targetLanguage );
+
+ if ( translation.status === 'draft' ) {
+ translationLinkUrl = new mw.Uri(
mw.cx.siteMapper.getCXUrl(
+ translation.sourceTitle,
+ translation.targetTitle,
+ translation.sourceLanguage,
+ translation.targetLanguage
+ ) ).extend( {
+ draft: translation.status === 'draft' ?
translation.id : undefined
+ } ).toString();
+ }
+
+ if ( translation.status === 'published' ) {
+ translationLinkUrl = translation.targetURL;
+ }
+
+ $translationLink = $( '<a>' )
+ .addClass( 'translation-link' )
+ .prop( 'href', translationLinkUrl )
+ // It must be a separate element to ensure
+ // separation from the target title
+ .append( $( '<span>' )
+ .text( translation.sourceTitle )
+ .addClass( 'source-title' )
+ .prop( {
+ lang:
translation.sourceLanguage,
+ dir: sourceDir
+ } )
+ );
+
+ // If the translated title is different from the source
title,
+ // show it near the source title
+ if ( translation.sourceTitle !==
translation.targetTitle ) {
+ $translationLink.append(
+ $( '<span>' ).html( ' ' ), // nbsp
to ensure separation between words
+ $( '<span>' )
+ .prop( {
+ lang:
translation.targetLanguage,
+ dir: targetDir
+ } )
+ .addClass( 'target-title' )
+ .text( translation.targetTitle )
+ );
+ }
+
+ $sourceLanguage = $( '<div>' )
+ .prop( {
+ lang: translation.sourceLanguage,
+ dir: sourceDir
+ } )
+ .addClass( 'cx-tlitem__languages__language
cx-tlitem__languages__language--source' )
+ .text( $.uls.data.getAutonym(
translation.sourceLanguage ) );
+
+ $targetLanguage = $( '<div>' )
+ .prop( {
+ lang: translation.targetLanguage,
+ dir: targetDir
+ } )
+ .addClass( 'cx-tlitem__languages__language
cx-tlitem__languages__language--target' )
+ .text( $.uls.data.getAutonym(
translation.targetLanguage ) );
+
+ $languageContainer = $( '<div>' )
+ .addClass( 'cx-tlitem__languages' )
+ .append( $sourceLanguage, $targetLanguage );
+
+ $status = $( '<div>' )
+ .addClass( 'status status-' +
translation.status )
+ .text( mw.msg( 'cx-translation-status-' +
translation.status ) );
+
+ // If the translation is draft, allow deleting it
+ if ( translation.status === 'draft' ) {
+ $actionsTrigger = $( '<div>' )
+ .addClass(
'cx-tlitem__actions__trigger' )
+ .text( '…' );
+ $deleteTranslation = $( '<li>' )
+ .addClass( 'cx-discard-translation' )
+ .text( mw.msg( 'cx-discard-translation'
) )
+ .data( 'translation', translation );
+ $menu = $( '<ul>' )
+ .append( $deleteTranslation );
+ $menuContainer = $( '<div>' )
+ .addClass( 'cx-tlitem__actions' )
+ .append( $actionsTrigger, $menu );
+ } else {
+ $menuContainer = $();
+ }
+
+ $titleLanguageBlock = $( '<div>' )
+ .addClass( 'cx-tlitem__details' )
+ .append( $translationLink, $progressbar,
$lastUpdated, $languageContainer );
+
+ $translation.append(
+ $menuContainer,
+ $imageBlock,
+ $titleLanguageBlock
+ );
+
+ $translations.push( $translation );
+
+ // Store reference to the DOM node
+ translation.$element = $translation;
+ }
+
+ if ( $translations.length ) {
+ this.$container.append( $( '<div>' )
+ .addClass( 'cx-translationlist' )
+ .append( $translations )
+ );
+ } else {
+ this.$container.append( $( '<div>' )
+ .addClass( 'cx-translationlist-empty' )
+ .append(
+ $( '<div>' )
+ .addClass(
'cx-translationlist-empty__img' ),
+ $( '<div>' )
+ .addClass(
'cx-translationlist-empty__title' )
+ .text( mw.msg(
'cx-translationlist-empty-title' ) ),
+ $( '<div>' )
+ .addClass(
'cx-translationlist-empty__desc' )
+ .text( mw.msg(
'cx-translationlist-empty-desc' ) )
+ ) );
+ }
+ };
+
+ CXSuggestionList.prototype.listen = function () {
+ var translationList = this;
+
+ this.$container.on( 'click', '.cx-discard-translation',
function ( e ) {
+ var translation;
+
+ e.stopPropagation();
+
+ translation = $( e.target ).data( 'translation' );
+
+ translationList.showDiscardConfirmation( translation
).done( function () {
+ translationList.discardTranslation( translation
).done( function ( response ) {
+ if ( response.cxdelete.result !==
'success' ) {
+ return;
+ }
+
+
translationList.markTranslationAsDeleted( translation );
+ mw.hook( 'mw.cx.translation.deleted'
).fire(
+ translation.sourceLanguage,
+ translation.targetLanguage,
+ translation.sourceTitle,
+ translation.targetTitle
+ );
+ } );
+ } );
+ } );
+
+ this.$container.on( 'click', '.cx-tlitem', function () {
+ if ( $( this ).hasClass( 'cx-translation-deleted' ) ) {
+ return;
+ }
+
+ location.href = $( this ).find( '.translation-link'
).attr( 'href' );
+ } );
+
+ $( window ).scroll( $.throttle( 250, $.proxy( this.scroll, this
) ) );
+ };
+
+ CXSuggestionList.prototype.scroll = function () {
+ var scrollTop = $( window ).scrollTop(),
+ offsetTop = this.$container.offset().top;
+
+ if ( scrollTop > offsetTop ) {
+ this.$container.addClass( 'sticky' );
+ } else if ( scrollTop <= offsetTop ) {
+ this.$container.removeClass( 'sticky' );
+ }
+ };
+
+ mw.cx.CXSuggestionList = CXSuggestionList;
+}( jQuery, mediaWiki ) );
diff --git a/modules/dashboard/styles/ext.cx.suggestionlist.less
b/modules/dashboard/styles/ext.cx.suggestionlist.less
new file mode 100644
index 0000000..f05d003
--- /dev/null
+++ b/modules/dashboard/styles/ext.cx.suggestionlist.less
@@ -0,0 +1,2 @@
+@import "../../widgets/common/ext.cx.common";
+@import "mediawiki.mixins";
--
To view, visit https://gerrit.wikimedia.org/r/232019
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I503310492a7bac3f4072042bb1e64b9e2ed4891b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ContentTranslation
Gerrit-Branch: master
Gerrit-Owner: Santhosh <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits