common/Session.cpp        |    7 +
 common/Session.hpp        |    2 
 kit/ChildSession.cpp      |   17 +++-
 kit/ChildSession.hpp      |    3 
 kit/Kit.cpp               |   10 +-
 test/Makefile.am          |   10 +-
 test/UnitWOPITemplate.cpp |  172 ++++++++++++++++++++++++++++++++++++++++++++++
 test/WhiteBoxTests.cpp    |    3 
 test/data/test.ott        |binary
 wsd/ClientSession.cpp     |   16 +++-
 wsd/DocumentBroker.cpp    |   14 ++-
 wsd/LOOLWSD.cpp           |    3 
 wsd/Storage.cpp           |   24 +++++-
 wsd/Storage.hpp           |   14 ++-
 14 files changed, 264 insertions(+), 31 deletions(-)

New commits:
commit 40dd5c05881675b223ea79c412ba75e83ad13eb3
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue May 21 23:33:26 2019 -0400
Commit:     Jan Holesovsky <ke...@collabora.com>
CommitDate: Fri Nov 15 11:07:10 2019 +0100

    wsd: introduce "TemplateSource" WOPI property
    
    Reviewed-on: https://gerrit.libreoffice.org/76190
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 760864870fa8992fda58be03f3f55232b247506f)
    
    Change-Id: I9df1d5d0d4be7fe10ee15c40c36195c86ccf857e
    Reviewed-on: https://gerrit.libreoffice.org/82710
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>

diff --git a/common/Session.cpp b/common/Session.cpp
index 040bbc25e..414d35869 100644
--- a/common/Session.cpp
+++ b/common/Session.cpp
@@ -76,7 +76,7 @@ bool Session::sendBinaryFrame(const char *buffer, int length)
     return sendMessage(buffer, length, WSOpCode::Binary) >= length;
 }
 
-void Session::parseDocOptions(const std::vector<std::string>& tokens, int& 
part, std::string& timestamp)
+void Session::parseDocOptions(const std::vector<std::string>& tokens, int& 
part, std::string& timestamp, std::string& doctemplate)
 {
     // First token is the "load" command itself.
     size_t offset = 1;
@@ -167,6 +167,11 @@ void Session::parseDocOptions(const 
std::vector<std::string>& tokens, int& part,
             timestamp = value;
             ++offset;
         }
+        else if (name == "template")
+        {
+            doctemplate = value;
+            ++offset;
+        }
     }
 
     Util::mapAnonymized(_userId, _userIdAnonym);
diff --git a/common/Session.hpp b/common/Session.hpp
index 1934c9465..d70d687c2 100644
--- a/common/Session.hpp
+++ b/common/Session.hpp
@@ -135,7 +135,7 @@ protected:
 
     /// Parses the options of the "load" command,
     /// shared between MasterProcessSession::loadDocument() and 
ChildProcessSession::loadDocument().
-    void parseDocOptions(const std::vector<std::string>& tokens, int& part, 
std::string& timestamp);
+    void parseDocOptions(const std::vector<std::string>& tokens, int& part, 
std::string& timestamp, std::string& doctemplate);
 
     void updateLastActivityTime()
     {
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index d75809532..da16d807f 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -545,8 +545,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, 
int /*length*/, const s
         return false;
     }
 
-    std::string timestamp;
-    parseDocOptions(tokens, part, timestamp);
+    std::string timestamp, doctemplate;
+    parseDocOptions(tokens, part, timestamp, doctemplate);
 
     std::string renderOpts;
     if (!getDocOptions().empty())
@@ -567,7 +567,7 @@ bool ChildSession::loadDocument(const char * /*buffer*/, 
int /*length*/, const s
     const bool loaded = _docManager.onLoad(getId(), getJailedFilePath(), 
getJailedFilePathAnonym(),
                                            getUserName(), getUserNameAnonym(),
                                            getDocPassword(), renderOpts, 
getHaveDocPassword(),
-                                           getLang(), getWatermarkText());
+                                           getLang(), getWatermarkText(), 
doctemplate);
     if (!loaded || _viewId < 0)
     {
         LOG_ERR("Failed to get LoKitDocument instance for [" << 
getJailedFilePathAnonym() << "].");
@@ -579,6 +579,17 @@ bool ChildSession::loadDocument(const char * /*buffer*/, 
int /*length*/, const s
 
     std::unique_lock<std::mutex> lockLokDoc(_docManager.getDocumentMutex());
 
+    if (!doctemplate.empty())
+    {
+        std::string url = getJailedFilePath();
+        bool success = getLOKitDocument()->saveAs(url.c_str(), nullptr, 
nullptr);
+        if (!success)
+        {
+            LOG_ERR("Failed to save template [" << getJailedFilePath() << 
"].");
+            return false;
+        }
+    }
+
     getLOKitDocument()->setView(_viewId);
 
     _docType = LOKitHelper::getDocumentTypeAsString(getLOKitDocument()->get());
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index bbcc2c773..e26b8b78f 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -47,7 +47,8 @@ public:
                         const std::string& renderOpts,
                         const bool haveDocPassword,
                         const std::string& lang,
-                        const std::string& watermarkText) = 0;
+                        const std::string& watermarkText,
+                        const std::string& docTemplate) = 0;
 
     /// Unload a client session, which unloads the document
     /// if it is the last and only.
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 66042d9d3..e449a9b95 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1241,7 +1241,8 @@ private:
                 const std::string& renderOpts,
                 const bool haveDocPassword,
                 const std::string& lang,
-                const std::string& watermarkText) override
+                const std::string& watermarkText,
+                const std::string& docTemplate) override
     {
         std::unique_lock<std::mutex> lock(_mutex);
 
@@ -1270,7 +1271,7 @@ private:
 
         try
         {
-            if (!load(session, uri, uriAnonym, userName, userNameAnonym, 
docPassword, renderOpts, haveDocPassword, lang, watermarkText))
+            if (!load(session, uri, uriAnonym, userName, userNameAnonym, 
docPassword, renderOpts, haveDocPassword, lang, watermarkText, docTemplate))
             {
                 return false;
             }
@@ -1525,7 +1526,8 @@ private:
                                         const std::string& renderOpts,
                                         const bool haveDocPassword,
                                         const std::string& lang,
-                                        const std::string& watermarkText)
+                                        const std::string& watermarkText,
+                                        const std::string& docTemplate)
     {
         const std::string sessionId = session->getId();
 
@@ -1558,7 +1560,7 @@ private:
 
             LOG_DBG("Calling lokit::documentLoad(" << uriAnonym << ", \"" << 
options << "\").");
             Timestamp timestamp;
-            _loKitDocument.reset(_loKit->documentLoad(uri.c_str(), 
options.c_str()));
+            _loKitDocument.reset(_loKit->documentLoad(docTemplate.empty() ? 
uri.c_str() : docTemplate.c_str(), options.c_str()));
             LOG_DBG("Returned lokit::documentLoad(" << uriAnonym << ") in " << 
(timestamp.elapsed() / 1000.) << "ms.");
 #ifdef IOS
             // The iOS app (and the Android one) has max one document open at 
a time, so we can keep
diff --git a/test/Makefile.am b/test/Makefile.am
index 8b4bb0ed0..cc41aa6a4 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,7 +9,7 @@ check_PROGRAMS = test fakesockettest
 
 noinst_PROGRAMS = test fakesockettest unittest
 
-AM_CXXFLAGS = $(CPPUNIT_CFLAGS) -DTDOC=\"$(top_srcdir)/test/data\" \
+AM_CXXFLAGS = $(CPPUNIT_CFLAGS) -DTDOC=\"$(abs_top_srcdir)/test/data\" \
        -I${top_srcdir}/common -I${top_srcdir}/net -I${top_srcdir}/wsd 
-I${top_srcdir}/kit
 
 noinst_LTLIBRARIES = \
@@ -21,8 +21,7 @@ noinst_LTLIBRARIES = \
        unit-wopi.la unit-wopi-saveas.la \
        unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
        unit-wopi-documentconflict.la unit_wopi_renamefile.la \
-       unit-wopi-loadencoded.la
-
+       unit-wopi-loadencoded.la unit-wopi-temp.la
 
 MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy
 AM_LDFLAGS = -pthread -module $(MAGIC_TO_FORCE_SHLIB_CREATION) $(ZLIB_LIBS)
@@ -112,6 +111,8 @@ unit_wopi_renamefile_la_SOURCES = UnitWOPIRenameFile.cpp
 unit_wopi_renamefile_la_LIBADD = $(CPPUNIT_LIBS)
 unit_wopi_loadencoded_la_SOURCES = UnitWOPILoadEncoded.cpp
 unit_wopi_loadencoded_la_LIBADD = $(CPPUNIT_LIBS)
+unit_wopi_temp_la_SOURCES = UnitWOPITemplate.cpp
+unit_wopi_temp_la_LIBADD = $(CPPUNIT_LIBS)
 
 if HAVE_LO_PATH
 SYSTEM_STAMP = @SYSTEMPLATE_PATH@/system_stamp
@@ -129,7 +130,8 @@ check-local:
 TESTS = unit-convert.la unit-prefork.la unit-tilecache.la unit-timeout.la \
         unit-oauth.la unit-wopi.la unit-wopi-saveas.la \
         unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
-        unit-wopi-documentconflict.la unit_wopi_renamefile.la
+        unit-wopi-documentconflict.la unit_wopi_renamefile.la \
+        unit-wopi-temp.la
 # TESTS = unit-client.la
 # TESTS += unit-admin.la
 # TESTS += unit-storage.la
diff --git a/test/UnitWOPITemplate.cpp b/test/UnitWOPITemplate.cpp
new file mode 100644
index 000000000..1ab588799
--- /dev/null
+++ b/test/UnitWOPITemplate.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config.h>
+
+#include <WopiTestServer.hpp>
+#include <Log.hpp>
+#include <Unit.hpp>
+#include <UnitHTTP.hpp>
+#include <helpers.hpp>
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/Util/LayeredConfiguration.h>
+
+class UnitWOPITemplate : public WopiTestServer
+{
+    enum class Phase
+    {
+        LoadTemplate,
+        SaveDoc,
+        CloseDoc,
+        Polling
+    } _phase;
+
+public:
+    UnitWOPITemplate() :
+        _phase(Phase::LoadTemplate)
+    {
+    }
+
+    virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, 
Poco::MemoryInputStream& /*message*/, std::shared_ptr<StreamSocket>& socket) 
override
+    {
+        Poco::URI uriReq(request.getURI());
+        LOG_INF("Fake wopi host request: " << uriReq.toString() << " method " 
<< request.getMethod());
+
+        // CheckFileInfo
+        if (request.getMethod() == "GET" && uriReq.getPath() == 
"/wopi/files/10")
+        {
+            LOG_INF("Fake wopi host request, handling CheckFileInfo: " << 
uriReq.getPath());
+
+            Poco::LocalDateTime now;
+            Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object();
+            fileInfo->set("BaseFileName", "test.odt");
+            fileInfo->set("TemplateSource", std::string("http://127.0.0.1:";) + 
std::to_string(helpers::getClientPort()) + "/test.ott"); // must be http, 
otherwise Neon in the core complains
+            fileInfo->set("Size", _fileContent.size());
+            fileInfo->set("Version", "1.0");
+            fileInfo->set("OwnerId", "test");
+            fileInfo->set("UserId", "test");
+            fileInfo->set("UserFriendlyName", "test");
+            fileInfo->set("UserCanWrite", "true");
+            fileInfo->set("PostMessageOrigin", "localhost");
+            fileInfo->set("LastModifiedTime", 
Poco::DateTimeFormatter::format(Poco::DateTime(_fileLastModifiedTime), 
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT));
+            fileInfo->set("EnableOwnerTermination", "true");
+
+            std::ostringstream jsonStream;
+            fileInfo->stringify(jsonStream);
+            std::string responseString = jsonStream.str();
+
+            const std::string mimeType = "application/json; charset=utf-8";
+
+            std::ostringstream oss;
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "Last-Modified: " << 
Poco::DateTimeFormatter::format(_fileLastModifiedTime, 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "Content-Length: " << responseString.size() << "\r\n"
+                << "Content-Type: " << mimeType << "\r\n"
+                << "\r\n"
+                << responseString;
+
+            socket->send(oss.str());
+            socket->shutdown();
+
+            return true;
+        }
+        else if ((request.getMethod() == "OPTIONS" || request.getMethod() == 
"HEAD" || request.getMethod() == "PROPFIND") && uriReq.getPath() == "/test.ott")
+        {
+            std::ostringstream oss;
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "Allow: GET\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "\r\n";
+
+            socket->send(oss.str());
+            socket->shutdown();
+
+            return true;
+        }
+        // Get the template
+        else if (request.getMethod() == "GET" && uriReq.getPath() == 
"/test.ott")
+        {
+            LOG_INF("Fake wopi host request, handling template GetFile: " << 
uriReq.getPath());
+
+            Poco::Net::HTTPResponse response;
+            HttpHelper::sendFile(socket, TDOC "/test.ott", "", response);
+            socket->shutdown();
+
+            return true;
+        }
+        // Save template
+        else if (request.getMethod() == "POST" && uriReq.getPath() == 
"/wopi/files/10/contents")
+        {
+            LOG_INF("Fake wopi host request, handling PutFile: " << 
uriReq.getPath());
+
+            std::streamsize size = request.getContentLength();
+            CPPUNIT_ASSERT( size > 0 );
+
+            std::ostringstream oss;
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "\r\n"
+                << "{\"LastModifiedTime\": \"" << 
Poco::DateTimeFormatter::format(_fileLastModifiedTime, 
Poco::DateTimeFormat::ISO8601_FRAC_FORMAT) << "\" }";
+
+            socket->send(oss.str());
+            socket->shutdown();
+
+            CPPUNIT_ASSERT_EQUAL(static_cast<int>(Phase::SaveDoc), 
static_cast<int>(_phase));
+            _phase = Phase::CloseDoc;
+
+            return true;
+        }
+
+
+        return false;
+    }
+
+
+    void invokeTest() override
+    {
+        constexpr char testName[] = "UnitWOPITemplate";
+
+        switch (_phase)
+        {
+            case Phase::LoadTemplate:
+            {
+                initWebsocket("/wopi/files/10?access_token=anything");
+
+                helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "load url=" + 
_wopiSrc, testName);
+                SocketPoll::wakeupWorld();
+
+                _phase = Phase::SaveDoc;
+                break;
+            }
+            case Phase::CloseDoc:
+            {
+                helpers::sendTextFrame(*_ws->getLOOLWebSocket(), 
"closedocument", testName);
+                _phase = Phase::Polling;
+                break;
+            }
+            case Phase::Polling:
+            {
+                exitTest(TestResult::Ok);
+                break;
+            }
+            case Phase::SaveDoc:
+            {
+                break;
+            }
+        }
+    }
+};
+
+UnitBase *unit_create_wsd(void)
+{
+    return new UnitWOPITemplate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index dd210dd7a..6154a6cf7 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -503,7 +503,8 @@ public:
                 const std::string& /*renderOpts*/,
                 const bool /*haveDocPassword*/,
                 const std::string& /*lang*/,
-                const std::string& /*watermarkText*/) override
+                const std::string& /*watermarkText*/,
+                const std::string& /*docTemplate*/) override
     {
         return false;
     }
diff --git a/test/data/test.ott b/test/data/test.ott
new file mode 100644
index 000000000..d7f63419c
Binary files /dev/null and b/test/data/test.ott differ
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 8517f0bb3..c898049d0 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -463,9 +463,9 @@ bool ClientSession::loadDocument(const char* /*buffer*/, 
int /*length*/,
     LOG_INF("Requesting document load from child.");
     try
     {
-        std::string timestamp;
+        std::string timestamp, doctemplate;
         int loadPart = -1;
-        parseDocOptions(tokens, loadPart, timestamp);
+        parseDocOptions(tokens, loadPart, timestamp, doctemplate);
 
         std::ostringstream oss;
         oss << "load";
@@ -523,6 +523,11 @@ bool ClientSession::loadDocument(const char* /*buffer*/, 
int /*length*/,
             oss << " options=" << getDocOptions();
         }
 
+        if (_wopiFileInfo && !_wopiFileInfo->getTemplateSource().empty())
+        {
+            oss << " template=" << _wopiFileInfo->getTemplateSource();
+        }
+
         return forwardToChild(oss.str(), docBroker);
     }
     catch (const Poco::SyntaxException&)
@@ -989,6 +994,13 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
         {
             setViewLoaded();
             docBroker->setLoaded();
+            // Wopi post load actions
+            if (_wopiFileInfo && !_wopiFileInfo->getTemplateSource().empty())
+            {
+                std::string result;
+                LOG_DBG("Saving template [" << 
_wopiFileInfo->getTemplateSource() << "] to storage");
+                docBroker->saveToStorage(getId(), true, result);
+            }
 
             for(auto &token : tokens)
             {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 32ae37ef4..0ddd086a1 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -290,7 +290,6 @@ void DocumentBroker::pollThread()
 
             if (_childProcess)
                 _childProcess->terminate();
-
             stop("Load timed out");
             continue;
         }
@@ -513,6 +512,7 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
     std::string userId, username;
     std::string userExtraInfo;
     std::string watermarkText;
+    std::string templateSource;
 
 #ifndef MOBILEAPP
     std::chrono::duration<double> getInfoCallDuration(0);
@@ -524,6 +524,7 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
         username = wopifileinfo->getUsername();
         userExtraInfo = wopifileinfo->getUserExtraInfo();
         watermarkText = wopifileinfo->getWatermarkText();
+        templateSource = wopifileinfo->getTemplateSource();
 
         if (!wopifileinfo->getUserCanWrite() ||
             LOOLWSD::IsViewFileExtension(wopiStorage->getFileExtension()))
@@ -558,6 +559,9 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
         if (!wopifileinfo->getTemplateSaveAs().empty())
             wopiInfo->set("TemplateSaveAs", wopifileinfo->getTemplateSaveAs());
 
+        if (!templateSource.empty())
+                wopiInfo->set("TemplateSource", templateSource);
+
         wopiInfo->set("HidePrintOption", wopifileinfo->getHidePrintOption());
         wopiInfo->set("HideSaveOption", wopifileinfo->getHideSaveOption());
         wopiInfo->set("HideExportOption", wopifileinfo->getHideExportOption());
@@ -673,7 +677,7 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
     // Let's load the document now, if not loaded.
     if (!_storage->isLoaded())
     {
-        std::string localPath = 
_storage->loadStorageFileToLocal(session->getAuthorization());
+        std::string localPath = 
_storage->loadStorageFileToLocal(session->getAuthorization(), templateSource);
 
 #ifndef MOBILEAPP
         // Check if we have a prefilter "plugin" for this document format
@@ -746,7 +750,8 @@ bool DocumentBroker::load(const 
std::shared_ptr<ClientSession>& session, const s
         _filename = fileInfo.getFilename();
 
         // Use the local temp file's timestamp.
-        _lastFileModifiedTime = 
Poco::File(_storage->getRootFilePath()).getLastModified();
+        _lastFileModifiedTime = templateSource.empty() ? 
Poco::File(_storage->getRootFilePath()).getLastModified() :
+                Poco::Timestamp::fromEpochTime(0);
         _tileCache.reset(new TileCache(_storage->getUriString(), 
_lastFileModifiedTime, _cacheRoot, LOOLWSD::TileCachePersistent));
         _tileCache->setThreadOwner(std::this_thread::get_id());
     }
@@ -1454,7 +1459,7 @@ void 
DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
 
     LOG_TRC("TileCombined request for " << tileCombined.serialize());
 
-    // Check which newly requested tiles needs rendering.
+    // Check which newly requested tiles need rendering.
     std::vector<TileDesc> tilesNeedsRendering;
     for (auto& tile : tileCombined.getTiles())
     {
@@ -1537,7 +1542,6 @@ void DocumentBroker::sendRequestedTiles(const 
std::shared_ptr<ClientSession>& se
     float tilesOnFlyUpperLimit = 0;
     if (normalizedVisArea.hasSurface() && session->getTileWidthInTwips() != 0 
&& session->getTileHeightInTwips() != 0)
     {
-
         const int tilesFitOnWidth = std::ceil(normalizedVisArea.getRight() / 
session->getTileWidthInTwips()) -
                                     std::ceil(normalizedVisArea.getLeft() / 
session->getTileWidthInTwips()) + 1;
         const int tilesFitOnHeight = std::ceil(normalizedVisArea.getBottom() / 
session->getTileHeightInTwips()) -
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 35fc79c48..ad387d104 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2822,6 +2822,9 @@ private:
         // Supports the TemplateSaveAs in CheckFileInfo?
         capabilities->set("hasTemplateSaveAs", true);
 
+        // Supports the TemplateSource in CheckFileInfo?
+        capabilities->set("hasTemplateSource", true);
+
         // Hint to encourage use on mobile devices
         capabilities->set("hasMobileSupport", true);
 
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 94c05d5e7..30650814a 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -272,7 +272,7 @@ std::unique_ptr<LocalStorage::LocalFileInfo> 
LocalStorage::getLocalFileInfo()
     return std::unique_ptr<LocalStorage::LocalFileInfo>(new 
LocalFileInfo({"localhost" + std::to_string(LastLocalStorageId), "LocalHost#" + 
std::to_string(LastLocalStorageId++)}));
 }
 
-std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/)
+std::string LocalStorage::loadStorageFileToLocal(const Authorization& 
/*auth*/, const std::string& /*templateUri*/)
 {
 #ifndef MOBILEAPP
     // /chroot/jailId/user/doc/childId/file.ext
@@ -515,6 +515,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> 
WopiStorage::getWOPIFileInfo(const Au
     std::string userExtraInfo;
     std::string watermarkText;
     std::string templateSaveAs;
+    std::string templateSource;
     bool canWrite = false;
     bool enableOwnerTermination = false;
     std::string postMessageOrigin;
@@ -549,6 +550,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> 
WopiStorage::getWOPIFileInfo(const Au
         JsonUtil::findJSONValue(object, "UserId", userId);
         JsonUtil::findJSONValue(object, "UserFriendlyName", userName);
         JsonUtil::findJSONValue(object, "TemplateSaveAs", templateSaveAs);
+        JsonUtil::findJSONValue(object, "TemplateSource", templateSource);
 
         // Anonymize key values.
         if (LOOLWSD::AnonymizeFilenames || LOOLWSD::AnonymizeUsernames)
@@ -587,6 +589,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> 
WopiStorage::getWOPIFileInfo(const Au
             {
                 object->remove("BaseFileName");
                 object->remove("TemplateSaveAs");
+                object->remove("TemplateSource");
             }
 
             if (LOOLWSD::AnonymizeUsernames)
@@ -643,8 +646,8 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> 
WopiStorage::getWOPIFileInfo(const Au
     setFileInfo(FileInfo({filename, ownerId, modifiedTime, size}));
 
     return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo(
-        {userId, obfuscatedUserId, userName, userExtraInfo, watermarkText, 
templateSaveAs, canWrite,
-         postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption,
+        {userId, obfuscatedUserId, userName, userExtraInfo, watermarkText, 
templateSaveAs, templateSource,
+         canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, 
hideExportOption,
          enableOwnerTermination, disablePrint, disableExport, disableCopy,
          disableInactiveMessages, userCanNotWriteRelative, 
enableInsertRemoteImage, enableShare,
          hideUserList, disableChangeTrackingShow, disableChangeTrackingRecord,
@@ -652,7 +655,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> 
WopiStorage::getWOPIFileInfo(const Au
 }
 
 /// uri format: http://server/<...>/wopi*/files/<id>/content
-std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth)
+std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth, 
const std::string& templateUri)
 {
     // WOPI URI to download files ends in '/contents'.
     // Add it here to get the payload instead of file info.
@@ -674,6 +677,17 @@ std::string WopiStorage::loadStorageFileToLocal(const 
Authorization& auth)
     uriObjectAnonym.setPath(LOOLWSD::anonymizeUrl(uriObjectAnonym.getPath()) + 
"/contents");
     const std::string uriAnonym = uriObjectAnonym.toString();
 
+    if (!templateUri.empty())
+    {
+        // template are created in kit process, so just obtain a reference
+        setRootFilePath(Poco::Path(getLocalRootPath(), 
getFileInfo().getFilename()).toString());
+        setRootFilePathAnonym(LOOLWSD::anonymizeUrl(getRootFilePath()));
+        LOG_INF("Template reference " << getRootFilePathAnonym());
+
+        setLoaded(true);
+        return Poco::Path(getJailPath(), 
getFileInfo().getFilename()).toString();
+    }
+
     LOG_DBG("Wopi requesting: " << uriAnonym);
 
     const auto startTime = std::chrono::steady_clock::now();
@@ -955,7 +969,7 @@ StorageBase::SaveResult 
WopiStorage::saveLocalFileToStorage(const Authorization&
     return saveResult;
 }
 
-std::string WebDAVStorage::loadStorageFileToLocal(const Authorization& 
/*auth*/)
+std::string WebDAVStorage::loadStorageFileToLocal(const Authorization& 
/*auth*/, const std::string& /*templateUri*/)
 {
     // TODO: implement webdav GET.
     setLoaded(true);
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index baa0317ad..a95e772b1 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -189,7 +189,7 @@ public:
 
     /// Returns a local file path for the given URI.
     /// If necessary copies the file locally first.
-    virtual std::string loadStorageFileToLocal(const Authorization& auth) = 0;
+    virtual std::string loadStorageFileToLocal(const Authorization& auth, 
const std::string& templateUri) = 0;
 
     /// Writes the contents of the file back to the source.
     /// @param savedFile When the operation was saveAs, this is the path to 
the file that was saved.
@@ -272,7 +272,7 @@ public:
     /// obtained using getFileInfo method
     std::unique_ptr<LocalFileInfo> getLocalFileInfo();
 
-    std::string loadStorageFileToLocal(const Authorization& auth) override;
+    std::string loadStorageFileToLocal(const Authorization& auth, const 
std::string& templateUri) override;
 
     SaveResult saveLocalFileToStorage(const Authorization& auth, const 
std::string& saveAsPath, const std::string& saveAsFilename, const bool 
isRename) override;
 
@@ -317,6 +317,7 @@ public:
                      const std::string& userExtraInfo,
                      const std::string& watermarkText,
                      const std::string& templateSaveAs,
+                     const std::string& templateSource,
                      const bool userCanWrite,
                      const std::string& postMessageOrigin,
                      const bool hidePrintOption,
@@ -342,6 +343,7 @@ public:
               _username(username),
               _watermarkText(watermarkText),
               _templateSaveAs(templateSaveAs),
+              _templateSource(templateSource),
               _userCanWrite(userCanWrite),
               _postMessageOrigin(postMessageOrigin),
               _hidePrintOption(hidePrintOption),
@@ -376,6 +378,8 @@ public:
 
         const std::string& getTemplateSaveAs() const { return _templateSaveAs; 
}
 
+        const std::string& getTemplateSource() const { return _templateSource; 
}
+
         bool getUserCanWrite() const { return _userCanWrite; }
 
         std::string& getPostMessageOrigin() { return _postMessageOrigin; }
@@ -433,6 +437,8 @@ public:
         std::string _watermarkText;
         /// In case we want to use this file as a template, it should be first 
re-saved under this name (using PutRelativeFile).
         std::string _templateSaveAs;
+        /// In case we want to use this file as a template.
+        std::string _templateSource;
         /// If user accessing the file has write permission
         bool _userCanWrite;
         /// WOPI Post message property
@@ -485,7 +491,7 @@ public:
     std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const Authorization& auth);
 
     /// uri format: http://server/<...>/wopi*/files/<id>/content
-    std::string loadStorageFileToLocal(const Authorization& auth) override;
+    std::string loadStorageFileToLocal(const Authorization& auth, const 
std::string& templateUri) override;
 
     SaveResult saveLocalFileToStorage(const Authorization& auth, const 
std::string& saveAsPath, const std::string& saveAsFilename, const bool 
isRename) override;
 
@@ -517,7 +523,7 @@ public:
     // Implement me
     // WebDAVFileInfo getWebDAVFileInfo(const Poco::URI& uriPublic);
 
-    std::string loadStorageFileToLocal(const Authorization& auth) override;
+    std::string loadStorageFileToLocal(const Authorization& auth, const 
std::string& templateUri) override;
 
     SaveResult saveLocalFileToStorage(const Authorization& auth, const 
std::string& saveAsPath, const std::string& saveAsFilename, const bool 
isRename) override;
 
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to