jenkins-bot has submitted this change and it was merged.
Change subject: ContentTranslation Dashboard
......................................................................
ContentTranslation Dashboard
Change-Id: Ie202728b01a8a8172e48dbbf58463b8534cb6c1c
---
M .jshintrc
M Resources.php
M i18n/en.json
M i18n/qqq.json
A modules/dashboard/ext.cx.dashboard.js
A modules/dashboard/ext.cx.translationlist.js
A modules/dashboard/styles/ext.cx.dashboard.less
A modules/dashboard/styles/ext.cx.translationlist.less
M modules/source/ext.cx.source.selector.js
M modules/tools/ext.cx.progressbar.js
M specials/SpecialContentTranslation.php
11 files changed, 480 insertions(+), 6 deletions(-)
Approvals:
Amire80: Looks good to me, approved
jenkins-bot: Verified
diff --git a/.jshintrc b/.jshintrc
index d7649a3..1ed42eb 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -24,6 +24,7 @@
"predef": [
"mediaWiki",
"jQuery",
- "QUnit"
+ "QUnit",
+ "moment"
]
}
diff --git a/Resources.php b/Resources.php
index 9ca7d71..c1e8dfe 100644
--- a/Resources.php
+++ b/Resources.php
@@ -35,6 +35,26 @@
),
) + $resourcePaths;
+$wgResourceModules['ext.cx.dashboard'] = array(
+ 'scripts' => array(
+ 'dashboard/ext.cx.dashboard.js',
+ ),
+ 'styles' => array(
+ 'dashboard/styles/ext.cx.dashboard.less',
+ ),
+ 'dependencies' => array(
+ 'ext.cx.model',
+ 'ext.cx.header',
+ 'ext.cx.sitemapper',
+ 'ext.cx.translationlist',
+ 'ext.cx.source.selector',
+ ),
+ 'messages' => array(
+ 'cx-create-new-translation',
+ 'cx-create-new-translation-desc',
+ ),
+) + $resourcePaths;
+
$wgResourceModules['ext.cx.util'] = array(
'scripts' => array(
'util/ext.cx.util.js',
@@ -462,6 +482,27 @@
),
) + $resourcePaths;
+$wgResourceModules['ext.cx.translationlist'] = array(
+ 'scripts' => array(
+ 'dashboard/ext.cx.translationlist.js',
+ ),
+ 'styles' => array(
+ 'dashboard/styles/ext.cx.translationlist.less',
+ ),
+ 'dependencies' => array(
+ 'moment',
+ 'jquery.uls.data',
+ 'ext.cx.progressbar'
+ ),
+ 'messages' => array(
+ 'cx-translation-filter-all-translations',
+ 'cx-translation-filter-published-translations',
+ 'cx-translation-filter-draft-translations',
+ 'cx-translation-filter-from-any-language',
+ 'cx-translation-filter-to-any-language',
+ ),
+) + $resourcePaths;
+
$wgResourceModules['ext.cx.editor'] = array(
'scripts' => array(
'editor/ext.cx.editor.js',
diff --git a/i18n/en.json b/i18n/en.json
index 1583328..c34d025 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -84,6 +84,13 @@
"cx-mt-abuse-warning-title": "Your translation contains $1% of
unmodified machine-translated text",
"cx-mt-abuse-warning-text": "Machine translations are provided only as
a template. You need to make sure that the content is accurate and reads
naturally in your language.",
"cx-publish-captcha-title": "Security question",
+ "cx-translation-filter-all-translations": "All translations",
+ "cx-translation-filter-published-translations": "Published
translations",
+ "cx-translation-filter-draft-translations": "Draft translations",
+ "cx-translation-filter-from-any-language": "From any language",
+ "cx-translation-filter-to-any-language": "To any language",
+ "cx-create-new-translation": "Create new translation",
+ "cx-create-new-translation-desc": "Create pages for topics that do not
exist in your language. It is easy with the help of our translation tools.
[https://en.wikipedia.org/wiki/Project:Translation#How_to_translate Learn
more]",
"apihelp-cxpublish-description": "Save a page created using the content
translation extension.",
"apihelp-cxpublish-param-title": "The title of the page to perform
actions on.",
"apihelp-cxpublish-param-html": "The content to save.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 3c048a2..9a96348 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -88,6 +88,13 @@
"cx-mt-abuse-warning-title": "Title text shown in machine translation
abuse card.\n* $1: Percentage of machine translation",
"cx-mt-abuse-warning-text": "Detailed explanation of machine
translation abuse.",
"cx-publish-captcha-title": "Title of captcha form while publishing the
translation",
+ "cx-translation-filter-all-translations": "Shown in translation filter
in Content Translation dashboard. Selects all translations by the current
translator.",
+ "cx-translation-filter-published-translations": "Shown in translation
filter in Content Translation dashboard. Selects all published translations by
the current translator.",
+ "cx-translation-filter-draft-translations": "Shown in translation
filter in Content Translation dashboard. Selects all draft translations by the
current translator.",
+ "cx-translation-filter-from-any-language": "Shown in translation filter
in Content Translation dashboard. Selects source language as any language.",
+ "cx-translation-filter-to-any-language": "Shown in translation filter
in Content Translation dashboard. Selects target language as any language.",
+ "cx-create-new-translation": "Button label for creating new translation
in the Content Translation dashboard.",
+ "cx-create-new-translation-desc": "Description about what the content
translation tool does. The link may point to a translation help page in your
language.",
"apihelp-cxpublish-description":
"{{doc-apihelp-description|cxpublish}}",
"apihelp-cxpublish-param-title":
"{{doc-apihelp-param|cxpublish|title}}",
"apihelp-cxpublish-param-html": "{{doc-apihelp-param|cxpublish|html}}",
diff --git a/modules/dashboard/ext.cx.dashboard.js
b/modules/dashboard/ext.cx.dashboard.js
new file mode 100644
index 0000000..98f4fe5
--- /dev/null
+++ b/modules/dashboard/ext.cx.dashboard.js
@@ -0,0 +1,89 @@
+/**
+ * ContentTranslation extension - Dashboard.
+ *
+ * @file
+ * @ingroup Extensions
+ * @copyright See AUTHORS.txt
+ * @license GPL-2.0+
+ */
+( function ( $, mw ) {
+ 'use strict';
+
+ /**
+ * CXDashboard
+ *
+ * @class
+ */
+ function CXDashboard( element, siteMapper, options ) {
+ this.$container = $( element );
+ this.options = $.extend( true, {}, $.fn.cxDashboard.defaults,
options );
+ this.siteMapper = siteMapper;
+ this.$header = null;
+ this.$translationList = null;
+ this.$newTranslationButton = null;
+ this.init();
+ }
+
+ CXDashboard.prototype.init = function () {
+ this.render();
+ this.initComponents();
+ this.listen();
+ };
+
+ /**
+ * Initialize the components
+ */
+ CXDashboard.prototype.initComponents = function () {
+ this.$header.cxHeader( this.siteMapper );
+ this.$translationList.cxTranslationList( this.siteMapper );
+ };
+
+ CXDashboard.prototype.render = function () {
+ var $content, $newTranslationContainer, $newTranslationDesc;
+
+ this.$header = $( '<div>' ).addClass( 'cx-widget__header' );
+ $newTranslationContainer = $( '<div>' ).addClass( 'cx-cta' );
+ this.$translationList = $( '<div>' ).addClass(
'cx-translationlist' );
+ $content = $( '<div>' ).addClass( 'cx-widget' )
+ .append( this.$header, $newTranslationContainer,
this.$translationList );
+
+ this.$container.append( $content );
+ this.$newTranslationButton = $( '<button>' )
+ .addClass( 'cx-cta__action mw-ui-button mw-ui-big
mw-ui-progressive' )
+ .text( mw.msg( 'cx-create-new-translation' ) );
+ $newTranslationDesc = $( '<div>' )
+ .addClass( 'cx-cta___description' )
+ .html( mw.message( 'cx-create-new-translation-desc'
).parse() );
+ $newTranslationContainer.append( this.$newTranslationButton,
$newTranslationDesc );
+ };
+
+ CXDashboard.prototype.listen = function () {
+ this.$newTranslationButton.cxSourceSelector( {
+ left: '25%'
+ } );
+ };
+
+ $.fn.cxDashboard = function ( siteMapper, options ) {
+ return this.each( function () {
+ var $this = $( this ),
+ data = $this.data( 'cxdashboard' );
+
+ if ( !data ) {
+ $this.data( 'cx', ( data = new CXDashboard(
this, siteMapper, options ) ) );
+ }
+
+ if ( typeof options === 'string' ) {
+ data[ options ].call( $this );
+ }
+ } );
+ };
+
+ $.fn.cxDashboard.defaults = {};
+
+ // Set the global siteMapper for code which we cannot inject it
+ mw.cx.siteMapper = new mw.cx.SiteMapper( mw.config.get(
'wgContentTranslationSiteTemplates' ) );
+
+ $( function () {
+ $( 'body' ).cxDashboard( mw.cx.siteMapper );
+ } );
+}( jQuery, mediaWiki ) );
diff --git a/modules/dashboard/ext.cx.translationlist.js
b/modules/dashboard/ext.cx.translationlist.js
new file mode 100644
index 0000000..0e5fb79
--- /dev/null
+++ b/modules/dashboard/ext.cx.translationlist.js
@@ -0,0 +1,186 @@
+/**
+ * ContentTranslation extension - Translation listing in dashboard.
+ *
+ * @file
+ * @ingroup Extensions
+ * @copyright See AUTHORS.txt
+ * @license GPL-2.0+
+ */
+( function ( $, mw ) {
+ 'use strict';
+
+ /**
+ * CXTranslationList
+ *
+ * @class
+ */
+ function CXTranslationList( element, siteMapper ) {
+ this.$container = $( element );
+ this.siteMapper = siteMapper;
+ this.translations = [];
+ this.init();
+ }
+
+ CXTranslationList.prototype.init = function () {
+ var translationList = this;
+
+ this.getTranslations( mw.user.getName() )
+ .done( function ( response ) {
+ translationList.translations =
response.query.contenttranslation.translations;
+ translationList.render();
+ } );
+ };
+
+ /**
+ * Get the thumbnail image of the given link
+ * @param {string} id translation id
+ * @param {string} language
+ * @param {string} title Title
+ * @return {jQuery.Promise}
+ */
+ CXTranslationList.prototype.getLinkImage = function ( id, 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
+ } );
+ };
+
+ /**
+ * Get all the translations of given user
+ * @param {string} username User name
+ * @return {jQuery.Promise}
+ */
+ CXTranslationList.prototype.getTranslations = function ( username ) {
+ var api = new mw.Api();
+
+ return api.get( {
+ action: 'query',
+ list: 'contenttranslation',
+ user: username,
+ format: 'json'
+ } );
+ };
+
+ /**
+ * Show a title image of the translation based on source title.
+ * @param {Object} translation
+ */
+ CXTranslationList.prototype.showTitleImage = function ( translation ) {
+ this.getLinkImage( translation.id, 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;
+ $( '#' + translation.id ).find(
'.image' ).attr( 'src', imgSrc );
+ }
+ } );
+
+ };
+
+ /**
+ * List all translations.
+ * @param {Object[]} translations
+ */
+ CXTranslationList.prototype.listTranslations = function ( translations
) {
+ var i, translation, $translation, $titleLanguageBlock,
$sourceLink, $sourceLanguage,
+ $targetLanguage, $imageBlock, $lastUpdated, $image,
$status, $progressbar;
+
+ for ( i = 0; i < translations.length; i++ ) {
+
+ translation = translations[ i ].translation;
+ $translation = $( '<div>' )
+ .addClass( 'translation' )
+ .attr( 'id', translation.id );
+ $lastUpdated = $( '<div>' )
+ .addClass( 'last-updated' )
+ .text( moment( translation.lastUpdateTimeStamp,
'YYYYMMDDHHmmss Z' ).fromNow() );
+ $imageBlock = $( '<div>' )
+ .addClass( 'image-block' );
+ $image = $( '<img>' )
+ .addClass( 'image' );
+ $progressbar = $( '<div>' )
+ .addClass( 'progressbar' )
+ .cxProgressBar( {
+ weights: JSON.parse(
translation.progress )
+ } );
+ $imageBlock.append( $image, $progressbar );
+ this.showTitleImage( translation );
+
+ $sourceLink = $( '<a>' )
+ .addClass( 'source-title' )
+ .attr( {
+ href: translation.sourceURL,
+ target: '_blank'
+ } ).text( translation.sourceTitle );
+ $sourceLanguage = $( '<div>' )
+ .addClass( 'source-language' )
+ .text( $.uls.data.getAutonym(
translation.sourceLanguage ) );
+ $targetLanguage = $( '<div>' )
+ .addClass( 'target-language' )
+ .text( $.uls.data.getAutonym(
translation.targetLanguage ) );
+ $status = $( '<div>' )
+ .addClass( 'status status-' +
translation.status )
+ .text( translation.status );
+ $titleLanguageBlock = $( '<div>' )
+ .addClass( 'title-language-block' )
+ .append( $sourceLink, $sourceLanguage,
$targetLanguage, $status );
+ $translation.append(
+ $lastUpdated,
+ $imageBlock,
+ $titleLanguageBlock
+ );
+ this.$container.append( $translation );
+ }
+ };
+
+ CXTranslationList.prototype.render = function () {
+ var $translationFilterContainer, $translationStatusFilter,
+ $sourceLanguageFilter, $targetLanguageFilter;
+
+ $translationFilterContainer = $( '<div>' )
+ .addClass( 'translation-filter' );
+ $translationStatusFilter = $( '<select>' ).addClass(
'translation-status-filter' );
+ $translationStatusFilter.append(
+ $( '<option>' ).text( mw.msg(
'cx-translation-filter-all-translations' ) ),
+ $( '<option>' ).text( mw.msg(
'cx-translation-filter-published-translations' ) ),
+ $( '<option>' ).text( mw.msg(
'cx-translation-filter-draft-translations' ) )
+ );
+ $sourceLanguageFilter = $( '<select>' ).addClass(
'translation-source-language-filter' );
+ $sourceLanguageFilter.append(
+ $( '<option>' ).text( mw.msg(
'cx-translation-filter-from-any-language' ) )
+ );
+ $targetLanguageFilter = $( '<select>' ).addClass(
'translation-target-language-filter' );
+ $targetLanguageFilter.append(
+ $( '<option>' ).text( mw.msg(
'cx-translation-filter-to-any-language' ) )
+ );
+ $translationFilterContainer.append(
+ $translationStatusFilter, $sourceLanguageFilter,
$targetLanguageFilter
+ );
+ this.$container.append( $translationFilterContainer );
+ this.listTranslations( this.translations );
+ };
+
+ $.fn.cxTranslationList = function ( siteMapper ) {
+ return this.each( function () {
+ var $this = $( this ),
+ data = $this.data( 'cxtranslationlist' );
+
+ if ( !data ) {
+ $this.data( 'cx', ( data = new
CXTranslationList( this, siteMapper ) ) );
+ }
+ } );
+ };
+
+}( jQuery, mediaWiki ) );
diff --git a/modules/dashboard/styles/ext.cx.dashboard.less
b/modules/dashboard/styles/ext.cx.dashboard.less
new file mode 100644
index 0000000..4a6c245
--- /dev/null
+++ b/modules/dashboard/styles/ext.cx.dashboard.less
@@ -0,0 +1,34 @@
+@import "../../base/styles/grid/agora-grid";
+@import "mediawiki.mixins";
+
+@gray-darker: #252525;
+@gray-dark: #565656;
+@gray: #C9C9C9;
+@gray-light: #f0f0f0;
+@gray-lighter: #fbfbfb;
+
+h2 {
+ color: @gray-darker;
+}
+
+.cx-column__title {
+ font-size: 2.2em;
+ font-family: 'Linux Libertine', Georgia, Times, serif;
+}
+
+.cx-cta {
+ .mw-ui-item;
+ .mw-ui-one-whole;
+ padding: 50px;
+ background-color: #fff;
+
+ .cx-cta__action {
+ .mw-ui-one-quarter;
+ }
+
+ .cx-cta___description {
+ .mw-ui-three-quarters;
+ font-size: larger;
+ padding: 0 20px;
+ }
+}
diff --git a/modules/dashboard/styles/ext.cx.translationlist.less
b/modules/dashboard/styles/ext.cx.translationlist.less
new file mode 100644
index 0000000..fd6f5d5
--- /dev/null
+++ b/modules/dashboard/styles/ext.cx.translationlist.less
@@ -0,0 +1,100 @@
+@import "../../base/styles/grid/agora-grid";
+@import "mediawiki.mixins";
+
+.cx-translationlist {
+ .mw-ui-item;
+ .mw-ui-two-thirds;
+ padding-left: 50px;
+
+ .translation {
+ .mw-ui-one-whole;
+ padding: 10px;
+
+ .last-updated {
+ .mw-ui-one-sixth;
+ color: #565656;
+ }
+
+ .image-block {
+ .mw-ui-one-sixth;
+ padding: 0 10px;
+ width: 120px;
+
+ .image {
+ height: 100px;
+ width: 100px;
+
+ &:empty {
+
.background-image-svg('../../tools/images/link_lightgray.svg',
'../images/link_lightgray.png');
+ background-repeat: no-repeat;
+ background-color: #fbfbfb;
+ background-position: center center;
+ border-right: 1px solid #eeeeee;
+ }
+ }
+ }
+
+ .title-language-block {
+ .mw-ui-one-half;
+ .source-title {
+ .mw-ui-one-whole;
+ font-size: larger;
+ }
+
+ .status,
+ .target-language,
+ .source-language {
+ float: left;
+ position: relative;
+ padding: 10px 10px 10px 0;
+ margin-right: 20px;
+ color: #565656;
+ }
+
+ .source-language:after,
+ .source-language:before {
+ left: 100%;
+ top: 50%;
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ }
+
+ .source-language:after {
+ border-color: rgba(255, 255, 255, 0);
+ border-left-color: #fff;
+ border-width: 5px;
+ margin-top: -5px;
+ }
+
+ .source-language:before {
+ border-color: rgba(85, 85, 85, 0);
+ border-left-color: #555;
+ border-width: 7px;
+ margin-top: -7px;
+ }
+
+ .status-published {
+ color: #00af89;
+ }
+ }
+ }
+
+ .translation-filter {
+ padding: 10px 0;
+ .mw-ui-one-whole;
+
+ select {
+ .mw-ui-one-quarter;
+ height: 40px;
+ background-color: white;
+ }
+
+ .translation-source-language-filter {
+ margin-left: 50px;
+ }
+ }
+}
diff --git a/modules/source/ext.cx.source.selector.js
b/modules/source/ext.cx.source.selector.js
index dbc9028..956ac59 100644
--- a/modules/source/ext.cx.source.selector.js
+++ b/modules/source/ext.cx.source.selector.js
@@ -17,7 +17,7 @@
*/
function CXSourceSelector( $trigger, options ) {
this.$trigger = $( $trigger );
- this.options = options;
+ this.options = $.extend( {}, options );
// @todo Refactor
this.siteMapper = mw.cx.siteMapper;
@@ -299,8 +299,6 @@
);
$( 'body' ).append( this.$dialog );
-
- this.show();
};
/**
diff --git a/modules/tools/ext.cx.progressbar.js
b/modules/tools/ext.cx.progressbar.js
index bf94838..d7fa565 100644
--- a/modules/tools/ext.cx.progressbar.js
+++ b/modules/tools/ext.cx.progressbar.js
@@ -25,6 +25,9 @@
ProgressBar.prototype.init = function () {
this.render();
this.listen();
+ if ( this.options.weights ) {
+ this.update( this.options.weights );
+ }
};
ProgressBar.prototype.render = function () {
diff --git a/specials/SpecialContentTranslation.php
b/specials/SpecialContentTranslation.php
index bb4b0d9..e1097f0 100644
--- a/specials/SpecialContentTranslation.php
+++ b/specials/SpecialContentTranslation.php
@@ -24,9 +24,17 @@
public function execute( $parameters ) {
$out = $this->getOutput();
$skin = $this->getSkin();
+ $request = $this->getRequest();
$out->addModuleStyles( 'mediawiki.ui.button' );
- $out->addModules( 'ext.cx.translationview' );
+ if ( $request->getVal( 'from' ) === null ||
+ $request->getVal( 'to' ) === null ||
+ $request->getVal( 'page' ) === null
+ ) {
+ $out->addModules( 'ext.cx.dashboard' );
+ } else {
+ $out->addModules( 'ext.cx.translationview' );
+ }
$this->setHeaders();
$out->setArticleBodyOnly( true );
@@ -50,7 +58,7 @@
null,
$skin->getPersonalToolsList() );
$out->addHTML( Html::rawElement( 'div',
- array( 'id' => 'p-personal'),
+ array( 'id' => 'p-personal' ),
$toolbarList ) );
$out->addHTML( $skin->bottomScripts() );
--
To view, visit https://gerrit.wikimedia.org/r/169347
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ie202728b01a8a8172e48dbbf58463b8534cb6c1c
Gerrit-PatchSet: 17
Gerrit-Project: mediawiki/extensions/ContentTranslation
Gerrit-Branch: master
Gerrit-Owner: Santhosh <[email protected]>
Gerrit-Reviewer: Amire80 <[email protected]>
Gerrit-Reviewer: Nikerabbit <[email protected]>
Gerrit-Reviewer: Santhosh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits