diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index b3b99f489..e1cfde78b 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -221,6 +221,18 @@ L.Socket = L.Class.extend({ this._map.fire('wopiprops', wopiInfo); return; } + else if (textMsg.startsWith('commandresult: ')) { + var commandresult = JSON.parse(textMsg.substring(textMsg.indexOf('{'))); + if (commandresult['command'] === 'savetostorage' && commandresult['success']) { + // Close any open confirmation dialogs + if (vex.dialogID > 0) { + var id = vex.dialogID; + vex.dialogID = -1; + vex.close(id); + } + } + return; + } else if (textMsg.startsWith('close: ')) { textMsg = textMsg.substring('close: '.length); msg = ''; diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 7af580bb5..20b50b22e 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -226,7 +226,10 @@ bool ClientSession::_handleInput(const char *buffer, int length) { int force = 0; getTokenInteger(tokens[1], "force", force); - docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true); + if (docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true)) + { + docBroker->broadcastMessage("commandresult: { \"command\": \"savetostorage\", \"success\": true }"); + } } else { diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 2aa10d215..dfad745e1 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -627,6 +627,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, // So set _documentLastModifiedTime then _documentLastModifiedTime = _storage->getFileInfo()._modifiedTime; + // After a successful save, we are sure that document in the storage is same as ours _documentChangedInStorage = false; @@ -662,11 +663,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, { LOG_ERR("PutFile says that Document changed in storage"); _documentChangedInStorage = true; - // Inform all clients - for (const auto& sessionIt : _sessions) - { - sessionIt.second->sendTextFrame("error: cmd=storage kind=documentconflict"); - } + broadcastMessage("error: cmd=storage kind=documentconflict"); } return false; @@ -1418,6 +1415,17 @@ void DocumentBroker::closeDocument(const std::string& reason) terminateChild(reason, false); } +void DocumentBroker::broadcastMessage(const std::string& message) +{ + assertCorrectThread(); + + LOG_DBG("Broadcasting message [" << message << "] to all sessions."); + for (const auto& sessionIt : _sessions) + { + sessionIt.second->sendTextFrame(message); + } +} + void DocumentBroker::updateLastActivityTime() { _lastActivityTime = std::chrono::steady_clock::now(); diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 7f5636476..1329fcb17 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -339,6 +339,9 @@ public: /// Sends the .uno:Save command to LoKit. bool sendUnoSave(const std::string& sessionId, bool dontTerminateEdit = true, bool dontSaveIfUnmodified = true); + /// Sends a message to all sessions + void broadcastMessage(const std::string& message); + private: /// Shutdown all client connections with the given reason. diff --git a/wsd/protocol.txt b/wsd/protocol.txt index e3bf12aba..7f43e472a 100644 --- a/wsd/protocol.txt +++ b/wsd/protocol.txt @@ -334,6 +334,10 @@ tile: part= width= height= tileposx= tileposy=< a hash of the tile contents, and can be included by the client in the next 'tile' message requesting the same tile. +commandresult: + This is used to acknowledge the commands from the client. + is { command: , success: 'true' } + Each LOK_CALLBACK_FOO_BAR callback except LOK_CALLBACK_INVALIDATE_TILES causes a corresponding message to the client, consisting of the FOO_BAR part in lowercase, without @@ -343,12 +347,12 @@ message as documented above.) For instance: invalidatecursor: -The payload contains a rectangle describing the cursor position. + The payload contains a rectangle describing the cursor position. -The communication between the parent process (the one keeping open the -Websocket connections to the clients) and a child process (handling -one document through LibreOfficeKit) uses the same protocol, with -the following additions and changes: + The communication between the parent process (the one keeping open the + Websocket connections to the clients) and a child process (handling + one document through LibreOfficeKit) uses the same protocol, with + the following additions and changes: unocommandresult: