diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/Ajax.xml b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/Ajax.xml deleted file mode 100644 index 72bc2d6f8d3a813c200b9de7a1e46feb77415359..0000000000000000000000000000000000000000 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/Ajax.xml +++ /dev/null @@ -1,131 +0,0 @@ -<?xml version="1.1" encoding="UTF-8"?> - -<!-- - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ---> - -<xwikidoc version="1.4" reference="RTFrontend.Ajax" locale=""> - <web>RTFrontend</web> - <name>Ajax</name> - <language/> - <defaultLanguage/> - <translation>0</translation> - <creator>xwiki:XWiki.Admin</creator> - <parent>xwiki:XWiki.WebHome</parent> - <author>xwiki:XWiki.Admin</author> - <contentAuthor>xwiki:XWiki.Admin</contentAuthor> - <version>1.1</version> - <title>Realtime Ajax Merge</title> - <comment/> - <minorEdit>false</minorEdit> - <syntaxId>xwiki/2.1</syntaxId> - <hidden>true</hidden> - <content>{{velocity wiki=false}} -#* - This script provides a serverside merge API which allows realtime sessions to peacefully coexist with simultaneous - non-realtime sessions. It expects the client to post its current realtime session content, the name of the relevant - document, and the version of its latest common ancestor (the last known savepoint). -*# -## -#set ($result = {}) -#set ($result.error = false) -#set ($CR = $util.decodeURI("%0D")) -## -#if ($request.version) - ## - ## the version the client expects to be its latest common ancestor - #set ($previousVersion = $request.get('version')) - ## the language of the document - #set ($language = $request.get('language')) - ## the name of the document - #set ($documentName = $request.get('document')) - ## the realtime editor's content - #set ($currentContent = $request.get('content')) - ## - #set ($nextDocument = $xwiki.getDocument($documentName).getTranslatedDocument($language)) - #set ($nextContent = $nextDocument.content) - #set ($previousDocument = $nextDocument.getDocumentRevision($previousVersion)) - #set ($previousContent = $previousDocument.content) - ## - ## Check if the translation in the requested language exists. - ## If the translation does not exist, i.e. "getTranslatedDocument" is the default document, it means it's a new translation. - ## In that case we have to set the contents to "" - #if ($language != "default" && $nextDocument.locale != $language) - #set ($nextContent = '') - #set ($previousContent = '') - #end - ## - #if ("$!{request.convertHTML}" == '1') - #set ($xdom = $services.rendering.parse($currentContent, 'xhtml/1.0')) - #set ($currentContent = $services.rendering.render($xdom, $nextDocument.syntax.toIdString())) - #end - ## - #set ($result.latestVersion = $previousVersion) - #set ($result.currentVersion = $nextDocument.version) - #set ($result.previousVersionContent = $nextDocument.content) ##TODO : convert if needed - ## - ## We don't need to merge if the content has not been changed outside of the real-time session (previous == next) or - ## if the changes done outside of the real-time session are the same as those done within the real-time session - ## (current == next). - #if ($previousContent == $nextContent || $currentContent == $nextContent) - #set ($result.content = $currentContent) - #set ($result.merged = false) - #set ($result.conflicts = false) - #set ($result.saveRequired = $currentContent != $nextContent) - ## Otherwise we need to perform a 3-way merge. - #else - #set ($placeholder = " ${escapetool.getNewline()} ") - ## - #set ($previousArray = $previousContent.replace($CR, '').replace($escapetool.getNewline(), $placeholder).split(' ')) - #set ($currentArray = $currentContent.replace($CR, '').replace($escapetool.getNewline(), $placeholder).split(' ')) - #set ($nextArray = $nextContent.replace($CR, '').replace($escapetool.getNewline(), $placeholder).split(' ')) - ## - ## $services.diff.merge() expects lists so we need to convert the arrays. - #set ($previousList = $previousArray.subList(0, $previousArray.size())) - #set ($currentList = $currentArray.subList(0, $currentArray.size())) - #set ($nextList = $nextArray.subList(0, $nextArray.size())) - #set ($mergeResult = $services.diff.merge($previousList, $nextList, $currentList, null)) - ## - #set ($mergedContent = $stringtool.join($mergeResult.merged, ' ').replace($placeholder, $escapetool.getNewline())) - ## - #if ("$!{request.convertHTML}" == '1') - ## FIXME: This requires programming rights. - #set ($discard = $xcontext.setDoc($nextDocument.document)) - #set ($source = { - 'content': $mergedContent, - 'syntax': $nextDocument.syntax, - 'documentReference': $nextDocument.documentReference - }) - ## FIXME: We shouldn't depend on a particular WYSIWYG editor. The code should work with the configured WYSIWYG - ## editor, whatever that may be. - #set ($mergedContent = $xwiki.getDocument('CKEditor.ContentSheet').getRenderedContent().replaceAll( - "([\t\r\n]|(\s{2,}))", '')) - #end - ## - #set ($result.content = $mergedContent) - #set ($result.merged = true) - #set ($result.conflicts = $mergeResult.log) - #set ($result.saveRequired = true) - #end -#end -#if ($xcontext.action == 'get') - #jsonResponse($result) -#end -{{/velocity}}</content> -</xwikidoc> diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/ConvertHTML.xml b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/ConvertHTML.xml deleted file mode 100644 index a9065d7cb5da5225c6e582531f1d905fa90e24ae..0000000000000000000000000000000000000000 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/main/resources/RTFrontend/ConvertHTML.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.1" encoding="UTF-8"?> - -<!-- - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ---> - -<xwikidoc version="1.5" reference="RTFrontend.ConvertHTML" locale=""> - <web>RTFrontend</web> - <name>ConvertHTML</name> - <language/> - <defaultLanguage/> - <translation>0</translation> - <creator>xwiki:XWiki.Admin</creator> - <parent>xwiki:XWiki.WebHome</parent> - <author>xwiki:XWiki.Admin</author> - <contentAuthor>xwiki:XWiki.Admin</contentAuthor> - <version>1.1</version> - <title>Realtime HTML Converter</title> - <comment/> - <minorEdit>false</minorEdit> - <syntaxId>xwiki/2.1</syntaxId> - <hidden>true</hidden> - <content>{{velocity wiki=false}} -## TODO: We should pass directly the document reference. -#set ($wiki = "$!request.getParameter('wiki')") -#set ($space = "$!request.getParameter('space')") -#set ($page = "$!request.getParameter('page')") -#set ($documentReference = $services.model.createDocumentReference($wiki, $space, $page)) -#set ($document = $xwiki.getDocument($documentReference)) -## FIXME: This requires programming rights! -#set ($discard = $xcontext.setDoc($document.document)) -## The $source variable is used inside the content sheet. -#set ($source = { - 'content': $request.text, - 'syntax': $document.syntax, - 'documentReference': $documentReference -}) -#if ($xcontext.action == 'get') - ## Check that the CSRF token matches the user. - #if (!$services.csrf.isTokenValid($request.form_token)) - $response.sendError(403, $services.localization.render('rtfFrontend.convertHtml.invalidCsrfToken')) - #else - ## FIXME: We shouldn't depend on CKEditor. This code should work independent of the configured WYSIWYG editor. - $xwiki.getDocument('CKEditor.ContentSheet').getRenderedContent() - #end -#end -{{/velocity}}</content> -</xwikidoc> diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/test/java/org/xwiki/realtime/ui/ConvertHTMLPageTest.java b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/test/java/org/xwiki/realtime/ui/ConvertHTMLPageTest.java deleted file mode 100644 index dde6b3d7cf049d1d45280a0b39d2228373c82354..0000000000000000000000000000000000000000 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-ui/src/test/java/org/xwiki/realtime/ui/ConvertHTMLPageTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.xwiki.realtime.ui; - -import javax.inject.Provider; - -import org.jsoup.nodes.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.xwiki.csrf.script.CSRFTokenScriptService; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.rendering.RenderingScriptServiceComponentList; -import org.xwiki.rendering.internal.configuration.DefaultRenderingConfigurationComponentList; -import org.xwiki.script.service.ScriptService; -import org.xwiki.test.page.HTML50ComponentList; -import org.xwiki.test.page.PageTest; -import org.xwiki.test.page.XWikiSyntax21ComponentList; - -import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.user.api.XWikiRightService; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RenderingScriptServiceComponentList -@DefaultRenderingConfigurationComponentList -@HTML50ComponentList -@XWikiSyntax21ComponentList -class ConvertHTMLPageTest extends PageTest -{ - - private static final String WIKI_NAME = "xwiki"; - - private static final String XWIKI_SPACE = "RTFrontend"; - - private static final DocumentReference RTF_FRONTEND_CONVERT_HTML = - new DocumentReference(WIKI_NAME, XWIKI_SPACE, "ConvertHTML"); - - private static final String CSRF_TOKEN = "a0a0a0a0"; - - private CSRFTokenScriptService tokenService; - - @BeforeEach - void setUp() throws Exception - { - // Mock the Token Service to get a consistent CSRF token throughout the tests. - this.tokenService = this.oldcore.getMocker().registerMockComponent(ScriptService.class, "csrf", - CSRFTokenScriptService.class, true); - when(this.tokenService.isTokenValid(CSRF_TOKEN)).thenReturn(true); - - this.xwiki.initializeMandatoryDocuments(this.context); - - this.context = mock(XWikiContext.class); - Provider<XWikiContext> xcontextProvider = - this.componentManager.registerMockComponent(XWikiContext.TYPE_PROVIDER); - when(xcontextProvider.get()).thenReturn(this.context); - when(this.context.getRequest()).thenReturn(this.request); - when(this.context.getResponse()).thenReturn(this.response); - when(this.context.getWiki()).thenReturn(this.xwiki); - - // Fake programming access level to display the complete page. - XWikiRightService rightService = this.oldcore.getMockRightService(); - when(this.xwiki.getRightService()).thenReturn(rightService); - when(rightService.hasProgrammingRights(this.context)).thenReturn(true); - this.response = spy(this.response); - when(this.context.getResponse()).thenReturn(this.response); - } - - @Test - void checkValidCSRFToken() throws Exception - { - when(this.context.getAction()).thenReturn("get"); - this.request.put("text", "Hello"); - this.request.put("form_token", CSRF_TOKEN); - Document result = renderHTMLPage(RTF_FRONTEND_CONVERT_HTML); - - verify(this.response, never()).setStatus(anyInt()); - verify(this.tokenService).isTokenValid(CSRF_TOKEN); - assertEquals("$xwiki.getDocument('CKEditor.ContentSheet').getRenderedContent()", - result.getElementsByTag("body").text()); - } - - @Test - void checkInvalidCSRFToken() throws Exception - { - String wrongToken = "wrong_token"; - when(this.context.getAction()).thenReturn("get"); - this.request.put("text", "Hello"); - this.request.put("form_token", wrongToken); - Document result = renderHTMLPage(RTF_FRONTEND_CONVERT_HTML); - - verify(this.response).sendError(403, "rtfFrontend.convertHtml.invalidCsrfToken"); - verify(this.tokenService).isTokenValid(wrongToken); - assertEquals("", result.getElementsByTag("body").text()); - } -} diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-webjar/src/main/webjar/saver.js b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-webjar/src/main/webjar/saver.js index 82839c4137607c294a95927559e63055849125eb..338dceec9f8f25b8586f738aad30ed11826d82e5 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-webjar/src/main/webjar/saver.js +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-webjar/src/main/webjar/saver.js @@ -95,89 +95,6 @@ define('xwiki-realtime-saver', [ return false; }, - /** - * Retrieves attributes about the local document for the purposes of AJAX merge (just data-xwiki-document and - * lastSaved.version). - */ - getDocumentStatistics = function() { - var $html = $('html'), - fields = [ - 'wiki', - 'document' // includes space and page - ], - result = {}; - - // We can't rely on people pushing the new lastSaved.version. If they quit before ISAVED other clients won't get the - // new version. This isn't such an issue, because they _will_ converge eventually. - result.version = lastSaved.version; - - fields.forEach(function (field) { - result[field] = $html.data('xwiki-' + field); - }); - - result.language = doc.language; - - return result; - }, - - ajaxMerge = function(content, cb) { - // version, document - var stats = getDocumentStatistics(); - - stats.content = content; - if (mainConfig.isHTML) { - stats.convertHTML = 1; - } - - verbose('Posting with the following stats:'); - verbose(stats); - - $.ajax({ - url: new XWiki.Document('Ajax', 'RTFrontend').getURL('get', 'xpage=plain&outputSyntax=plain'), - method: 'POST', - dataType: 'json', - success: function (data) { - try { - // Data is already an "application/json". - var merge = data; - var error = merge.conflicts && merge.conflicts.length && merge.conflicts[0].formattedMessage; - if (error) { - merge.error = error; - cb(error, merge); - } else { - // Let the callback handle textarea writes. - cb(null, merge); - } - } catch (err) { - var debugLog = { - state: 'ajaxMerge/parseError', - lastSavedVersion: lastSaved.version, - lastSavedContent: lastSaved.content, - cUser: mainConfig.userName, - mergeData: data, - error: err - }; - ErrorBox.show('parse', JSON.stringify(debugLog)); - warn(err); - cb(err, data); - } - }, - data: stats, - error: function (err) { - var debugLog = { - state: 'ajaxMerger/velocityError', - lastSavedVersion: lastSaved.version, - lastSavedContent: lastSaved.content, - cContent: content, - cUser: mainConfig.userName, - err: err - }; - ErrorBox.show('velocity', JSON.stringify(debugLog)); - warn(err); - }, - }); - }, - bumpVersion = function(callback, versionData) { var success = function(doc) { debug('Triggering lastSaved refresh on remote clients.'); @@ -246,22 +163,6 @@ define('xwiki-realtime-saver', [ }); }, - presentMergeDialog = function(question, labelDefault, choiceDefault, labelAlternative, choiceAlternative) { - var behave = { - onYes: choiceDefault, - onNo: choiceAlternative - }; - - var param = { - confirmationText: question, - yesButtonText: labelDefault, - noButtonText: labelAlternative, - showCancelButton: true - }; - - new XWiki.widgets.ConfirmationBox(behave, param); - }, - destroyDialog = Saver.destroyDialog = function(callback) { var $box = $('.xdialog-box.xdialog-box-confirmation'), $content = $box.find('.xdialog-content'); @@ -283,154 +184,6 @@ define('xwiki-realtime-saver', [ lastSaved.wasEditedLocally = condition; }, - resolveMergeConflictsPromise = function(merge, deferred) { - // There was a merge conflict we'll need to resolve. - warn(merge.error); - - // Halt the autosave cycle to give the user time. Don't halt forever though, because you might disconnect and hang. - mergeDialogCurrentlyDisplayed = true; - - presentMergeDialog( - Messages['mergeDialog.prompt'], - - Messages['mergeDialog.keepRealtime'], - - function() { - debug('User chose to use the realtime version!'); - // Unset the merge dialog flag. - mergeDialogCurrentlyDisplayed = false; - deferred.resolve(); - }, - - Messages['mergeDialog.keepRemote'], - - function() { - debug('User chose to use the remote version!'); - // Unset the merge dialog flag. - mergeDialogCurrentlyDisplayed = false; - var restURL = XWiki.currentDocument.getRestURL(); - if (doc.language && !/\/pages\/(.+)\/translations\//.test(restURL)) { - restURL = restURL + doc.language; - } - - $.getJSON(restURL).then(data => { - mainConfig.setTextValue(data.content, true, function() { - debug("Overwrote the realtime session's content with the latest saved state."); - bumpVersion(function() { - lastSaved.mergeMessage('mergeOverwrite', []); - }); - deferred.resolve(); - }); - }).catch(error => { - mainConfig.safeCrash('keepremote'); - warn("Encountered an error while fetching remote content."); - warn(error); - deferred.reject(); - }); - } - ); - }, - - resolveMergeConflicts = function(merge) { - return new Promise((resolve, reject) => resolveMergeConflictsPromise(merge, {resolve, reject})); - }, - - mergedWithoutConflicts = function(merge, preMergeContent) { - return new Promise((resolve, reject) => { - // The content was merged and there were no errors / conflicts. - if (preMergeContent !== mainConfig.getTextValue()) { - // There have been changes since merging. Don't overwrite if there have been changes while merging - // See http://jira.xwiki.org/browse/RTWIKI-37 - // Try again in one cycle. - reject(); - } else { - // Walk the tree of hashes and if merge.previousVersionContent exists, then this merge is quite possibly faulty. - if (mainConfig.realtime.getDepthOfState(merge.previousVersionContent) !== -1) { - debug("The server merged a version which already existed in the history. " + - "Reversions shouldn't merge. Ignoring merge."); - debug("waseverstate=true"); - resolve(); - } else { - debug("The latest version content does not exist anywhere in our history."); - debug("Continuing..."); - // There were no errors or local changes. Push to the textarea. - mainConfig.setTextValue(merge.content, false, resolve); - } - } - }); - }, - - // callback takes signature (error, shouldSave) - mergeContinuation = function(merge, callback) { - // Our continuation has three cases: - if (isaveInterrupt()) { - // 1. ISAVE interrupt error - callback('ISAVED interrupt', null); - } else if (merge.saveRequired) { - // 2. saveRequired - callback(null, true); - } else { - // 3. saveNotRequired - callback(null, false); - } - }, - - mergeCallback = function(preMergeContent, andThen, error, merge) { - if (error) { - if (!merge || typeof merge === 'undefined') { - warn('The ajax merge API did not return an object. Something went wrong'); - warn(error); - return; - } else if (error === merge.error) { - // There was a merge error. Continue and handle elsewhere. - warn(error); - } else { - // It was some other kind of error... parsing? Complain and return. This means the script failed. - warn(error); - return; - } - } - - if (isaveInterrupt()) { - andThen('ISAVED interrupt', null); - - } else if (merge.content === lastSaved.content) { - // Don't dead end, but indicate that you shouldn't save. - andThen("Merging didn't result in a change.", false); - setLocalEditFlag(false); - - // http://jira.xwiki.org/browse/RTWIKI-34 - // Give Messages when merging. - } else if (merge.merged) { - // TODO update version field with merge.currentVersion - console.log("Force updating version to: " + merge.currentVersion); - if (xwikiMeta.setVersion) { - xwikiMeta.setVersion(merge.currentVersion); - } - // A merge took place. - if (merge.error) { - resolveMergeConflicts(merge).then(mergeContinuation.bind(null, merge, andThen)); - } else { - mergedWithoutConflicts(merge, preMergeContent).then(() => { - mergeContinuation(merge, andThen); - }).catch(() => { - andThen("The realtime content changed while we were performing our asynchronous merge.", false); - }); - } - - } else { - // No merge was necessary, but you might still have to save. - mergeContinuation(merge, andThen); - } - }, - - mergeRoutine = function(andThen) { - // Post your current version to the server to see if it must merge. Remember the current state so you can check if - // it has changed. - var preMergeContent = mainConfig.getTextValue(); - ajaxMerge(preMergeContent, mergeCallback.bind(null, preMergeContent, andThen)); - }, - onMessage = function(data) { // Set a flag so any concurrent processes know to abort. lastSaved.receivedISAVE = true; @@ -524,7 +277,7 @@ define('xwiki-realtime-saver', [ rtData = data; return false; - }, + }; /** * This contains some of the more complicated logic in this script. Clients check for remote changes on random @@ -535,7 +288,6 @@ define('xwiki-realtime-saver', [ * their local state to match. During this process, a series of checks are made to reduce the number of unnecessary * saves, as well as the number of unnecessary merges. */ - mergeDialogCurrentlyDisplayed = false; Saver.create = function(config) { $.extend(mainConfig, config); mainConfig.formId = mainConfig.formId || 'edit'; @@ -545,91 +297,6 @@ define('xwiki-realtime-saver', [ lastSaved.time = now(); var onOpen = function(chan) { - // Originally implemented as part of 'saveRoutine', abstracted logic such that the merge/save algorithm can - // terminate with different callbacks for different use cases. - var saveFinalizer = function(e, shouldSave) { - var toSave = mainConfig.getTextValue(); - if (toSave === null) { - e = "Unable to get the content of the document. Don't save."; - } - if (e) { - warn(e); - } else if (shouldSave) { - saveDocument(mainConfig.getSaveValue()).then(doc => { - // Cache this because bumpVersion will increment it. - var lastVersion = lastSaved.version; - - // Update values in lastSaved. - updateLastSaved(toSave); - - // Get document version. - bumpVersion(function(doc) { - if (doc.version === '1.1') { - debug('Created document version 1.1'); - } else { - debug(`Version bumped from ${lastVersion} to ${doc.version}.`); - } - lastSaved.mergeMessage('saved', [doc.version]); - }, doc); - }); - } else { - // local content matches that of the latest version - verbose("No save was necessary"); - lastSaved.content = toSave; - // didn't save, don't need a callback - bumpVersion(); - } - }; - - function saveRoutine(andThen, force) { - // If this is ever true in your save routine, complain and abort. - lastSaved.receivedISAVE = false; - - const toSave = mainConfig.getTextValue(); - if (toSave === null) { - warn("Unable to get the content of the document. Don't save."); - return; - } - - if (lastSaved.content === toSave && !force ) { - verbose("No changes made since last save. Avoiding unnecessary commits."); - return; - } - - if (mainConfig.mergeContent) { - mergeRoutine(andThen); - } else { - andThen(null, true); - } - } - - let preventDefaultSave = true; - function saveButtonAction(continueEditing) { - debug("Saver.create.saveand" + (continueEditing ? 'continue' : 'view')); - - saveRoutine(function(e) { - if (e) { - warn(e); - } - - lastSaved.shouldRedirect = !continueEditing; - preventDefaultSave = false; - // We use the Save & Continue button to trigger the save because we want to perform some action after the - // content is saved and we handle the redirect to view mode ourselves. - $(`#${config.formId} [name="action_saveandcontinue"]`).click(); - }, /* force: */ true); - } - - // Special handling of save action. - $(document).off('xwiki:actions:beforeSave.realtime-saver') - .on('xwiki:actions:beforeSave.realtime-saver', function(event) { - if (preventDefaultSave) { - event.preventDefault(); - saveButtonAction($(event.target).attr('name') === 'action_saveandcontinue'); - } - // Reset the flag for the next save. - preventDefaultSave = true; - }); // There's a very small chance that the preview button might cause problems, so let's just get rid of it. $('[name="action_preview"]').remove(); @@ -643,33 +310,28 @@ define('xwiki-realtime-saver', [ updateLastSaved(toSave); doc.reload().then(doc => { - if (doc.isNew) { - // It didn't actually save? - ErrorBox.show('save'); - } else { - lastSaved.onReceiveOwnIsave = function() { - // Once you get your isaved back, redirect. - debug("lastSaved.shouldRedirect " + lastSaved.shouldRedirect); - if (lastSaved.shouldRedirect) { - debug('Saver.create.saveandview.receivedOwnIsaved'); - debug("redirecting!"); - redirectToView(); - } else { - debug('Saver.create.saveandcontinue.receivedOwnIsaved'); - } - // Clean up after yourself.. - lastSaved.onReceiveOwnIsave = null; - }; - // Bump the version, fire your isaved. - bumpVersion(function(doc) { - if (doc.version === '1.1') { - debug('Created document version 1.1'); - } else { - debug(`Version bumped from ${lastVersion} to ${doc.version}.`); - } - lastSaved.mergeMessage('saved', [doc.version]); - }, doc); - } + lastSaved.onReceiveOwnIsave = function() { + // Once you get your isaved back, redirect. + debug("lastSaved.shouldRedirect " + lastSaved.shouldRedirect); + if (lastSaved.shouldRedirect) { + debug('Saver.create.saveandview.receivedOwnIsaved'); + debug("redirecting!"); + redirectToView(); + } else { + debug('Saver.create.saveandcontinue.receivedOwnIsaved'); + } + // Clean up after yourself.. + lastSaved.onReceiveOwnIsave = null; + }; + // Bump the version, fire your isaved. + bumpVersion(function(doc) { + if (doc.version === '1.1') { + debug('Created document version 1.1'); + } else { + debug(`Version bumped from ${lastVersion} to ${doc.version}.`); + } + lastSaved.mergeMessage('saved', [doc.version]); + }, doc); }).catch(error => { warn(error); ErrorBox.show('save'); @@ -695,37 +357,52 @@ define('xwiki-realtime-saver', [ console.log(ev); } }; - // FIXME: Find a way to remove the old listener without using Prototype.js API. - document.stopObserving('xwiki:document:saveFailed'); - $(document).on('xwiki:document:saveFailed.realtime-saver', onSaveFailedHandler); // TimeOut var check = function() { + lastSaved.receivedISAVE = false; + + const toSave = mainConfig.getTextValue(); + if (toSave === null) { + warn("Unable to get the content of the document. Don't save."); + return; + } + + if (lastSaved.content === toSave) { + verbose("No changes made since last save. Avoiding unnecessary commits."); + return; + } + clearTimeout(mainConfig.autosaveTimeout); verbose("Saver.create.check"); var periodDuration = Math.random() * SAVE_DOC_CHECK_CYCLE; mainConfig.autosaveTimeout = setTimeout(check, periodDuration); verbose(`Will attempt to save again in ${periodDuration} ms.`); - - if (!lastSaved.wasEditedLocally) { - verbose("Skipping save routine because no changes have been made locally"); + if (!lastSaved.wasEditedLocally || now() - lastSaved.time < SAVE_DOC_TIME) { + verbose("!lastSaved.wasEditedLocally || (Now - lastSaved.time) < SAVE_DOC_TIME"); return; - } else { - verbose("There have been local changes!"); } - if (now() - lastSaved.time < SAVE_DOC_TIME) { - verbose("(Now - lastSaved.time) < SAVE_DOC_TIME"); - return; - } - // Avoid queuing up multiple merge dialogs. - if (mergeDialogCurrentlyDisplayed) { - return; + + // The merge conflict modal is displayed after clicking on the save button when there is a merge conflict. + // Clicking the save button again would reopen the same modal and reset the fields the user did not submit yet. + if (!$('#previewDiffModal').is(':visible')) { + $(`#${config.formId} [name="action_saveandcontinue"]`).click(); } + }; - saveRoutine(saveFinalizer); + // Prevent the save buttons from reloading the page. Instead, reset the editor's content. + var overrideAjaxSaveAndContinue = function() { + var originalAjaxSaveAndContinue = $.extend({}, XWiki.actionButtons.AjaxSaveAndContinue.prototype); + $.extend(XWiki.actionButtons.AjaxSaveAndContinue.prototype, { + reloadEditor: function() { + // TODO: Handle the page title. + mainConfig.getTextAtCurrentRevision().then((data) => {mainConfig.setTextValue(data);}); + } + }); }; + overrideAjaxSaveAndContinue(); check(); }; @@ -797,12 +474,6 @@ define('xwiki-realtime-saver', [ } clearTimeout(mainConfig.autosaveTimeout); rtData = {}; - // Remove the merge routine from the save buttons. - $(document).off([ - 'xwiki:actions:beforeSave.realtime-saver', - 'xwiki:document:saved.realtime-saver', - 'xwiki:document:saveFailed.realtime-saver' - ].join(' ')); }; Saver.setLastSavedContent = function(content) { diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wiki/xwiki-platform-realtime-wiki-webjar/src/main/webjar/wikiEditor.js b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wiki/xwiki-platform-realtime-wiki-webjar/src/main/webjar/wikiEditor.js index 494ffbba529dc310a63bca5da6d8ce83aa7d61d5..b5e04d89073989f9235311c2d0cd8d51fba78b58 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wiki/xwiki-platform-realtime-wiki-webjar/src/main/webjar/wikiEditor.js +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wiki/xwiki-platform-realtime-wiki-webjar/src/main/webjar/wikiEditor.js @@ -245,6 +245,11 @@ define('xwiki-realtime-wikiEditor', [ getTextValue: function() { return editor.getValue(); }, + getTextAtCurrentRevision: function() { + return $.get(XWiki.currentDocument.getRestURL('', $.param({media:'json'}))).then(data => { + return data.content; + }); + }, realtime: info.realtime, userList: info.userList, userName: editorConfig.userName, diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/pom.xml b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/pom.xml index 87bdcd73ce57fe1f16d816e0c84800a8449b305f..224cb049d3c18f7e1d4dbea79257ec33b180d31d 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/pom.xml +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/pom.xml @@ -90,6 +90,13 @@ <version>${project.version}</version> <scope>test</scope> </dependency> + <!-- Used to handle the edit conflict modal. --> + <dependency> + <groupId>org.xwiki.platform</groupId> + <artifactId>xwiki-platform-flamingo-skin-test-pageobjects</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <!-- Realtime synchronization is done through WebSockets. --> <dependency> <groupId>javax.websocket</groupId> diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/src/test/it/org/xwiki/realtime/wysiwyg/test/ui/RealtimeWYSIWYGEditorIT.java b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/src/test/it/org/xwiki/realtime/wysiwyg/test/ui/RealtimeWYSIWYGEditorIT.java index 51c5df3a7a7a0c50e27536e7ba563a82c501383e..ea3d7f47b29b755c7adbc3060db9ac6365e15bf5 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/src/test/it/org/xwiki/realtime/wysiwyg/test/ui/RealtimeWYSIWYGEditorIT.java +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-docker/src/test/it/org/xwiki/realtime/wysiwyg/test/ui/RealtimeWYSIWYGEditorIT.java @@ -34,6 +34,8 @@ import org.xwiki.ckeditor.test.po.MacroDialogEditModal; import org.xwiki.ckeditor.test.po.image.ImageDialogEditModal; import org.xwiki.ckeditor.test.po.image.ImageDialogSelectModal; +import org.xwiki.flamingo.skin.test.po.EditConflictModal; +import org.xwiki.flamingo.skin.test.po.EditConflictModal.ConflictChoice; import org.xwiki.model.reference.AttachmentReference; import org.xwiki.model.reference.DocumentReference; import org.xwiki.realtime.wysiwyg.test.po.RealtimeCKEditor; @@ -740,4 +742,124 @@ void editSameMacro(TestReference testReference, TestUtils setup) assertEquals("{{info cssClass=\"bar\" title=\"Some cool title\"}}\ntwo one\n{{/info}}\n\n ", WikiEditPage.gotoPage(testReference).getContent()); } + + @Test + @Order(9) + void reloadEditorsMergeConflictManualSave(TestReference testReference, TestUtils setup) + { + // + // First Tab + // + + // Start fresh. + setup.deletePage(testReference); + + RealtimeWYSIWYGEditPage firstEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor firstEditor = firstEditPage.getContenEditor(); + RealtimeRichTextAreaElement firstTextArea = firstEditor.getRichTextArea(); + firstTextArea.sendKeys("First"); + firstEditPage.clickSaveAndContinue(); + + // + // Second Tab + // + + String secondTabHandle = setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle(); + + RealtimeWYSIWYGEditPage secondEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor secondEditor = secondEditPage.getContenEditor(); + RealtimeRichTextAreaElement secondTextArea = secondEditor.getRichTextArea(); + + secondTextArea.waitUntilContentContains("First"); + + // + // Third Tab + // + + String thirdTabHandle = setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle(); + + RealtimeWYSIWYGEditPage thirdEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor thirdEditor = thirdEditPage.getContenEditor(); + RealtimeRichTextAreaElement thirdTextArea = thirdEditor.getRichTextArea(); + thirdTextArea.waitUntilContentContains("First"); + + thirdEditPage.leaveRealtimeEditing(); + + thirdTextArea.sendKeys(Keys.END, " Second"); + thirdEditPage.clickSaveAndContinue(); + + // First tab + + setup.getDriver().switchTo().window(firstTabHandle); + firstTextArea.sendKeys(Keys.END, " First"); + firstTextArea.waitUntilContentContains("First First"); + firstEditPage.clickSaveAndContinue(false); + + EditConflictModal editConflictModal = new EditConflictModal(); + editConflictModal.makeChoiceAndSubmit(ConflictChoice.RELOAD, false); + + firstTextArea.waitUntilContentContains("First Second"); + + // Second tab + setup.getDriver().switchTo().window(secondTabHandle); + secondTextArea.waitUntilContentContains("First Second"); + } + + @Test + @Order(10) + void reloadEditorsSilentMergeConflictManualSave(TestReference testReference, TestUtils setup) + { + // + // First Tab + // + + // Start fresh. + setup.deletePage(testReference); + + RealtimeWYSIWYGEditPage firstEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor firstEditor = firstEditPage.getContenEditor(); + RealtimeRichTextAreaElement firstTextArea = firstEditor.getRichTextArea(); + firstTextArea.sendKeys("First"); + firstEditPage.clickSaveAndContinue(); + + // + // Second Tab + // + + String secondTabHandle = setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle(); + + RealtimeWYSIWYGEditPage secondEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor secondEditor = secondEditPage.getContenEditor(); + RealtimeRichTextAreaElement secondTextArea = secondEditor.getRichTextArea(); + + secondTextArea.waitUntilContentContains("First"); + + // + // Third Tab + // + + String thirdTabHandle = setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle(); + + RealtimeWYSIWYGEditPage thirdEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference); + RealtimeCKEditor thirdEditor = thirdEditPage.getContenEditor(); + RealtimeRichTextAreaElement thirdTextArea = thirdEditor.getRichTextArea(); + thirdTextArea.waitUntilContentContains("First"); + + thirdEditPage.leaveRealtimeEditing(); + + thirdTextArea.sendKeys(Keys.END, Keys.ENTER, "Second"); + thirdEditPage.clickSaveAndContinue(); + + // First tab + setup.getDriver().switchTo().window(firstTabHandle); + firstEditPage.clickSaveAndContinue(); + + firstTextArea.waitUntilContentContains("First"); + firstTextArea.waitUntilContentContains("Second"); + + // Second tab + setup.getDriver().switchTo().window(secondTabHandle); + secondTextArea.waitUntilContentContains("First"); + secondTextArea.waitUntilContentContains("Second"); + } } diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-pageobjects/src/main/java/org/xwiki/realtime/wysiwyg/test/po/RealtimeWYSIWYGEditPage.java b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-pageobjects/src/main/java/org/xwiki/realtime/wysiwyg/test/po/RealtimeWYSIWYGEditPage.java index 2b45c2b5081ba4430958d3751967d3252f1d0324..9b11287daeba1154f885f89841c8556e07e742ff 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-pageobjects/src/main/java/org/xwiki/realtime/wysiwyg/test/po/RealtimeWYSIWYGEditPage.java +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-test/xwiki-platform-realtime-wysiwyg-test-pageobjects/src/main/java/org/xwiki/realtime/wysiwyg/test/po/RealtimeWYSIWYGEditPage.java @@ -19,6 +19,7 @@ */ package org.xwiki.realtime.wysiwyg.test.po; +import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.xwiki.model.reference.EntityReference; @@ -71,7 +72,7 @@ public void leaveRealtimeEditing() { if (isRealtimeEditing()) { this.allowRealtimeCheckbox.click(); - // TODO: Handle the confirmation modal. + getDriver().findElement(By.cssSelector(".modal-popup .realtime-buttons .btn-primary")).click(); } } diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/editor.js b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/editor.js index cae23f3be68c5d7c9b9d229611fcce725c80d2df..455d636d94e8b163507494e024b35331aada2f44 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/editor.js +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/editor.js @@ -93,8 +93,10 @@ define('xwiki-realtime-wysiwyg-editor', [ * * @param {Node[]} updatedNodes the DOM nodes that have been updated (added, modified directly or with removed * descendants) + * + * @param {boolean} propagate true when the new content should be propagated to coeditors */ - contentUpdated(updatedNodes) { + contentUpdated(updatedNodes, propagate) { try { this._initializeWidgets(updatedNodes); } catch (e) { @@ -103,7 +105,7 @@ define('xwiki-realtime-wysiwyg-editor', [ // Notify the content change (e.g. to update the empty line placeholders) without triggering our own change // handler (see #onChange()). - this._ckeditor.fire('change', {remote: true}); + this._ckeditor.fire('change', {remote: !propagate}); } /** @@ -155,6 +157,16 @@ define('xwiki-realtime-wysiwyg-editor', [ this._CKEDITOR.plugins.xwikiSelection.restoreSelection(this._ckeditor); } + /** + * Converts input data accepted by the editor to html that can be directly inserted in the editor's DOM. + * + * @param {string} data the data to be converted + * @returns {string} html representation of the input data that can be inserted in the editor's DOM. + */ + convertDataToHtml(data) { + return this._ckeditor.dataProcessor.toHtml(data); + } + _initializeWidgets(updatedNodes) { // Reset the focused and selected widgets, as well as the widget holding the focused editable because they may // have been invalidated by the DOM changes. @@ -213,4 +225,4 @@ define('xwiki-realtime-wysiwyg-editor', [ } return Editor; -}); \ No newline at end of file +}); diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/patches.js b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/patches.js index 6836180a53f2ad30083665037b7f91ccd411fa1e..1bbf9948e6c95a029f6bad89bacb511f4e4c6d50 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/patches.js +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/patches.js @@ -112,8 +112,10 @@ define('xwiki-realtime-wysiwyg-patches', [ * * @param {string} remoteFilteredHyperJSON the new normalized (filtered) content (usually the result of a remote * change), serialized as HyperJSON + * + * @param {boolean} propagate true when the new content should be propagated to coeditors */ - setHyperJSON(remoteFilteredHyperJSON) { + setHyperJSON(remoteFilteredHyperJSON, propagate) { let remoteHyperJSON; try { remoteHyperJSON = this._revertHyperJSONFilters(remoteFilteredHyperJSON); @@ -137,32 +139,39 @@ define('xwiki-realtime-wysiwyg-patches', [ return; } - this._updateContent(newContent); + this._updateContent(newContent, propagate); } /** * Update the editor content without affecting its caret / selection. * * @param {string} html the new HTML content + * @param {boolean} propagate true when the new content should be propagated to coeditors */ - setHTML(html) { + setHTML(html, propagate) { + const fixedHtml = this._editor.convertDataToHtml(html); + let doc; try { - doc = new DOMParser().parseFromString(html, 'text/html'); + doc = new DOMParser().parseFromString(fixedHtml, 'text/html'); } catch (e) { console.error('Failed to parse the given HTML string: ' + html, e); return; } - this._updateContent(doc.body); + // We convert to HyperJSON and set the HyperJSON so that we can use the same filters + // as when receiving content from coeditors. + const hjson = Patches._stringifyNode(this._normalizeContent(doc.body), false); + this.setHyperJSON(hjson, propagate); } /** * Update the editor content without affecting its caret / selection. * * @param {Node} newContent the new content to set, as a DOM node + * @param {boolean} propagate true when the new content should be propagated to coeditors */ - _updateContent(newContent) { + _updateContent(newContent, propagate) { // Remember where the selection is, to be able to restore it in case the content update affects it. this._editor.saveSelection(); const selection = this._editor.getSelection(); @@ -179,7 +188,7 @@ define('xwiki-realtime-wysiwyg-patches', [ document: oldContent.ownerDocument }); - this._editor.contentUpdated(this._diffDOM._updatedNodes); + this._editor.contentUpdated(this._diffDOM._updatedNodes, propagate); // Restore the selection if the editor had a selection (i.e. if the selection was inside the editing area) before // the content update and it was affected by the content update. Note that the selection restore focuses the @@ -302,4 +311,4 @@ define('xwiki-realtime-wysiwyg-patches', [ } return Patches; -}); \ No newline at end of file +}); diff --git a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/wysiwygEditor.js b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/wysiwygEditor.js index 99296bdc40160514df9b4e84e87bb2b0c70a4b79..9a7dd67876219bb2995486c81fabb016f43d50e8 100644 --- a/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/wysiwygEditor.js +++ b/xwiki-platform-core/xwiki-platform-realtime/xwiki-platform-realtime-wysiwyg/xwiki-platform-realtime-wysiwyg-webjar/src/main/webjar/wysiwygEditor.js @@ -191,39 +191,7 @@ define('xwiki-realtime-wysiwyg', [ // Id of the wiki page form. formId: window.XWiki.editor === 'wysiwyg' ? 'edit' : 'inline', setTextValue: function(newText, toConvert, callback) { - function andThen(data) { - patchedEditor.setHTML(data); - - // If available, transform the HTML comments for XWiki macros into macros before saving - // (<!--startmacro:{...}-->). We can do that by using the "xwiki-refresh" command provided the by - // CKEditor Integration application. - if (editor.plugins['xwiki-macro'] && findMacroComments(genericEditor.getContent()).length > 0) { - initializing = true; - editor.execCommand('xwiki-refresh'); - afterRefresh.push(callback); - } else { - callback(); - realtimeOptions.onLocal(); - } - } - if (toConvert) { - const object = { - wiki: XWiki.currentWiki, - space: XWiki.currentSpace, - page: XWiki.currentPage, - convert: true, - text: newText - }; - $.post(editorConfig.htmlConverterUrl, object).then(andThen).catch(() => { - const debugLog = { - state: editorId + '/convertHTML', - postData: object - }; - module.onAbort(null, 'converthtml', JSON.stringify(debugLog)); - }); - } else { - andThen(newText); - } + patchedEditor.setHTML(newText, true); }, getSaveValue: function() { return { @@ -240,6 +208,13 @@ define('xwiki-realtime-wysiwyg', [ return null; } }, + getTextAtCurrentRevision: function(revision) { + return $.get(XWiki.currentDocument.getURL('get', $.param({xpage:'get', + outputSyntax:'annotatedhtml', + outputSyntaxVersion:'5.0', + transformations:'macro', + rev:revision}))); + }, realtime: info.realtime, userList: info.userList, userName: editorConfig.userName,