loleaflet/src/control/Toolbar.js |    4 +++-
 loleaflet/src/core/Socket.js     |   30 ++++++++++++++++++++++++++++++
 wsd/ClientSession.cpp            |   14 ++++++++++++++
 wsd/DocumentBroker.cpp           |   17 ++++++++++++-----
 wsd/DocumentBroker.hpp           |    8 +++++++-
 wsd/Storage.cpp                  |   15 ++++++++++++---
 wsd/Storage.hpp                  |    8 +++++++-
 wsd/protocol.txt                 |   12 ++++++++++++
 8 files changed, 97 insertions(+), 11 deletions(-)

New commits:
commit 41234773e3b57e1695951bddaedb4f61ad44026d
Author: Pranav Kant <pran...@collabora.co.uk>
Date:   Thu Jun 1 19:46:03 2017 +0530

    If user permits, save to storage force-fully in case of doc conflict
    
    There is one known problem still - after any user decides to overwrite
    the file to storage, other users are not informed, so their dialog keeps
    hanging on the screen until they press the cancel or reload button.
    
    Change-Id: I6dad1585e4c53eeed79cd38316892a7f239d44ef

diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 681a8ecc..b5f68749 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -131,7 +131,9 @@ L.Map.include({
        },
 
        save: function(dontTerminateEdit, dontSaveIfUnmodified) {
-               this._socket.sendMessage('save dontTerminateEdit=' + 
(dontTerminateEdit ? 1 : 0) + ' dontSaveIfUnmodified=' + (dontSaveIfUnmodified 
? 1 : 0));
+               this._socket.sendMessage('save' +
+                                        ' dontTerminateEdit=' + 
(dontTerminateEdit ? 1 : 0) +
+                                        ' dontSaveIfUnmodified=' + 
(dontSaveIfUnmodified ? 1 : 0));
        },
 
        sendUnoCommand: function (command, json) {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index a65a330a..1d6b884f 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -357,7 +357,7 @@ L.Socket = L.Class.extend({
                                                        
this.sendMessage('closedocument');
                                                } else {
                                                        // They want to 
overwrite
-                                                       
this.sendMessage('documentconflict.overwrite');
+                                                       
this.sendMessage('savetostorage force=1');
                                                }
                                        }, this)
                                });
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 4ba8988f..7af580bb 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -136,6 +136,7 @@ bool ClientSession::_handleInput(const char *buffer, int 
length)
              tokens[0] != "resetselection" &&
              tokens[0] != "save" &&
              tokens[0] != "saveas" &&
+             tokens[0] != "savetostorage" &&
              tokens[0] != "selectgraphic" &&
              tokens[0] != "selecttext" &&
              tokens[0] != "setclientpart" &&
@@ -221,6 +222,12 @@ bool ClientSession::_handleInput(const char *buffer, int 
length)
         getTokenInteger(tokens[2], "dontSaveIfUnmodified", 
dontSaveIfUnmodified);
         docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, 
dontSaveIfUnmodified != 0);
     }
+    else if (tokens[0] == "savetostorage")
+    {
+        int force = 0;
+        getTokenInteger(tokens[1], "force", force);
+        docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when 
success is true*/, true);
+    }
     else
     {
         if (!filterMessage(firstLine))
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 9b282165..981834fa 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -544,10 +544,15 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
 }
 
 bool DocumentBroker::saveToStorage(const std::string& sessionId,
-                                   bool success, const std::string& result)
+                                   bool success, const std::string& result, 
bool force)
 {
     assertCorrectThread();
 
+    if (force)
+    {
+        LOG_TRC("Document will be saved forcefully to storage.");
+        _storage->forceSave();
+    }
     const bool res = saveToStorageInternal(sessionId, success, result);
 
     // If marked to destroy, or session is disconnected, remove.
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 588df3fd..7f563647 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -240,7 +240,7 @@ public:
     bool isDocumentChangedInStorage() { return _documentChangedInStorage; }
 
     /// Save the document to Storage if it needs persisting.
-    bool saveToStorage(const std::string& sesionId, bool success, const 
std::string& result = "");
+    bool saveToStorage(const std::string& sesionId, bool success, const 
std::string& result = "", bool force = false);
     bool isModified() const { return _isModified; }
     void setModified(const bool value);
     /// Save the document if the document is modified.
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 7c677a6d..af31674e 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -668,13 +668,14 @@ StorageBase::SaveResult 
WopiStorage::saveLocalFileToStorage(const std::string& a
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, 
uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
         request.set("X-WOPI-Override", "PUT");
-        if (!_forceOverwrite)
+        if (!_forceSave)
         {
             // Request WOPI host to not overwrite if timestamps mismatch
             request.set("X-LOOL-WOPI-Timestamp",
                         
Poco::DateTimeFormatter::format(Poco::DateTime(_fileInfo._modifiedTime),
                                                         
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT));
         }
+
         request.setContentType("application/octet-stream");
         request.setContentLength(size);
         addStorageDebugCookie(request);
@@ -699,6 +700,10 @@ StorageBase::SaveResult 
WopiStorage::saveLocalFileToStorage(const std::string& a
                 const std::string lastModifiedTime = 
getJSONValue<std::string>(object, "LastModifiedTime");
                 LOG_TRC("WOPI::PutFile returns LastModifiedTime [" << 
lastModifiedTime << "].");
                 _fileInfo._modifiedTime = iso8601ToTimestamp(lastModifiedTime);
+
+                // Reset the force save flag now, if any, since we are done 
saving
+                // Next saves shouldn't be saved forcefully unless commanded
+                _forceSave = false;
             }
             else
             {
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index bb25a2d7..60c73275 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -78,7 +78,7 @@ public:
         _jailPath(jailPath),
         _fileInfo("", "lool", Poco::Timestamp::fromEpochTime(0), 0),
         _isLoaded(false),
-        _forceOverwrite(false)
+        _forceSave(false)
     {
         LOG_DBG("Storage ctor: " << uri.toString());
     }
@@ -92,7 +92,7 @@ public:
 
     /// Asks the storage object to force overwrite to storage upon next save
     /// even if document turned out to be changed in storage
-    void forceOverwrite() { _forceOverwrite = true; }
+    void forceSave() { _forceSave = true; }
 
     /// Returns the basic information about the file.
     FileInfo getFileInfo() { return _fileInfo; }
@@ -125,7 +125,7 @@ protected:
     std::string _jailedFilePath;
     FileInfo _fileInfo;
     bool _isLoaded;
-    bool _forceOverwrite;
+    bool _forceSave;
 
     static bool FilesystemEnabled;
     static bool WopiEnabled;
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index b0617b2f..e3bf12ab 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -160,6 +160,14 @@ save dontTerminateEdit=<value> dontSaveIfUnmodified=<value>
     non-zero. 'dontSaveIfUnmodified' when set to non-zero skips saving the 
document when it is
     unmodified.
 
+savetostorage force=<value>
+
+    Saves the files to storage. A 'save' command automatically saves the file 
to
+    storage almost always except in the case where there is a document conflict
+    i.e document in the storage is changed behind our back. We might need to do
+    call this command with force=1 in that case to forcefully save it to 
storage
+    after asking from the user.
+
 clientvisiblearea x=<x> y=<y> width=<width> height=<height>
 
     Invokes lok::Document::setClientVisibleArea().
commit 4d61cae4c8b6ecc7890fb0426c9600e1cf77bbdb
Author: Pranav Kant <pran...@collabora.co.uk>
Date:   Thu Jun 1 18:26:54 2017 +0530

    If user commands, refresh the document for all in case of doc conflict
    
    Change-Id: I42c61fb8099b0bcc60f942e602561cc97486a918

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 37b67fd4..a65a330a 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -266,6 +266,21 @@ L.Socket = L.Class.extend({
                                        }
                                }, timeoutMs);
                        }
+                       else if (textMsg.startsWith('documentconflict')) {
+                               var username = 
textMsg.substring('documentconflict '.length);
+                               msg = _('%user asked to refresh the document. 
Document will now refresh automatically.').replace('%user', username);
+
+                               // Reload the document
+                               this._map._active = false;
+                               map = this._map;
+                               vex.timer = setInterval(function() {
+                                       try {
+                                               // Activate and cancel timer 
and dialogs.
+                                               map._activate();
+                                       } catch (error) {
+                                       }
+                               }, 3000);
+                       }
 
                        // Close any open dialogs first.
                        if (vex.dialogID > 0) {
@@ -334,6 +349,21 @@ L.Socket = L.Class.extend({
                        else if (command.errorKind === 'documentconflict')
                        {
                                storageError = 
errorMessages.storage.documentconflict;
+                               vex.dialog.confirm({
+                                       message: _('Document has been changed 
in storage. Do you want to refresh the page to load the new document ? 
Cancelling will continue editing and overwrite.'),
+                                       callback: L.bind(function(value) {
+                                               if (value) {
+                                                       // They want to refresh 
the page and load document again for all
+                                                       
this.sendMessage('closedocument');
+                                               } else {
+                                                       // They want to 
overwrite
+                                                       
this.sendMessage('documentconflict.overwrite');
+                                               }
+                                       }, this)
+                               });
+                               vex.dialogID = vex.globalID - 1;
+
+                               return;
                        }
 
                        // Parse the storage url as link
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 21b23d12..4ba8988f 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -173,6 +173,13 @@ bool ClientSession::_handleInput(const char *buffer, int 
length)
             LOG_DBG("Session [" << getId() << "] requested owner termination");
             docBroker->closeDocument("ownertermination");
         }
+        else if (docBroker->isDocumentChangedInStorage())
+        {
+            LOG_DBG("Document marked as changed in storage and user ["
+                    << getUserId() << ", " << getUserName()
+                    << "] wants to refresh the document for all.");
+            docBroker->closeDocument("documentconflict " + getUserName());
+        }
 
         return true;
     }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index ee674f36..9b282165 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -147,6 +147,7 @@ DocumentBroker::DocumentBroker(const std::string& uri,
     _docId(Util::encodeId(DocBrokerId++, 3)),
     _childRoot(childRoot),
     _cacheRoot(getCachePath(uriPublic.toString())),
+    _documentChangedInStorage(false),
     _lastSaveTime(std::chrono::steady_clock::now()),
     _lastSaveRequestTime(std::chrono::steady_clock::now() - 
std::chrono::milliseconds(COMMAND_TIMEOUT_MS)),
     _markToDestroy(false),
@@ -495,6 +496,7 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
             _documentLastModifiedTime != fileInfo._modifiedTime)
         {
             LOG_WRN("Document [" << _docKey << "] has been modified behind our 
back. Informing all clients.");
+            _documentChangedInStorage = true;
             // Inform all clients
             for (const auto& sessionIt : _sessions)
             {
@@ -652,7 +654,7 @@ bool DocumentBroker::saveToStorageInternal(const 
std::string& sessionId,
     else if (storageSaveResult == StorageBase::SaveResult::DOC_CHANGED)
     {
         LOG_ERR("PutFile says that Document changed in storage");
-
+        _documentChangedInStorage = true;
         // Inform all clients
         for (const auto& sessionIt : _sessions)
         {
@@ -1334,7 +1336,7 @@ bool DocumentBroker::forwardToClient(const 
std::shared_ptr<Message>& payload)
 void DocumentBroker::shutdownClients(const std::string& closeReason)
 {
     assertCorrectThread();
-    LOG_INF("Terminating " << _sessions.size() << " clients of doc [" << 
_docKey << "].");
+    LOG_INF("Terminating " << _sessions.size() << " clients of doc [" << 
_docKey << "] with reason: " << closeReason);
 
     // First copy into local container, since removeSession
     // will erase from _sessions, but will leave the last.
@@ -1376,7 +1378,7 @@ void DocumentBroker::terminateChild(const std::string& 
closeReason, const bool r
 {
     assertCorrectThread();
 
-    LOG_INF("Terminating doc [" << _docKey << "].");
+    LOG_INF("Terminating doc [" << _docKey << "] with reason: " << 
closeReason);
 
     // Close all running sessions
     if (!rude)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 27d525c0..588df3fd 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -237,6 +237,8 @@ public:
     bool isLoaded() const { return _isLoaded; }
     void setLoaded();
 
+    bool isDocumentChangedInStorage() { return _documentChangedInStorage; }
+
     /// Save the document to Storage if it needs persisting.
     bool saveToStorage(const std::string& sesionId, bool success, const 
std::string& result = "");
     bool isModified() const { return _isModified; }
@@ -376,6 +378,10 @@ private:
     std::string _jailId;
     std::string _filename;
 
+    /// Set to true when document changed in storage and we are waiting
+    /// for user's command to act.
+    bool _documentChangedInStorage;
+
     /// The last time we tried saving, regardless of whether the
     /// document was modified and saved or not.
     std::chrono::steady_clock::time_point _lastSaveTime;
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 394b9136..7c677a6d 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -668,9 +668,13 @@ StorageBase::SaveResult 
WopiStorage::saveLocalFileToStorage(const std::string& a
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, 
uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
         request.set("X-WOPI-Override", "PUT");
-        request.set("X-LOOL-WOPI-Timestamp",
-                    
Poco::DateTimeFormatter::format(Poco::DateTime(_fileInfo._modifiedTime),
-                                                    
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT));
+        if (!_forceOverwrite)
+        {
+            // Request WOPI host to not overwrite if timestamps mismatch
+            request.set("X-LOOL-WOPI-Timestamp",
+                        
Poco::DateTimeFormatter::format(Poco::DateTime(_fileInfo._modifiedTime),
+                                                        
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT));
+        }
         request.setContentType("application/octet-stream");
         request.setContentLength(size);
         addStorageDebugCookie(request);
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 89cc4de9..bb25a2d7 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -77,7 +77,8 @@ public:
         _localStorePath(localStorePath),
         _jailPath(jailPath),
         _fileInfo("", "lool", Poco::Timestamp::fromEpochTime(0), 0),
-        _isLoaded(false)
+        _isLoaded(false),
+        _forceOverwrite(false)
     {
         LOG_DBG("Storage ctor: " << uri.toString());
     }
@@ -89,6 +90,10 @@ public:
 
     bool isLoaded() const { return _isLoaded; }
 
+    /// Asks the storage object to force overwrite to storage upon next save
+    /// even if document turned out to be changed in storage
+    void forceOverwrite() { _forceOverwrite = true; }
+
     /// Returns the basic information about the file.
     FileInfo getFileInfo() { return _fileInfo; }
 
@@ -120,6 +125,7 @@ protected:
     std::string _jailedFilePath;
     FileInfo _fileInfo;
     bool _isLoaded;
+    bool _forceOverwrite;
 
     static bool FilesystemEnabled;
     static bool WopiEnabled;
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index 89c2ce43..b0617b2f 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -269,6 +269,10 @@ close: <reason>
     shutting down to let clients know they can try connecting
     after a short interval.
 
+    * documentconflict <user name> - All sessions of this document are going 
down
+    because file was changed in storage and one of the user ( with <user
+    name>) asked to reload the session for all.
+
 getchildid: id=<id>
 
     Returns the child id
commit d6ef241cf581f981a0b7a7cfcd60f0e62546e633
Author: Pranav Kant <pran...@collabora.co.uk>
Date:   Thu Jun 1 18:16:20 2017 +0530

    Don't be so rude
    
    If we are rude, then we don't tell the reason behind closing the
    document to our clients.
    
    This method earlier was used to do 'ownertermination', but without this
    patch, ownertermination is not going to work. We regressed somewhere in
    the past.
    
    Change-Id: I7a2513e567f72b1adf00d5a74b118e116d6d99d3

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index a97e34da..ee674f36 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1406,7 +1406,7 @@ void DocumentBroker::closeDocument(const std::string& 
reason)
     assertCorrectThread();
 
     LOG_DBG("Closing DocumentBroker for docKey [" << _docKey << "] with 
reason: " << reason);
-    terminateChild(reason, true);
+    terminateChild(reason, false);
 }
 
 void DocumentBroker::updateLastActivityTime()
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to