Petar.petkovic has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/372537 )

Change subject: Add article languages count
......................................................................

Add article languages count

Add languages count while searching and selecting
Add missing in target language information

Bug:T111094
Change-Id: I8fffe5b2056b977f1524b1978789266c281239d9
---
M extension.json
M i18n/api/en.json
M i18n/api/qqq.json
M i18n/en.json
M i18n/qqq.json
M modules/source/ext.cx.source.selector.js
M modules/source/styles/ext.cx.source.selector.less
M modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
8 files changed, 224 insertions(+), 12 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ContentTranslation 
refs/changes/37/372537/1

diff --git a/extension.json b/extension.json
index e1dc515..6a9ff0c 100644
--- a/extension.json
+++ b/extension.json
@@ -56,6 +56,9 @@
                "cxpublishedtranslations": "ApiQueryPublishedTranslations",
                "cxtranslatorstats": "ApiQueryTranslatorStats"
        },
+       "APIPropModules": {
+               "langlinkscount": "ApiQueryLangLinksCount"
+       },
        "MessagesDirs": {
                "ContentTranslation": "i18n",
                "ContentTranslationApi": "i18n/api"
@@ -76,6 +79,7 @@
                "ApiQueryContentTranslationLanguageTrend": 
"api/ApiQueryContentTranslationLanguageTrend.php",
                "ApiQueryContentTranslationStats": 
"api/ApiQueryContentTranslationStats.php",
                "ApiQueryContentTranslationSuggestions": 
"api/ApiQueryContentTranslationSuggestions.php",
+               "ApiQueryLangLinksCount": "api/ApiQueryLangLinksCount.php",
                "ApiQueryPublishedTranslations": 
"api/ApiQueryPublishedTranslations.php",
                "ApiQueryTranslatorStats": "api/ApiQueryTranslatorStats.php",
                "ContentTranslationHooks": "ContentTranslation.hooks.php",
@@ -2050,8 +2054,23 @@
                        ],
                        "dependencies": [
                                "mediawiki.widgets",
+                               "mw.cx.ui.TitleOptionWidget",
                                "oojs-ui.styles.icons-interactions"
                        ]
+               },
+               "mw.cx.ui.TitleOptionWidget": {
+                       "scripts": [
+                               "ui/widgets/mw.cx.ui.TitleOptionWidget.js"
+                       ],
+                       "styles": [
+                               
"ui/styles/widgets/mw.cx.ui.TitleOptionWidget.less"
+                       ],
+                       "messages": [
+                               "cx-sourceselector-missing-in-target-language"
+                       ],
+                       "dependencies": [
+                               "mediawiki.widgets"
+                       ]
                }
        },
        "ResourceFileModulePaths": {
diff --git a/i18n/api/en.json b/i18n/api/en.json
index dd7f545..ebf9983 100644
--- a/i18n/api/en.json
+++ b/i18n/api/en.json
@@ -116,5 +116,8 @@
        "apierror-cx-mustbeloggedin-viewtranslations": "To view your 
translations, you must log in.",
        "apierror-cx-samelanguages": "Source and target languages cannot be the 
same.",
        "apierror-cx-suggestionsdisabled": "Suggestions not enabled for this 
wiki.",
-       "apierror-cx-translationnotfound": "Translation not found."
+       "apierror-cx-translationnotfound": "Translation not found.",
+       "apihelp-query+langlinkscount-description": "Get the total number of 
languages that article exists in.",
+       "apihelp-query+langlinkscount-summary": "Get the number of langlinks.",
+       "apihelp-query+langlinkscount-example-1": "Get number of langlinks for 
'Dog' article"
 }
diff --git a/i18n/api/qqq.json b/i18n/api/qqq.json
index a095870..f9bcd7b 100644
--- a/i18n/api/qqq.json
+++ b/i18n/api/qqq.json
@@ -113,5 +113,8 @@
        "apierror-cx-mustbeloggedin-viewtranslations": "{{doc-apierror}}",
        "apierror-cx-samelanguages": "{{doc-apierror}}",
        "apierror-cx-suggestionsdisabled": "{{doc-apierror}}",
-       "apierror-cx-translationnotfound": "{{doc-apierror}}"
+       "apierror-cx-translationnotfound": "{{doc-apierror}}",
+       "apihelp-query+langlinkscount-description": 
"{{doc-apihelp-description|query+langlinkscount}}",
+       "apihelp-query+langlinkscount-summary": 
"{{doc-apihelp-summary|query+langlinkscount}}",
+       "apihelp-query+langlinkscount-example-1": 
"{{doc-apihelp-example|query+langlinkscount}}"
 }
diff --git a/i18n/en.json b/i18n/en.json
index 6e39db4..449d4af 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -97,6 +97,7 @@
        "cx-sourceselector-dialog-error-page-exists": "The page [$1 already 
exists] in $2",
        "cx-sourceselector-dialog-error-title-in-use": "The title for the new 
page is [$1 already in use]",
        "cx-sourceselector-dialog-error-no-source-article": "The page to 
translate does not exist in $1",
+       "cx-sourceselector-missing-in-target-language": "Missing in $1",
        "cx-mt-abuse-warning-title": "Your translation {{PLURAL:$1|contains}} 
$1% of unmodified machine-translated text",
        "cx-mt-abuse-warning-text": "Machine translation is provided only as a 
starting point. You need to make sure that the content is accurate and reads 
naturally in your language.",
        "cx-publish-captcha-title": "Security question",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 7f0a630..7f849b1 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -106,6 +106,7 @@
        "cx-sourceselector-dialog-error-page-exists": "An error message that 
indicates there is a page in the target wiki with the same title as the 
proposed source title.\n\nParameters:\n* $1 - link to existing target page.\n* 
$2 - target language name.",
        "cx-sourceselector-dialog-error-title-in-use": "Error that indicates 
there is already a page in the target wiki with the same title as the proposed 
target title.\n\nParameters:\n* $1 - link to target page with same title",
        "cx-sourceselector-dialog-error-no-source-article": "Error that 
indicates there is no page with the specified title in the source language to 
translate.\n\nParameters:\n* $1 - the source language",
+       "cx-sourceselector-missing-in-target-language": "Label appended to 
search result indicating that matching article is missing in target language",
        "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",
diff --git a/modules/source/ext.cx.source.selector.js 
b/modules/source/ext.cx.source.selector.js
index 6bd6c62..0ba5c74 100644
--- a/modules/source/ext.cx.source.selector.js
+++ b/modules/source/ext.cx.source.selector.js
@@ -32,6 +32,7 @@
                this.sourceLanguage = null;
                this.targetLanguage = null;
                this.$container = this.embedded ? options.container : null;
+               this.sourceTitles = null;
                this.$selectedItem = null;
                this.$selectedItemImage = null;
                this.$selectedItemInfo = null;
@@ -748,14 +749,15 @@
                }
 
                this.$selectedItemInfo.append(
-                       $( '<a>' )
-                               .prop( {
-                                       href: item.$label.prop( 'href' ),
-                                       title: item.$label.prop( 'title' ),
-                                       target: '_blank',
-                                       text: item.$label.text()
-                               } )
-                               .click( this.openLinkInNewTab )
+                       $( '<a>' ).prop( {
+                               href: item.$label.prop( 'href' ),
+                               title: item.$label.prop( 'title' ),
+                               target: '_blank',
+                               text: item.$label.text()
+                       } ),
+                       $( '<span>' )
+                               .addClass( 
'cx-sourceselector-embedded-selected-item__language-count' )
+                               .text( item.initialConfig.numOfLanguages )
                );
                this.$container
                        .toggleClass( 'cx-sourceselector-embedded--selected' );
@@ -1005,7 +1007,8 @@
                        siteMapper: this.siteMapper,
                        value: this.options.sourceTitle,
                        validateTitle: true,
-                       placeholder: mw.msg( 
'cx-sourceselector-dialog-source-title-placeholder' )
+                       placeholder: mw.msg( 
'cx-sourceselector-dialog-source-title-placeholder' ),
+                       showRedirectTargets: true
                } );
                this.sourcePageSelector.onLookupMenuItemChoose = 
this.setSelectedItem.bind( this );
 
diff --git a/modules/source/styles/ext.cx.source.selector.less 
b/modules/source/styles/ext.cx.source.selector.less
index cbef4c5..6d0221a 100644
--- a/modules/source/styles/ext.cx.source.selector.less
+++ b/modules/source/styles/ext.cx.source.selector.less
@@ -94,12 +94,23 @@
                        padding-left: 0.7em;
 
                        font-size: 1.2em;
-                       font-weight: bold;
 
                        // Needs to be overridden for specificity
                        > a {
                                color: @colorGray1;
+                               font-weight: bold;
                        }
+               }
+
+               &__language-count {
+                       display: block;
+
+                       .background-image-svg('../images/languages.svg', 
'../images/languages.png');
+                       background-size: 24px;
+                       background-repeat: no-repeat;
+                       background-position: left center;
+                       padding-left: 26px;
+                       font-size: 0.8em;
                }
 
                .cx-sourceselector-embedded-discard {
@@ -145,6 +156,8 @@
 
                .flex-display( box ); // Support 2009 syntax
                .flex-display();
+               -ms-flex-item-align: center; // Support IE10+
+               align-self: center;
                margin-left: auto;
 
                .cx-sourceselector-embedded__source-language,
diff --git a/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js 
b/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
index 6a7390c..4d6d840 100644
--- a/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
+++ b/modules/ui/widgets/mw.cx.ui.PageSelectorWidget.js
@@ -69,6 +69,175 @@
        optionWidgetData = 
mw.widgets.TitleWidget.prototype.getOptionWidgetData.call( this, title, data );
        // Correct the URL so that it can point to the source language wiki.
        optionWidgetData.url = this.siteMapper.getPageUrl( this.language, title 
);
+       optionWidgetData.numOfLanguages = data.langlinkscount;
+       optionWidgetData.missingInTargetLanguage = !data.langlinks;
+       optionWidgetData.targetLanguage = this.targetLanguage;
 
        return optionWidgetData;
 };
+
+mw.cx.ui.PageSelectorWidget.prototype.getSuggestionsPromise = function () {
+       var req,
+               api = this.getApi(),
+               query = this.getQueryValue(),
+               widget = this,
+               promiseAbortObject = { abort: function () {
+                       // Do nothing. This is just so OOUI doesn't break due 
to abort being undefined.
+               } };
+
+       // Set API URL to localhost for local testing of langlinks API
+       // api.apiUrl = this.api.defaults.ajax.url;
+       // api.defaults.ajax.url = this.api.defaults.ajax.url;
+
+       if ( mw.Title.newFromText( query ) ) {
+               return this.getInterwikiPrefixesPromise().then( function ( 
interwikiPrefixes ) {
+                       var params,
+                               interwiki = query.substring( 0, query.indexOf( 
':' ) );
+                       if (
+                               interwiki && interwiki !== '' &&
+                               interwikiPrefixes.indexOf( interwiki ) !== -1
+                       ) {
+                               return $.Deferred().resolve( { query: {
+                                       pages: [ {
+                                               title: query
+                                       } ]
+                               } } ).promise( promiseAbortObject );
+                       } else {
+                               params = {
+                                       action: 'query',
+                                       prop: [ 'info', 'pageprops', 
'langlinks', 'langlinkscount' ],
+                                       generator: 'prefixsearch',
+                                       gpssearch: query,
+                                       gpsnamespace: widget.namespace !== null 
? widget.namespace : undefined,
+                                       gpslimit: widget.limit,
+                                       ppprop: 'disambiguation',
+                                       lllang: widget.targetLanguage
+                               };
+                               if ( widget.showRedirectTargets ) {
+                                       params.redirects = 1;
+                               }
+                               if ( widget.showImages ) {
+                                       params.prop.push( 'pageimages' );
+                                       params.pithumbsize = 80;
+                                       params.pilimit = widget.limit;
+                               }
+                               if ( widget.showDescriptions ) {
+                                       params.prop.push( 'pageterms' );
+                                       params.wbptterms = 'description';
+                               }
+                               req = api.get( params );
+                               promiseAbortObject.abort = req.abort.bind( req 
); // TODO ew
+                               return req.then( function ( ret ) {
+                                       if ( ret.query === undefined ) {
+                                               ret = api.get( { action: 
'query', titles: query } );
+                                               promiseAbortObject.abort = 
ret.abort.bind( ret );
+                                       }
+                                       return ret;
+                               } );
+                       }
+               } ).promise( promiseAbortObject );
+       } else {
+               // Don't send invalid titles to the API.
+               // Just pretend it returned nothing so we can show the 'invalid 
title' section
+               return $.Deferred().resolve( {} ).promise( promiseAbortObject );
+       }
+};
+
+mw.cx.ui.PageSelectorWidget.prototype.getOptionsFromData = function ( data ) {
+       var i, len, index, pageExists, pageExistsExact, suggestionPage, page, 
redirect, redirects,
+               currentPageName = new mw.Title( mw.config.get( 
'wgRelevantPageName' ) ).getPrefixedText(),
+               items = [],
+               titles = [],
+               titleObj = mw.Title.newFromText( this.getQueryValue() ),
+               redirectsTo = {},
+               pageData = {};
+
+       if ( data.redirects ) {
+               for ( i = 0, len = data.redirects.length; i < len; i++ ) {
+                       redirect = data.redirects[ i ];
+                       redirectsTo[ redirect.to ] = redirectsTo[ redirect.to ] 
|| [];
+                       redirectsTo[ redirect.to ].push( redirect.from );
+               }
+       }
+
+       for ( index in data.pages ) {
+               suggestionPage = data.pages[ index ];
+               // When excludeCurrentPage is set, don't list the current page 
unless the user has type the full title
+               if ( this.excludeCurrentPage && suggestionPage.title === 
currentPageName && suggestionPage.title !== titleObj.getPrefixedText() ) {
+                       continue;
+               }
+               pageData[ suggestionPage.title ] = {
+                       known: suggestionPage.known !== undefined,
+                       missing: suggestionPage.missing !== undefined,
+                       redirect: suggestionPage.redirect !== undefined,
+                       disambiguation: OO.getProp( suggestionPage, 
'pageprops', 'disambiguation' ) !== undefined,
+                       imageUrl: OO.getProp( suggestionPage, 'thumbnail', 
'source' ),
+                       description: OO.getProp( suggestionPage, 'terms', 
'description' ),
+                       langlinkscount: suggestionPage.langlinkscount,
+                       langlinks: suggestionPage.langlinks,
+                       // Sort index
+                       index: suggestionPage.index
+               };
+
+               // Throw away pages from wrong namespaces. This can happen when 
'showRedirectTargets' is true
+               // and we encounter a cross-namespace redirect.
+               if ( this.namespace === null || this.namespace === 
suggestionPage.ns ) {
+                       titles.push( suggestionPage.title );
+               }
+
+               redirects = redirectsTo[ suggestionPage.title ] || [];
+               for ( i = 0, len = redirects.length; i < len; i++ ) {
+                       pageData[ redirects[ i ] ] = {
+                               missing: false,
+                               known: true,
+                               redirect: true,
+                               disambiguation: false,
+                               description: mw.msg( 
'mw-widgets-titleinput-description-redirect', suggestionPage.title ),
+                               langlinks: suggestionPage.langlinks,
+                               // Sort index, just below its target
+                               index: suggestionPage.index + 0.5
+                       };
+                       titles.push( redirects[ i ] );
+               }
+       }
+
+       titles.sort( function ( a, b ) {
+               return pageData[ a ].index - pageData[ b ].index;
+       } );
+
+       // If not found, run value through mw.Title to avoid treating a match 
as a
+       // mismatch where normalisation would make them matching (T50476)
+
+       pageExistsExact = (
+               Object.prototype.hasOwnProperty.call( pageData, 
this.getQueryValue() ) &&
+               (
+                       !pageData[ this.getQueryValue() ].missing ||
+                       pageData[ this.getQueryValue() ].known
+               )
+       );
+       pageExists = pageExistsExact || (
+               titleObj &&
+               Object.prototype.hasOwnProperty.call( pageData, 
titleObj.getPrefixedText() ) &&
+               (
+                       !pageData[ titleObj.getPrefixedText() ].missing ||
+                       pageData[ titleObj.getPrefixedText() ].known
+               )
+       );
+
+       if ( this.cache ) {
+               this.cache.set( pageData );
+       }
+
+       // Offer the exact text as a suggestion if the page exists
+       if ( pageExists && !pageExistsExact ) {
+               titles.unshift( this.getQueryValue() );
+               pageData[ this.getQueryValue() ] = pageData[ 
titleObj.getPrefixedText() ];
+       }
+
+       for ( i = 0, len = titles.length; i < len; i++ ) {
+               page = pageData[ titles[ i ] ] || {};
+               items.push( new mw.cx.ui.TitleOptionWidget( 
this.getOptionWidgetData( titles[ i ], page ) ) );
+       }
+
+       return items;
+};

-- 
To view, visit https://gerrit.wikimedia.org/r/372537
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8fffe5b2056b977f1524b1978789266c281239d9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ContentTranslation
Gerrit-Branch: master
Gerrit-Owner: Petar.petkovic <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to