diff --git a/VisualEditor.hooks.php b/VisualEditor.hooks.php index 3e2546489e..a54997b366 100644 --- a/VisualEditor.hooks.php +++ b/VisualEditor.hooks.php @@ -58,6 +58,54 @@ class VisualEditorHooks { return true; } + /** + * Decide whether to bother showing the wikitext editor at all. + * If not, we expect the VE initialisation JS to activate. + * @param $article Article + * @param $user User + * @return bool Whether to show the wikitext editor or not. + */ + public static function onCustomEditor( Article $article, User $user ) { + $req = RequestContext::getMain()->getRequest(); + $veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' ); + + if ( + !$user->getOption( 'visualeditor-enable' ) || + $user->getOption( 'visualeditor-betatempdisable' ) || + $user->getOption( 'visualeditor-autodisable' ) || + $user->getOption( 'visualeditor-tabs' ) === 'preferwt' || + ( $veConfig->get( 'VisualEditorDisableForAnons' ) && $user->isAnon() ) || + false // TODO: Detect incompatibility - P2373 + ) { + return true; + } + + $title = $article->getTitle(); + + $availableNamespaces = $veConfig->get( 'VisualEditorAvailableNamespaces' ); + + $params = $req->getValueNames(); + + if ( $user->isAnon() ) { + $editor = $req->getCookie( + 'VEE', + '', + User::getDefaultOption( 'visualeditor-editor' ) + ); + } else { + $editor = $user->getOption( 'visualeditor-editor' ); + } + return $req->getVal( 'action' ) !== 'edit' || + !$veConfig->get( 'VisualEditorUseSingleEditTab' ) || + $editor === 'wikitext' || + !$title->inNamespaces( array_keys( array_filter( $availableNamespaces ) ) ) || + $title->getContentModel() !== CONTENT_MODEL_WIKITEXT || + // check for parameters that VE does not handle + in_array( 'preload', $params ) || + in_array( 'editintro', $params ) || + in_array( 'veswitched', $params ); + } + /** * Convert the content model of messages that are actually JSON to JSON. * This only affects validation and UI when saving and editing, not @@ -92,6 +140,42 @@ class VisualEditorHooks { public static function onSkinTemplateNavigation( SkinTemplate &$skin, array &$links ) { $config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' ); + // Exit if there's no edit link for whatever reason (e.g. protected page) + if ( !isset( $links['views']['edit'] ) ) { + return true; + } + + // Exit if we're using the single edit tab. + if ( + $config->get( 'VisualEditorUseSingleEditTab' ) && + $skin->getUser()->getOption( 'visualeditor-tabs' ) !== 'multi-tab' + ) { + $dbr = wfGetDB( DB_SLAVE ); + $user = RequestContext::getMain()->getUser(); + if ( + $config->get( 'VisualEditorUseSingleEditTab' ) && + !$user->isAnon() && + !$user->getOption( 'visualeditor-hidetabdialog' ) && + $user->getOption( 'visualeditor-tabs' ) === 'remember-last' && + $dbr->select( + 'revision', + '1', + array( + 'rev_user' => $user->getId(), + 'rev_timestamp < ' . $dbr->addQuotes( + $config->get( 'VisualEditorSingleEditTabSwitchTime' ) + ) + ), + __METHOD__, + array( 'LIMIT' => 1 ) + )->numRows() === 1 + ) { + wfDebugLog( 'debug', 'ok' ); + $links['views']['edit']['class'] .= ' visualeditor-showtabdialog'; + } + return true; + } + // Exit if the user doesn't have VE enabled if ( !$skin->getUser()->getOption( 'visualeditor-enable' ) || @@ -102,11 +186,6 @@ class VisualEditorHooks { return true; } - // Exit if there's no edit link for whatever reason (e.g. protected page) - if ( !isset( $links['views']['edit'] ) ) { - return true; - } - $availableNamespaces = $config->get( 'VisualEditorAvailableNamespaces' ); $title = $skin->getRelevantTitle(); $namespaceEnabled = $title->inNamespaces( array_keys( array_filter( $availableNamespaces ) ) ); @@ -238,6 +317,14 @@ class VisualEditorHooks { ) { $config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' ); + // Exit if we're using the single edit tab. + if ( + $config->get( 'VisualEditorUseSingleEditTab' ) && + $skin->getUser()->getOption( 'visualeditor-tabs' ) !== 'multi-tab' + ) { + return true; + } + // Exit if we're in parserTests if ( isset( $GLOBALS[ 'wgVisualEditorInParserTests' ] ) ) { return true; @@ -342,9 +429,27 @@ class VisualEditorHooks { 'default' => $user->getOption( 'visualeditor-betatempdisable' ) || $user->getOption( 'visualeditor-autodisable' ) ); + + $config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' ); + if ( $config->get( 'VisualEditorUseSingleEditTab' ) ) { + $preferences['visualeditor-tabs'] = array( + 'type' => 'select', + 'label-message' => 'visualeditor-preference-tabs', + 'section' => 'editing/editor', + 'options' => array( + wfMessage( 'visualeditor-preference-tabs-remember-last' )->escaped() => 'remember-last', + wfMessage( 'visualeditor-preference-tabs-prefer-ve' )->escaped() => 'prefer-ve', + wfMessage( 'visualeditor-preference-tabs-prefer-wt' )->escaped() => 'prefer-wt', + wfMessage( 'visualeditor-preference-tabs-multi-tab' )->escaped() => 'multi-tab' + ) + ); + } + $api = array( 'type' => 'api' ); $preferences['visualeditor-autodisable'] = $api; + $preferences['visualeditor-editor'] = $api; $preferences['visualeditor-hidebetawelcome'] = $api; + $preferences['visualeditor-hidetabdialog'] = $api; $preferences['visualeditor-hideusered'] = $api; $preferences['visualeditor-findAndReplace-findText'] = $api; $preferences['visualeditor-findAndReplace-replaceText'] = $api; @@ -468,6 +573,7 @@ class VisualEditorHooks { 'skins' => $veConfig->get( 'VisualEditorSupportedSkins' ), 'tabPosition' => $veConfig->get( 'VisualEditorTabPosition' ), 'tabMessages' => $veConfig->get( 'VisualEditorTabMessages' ), + 'singleEditTab' => $veConfig->get( 'VisualEditorUseSingleEditTab' ), 'showBetaWelcome' => $veConfig->get( 'VisualEditorShowBetaWelcome' ), 'enableTocWidget' => $veConfig->get( 'VisualEditorEnableTocWidget' ), 'svgMaxSize' => $coreConfig->get( 'SVGMaxSize' ), diff --git a/extension.json b/extension.json index 41b25d35d1..009b578f69 100644 --- a/extension.json +++ b/extension.json @@ -106,7 +106,9 @@ "_merge_strategy": "array_plus" }, "VisualEditorSkinToolbarScrollOffset": [], - "VisualEditorParsoidTimeout": 100 + "VisualEditorParsoidTimeout": 100, + "VisualEditorUseSingleEditTab": false, + "VisualEditorSingleEditTabSwitchTime": 20160101000000 }, "APIModules": { "visualeditor": { @@ -185,6 +187,9 @@ ], "AuthPluginAutoCreate": [ "VisualEditorHooks::onAuthPluginAutoCreate" + ], + "CustomEditor": [ + "VisualEditorHooks::onCustomEditor" ] }, "ResourceModules": { @@ -266,6 +271,7 @@ "mediawiki.Title", "mediawiki.Uri", "mediawiki.util", + "mediawiki.api.options", "user.options", "ext.visualEditor.track" ], @@ -977,7 +983,8 @@ }, "ext.visualEditor.switching": { "scripts": [ - "modules/ve-mw/init/ve.init.MWVESwitchConfirmDialog.js" + "modules/ve-mw/init/ve.init.MWVESwitchConfirmDialog.js", + "modules/ve-mw/init/ve.init.MWEditingTabDialog.js" ], "styles": "modules/ve-mw/init/styles/ve.init.MWVESwitchConfirmDialog.css", "skinStyles": { @@ -999,7 +1006,13 @@ "visualeditor-mweditmodeve-warning", "visualeditor-mweditmodesource-warning-cancel", "visualeditor-mweditmodesource-warning-switch", - "visualeditor-mweditmodesource-warning-switch-discard" + "visualeditor-mweditmodesource-warning-switch-discard", + "visualeditor-editingtabdialog-body", + "visualeditor-editingtabdialog-ok", + "visualeditor-editingtabdialog-title", + "visualeditor-preference-tabs-prefer-ve", + "visualeditor-preference-tabs-prefer-wt", + "visualeditor-preference-tabs-multi-tab" ], "targets": [ "desktop", @@ -1725,7 +1738,10 @@ "visualeditor-enable-experimental": 0, "visualeditor-enable-language": 0, "visualeditor-hidebetawelcome": 0, - "visualeditor-autodisable": 0 + "visualeditor-autodisable": 0, + "visualeditor-tabs": "remember-last", + "visualeditor-editor": "wikitext", + "visualeditor-hidetabdialog": 0 }, "AutoloadClasses": { "ApiVisualEditor": "ApiVisualEditor.php", diff --git a/modules/ve-mw/i18n/en.json b/modules/ve-mw/i18n/en.json index db9a8d30d0..797e131c8c 100644 --- a/modules/ve-mw/i18n/en.json +++ b/modules/ve-mw/i18n/en.json @@ -239,6 +239,9 @@ "visualeditor-differror": "Error loading data from server: $1.", "visualeditor-donebutton-tooltip": "Done editing", "visualeditor-editconflict": "Your changes could not be saved because of an edit conflict. Would {{GENDER:|you}} like to resolve the conflict manually?", + "visualeditor-editingtabdialog-body": "{{SITENAME}} now remembers which editor you used last when you click on the \"$1\" tab.", + "visualeditor-editingtabdialog-ok": "OK", + "visualeditor-editingtabdialog-title": "Editing tabs", "visualeditor-editnotices-tool": "$1 {{PLURAL:$1|notice|notices}}", "visualeditor-editnotices-tooltip": "Edit notices", "visualeditor-editsummary": "Describe what you changed", @@ -297,6 +300,11 @@ "visualeditor-preference-core-info-link": "\/\/mediawiki.org\/wiki\/Special:MyLanguage\/VisualEditor\/Beta_Features\/General", "visualeditor-preference-core-label": "Visual editing", "visualeditor-preference-enable": "Enable the visual editor. It will be available in the following {{PLURAL:$2|namespace|namespaces}}: $1", + "visualeditor-preference-tabs": "Editing tabs", + "visualeditor-preference-tabs-multi-tab": "Show me both editor tabs", + "visualeditor-preference-tabs-prefer-ve": "Always give me the visual editor if possible", + "visualeditor-preference-tabs-prefer-wt": "Always give me the wikitext editor", + "visualeditor-preference-tabs-remember-last": "Remember my last editor", "visualeditor-recreate": "The page has been deleted since you started editing. Press \"$1\" to recreate it.", "visualeditor-reference-input-placeholder": "Search within current citations", "visualeditor-referenceslist-isempty": "There are no references with the group \"$1\" on this page to include in this list.", diff --git a/modules/ve-mw/i18n/qqq.json b/modules/ve-mw/i18n/qqq.json index 87c4c6fc26..366b16ed89 100644 --- a/modules/ve-mw/i18n/qqq.json +++ b/modules/ve-mw/i18n/qqq.json @@ -249,6 +249,9 @@ "visualeditor-differror": "Text shown when the editor fails to load the diff.\n\nParameters:\n* $1 is an error message, in English.", "visualeditor-donebutton-tooltip": "Tooltip text for done editing button in mobile, closing the edit toolbar and blurring the surface.", "visualeditor-editconflict": "Alert message when saving a page causes an edit conflict", + "visualeditor-editingtabdialog-body": "Text shown to users to explain the single edit tab and give them the option to change.\n\nParameters:\n* $1 is the localised label of the edit tab.", + "visualeditor-editingtabdialog-ok": "Label of the button shown to users in the editing tab dialog to close the dialog and leave the default preference.", + "visualeditor-editingtabdialog-title": "Title of the editing tab dialog.", "visualeditor-editnotices-tool": "Text of tool in the toolbar that shows edit notices (such as [[MediaWiki:Editnotice-0]] and [[MediaWiki:Editnotice-8/en]]) as a pop-up.\n\nParameters:\n* $1 - the number of notices\n{{Identical|Notice}}", "visualeditor-editnotices-tooltip": "Text of tooltip for the tool in the toolbar that shows edit notices (i.e. “messages about the editing”), e.g. the “you are not currently logged in” notice", "visualeditor-editsummary": "Label for the edit summary box", @@ -307,6 +310,11 @@ "visualeditor-preference-core-info-link": "{{optional|Used on [[Special:Preferences]] as a link to a page where users can learn about this Beta Feature. Defaults to a page on MediaWiki.org.}}", "visualeditor-preference-core-label": "Used in [[Special:Preferences]].\n\nUsed as label for checkbox to enable VisualEditor.\n\nThe description for this checkbox is:\n* {{msg-mw|Visualeditor-preference-core-description}}", "visualeditor-preference-enable": "Label for the user preference to enable VisualEditor while it is in alpha (opt-in) mode.\nLinks are in {{msg-mw|Visualeditor-mainnamespacepagelink}} and {{msg-mw|visualeditor-usernamespacepagelink}}.\n\nParameters:\n* $1 - Comma separated list of namespace names.\n* $2 - Number of namespaces in which it will be used.\n\nSee also:\n* {{msg-mw|Visualeditor-preference-core-description}}", + "visualeditor-preference-tabs": "Label for the user preference to change how VisualEditor tabs display to the user", + "visualeditor-preference-tabs-multi-tab": "Used in [[Special:Preferences]].\n\nUsed as label for a radio button to have VisualEditor always use a separate tab.", + "visualeditor-preference-tabs-prefer-ve": "Used in [[Special:Preferences]].\n\nUsed as label for a radio button to have VisualEditor be the preferred editor.", + "visualeditor-preference-tabs-prefer-wt": "Used in [[Special:Preferences]].\n\nUsed as label for a radio button to have the wikitext editor be the preferred editor.", + "visualeditor-preference-tabs-remember-last": "Used in [[Special:Preferences]].\n\nUsed as label for a radio button to have VisualEditor remember whether it was the last editor or not.", "visualeditor-recreate": "Text shown when the editor fails to save the page due to it having been deleted since they opened VE. $1 is the message {{msg-mw|ooui-dialog-process-continue}}.", "visualeditor-reference-input-placeholder": "Placeholder text for reference search field: searches existing on-page references.", "visualeditor-referenceslist-isempty": "Message that appears in the references list when there are no references on the page of that group.\n\nParameters:\n* $1 - reference-group name", 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 bd7e893bdb..15f940afbe 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 @@ -18,9 +18,9 @@ * @singleton */ ( function () { - var conf, tabMessages, uri, pageExists, viewUri, veEditUri, isViewPage, pageCanLoadVE, - init, support, targetPromise, enable, tempdisable, autodisable, userPrefEnabled, - initialWikitext, + var conf, tabMessages, uri, pageExists, viewUri, veEditUri, isViewPage, isEditPage, + pageCanLoadVE, init, support, targetPromise, enable, tempdisable, autodisable, + userPrefEnabled, initialWikitext, active = false, progressStep = 0, progressSteps = [ @@ -148,17 +148,23 @@ trackActivateStart( { type: 'page', mechanism: 'click' } ); if ( !active ) { - if ( history.pushState ) { - // Replace the current state with one that is tagged as ours, to prevent the - // back button from breaking when used to exit VE. FIXME: there should be a better - // way to do this. See also similar code in the DesktopArticleTarget constructor. - history.replaceState( { tag: 'visualeditor' }, document.title, uri ); - // Set veaction to edit - history.pushState( { tag: 'visualeditor' }, document.title, veEditUri ); + if ( $( '#ca-edit a' ).data( 'original-text' ) ) { + $( '#ca-edit a' ).text( $( '#ca-edit a' ).data( 'original-text' ) ); } - // Update mw.Uri instance - uri = veEditUri; + if ( uri.query.action !== 'edit' ) { + if ( history.pushState ) { + // Replace the current state with one that is tagged as ours, to prevent the + // back button from breaking when used to exit VE. FIXME: there should be a better + // way to do this. See also similar code in the DesktopArticleTarget constructor. + history.replaceState( { tag: 'visualeditor' }, document.title, uri ); + // Set veaction to edit + history.pushState( { tag: 'visualeditor' }, document.title, veEditUri ); + } + + // Update mw.Uri instance + uri = veEditUri; + } activateTarget(); } @@ -188,6 +194,10 @@ incrementLoadingProgress(); } ); + $.cookie( 'VEE', 'visualeditor', { path: '/', expires: 30 } ); + new mw.Api().saveOption( 'visualeditor-editor', 'visualeditor' ); + mw.user.options.set( 'visualeditor-editor', 'visualeditor' ); + $( 'html' ).addClass( 've-activated ve-loading' ); showLoading(); incrementLoadingProgress(); @@ -218,6 +228,14 @@ mw.libs.ve.activationStart = ve.now(); } + function getLastEditor() { + var editor = $.cookie( 'VEE' ); + if ( !mw.user.isAnon() || !editor ) { + editor = mw.user.options.get( 'visualeditor-editor' ); + } + return editor; + } + conf = mw.config.get( 'wgVisualEditorConfig' ); tabMessages = conf.tabMessages; uri = new mw.Uri(); @@ -229,10 +247,19 @@ mw.config.get( 'wgAction' ) === 'edit' || mw.config.get( 'wgAction' ) === 'submit' ); + isEditPage = conf.singleEditTab && ( + uri.query.action === 'edit' || + uri.query.action === 'submit' + ); // On a view page, extend the current URI so parameters like oldid are carried over // On a non-view page, use viewUri - veEditUri = ( pageCanLoadVE ? uri : viewUri ).clone().extend( { veaction: 'edit' } ); - delete veEditUri.query.action; + if ( conf.singleEditTab ) { + veEditUri = viewUri.clone().extend( { action: 'edit' } ); + delete veEditUri.query.veaction; + } else { + veEditUri = ( pageCanLoadVE ? uri : viewUri ).clone().extend( { veaction: 'edit' } ); + delete veEditUri.query.action; + } support = { es5: !!( @@ -382,22 +409,6 @@ } else if ( pageCanLoadVE ) { // Allow instant switching to edit mode, without refresh $caVeEdit.click( init.onEditTabClick ); - - if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) { - mw.loader.load( 'ext.visualEditor.switching' ); - $( '#wpTextbox1' ).on( 'wikiEditor-toolbar-doneInitialSections', function () { - mw.loader.using( 'ext.visualEditor.switching' ).done( function () { - $( '.wikiEditor-ui-toolbar' ).prepend( - new OO.ui.ButtonWidget( { - framed: false, - icon: 'edit', - title: mw.msg( 'visualeditor-mweditmodeve-tool' ), - classes: [ 've-init-mw-desktopArticleTarget-editSwitch' ] - } ).on( 'click', init.activateVe ).$element - ); - } ); - } ); - } } // Alter the edit tab (#ca-edit) @@ -636,6 +647,9 @@ conf.namespaces ) !== -1 && + // Not on pages like Special:RevisionDelete + mw.config.get( 'wgNamespaceNumber' ) !== -1 && + // Not on pages which are outputs of the Page Translation feature mw.config.get( 'wgTranslatePageTranslation' ) !== 'translation' && @@ -664,36 +678,134 @@ } $( function () { - var currentUri = new mw.Uri( location.href ), - isSection; - - if ( currentUri.query.action === 'edit' && $( '#wpTextbox1' ).length ) { + var key; + if ( uri.query.action === 'edit' && $( '#wpTextbox1' ).length ) { initialWikitext = $( '#wpTextbox1' ).val(); } if ( init.isAvailable ) { - if ( pageCanLoadVE && uri.query.veaction === 'edit' ) { - isSection = uri.query.vesection !== undefined; - - trackActivateStart( { type: isSection ? 'section' : 'page', mechanism: 'url' } ); + if ( + ( + ( isViewPage && uri.query.veaction === 'edit' ) || + ( + isEditPage && + mw.user.options.get( 'visualeditor-tabs' ) !== 'prefer-wt' && + mw.user.options.get( 'visualeditor-tabs' ) !== 'multi-tab' + ) + ) && + uri.query.veswitched === undefined && // TODO: other params too? + ( + ( + mw.user.options.get( 'visualeditor-tabs' ) === 'prefer-ve' && + mw.config.get( 'wgAction' ) !== 'submit' + ) || getLastEditor() !== 'wikitext' + ) + ) { + trackActivateStart( { + type: uri.query.vesection === undefined ? 'page' : 'section', + mechanism: 'url' + } ); activateTarget(); } + + if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) { + mw.loader.load( 'ext.visualEditor.switching' ); + $( '#wpTextbox1' ).on( 'wikiEditor-toolbar-doneInitialSections', function () { + mw.loader.using( 'ext.visualEditor.switching' ).done( function () { + var windowManager, editingTabDialog; + $( '.wikiEditor-ui-toolbar' ).prepend( + new OO.ui.ButtonWidget( { + framed: false, + icon: 'edit', + title: mw.msg( 'visualeditor-mweditmodeve-tool' ), + classes: [ 've-init-mw-desktopArticleTarget-editSwitch' ] + } ).on( 'click', init.activateVe ).$element + ); + + // Duplicate of this code in ve.init.mw.DesktopArticleTarget.js + if ( $( '#ca-edit' ).hasClass( 'visualeditor-showtabdialog' ) ) { + // Set up a temporary window manager + windowManager = new OO.ui.WindowManager(); + $( 'body' ).append( windowManager.$element ); + editingTabDialog = new mw.libs.ve.EditingTabDialog(); + windowManager.addWindows( [ editingTabDialog ] ); + windowManager.openWindow( editingTabDialog, { message: mw.msg( + 'visualeditor-editingtabdialog-body', + $( '#ca-edit' ).text() + ) } ) + .then( function ( opened ) { return opened; } ) + .then( function ( closing ) { return closing; } ) + .then( function ( data ) { + // Detach the temporary window manager + windowManager.destroy(); + + if ( data && data.action === 'prefer-ve' ) { + location.href = veEditUri; + } + } ); + } + } ); + } ); + } + if ( + conf.singleEditTab && + mw.user.options.get( 'visualeditor-tabs' ) !== 'multi-tab' && + userPrefEnabled + ) { + // Allow instant switching to edit mode, without refresh + $( '#ca-edit' ).click( function ( e ) { + if ( + mw.user.options.get( 'visualeditor-tabs' ) === 'prefer-ve' || + ( + getLastEditor() !== 'wikitext' && + mw.user.options.get( 'visualeditor-tabs' ) !== 'prefer-wt' + ) + ) { + trackActivateStart( { type: 'page', mechanism: 'click' } ); + activateTarget(); + e.preventDefault(); + } + } ); + } else if ( userPrefEnabled ) { + init.setupSkin(); + } } - if ( userPrefEnabled ) { - init.setupSkin(); + if ( + conf.singleEditTab && + mw.user.options.get( 'visualeditor-tabs' ) === 'multi-tab' && + userPrefEnabled && + ( + init.isAvailable && + !mw.user.isAnon() && + getLastEditor() === 'wikitext' + ) || ( + !init.isAvailable && + mw.user.options.get( 'visualeditor-tabs' ) === 'prefer-ve' + ) + ) { + key = pageExists ? 'edit' : 'create'; + if ( $( '#ca-view-foreign' ).length ) { + key += 'localdescription'; + } + key += 'source'; + if ( tabMessages[ key ] !== null ) { + $( '#ca-edit a' ) + .data( 'original-text', $( '#ca-edit a' ).text() ) + .text( mw.msg( tabMessages[ key ] ) ); + } } - if ( currentUri.query.venotify ) { + if ( uri.query.venotify ) { // The following messages can be used here: // postedit-confirmation-saved // postedit-confirmation-created // postedit-confirmation-restored mw.hook( 'postEdit' ).fire( { - message: mw.msg( 'postedit-confirmation-' + currentUri.query.venotify, mw.user ) + message: mw.msg( 'postedit-confirmation-' + uri.query.venotify, mw.user ) } ); - delete currentUri.query.venotify; + delete uri.query.venotify; } } ); }() ); 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 73a964037d..234867b412 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js @@ -232,12 +232,35 @@ ve.init.mw.DesktopArticleTarget.prototype.setupLocalNoticeMessages = function () * @inheritdoc */ ve.init.mw.DesktopArticleTarget.prototype.loadSuccess = function ( response ) { - var $checkboxes, defaults, data, + var $checkboxes, defaults, data, windowManager, editingTabDialog, target = this; // Parent method ve.init.mw.DesktopArticleTarget.super.prototype.loadSuccess.apply( this, arguments ); + // Duplicate of this code in ve.init.mw.DesktopArticleTarget.init.js + if ( $( '#ca-edit' ).hasClass( 'visualeditor-showtabdialog' ) ) { + // Set up a temporary window manager + windowManager = new OO.ui.WindowManager(); + $( 'body' ).append( windowManager.$element ); + editingTabDialog = new mw.libs.ve.EditingTabDialog(); + windowManager.addWindows( [ editingTabDialog ] ); + windowManager.openWindow( editingTabDialog, { message: mw.msg( + 'visualeditor-editingtabdialog-body', + $( '#ca-edit' ).text() + ) } ) + .then( function ( opened ) { return opened; } ) + .then( function ( closing ) { return closing; } ) + .then( function ( data ) { + // Detach the temporary window manager + windowManager.destroy(); + + if ( data && data.action === 'prefer-wt' ) { + target.switchToWikitextEditor( true, false ); + } + } ); + } + data = response ? response.visualeditor : {}; this.checkboxFields = []; @@ -989,6 +1012,12 @@ ve.init.mw.DesktopArticleTarget.prototype.transformPage = function () { // separate tab sections for content actions and namespaces the below is a no-op. $( '#p-views' ).find( 'li.selected' ).removeClass( 'selected' ); $( '#ca-ve-edit' ).addClass( 'selected' ); + if ( + mw.config.get( 'wgVisualEditorConfig' ).singleEditTab && + mw.user.options.get( 'visualeditor-tabs' ) !== 'multi-tab' + ) { + $( '#ca-edit' ).addClass( 'selected' ); + } mw.hook( 've.activate' ).fire(); @@ -997,12 +1026,23 @@ ve.init.mw.DesktopArticleTarget.prototype.transformPage = function () { // Push veaction=edit url in history (if not already. If we got here by a veaction=edit // permalink then it will be there already and the constructor called #activate) - if ( !this.actFromPopState && history.pushState && this.currentUri.query.veaction !== 'edit' ) { + if ( + !this.actFromPopState && + history.pushState && + this.currentUri.query.veaction !== 'edit' && + this.currentUri.query.action !== 'edit' + ) { // Set the current URL uri = this.currentUri; - uri.query.veaction = 'edit'; - delete uri.query.action; - mw.config.set( 'wgAction', 'view' ); + + if ( mw.config.get( 'wgVisualEditorConfig' ).singleEditTab ) { + uri.query.action = 'edit'; + mw.config.set( 'wgAction', 'edit' ); + } else { + uri.query.veaction = 'edit'; + delete uri.query.action; + mw.config.set( 'wgAction', 'view' ); + } history.pushState( this.popState, document.title, uri ); } @@ -1019,6 +1059,12 @@ ve.init.mw.DesktopArticleTarget.prototype.restorePage = function () { // selected. We didn't deselect the namespace tab, so we're ready after deselecting #ca-ve-edit. // In skins having #ca-view (like Vector), select that. $( '#ca-ve-edit' ).removeClass( 'selected' ); + if ( + mw.config.get( 'wgVisualEditorConfig' ).singleEditTab && + mw.user.options.get( 'visualeditor-tabs' ) !== 'multi-tab' + ) { + $( '#ca-edit' ).removeClass( 'selected' ); + } $( '#ca-view' ).addClass( 'selected' ); mw.hook( 've.deactivate' ).fire(); @@ -1329,6 +1375,9 @@ ve.init.mw.DesktopArticleTarget.prototype.onUnload = function () { */ ve.init.mw.DesktopArticleTarget.prototype.switchToWikitextEditor = function ( discardChanges, modified ) { var target = this; + $.cookie( 'VEE', 'wikitext', { path: '/', expires: 30 } ); + new mw.Api().saveOption( 'visualeditor-editor', 'wikitext' ); + mw.user.options.set( 'visualeditor-editor', 'wikitext' ); if ( discardChanges ) { if ( modified ) { ve.track( 'mwedit.abort', { type: 'switchwithout', mechanism: 'navigate' } ); diff --git a/modules/ve-mw/init/ve.init.MWEditingTabDialog.js b/modules/ve-mw/init/ve.init.MWEditingTabDialog.js new file mode 100644 index 0000000000..fd9018bca5 --- /dev/null +++ b/modules/ve-mw/init/ve.init.MWEditingTabDialog.js @@ -0,0 +1,86 @@ +/*! + * VisualEditor user interface MWEditingTabDialog class. + * + * @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +mw.libs.ve = mw.libs.ve || {}; +/** + * Dialog for allowing new users to change editing tab preferences to VisualEditor. + * + * @class + * @extends OO.ui.MessageDialog + * + * @constructor + * @param {Object} [config] Configuration options + */ +mw.libs.ve.EditingTabDialog = function MWLibsVEMWEditingTabDialog( config ) { + // Parent constructor + mw.libs.ve.EditingTabDialog.super.call( this, config ); +}; + +/* Inheritance */ + +OO.inheritClass( mw.libs.ve.EditingTabDialog, OO.ui.MessageDialog ); + +/* Static Properties */ + +mw.libs.ve.EditingTabDialog.static.name = 'editingtab'; + +mw.libs.ve.EditingTabDialog.static.size = 'medium'; + +mw.libs.ve.EditingTabDialog.static.verbose = true; + +mw.libs.ve.EditingTabDialog.static.icon = 'help'; + +mw.libs.ve.EditingTabDialog.static.title = mw.msg( 'visualeditor-editingtabdialog-title' ); + +mw.libs.ve.EditingTabDialog.static.actions = [ + + { + action: 'prefer-wt', + label: mw.msg( 'visualeditor-preference-tabs-prefer-wt' ) + }, + { + action: 'prefer-ve', + label: mw.msg( 'visualeditor-preference-tabs-prefer-ve' ) + }, + { + action: 'multi-tab', + label: mw.msg( 'visualeditor-preference-tabs-multi-tab' ) + }, + { + label: mw.msg( 'visualeditor-editingtabdialog-ok' ), + flags: [ 'progressive', 'primary' ] + } +]; + +/* Methods */ + +/** + * @inheritdoc + */ +mw.libs.ve.EditingTabDialog.prototype.getSetupProcess = function ( action ) { + return mw.libs.ve.EditingTabDialog.super.prototype.getSetupProcess.call( this, action ) + .next( function () { + new mw.Api().saveOption( 'visualeditor-hidetabdialog', 1 ); + mw.user.options.set( 'visualeditor-hidetabdialog', 1 ); + } ); +}; + +/** + * @inheritdoc + */ +mw.libs.ve.EditingTabDialog.prototype.getActionProcess = function ( action ) { + if ( action ) { + return new OO.ui.Process( function () { + new mw.Api().saveOption( 'visualeditor-tabs', action ); + mw.user.options.set( 'visualeditor-tabs', action ); + this.close( { action: action } ); + }, this ); + } else { + // Parent method + return mw.libs.ve.EditingTabDialog.super.prototype.getActionProcess.call( this, action ); + } +};