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

Reply via email to