mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-23 14:06:52 +00:00
Implement add a reference edit check
Change-Id: I4cebc5bbaa34300d1c5bb5fde8277269b14779c9
This commit is contained in:
parent
228596cba0
commit
3ece481e71
|
@ -6,6 +6,6 @@
|
||||||
/docs/
|
/docs/
|
||||||
|
|
||||||
# Language files written automatically by TranslateWiki
|
# Language files written automatically by TranslateWiki
|
||||||
/i18n/**/*.json
|
**/i18n/**/*.json
|
||||||
!/i18n/**/en.json
|
!**/i18n/**/en.json
|
||||||
!/i18n/**/qqq.json
|
!**/i18n/**/qqq.json
|
||||||
|
|
|
@ -180,6 +180,10 @@
|
||||||
"value": false,
|
"value": false,
|
||||||
"description": "For testing only. Tag edits for the Edit Check project."
|
"description": "For testing only. Tag edits for the Edit Check project."
|
||||||
},
|
},
|
||||||
|
"VisualEditorEditCheck": {
|
||||||
|
"value": false,
|
||||||
|
"description": "Enable experimental Edit Check feature."
|
||||||
|
},
|
||||||
"VisualEditorUseSingleEditTab": {
|
"VisualEditorUseSingleEditTab": {
|
||||||
"value": false
|
"value": false
|
||||||
}
|
}
|
||||||
|
@ -218,7 +222,8 @@
|
||||||
"i18n/ve-mw",
|
"i18n/ve-mw",
|
||||||
"i18n/ve-mw/api",
|
"i18n/ve-mw/api",
|
||||||
"i18n/ve-mw/mwlanguagevariant",
|
"i18n/ve-mw/mwlanguagevariant",
|
||||||
"i18n/ve-wmf"
|
"i18n/ve-wmf",
|
||||||
|
"modules/editcheck/i18n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ExtensionMessagesFiles": {
|
"ExtensionMessagesFiles": {
|
||||||
|
@ -598,10 +603,32 @@
|
||||||
"ext.visualEditor.editCheck": {
|
"ext.visualEditor.editCheck": {
|
||||||
"group": "visualEditorA",
|
"group": "visualEditorA",
|
||||||
"scripts": [
|
"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": [
|
"targets": [
|
||||||
"desktop",
|
"desktop",
|
||||||
"mobile"
|
"mobile"
|
||||||
|
|
|
@ -1113,6 +1113,7 @@ class Hooks implements TextSlotDiffRendererTablePrefixHook {
|
||||||
),
|
),
|
||||||
'useChangeTagging' => $veConfig->get( 'VisualEditorUseChangeTagging' ),
|
'useChangeTagging' => $veConfig->get( 'VisualEditorUseChangeTagging' ),
|
||||||
'editCheckTagging' => $veConfig->get( 'VisualEditorEditCheckTagging' ),
|
'editCheckTagging' => $veConfig->get( 'VisualEditorEditCheckTagging' ),
|
||||||
|
'editCheck' => $veConfig->get( 'VisualEditorEditCheck' ),
|
||||||
'namespacesWithSubpages' => $namespacesWithSubpagesEnabled,
|
'namespacesWithSubpages' => $namespacesWithSubpagesEnabled,
|
||||||
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
|
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
|
||||||
'rebaserUrl' => $coreConfig->get( 'VisualEditorRebaserURL' ),
|
'rebaserUrl' => $coreConfig->get( 'VisualEditorRebaserURL' ),
|
||||||
|
|
35
modules/editcheck/EditCheck.less
Normal file
35
modules/editcheck/EditCheck.less
Normal file
|
@ -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;
|
||||||
|
}
|
126
modules/editcheck/EditCheckContextItem.js
Normal file
126
modules/editcheck/EditCheckContextItem.js
Normal file
|
@ -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(
|
||||||
|
$( '<p>' ).text( ve.msg( 'editcheck-dialog-addref-description' ) ),
|
||||||
|
$( '<div>' ).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 );
|
151
modules/editcheck/EditCheckInspector.js
Normal file
151
modules/editcheck/EditCheckInspector.js
Normal file
|
@ -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 );
|
14
modules/editcheck/i18n/en.json
Normal file
14
modules/editcheck/i18n/en.json
Normal file
|
@ -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"
|
||||||
|
}
|
14
modules/editcheck/i18n/fr.json
Normal file
14
modules/editcheck/i18n/fr.json
Normal file
|
@ -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"
|
||||||
|
}
|
14
modules/editcheck/i18n/qqq.json
Normal file
14
modules/editcheck/i18n/qqq.json
Normal file
|
@ -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."
|
||||||
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
mw.editcheck = {};
|
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 {ve.dm.DocumentModel} documentModel Document model
|
||||||
* @param {boolean} [includeReferencedContent] Include content ranges that already
|
* @param {boolean} [includeReferencedContent] Include content ranges that already
|
||||||
* have a reference.
|
* 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' )[ '' ] ) {
|
if ( mw.config.get( 'wgNamespaceNumber' ) !== mw.config.get( 'wgNamespaceIds' )[ '' ] ) {
|
||||||
return false;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !documentModel.completeHistory.getLength() ) {
|
if ( !documentModel.completeHistory.getLength() ) {
|
||||||
return false;
|
return [];
|
||||||
}
|
}
|
||||||
var operations;
|
var operations;
|
||||||
try {
|
try {
|
||||||
|
@ -23,7 +23,7 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe
|
||||||
// TransactionSquasher can sometimes throw errors; until T333710 is
|
// TransactionSquasher can sometimes throw errors; until T333710 is
|
||||||
// fixed just count this as not needing a reference.
|
// fixed just count this as not needing a reference.
|
||||||
mw.errorLogger.logError( err, 'error.visualeditor' );
|
mw.errorLogger.logError( err, 'error.visualeditor' );
|
||||||
return false;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var ranges = [];
|
var ranges = [];
|
||||||
|
@ -45,7 +45,7 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe
|
||||||
// Reached the end of the doc / start of internal list, stop searching
|
// Reached the end of the doc / start of internal list, stop searching
|
||||||
return offset < endOffset;
|
return offset < endOffset;
|
||||||
} );
|
} );
|
||||||
return ranges.some( function ( range ) {
|
var addedTextRanges = ranges.filter( function ( range ) {
|
||||||
var minimumCharacters = 50;
|
var minimumCharacters = 50;
|
||||||
// 1. Check that at least minimumCharacters characters have been inserted sequentially
|
// 1. Check that at least minimumCharacters characters have been inserted sequentially
|
||||||
if ( range.getLength() >= minimumCharacters ) {
|
if ( range.getLength() >= minimumCharacters ) {
|
||||||
|
@ -66,6 +66,10 @@ mw.editcheck.doesAddedContentNeedReference = function ( documentModel, includeRe
|
||||||
}
|
}
|
||||||
return false;
|
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(
|
||||||
|
$( '<span>' ).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 );
|
||||||
|
|
|
@ -1548,11 +1548,11 @@ ve.init.mw.ArticleTarget.prototype.save = function ( doc, options ) {
|
||||||
) {
|
) {
|
||||||
var documentModel = this.getSurface().getModel().getDocument();
|
var documentModel = this.getSurface().getModel().getDocument();
|
||||||
// New content needing a reference
|
// New content needing a reference
|
||||||
if ( mw.editcheck.doesAddedContentNeedReference( documentModel ) ) {
|
if ( mw.editcheck.findAddedContentNeedingReference( documentModel ).length ) {
|
||||||
taglist.push( 'editcheck-references' );
|
taglist.push( 'editcheck-references' );
|
||||||
}
|
}
|
||||||
// New content, regardless of if it needs a reference
|
// 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' );
|
taglist.push( 'editcheck-newcontent' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
modules.push( 'ext.visualEditor.mwwikitext' );
|
modules.push( 'ext.visualEditor.mwwikitext' );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( conf.editCheckTagging ) {
|
if ( conf.editCheckTagging || conf.editCheck ) {
|
||||||
modules.push( 'ext.visualEditor.editCheck' );
|
modules.push( 'ext.visualEditor.editCheck' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue