jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/326003 )
Change subject: Support section=new in NWE ...................................................................... Support section=new in NWE Bug: T150709 Change-Id: I1002b97060d642df0988da15860a36c13712bade --- M ApiVisualEditor.php M ApiVisualEditorEdit.php M extension.json M modules/ve-mw/i18n/en.json M modules/ve-mw/i18n/qqq.json M modules/ve-mw/init/styles/ve.init.mw.DesktopArticleTarget.css M modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-monobook.css M modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-vector.css M modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.init.js M modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js M modules/ve-mw/init/ve.init.mw.ArticleTarget.js M modules/ve-mw/init/ve.init.mw.ArticleTargetLoader.js M modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js 13 files changed, 179 insertions(+), 50 deletions(-) Approvals: Bartosz Dziewoński: Looks good to me, approved jenkins-bot: Verified diff --git a/ApiVisualEditor.php b/ApiVisualEditor.php index 823314e..09d5e80 100644 --- a/ApiVisualEditor.php +++ b/ApiVisualEditor.php @@ -244,30 +244,36 @@ ]; if ( isset( $params['section'] ) ) { - $apiParams['rvsection'] = $params['section']; + $section = $params['section']; } - $api = new ApiMain( - new DerivativeRequest( - $this->getRequest(), - $apiParams, - false // was posted? - ), - true // enable write? - ); - $api->execute(); - $result = $api->getResult()->getResultData(); - $pid = $title->getArticleID(); - $content = false; - if ( isset( $result['query']['pages'][$pid]['revisions'] ) ) { - foreach ( $result['query']['pages'][$pid]['revisions'] as $revArr ) { - if ( $revArr['revid'] === $oldid ) { - $content = $revArr['content']; + if ( $section === 'new' ) { + $content = ''; + } else { + $apiParams['rvsection'] = $section; + + $api = new ApiMain( + new DerivativeRequest( + $this->getRequest(), + $apiParams, + false // was posted? + ), + true // enable write? + ); + $api->execute(); + $result = $api->getResult()->getResultData(); + $pid = $title->getArticleID(); + $content = false; + if ( isset( $result['query']['pages'][$pid]['revisions'] ) ) { + foreach ( $result['query']['pages'][$pid]['revisions'] as $revArr ) { + if ( $revArr['revid'] === $oldid ) { + $content = $revArr['content']; + } } } - } - if ( $content === false ) { - $this->dieWithError( 'apierror-visualeditor-docserver', 'docserver' ); + if ( $content === false ) { + $this->dieWithError( 'apierror-visualeditor-docserver', 'docserver' ); + } } } diff --git a/ApiVisualEditorEdit.php b/ApiVisualEditorEdit.php index 443fdfb..c63d945 100644 --- a/ApiVisualEditorEdit.php +++ b/ApiVisualEditorEdit.php @@ -405,6 +405,7 @@ ], 'wikitext' => null, 'section' => null, + 'sectiontitle' => null, 'basetimestamp' => null, 'starttimestamp' => null, 'oldid' => null, diff --git a/extension.json b/extension.json index afe3f0a..f6defd1 100644 --- a/extension.json +++ b/extension.json @@ -390,12 +390,15 @@ "parentheses", "readonlywarning", "redirectpagesub", + "subject", "visualeditor-loadwarning", "visualeditor-loadwarning-noconnect", "visualeditor-loadwarning-token", "visualeditor-savedialog-identify-anon", "visualeditor-savedialog-identify-trylogin", - "visualeditor-savedialog-identify-user" + "visualeditor-savedialog-identify-user", + "visualeditor-section-body-placeholder", + "visualeditor-section-title-placeholder" ] }, "ext.visualEditor.mobileArticleTarget": { @@ -512,7 +515,8 @@ "ext.visualEditor.mwcore" ], "messages": [ - "accesskey-save" + "accesskey-save", + "newsectionsummary" ], "targets": [ "desktop", diff --git a/modules/ve-mw/i18n/en.json b/modules/ve-mw/i18n/en.json index d10af76..9996404 100644 --- a/modules/ve-mw/i18n/en.json +++ b/modules/ve-mw/i18n/en.json @@ -44,6 +44,7 @@ "apihelp-visualeditoredit-param-paction": "Action to perform.", "apihelp-visualeditoredit-param-page": "The page to perform actions on.", "apihelp-visualeditoredit-param-section": "The section on which to act.", + "apihelp-visualeditoredit-param-sectiontitle": "Title for new section.", "apihelp-visualeditoredit-param-starttimestamp": "When saving, set this to the timestamp of when the page was loaded. Used to detect edit conflicts.", "apihelp-visualeditoredit-param-summary": "Edit summary.", "apihelp-visualeditoredit-param-watch": "", @@ -355,6 +356,8 @@ "visualeditor-savedialog-warning-dirty": "Your edit may have been corrupted – please review before saving.", "visualeditor-saveerror": "Error saving data to server: $1.", "visualeditor-saveerror-titleblacklist": "Error saving page: This page's title is blacklisted", + "visualeditor-section-body-placeholder": "New section", + "visualeditor-section-title-placeholder": "Subject", "visualeditor-serializeerror": "Error loading data from server: $1.", "visualeditor-settings-tool": "Page settings", "visualeditor-shortcuts-insert": "Insert", diff --git a/modules/ve-mw/i18n/qqq.json b/modules/ve-mw/i18n/qqq.json index 70c9b35..673ca72 100644 --- a/modules/ve-mw/i18n/qqq.json +++ b/modules/ve-mw/i18n/qqq.json @@ -57,6 +57,7 @@ "apihelp-visualeditoredit-param-paction": "{{doc-apihelp-param|visualeditoredit|paction}}", "apihelp-visualeditoredit-param-page": "{{doc-apihelp-param|visualeditoredit|page}}", "apihelp-visualeditoredit-param-section": "{{doc-apihelp-param|visualeditoredit|section}}", + "apihelp-visualeditoredit-param-sectiontitle": "{{doc-apihelp-param|visualeditoredit|sectiontitle}}", "apihelp-visualeditoredit-param-starttimestamp": "{{doc-apihelp-param|visualeditoredit|starttimestamp}}", "apihelp-visualeditoredit-param-summary": "{{doc-apihelp-param|visualeditoredit|summary}}\n{{Identical|Edit summary}}", "apihelp-visualeditoredit-param-watch": "{{doc-apihelp-param|visualeditoredit|watch}}", @@ -368,6 +369,8 @@ "visualeditor-savedialog-warning-dirty": "Note displayed to users in the save dialog if VisualEditor believes that it may have corrupted the page.", "visualeditor-saveerror": "Text shown when the editor fails to save properly.\n\nParameters:\n* $1 is an error message, in English.", "visualeditor-saveerror-titleblacklist": "Text shown when the editor fails to save properly due to the TitleBlacklist extension.", + "visualeditor-section-body-placeholder": "Placeholder for surface when adding a new section", + "visualeditor-section-title-placeholder": "Placeholder for title input when adding a new section", "visualeditor-serializeerror": "Text shown when the editor fails to load the wikitext for saving.\n\nParameters:\n* $1 is an error message, in English.", "visualeditor-settings-tool": "Text of tool in the toolbar the lets users set specific page settings.\n{{Identical|Page settings}}", "visualeditor-shortcuts-insert": "Heading for insertion shortcuts\n{{Identical|Insert}}", diff --git a/modules/ve-mw/init/styles/ve.init.mw.DesktopArticleTarget.css b/modules/ve-mw/init/styles/ve.init.mw.DesktopArticleTarget.css index 9065a74..a70bab3 100644 --- a/modules/ve-mw/init/styles/ve.init.mw.DesktopArticleTarget.css +++ b/modules/ve-mw/init/styles/ve.init.mw.DesktopArticleTarget.css @@ -61,3 +61,18 @@ .ve-init-mw-desktopArticleTarget #bodyContent { /* stylelint-disable-line selector-no-id */ z-index: 1; } + +.ve-ui-init-desktopArticleTarget-sectionTitle { + max-width: none; + margin: 1em 0 0.5em 0; +} + +/* stylelint-disable declaration-no-important */ +.ve-ui-init-desktopArticleTarget-sectionTitle .oo-ui-inputWidget-input { + padding: 0 !important; + border: 0 !important; + outline: 0 !important; + box-shadow: none !important; + line-height: inherit; +} +/* stylelint-enable declaration-no-important */ diff --git a/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-monobook.css b/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-monobook.css index 9e4ec8a..7a4584a 100644 --- a/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-monobook.css +++ b/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-monobook.css @@ -24,3 +24,8 @@ /* Monobook sets a background:url(); rule which overrides the colour. TODO: Fix upstream */ background-color: #e6f1ff; } + +.ve-ui-init-desktopArticleTarget-sectionTitle { + font-size: 150%; + border-bottom: 1px solid #aaa; +} diff --git a/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-vector.css b/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-vector.css index 7e0389c..c7b4095 100644 --- a/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-vector.css +++ b/modules/ve-mw/init/styles/ve.init.mw.DesktopTarget-vector.css @@ -32,3 +32,14 @@ /* Reset -2px from VE-core */ margin-right: 0; } + +.ve-ui-surface-placeholder { + opacity: 0.5; +} + +.ve-ui-init-desktopArticleTarget-sectionTitle { + font-family: 'Linux Libertine', 'Georgia', 'Times', serif; + line-height: 1.3; + font-size: 1.5em; + border-bottom: 1px solid #aaa; +} diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.init.js b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.init.js index 11ff2ef..5e9cbca 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.init.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.init.js @@ -103,7 +103,7 @@ function parseSection( section ) { var parsedSection = section; - // Section must be 'new' (not yet supported) or a number + // Section must be 'new' or a number if ( section !== 'new' ) { parsedSection = +section; if ( isNaN( parsedSection ) ) { @@ -240,7 +240,7 @@ * * @private * @param {string} mode Target mode: 'visual' or 'source' - * @param {number} [section] Section to edit (currently just source mode) + * @param {number|string} [section] Section to edit (currently just source mode) * @param {jQuery.Promise} [targetPromise] Promise that will be resolved with a ve.init.mw.DesktopArticleTarget * @param {boolean} [modified] The page was been modified before loading (e.g. in source mode) */ @@ -478,7 +478,7 @@ $caVeEdit.on( 'click', init.onEditTabClick.bind( init, 'visual' ) ); } if ( pageCanLoadEditor && init.isWikitextAvailable ) { - $caEdit.on( 'click', init.onEditTabClick.bind( init, 'source' ) ); + $caEdit.add( '#ca-addsection' ).on( 'click', init.onEditTabClick.bind( init, 'source' ) ); } // Alter the edit tab (#ca-edit) @@ -581,6 +581,7 @@ }, onEditTabClick: function ( mode, e ) { + var isNewSection; if ( !init.isUnmodifiedLeftClick( e ) ) { return; } @@ -588,7 +589,12 @@ if ( isLoading ) { return; } - if ( active ) { + + isNewSection = !!$( e.target ).closest( '#ca-addsection' ).length; + + if ( isNewSection ) { + this.onEditSectionLinkClick( mode, e, 'new' ); + } else if ( active ) { targetPromise.done( function ( target ) { if ( mode === 'visual' && target.getDefaultMode() === 'source' ) { target.switchToVisualEditor(); @@ -646,8 +652,15 @@ } }, - onEditSectionLinkClick: function ( mode, e ) { - var section, targetPromise; + /** + * Handle section edit links being clicked + * + * @param {string} mode Edit mode + * @param {jQuery.Event} e Click event + * @param {number|string} [section] Override edit section, taken from link URL if not specified + */ + onEditSectionLinkClick: function ( mode, e, section ) { + var targetPromise; if ( !init.isUnmodifiedLeftClick( e ) ) { return; } diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js index e20c935..edc635f 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js @@ -462,11 +462,15 @@ ve.init.mw.DesktopArticleTarget.prototype.afterActivate = function () { $( 'html' ).removeClass( 've-activating' ).addClass( 've-active' ); if ( !this.editingTabDialog ) { - // We have to focus the page after hiding the original content, otherwise - // in firefox the contentEditable container was below the view page, and - // 'focus' scrolled the screen down. - // Support: Firefox - this.getSurface().getView().focus(); + if ( this.sectionTitle ) { + this.sectionTitle.focus(); + } else { + // We have to focus the page after hiding the original content, otherwise + // in firefox the contentEditable container was below the view page, and + // 'focus' scrolled the screen down. + // Support: Firefox + this.getSurface().getView().focus(); + } } }; @@ -475,11 +479,45 @@ */ ve.init.mw.DesktopArticleTarget.prototype.setSurface = function ( surface ) { if ( surface !== this.surface ) { + this.setupNewSection( surface ); this.$editableContent.after( surface.$element ); } // Parent method ve.init.mw.DesktopArticleTarget.super.prototype.setSurface.apply( this, arguments ); +}; + +/** + * Setup new section inputs if required + * + * @param {ve.ui.Surface} surface Surface + */ +ve.init.mw.DesktopArticleTarget.prototype.setupNewSection = function ( surface ) { + if ( surface.getMode() === 'source' && this.section === 'new' ) { + if ( !this.sectionTitle ) { + this.sectionTitle = new OO.ui.TextInputWidget( { + classes: [ 've-ui-init-desktopArticleTarget-sectionTitle' ], + maxLength: 255, + placeholder: ve.msg( 'visualeditor-section-title-placeholder' ) + } ); + this.sectionTitle.connect( this, { change: 'updateToolbarSaveButtonState' } ); + } + surface.setPlaceholder( ve.msg( 'visualeditor-section-body-placeholder' ) ); + this.$editableContent.before( this.sectionTitle.$element ); + } +}; + +/** + * Teardown new section inputs + */ +ve.init.mw.DesktopArticleTarget.prototype.teardownNewSection = function () { + if ( this.getSurface() ) { + this.getSurface().setPlaceholder( '' ); + } + if ( this.sectionTitle ) { + this.sectionTitle.$element.remove(); + this.sectionTitle = null; + } }; /** @@ -506,6 +544,7 @@ if ( this.editingTabDialog ) { this.editingTabDialog.close(); } + this.teardownNewSection(); this.editingTabDialog = null; if ( noDialog || this.activating || !this.edited ) { diff --git a/modules/ve-mw/init/ve.init.mw.ArticleTarget.js b/modules/ve-mw/init/ve.init.mw.ArticleTarget.js index 2beeca6..814f7b1 100644 --- a/modules/ve-mw/init/ve.init.mw.ArticleTarget.js +++ b/modules/ve-mw/init/ve.init.mw.ArticleTarget.js @@ -40,6 +40,7 @@ this.pageExists = mw.config.get( 'wgRelevantArticleId', 0 ) !== 0; this.toolbarScrollOffset = mw.config.get( 'wgVisualEditorToolbarScrollOffset', 0 ); this.section = null; + this.sectionTitle = null; this.checkboxFields = null; this.checkboxesByName = null; @@ -985,16 +986,23 @@ * @fires savePreview */ ve.init.mw.ArticleTarget.prototype.onSaveDialogPreview = function () { - var target = this; + var wikitext, + target = this; if ( !this.saveDialog.$previewViewer.children().length ) { this.emit( 'savePreview' ); this.saveDialog.getActions().setAbilities( { approve: false } ); this.saveDialog.pushPending(); + + wikitext = this.getDocToSave(); + if ( this.sectionTitle && this.sectionTitle.getValue() ) { + wikitext = '== ' + this.sectionTitle.getValue() + ' ==\n\n' + wikitext; + } + new mw.Api().post( { action: 'visualeditor', paction: 'parsefragment', page: mw.config.get( 'wgRelevantPageName' ), - wikitext: this.getDocToSave(), + wikitext: wikitext, pst: true } ).always( function ( response, details ) { if ( ve.getProp( response, 'visualeditor', 'result' ) === 'success' ) { @@ -1004,12 +1012,23 @@ ve.msg( 'visualeditor-loaderror-message', ve.getProp( details, 'error', 'info' ) || 'Failed to connect' ) ).html() ); } - target.getSurface().getModel().getDocument().once( 'transact', - target.saveDialog.clearDiff.bind( target.saveDialog ) - ); + target.bindSaveDialogClearDiff(); } ); } else { this.saveDialog.swapPanel( 'preview' ); + } +}; + +/** + * Clear the save dialog's diff cache when the document changes + */ +ve.init.mw.ArticleTarget.prototype.bindSaveDialogClearDiff = function () { + // Invalidate the viewer wikitext on next change + this.getSurface().getModel().getDocument().once( 'transact', + this.saveDialog.clearDiff.bind( this.saveDialog ) + ); + if ( this.sectionTitle ) { + this.sectionTitle.once( 'change', this.saveDialog.clearDiff.bind( this.saveDialog ) ); } }; @@ -1020,10 +1039,7 @@ * @param {string} wikitext */ ve.init.mw.ArticleTarget.prototype.onSaveDialogReviewComplete = function ( wikitext ) { - // Invalidate the viewer wikitext on next change - this.getSurface().getModel().getDocument().once( 'transact', - this.saveDialog.clearDiff.bind( this.saveDialog ) - ); + this.bindSaveDialogClearDiff(); this.saveDialog.setDiffAndReview( $( '<pre>' ).text( wikitext ) ); }; @@ -1299,11 +1315,15 @@ wikitext: doc, format: 'json' }; - if ( this.section !== null ) { - data.section = this.section; - } postData = ve.extendObject( {}, options, data ); - if ( data.token ) { + if ( this.section !== null ) { + postData.section = this.section; + } + if ( this.sectionTitle ) { + postData.sectiontitle = this.sectionTitle.getValue(); + postData.summary = undefined; + } + if ( postData.token ) { return new mw.Api().post( postData, { contentType: 'multipart/form-data' } ); } return new mw.Api().postWithToken( 'csrf', postData, { contentType: 'multipart/form-data' } ); @@ -1754,6 +1774,9 @@ var isDisabled; this.edited = this.getSurface().getModel().hasBeenModified() || this.fromEditedState; + if ( this.sectionTitle ) { + this.edited = this.edited || this.sectionTitle.getValue() !== ''; + } // Disable the save button if we have no history isDisabled = !this.edited && !this.restoring; this.toolbarSaveButton.setDisabled( isDisabled ); diff --git a/modules/ve-mw/init/ve.init.mw.ArticleTargetLoader.js b/modules/ve-mw/init/ve.init.mw.ArticleTargetLoader.js index 8b62bd9..804f874 100644 --- a/modules/ve-mw/init/ve.init.mw.ArticleTargetLoader.js +++ b/modules/ve-mw/init/ve.init.mw.ArticleTargetLoader.js @@ -108,7 +108,7 @@ * * @param {string} mode Target mode: 'visual' or 'source' * @param {string} pageName Page name to request - * @param {number} [section] Section to edit (currently just source mode) + * @param {number|string} [section] Section to edit, number or 'new' (currently just source mode) * @param {string} [oldid] Old revision ID, current if omitted * @param {string} [targetName] Optional target name for tracking * @param {boolean} [modified] The page was been modified before loading (e.g. in source mode) diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js b/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js index bcfa856..3dabd46 100644 --- a/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js +++ b/modules/ve-mw/ui/dialogs/ve.ui.MWSaveDialog.js @@ -26,7 +26,6 @@ this.restoring = false; this.messages = {}; this.setupDeferred = $.Deferred(); - this.target = null; this.checkboxesByName = null; this.changedEditSummary = false; @@ -262,6 +261,11 @@ // Only show preview in source mode this.actions.forEach( { actions: 'preview' }, function ( action ) { action.toggle( ve.init.target.getSurface().mode === 'source' ); + } ); + + // Diff API doesn't support section=new + this.actions.forEach( { actions: 'review' }, function ( action ) { + action.toggle( !( ve.init.target.getSurface().mode === 'source' && ve.init.target.section === 'new' ) ); } ); mw.hook( 've.saveDialog.stateChanged' ).fire(); @@ -527,7 +531,10 @@ ve.ui.MWSaveDialog.prototype.getSetupProcess = function ( data ) { return ve.ui.MWSaveDialog.super.prototype.getSetupProcess.call( this, data ) .next( function () { - if ( !this.changedEditSummary ) { + if ( ve.init.target.sectionTitle ) { + this.setEditSummary( ve.msg( 'newsectionsummary', ve.init.target.sectionTitle.getValue() ) ); + this.editSummaryInput.setDisabled( true ); + } else if ( !this.changedEditSummary ) { this.setEditSummary( data.editSummary ); } this.setupCheckboxes( data.checkboxFields || [] ); @@ -561,7 +568,6 @@ return ve.ui.MWSaveDialog.super.prototype.getTeardownProcess.call( this, data ) .next( function () { this.emit( 'close' ); - this.target = null; }, this ); }; -- To view, visit https://gerrit.wikimedia.org/r/326003 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1002b97060d642df0988da15860a36c13712bade Gerrit-PatchSet: 8 Gerrit-Project: mediawiki/extensions/VisualEditor Gerrit-Branch: master Gerrit-Owner: Esanders <esand...@wikimedia.org> Gerrit-Reviewer: Alex Monk <a...@wikimedia.org> Gerrit-Reviewer: Bartosz Dziewoński <matma....@gmail.com> Gerrit-Reviewer: DLynch <dly...@wikimedia.org> Gerrit-Reviewer: Esanders <esand...@wikimedia.org> Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits