jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/340961 )
Change subject: Add mw.cx.TargetArticle class
......................................................................
Add mw.cx.TargetArticle class
MediaWiki specific validations, publishing, success and error
handlers. Supposed to replace ext.cx.publish module in old CX
The success/error message system is not ready so those can't be
tested now - to be done in follow up.
Captcha handler and title conflict errors need to present a dialog
to user to resolve it - to be done in followup.
Change-Id: I5eab6fec79895591ea0b404196fb30f17452f75a
---
M extension.json
M modules/dm/mw.cx.dm.TargetPage.js
M modules/dm/mw.cx.dm.Translation.js
M modules/dm/translationunits/mw.cx.dm.TranslationUnit.js
A modules/mw.cx.TargetArticle.js
M modules/ui/mw.cx.ui.TranslationView.js
6 files changed, 612 insertions(+), 14 deletions(-)
Approvals:
jenkins-bot: Verified
Nikerabbit: Looks good to me, approved
diff --git a/extension.json b/extension.json
index 4bd5fbc..4260df6 100644
--- a/extension.json
+++ b/extension.json
@@ -1360,7 +1360,19 @@
],
"dependencies": [
"mw.cx.dm.Translation",
- "mw.cx.ui.TranslationView"
+ "mw.cx.ui.TranslationView",
+ "mw.cx.TargetArticle"
+ ]
+ },
+ "mw.cx.TargetArticle": {
+ "scripts": [
+ "mw.cx.TargetArticle.js"
+ ],
+ "dependencies": [
+ "easy-deflate.deflate",
+ "mw.cx.dm.Translation",
+ "mw.cx.ui.TranslationView",
+ "mediawiki.api.edit"
]
},
"mw.cx.MwApiRequestManager": {
diff --git a/modules/dm/mw.cx.dm.TargetPage.js
b/modules/dm/mw.cx.dm.TargetPage.js
index f8e7a28..aa45b24 100644
--- a/modules/dm/mw.cx.dm.TargetPage.js
+++ b/modules/dm/mw.cx.dm.TargetPage.js
@@ -28,11 +28,32 @@
/**
* Build the target document for publishing
+ * @param {mw.cx.dm.TranslationUnit[]} translationUnits
*/
-mw.cx.dm.TargetPage.prototype.build = function () {
+mw.cx.dm.TargetPage.prototype.build = function ( translationUnits ) {
+ var i, targetDoc,
+ wrapper = document.createElement( 'div' );
+
+ for ( i = 0; i < translationUnits.length; i++ ) {
+ targetDoc = translationUnits[ i ].getTargetDocument();
+ if ( !targetDoc ) {
+ continue;
+ }
+ wrapper.appendChild( targetDoc.cloneNode( true ) );
+ }
+
+ this.targetPageDocument = wrapper;
};
-mw.cx.dm.TargetPage.prototype.publish = function () {};
+/**
+ * Get the HTML content for publishing
+ * @param {mw.cx.dm.Translation} translation
+ * @return {string}
+ */
+mw.cx.dm.TargetPage.prototype.getContent = function ( translation ) {
+ this.build( translation.getTranslationUnits() );
+ return this.targetPageDocument.innerHTML;
+};
/**
* Get categories for the target article.
diff --git a/modules/dm/mw.cx.dm.Translation.js
b/modules/dm/mw.cx.dm.Translation.js
index 49cf76f..ffc65a0 100644
--- a/modules/dm/mw.cx.dm.Translation.js
+++ b/modules/dm/mw.cx.dm.Translation.js
@@ -60,6 +60,10 @@
return this.translationUnits;
};
+mw.cx.dm.Translation.prototype.getTargetPage = function() {
+ return this.targetPage;
+};
+
/**
* Get Translation id
*
diff --git a/modules/dm/translationunits/mw.cx.dm.TranslationUnit.js
b/modules/dm/translationunits/mw.cx.dm.TranslationUnit.js
index 55ceec2..7d5fe3d 100644
--- a/modules/dm/translationunits/mw.cx.dm.TranslationUnit.js
+++ b/modules/dm/translationunits/mw.cx.dm.TranslationUnit.js
@@ -92,6 +92,10 @@
return this.translationUnits;
};
+mw.cx.dm.TranslationUnit.prototype.getTargetDocument = function () {
+ return this.targetDocument;
+};
+
mw.cx.dm.TranslationUnit.prototype.getParentTranslationUnit = function () {
return this.parentTranslationUnit;
};
diff --git a/modules/mw.cx.TargetArticle.js b/modules/mw.cx.TargetArticle.js
new file mode 100644
index 0000000..04fd8a0
--- /dev/null
+++ b/modules/mw.cx.TargetArticle.js
@@ -0,0 +1,561 @@
+/**
+ * Target Article for CX - Validation, Publishing, Success and Error handling.
+ * @param {mw.cx.dm.Translation} translation
+ * @param {mw.cx.ui.TranslationView} translationView
+ * @param {object} config Translation configuration
+ */
+mw.cx.TargetArticle = function MWCXTargetArticle( translation,
translationView, config ) {
+ this.translation = translation;
+ this.view = translationView;
+ this.config = config;
+ this.siteMapper = config.siteMapper;
+ this.sourceTitle = config.sourceTitle;
+ this.targetTitle = config.targetTitle;
+ this.sourceLanguage = config.sourceLanguage;
+ this.targetLanguage = config.targetLanguage;
+ this.sourceRevision = config.sourceRevision;
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+ // Properties
+ this.publishDeferred = null;
+ this.captcha = null;
+};
+
+/* Inheritance */
+
+OO.mixinClass( mw.cx.TargetArticle, OO.EventEmitter );
+
+/**
+ * Publish the translated content to target wiki.
+ * @return {jQuery.Promise}
+ */
+mw.cx.TargetArticle.prototype.publish = function () {
+ var apiParams;
+
+ apiParams = $.extend( {}, this.captcha || {}, {
+ assert: 'user',
+ action: 'cxpublish',
+ from: this.sourceLanguage,
+ to: this.targetLanguage,
+ sourcetitle: this.sourceTitle,
+ html: this.getContent( true ),
+ categories: this.getCategories()
+ } );
+
+ this.publishDeferred = $.Deferred();
+ // Check for title conflicts
+ this.checkTargetTitle( this.targetTitle ).then( function ( title ) {
+ apiParams.title = title;
+ // Post the content to publish.
+ return mw.Api().postWithToken( 'csrf', apiParams, {
+ // A bigger timeout since publishing after converting
html to wikitext
+ // parsoid is not a fast operation.
+ timeout: 100 * 1000 // in milliseconds
+ } ).then( this.publishSuccess.bind( this ) )
+ // Failure handler
+ .fail( this.publishFail.bind( this ) );
+ }.bind( this ) );
+
+ return this.publishDeferred.promise();
+};
+
+/**
+ * Publish success handler
+ * @param {Object} response Response object from the publishing api
+ * @return {null|jQuery.Promise}
+ */
+mw.cx.TargetArticle.prototype.publishSuccess = function ( response ) {
+ var publishResult = response.cxpublish;
+ if ( publishResult.result === 'success' ) {
+ return this.publishComplete();
+ }
+
+ if ( publishResult.edit.captcha ) {
+ // If there is a captcha challenge, get the solution and retry.
+ return this.showErrorCaptcha( publishResult.edit.captcha )
+ .then( this.publish.bind( this ) );
+ }
+
+ // Any other failure
+ return this.publishFail( publishResult );
+};
+
+/**
+ * @fires publish
+ */
+mw.cx.TargetArticle.prototype.publishComplete = function () {
+ this.publishDeferred.resolve( true );
+ this.emit( 'publish' );
+ // TODO: Event logging the publishing success
+};
+
+/**
+ * Publish failure handler
+ * @param {Object} jqXHR
+ * @param {string} status Text status message
+ * @param {Object|null} data API response data
+ */
+mw.cx.TargetArticle.prototype.publishFail = function ( jqXHR, status, data ) {
+ var editError, editResult;
+
+ // Handle empty response
+ if ( !data ) {
+ this.showErrorEmpty();
+ return;
+ }
+
+ if ( data.exception instanceof Error ) {
+ data.exception = data.exception.toString();
+ }
+
+ editResult = data.edit;
+ if ( editResult ) {
+ // Handle spam blacklist error (either from core or from
Extension:SpamBlacklist)
+ //
{"result":"error","edit":{"spamblacklist":"facebook.com","result":"Failure"}}
+ if ( editResult.spamblacklist ) {
+ this.showErrorSpamBlacklist( data.edit );
+ return;
+ }
+ // Handle abusefilter errors.
+ if ( editResult.code && editResult.code.indexOf( 'abusefilter'
) === 0 ) {
+ this.showErrorAbuseFilter( editResult.info,
editResult.warning );
+ }
+ }
+
+ // Handle token errors
+ editError = data.error;
+ if ( editError && editError.code === 'badtoken' || editError.code ===
'assertuserfailed' ) {
+ this.showErrorBadToken( null, true );
+ return;
+ } else if ( data.error && data.error.code ===
'titleblacklist-forbidden' ) {
+ this.showErrorTitleBlacklist();
+ return;
+ } else if ( data.error && data.error.code === 'readonly' ) {
+ this.showErrorReadOnly( data.error.readonlyreason );
+ return;
+ }
+
+ // Handle captcha
+ // Captcha "errors" usually aren't errors. We simply don't know about
them ahead of time,
+ // so we save once, then (if required) we get an error with a captcha
back and try again after
+ // the user solved the captcha.
+ // TODO: ConfirmEdit API is horrible, there is no reliable way to know
whether it is a "math",
+ // "question" or "fancy" type of captcha. They all expose differently
named properties in the
+ // API for different things in the UI. At this point we only support
the SimpleCaptcha and FancyCaptcha
+ // which we very intuitively detect by the presence of a "url" property.
+ if ( editResult && editResult.captcha && (
+ editResult.captcha.url ||
+ editResult.captcha.type === 'simple' ||
+ editResult.captcha.type === 'math' ||
+ editResult.captcha.type === 'question'
+ ) ) {
+ this.showErrorCaptcha( editResult );
+ return;
+ }
+
+ // Handle (other) unknown and/or unrecoverable errors
+ this.showErrorUnknown( editResult, data );
+ // TODO: Event logging the publishing error
+};
+
+/**
+ * AbuseFilter error handler
+ * @method
+ * @fires publishErrorAbuseFilter
+ */
+mw.cx.TargetArticle.prototype.showErrorAbuseFilter = function () {
+ this.showPublishError( mw.msg( 'cx-publishError-abusefilter' ), true );
+ // Don't disable the publish button. If the action is not disallowed
the user may save the
+ // edit by pressing Publish again. The AbuseFilter API currently has no
way to distinguish
+ // between filter triggers that are and aren't disallowing the action.
+ this.emit( 'publishErrorAbuseFilter' );
+};
+
+/**
+ * Handle publish error with empty API response
+ * @method
+ * @fires publishErrorEmpty
+ */
+mw.cx.TargetArticle.prototype.showErrorEmpty = function () {
+ this.showSaveError( mw.msg( 'cx-publisherror-empty', 'Empty server
response' ), false /* prevents reapply */ );
+ this.emit( 'publishErrorEmpty' );
+};
+
+/**
+ * Handle title blacklist save error
+ *
+ * @method
+ * @fires publishErrorTitleBlacklist
+ */
+mw.cx.TargetArticle.prototype.showErrorTitleBlacklist = function () {
+ this.showPublishError( mw.msg( 'cx-publishError-titleblacklist' ) );
+ this.emit( 'publishErrorTitleBlacklist' );
+};
+
+/**
+ * Handle captcha challenge error
+ *
+ * @method
+ * @param {Object} apiResult publishing api result
+ * @fires publishErrorTitleBlacklist
+ */
+mw.cx.TargetArticle.prototype.showErrorCaptcha = function ( apiResult ) {
+ var $captchaDiv = $( '<div>' ),
+ $captchaParagraph = $( '<p>' );
+
+ // TODO Not tested
+ this.captcha = {
+ input: new OO.ui.TextInputWidget(),
+ id: apiResult.captcha.id
+ };
+ $captchaDiv.append( $captchaParagraph );
+ $captchaParagraph.append(
+ $( '<strong>' ).text( mw.msg( 'captcha-label' ) ),
+ document.createTextNode( mw.msg( 'colon-separator' ) )
+ );
+ if ( apiResult.captcha.url ) {
+ // FancyCaptcha
+ // Based on FancyCaptcha::getFormInformation()
(https://git.io/v6mml) and
+ // ext.confirmEdit.fancyCaptcha.js in the ConfirmEdit extension.
+ mw.loader.load( 'ext.confirmEdit.fancyCaptcha' );
+ $captchaDiv.addClass( 'fancycaptcha-captcha-container' );
+ $captchaParagraph.append(
+ $( $.parseHTML( mw.message( 'fancycaptcha-edit'
).parse() ) )
+ .filter( 'a' ).attr( 'target', '_blank' ).end()
+ );
+ $captchaDiv.append(
+ $( '<img>' ).attr( 'src', apiResult.captcha.url
).addClass( 'fancycaptcha-image' ),
+ ' ',
+ $( '<a>' ).addClass( 'fancycaptcha-reload' ).text(
mw.msg( 'fancycaptcha-reload-text' ) )
+ );
+ } else if ( apiResult.captcha.type === 'simple' ||
apiResult.captcha.type === 'math' ) {
+ // SimpleCaptcha and MathCaptcha
+ $captchaParagraph.append(
+ mw.message( 'captcha-edit' ).parse(),
+ '<br>',
+ document.createTextNode( apiResult.captcha.question )
+ );
+ } else if ( apiResult.captcha.type === 'question' ) {
+ // QuestyCaptcha
+ $captchaParagraph.append(
+ mw.message( 'questycaptcha-edit' ).parse(),
+ '<br>',
+ apiResult.captcha.question
+ );
+ }
+
+ $captchaDiv.append( this.captcha.input.$element );
+
+ // ProcessDialog's error system isn't great for this yet.
+ this.publishDialog.clearMessage( 'api-save-error' );
+ this.publishDialog.showMessage( 'api-save-error', $captchaDiv );
+ this.publishDialog.popPending();
+
+ this.publishDialog.updateSize();
+
+ this.captcha.input.focus();
+
+ this.emit( 'publishErrorCaptcha' );
+};
+
+/**
+ * Handle token fetch errors.
+ *
+ * @method
+ * @param {boolean} [error=false] Whether there was an error trying to figure
out who we're logged in as
+ * @fires publishErrorBadToken
+ */
+mw.cx.TargetArticle.prototype.showErrorBadToken = function ( error ) {
+ var $msg = $( document.createTextNode( mw.msg(
'cx-publisherror-badtoken' ) + ' ' ) );
+
+ if ( error ) {
+ this.emit( 'publishErrorBadToken', false );
+ $msg = $msg.add( document.createTextNode( mw.msg(
'cx-publisherror-identify-trylogin' ) ) );
+ }
+ this.showSaveError( $msg );
+ this.emit( 'publishErrorBadToken', error );
+};
+
+/**
+ * Handle unknown publish error
+ *
+ * @method
+ * @param {Object} editResult
+ * @param {Object|null} data API response data
+ * @fires saveErrorUnknown
+ */
+mw.cx.TargetArticle.prototype.saveErrorUnknown = function ( editResult, data )
{
+ var errorMsg = ( editResult && editResult.info ) || ( data &&
data.error && data.error.info ),
+ errorCode = ( editResult && editResult.code ) || ( data &&
data.error && data.error.code ),
+ unknown = 'Unknown error';
+
+ if ( data.xhr.status !== 200 ) {
+ unknown += ', HTTP status ' + data.xhr.status;
+ }
+
+ this.showPublishError(
+ $( document.createTextNode( errorMsg || errorCode || unknown )
),
+ false // prevents reapply
+ );
+ this.emit( 'publishErrorUnknown', errorCode || errorMsg || unknown );
+};
+
+/**
+ * Show an publish process error message
+ *
+ * @method
+ * @param {string|jQuery|Node[]} msg Message content (string of HTML, jQuery
object or array of
+ * Node objects)
+ * @param {boolean} [allowReapply=true] Whether or not to allow the user to
reapply.
+ * Reset when swapping panels. Assumed to be true unless explicitly set to
false.
+ * @param {boolean} [warning=false] Whether or not this is a warning.
+ */
+mw.cx.TargetArticle.prototype.showPublishError = function ( msg, allowReapply,
warning ) {
+ this.publishDeferred.reject(
+ [ new OO.ui.Error( msg, { recoverable: allowReapply, warning:
warning } ) ]
+ );
+};
+
+/**
+ * Get content for publishing
+ *
+ * @param {boolean} deflate Whether the content need to deflated
+ * @return {string} Content for publishing, may be deflated
+ */
+mw.cx.TargetArticle.prototype.getContent = function ( deflate ) {
+ var content;
+ content = this.translation.getTargetPage().getContent( this.translation
);
+ if ( deflate ) {
+ content = EasyDeflate.deflate( content );
+ }
+ return content;
+};
+
+/**
+ * Increase the version number of a title starting with 1.
+ *
+ * @param {string} title The title to increase the version on.
+ * @return {string}
+ */
+mw.cx.TargetArticle.prototype.increaseVersion = function( title ) {
+ var match, version;
+
+ match = title.match( /^.*\((\d+)\)$/ );
+ if ( match ) {
+ version = parseInt( match[ 1 ], 10 ) + 1;
+
+ return title.replace( /\(\d+\)$/, '(' + version + ')' );
+ }
+
+ return title + ' (1)';
+};
+
+/**
+ * Generate an alternate title in case of title collision.
+ *
+ * @param {string} title The title
+ * @return {string}
+ */
+mw.cx.TargetArticle.prototype.getAlternateTitle = function ( title ) {
+ var username, mwTitle;
+
+ username = mw.user.getName();
+ mwTitle = mw.Title.newFromText( title );
+
+ if ( mwTitle && mwTitle.getNamespaceId() === 2 ) {
+ return this.increaseVersion( title );
+ } else {
+ return 'User:' + username + '/' + title;
+ }
+};
+
+/**
+ * Get categories for the target article
+ *
+ * @return {string[]} Category titles
+ */
+mw.cx.TargetArticle.prototype.getCategories = function () {
+ return this.translation.getTargetPage().getCategories();
+};
+
+/**
+ * Checks to see if there is already a published article with the title.
+ * If exists ask the translator a resolution for the conflict.
+ *
+ * @param {string} title The title to check
+ * @return {jQuery.Promise}
+ */
+mw.cx.TargetArticle.prototype.checkTargetTitle = function ( title ) {
+ return this.isTitleExistInLanguage( this.targetLanguage, title ).then(
function ( titleExists ) {
+ var $dialog;
+
+ if ( !titleExists ) {
+ return title;
+ }
+
+ // Show a dialog to decide what to do now
+ this.view.publishButton.$element.cxPublishingDialog();
+ $dialog = this.translationView.publishButton.$element.data(
'cxPublishingDialog' );
+
+ return $dialog.listen().then( function ( overwrite ) {
+ if ( overwrite ) {
+ return title;
+ }
+
+ return this.getAlternateTitle( title );
+ }.bind( this ) );
+ }.bind( this ) );
+};
+
+/**
+ * Link the source and target articles in the Wikibase repo
+ * @param {string} sourceLanguage
+ * @param {string} targetLanguage
+ * @param {string} sourceTitle
+ * @param {string} targetTitle
+ * @return {jQuery.Promise}
+ */
+mw.cx.TargetArticle.prototype.linkAtWikibase = function ( sourceLanguage,
targetLanguage, sourceTitle, targetTitle ) {
+ var title, sourceApi;
+
+ // Link only pages in the main space
+ title = new mw.Title( targetTitle );
+ if ( title.getNamespaceId() !== 0 ) {
+ return;
+ }
+
+ sourceApi = this.siteMapper.getApi( this.sourceLanguage );
+
+ // TODO: Use action=query&meta=wikibase API
+ // that expose siteid as per
+ // https://gerrit.wikimedia.org/r/#/c/214517/
+ return sourceApi.get( {
+ action: 'query',
+ meta: 'siteinfo',
+ siprop: 'general'
+ } ).then( function ( result ) {
+ /* global wikibase */
+ var repoApi, targetWikiId, sourceWikiId, pageConnector;
+
+ repoApi = new wikibase.api.RepoApi(
wikibase.client.getMwApiForRepo() );
+ targetWikiId = mw.config.get( 'wbCurrentSite' ).globalSiteId;
+ sourceWikiId = result.query.general.wikiid;
+
+ pageConnector = new wikibase.PageConnector(
+ repoApi,
+ targetWikiId,
+ targetTitle,
+ sourceWikiId,
+ sourceTitle
+ );
+
+ return pageConnector.linkPages().then( function () {
+ var api = new mw.Api();
+
+ // Purge the newly-created page after adding the link,
+ // so that they will appear as soon as possible without
manual purging
+ api.post( {
+ action: 'purge',
+ titles: targetTitle
+ } );
+ } );
+ } );
+};
+
+/**
+ * Checks to see if a title exists in the specified language wiki. Returns
+ * the normalised title and resolves redirects.
+ *
+ * @param {string} language The language of the wiki to check
+ * @param {string} title The title to look for
+ * @return {jQuery.promise}
+ * @return {Function} return.done If title exists
+ * @return {string|false} return.done.title
+ */
+mw.cx.TargetArticle.prototype.isTitleExistInLanguage = function ( language,
title ) {
+ var api = this.siteMapper.getApi( language );
+
+ // Short circuit empty titles
+ if ( title === '' ) {
+ return $.Deferred().resolve( false ).promise();
+ }
+
+ // Reject titles with pipe in the name, as it has special meaning in
the api
+ if ( /\|/.test( title ) ) {
+ return $.Deferred().resolve( false ).promise();
+ }
+
+ return api.get( {
+ formatversion: 2,
+ action: 'query',
+ titles: title,
+ redirects: 1
+ } ).then( function ( response ) {
+ var page = response.query.pages[ 0 ];
+
+ if ( page.missing || page.invalid ) {
+ return false;
+ }
+
+ return page.title;
+ } );
+};
+
+/**
+ * Checks for an equivalent page in the target wiki based on source title.
+ *
+ * @param {string} sourceLanguage the source language
+ * @param {string} targetLanguage the target language
+ * @param {string} sourceTitle the title to check
+ * @return {jQuery.promise}
+ */
+mw.cx.TargetArticle.prototype.isTitleConnectedInLanguages = function (
+ sourceLanguage,
+ targetLanguage,
+ sourceTitle
+) {
+ var api = this.siteMapper.getApi( sourceLanguage );
+
+ return api.get( {
+ action: 'query',
+ prop: 'langlinks',
+ titles: sourceTitle,
+ lllang: this.siteMapper.getWikiDomainCode( targetLanguage ),
+ lllimit: 1,
+ redirects: true
+ } ).then( function ( response ) {
+ var equivalentTargetPage = false;
+
+ if ( response.query && response.query.pages ) {
+ $.each( response.query.pages, function ( pageId, page )
{
+ if ( page.langlinks ) {
+ equivalentTargetPage = page.langlinks[
0 ][ '*' ];
+ }
+ } );
+ }
+
+ return equivalentTargetPage;
+ } );
+};
+
+/**
+ * Check whether a page with the same title already exists
+ * and show a warning if needed.
+ */
+mw.cx.TargetArticle.prototype.validateTargetTitle = function () {
+ var viewTargetUrl = this.siteMapper.getPageUrl( this.targetLanguage,
this.targetTitle );
+
+ this.isTitleExistInLanguage( this.targetLanguage, this.targetTitle )
+ .then( function ( pageExist ) {
+ // If page doesn't exist, it's OK
+ if ( !pageExist ) {
+ return;
+ }
+
+ mw.hook( 'mw.cx.warning' ).fire( mw.message(
+ 'cx-translation-target-page-exists',
+ viewTargetUrl,
+ this.targetTitle
+ ) );
+ } );
+};
diff --git a/modules/ui/mw.cx.ui.TranslationView.js
b/modules/ui/mw.cx.ui.TranslationView.js
index c85cf06..c73177d 100644
--- a/modules/ui/mw.cx.ui.TranslationView.js
+++ b/modules/ui/mw.cx.ui.TranslationView.js
@@ -18,6 +18,7 @@
// Parent constructor
mw.cx.ui.TranslationView.parent.call( this, this.config );
this.translation = null;
+ this.targetArticle = null;
this.publishButton = null;
this.init();
this.listen();
@@ -119,7 +120,7 @@
label: mw.msg( 'cx-publish-button' )
} );
this.publishButton.connect( this, {
- click: 'onPublishButtonClick'
+ click: 'publish'
} );
mw.hook( 'mw.cx.progress' ).add( function ( weights ) {
self.publishButton.setDisabled( weights.any === 0 );
@@ -140,22 +141,17 @@
} ).$element );
};
-mw.cx.ui.TranslationView.prototype.onPublishButtonClick = function () {
- this.publish();
-};
-
/**
* Publish the translation
*/
mw.cx.ui.TranslationView.prototype.publish = function () {
- var publisher, self = this;
-
// Disable the trigger button
this.publishButton.setDisabled( true ).setLabel( mw.msg(
'cx-publish-button-publishing' ) );
- publisher = new mw.cx.Publish( this.publishButton,
this.config.siteMapper );
- publisher.publish().always( function () {
- self.publishButton.setDisabled( true ).setLabel( mw.msg(
'cx-publish-button' ) );
- } );
+ this.targetArticle = this.targetArticle ||
+ new mw.cx.TargetArticle( this.translation, this, this.config );
+ this.targetArticle.publish().always( function () {
+ this.publishButton.setDisabled( true ).setLabel( mw.msg(
'cx-publish-button' ) );
+ }.bind( this ) );
};
/**
--
To view, visit https://gerrit.wikimedia.org/r/340961
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I5eab6fec79895591ea0b404196fb30f17452f75a
Gerrit-PatchSet: 12
Gerrit-Project: mediawiki/extensions/ContentTranslation
Gerrit-Branch: master
Gerrit-Owner: Santhosh <[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