[Libreoffice-commits] online.git: Branch 'libreoffice-5-4' - 26 commits - common/Session.cpp configure.ac .gitignore kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp loleaflet/dist loleaflet/main.js loleaflet/po loleaflet/src Makefile.am test/httpwstest.cpp test/WhiteBoxTests.cpp wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/Exceptions.hpp wsd/FileServer.cpp wsd/LOOLWSD.cpp wsd/reference.txt wsd/Storage.cpp wsd/Storage.hpp

Andras Timar andras.timar at collabora.com
Wed Jun 7 13:26:23 UTC 2017


 .gitignore                                |    1 
 Makefile.am                               |    9 -
 common/Session.cpp                        |    3 
 configure.ac                              |    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 -
 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 -
 33 files changed, 355 insertions(+), 290 deletions(-)

New commits:
commit b396188c8a36bc2d9cfa2bba9ae15567b7f2bc60
Author: Andras Timar <andras.timar at collabora.com>
Date:   Wed Jun 7 15:25:56 2017 +0200

    Bump version to 5.4.0.0.beta2
    
    Change-Id: I648b23427d92dd10f582d8757387b4221d0cbe88

diff --git a/configure.ac b/configure.ac
index 876bb9bf..446c1f08 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])
commit 28905a536d1128d6b9088955af8eddc56984c608
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed May 31 14:20:58 2017 +0530

    tdf#103673 - Use hex color value in API
    
    Change-Id: I51e2431e9253a625fb590af276d991a34e720141
    (cherry picked from commit 073b58269b1fd63f946f3b7ba7b8b11754e5092c)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 090bafab..c13a2083 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -160,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)});
commit b61b83ac081d401a58d925be67f331b7c04252fa
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed May 31 11:51:12 2017 +0530

    Handle WOPI PutFile unauthorized access token
    
    Change-Id: I29ee8cc0c9f3ea42f70628eca6f74d161d1a38f9
    (cherry picked from commit d840e8720a2ba45ae981b53e5a06b4d28f847314)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index 89f31fc7..8ea1185c 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -12,5 +12,6 @@ exports.faileddocloading = _('Failed to load the document. Please ensure the fil
 exports.storage = {
 	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/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 25f5f83f..fdce2a54 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -320,6 +320,9 @@ L.Socket = L.Class.extend({
 			else if (command.errorKind === '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,
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index c4d672f5..ea40f44d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -643,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?
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index d096ea3e..54aa2d48 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -660,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 6613fbbc..d0dff593 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -57,6 +57,7 @@ public:
     {
         OK,
         DISKFULL,
+        UNAUTHORIZED,
         FAILED
     };
 
commit 165e1ec8627eba91ed07c91876dfb6e71d31f4d6
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed May 31 11:25:03 2017 +0530

    User save should terminate edit in calc; and save if unmodified
    
    Only autosave should do the opposite which it already does in
    DocumentBroker.
    
    Change-Id: Ibb86e0b29fc058318de96d2684c326dbe958e6fb
    (cherry picked from commit ac8e458bda37a17e289ee2da71a59b234db5e753)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 18c996bf..d98dbef2 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');
diff --git a/loleaflet/src/map/handler/Map.Keyboard.js b/loleaflet/src/map/handler/Map.Keyboard.js
index 812ea847..1109b35f 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)
commit 5e6d6d720700870edaeca72b820c1a84264915e9
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue May 30 22:26:05 2017 -0400

    wsd: test: undo conflict
    
    Change-Id: Iafdcf3e206a425b7e55cca7818beb620e9d06f85
    (cherry picked from commit d31e0e3aacfa31990f43d68a39ff43f2203036bc)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 5d80f40e..3df78569 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);
 
@@ -2601,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: */
commit e82dfc34139c50eaa41072e845225f5c2fbe14b9
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue May 30 15:59:00 2017 -0400

    loleaflet: merge comments and redlines items
    
    Comments and Redlines were mutually exclusive, but a document
    can contain both.
    
    Change-Id: Idb13f0c0e1216edc6bed3291c0c486b45ad74b56
    (cherry picked from commit 143afd674a6a18d2a2618d419a3a1f259fc1a8b3)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/layer/AnnotationManager.js b/loleaflet/src/layer/AnnotationManager.js
index 3ef0dc8a..7dc2e5c6 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();
 		}
commit 549e310f84a36181cf44f8d5a4b439fc270a77ef
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun May 28 23:59:12 2017 -0400

    wsd: avoid race during viewinfo notification
    
    Change-Id: If2d8adc67337a5529cb6898808a84727ff1df38e
    Reviewed-on: https://gerrit.libreoffice.org/38123
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 8a2c60e5b6f4f0ad8cac44ba1dd39c7594c7bbde)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 1fbc1944..d68bcf59 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 b1fad059..703df776 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 628e9295..304a62e5 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/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index de4e79aa..3493bed1 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
     {
     }
 
commit 51983b79d5d4a76a267dc4bb2ae7354c0fb9e4bc
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun May 28 22:46:25 2017 -0400

    loleaflet: suppress docunloading error
    
    This is a soft-error and reconnection
    is done transparently, so no need to
    show the user unhelpful message.
    
    Change-Id: Ie240897fec1ecdc29a9fc1c1482a416a89ca5323
    Reviewed-on: https://gerrit.libreoffice.org/38122
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit d76ce9169c61cdd6482597d18f1d844253280076)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.Dialog.js
index 733c5cdf..daad5e6e 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);
commit 7525844a994d50a851f6c528d3d33ac00f972eed
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Mon May 22 18:05:05 2017 +0200

    loleflet - sc: header incorrectly shows selection sometimes
    
    Problem:
    - Select a whole row, let's say row 10.
    - Select a whole column, let's say column I.
    
    => Columns headers right from I, (J, K etc.) are still highlighted.
    Similarly if a column selected first, and a row next.
    
    Change-Id: I13ad0e2a152ddbf96c4889a3aea5129885bc95b5
    Reviewed-on: https://gerrit.libreoffice.org/38000
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit 1588d2fe12960f2f530e39340636b6393ca21571)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js
index 13bcd947..7b309339 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
commit 8aa749563678b6016b490134648ea8c6cffdaa27
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Tue May 23 15:20:34 2017 +0200

    loleaflet: calc: styles are visible in fonts dropdown
    
    Change-Id: Ia909ed9f35d6a1c80f6696abeddefe2d4ad2d6a0
    Reviewed-on: https://gerrit.libreoffice.org/38001
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit 061c720ee1b7b018c58a1c994e5352ca268a2e67)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index e295c9ea..18c996bf 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -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') {
commit 51f84271c929ad1e1aac7bb2b55122b4229958b0
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu May 25 14:11:33 2017 +0530

    wsd: Only disable copying when DisableCopy is mentioned
    
    Earlier, DisableCopy was a misnomer disabling both copy and paste. Now
    it only disables copy from the document but allows pasting into the
    document.
    
    Change-Id: I8ddfdd493918331276f0656468d3b94c4283fa4d
    (cherry picked from commit 1a1ab5fd5751f9d6353bb91be918e96cf9c53b90)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 105b30d3..1f454a69 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())
diff --git a/wsd/reference.txt b/wsd/reference.txt
index e1af6d4b..5c706621 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 =
commit 4a10de00210e24b8f9fe993036065b948ff79961
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue May 23 16:35:37 2017 -0400

    loleaflet: fix undo repair document popup
    
    UNDO_CONFLICT was intented to notify views, but
    the constant value changed with latest patch upstream
    
    Change-Id: I09062725539df3d974a47d89374d822d1d852770
    (cherry picked from commit 05deed08b47f3b4b49372fd0cebacf5a8eeb6894)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 55d50353..e295c9ea 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -1426,7 +1426,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>'});
 	}
commit 822811b90e8be2a749901f7c3bed8336f0ae24f8
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue May 23 17:05:07 2017 +0530

    Revert "wsd: Use hostname and port in doc key too"
    
    This reverts commit e8ff26899203b6994579afacdd5f89aa68c8a696.
    
    To have support for both multitenancy - several WOPI hosts using same wsd -
    and WOPI host aliases, using a ID unique across a WOPI host instance as
    part of the WOPI URL is a better approach that handles both of above
    mentioned issues cleanly.
    
    (cherry picked from commit c05aec945d2f33c73dba5910f94010b607e5822b)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 6260f964..c4d672f5 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -103,13 +103,14 @@ Poco::URI DocumentBroker::sanitizeURI(const std::string& uri)
 std::string DocumentBroker::getDocKey(const Poco::URI& uri)
 {
     // If multiple host-names are used to access us, then
-    // we force same document (when opened from
+    // 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.
-    // But we also do not want different WOPI hosts using the same path
-    // for some file getting shared across WOPI hosts
     std::string docKey;
-    Poco::URI::encode(uri.getHost() + ":" + std::to_string(uri.getPort()) + uri.getPath(), "", docKey);
+    Poco::URI::encode(uri.getPath(), "", docKey);
     return docKey;
 }
 
commit 3b473b11df21d57f99da117aff63d765868f1762
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Mon May 22 15:04:35 2017 -0400

    wsd: don't stop doc on unauthorized loading
    
    When a client connects with expired/invalid
    access_token, the document should remain
    active for other/existing clients, if any.
    
    However, if no clients exists (i.e. the
    first client has invalid access_token),
    then the document should be unloaded and
    cleaned up.
    
    Change-Id: Iaad95a4286325cc6ee130b37e3ad635993a71c72
    Reviewed-on: https://gerrit.libreoffice.org/37916
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit e877adc84bc6f90bcfa52f9f63666358e6614885)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e05bb2c2..6260f964 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -783,6 +783,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
@@ -813,12 +832,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;
@@ -827,6 +841,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.");
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 14fe17e0..23b699e4 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -350,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);
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 3a877242..7c188f17 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2127,19 +2127,16 @@ private:
                                 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);
-                                docBroker->stop();
                             }
                             catch (const std::exception& exc)
                             {
                                 LOG_ERR("Error while loading : " << exc.what());
-                                docBroker->stop();
                             }
                         });
                     });
commit 729d960b340d96aa3b5ea71e148b0f8815c1e800
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri May 19 17:11:20 2017 +0200

    Fix HTTPWSTest::testInsertDelete() failure
    
    .uno:DeletePage deletes the "current" page, while the intent here is to
    delete all slides except the first one. Be explicit about this.
    
    Change-Id: Ia8a8a5bf907e3d79cc646d54803447525375ce72
    Reviewed-on: https://gerrit.libreoffice.org/37833
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit 43eabc09d12d666b1a60f7ae26fdf91864fd273b)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 4382158a..5d80f40e 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -1149,6 +1149,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());
commit de517fa08168c00cb1ce1319c0bc3c52d068c75c
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon May 22 11:04:26 2017 +0530

    wsd: Arrange exception handling a bit
    
    Remove incorrect usage of std::uncaught_exception and handle
    StorageConnectionException later.
    
    Change-Id: I15ecd46b51e8ed33649fe876d46ce3d5fbae07cc
    (cherry picked from commit 1ea87b627e077590c6eefa455ce3c8f7d0564068)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 19fe4c50..e05bb2c2 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -795,12 +795,6 @@ size_t DocumentBroker::addSession(const std::shared_ptr<ClientSession>& session)
             throw std::runtime_error(msg);
         }
     }
-    catch (const StorageConnectionException& exc)
-    {
-        // Alert user about failed load
-        session->sendMessage("error: cmd=storage kind=loadfailed");
-        throw;
-    }
     catch (const StorageSpaceLowException&)
     {
         LOG_ERR("Out of storage while loading document with URI [" << session->getPublicUri().toString() << "].");
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index a2e868d9..3a877242 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2129,18 +2129,16 @@ private:
                                 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);
+                                docBroker->stop();
+                            }
                             catch (const std::exception& exc)
                             {
-                                LOG_ERR("Error while handling loading : " << exc.what());
-                                // only send our default error message if we haven't handled the
-                                // exception already up the stack
-                                if (std::uncaught_exception())
-                                {
-                                    // FIXME: Are we sure we want to say that all other failures due
-                                    // to an 'unauthorized' WOPI host ?
-                                    const std::string msg = "error: cmd=internal kind=unauthorized";
-                                    clientSession->sendMessage(msg);
-                                }
+                                LOG_ERR("Error while loading : " << exc.what());
                                 docBroker->stop();
                             }
                         });
commit 4acb30242f75c6e8377e949fad89528696039fe1
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Mon May 22 00:26:07 2017 -0400

    Hide connecting spinner on authorization failure
    
    Also, fix regression resulting in "This is embarrassing..."
    instead of "Unauthorized..." message.
    
    Change-Id: If6219bfbc445b186bb4064b3f20a0ccd2854d617
    Reviewed-on: https://gerrit.libreoffice.org/37893
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 4e6fee5407219371945edf2ec49b90a333a8311d)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 6d1ee6bb..25f5f83f 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -344,6 +344,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/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 84d332a7..a2e868d9 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2122,6 +2122,13 @@ private:
                                 // Add and load the session.
                                 docBroker->addSession(clientSession);
                             }
+                            catch (const UnauthorizedRequestException& exc)
+                            {
+                                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 std::exception& exc)
                             {
                                 LOG_ERR("Error while handling loading : " << exc.what());
@@ -2136,7 +2143,6 @@ private:
                                 }
                                 docBroker->stop();
                             }
-
                         });
                     });
                 }
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 6e8c1dff..d096ea3e 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -472,7 +472,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
     }
     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;
     }
@@ -521,7 +521,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
     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);
commit 04a752842c8eada1ba8cbe38453649dd20d3a19d
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun May 21 21:51:49 2017 -0400

    loleaflet: correctly reload after server recycling
    
    When the server recylces, the client should
    transparently restore connection when the
    server is back on. This was broken because
    of the different times when moving in and out
    of focus for dimming/inactivity purposes.
    
    Here we track server recycling state
    separately, and skip the dimming/inactivity
    checks in that case.
    
    Change-Id: I432b97b3278b5a6846c8d2c4fa964184f156d5f3
    Reviewed-on: https://gerrit.libreoffice.org/37892
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 7bc5989aca61b128ff721f52070b1b5b19adbf94)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 4db52fef..6d1ee6bb 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);
@@ -238,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;
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 0e8d6311..090bafab 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;
 
@@ -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);
 
commit d0742986d8c74927f227f1f17b924428af144540
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun May 21 19:13:55 2017 -0400

    Correctly send termination reason to clients
    
    Fixes the case when the client reconnects on idle
    disconnection (because it never got the 'close: idle'
    message).
    
    Also, show informative message to users in this case
    instead of grey screen.
    
    Change-Id: Ia2e1f2ffefe6d35dd1552e7cc44e490aab86c600
    Reviewed-on: https://gerrit.libreoffice.org/37891
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 407c538f046f9245661a77e2452779c465b75087)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/common/Session.cpp b/common/Session.cpp
index d8756689..d29613db 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/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 1047c4b7..4db52fef 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -228,6 +228,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)');
 			}
@@ -301,11 +304,7 @@ 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();
 			}
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 7f8b9bce..19fe4c50 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -223,6 +223,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)
@@ -240,30 +241,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)
@@ -281,6 +260,7 @@ void DocumentBroker::pollThread()
         {
             LOG_INF("Terminating " << (idle ? "idle" : "dead") <<
                     " DocumentBroker for docKey [" << getDocKey() << "].");
+            closeReason = (idle ? "idle" : "dead");
             _stop = true;
         }
     }
@@ -289,11 +269,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())
@@ -306,6 +282,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();
@@ -1323,6 +1302,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();
@@ -1334,17 +1340,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)
@@ -1356,17 +1352,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 46481cba..14fe17e0 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);
commit 04b2668ef49f5d37ddfb32284aff6452def0462d
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat May 20 12:16:36 2017 +0530

    ENABLE_DEBUG has values either 0 or 1, otherwise its always defined.
    
    Change-Id: I0253dc2d83c11888e79ddb991b065eb62bbbd805
    (cherry picked from commit f47936a2da64accd262d24be0357fe0ea7a8e791)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 769193ba..a50be219 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
commit 21c268e7d56cd5130d11f8321ecb7bf487f6c26a
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri May 19 21:04:03 2017 +0530

    Didn't mean to truncate this string literal
    
    Otherwise getStatus() returns an integer which increments the char*
    
    Change-Id: I189addb60a02de14085b1501c75362b13dcb3ae2
    (cherry picked from commit aed840ea04ce2e7ae120698f50e78268b542db12)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index e259836b..6e8c1dff 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -464,7 +464,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
 
         if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
         {
-            LOG_ERR("WOPI::CheckFileInfo failed with " + response.getStatus() + response.getReason());
+            LOG_ERR("WOPI::CheckFileInfo failed with " << response.getStatus() << ' ' << response.getReason());
             throw StorageConnectionException("WOPI::CheckFileInfo failed");
         }
 
@@ -590,7 +590,7 @@ std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
 
         if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
         {
-            LOG_ERR("WOPI::GetFile failed with " + response.getStatus() + response.getReason());
+            LOG_ERR("WOPI::GetFile failed with " << response.getStatus() << ' ' << response.getReason());
             throw StorageConnectionException("WOPI::GetFile failed");
         }
         else // Successful
commit 08c0f74f06a0005bb1154fdee46b90936dac462e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu May 18 23:35:45 2017 +0530

    wsd: Fail gracefully when storage misbehaves
    
    When WOPI's CheckFileInfo or GetFile responds with status code other
    than HTTP 200, show a message to the user indicating some problem in the
    storage.
    
    Currently, we open an empty document if storage doesn't return a
    document which surely is not correct.
    
    Mention the storage server address when asking user to contact the
    server administrator to be more friendly.
    
    Change-Id: I15f0489f36db8689b43d42f6b691fdd21815e4fa
    (cherry picked from commit bcf958c50056637d4899fc9cd38b87a15dfa57b0)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index d1eccac1..89f31fc7 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -10,6 +10,7 @@ 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.'),
+	savefailed: _('Document cannot be saved to storage. Check your permissions or contact the storage server (%storageserver) administrator.')
 };
diff --git a/loleaflet/main.js b/loleaflet/main.js
index fed59570..48200cd9 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/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 03643901..1047c4b7 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -312,13 +312,28 @@ L.Socket = L.Class.extend({
 			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 === '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;
 		}
diff --git a/loleaflet/src/map/handler/Map.WOPI.js b/loleaflet/src/map/handler/Map.WOPI.js
index 110c0adb..20d11a6f 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/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e5924f87..7f8b9bce 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -452,7 +452,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)
@@ -813,6 +816,12 @@ size_t DocumentBroker::addSession(const std::shared_ptr<ClientSession>& session)
             throw std::runtime_error(msg);
         }
     }
+    catch (const StorageConnectionException& exc)
+    {
+        // Alert user about failed load
+        session->sendMessage("error: cmd=storage kind=loadfailed");
+        throw;
+    }
     catch (const StorageSpaceLowException&)
     {
         LOG_ERR("Out of storage while loading document with URI [" << session->getPublicUri().toString() << "].");
diff --git a/wsd/Exceptions.hpp b/wsd/Exceptions.hpp
index 2c4905a6..122c618b 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/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 1c6c91d6..84d332a7 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.
@@ -2125,10 +2125,18 @@ private:
                             catch (const std::exception& exc)
                             {
                                 LOG_ERR("Error while handling loading : " << exc.what());
-                                const std::string msg = "error: cmd=internal kind=unauthorized";
-                                clientSession->sendMessage(msg);
+                                // only send our default error message if we haven't handled the
+                                // exception already up the stack
+                                if (std::uncaught_exception())
+                                {
+                                    // FIXME: Are we sure we want to say that all other failures due
+                                    // to an 'unauthorized' WOPI host ?
+                                    const std::string msg = "error: cmd=internal kind=unauthorized";
+                                    clientSession->sendMessage(msg);
+                                }
                                 docBroker->stop();
                             }
+
                         });
                     });
                 }
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 12f0a49e..e259836b 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -462,6 +462,12 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
             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)
@@ -582,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)
     {
@@ -599,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)
commit b3cc19af99d5a61a09ebe4914d90451eebeee5f0
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri May 19 10:32:24 2017 +0200

    Revert "wsd: use WOPI host instance ID to support hostname aliases"
    
    Turns out this introduces two calls to the CheckFileInfo which is not really
    what we should be doing; instead, let's do a kind of cannonicalization in the
    WOPI host directly.
    
    This reverts commit ec2fd0844f997f9a7347229e282daeb8dc4471bc.
    
    Change-Id: I311bf8a45b706ed9a4d8cd00db0a990ac6d461b4
    (cherry picked from commit 9db41725f423f00f95db4b68d70827d24f80bb6c)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 479bc2b9..105b30d3 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -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 66a5de80..e5924f87 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -102,7 +102,15 @@ 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
+    // we force same document (when opened from
+    // alias hosts) to load as separate documents and sharing doesn't
+    // work. Worse, saving overwrites one another.
+    // But we also do not want different WOPI hosts using the same path
+    // for some file getting shared across WOPI hosts
+    std::string docKey;
+    Poco::URI::encode(uri.getHost() + ":" + std::to_string(uri.getPort()) + uri.getPath(), "", docKey);
+    return docKey;
 }
 
 /// The Document Broker Poll - one of these in a thread per document
@@ -401,7 +409,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 +423,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)
         {
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 8ac67ee2..46481cba 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -369,7 +369,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/Storage.cpp b/wsd/Storage.cpp
index 0c31e53c..12f0a49e 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() << "].");
@@ -509,7 +477,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,7 +511,6 @@ 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
     {
@@ -574,7 +540,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
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 7cb77432..6613fbbc 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -100,29 +100,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 +176,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 +191,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 +210,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 +236,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 +246,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 +273,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;
 };
commit f48aae0e2cf94ea8c969c243c54e5e3406506499
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu May 18 22:54:44 2017 +0530

    wsd: Echo back port number in CSP too if found in Referer header
    
    Otherwise, WOPI implementations that use non-standard ports get CSP
    voilation errors in the browsers because Poco's URI::getHost() method
    strips the port number from the host.
    
    No harm in mentioning the port number always even if its a standard one,
    so always use Poco::URI::getPort() to append the port to the frame
    ancestor.
    
    Change-Id: I9e7a7021b38f717e14af3d389e30f24ecaf6d122
    (cherry picked from commit 4ef373ce5faa0c133239aa6704c5d16e9db89229)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 60b6a710..769193ba 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -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
commit c43566b8eceea5a154fddd542470a36b72697d5b
Author: Andras Timar <andras.timar at collabora.com>
Date:   Thu Jun 1 16:47:43 2017 +0200

    loolnb was killed earlier
    
    Change-Id: Ie4b1f3c890c59a8b9204af5a25842c74929ecad8
    Reviewed-on: https://gerrit.libreoffice.org/38314
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>
    (cherry picked from commit 865cd4a79d3a38819315d934a460864bb81797be)
    Signed-off-by: Andras Timar <andras.timar at collabora.com>

diff --git a/.gitignore b/.gitignore
index 6eaa7f0f..6a0481e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,7 +58,6 @@ loolforkit-nocaps
 loadtest
 unittest
 loolwsd_fuzzer
-loolnb
 clientnb
 
 # make tags
diff --git a/Makefile.am b/Makefile.am
index 73c26140..9cc44025 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
commit 8e3646f5c80075074969e5cc403b9ccdaa3b1e5c
Author: Andras Timar <andras.timar at collabora.com>
Date:   Wed Jun 7 14:34:53 2017 +0200

    Updated translations from Pootle
    
    Change-Id: I053db203a6db8e1b11886efa9d7f2c15805e3802

diff --git a/loleaflet/po/help-de.po b/loleaflet/po/help-de.po
index 06d6437e..6140af04 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 19ecaea2..2b77fcaf 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 fc5d30cf..310b4518 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 7933a701..e40195ae 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 f547f6a2..296c277c 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"


More information about the Libreoffice-commits mailing list