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