[Libreoffice-commits] online.git: 2 commits - loleaflet/dist loleaflet/src loolwsd/ChildProcessSession.cpp loolwsd/DocumentBroker.cpp loolwsd/DocumentBroker.hpp loolwsd/LOOLWSD.cpp loolwsd/MasterProcessSession.cpp loolwsd/MasterProcessSession.hpp

Pranav Kant pranavk at collabora.com
Thu Mar 31 13:57:22 UTC 2016


 loleaflet/dist/toolbar/toolbar.js     |   36 ++++++++++++++++++++
 loleaflet/src/layer/tile/TileLayer.js |   10 +++++
 loolwsd/ChildProcessSession.cpp       |    7 +++
 loolwsd/DocumentBroker.cpp            |   53 ++++++++++++++++++++++++++++++
 loolwsd/DocumentBroker.hpp            |   18 +++++++++-
 loolwsd/LOOLWSD.cpp                   |   60 ++++++++++++++++++----------------
 loolwsd/MasterProcessSession.cpp      |   22 +++++++++++-
 loolwsd/MasterProcessSession.hpp      |   13 ++++++-
 8 files changed, 188 insertions(+), 31 deletions(-)

New commits:
commit b81abc4079047c2735b1f5160f18fffda70aed72
Author: Pranav Kant <pranavk at collabora.com>
Date:   Thu Mar 24 00:48:21 2016 +0530

    loleaflet: Edit lock implemention on front-end
    
    Change-Id: I97c2b2e0a4ef51c6335cefe3e9ec8f5904deb9f4

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 5f1da68..6e3e174 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -137,6 +137,10 @@ $(function () {
 			{ type: 'button',  id: 'duplicatepage', img: 'duplicatepage', hint: _("Duplicate Page") },
 			{ type: 'button',  id: 'deletepage', img: 'deletepage', hint: _("Delete Page") },
 			{ type: 'html', id: 'right' },
+			{ type: 'break' },
+			{ type: 'button',  id: 'takeedit', img: 'edit', hint: _("Take edit lock (others can only view)")},
+			{ type: 'html',    id: 'takeedit_text', html: '<div id="takeedit_text">VIEWING</div>' },
+			{ type: 'break' },
 			{ type: 'button',  id: 'prev', img: 'prev', hint: _("Previous page/part") },
 			{ type: 'button',  id: 'next', img: 'next', hint: _("Next page/part") },
 			{ type: 'break' },
@@ -271,6 +275,11 @@ function onClick(id) {
 			callback: onSaveAs
 		});
 	}
+	else if (id === 'takeedit') {
+		if (!item.checked) {
+			map._socket.sendMessage('takeedit');
+		}
+	}
 	else if (id === 'searchprev') {
 		map.search(L.DomUtil.get('search-input').value, true);
 	}
@@ -869,6 +878,33 @@ map.on('hyperlinkclicked', function (e) {
 	window.open(e.url, '_blank');
 });
 
+map.on('editlock', function (e) {
+	var toolbar = w2ui['toolbar-down'];
+	if (e.value) {
+		toolbar.check('takeedit');
+		toolbar.disable('takeedit');
+		toolbar.set('takeedit', {hint: _('You are editing (others can only view)')});
+
+		$('#takeedit_text')
+			.w2tag('You are editing now')
+			.html('EDITING');
+		setTimeout(function() {
+			$('#takeedit_text').w2tag('');
+		}, 5000);
+	}
+	else {
+		toolbar.uncheck('takeedit');
+		toolbar.enable('takeedit');
+		toolbar.set('takeedit', {hint: _('Take edit lock (others can only view)')});
+		$('#takeedit_text')
+			.w2tag('You are viewing now')
+			.html('VIEWING');
+		setTimeout(function() {
+			$('#takeedit_text').w2tag('');
+		}, 5000);
+	}
+});
+
 $(window).resize(function() {
 	resizeToolbar();
 });
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 202fb0d..5eee647 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -330,6 +330,9 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('unocommandresult:')) {
 			this._onUnoCommandResultMsg(textMsg);
 		}
+		else if (textMsg.startsWith('editlock')) {
+			this._onEditLock(textMsg);
+		}
 	},
 
 	_onCommandValuesMsg: function (textMsg) {
@@ -1237,6 +1240,13 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
+	_onEditLock: function (textMsg) {
+		var val = parseInt(textMsg.split(' ')[1]);
+		if (!isNaN(val)) {
+			this._map.fire('editlock', {value: val});
+		}
+	},
+
     _invalidatePreviews: function () {
 		if (this._map._docPreviews && this._previewInvalidations.length > 0) {
 			var toInvalidate = {};
commit b8e9075f23f0f474874c52fd62bd6057de260c86
Author: Pranav Kant <pranavk at collabora.com>
Date:   Wed Mar 23 22:25:28 2016 +0530

    bccu#1621: Introduce an edit lock
    
    ... which can be taken only one at a time. Others can only view,
    not edit. When a session with edit lock exits, the edit lock is
    handed over to the next alive session.
    
    Change-Id: I712a4e70369f1d07c1d83af416a0f5c288b05c7d

diff --git a/loolwsd/ChildProcessSession.cpp b/loolwsd/ChildProcessSession.cpp
index 3c74b15..32bd228 100644
--- a/loolwsd/ChildProcessSession.cpp
+++ b/loolwsd/ChildProcessSession.cpp
@@ -334,7 +334,12 @@ bool ChildProcessSession::_handleInput(const char *buffer, int length)
     const std::string firstLine = getFirstLine(buffer, length);
     StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
 
-    if (tokens[0] == "canceltiles")
+    if (tokens[0] == "dummymsg")
+    {
+        // Just to update the activity of view-only mode
+        return true;
+    }
+    else if (tokens[0] == "canceltiles")
     {
         // this command makes sense only on the command queue level, nothing
         // to do here
diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 5e35a86..c36067e 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -137,4 +137,57 @@ std::string DocumentBroker::getJailRoot() const
     return Poco::Path(_childRoot, _jailId).toString();
 }
 
+void DocumentBroker::takeEditLock(const std::string id)
+{
+    std::lock_guard<std::mutex> sessionsLock(_wsSessionsMutex);
+    for (auto& it: _wsSessions)
+    {
+        if (it.first != id)
+        {
+            it.second->setEditLock(false);
+            it.second->sendTextFrame("editlock 0");
+        }
+        else
+        {
+            it.second->setEditLock(true);
+            it.second->sendTextFrame("editlock 1");
+        }
+    }
+}
+
+void DocumentBroker::addWSSession(const std::string id, std::shared_ptr<MasterProcessSession>& ws)
+{
+    std::lock_guard<std::mutex> sessionsLock(_wsSessionsMutex);
+    auto ret = _wsSessions.emplace(id, ws);
+    if (!ret.second)
+    {
+        Log::warn("DocumentBroker: Trying to add already existed session.");
+    }
+}
+
+void DocumentBroker::removeWSSession(const std::string id)
+{
+    std::lock_guard<std::mutex> sessionsLock(_wsSessionsMutex);
+    bool bEditLock = false;
+    auto it = _wsSessions.find(id);
+    if (it != _wsSessions.end())
+    {
+        if (it->second->isEditLocked())
+            bEditLock = true;
+
+        _wsSessions.erase(it);
+    }
+
+    if (bEditLock)
+    {
+        // pass the edit lock to first session in map
+        it = _wsSessions.begin();
+        if (it != _wsSessions.end())
+        {
+            it->second->setEditLock(true);
+            it->second->sendTextFrame("editlock 1");
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp
index c2fa43b..3d848de 100644
--- a/loolwsd/DocumentBroker.hpp
+++ b/loolwsd/DocumentBroker.hpp
@@ -14,10 +14,12 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <map>
 
 #include <Poco/URI.h>
 
-#include <Util.hpp>
+#include "MasterProcessSession.hpp"
+#include "Util.hpp"
 
 // Forwards.
 class StorageBase;
@@ -68,6 +70,20 @@ public:
 
     std::string getJailRoot() const;
 
+    /// Ignore input events from all web socket sessions
+    /// except this one
+    void takeEditLock(const std::string id);
+
+    void addWSSession(const std::string id, std::shared_ptr<MasterProcessSession>& ws);
+
+    void removeWSSession(const std::string id);
+
+    unsigned getWSSessionsCount() { return _wsSessions.size(); }
+
+public:
+    std::map<std::string, std::shared_ptr<MasterProcessSession>> _wsSessions;
+    std::mutex _wsSessionsMutex;
+
 private:
     const Poco::URI _uriPublic;
     const std::string _docKey;
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 4f6cb52..8246a01 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -178,9 +178,6 @@ using Poco::XML::NodeList;
 static std::map<std::string, std::shared_ptr<DocumentBroker>> docBrokers;
 static std::mutex docBrokersMutex;
 
-static std::unordered_set<std::shared_ptr<MasterProcessSession>> sessions;
-static std::mutex sessionsMutex;
-
 /// Handles the filename part of the convert-to POST request payload.
 class ConvertToPartHandler : public PartHandler
 {
@@ -255,6 +252,7 @@ private:
                     std::shared_ptr<WebSocket> ws;
                     const LOOLSession::Kind kind = LOOLSession::Kind::ToClient;
                     auto session = std::make_shared<MasterProcessSession>(id, kind, ws, docBroker);
+                    session->setEditLock(true);
                     docBroker->incSessions();
                     lock.unlock();
 
@@ -455,10 +453,11 @@ private:
         docBroker->incSessions();
         docBrokersLock.unlock();
 
-        std::unique_lock<std::mutex> sessionsLock(sessionsMutex);
-        sessions.insert(session);
-        Log::debug("sessions++: " + std::to_string(sessions.size()));
-        sessionsLock.unlock();
+        docBroker->addWSSession(id, session);
+        unsigned wsSessionsCount = docBroker->getWSSessionsCount();
+        Log::warn(docKey + ", ws_sessions++: " + std::to_string(wsSessionsCount));
+        if (wsSessionsCount == 1)
+            session->setEditLock(true);
 
         // Request a kit process for this doc.
         const std::string aMessage = "request " + id + " " + docKey + "\n";
@@ -509,10 +508,9 @@ private:
             queue.clear();
         }
 
-        sessionsLock.lock();
-        sessions.erase(session);
-        Log::debug("sessions--: " + std::to_string(sessions.size()));
-        sessionsLock.unlock();
+        docBroker->removeWSSession(id);
+        wsSessionsCount = docBroker->getWSSessionsCount();
+        Log::warn(docKey + ", ws_sessions--: " + std::to_string(wsSessionsCount));
 
         Log::info("Finishing GET request handler for session [" + id + "]. Joining the queue.");
         queue.put("eof");
@@ -1327,16 +1325,20 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
                 Log::debug("30-second check");
                 last30SecCheck = now;
 
-                std::unique_lock<std::mutex> sessionsLock(sessionsMutex);
-                for (auto& it : sessions)
+                std::unique_lock<std::mutex> docBrokersLock(docBrokersMutex);
+                for (auto& brokerIt : docBrokers)
                 {
-                    if (it->_lastMessageTime > it->_idleSaveTime &&
-                        it->_lastMessageTime < now - 30)
+                    std::unique_lock<std::mutex> sessionsLock(brokerIt.second->_wsSessionsMutex);
+                    for (auto& sessionIt: brokerIt.second->_wsSessions)
                     {
-                        // Trigger a .uno:Save
-                        Log::info("Idle save triggered for session " + it->getId());
-
-                        it->_idleSaveTime = now;
+                        if (sessionIt.second->_lastMessageTime > sessionIt.second->_idleSaveTime &&
+                            sessionIt.second->_lastMessageTime < now - 30)
+                        {
+                            // Trigger a .uno:Save
+                            Log::info("Idle save triggered for session " + sessionIt.second->getId());
+
+                            sessionIt.second->_idleSaveTime = now;
+                        }
                     }
                 }
             }
@@ -1345,16 +1347,20 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
                 Log::debug("Five-minute check");
                 lastFiveMinuteCheck = now;
 
-                std::unique_lock<std::mutex> sessionsLock(sessionsMutex);
-                for (auto& it : sessions)
+                std::unique_lock<std::mutex> docBrokersLock(docBrokersMutex);
+                for (auto& brokerIt : docBrokers)
                 {
-                    if (it->_lastMessageTime >= it->_idleSaveTime &&
-                        it->_lastMessageTime >= it->_autoSaveTime)
+                    std::unique_lock<std::mutex> sessionsLock(brokerIt.second->_wsSessionsMutex);
+                    for (auto& sessionIt: brokerIt.second->_wsSessions)
                     {
-                        // Trigger a .uno:Save
-                        Log::info("Auto-save triggered for session " + it->getId());
-
-                        it->_autoSaveTime = now;
+                        if (sessionIt.second->_lastMessageTime >= sessionIt.second->_idleSaveTime &&
+                            sessionIt.second->_lastMessageTime >= sessionIt.second->_autoSaveTime)
+                        {
+                            // Trigger a .uno:Save
+                            Log::info("Auto-save triggered for session " + sessionIt.second->getId());
+
+                            sessionIt.second->_autoSaveTime = now;
+                        }
                     }
                 }
             }
diff --git a/loolwsd/MasterProcessSession.cpp b/loolwsd/MasterProcessSession.cpp
index 90a82af..a494c5e 100644
--- a/loolwsd/MasterProcessSession.cpp
+++ b/loolwsd/MasterProcessSession.cpp
@@ -232,6 +232,11 @@ bool MasterProcessSession::_handleInput(const char *buffer, int length)
             else if (tokens[0] == "status:")
             {
                 _docBroker->tileCache().saveTextFile(std::string(buffer, length), "status.txt");
+
+                // let clients know if they hold the edit lock
+                std::string message = "editlock ";
+                message += std::to_string(peer->isEditLocked());
+                forwardToPeer(message.c_str(), message.size());
             }
             else if (tokens[0] == "commandvalues:")
             {
@@ -294,6 +299,11 @@ bool MasterProcessSession::_handleInput(const char *buffer, int length)
         Log::error(getName() + ": Unexpected request [" + tokens[0] + "].");
         assert(false);
     }
+    else if (tokens[0] == "takeedit")
+    {
+        _docBroker->takeEditLock(getId());
+        return true;
+    }
     else if (tokens[0] == "load")
     {
         if (_docURL != "")
@@ -389,7 +399,12 @@ bool MasterProcessSession::_handleInput(const char *buffer, int length)
             _docBroker->tileCache().removeFile("status.txt");
         }
 
-        if (tokens[0] != "requestloksession")
+        if (_kind == Kind::ToClient && !isEditLocked())
+        {
+            std::string dummyFrame = "dummymsg";
+            forwardToPeer(dummyFrame.c_str(), dummyFrame.size());
+        }
+        else if (tokens[0] != "requestloksession")
         {
             forwardToPeer(buffer, length);
         }
@@ -460,6 +475,10 @@ bool MasterProcessSession::getStatus(const char *buffer, int length)
     if (status.size() > 0)
     {
         sendTextFrame(status);
+        // let clients know if they hold the edit lock
+        std::string message = "editlock ";
+        message += std::to_string(isEditLocked());
+        sendTextFrame(message);
         return true;
     }
 
@@ -812,6 +831,7 @@ void MasterProcessSession::forwardToPeer(const char *buffer, int length)
         Log::error(getName() + ": no peer to forward to.");
         return;
     }
+
     peer->sendBinaryFrame(buffer, length);
 }
 
diff --git a/loolwsd/MasterProcessSession.hpp b/loolwsd/MasterProcessSession.hpp
index be09337..7b57709 100644
--- a/loolwsd/MasterProcessSession.hpp
+++ b/loolwsd/MasterProcessSession.hpp
@@ -14,10 +14,11 @@
 
 #include <Poco/Random.h>
 
-#include "DocumentBroker.hpp"
 #include "LOOLSession.hpp"
 #include "TileCache.hpp"
 
+class DocumentBroker;
+
 class MasterProcessSession final : public LOOLSession, public std::enable_shared_from_this<MasterProcessSession>
 {
  public:
@@ -44,6 +45,11 @@ class MasterProcessSession final : public LOOLSession, public std::enable_shared
 
     std::shared_ptr<DocumentBroker> getDocumentBroker() const { return _docBroker; }
 
+    void setEditLock(const bool value) { _bEditLock = value; }
+
+    bool isEditLocked() const { return _bEditLock; }
+
+public:
     // Sessions to pre-spawned child processes that have connected but are not yet assigned a
     // document to work on.
     static std::map<std::string, std::shared_ptr<MasterProcessSession>> AvailableChildSessions;
@@ -88,6 +94,11 @@ class MasterProcessSession final : public LOOLSession, public std::enable_shared
     /// Kind::ToClient instances store URLs of completed 'save as' documents.
     MessageQueue _saveAsQueue;
     std::shared_ptr<DocumentBroker> _docBroker;
+
+    // If this document holds the edit lock.
+    // An edit lock will only allow the current session to make edits,
+    // while other session opening the same document can only see
+    bool _bEditLock = false;
 };
 
 #endif


More information about the Libreoffice-commits mailing list