[Libreoffice-commits] online.git: 3 commits - common/Util.cpp common/Util.hpp loleaflet/src loolwsd.xml.in wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/Storage.cpp wsd/Storage.hpp
Michael Meeks (via logerrit)
logerrit at kemper.freedesktop.org
Wed Nov 27 19:17:55 UTC 2019
common/Util.cpp | 9 ++
common/Util.hpp | 5 +
loleaflet/src/control/Control.DownloadProgress.js | 2
loleaflet/src/layer/tile/TileLayer.js | 4 -
loleaflet/src/map/Clipboard.js | 42 +++++++++----
loolwsd.xml.in | 4 +
wsd/ClientSession.cpp | 5 +
wsd/DocumentBroker.cpp | 68 ++++++++++++++++------
wsd/DocumentBroker.hpp | 4 +
wsd/LOOLWSD.cpp | 1
wsd/Storage.cpp | 21 ++++++
wsd/Storage.hpp | 8 ++
12 files changed, 136 insertions(+), 37 deletions(-)
New commits:
commit 84c218786b29f7e8b0d3688980a09043041138a2
Author: Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 19:05:57 2019 +0000
Commit: Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000
fix calc simple, single cell content copy; add origin to plain text.
Change-Id: I7a6b0c90a74f6e3a91a840bf77c0935a300321f2
diff --git a/loleaflet/src/control/Control.DownloadProgress.js b/loleaflet/src/control/Control.DownloadProgress.js
index 8df498997..2fce6a714 100644
--- a/loleaflet/src/control/Control.DownloadProgress.js
+++ b/loleaflet/src/control/Control.DownloadProgress.js
@@ -159,7 +159,7 @@ L.Control.DownloadProgress = L.Control.extend({
var idx = text.indexOf('<!DOCTYPE HTML');
if (idx > 0)
text = text.substring(idx, text.length);
- that._map._clip.setTextSelectionContent(text);
+ that._map._clip.setTextSelectionHTML(text);
};
// TODO: failure to parse ? ...
reader.readAsText(response);
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 25cf5ba5a..1be76a03c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -597,7 +597,7 @@ L.TileLayer = L.GridLayer.extend({
this._onTextSelectionMsg(textMsg);
}
else if (textMsg.startsWith('textselectioncontent:')) {
- this._map._clip.setTextSelectionContent(textMsg.substr(22));
+ this._map._clip.setTextSelectionHTML(textMsg.substr(22));
}
else if (textMsg.startsWith('textselectionend:')) {
this._onTextSelectionEndMsg(textMsg);
@@ -733,7 +733,7 @@ L.TileLayer = L.GridLayer.extend({
// message is received from lowsd, *then* a 'celladdress' message.
var address = textMsg.substring(13);
if (!this._map['wopi'].DisableCopy) {
- this._map._clip.setTextSelectionContent(this._lastFormula);
+ this._map._clip.setTextSelectionText(this._lastFormula);
}
this._map.fire('celladdress', {address: address});
},
diff --git a/loleaflet/src/map/Clipboard.js b/loleaflet/src/map/Clipboard.js
index 1acfff606..2808f2690 100644
--- a/loleaflet/src/map/Clipboard.js
+++ b/loleaflet/src/map/Clipboard.js
@@ -109,21 +109,30 @@ L.Clipboard = L.Class.extend({
return text.indexOf(this._getHtmlStubMarker()) > 0;
},
+ // wrap some content with our stub magic
+ _originWrapBody: function(body, isStub) {
+ var encodedOrigin = encodeURIComponent(this.getMetaPath());
+ var text = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' +
+ '<html>\n' +
+ ' <head>\n';
+ if (isStub)
+ text += ' ' + this._getHtmlStubMarker() + '\n';
+ text += ' <meta http-equiv="content-type" content="text/html; charset=utf-8"/>\n' +
+ ' <meta name="origin" content="' + encodedOrigin + '"/>\n' +
+ ' </head>\n'
+ + body +
+ '</html>';
+ return text;
+ },
+
+ // what an empty clipboard has on it
_getStubHtml: function() {
var lang = 'en_US'; // FIXME: l10n
- var encodedOrigin = encodeURIComponent(this.getMetaPath());
- var stub = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' +
- '<html>\n' +
- ' <head>\n' +
- ' ' + this._getHtmlStubMarker() + '\n' +
- ' <meta http-equiv="content-type" content="text/html; charset=utf-8"/>\n' +
- ' <meta name="origin" content="' + encodedOrigin + '"/>\n' +
- ' </head>\n' +
+ return this._substProductName(this._originWrapBody(
' <body lang="' + lang + '" dir="ltr">\n' +
' <p>' + _('To paste outside %productName, please first click the \'download\' button') + '</p>\n' +
- ' </body>\n' +
- '</html>';
- return this._substProductName(stub);
+ ' </body>\n', true
+ ));
},
_getMetaOrigin: function (html) {
@@ -688,9 +697,16 @@ L.Clipboard = L.Class.extend({
},
// textselectioncontent: message
- setTextSelectionContent: function(text) {
+ setTextSelectionHTML: function(html) {
+ this._selectionType = 'text';
+ this._selectionContent = html;
+ },
+
+ // sets the selection to some (cell formula) text)
+ setTextSelectionText: function(text) {
this._selectionType = 'text';
- this._selectionContent = text;
+ this._selectionContent = this._originWrapBody(
+ '<body>' + text + '</body>');
},
// complexselection: message
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index db51d5836..51ec92ef5 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1006,7 +1006,10 @@ void ClientSession::postProcessCopyPayload(std::shared_ptr<Message> payload)
{
// Insert our meta origin if we can
payload->rewriteDataBody([=](std::vector<char>& data) {
- const size_t pos = Util::findInVector(data, "<meta name=\"generator\" content=\"");
+ size_t pos = Util::findInVector(data, "<meta name=\"generator\" content=\"");
+
+ if (pos == std::string::npos)
+ pos = Util::findInVector(data, "<meta http-equiv=\"content-type\" content=\"text/html;");
// cf. TileLayer.js /_dataTransferToDocument/
if (pos != std::string::npos) // assume text/html
commit 0d97efbfccc680e961086705e8a855a38a7af6c4
Author: Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 08:43:05 2019 +0000
Commit: Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000
locking: renew lock after timeout.
Change-Id: I6191ee34239b978292aeb6795be74312a954e240
diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 416dd9b2a..d8e19b9c9 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -124,6 +124,10 @@
<host desc="Regex pattern of hostname to allow or deny." allow="false">192\.168\.1\.1</host>
<max_file_size desc="Maximum document size in bytes to load. 0 for unlimited." type="uint">0</max_file_size>
<reuse_cookies desc="When enabled, cookies from the browser will be captured and set on WOPI requests." type="bool" default="false">false</reuse_cookies>
+ <locking desc="Locking settings">
+ <refresh desc="How frequently we should re-acquire a lock with the storage server, in seconds (default 15 mins) or 0 for no refresh"
+ type="int" default="900">900</refresh>
+ </locking>
</wopi>
<webdav desc="Allow/deny webdav storage. Mutually exclusive with wopi." allow="false">
<host desc="Hostname to allow" allow="false">localhost</host>
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 8cb159e4d..ddfaa922d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -323,6 +323,9 @@ void DocumentBroker::pollThread()
adminRecv = recv;
LOG_TRC("Doc [" << _docKey << "] added stats sent: " << sent << ", recv: " << recv << " bytes to totals.");
}
+
+ if (_storage && _lockCtx->needsRefresh(now))
+ refreshLock();
#endif
if (isSaving() &&
@@ -1078,23 +1081,10 @@ void DocumentBroker::setLoaded()
}
}
-bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
+std::string DocumentBroker::getWriteableSessionId() const
{
assertCorrectThread();
- LOG_TRC("autoSave(): forceful? " << force);
- if (_sessions.empty() || _storage == nullptr || !_isLoaded ||
- !_childProcess->isAlive() || (!_isModified && !force))
- {
- // Nothing to do.
- LOG_TRC("Nothing to autosave [" << _docKey << "].");
- return false;
- }
-
- // Remember the last save time, since this is the predicate.
- LOG_TRC("Checking to autosave [" << _docKey << "].");
-
- // Which session to use when auto saving ?
std::string savingSessionId;
for (auto& sessionIt : _sessions)
{
@@ -1113,6 +1103,45 @@ bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
break;
}
}
+ return savingSessionId;
+}
+
+void DocumentBroker::refreshLock()
+{
+ assertCorrectThread();
+
+ std::string savingSessionId = getWriteableSessionId();
+ LOG_TRC("Refresh lock " << _lockCtx->_lockToken << " with session " << savingSessionId);
+
+ auto it = _sessions.find(savingSessionId);
+ if (it == _sessions.end())
+ LOG_ERR("No write-able session to refresh lock with");
+ else
+ {
+ std::shared_ptr<ClientSession> session = it->second;
+ if (!session || !_storage->updateLockState(session->getAuthorization(), *_lockCtx, true))
+ LOG_ERR("Failed to refresh lock");
+ }
+}
+
+bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
+{
+ assertCorrectThread();
+
+ LOG_TRC("autoSave(): forceful? " << force);
+ if (_sessions.empty() || _storage == nullptr || !_isLoaded ||
+ !_childProcess->isAlive() || (!_isModified && !force))
+ {
+ // Nothing to do.
+ LOG_TRC("Nothing to autosave [" << _docKey << "].");
+ return false;
+ }
+
+ // Remember the last save time, since this is the predicate.
+ LOG_TRC("Checking to autosave [" << _docKey << "].");
+
+ // Which session to use when auto saving ?
+ std::string savingSessionId = getWriteableSessionId();
bool sent = false;
if (force)
@@ -2225,6 +2254,7 @@ void DocumentBroker::dumpState(std::ostream& os)
os << "\n idle time: " << getIdleTimeSecs();
os << "\n cursor " << _cursorPosX << ", " << _cursorPosY
<< "( " << _cursorWidth << "," << _cursorHeight << ")\n";
+ _lockCtx->dumpState(os);
if (_tileCache)
_tileCache->dumpState(os);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index f0a1ac061..91daee6a2 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -369,6 +369,10 @@ public:
size_t getMemorySize() const;
private:
+ /// get the session id of a session that can write the document for save / locking.
+ std::string getWriteableSessionId() const;
+
+ void refreshLock();
/// Loads a document from the public URI into the jail.
bool load(const std::shared_ptr<ClientSession>& session, const std::string& jailId);
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index c3e22d240..221d179ef 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -860,6 +860,7 @@ void LOOLWSD::initialize(Application& self)
{ "storage.wopi.host[0][@allow]", "true" },
{ "storage.wopi.max_file_size", "0" },
{ "storage.wopi[@allow]", "true" },
+ { "storage.wopi.locking.refresh", "900" },
{ "sys_template_path", "systemplate" },
{ "trace.path[@compress]", "true" },
{ "trace.path[@snapshot]", "false" },
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index efe0fc130..e8b769d3f 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -472,6 +472,24 @@ void LockContext::initSupportsLocks()
_lockToken = "lool-lock" + Util::rng::getHexString(8);
}
+bool LockContext::needsRefresh(const std::chrono::steady_clock::time_point &now) const
+{
+ static int refreshSeconds = LOOLWSD::getConfigValue<int>("storage.wopi.locking.refresh", 900);
+ return _supportsLocks && _isLocked && refreshSeconds > 0 &&
+ std::chrono::duration_cast<std::chrono::seconds>
+ (now - _lastLockTime).count() >= refreshSeconds;
+}
+
+void LockContext::dumpState(std::ostream& os)
+{
+ if (!_supportsLocks)
+ return;
+ os << " lock:\n";
+ os << " locked: " << _isLocked << "\n";
+ os << " token: '" << _lockToken << "'\n";
+ os << " last locked: " << Util::getSteadyClockAsString(_lastLockTime) << "\n";
+}
+
std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth, LockContext &lockCtx)
{
// update the access_token to the one matching to the session
@@ -734,11 +752,12 @@ bool WopiStorage::updateLockState(const Authorization &auth, LockContext &lockCt
if (response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK)
{
lockCtx._isLocked = lock;
+ lockCtx._lastLockTime = std::chrono::steady_clock::now();
return true;
}
else
{
- LOG_WRN("Un-successfull " << wopiLog << " with status " << response.getStatus() <<
+ LOG_WRN("Un-successful " << wopiLog << " with status " << response.getStatus() <<
" and response: " << responseString);
}
}
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index cad148c7e..1e6eb18d3 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -13,6 +13,7 @@
#include <set>
#include <string>
+#include <chrono>
#include <Poco/URI.h>
#include <Poco/Util/Application.h>
@@ -34,11 +35,18 @@ struct LockContext
bool _isLocked;
/// Name if we need it to use consistently for locking
std::string _lockToken;
+ /// Time of last successful lock (re-)acquisition
+ std::chrono::steady_clock::time_point _lastLockTime;
LockContext() : _supportsLocks(false), _isLocked(false) { }
/// one-time setup for supporting locks & create token
void initSupportsLocks();
+
+ /// do we need to refresh our lock ?
+ bool needsRefresh(const std::chrono::steady_clock::time_point &now) const;
+
+ void dumpState(std::ostream& os);
};
/// Base class of all Storage abstractions.
commit e3864a060e95f224edfed0cd35d4f12143bfba56
Author: Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 09:12:59 2019 +0000
Commit: Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000
Dump DocumentBroker state more completely.
Change-Id: I3477fe70ba25d6e9a95c12f30138c3353994e384
diff --git a/common/Util.cpp b/common/Util.cpp
index 931de51d2..4dc0bdadf 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -900,6 +900,15 @@ namespace Util
return timestamp;
}
+
+ std::string getSteadyClockAsString(const std::chrono::steady_clock::time_point &time)
+ {
+ auto now = std::chrono::steady_clock::now();
+ const std::time_t t = std::chrono::system_clock::to_time_t(
+ std::chrono::time_point_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now() + (time - now)));
+ return std::ctime(&t);
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util.hpp b/common/Util.hpp
index 66ce0b5b1..5dc39f942 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -939,9 +939,12 @@ int main(int argc, char**argv)
/// Convert a time_point to iso8601 formatted string.
std::string time_point_to_iso8601(std::chrono::system_clock::time_point tp);
- //// Convert time from ISO8061 fraction format
+ /// Convert time from ISO8061 fraction format
std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName);
+ /// conversion from steady_clock for debugging / tracing
+ std::string getSteadyClockAsString(const std::chrono::steady_clock::time_point &time);
+
/// Automatically execute code at end of current scope.
/// Used for exception-safe code.
class ScopeGuard
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 7d669e00c..8cb159e4d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -2217,10 +2217,12 @@ void DocumentBroker::dumpState(std::ostream& os)
os << "\n doc key: " << _docKey;
os << "\n doc id: " << _docId;
os << "\n num sessions: " << _sessions.size();
- const std::time_t t = std::chrono::system_clock::to_time_t(
- std::chrono::time_point_cast<std::chrono::seconds>(
- std::chrono::system_clock::now() + (_lastSaveTime - now)));
- os << "\n last saved: " << std::ctime(&t);
+ os << "\n last saved: " << Util::getSteadyClockAsString(_lastSaveTime);
+ os << "\n last save request: " << Util::getSteadyClockAsString(_lastSaveRequestTime);
+ os << "\n last save response: " << Util::getSteadyClockAsString(_lastSaveResponseTime);
+ os << "\n last modifed: " << Util::getHttpTime(_documentLastModifiedTime);
+ os << "\n file last modifed: " << Util::getHttpTime(_lastFileModifiedTime);
+ os << "\n idle time: " << getIdleTimeSecs();
os << "\n cursor " << _cursorPosX << ", " << _cursorPosY
<< "( " << _cursorWidth << "," << _cursorHeight << ")\n";
if (_tileCache)
More information about the Libreoffice-commits
mailing list