[libreoffice-online] 01/04: Imported Upstream version 5.4.0.0.beta2

Rene Engelhard rene at moszumanska.debian.org
Thu Jun 15 19:11:15 UTC 2017


This is an automated email from the git hooks/post-receive script.

rene pushed a commit to branch experimental
in repository libreoffice-online.

commit a7b23dd11ae2a1bce4b20695e96cf3917e9330da
Author: Rene Engelhard <rene at rene-engelhard.de>
Date:   Tue Jun 13 09:54:07 2017 +0200

    Imported Upstream version 5.4.0.0.beta2
---
 Makefile.am                               |   9 --
 Makefile.in                               |   7 +-
 common/Session.cpp                        |   3 +-
 configure                                 |  20 ++--
 configure.ac                              |   2 +-
 dist_git_hash                             |   2 +-
 kit/ChildSession.cpp                      |  20 +---
 kit/ChildSession.hpp                      |   5 +-
 kit/Kit.cpp                               |  37 ++++----
 loleaflet/dist/errormessages.js           |   6 +-
 loleaflet/dist/toolbar/toolbar.js         |   5 +-
 loleaflet/main.js                         |   2 +-
 loleaflet/po/help-de.po                   |   6 +-
 loleaflet/po/ui-cs.po                     |  20 ++--
 loleaflet/po/ui-da.po                     |  23 +++--
 loleaflet/po/ui-eu.po                     |   8 +-
 loleaflet/po/ui-pt.po                     |   8 +-
 loleaflet/src/control/Control.Dialog.js   |   5 +-
 loleaflet/src/control/Control.Header.js   |  11 ++-
 loleaflet/src/core/Socket.js              |  36 ++++++--
 loleaflet/src/layer/AnnotationManager.js  |  16 ++++
 loleaflet/src/map/Map.js                  |  15 ++-
 loleaflet/src/map/handler/Map.Keyboard.js |   3 +-
 loleaflet/src/map/handler/Map.WOPI.js     |   6 +-
 loolwsd.spec                              |   6 +-
 test/WhiteBoxTests.cpp                    |   2 +-
 test/httpwstest.cpp                       |  53 +++++++++++
 wsd/ClientSession.cpp                     |   5 +-
 wsd/DocumentBroker.cpp                    | 147 ++++++++++++++++--------------
 wsd/DocumentBroker.hpp                    |   8 +-
 wsd/Exceptions.hpp                        |   8 ++
 wsd/FileServer.cpp                        |   5 +-
 wsd/LOOLWSD.cpp                           |  17 +++-
 wsd/Storage.cpp                           |  92 ++++++++-----------
 wsd/Storage.hpp                           |  53 +----------
 wsd/reference.txt                         |   8 +-
 36 files changed, 371 insertions(+), 308 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 73c2614..9cc4402 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -107,15 +107,6 @@ loolwsd_fuzzer_SOURCES = $(loolwsd_sources) \
                          $(shared_sources) \
                          kit/DummyLibreOfficeKit.cpp
 
-loolnb_SOURCES = net/loolnb.cpp \
-                 net/Socket.cpp \
-                 common/Log.cpp \
-                 common/SigUtil.cpp \
-                 common/Util.cpp
-if ENABLE_SSL
-loolnb_SOURCES += net/Ssl.cpp
-endif
-
 clientnb_SOURCES = net/clientnb.cpp \
                    common/Log.cpp \
                    common/Util.cpp
diff --git a/Makefile.in b/Makefile.in
index 2c0722d..bf49f71 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -89,8 +89,7 @@ bin_PROGRAMS = loolwsd$(EXEEXT) loolforkit$(EXEEXT) loolmap$(EXEEXT) \
 noinst_PROGRAMS = clientnb$(EXEEXT) connect$(EXEEXT) \
 	lokitclient$(EXEEXT) loolforkit-nocaps$(EXEEXT) \
 	loolwsd_fuzzer$(EXEEXT)
- at ENABLE_SSL_TRUE@am__append_4 = net/Ssl.cpp
- at ENABLE_SSL_TRUE@am__append_5 = net/Ssl.hpp \
+ at ENABLE_SSL_TRUE@am__append_4 = net/Ssl.hpp \
 @ENABLE_SSL_TRUE@                  net/SslSocket.hpp
 
 subdir = .
@@ -639,8 +638,6 @@ loolwsd_fuzzer_SOURCES = $(loolwsd_sources) \
                          $(shared_sources) \
                          kit/DummyLibreOfficeKit.cpp
 
-loolnb_SOURCES = net/loolnb.cpp net/Socket.cpp common/Log.cpp \
-	common/SigUtil.cpp common/Util.cpp $(am__append_4)
 clientnb_SOURCES = net/clientnb.cpp \
                    common/Log.cpp \
                    common/Util.cpp
@@ -681,7 +678,7 @@ shared_headers = common/Common.hpp common/IoUtil.hpp \
 	common/Rectangle.hpp common/SigUtil.hpp common/security.h \
 	common/SpookyV2.h net/DelaySocket.hpp net/ServerSocket.hpp \
 	net/Socket.hpp net/WebSocketHandler.hpp tools/Replay.hpp \
-	$(am__append_5)
+	$(am__append_4)
 kit_headers = kit/ChildSession.hpp \
               kit/DummyLibreOfficeKit.hpp \
               kit/Kit.hpp \
diff --git a/common/Session.cpp b/common/Session.cpp
index d875668..d29613d 100644
--- a/common/Session.cpp
+++ b/common/Session.cpp
@@ -164,8 +164,7 @@ void Session::shutdown(const WebSocketHandler::StatusCodes statusCode, const std
             static_cast<unsigned>(statusCode) << "] and reason [" << statusMessage << "].");
 
     // See protocol.txt for this application-level close frame.
-    const std::string msg = "close: " + statusMessage;
-    sendTextFrame(msg.data(), msg.size());
+    sendMessage("close: " + statusMessage);
 
     WebSocketHandler::shutdown(statusCode, statusMessage);
 }
diff --git a/configure b/configure
index 7325efd..790a4d8 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for libreoffice-online 5.4.0.0.beta1.
+# Generated by GNU Autoconf 2.69 for libreoffice-online 5.4.0.0.beta2.
 #
 # Report bugs to <libreoffice at lists.freedesktop.org>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='libreoffice-online'
 PACKAGE_TARNAME='libreoffice-online'
-PACKAGE_VERSION='5.4.0.0.beta1'
-PACKAGE_STRING='libreoffice-online 5.4.0.0.beta1'
+PACKAGE_VERSION='5.4.0.0.beta2'
+PACKAGE_STRING='libreoffice-online 5.4.0.0.beta2'
 PACKAGE_BUGREPORT='libreoffice at lists.freedesktop.org'
 PACKAGE_URL=''
 
@@ -1376,7 +1376,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures libreoffice-online 5.4.0.0.beta1 to adapt to many kinds of systems.
+\`configure' configures libreoffice-online 5.4.0.0.beta2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1447,7 +1447,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libreoffice-online 5.4.0.0.beta1:";;
+     short | recursive ) echo "Configuration of libreoffice-online 5.4.0.0.beta2:";;
    esac
   cat <<\_ACEOF
 
@@ -1599,7 +1599,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libreoffice-online configure 5.4.0.0.beta1
+libreoffice-online configure 5.4.0.0.beta2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2089,7 +2089,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by libreoffice-online $as_me 5.4.0.0.beta1, which was
+It was created by libreoffice-online $as_me 5.4.0.0.beta2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -11466,7 +11466,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='libreoffice-online'
- VERSION='5.4.0.0.beta1'
+ VERSION='5.4.0.0.beta2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -17571,7 +17571,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by libreoffice-online $as_me 5.4.0.0.beta1, which was
+This file was extended by libreoffice-online $as_me 5.4.0.0.beta2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -17637,7 +17637,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-libreoffice-online config.status 5.4.0.0.beta1
+libreoffice-online config.status 5.4.0.0.beta2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 876bb9b..446c1f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.69])
 
-AC_INIT([libreoffice-online], [5.4.0.0.beta1], [libreoffice at lists.freedesktop.org])
+AC_INIT([libreoffice-online], [5.4.0.0.beta2], [libreoffice at lists.freedesktop.org])
 LT_INIT([shared, disable-static, dlopen])
 
 AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects tar-pax -Wno-portability])
diff --git a/dist_git_hash b/dist_git_hash
index 12d7ea7..93404fd 100644
--- a/dist_git_hash
+++ b/dist_git_hash
@@ -1 +1 @@
-3cb12a2
+b396188
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 1fbc194..d68bcf5 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -97,19 +97,14 @@ bool ChildSession::_handleInput(const char *buffer, int length)
 
         getLOKitDocument()->setView(_viewId);
 
-        // Get the list of view ids from the core
-        const int viewCount = getLOKitDocument()->getViewsCount();
-        std::vector<int> viewIds(viewCount);
-        getLOKitDocument()->getViewIds(viewIds.data(), viewCount);
-
         int curPart = 0;
         if (getLOKitDocument()->getDocumentType() != LOK_DOCTYPE_TEXT)
             curPart = getLOKitDocument()->getPart();
 
-        lockLokDoc.unlock();
-
         // Notify all views about updated view info
-        _docManager.notifyViewInfo(viewIds);
+        _docManager.notifyViewInfo();
+
+        lockLokDoc.unlock();
 
         if (getLOKitDocument()->getDocumentType() != LOK_DOCTYPE_TEXT)
         {
@@ -370,15 +365,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, const s
         return false;
     }
 
-    // Get the list of view ids from the core
-    const int viewCount = getLOKitDocument()->getViewsCount();
-    std::vector<int> viewIds(viewCount);
-    getLOKitDocument()->getViewIds(viewIds.data(), viewCount);
-
-    lockLokDoc.unlock();
-
     // Inform everyone (including this one) about updated view info
-    _docManager.notifyViewInfo(viewIds);
+    _docManager.notifyViewInfo();
 
     LOG_INF("Loaded session " << getId());
     return true;
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index b1fad05..703df77 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -45,8 +45,9 @@ public:
     /// Access to the document instance.
     virtual std::shared_ptr<lok::Document> getLOKitDocument() = 0;
 
-    /// Send updated view info to all active sessions
-    virtual void notifyViewInfo(const std::vector<int>& viewIds) = 0;
+    /// Send updated view info to all active sessions.
+    virtual void notifyViewInfo() = 0;
+
     /// Get a view ID <-> UserInfo map.
     virtual std::map<int, UserInfo> getViewInfo() = 0;
     virtual std::mutex& getMutex() = 0;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 628e929..304a62e 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1022,14 +1022,8 @@ private:
 
         if (viewCount > 0)
         {
-            // Get the list of view ids from the core
-            std::vector<int> viewIds(viewCount);
-            _loKitDocument->getViewIds(viewIds.data(), viewCount);
-
-            lockLokDoc.unlock();
-
             // Broadcast updated view info
-            notifyViewInfo(viewIds);
+            notifyViewInfo();
         }
     }
 
@@ -1051,11 +1045,18 @@ private:
     }
 
     /// Notify all views of viewId and their associated usernames
-    void notifyViewInfo(const std::vector<int>& viewIds) override
+    void notifyViewInfo() override
     {
-        // Store the list of viewid, username mapping in a map
-        std::map<int, UserInfo> viewInfoMap = getViewInfo();
-        std::map<std::string, int> viewColorsMap = getViewColors();
+        Util::assertIsLocked(_documentMutex);
+
+        // Get the list of view ids from the core
+        const int viewCount = getLOKitDocument()->getViewsCount();
+        std::vector<int> viewIds(viewCount);
+        getLOKitDocument()->getViewIds(viewIds.data(), viewCount);
+
+        const std::map<int, UserInfo> viewInfoMap = _sessionUserInfo;
+
+        const std::map<std::string, int> viewColorsMap = getViewColors();
 
         // Double check if list of viewids from core and our list matches,
         // and create an array of JSON objects containing id and username
@@ -1101,17 +1102,13 @@ private:
     // Get the color value for all author names from the core
     std::map<std::string, int> getViewColors()
     {
-        std::string colorValues;
-        std::map<std::string, int> viewColors;
-
-        {
-            std::unique_lock<std::mutex> lock(_documentMutex);
+        Util::assertIsLocked(_documentMutex);
 
-            char* values = _loKitDocument->getCommandValues(".uno:TrackedChangeAuthors");
-            colorValues = std::string(values == nullptr ? "" : values);
-            std::free(values);
-        }
+        char* values = _loKitDocument->getCommandValues(".uno:TrackedChangeAuthors");
+        const std::string colorValues = std::string(values == nullptr ? "" : values);
+        std::free(values);
 
+        std::map<std::string, int> viewColors;
         try
         {
             if (!colorValues.empty())
diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index d1eccac..8ea1185 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -10,6 +10,8 @@ exports.sessionexpired = _('Your session has been expired. Further changes to do
 exports.faileddocloading = _('Failed to load the document. Please ensure the file type is supported and not corrupted, and try again.');
 
 exports.storage = {
-	savediskfull: _('Save failed due to no disk space left on storage server. Document will now be read-only. Please contact the server administrator to continue editing.'),
-	savefailed: _('Document cannot be saved to storage. Check your permissions or contact the storage server administrator.')
+	loadfailed: _('Failed to read document from storage. Please contact your storage server (%storageserver) administrator.'),
+	savediskfull: _('Save failed due to no disk space left on storage server. Document will now be read-only. Please contact the server (%storageserver) administrator to continue editing.'),
+	saveunauthorized: _('Document cannot be saved to storage server (%storageserver) due to expired or invalid access token. Refresh your session to not to lose your work.'),
+	savefailed: _('Document cannot be saved to storage. Check your permissions or contact the storage server (%storageserver) administrator.')
 };
diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 55d5035..d98dbef 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -219,7 +219,7 @@ function onClick(id, item, subItem) {
 		}
 	}
 	else if (id === 'save') {
-		map.save(true, true);
+		map.save(false /* An explicit save should terminate cell edit */, false /* An explicit save should save it again */);
 	}
 	else if (id === 'repair') {
 		map._socket.sendMessage('commandvalues command=.uno:DocumentRepair');
@@ -1331,6 +1331,7 @@ function updateCommandValues() {
 		if (typeof commandValues === 'undefined') {
 			return;
 		}
+		data = []; // reset data in order to avoid that the font select box is populated with styles, too.
 		// Old browsers like IE11 et al don't like Object.keys with
 		// empty arguments
 		if (typeof commandValues === 'object') {
@@ -1426,7 +1427,7 @@ map.on('commandresult', function (e) {
 		}
 	}
 	else if ((commandName === '.uno:Undo' || commandName === '.uno:Redo') &&
-		e.success === true && e.result.value && e.result.value === '130') { /*UNDO_CONFLICT*/
+		e.success === true && e.result.value && !isNaN(e.result.value)) { /*UNDO_CONFLICT*/
 		$('#tb_toolbar-up_item_repair').w2overlay({ html: '<div style="padding: 10px; line-height: 150%">' +
 			_('Conflict Undo/Redo with multiple users. Please use document repair to resolve') + '</div>'});
 	}
diff --git a/loleaflet/main.js b/loleaflet/main.js
index fed5957..48200cd 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -113,7 +113,7 @@ var map = L.map('map', {
 	wopi: isWopi,
 	alwaysActive: alwaysActive,
 	idleTimeoutSecs: idleTimeoutSecs,  // Dim when user is idle.
-	outOfFocusTimeoutSecs: outOfFocusTimeoutSecs, // Dim after switching tabs.
+	outOfFocusTimeoutSecs: outOfFocusTimeoutSecs // Dim after switching tabs.
 });
 // toolbar.js (loaded in <script> tag accesses map as global variable,
 // so expose it
diff --git a/loleaflet/po/help-de.po b/loleaflet/po/help-de.po
index 06d6437..6140af0 100644
--- a/loleaflet/po/help-de.po
+++ b/loleaflet/po/help-de.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-24 18:10+0000\n"
+"POT-Creation-Date: 2017-06-07 08:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10,7 +10,7 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Translate Toolkit 2.0.0\n"
+"X-Generator: Translate Toolkit 2.1.0\n"
 "X-Pootle-Path: /de/libo_online/loleaflet-help-de.po\n"
 "X-Pootle-Revision: 829964\n"
 
@@ -775,7 +775,7 @@ msgstr "Strg + Alt + C"
 
 #: dist%2Floleaflet-help.html+div.div.table.tr.td%3A123
 msgid "Insert soft hyphen"
-msgstr "Weichen Trennstrich einfügen"
+msgstr "Weiches Trennzeichen einfügen"
 
 #: dist%2Floleaflet-help.html+div.div.table.tr.td%3A123
 msgid "Ctrl + -"
diff --git a/loleaflet/po/ui-cs.po b/loleaflet/po/ui-cs.po
index 19ecaea..2b77fca 100644
--- a/loleaflet/po/ui-cs.po
+++ b/loleaflet/po/ui-cs.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-22 08:00+0000\n"
+"POT-Creation-Date: 2017-06-07 08:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10,9 +10,9 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Translate Toolkit 2.0.0\n"
+"X-Generator: Translate Toolkit 2.1.0\n"
 "X-Pootle-Path: /cs/libo_online/loleaflet-ui-cs.po\n"
-"X-Pootle-Revision: 2522834\n"
+"X-Pootle-Revision: 2581025\n"
 
 #: admin.strings.js:5
 msgid "Admin console"
@@ -2004,12 +2004,11 @@ msgstr ""
 
 #: src/control/Control.Menubar.js:35
 msgid "Previous"
-msgstr ""
+msgstr "Předchozí"
 
 #: src/control/Control.Menubar.js:36
-#, fuzzy
 msgid "Next"
-msgstr "Text"
+msgstr "Další"
 
 #: src/control/Control.Menubar.js:39
 #: src/control/Control.Menubar.js:194
@@ -2024,9 +2023,8 @@ msgid "Full screen"
 msgstr "Celá obrazovka"
 
 #: src/control/Control.Menubar.js:46
-#, fuzzy
 msgid "Formatting Marks"
-msgstr "Formátovací značka"
+msgstr "Řídicí znaky"
 
 #: src/control/Control.Menubar.js:50
 #: src/control/Control.Menubar.js:202
@@ -2577,7 +2575,7 @@ msgstr "Toto je nepříjemné, nemůžeme se připojit k dokumentu. Zkuste to zn
 
 #: src/layer/marker/Annotation.js:156
 msgid "Accept change"
-msgstr ""
+msgstr "Přijmout změnu"
 
 #: src/layer/marker/Annotation.js:161
 msgid "Reject change"
@@ -2589,11 +2587,11 @@ msgstr "Otevřít nabídku"
 
 #: src/layer/tile/TileLayer.js:182
 msgid "Modify"
-msgstr ""
+msgstr "Změnit"
 
 #: src/layer/tile/TileLayer.js:194
 msgid "Remove"
-msgstr ""
+msgstr "Odstranit"
 
 #: src/map/Map.js:131
 msgid "Initializing..."
diff --git a/loleaflet/po/ui-da.po b/loleaflet/po/ui-da.po
index fc5d30c..310b451 100644
--- a/loleaflet/po/ui-da.po
+++ b/loleaflet/po/ui-da.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-22 08:00+0000\n"
+"POT-Creation-Date: 2017-06-07 08:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10,9 +10,9 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Translate Toolkit 2.0.0\n"
+"X-Generator: Translate Toolkit 2.1.0\n"
 "X-Pootle-Path: /da/libo_online/loleaflet-ui-da.po\n"
-"X-Pootle-Revision: 2561692\n"
+"X-Pootle-Revision: 2604903\n"
 
 #: admin.strings.js:5
 msgid "Admin console"
@@ -724,7 +724,7 @@ msgstr "Dig"
 
 #: dist/toolbar/toolbar.js:1604
 msgid "Readonly"
-msgstr ""
+msgstr "Skrivebeskyttet"
 
 #: src/admin/AdminSocketBase.js:46
 msgid "Connection error"
@@ -1870,19 +1870,19 @@ msgstr "Optimal kolonnebredde"
 
 #: src/control/Control.ContextMenu.js:135
 msgid "Internal Cut"
-msgstr ""
+msgstr "Klip internt"
 
 #: src/control/Control.ContextMenu.js:138
 msgid "Internal Copy"
-msgstr ""
+msgstr "Kopier internt"
 
 #: src/control/Control.ContextMenu.js:141
 msgid "Internal Paste"
-msgstr ""
+msgstr "Indsæt internt"
 
 #: src/control/Control.ContextMenu.js:163
 msgid "Internal Paste Special"
-msgstr ""
+msgstr "Indsæt speciel internt"
 
 #: src/control/Control.DocumentRepair.js:32
 msgid "Repair Document"
@@ -1991,7 +1991,7 @@ msgstr "Vælg alle"
 
 #: src/control/Control.Menubar.js:31
 msgid "Track Changes"
-msgstr ""
+msgstr "Registrer ændringer"
 
 #: src/control/Control.Menubar.js:32
 msgid "Record"
@@ -2006,9 +2006,8 @@ msgid "Previous"
 msgstr "Forrige"
 
 #: src/control/Control.Menubar.js:36
-#, fuzzy
 msgid "Next"
-msgstr "Tekst"
+msgstr "Næste"
 
 #: src/control/Control.Menubar.js:39
 #: src/control/Control.Menubar.js:194
@@ -2447,7 +2446,7 @@ msgstr "Er du sikker på, at du vil slette dette dias?"
 
 #: src/control/Control.MetricInput.js:38
 msgid "(100th/mm)"
-msgstr ""
+msgstr "(100th/mm)"
 
 #: src/control/Control.MetricInput.js:44
 msgid "Add: "
diff --git a/loleaflet/po/ui-eu.po b/loleaflet/po/ui-eu.po
index 7933a70..e40195a 100644
--- a/loleaflet/po/ui-eu.po
+++ b/loleaflet/po/ui-eu.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-22 08:01+0000\n"
+"POT-Creation-Date: 2017-06-07 08:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10,9 +10,9 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Translate Toolkit 2.0.0\n"
+"X-Generator: Translate Toolkit 2.1.0\n"
 "X-Pootle-Path: /eu/libo_online/loleaflet-ui-eu.po\n"
-"X-Pootle-Revision: 2534367\n"
+"X-Pootle-Revision: 2608213\n"
 
 #: admin.strings.js:5
 msgid "Admin console"
@@ -2446,7 +2446,7 @@ msgstr "Ziur zaude diapositiba hau ezabatu nahi duzula?"
 
 #: src/control/Control.MetricInput.js:38
 msgid "(100th/mm)"
-msgstr ""
+msgstr "(1/100 mm)"
 
 #: src/control/Control.MetricInput.js:44
 msgid "Add: "
diff --git a/loleaflet/po/ui-pt.po b/loleaflet/po/ui-pt.po
index f547f6a..296c277 100644
--- a/loleaflet/po/ui-pt.po
+++ b/loleaflet/po/ui-pt.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-04-24 08:02+0000\n"
+"POT-Creation-Date: 2017-06-07 12:29+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10,9 +10,9 @@ msgstr ""
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Translate Toolkit 2.0.0\n"
+"X-Generator: Translate Toolkit 2.1.0\n"
 "X-Pootle-Path: /pt/libo_online/loleaflet-ui-pt.po\n"
-"X-Pootle-Revision: 2509302\n"
+"X-Pootle-Revision: 2578810\n"
 
 #: admin.strings.js:5
 msgid "Admin console"
@@ -651,7 +651,7 @@ msgstr "Média"
 
 #: dist/toolbar/toolbar.js:972
 msgid "CountA"
-msgstr "ContarA"
+msgstr "Contar.VAL"
 
 #: dist/toolbar/toolbar.js:973
 msgid "Count"
diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.Dialog.js
index 733c5cd..daad5e6 100644
--- a/loleaflet/src/control/Control.Dialog.js
+++ b/loleaflet/src/control/Control.Dialog.js
@@ -21,7 +21,10 @@ L.Control.Dialog = L.Control.extend({
 		if (e.msg) {
 			vex.dialog.alert(e.msg);
 		}
-		else if (e.cmd && e.kind) {
+		else if (e.cmd == 'load' && e.kind == 'docunloading') {
+			// Handled by transparently retrying.
+			return;
+		} else if (e.cmd && e.kind) {
 			var msg = 'The server encountered a \'' + e.kind + '\' error while' +
 						' parsing the \'' + e.cmd + '\' command.';
 			vex.dialog.alert(msg);
diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js
index 13bcd94..7b30933 100644
--- a/loleaflet/src/control/Control.Header.js
+++ b/loleaflet/src/control/Control.Header.js
@@ -33,11 +33,8 @@ L.Control.Header = L.Control.extend({
 		if (this._selection.start === -1 && this._selection.end === -1)
 			return;
 		var childs = element.children;
-		// if the selection is cleared when the end selection cell is not in the current viewport,
-		// we have _selection.end === -1, since only a portion of the header is fetched;
-		// so, without the following hack, the selection would not be cleared correctly
 		var start = (this._selection.start === -1) ? 0 : this._selection.start;
-		var end = (this._selection.end === -1) ? childs.length : this._selection.end + 1;
+		var end = this._selection.end + 1;
 		for (var iterator = start; iterator < end; iterator++) {
 			this.unselect(childs[iterator]);
 		}
@@ -72,6 +69,12 @@ L.Control.Header = L.Control.extend({
 			}
 		}
 
+		// if end is greater than the last fetched header position set itEnd to the max possible value
+		// without this hack selecting a whole row and then a whole column (or viceversa) leads to an incorrect selection
+		if (itStart !== -1 && itEnd === -1) {
+			itEnd = childs.length - 1;
+		}
+
 		// we need to unselect the row (column) header entry for the current cell cursor position
 		// since the selection could be due to selecting a whole row (column), so the selection
 		// does not start by clicking on a cell
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 0364390..fdce2a5 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -111,6 +111,7 @@ L.Socket = L.Class.extend({
 
 	_onSocketOpen: function () {
 		console.debug('_onSocketOpen:');
+		this._map._serverRecycling = false;
 		// Always send the protocol version number.
 		// TODO: Move the version number somewhere sensible.
 		this._doSend('loolclient ' + this.ProtocolVersionNumber);
@@ -228,6 +229,9 @@ L.Socket = L.Class.extend({
 			if (textMsg === 'ownertermination') {
 				msg = _('Session terminated by document owner');
 			}
+			else if (textMsg === 'idle') {
+				msg = _('Session terminated due to idleness');
+			}
 			else if (textMsg === 'shuttingdown') {
 				msg = _('Server is shutting down for maintenance (auto-saving)');
 			}
@@ -235,6 +239,7 @@ L.Socket = L.Class.extend({
 				msg = _('Server is recycling and will be available shortly');
 
 				this._map._active = false;
+				this._map._serverRecycling = true;
 
 				// Prevent reconnecting the world at the same time.
 				var min = 5000;
@@ -301,24 +306,38 @@ L.Socket = L.Class.extend({
 				this._map.fire('postMessage', {msgId: 'Session_Closed'});
 			}
 
-			if (textMsg === 'idle') {
-				this._map._active = false;
-			}
-
-			if (textMsg === 'ownertermination') {
+			if (textMsg === 'idle' || textMsg === 'ownertermination') {
 				this._map.remove();
 			}
 
 			return;
 		}
 		else if (textMsg.startsWith('error:') && command.errorCmd === 'storage') {
+			var storageError;
 			if (command.errorKind === 'savediskfull') {
-				this._map.fire('error', {msg: errorMessages.storage.savediskfull});
+				storageError = errorMessages.storage.savediskfull;
 			}
 			else if (command.errorKind === 'savefailed') {
-				// Just warn the user
-				this._map.fire('warn', {msg: errorMessages.storage.savefailed});
+				storageError = errorMessages.storage.savefailed;
+			}
+			else if (command.errorKind === 'saveunauthorized') {
+				storageError = errorMessages.storage.saveunauthorized;
 			}
+			else if (command.errorKind === 'loadfailed') {
+				storageError = errorMessages.storage.loadfailed;
+				// Since this is a document load failure, wsd will disconnect the socket anyway,
+				// better we do it first so that another error message doesn't override this one
+				// upon socket close.
+				this._map.hideBusy();
+				this.close();
+			}
+
+			// Parse the storage url as link
+			var tmpLink = document.createElement('a');
+			tmpLink.href = this._map.options.doc;
+			// Insert the storage server address to be more friendly
+			storageError = storageError.replace('%storageserver', tmpLink.host);
+			this._map.fire('warn', {msg: storageError});
 
 			return;
 		}
@@ -328,6 +347,7 @@ L.Socket = L.Class.extend({
 				this._map.fire('error', {msg: errorMessages.diskfull});
 			}
 			else if (command.errorKind === 'unauthorized') {
+				this._map.hideBusy();
 				this._map.fire('error', {msg: errorMessages.unauthorized});
 			}
 
diff --git a/loleaflet/src/layer/AnnotationManager.js b/loleaflet/src/layer/AnnotationManager.js
index 3ef0dc8..7dc2e5c 100644
--- a/loleaflet/src/layer/AnnotationManager.js
+++ b/loleaflet/src/layer/AnnotationManager.js
@@ -118,12 +118,20 @@ L.AnnotationManager = L.Class.extend({
 	fill: function (comments) {
 		var comment;
 		this.clear();
+		// items contains redlines
+		var ordered = !this._items.length > 0;
 		for (var index in comments) {
 			comment = comments[index];
 			this.adjustComment(comment);
 			this._items.push(L.annotation(this._map.options.maxBounds.getSouthEast(), comment).addTo(this._map));
 		}
 		if (this._items.length > 0) {
+			if (!ordered) {
+				this._items.sort(function(a, b) {
+					return Math.abs(a._data.anchorPos.min.y) - Math.abs(b._data.anchorPos.min.y) ||
+						Math.abs(a._data.anchorPos.min.x) - Math.abs(b._data.anchorPos.min.x);
+				});
+			}
 			this._map._docLayer._updateMaxBounds(true);
 			this.layout();
 		}
@@ -132,6 +140,8 @@ L.AnnotationManager = L.Class.extend({
 	fillChanges: function(redlines) {
 		var changecomment;
 		this.clearChanges();
+		// items contains comments
+		var ordered = !this._items.length > 0;
 		for (var idx in redlines) {
 			changecomment = redlines[idx];
 			if (!this.adjustRedLine(changecomment)) {
@@ -141,6 +151,12 @@ L.AnnotationManager = L.Class.extend({
 			this._items.push(L.annotation(this._map.options.maxBounds.getSouthEast(), changecomment).addTo(this._map));
 		}
 		if (this._items.length > 0) {
+			if (!ordered) {
+				this._items.sort(function(a, b) {
+					return Math.abs(a._data.anchorPos.min.y) - Math.abs(b._data.anchorPos.min.y) ||
+						Math.abs(a._data.anchorPos.min.x) - Math.abs(b._data.anchorPos.min.x);
+				});
+			}
 			this._map._docLayer._updateMaxBounds(true);
 			this.layout();
 		}
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 0e8d631..c13a208 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -71,6 +71,7 @@ L.Map = L.Evented.extend({
 		this._fatal = false;
 		this._enabled = true;
 		this._debugAlwaysActive = false; // disables the dimming / document inactivity when true
+		this._serverRecycling = false;
 
 		vex.dialogID = -1;
 
@@ -159,7 +160,7 @@ L.Map = L.Evented.extend({
 
 	addView: function(viewInfo) {
 		this._viewInfo[viewInfo.id] = viewInfo;
-		this.fire('postMessage', {msgId: 'View_Added', args: {ViewId: viewInfo.id, UserId: viewInfo.userid, UserName: viewInfo.username, Color: viewInfo.color, ReadOnly: viewInfo.readonly}});
+		this.fire('postMessage', {msgId: 'View_Added', args: {ViewId: viewInfo.id, UserId: viewInfo.userid, UserName: viewInfo.username, Color: L.LOUtil.rgbToHex(viewInfo.color), ReadOnly: viewInfo.readonly}});
 
 		// Fire last, otherwise not all events are handled correctly.
 		this.fire('addview', {viewId: viewInfo.id, username: viewInfo.username, readonly: this.isViewReadOnly(viewInfo.id)});
@@ -777,6 +778,10 @@ L.Map = L.Evented.extend({
 	},
 
 	_activate: function () {
+		if (this._serverRecycling) {
+			return;
+		}
+
 		console.debug('_activate:');
 		clearTimeout(vex.timer);
 
@@ -867,6 +872,10 @@ L.Map = L.Evented.extend({
 	},
 
 	_startInactiveTimer: function () {
+		if (this._serverRecycling) {
+			return;
+		}
+
 		console.debug('_startInactiveTimer:');
 		clearTimeout(vex.timer);
 		var map = this;
@@ -876,6 +885,10 @@ L.Map = L.Evented.extend({
 	},
 
 	_deactivate: function () {
+		if (this._serverRecycling) {
+			return;
+		}
+
 		console.debug('_deactivate:');
 		clearTimeout(vex.timer);
 
diff --git a/loleaflet/src/map/handler/Map.Keyboard.js b/loleaflet/src/map/handler/Map.Keyboard.js
index 812ea84..1109b35 100644
--- a/loleaflet/src/map/handler/Map.Keyboard.js
+++ b/loleaflet/src/map/handler/Map.Keyboard.js
@@ -511,7 +511,8 @@ L.Map.Keyboard = L.Handler.extend({
 			this._map.print();
 			return true;
 		case 83: // s
-			this._map.save(true, true);
+			this._map.save(false /* An explicit save should terminate cell edit */,
+			               false /* An explicit save should save it again */);
 			return true;
 		case 86: // v
 		case 118: // v (Safari)
diff --git a/loleaflet/src/map/handler/Map.WOPI.js b/loleaflet/src/map/handler/Map.WOPI.js
index 110c0ad..20d11a6 100644
--- a/loleaflet/src/map/handler/Map.WOPI.js
+++ b/loleaflet/src/map/handler/Map.WOPI.js
@@ -4,8 +4,10 @@
 
 /* global title */
 L.Map.WOPI = L.Handler.extend({
-
-	PostMessageOrigin: false,
+	// If the CheckFileInfo call fails on server side, we won't have any PostMessageOrigin.
+	// So use '*' because we still needs to send 'close' message to the parent frame which
+	// wouldn't be possible otherwise.
+	PostMessageOrigin: '*',
 	DocumentLoadedTime: false,
 	HidePrintOption: false,
 	HideSaveOption: false,
diff --git a/loolwsd.spec b/loolwsd.spec
index af5d0c8..97fdbcf 100644
--- a/loolwsd.spec
+++ b/loolwsd.spec
@@ -11,12 +11,12 @@ Name:           loolwsd%{name_suffix}
 %else
 Name:           loolwsd
 %endif
-Version:        5.4.0.0.beta1
+Version:        5.4.0.0.beta2
 Release:        1%{?dist}
 Vendor:         %{vendor}
 Summary:        LibreOffice Online WebSocket Daemon
 License:        MPL
-Source0:        loolwsd-5.4.0.0.beta1.tar.gz
+Source0:        loolwsd-5.4.0.0.beta2.tar.gz
 BuildRequires:  libcap-devel libpng-devel poco-devel >= 1.7.5
 %if 0%{?fedora} || 0%{?rhel} >= 7
 BuildRequires:  libpcap
@@ -58,7 +58,7 @@ Obsoletes:      loleaflet <= 1.5.8
 %debug_package
 %endif
 %prep
-%setup -n loolwsd-5.4.0.0.beta1
+%setup -n loolwsd-5.4.0.0.beta2
 
 %build
 %configure \
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index de4e79a..3493bed 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -347,7 +347,7 @@ public:
         return nullptr;
     }
 
-    void notifyViewInfo(const std::vector<int>& /*viewIds*/) override
+    void notifyViewInfo() override
     {
     }
 
diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 4382158..3df7856 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -107,6 +107,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testCursorPosition);
     CPPUNIT_TEST(testAlertAllUsers);
     CPPUNIT_TEST(testViewInfoMsg);
+    CPPUNIT_TEST(testUndoConflict);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -163,6 +164,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     void testCursorPosition();
     void testAlertAllUsers();
     void testViewInfoMsg();
+    void testUndoConflict();
 
     void loadDoc(const std::string& documentURL, const std::string& testname);
 
@@ -1149,6 +1151,8 @@ void HTTPWSTest::testInsertDelete()
         std::cerr << "Deleting 10 slides." << std::endl;
         for (size_t it = 1; it <= 10; it++)
         {
+            // Explicitly delete the nth slide.
+            sendTextFrame(socket, "setclientpart part=" + std::to_string(it));
             sendTextFrame(socket, "uno .uno:DeletePage");
             response = getResponseString(socket, "status:");
             CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty());
@@ -2599,6 +2603,55 @@ void HTTPWSTest::testViewInfoMsg()
     }
 }
 
+void HTTPWSTest::testUndoConflict()
+{
+    const std::string testname = "testUndoConflict-";
+    Poco::JSON::Parser parser;
+    std::string docPath;
+    std::string docURL;
+    int conflict;
+
+    getDocumentPathAndURL("empty.odt", docPath, docURL, testname);
+
+    Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL);
+    auto socket0 = connectLOKit(_uri, request, _response);
+    auto socket1 = connectLOKit(_uri, request, _response);
+
+    std::string response;
+    try
+    {
+        // Load first view
+        sendTextFrame(socket0, "load url=" + docURL);
+        response = getResponseString(socket0, "invalidatecursor:", testname + "0 ");
+
+        // Load second view
+        sendTextFrame(socket1, "load url=" + docURL);
+        response = getResponseString(socket1, "invalidatecursor:", testname + "1 ");
+
+        // edit first view
+        sendTextFrame(socket0, "key type=input char=97 key=0", testname);
+        response = getResponseString(socket0, "invalidatecursor:", testname + "0 ");
+        // edit second view
+        sendTextFrame(socket1, "key type=input char=98 key=0", testname);
+        response = getResponseString(socket1, "invalidatecursor:", testname + "1 ");
+        // try to undo first view
+        sendTextFrame(socket0, "uno .uno:Undo", testname);
+        // undo conflict
+        response = getResponseString(socket0, "unocommandresult:", testname + "0 ");
+        auto objJSON = parser.parse(response.substr(17)).extract<Poco::JSON::Object::Ptr>();
+        Poco::DynamicStruct dsJSON = *objJSON;
+        CPPUNIT_ASSERT_EQUAL(dsJSON["commandName"].toString(), std::string(".uno:Undo"));
+        CPPUNIT_ASSERT_EQUAL(dsJSON["success"].toString(), std::string("true"));
+        CPPUNIT_ASSERT_EQUAL(dsJSON["result"]["type"].toString(), std::string("long"));
+        CPPUNIT_ASSERT(Poco::strToInt(dsJSON["result"]["value"].toString(), conflict, 10));
+        CPPUNIT_ASSERT(conflict > 0); /*UNDO_CONFLICT*/
+    }
+    catch(const Poco::Exception& exc)
+    {
+        CPPUNIT_FAIL(exc.displayText());
+    }
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 479bc2b..1f454a6 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -419,12 +419,12 @@ bool ClientSession::filterMessage(const std::string& message) const
                 LOG_WRN("No value of id in downloadas message");
         }
     }
-    else if (tokens[0] == "gettextselection" || tokens[0] == "paste" || tokens[0] == "insertfile")
+    else if (tokens[0] == "gettextselection")
     {
         if (_wopiFileInfo && _wopiFileInfo->_disableCopy)
         {
             allowed = false;
-            LOG_WRN("WOPI host has disabled copying to/from the document");
+            LOG_WRN("WOPI host has disabled copying from the document");
         }
     }
     else if (isReadOnly())
@@ -737,6 +737,7 @@ bool ClientSession::forwardToClient(const std::shared_ptr<Message>& payload)
 
 std::string ClientSession::getAccessToken() const
 {
+    std::string accessToken;
     Poco::URI::QueryParameters queryParams = _uriPublic.getQueryParameters();
     for (auto& param: queryParams)
     {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 66a5de8..ea40f44 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -102,7 +102,16 @@ Poco::URI DocumentBroker::sanitizeURI(const std::string& uri)
 
 std::string DocumentBroker::getDocKey(const Poco::URI& uri)
 {
-    return StorageBase::getUniqueDocId(uri);
+    // If multiple host-names are used to access us, then
+    // they must be aliases. Permission to access aliased hosts
+    // is checked at the point of accepting incoming connections.
+    // At this point storing the hostname artificially discriminates
+    // between aliases and forces same document (when opened from
+    // alias hosts) to load as separate documents and sharing doesn't
+    // work. Worse, saving overwrites one another.
+    std::string docKey;
+    Poco::URI::encode(uri.getPath(), "", docKey);
+    return docKey;
 }
 
 /// The Document Broker Poll - one of these in a thread per document
@@ -215,6 +224,7 @@ void DocumentBroker::pollThread()
     static const bool AutoSaveEnabled = !std::getenv("LOOL_NO_AUTOSAVE");
     static const size_t IdleDocTimeoutSecs = LOOLWSD::getConfigValue<int>(
                                                       "per_document.idle_timeout_secs", 3600);
+    std::string closeReason = "stopped";
 
     // Main polling loop goodness.
     while (!_stop && _poll->continuePolling() && !TerminationFlag)
@@ -232,30 +242,8 @@ void DocumentBroker::pollThread()
 
         if (ShutdownRequestFlag)
         {
-            // Shutting down the server: notify clients, save, and stop.
-            static const std::string msg("close: recycling");
-
-            // First copy into local container, since removeSession
-            // will erase from _sessions, but will leave the last.
-            std::map<std::string, std::shared_ptr<ClientSession>> sessions = _sessions;
-            for (const auto& pair : sessions)
-            {
-                std::shared_ptr<ClientSession> session = pair.second;
-                try
-                {
-                    // Notify the client and disconnect.
-                    session->sendMessage(msg);
-                    session->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, "recycling");
-
-                    // Remove session, save, and mark to destroy.
-                    removeSession(session->getId(), true);
-                }
-                catch (const std::exception& exc)
-                {
-                    LOG_WRN("Error while shutting down client [" <<
-                            session->getName() << "]: " << exc.what());
-                }
-            }
+            closeReason = "recycling";
+            shutdownClients(closeReason);
         }
         else if (AutoSaveEnabled && !_stop &&
                  std::chrono::duration_cast<std::chrono::seconds>(now - last30SecCheckTime).count() >= 30)
@@ -273,6 +261,7 @@ void DocumentBroker::pollThread()
         {
             LOG_INF("Terminating " << (idle ? "idle" : "dead") <<
                     " DocumentBroker for docKey [" << getDocKey() << "].");
+            closeReason = (idle ? "idle" : "dead");
             _stop = true;
         }
     }
@@ -281,11 +270,7 @@ void DocumentBroker::pollThread()
             _poll->continuePolling() << ", ShutdownRequestFlag: " << ShutdownRequestFlag <<
             ", TerminationFlag: " << TerminationFlag << ".");
 
-    // Terminate properly while we can.
-    //TODO: pass some sensible reason.
-    terminateChild("", false);
-
-    // Flush socket data.
+    // Flush socket data first.
     const int flushTimeoutMs = POLL_TIMEOUT_MS * 2; // ~1000ms
     const auto flushStartTime = std::chrono::steady_clock::now();
     while (_poll->getSocketCount())
@@ -298,6 +283,9 @@ void DocumentBroker::pollThread()
         _poll->poll(std::min(flushTimeoutMs - elapsedMs, POLL_TIMEOUT_MS / 5));
     }
 
+    // Terminate properly while we can.
+    terminateChild(closeReason, false);
+
     // Stop to mark it done and cleanup.
     _poll->stop();
     _poll->removeSockets();
@@ -401,7 +389,6 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
             LOG_ERR("Failed to create Storage instance for [" << _docKey << "] in " << jailPath.toString());
             return false;
         }
-
         firstInstance = true;
     }
 
@@ -416,14 +403,6 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
         std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo(session->getAccessToken());
         userid = wopifileinfo->_userid;
         username = wopifileinfo->_username;
-        if (firstInstance)
-        {
-            _hostInstanceId = wopifileinfo->_hostInstanceId;
-        }
-        else if (!_hostInstanceId.empty() && _hostInstanceId != wopifileinfo->_hostInstanceId)
-        {
-            throw UnauthorizedRequestException("Unauthorized WOPI host.");
-        }
 
         if (!wopifileinfo->_userCanWrite)
         {
@@ -453,7 +432,10 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
 
         std::ostringstream ossWopiInfo;
         wopiInfo->stringify(ossWopiInfo);
-        session->sendTextFrame("wopi: " + ossWopiInfo.str());
+        // Contains PostMessageOrigin property which is necessary to post messages to parent
+        // frame. Important to send this message immediately and not enqueue it so that in case
+        // document load fails, loleaflet is able to tell its parent frame via PostMessage API.
+        session->sendMessage("wopi: " + ossWopiInfo.str());
 
         // Mark the session as 'Document owner' if WOPI hosts supports it
         if (userid == _storage->getFileInfo()._ownerId)
@@ -661,6 +643,11 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId,
             sessionIt.second->sendTextFrame("error: cmd=storage kind=savediskfull");
         }
     }
+    else if (storageSaveResult == StorageBase::SaveResult::UNAUTHORIZED)
+    {
+        LOG_ERR("Cannot save docKey [" << _docKey << "] to storage URI [" << uri << "]. Invalid or expired access token. Notifying client.");
+        it->second->sendTextFrame("error: cmd=storage kind=saveunauthorized");
+    }
     else if (storageSaveResult == StorageBase::SaveResult::FAILED)
     {
         //TODO: Should we notify all clients?
@@ -802,6 +789,25 @@ std::string DocumentBroker::getJailRoot() const
 
 size_t DocumentBroker::addSession(const std::shared_ptr<ClientSession>& session)
 {
+    try
+    {
+        return addSessionInternal(session);
+    }
+    catch (const std::exception& exc)
+    {
+        LOG_ERR("Failed to add session to [" << _docKey << "] with URI [" << session->getPublicUri().toString() << "]: " << exc.what());
+        if (_sessions.empty())
+        {
+            LOG_INF("Doc [" << _docKey << "] has no more sessions. Marking to destroy.");
+            _markToDestroy = true;
+        }
+
+        throw;
+    }
+}
+
+size_t DocumentBroker::addSessionInternal(const std::shared_ptr<ClientSession>& session)
+{
     assertCorrectThread();
 
     try
@@ -832,12 +838,7 @@ size_t DocumentBroker::addSession(const std::shared_ptr<ClientSession>& session)
     _markToDestroy = false;
     _stop = false;
 
-    // Add and attach the session.
-    _sessions.emplace(session->getId(), session);
-    session->setAttached();
-
     const auto id = session->getId();
-    const auto count = _sessions.size();
 
     // Request a new session from the child kit.
     const std::string aMessage = "session " + id + ' ' + _docKey + ' ' + _docId;
@@ -846,6 +847,11 @@ size_t DocumentBroker::addSession(const std::shared_ptr<ClientSession>& session)
     // Tell the admin console about this new doc
     Admin::instance().addDoc(_docKey, getPid(), getFilename(), id, session->getUserName());
 
+    // Add and attach the session.
+    _sessions.emplace(session->getId(), session);
+    session->setAttached();
+
+    const auto count = _sessions.size();
     LOG_TRC("Added " << (session->isReadOnly() ? "readonly" : "non-readonly") <<
             " session [" << id << "] to docKey [" <<
             _docKey << "] to have " << count << " sessions.");
@@ -1315,6 +1321,33 @@ bool DocumentBroker::forwardToClient(const std::shared_ptr<Message>& payload)
     return false;
 }
 
+void DocumentBroker::shutdownClients(const std::string& closeReason)
+{
+    assertCorrectThread();
+    LOG_INF("Terminating " << _sessions.size() << " clients of doc [" << _docKey << "].");
+
+    // First copy into local container, since removeSession
+    // will erase from _sessions, but will leave the last.
+    std::map<std::string, std::shared_ptr<ClientSession>> sessions = _sessions;
+    for (const auto& pair : sessions)
+    {
+        std::shared_ptr<ClientSession> session = pair.second;
+        try
+        {
+            // Notify the client and disconnect.
+            session->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, closeReason);
+
+            // Remove session, save, and mark to destroy.
+            removeSession(session->getId(), true);
+        }
+        catch (const std::exception& exc)
+        {
+            LOG_WRN("Error while shutting down client [" <<
+                    session->getName() << "]: " << exc.what());
+        }
+    }
+}
+
 void DocumentBroker::childSocketTerminated()
 {
     assertCorrectThread();
@@ -1326,17 +1359,7 @@ void DocumentBroker::childSocketTerminated()
 
     // We could restore the kit if this was unexpected.
     // For now, close the connections to cleanup.
-    for (auto& pair : _sessions)
-    {
-        try
-        {
-            pair.second->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, "");
-        }
-        catch (const std::exception& ex)
-        {
-            LOG_ERR("Error while terminating client connection [" << pair.first << "]: " << ex.what());
-        }
-    }
+    shutdownClients("terminated");
 }
 
 void DocumentBroker::terminateChild(const std::string& closeReason, const bool rude)
@@ -1348,17 +1371,7 @@ void DocumentBroker::terminateChild(const std::string& closeReason, const bool r
     // Close all running sessions
     if (!rude)
     {
-        for (const auto& pair : _sessions)
-        {
-            try
-            {
-                pair.second->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, closeReason);
-            }
-            catch (const std::exception& ex)
-            {
-                LOG_ERR("Error while terminating client connection [" << pair.first << "]: " << ex.what());
-            }
-        }
+        shutdownClients(closeReason);
     }
 
     if (_childProcess)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 8ac67ee..23b699e 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -339,6 +339,10 @@ public:
     bool sendUnoSave(const std::string& sessionId, bool dontTerminateEdit = true, bool dontSaveIfUnmodified = true);
 
 private:
+
+    /// Shutdown all client connections with the given reason.
+    void shutdownClients(const std::string& closeReason);
+
     /// This gracefully terminates the connection
     /// with the child and cleans up ChildProcess etc.
     void terminateChild(const std::string& closeReason, const bool rude);
@@ -346,6 +350,9 @@ private:
     /// Saves the doc to the storage.
     bool saveToStorageInternal(const std::string& sesionId, bool success, const std::string& result = "");
 
+    /// Loads a new session and adds to the sessions container.
+    size_t addSessionInternal(const std::shared_ptr<ClientSession>& session);
+
     /// Removes a session by ID. Returns the new number of sessions.
     size_t removeSessionInternal(const std::string& id);
 
@@ -369,7 +376,6 @@ private:
     Poco::URI _uriJailed;
     std::string _jailId;
     std::string _filename;
-    std::string _hostInstanceId;
 
     /// The last time we tried saving, regardless of whether the
     /// document was modified and saved or not.
diff --git a/wsd/Exceptions.hpp b/wsd/Exceptions.hpp
index 2c4905a..122c618 100644
--- a/wsd/Exceptions.hpp
+++ b/wsd/Exceptions.hpp
@@ -34,6 +34,14 @@ public:
     using LoolException::LoolException;
 };
 
+/// General exception thrown when we are not able to
+/// connect to storage.
+class StorageConnectionException : public LoolException
+{
+public:
+    using LoolException::LoolException;
+};
+
 /// A bad-request exception that is meant to signify,
 /// and translate into, an HTTP bad request.
 class BadRequestException : public LoolException
diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 60b6a71..a50be21 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -207,7 +207,7 @@ void FileServerRequestHandler::handleRequest(const HTTPRequest& request, Poco::M
 
             bool gzip = request.hasToken("Accept-Encoding", "gzip");
             const std::string *content;
-#ifdef ENABLE_DEBUG
+#if ENABLE_DEBUG
             if (std::getenv("LOOL_SERVE_FROM_FS"))
             {
                 // Useful to not serve from memory sometimes especially during loleaflet development
@@ -507,7 +507,8 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
     Poco::URI uriFrameAncestor(frameAncestor);
     if (!frameAncestor.empty() && !uriFrameAncestor.getScheme().empty() && !uriFrameAncestor.getHost().empty())
     {
-        frameAncestor = uriFrameAncestor.getScheme() + "://" + uriFrameAncestor.getHost();
+        frameAncestor = uriFrameAncestor.getScheme() + "://" + uriFrameAncestor.getHost() + ":" + std::to_string(uriFrameAncestor.getPort());
+
         LOG_TRC("Final frame ancestor: " << frameAncestor);
 
         // Replaced by frame-ancestors in CSP but some oldies don't know about that
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 1c6c91d..7c188f1 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1883,7 +1883,7 @@ private:
 
                         docBroker->addCallback([docBroker, moveSocket, clientSession, format]()
                         {
-			    auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket);
+                            auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket);
                             clientSession->setSaveAsSocket(streamSocket);
 
                             // Move the socket into DocBroker.
@@ -2122,12 +2122,21 @@ private:
                                 // Add and load the session.
                                 docBroker->addSession(clientSession);
                             }
-                            catch (const std::exception& exc)
+                            catch (const UnauthorizedRequestException& exc)
                             {
-                                LOG_ERR("Error while handling loading : " << exc.what());
+                                LOG_ERR("Unauthorized Request while loading session for " << docBroker->getDocKey() << ": " << exc.what());
                                 const std::string msg = "error: cmd=internal kind=unauthorized";
                                 clientSession->sendMessage(msg);
-                                docBroker->stop();
+                            }
+                            catch (const StorageConnectionException& exc)
+                            {
+                                // Alert user about failed load
+                                const std::string msg = "error: cmd=storage kind=loadfailed";
+                                clientSession->sendMessage(msg);
+                            }
+                            catch (const std::exception& exc)
+                            {
+                                LOG_ERR("Error while loading : " << exc.what());
                             }
                         });
                     });
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 0c31e53..54aa2d4 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -125,7 +125,7 @@ void StorageBase::initialize()
 #endif
 }
 
-bool StorageBase::isLocalhost(const std::string& targetHost)
+bool isLocalhost(const std::string& targetHost)
 {
     std::string targetAddress;
     try
@@ -202,7 +202,7 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
     {
         LOG_INF("Public URI [" << uri.toString() << "] considered WOPI.");
         const auto& targetHost = uri.getHost();
-        if (isWopiHostAuthorized(targetHost))
+        if (WopiHosts.match(targetHost) || isLocalhost(targetHost))
         {
             return std::unique_ptr<StorageBase>(new WopiStorage(uri, jailRoot, jailPath));
         }
@@ -213,39 +213,6 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
     throw BadRequestException("No Storage configured or invalid URI.");
 }
 
-std::string StorageBase::getUniqueDocId(const Poco::URI& uri)
-{
-    std::string docId;
-    if (uri.isRelative() || uri.getScheme() == "file")
-    {
-        Poco::URI::encode(uri.getPath(), "", docId);
-    }
-    else if (WopiEnabled)
-    {
-        const auto& targetHost = uri.getHost();
-        if (isWopiHostAuthorized(targetHost))
-        {
-            std::string accessToken;
-            Poco::URI::QueryParameters queryParams = uri.getQueryParameters();
-            for (auto& param: queryParams)
-            {
-                if (param.first == "access_token")
-                    accessToken = param.second;
-            }
-
-            const std::unique_ptr<WopiStorage::WOPIFileInfo> info = WopiStorage::getWOPIFileInfo(uri, accessToken);
-            const std::string prefix = !info->_hostInstanceId.empty()
-                                     ? info->_hostInstanceId
-                                     : (uri.getHost() + ':' + std::to_string(uri.getPort()));
-            Poco::URI::encode(prefix + uri.getPath(), "", docId);
-        }
-        else
-            throw UnauthorizedRequestException("No acceptable WOPI hosts found matching the target host [" + targetHost + "] in config.");
-    }
-
-    return docId;
-}
-
 std::atomic<unsigned> LocalStorage::LastLocalStorageId;
 
 std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo()
@@ -459,9 +426,10 @@ void addStorageDebugCookie(Poco::Net::HTTPRequest& request)
 
 } // anonymous namespace
 
-std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken)
+std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const std::string& accessToken)
 {
     // update the access_token to the one matching to the session
+    Poco::URI uriObject(_uri);
     setQueryParameter(uriObject, "access_token", accessToken);
 
     LOG_DBG("Getting info for wopi uri [" << uriObject.toString() << "].");
@@ -494,11 +462,17 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::UR
             LOG_END(logger);
         }
 
+        if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
+        {
+            LOG_ERR("WOPI::CheckFileInfo failed with " << response.getStatus() << ' ' << response.getReason());
+            throw StorageConnectionException("WOPI::CheckFileInfo failed");
+        }
+
         Poco::StreamCopier::copyToString(rs, resMsg);
     }
     catch(const Poco::Exception& pexc)
     {
-        LOG_ERR("Cannot get file info from WOPI storage uri [" + uriObject.toString() + "]. Error: " << pexc.displayText() <<
+        LOG_ERR("Cannot get file info from WOPI storage uri [" << uriObject.toString() << "]. Error: " << pexc.displayText() <<
                 (pexc.nested() ? " (" + pexc.nested()->displayText() + ")" : ""));
         throw;
     }
@@ -509,7 +483,6 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::UR
     std::string ownerId;
     std::string userId;
     std::string userName;
-    std::string hostInstanceId;
     bool canWrite = false;
     bool enableOwnerTermination = false;
     std::string postMessageOrigin;
@@ -544,12 +517,11 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::UR
         getWOPIValue(object, "DisableExport", disableExport);
         getWOPIValue(object, "DisableCopy", disableCopy);
         getWOPIValue(object, "LastModifiedTime", lastModifiedTime);
-        getWOPIValue(object, "HostInstanceId", hostInstanceId);
     }
     else
     {
         LOG_ERR("WOPI::CheckFileInfo failed and no JSON payload returned. Access denied.");
-        throw UnauthorizedRequestException("Access denied.");
+        throw UnauthorizedRequestException("Access denied. WOPI::CheckFileInfo failed on: " + uriObject.toString());
     }
 
     Poco::Timestamp modifiedTime = Poco::Timestamp::fromEpochTime(0);
@@ -574,7 +546,9 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::UR
         }
     }
 
-    return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo(filename, ownerId, modifiedTime, size, userId, userName, hostInstanceId, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration));
+    _fileInfo = FileInfo({filename, ownerId, modifiedTime, size});
+
+    return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration}));
 }
 
 /// uri format: http://server/<...>/wopi*/files/<id>/content
@@ -614,15 +588,25 @@ std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
             LOG_END(logger);
         }
 
-        _jailedFilePath = Poco::Path(getLocalRootPath(), _fileInfo._filename).toString();
-        std::ofstream ofs(_jailedFilePath);
-        std::copy(std::istreambuf_iterator<char>(rs),
-                  std::istreambuf_iterator<char>(),
-                  std::ostreambuf_iterator<char>(ofs));
-
-        LOG_INF("WOPI::GetFile downloaded " << getFileSize(_jailedFilePath) << " bytes from [" << uriObject.toString() <<
-                "] -> " << _jailedFilePath << " in " << diff.count() << "s : " <<
-                response.getStatus() << " " << response.getReason());
+        if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
+        {
+            LOG_ERR("WOPI::GetFile failed with " << response.getStatus() << ' ' << response.getReason());
+            throw StorageConnectionException("WOPI::GetFile failed");
+        }
+        else // Successful
+        {
+            _jailedFilePath = Poco::Path(getLocalRootPath(), _fileInfo._filename).toString();
+            std::ofstream ofs(_jailedFilePath);
+            std::copy(std::istreambuf_iterator<char>(rs),
+                      std::istreambuf_iterator<char>(),
+                      std::ostreambuf_iterator<char>(ofs));
+            LOG_INF("WOPI::GetFile downloaded " << getFileSize(_jailedFilePath) << " bytes from [" << uriObject.toString() <<
+                    "] -> " << _jailedFilePath << " in " << diff.count() << "s");
+
+            _isLoaded = true;
+            // Now return the jailed path.
+            return Poco::Path(_jailPath, _fileInfo._filename).toString();
+        }
     }
     catch(const Poco::Exception& pexc)
     {
@@ -631,9 +615,7 @@ std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
         throw;
     }
 
-    _isLoaded = true;
-    // Now return the jailed path.
-    return Poco::Path(_jailPath, _fileInfo._filename).toString();
+    return "";
 }
 
 StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const std::string& accessToken)
@@ -678,6 +660,10 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const std::string& a
         {
             saveResult = StorageBase::SaveResult::DISKFULL;
         }
+        else if (response.getStatus() == Poco::Net::HTTPResponse::HTTP_UNAUTHORIZED)
+        {
+            saveResult = StorageBase::SaveResult::UNAUTHORIZED;
+        }
     }
     catch(const Poco::Exception& pexc)
     {
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 7cb7743..d0dff59 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -57,6 +57,7 @@ public:
     {
         OK,
         DISKFULL,
+        UNAUTHORIZED,
         FAILED
     };
 
@@ -100,29 +101,11 @@ public:
     static std::unique_ptr<StorageBase> create(const Poco::URI& uri,
                                                const std::string& jailRoot,
                                                const std::string& jailPath);
-
-    /// Given the URI of a doc, return a unique doc ID.
-    /// Wopi host aliases are resolved to unique host ID.
-    static std::string getUniqueDocId(const Poco::URI& uri);
-
 protected:
 
     /// Returns the root path of the jail directory of docs.
     std::string getLocalRootPath() const;
 
-    /// Returns true iff WOPI is enabled, and the host is whitelisted (or local).
-    static bool isWopiHostAuthorized(const std::string& host)
-    {
-        if (WopiEnabled)
-        {
-            return (WopiHosts.match(host) || isLocalhost(host));
-        }
-
-        return false;
-    }
-
-    static bool isLocalhost(const std::string& host);
-
 protected:
     const Poco::URI _uri;
     std::string _localStorePath;
@@ -194,16 +177,11 @@ public:
                 "], jailPath: [" << jailPath << "], uri: [" << uri.toString() << "].");
     }
 
-    class WOPIFileInfo : public FileInfo
+    class WOPIFileInfo
     {
     public:
-        WOPIFileInfo(const std::string& filename,
-                     const std::string& ownerId,
-                     const Poco::Timestamp& modifiedTime,
-                     size_t size,
-                     const std::string& userid,
+        WOPIFileInfo(const std::string& userid,
                      const std::string& username,
-                     const std::string& hostInstanceId,
                      const bool userCanWrite,
                      const std::string& postMessageOrigin,
                      const bool hidePrintOption,
@@ -214,10 +192,8 @@ public:
                      const bool disableExport,
                      const bool disableCopy,
                      const std::chrono::duration<double> callDuration)
-            : FileInfo(filename, ownerId, modifiedTime, size),
-              _userid(userid),
+            : _userid(userid),
               _username(username),
-              _hostInstanceId(hostInstanceId),
               _userCanWrite(userCanWrite),
               _postMessageOrigin(postMessageOrigin),
               _hidePrintOption(hidePrintOption),
@@ -235,8 +211,6 @@ public:
         std::string _userid;
         /// Display Name of user accessing the file
         std::string _username;
-        /// Host instance ID (unique to the given host).
-        std::string _hostInstanceId;
         /// If user accessing the file has write permission
         bool _userCanWrite;
         /// WOPI Post message property
@@ -263,12 +237,7 @@ public:
     /// provided during the initial creation of the WOPI storage.
     /// Also extracts the basic file information from the response
     /// which can then be obtained using getFileInfo()
-    std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const std::string& accessToken)
-    {
-        std::unique_ptr<WOPIFileInfo> info = getWOPIFileInfo(_uri, accessToken);
-        _fileInfo = FileInfo(info->_filename, info->_ownerId, info->_modifiedTime, info->_size);
-        return info;
-    }
+    std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const std::string& accessToken);
 
     /// uri format: http://server/<...>/wopi*/files/<id>/content
     std::string loadStorageFileToLocal(const std::string& accessToken) override;
@@ -278,11 +247,6 @@ public:
     /// Total time taken for making WOPI calls during load
     std::chrono::duration<double> getWopiLoadDuration() const { return _wopiLoadDuration; }
 
-    /// Given the URI of a doc, return a unique doc ID.
-    static std::string getUniqueDocId(const Poco::URI& uri);
-
-    static std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken);
-
 private:
     // Time spend in loading the file from storage
     std::chrono::duration<double> _wopiLoadDuration;
@@ -310,13 +274,6 @@ public:
 
     SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
 
-    /// Given the URI of a doc, return a unique doc ID.
-    static std::string getUniqueDocId(const std::string& uri)
-    {
-        // TODO: Implement.
-        return uri;
-    }
-
 private:
     std::unique_ptr<AuthBase> _authAgent;
 };
diff --git a/wsd/reference.txt b/wsd/reference.txt
index e1af6d4..5c70662 100644
--- a/wsd/reference.txt
+++ b/wsd/reference.txt
@@ -41,11 +41,9 @@ DisableExport
 	HideExportOption is assumed to be true
 
 DisableCopy
-	Disables copy/paste from/to the document in libreoffice online backend.
-	However, it is still possible to do an "internal" cut/copy/paste i.e
-	copy from the document and paste to the same document view. The context
-	menu options pertaining to cut/copy/paste would be renamed to 'Internal
-	Cut/Copy/Paste' when this option is mentioned.
+	Disables copying from the document in libreoffice online
+	backend. Pasting into the document would still be possible.
+	However, it is still possible to do an "internal" cut/copy/paste.
 
 EnableOwnerTermination
 	If set to true, it allows the document owner (the one with OwnerId =

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-openoffice/libreoffice-online.git



More information about the Pkg-openoffice-commits mailing list