diff --git a/.eslintignore b/.eslintignore index df482daf18..670f54e68f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,6 +6,6 @@ /docs/ # Language files written automatically by TranslateWiki -/i18n/**/*.json -!/i18n/**/en.json -!/i18n/**/qqq.json +**/i18n/**/*.json +!**/i18n/**/en.json +!**/i18n/**/qqq.json diff --git a/extension.json b/extension.json index 3f07b04c0c..18a133ebc0 100644 --- a/extension.json +++ b/extension.json @@ -180,6 +180,10 @@ "value": false, "description": "For testing only. Tag edits for the Edit Check project." }, + "VisualEditorEditCheck": { + "value": false, + "description": "Enable experimental Edit Check feature." + }, "VisualEditorUseSingleEditTab": { "value": false } @@ -220,7 +224,8 @@ "i18n/ve-mw", "i18n/ve-mw/api", "i18n/ve-mw/mwlanguagevariant", - "i18n/ve-wmf" + "i18n/ve-wmf", + "modules/editcheck/i18n" ] }, "ExtensionMessagesFiles": { @@ -604,10 +609,32 @@ "ext.visualEditor.editCheck": { "group": "visualEditorA", "scripts": [ - "modules/editcheck/init.js" + "modules/editcheck/init.js", + "modules/editcheck/EditCheckContextItem.js", + "modules/editcheck/EditCheckInspector.js" + ], + "styles": [ + "modules/editcheck/EditCheck.less" + ], + "dependencies": [ + "ext.visualEditor.core", + "ext.visualEditor.mwsave" + ], + "messages": [ + "editcheck-dialog-action-no", + "editcheck-dialog-action-yes", + "editcheck-dialog-addref-description", + "editcheck-dialog-addref-reject-question", + "editcheck-dialog-addref-reject-description", + "editcheck-dialog-addref-reject-no-info", + "editcheck-dialog-addref-reject-already-cited", + "editcheck-dialog-addref-reject-not-sure", + "editcheck-dialog-addref-reject-other", + "editcheck-dialog-addref-success-notify", + "editcheck-dialog-addref-title", + "editcheck-dialog-title", + "visualeditor-backbutton-tooltip" ], - "dependencies": [], - "messages": [], "targets": [ "desktop", "mobile" diff --git a/includes/Hooks.php b/includes/Hooks.php index 1b23718480..6f15f5e0dd 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -1085,6 +1085,7 @@ class Hooks implements TextSlotDiffRendererTablePrefixHook { ), 'useChangeTagging' => $veConfig->get( 'VisualEditorUseChangeTagging' ), 'editCheckTagging' => $veConfig->get( 'VisualEditorEditCheckTagging' ), + 'editCheck' => $veConfig->get( 'VisualEditorEditCheck' ), 'namespacesWithSubpages' => $namespacesWithSubpagesEnabled, 'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ), 'rebaserUrl' => $coreConfig->get( 'VisualEditorRebaserURL' ), diff --git a/modules/editcheck/EditCheck.less b/modules/editcheck/EditCheck.less new file mode 100644 index 0000000000..ec4559c447 --- /dev/null +++ b/modules/editcheck/EditCheck.less @@ -0,0 +1,35 @@ +/* Toolbar */ + +.ve-ui-toolbar-group-title { + font-weight: bold; + flex: 5 !important; /* stylelint-disable-line declaration-no-important */ + line-height: 3em; +} + +/* Context item */ + +.ve-ui-editCheckContextItem { + > .ve-ui-linearContextItem-head { + background: #fce7fe; + } + + &-actions { + margin-top: 16px; + } +} + +/* Selections */ + +.ve-ce-surface-selections-editCheck .ve-ce-surface-selection { + opacity: 0.2; +} + +.ve-ce-surface-selections-editCheck .ve-ce-surface-selection > div { + mix-blend-mode: darken; + // Adjust target colours to account for 50% opacity + background: ( #fce7fe - 0.8 * ( #fff ) ) / 0.2; + // border: 1px solid ( ( #d02aac - 0.8 * ( #fff ) ) / 0.2 ); + border-radius: 2px; + padding: 2px; + margin: -2px 0 0 -2px; +} diff --git a/modules/editcheck/EditCheckContextItem.js b/modules/editcheck/EditCheckContextItem.js new file mode 100644 index 0000000000..ea2a55cea8 --- /dev/null +++ b/modules/editcheck/EditCheckContextItem.js @@ -0,0 +1,126 @@ +/*! + * VisualEditor EditCheckContextItem class. + * + * @copyright 2011-2019 VisualEditor Team and others; see http://ve.mit-license.org + */ + +/** + * Context item shown after a rich text paste. + * + * @class + * @extends ve.ui.PersistentContextItem + * + * @constructor + * @param {ve.ui.LinearContext} context Context the item is in + * @param {ve.dm.Model} model Model the item is related to + * @param {Object} [config] + */ +ve.ui.EditCheckContextItem = function VeUiEditCheckContextItem() { + // Parent constructor + ve.ui.EditCheckContextItem.super.apply( this, arguments ); + + // Initialization + this.$element.addClass( 've-ui-editCheckContextItem' ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.EditCheckContextItem, ve.ui.PersistentContextItem ); + +/* Static Properties */ + +ve.ui.EditCheckContextItem.static.name = 'editCheck'; + +ve.ui.EditCheckContextItem.static.icon = 'quotes'; + +ve.ui.EditCheckContextItem.static.label = OO.ui.deferMsg( 'editcheck-dialog-addref-title' ); + +/* Methods */ + +/** + * @inheritdoc + */ +ve.ui.EditCheckContextItem.prototype.renderBody = function () { + // Prompt panel + var acceptButton = new OO.ui.ButtonWidget( { + label: ve.msg( 'editcheck-dialog-action-yes' ), + icon: 'check' + } ); + var rejectButton = new OO.ui.ButtonWidget( { + label: ve.msg( 'editcheck-dialog-action-no' ), + icon: 'close' + } ); + + acceptButton.connect( this, { click: 'onAcceptClick' } ); + rejectButton.connect( this, { click: 'onRejectClick' } ); + + // HACK: Suppress close button on mobile context + if ( this.context.isMobile() ) { + this.context.closeButton.toggle( false ); + } + + this.$body.append( + $( '

' ).text( ve.msg( 'editcheck-dialog-addref-description' ) ), + $( '

' ).addClass( 've-ui-editCheckContextItem-actions' ).append( + acceptButton.$element, rejectButton.$element + ) + ); +}; + +ve.ui.EditCheckContextItem.prototype.close = function ( data ) { + // HACK: Un-suppress close button on mobile context + if ( this.context.isMobile() ) { + this.context.closeButton.toggle( true ); + } + this.data.saveProcessDeferred.resolve( data ); +}; + +ve.ui.EditCheckContextItem.prototype.onAcceptClick = function () { + var contextItem = this; + var fragment = this.data.fragment; + var windowAction = ve.ui.actionFactory.create( 'window', this.context.getSurface() ); + fragment.collapseToEnd().select(); + + windowAction.open( 'citoid' ).then( function ( instance ) { + return instance.closing; + } ).then( function ( data ) { + if ( !data ) { + // Reference was not inserted - re-open this context + setTimeout( function () { + // Deactivate again for mobile after teardown has modified selections + contextItem.context.getSurface().getView().deactivate(); + contextItem.context.afterContextChange(); + }, 500 ); + } else { + // Edit check inspector is already closed by this point, but + // we need to end the workflow. + contextItem.close( data ); + } + } ); +}; + +ve.ui.EditCheckContextItem.prototype.onRejectClick = function () { + var contextItem = this; + var windowAction = ve.ui.actionFactory.create( 'window', this.context.getSurface() ); + windowAction.open( + 'editCheckInspector', + { + fragment: this.data.fragment, + saveProcessDeferred: this.data.saveProcessDeferred + } + ).then( function ( instance ) { + // contextItem.openingCitoid = false; + return instance.closing; + } ).then( function ( data ) { + if ( !data ) { + // Form was closed, re-open this context + contextItem.context.afterContextChange(); + } else { + contextItem.close( data ); + } + } ); +}; + +/* Registration */ + +ve.ui.contextItemFactory.register( ve.ui.EditCheckContextItem ); diff --git a/modules/editcheck/EditCheckInspector.js b/modules/editcheck/EditCheckInspector.js new file mode 100644 index 0000000000..4ab2ee1c75 --- /dev/null +++ b/modules/editcheck/EditCheckInspector.js @@ -0,0 +1,151 @@ +/*! + * VisualEditor UserInterface EditCheckInspector class. + * + * @copyright 2011-2020 VisualEditor Team and others; see http://ve.mit-license.org + */ + +/** + * Edit check inspector + * + * @class + * @extends ve.ui.FragmentInspector + * + * @constructor + * @param {Object} [config] Configuration options + */ +ve.ui.EditCheckInspector = function VeUiEditCheckInspector( config ) { + // Parent constructor + ve.ui.EditCheckInspector.super.call( this, config ); + + // Pre-initialization + this.$element.addClass( 've-ui-editCheckInspector' ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.EditCheckInspector, ve.ui.FragmentInspector ); + +ve.ui.EditCheckInspector.static.name = 'editCheckInspector'; + +// ve.ui.EditCheckInspector.static.title = OO.ui.deferMsg( 'editcheck-dialog-title' ); +ve.ui.EditCheckInspector.static.title = OO.ui.deferMsg( 'editcheck-dialog-addref-title' ); + +// ve.ui.EditCheckInspector.static.size = 'context'; + +ve.ui.EditCheckInspector.static.actions = [ + { + label: OO.ui.deferMsg( 'visualeditor-dialog-action-cancel' ), + flags: [ 'safe', 'back' ], + modes: [ 'mobile', 'desktop' ] + }, + { + action: 'continue', + icon: 'next', + flags: [ 'primary', 'progressive' ], + modes: [ 'mobile' ] + } +]; + +/* Methods */ + +/** + * @inheritdoc + */ +ve.ui.EditCheckInspector.prototype.initialize = function () { + // Parent method + ve.ui.EditCheckInspector.super.prototype.initialize.call( this ); + + // Survey panel + this.answerRadioSelect = new OO.ui.RadioSelectWidget( { + items: [ + new OO.ui.RadioOptionWidget( { + data: 'no-info', + label: ve.msg( 'editcheck-dialog-addref-reject-no-info' ) + } ), + new OO.ui.RadioOptionWidget( { + data: 'already-cited', + label: ve.msg( 'editcheck-dialog-addref-reject-already-cited' ) + } ), + new OO.ui.RadioOptionWidget( { + data: 'not-sure', + label: ve.msg( 'editcheck-dialog-addref-reject-not-sure' ) + } ), + new OO.ui.RadioOptionWidget( { + data: 'other', + label: ve.msg( 'editcheck-dialog-addref-reject-other' ) + } ) + ] + } ); + this.answerRadioSelect.connect( this, { select: 'updateActions' } ); + + this.answerConfirm = new OO.ui.ButtonWidget( { + flags: [ 'progressive' ], + framed: false, + label: 'Continue', + disabled: true + } ); + this.answerConfirm.toggle( !OO.ui.isMobile() ); + this.answerConfirm.connect( this, { click: [ 'executeAction', 'continue' ] } ); + + this.form.addItems( + new OO.ui.FieldsetLayout( { + label: ve.msg( 'editcheck-dialog-addref-reject-question' ), + items: [ + new OO.ui.FieldLayout( this.answerRadioSelect, { + label: ve.msg( 'editcheck-dialog-addref-reject-description' ), + align: 'top' + } ), + new OO.ui.FieldLayout( this.answerConfirm, { + align: 'left' + } ) + ] + } ) + ); +}; + +ve.ui.EditCheckInspector.prototype.updateActions = function () { + this.answerConfirm.setDisabled( !this.answerRadioSelect.findSelectedItem() ); +}; + +/** + * @inheritdoc + */ +ve.ui.EditCheckInspector.prototype.getSetupProcess = function ( data ) { + data = data || {}; + return ve.ui.EditCheckInspector.super.prototype.getSetupProcess.call( this, data ) + .first( function () { + this.surface = data.surface; + this.saveProcessDeferred = data.saveProcessDeferred; + this.answerRadioSelect.selectItem( null ); + }, this ); +}; + +/** + * @inheritdoc + */ +ve.ui.EditCheckInspector.prototype.getReadyProcess = function ( data ) { + return ve.ui.EditCheckInspector.super.prototype.getReadyProcess.call( this, data ) + .first( function () { + this.actions.setMode( OO.ui.isMobile() ? 'mobile' : 'desktop' ); + }, this ); +}; + +ve.ui.EditCheckInspector.prototype.getActionProcess = function ( action ) { + if ( action === '' ) { + return new OO.ui.Process( function () { + this.close(); + }, this ); + } + + if ( action === 'continue' ) { + return new OO.ui.Process( function () { + this.close( { action: 'reject', reason: this.answerRadioSelect.findSelectedItem().getData() } ); + }, this ); + } + + return ve.ui.EditCheckInspector.super.prototype.getActionProcess.call( this, action ); +}; + +/* Registration */ + +ve.ui.windowFactory.register( ve.ui.EditCheckInspector ); diff --git a/modules/editcheck/i18n/en.json b/modules/editcheck/i18n/en.json new file mode 100644 index 0000000000..fa8dac812f --- /dev/null +++ b/modules/editcheck/i18n/en.json @@ -0,0 +1,14 @@ +{ + "editcheck-dialog-action-no": "No", + "editcheck-dialog-action-yes": "Yes", + "editcheck-dialog-addref-description": "Help readers understand where this information is coming from by adding a citation.", + "editcheck-dialog-addref-reject-question": "Why are you not adding a citation?", + "editcheck-dialog-addref-reject-description": "Other editors would value learning more about your decision to dismiss the citation.", + "editcheck-dialog-addref-reject-no-info": "I didn't add new information", + "editcheck-dialog-addref-reject-already-cited": "My changes are already cited earlier", + "editcheck-dialog-addref-reject-not-sure": "I'm not sure what citation to add", + "editcheck-dialog-addref-reject-other": "Other", + "editcheck-dialog-addref-success-notify": "Thank you for adding a citation!", + "editcheck-dialog-addref-title": "Add a citation", + "editcheck-dialog-title": "Before publishing" +} diff --git a/modules/editcheck/i18n/fr.json b/modules/editcheck/i18n/fr.json new file mode 100644 index 0000000000..7aa3b5510d --- /dev/null +++ b/modules/editcheck/i18n/fr.json @@ -0,0 +1,14 @@ +{ + "editcheck-dialog-action-no": "Non", + "editcheck-dialog-action-yes": "Oui", + "editcheck-dialog-addref-description": "Aidez les lecteurs à comprendre d'où proviennent ces informations en ajoutant une source.", + "editcheck-dialog-addref-reject-question": "Pourquoi n'ajoutez-vous pas une source ?", + "editcheck-dialog-addref-reject-description": "Les autres éditeurs aimeraient en savoir plus sur votre décision de ne pas ajouter une source.", + "editcheck-dialog-addref-reject-no-info": "Je n'ai pas ajouté de nouvelles informations", + "editcheck-dialog-addref-reject-already-cited": "Mes modifications sont déjà sourcées plus haut", + "editcheck-dialog-addref-reject-not-sure": "Je ne sais pas quelle source ajouter", + "editcheck-dialog-addref-reject-other": "Autre", + "editcheck-dialog-addref-success-notify": "Merci d'avoir ajouté une source !", + "editcheck-dialog-addref-title": "Ajouter une source", + "editcheck-dialog-title": "Avant de publier" +} diff --git a/modules/editcheck/i18n/qqq.json b/modules/editcheck/i18n/qqq.json new file mode 100644 index 0000000000..3b123f7400 --- /dev/null +++ b/modules/editcheck/i18n/qqq.json @@ -0,0 +1,14 @@ +{ + "editcheck-dialog-action-no": "Label for the no option when asking users if they want to add a citation.", + "editcheck-dialog-action-yes": "Label for the no option when asking users if they want to add a citation.", + "editcheck-dialog-addref-description": "Help text explaining why it is helpful to add a citation.", + "editcheck-dialog-addref-reject-question": "Heading for form question asking why the user didn't add a citation.", + "editcheck-dialog-addref-reject-description": "Help text for form question asking why the user didn't add a citation.", + "editcheck-dialog-addref-reject-no-info": "Answer option in repsonse to {{msg-mw|editcheck-dialog-addref-reject-question}}", + "editcheck-dialog-addref-reject-already-cited": "Answer option in repsonse to {{msg-mw|editcheck-dialog-addref-reject-question}}", + "editcheck-dialog-addref-reject-not-sure": "Answer option in repsonse to {{msg-mw|editcheck-dialog-addref-reject-question}}", + "editcheck-dialog-addref-reject-other": "Answer option in repsonse to {{msg-mw|editcheck-dialog-addref-reject-question}}", + "editcheck-dialog-addref-success-notify": "Notification messages shown after a citation is added successfully.", + "editcheck-dialog-addref-title": "Title for the edit check context asking user to add a citation.", + "editcheck-dialog-title": "Title shown in the toolbar while the user is in the add a citation workflow." +} diff --git a/modules/editcheck/init.js b/modules/editcheck/init.js index b2009aad8e..7cb270f6a3 100644 --- a/modules/editcheck/init.js +++ b/modules/editcheck/init.js @@ -1,20 +1,20 @@ mw.editcheck = {}; /** - * Check if added content in the document model might need a reference + * Find added content in the document model that might need a reference * * @param {ve.dm.DocumentModel} documentModel Document model * @param {boolean} [includeReferencedContent] Include content ranges that already * have a reference. - * @return {boolean} + * @return {ve.dm.Selection[]} Content ranges that might need a reference */ -mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeReferencedContent ) { +mw.editcheck.findAddedContentNeedingReference = function ( documentModel, includeReferencedContent ) { if ( mw.config.get( 'wgNamespaceNumber' ) !== mw.config.get( 'wgNamespaceIds' )[ '' ] ) { - return false; + return []; } if ( !documentModel.completeHistory.getLength() ) { - return false; + return []; } var operations; try { @@ -23,7 +23,7 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe // TransactionSquasher can sometimes throw errors; until T333710 is // fixed just count this as not needing a reference. mw.errorLogger.logError( err, 'error.visualeditor' ); - return false; + return []; } var ranges = []; @@ -45,7 +45,7 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe // Reached the end of the doc / start of internal list, stop searching return offset < endOffset; } ); - return ranges.some( function ( range ) { + var addedTextRanges = ranges.filter( function ( range ) { var minimumCharacters = 50; // 1. Check that at least minimumCharacters characters have been inserted sequentially if ( range.getLength() >= minimumCharacters ) { @@ -66,6 +66,10 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe } return false; } ); + + return addedTextRanges.map( function ( range ) { + return new ve.dm.LinearSelection( range ); + } ); }; /** @@ -118,3 +122,152 @@ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheckTagging ) { }; } ); } + +if ( mw.config.get( 'wgVisualEditorConfig' ).editCheck ) { + mw.hook( 've.preSaveProcess' ).add( function ( saveProcess, target ) { + var surface = target.getSurface(); + + var selections = mw.editcheck.findAddedContentNeedingReference( surface.getModel().getDocument() ); + + if ( selections.length ) { + var surfaceView = surface.getView(); + var toolbar = target.getToolbar(); + var reviewToolbar = new ve.ui.PositionedTargetToolbar( target, target.toolbarConfig ); + reviewToolbar.setup( [ + { + name: 'back', + type: 'bar', + include: [ 'editCheckBack' ] + }, + // Placeholder toolbar groups + // TODO: Make a proper TitleTool? + { + name: 'title', + type: 'bar', + include: [] + }, + { + name: 'save', + // TODO: MobileArticleTarget should ignore 'align' + align: OO.ui.isMobile() ? 'before' : 'after', + type: 'bar', + include: [ 'showSaveDisabled' ] + } + ], surface ); + + reviewToolbar.items[ 1 ].$element.removeClass( 'oo-ui-toolGroup-empty' ); + reviewToolbar.items[ 1 ].$group.append( + $( '' ).addClass( 've-ui-editCheck-toolbar-title' ).text( ve.msg( 'editcheck-dialog-title' ) ) + ); + if ( OO.ui.isMobile() ) { + reviewToolbar.$element.addClass( 've-init-mw-mobileArticleTarget-toolbar' ); + } + target.toolbar.$element.before( reviewToolbar.$element ); + target.toolbar = reviewToolbar; + + var selection = selections[ 0 ]; + var highlightNodes = surfaceView.getDocument().selectNodes( selection.getCoveringRange(), 'branches' ).map( function ( spec ) { + return spec.node; + } ); + + surfaceView.drawSelections( 'editCheck', [ ve.ce.Selection.static.newFromModel( selection, surfaceView ) ] ); + surfaceView.setReviewMode( true, highlightNodes ); + toolbar.toggle( false ); + target.onContainerScroll(); + + saveProcess.next( function () { + var saveProcessDeferred = ve.createDeferred(); + var fragment = surface.getModel().getFragment( selection, true ); + + var context = surface.getContext(); + + // Select the found content to correctly the context on desktop + fragment.select(); + // Deactivate to prevent selection suppressing mobile context + surface.getView().deactivate(); + + context.addPersistentSource( { + embeddable: false, + data: { + fragment: fragment, + saveProcessDeferred: saveProcessDeferred + }, + name: 'editCheck' + } ); + + // Once the context is positioned, clear the selection + setTimeout( function () { + surface.getModel().setNullSelection(); + } ); + + return saveProcessDeferred.promise().then( function ( data ) { + context.removePersistentSource( 'editCheck' ); + + surfaceView.drawSelections( 'editCheck', [] ); + surfaceView.setReviewMode( false ); + + reviewToolbar.$element.remove(); + toolbar.toggle( true ); + target.toolbar = toolbar; + target.onContainerScroll(); + + // Check the user inserted a citation + if ( data && data.action ) { + if ( data.action !== 'reject' ) { + mw.notify( ve.msg( 'editcheck-dialog-addref-success-notify' ), { type: 'success' } ); + } + var delay = ve.createDeferred(); + // If they inserted, wait 2 seconds on desktop before showing save dialog + setTimeout( function () { + delay.resolve(); + }, !OO.ui.isMobile() && data.action !== 'reject' ? 2000 : 0 ); + return delay.promise(); + } else { + return ve.createDeferred().reject().promise(); + } + } ); + } ); + } + } ); +} + +ve.ui.EditCheckBack = function VeUiEditCheckBack() { + // Parent constructor + ve.ui.EditCheckBack.super.apply( this, arguments ); + + this.setDisabled( false ); +}; +OO.inheritClass( ve.ui.EditCheckBack, ve.ui.Tool ); +ve.ui.EditCheckBack.static.name = 'editCheckBack'; +ve.ui.EditCheckBack.static.icon = 'previous'; +ve.ui.EditCheckBack.static.autoAddToCatchall = false; +ve.ui.EditCheckBack.static.autoAddToGroup = false; +ve.ui.EditCheckBack.static.title = + OO.ui.deferMsg( 'visualeditor-backbutton-tooltip' ); +ve.ui.EditCheckBack.prototype.onSelect = function () { + var context = this.toolbar.getSurface().getContext(); + if ( context.inspector ) { + context.inspector.close(); + } else { + context.items[ 0 ].close(); + } + this.setActive( false ); +}; +ve.ui.EditCheckBack.prototype.onUpdateState = function () { + this.setDisabled( false ); +}; +ve.ui.toolFactory.register( ve.ui.EditCheckBack ); + +ve.ui.EditCheckSaveDisabled = function VeUiEditCheckSaveDisabled() { + // Parent constructor + ve.ui.EditCheckSaveDisabled.super.apply( this, arguments ); +}; +OO.inheritClass( ve.ui.EditCheckSaveDisabled, ve.ui.MWSaveTool ); +ve.ui.EditCheckSaveDisabled.static.name = 'showSaveDisabled'; +ve.ui.EditCheckSaveDisabled.static.autoAddToCatchall = false; +ve.ui.EditCheckSaveDisabled.static.autoAddToGroup = false; +ve.ui.EditCheckSaveDisabled.prototype.onUpdateState = function () { + this.setDisabled( true ); +}; + +ve.ui.toolFactory.register( ve.ui.EditCheckSaveDisabled ); diff --git a/modules/ve-mw/init/targets/ve.init.mw.ArticleTarget.js b/modules/ve-mw/init/targets/ve.init.mw.ArticleTarget.js index 770c757ff4..1e413e452a 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.ArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.ArticleTarget.js @@ -1548,11 +1548,11 @@ ve.init.mw.ArticleTarget.prototype.save = function ( doc, options ) { ) { var documentModel = this.getSurface().getModel().getDocument(); // New content needing a reference - if ( mw.editcheck.doesAddedContentNeedReference( documentModel ) ) { + if ( mw.editcheck.findAddedContentNeedingReference( documentModel ).length ) { taglist.push( 'editcheck-references' ); } // New content, regardless of if it needs a reference - if ( mw.editcheck.doesAddedContentNeedReference( documentModel, true ) ) { + if ( mw.editcheck.findAddedContentNeedingReference( documentModel, true ).length ) { taglist.push( 'editcheck-newcontent' ); } } diff --git a/modules/ve-mw/preinit/ve.init.mw.ArticleTargetLoader.js b/modules/ve-mw/preinit/ve.init.mw.ArticleTargetLoader.js index 3e4bf0be7c..785bc8e1cd 100644 --- a/modules/ve-mw/preinit/ve.init.mw.ArticleTargetLoader.js +++ b/modules/ve-mw/preinit/ve.init.mw.ArticleTargetLoader.js @@ -37,7 +37,7 @@ modules.push( 'ext.visualEditor.mwwikitext' ); } - if ( conf.editCheckTagging ) { + if ( conf.editCheckTagging || conf.editCheck ) { modules.push( 'ext.visualEditor.editCheck' ); }