desktop/source/lib/init.cxx | 9 ++- sw/qa/uibase/uno/uno.cxx | 37 ++++++++++++++ sw/source/uibase/uno/unotxdoc.cxx | 94 ++++++++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 26 deletions(-)
New commits: commit 5e8f6dcb8ce00d2d5e35b3cf5654187b3068276c Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Nov 29 16:18:16 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Nov 30 08:13:28 2022 +0100 sw lok, .uno:SetDocumentProperties: expose value of custom document properties LOK API clients currently have no knowledge about document properties. Clients like Zotero that want to store custom properties on documents need a way to read and write such properties. This commit focuses on the reading side. Add a getter for .uno:SetDocumentProperties that allows filtering for a certain prefix, this way the returned value can contain only the relevant information. Rework doc_getCommandValues() and SwXTextDocument::getCommandValues() a bit, so adding new getters require less duplication. Change-Id: I0c52cd2efcc8b1ea7307763c8252dd1e8ffdea2d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143468 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 15371581c818..734f76f76362 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -5702,7 +5702,10 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo static constexpr OStringLiteral aSheetGeometryData(".uno:SheetGeometryData"); static constexpr OStringLiteral aCellCursor(".uno:CellCursor"); static constexpr OStringLiteral aFontSubset(".uno:FontSubset&name="); - static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields"); + static const std::initializer_list<std::u16string_view> vForward = { + u"TextFormFields", + u"SetDocumentProperties" + }; if (!strcmp(pCommand, ".uno:LanguageStatus")) { @@ -5879,7 +5882,9 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo { return getFontSubset(aCommand.substr(aFontSubset.getLength())); } - else if (o3tl::starts_with(aCommand, aTextFormFields)) + else if (std::find(vForward.begin(), vForward.end(), + INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath()) + != vForward.end()) { ITiledRenderable* pDoc = getTiledRenderable(pThis); if (!pDoc) diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx index 02d95df370c4..8e6b06453d86 100644 --- a/sw/qa/uibase/uno/uno.cxx +++ b/sw/qa/uibase/uno/uno.cxx @@ -15,6 +15,7 @@ #include <com/sun/star/text/XTextViewTextRangeSupplier.hpp> #include <com/sun/star/util/XCloseable.hpp> #include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> #include <vcl/scheduler.hxx> #include <tools/json_writer.hxx> @@ -195,12 +196,46 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetTextFormFields) std::stringstream aStream(pJSON.get()); boost::property_tree::ptree aTree; boost::property_tree::read_json(aStream, aTree); - // Without the needed PixelToLogic() call in place, this test would have failed with: + // Without the accompanying fix in place, this test would have failed with: // - No such node (fields) // i.e. the returned JSON was just empty. CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aTree.get_child("fields").count("")); } +CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetDocumentProperties) +{ + // Given a document with 3 custom properties: 2 zotero ones and an other one: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwDocShell* pDocShell = pDoc->GetDocShell(); + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(), + uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDP = xDPS->getDocumentProperties(); + uno::Reference<beans::XPropertyContainer> xUDP = xDP->getUserDefinedProperties(); + xUDP->addProperty("ZOTERO_PREF_1", beans::PropertyAttribute::REMOVABLE, + uno::Any(OUString("foo"))); + xUDP->addProperty("ZOTERO_PREF_2", beans::PropertyAttribute::REMOVABLE, + uno::Any(OUString("bar"))); + xUDP->addProperty("OTHER", beans::PropertyAttribute::REMOVABLE, uno::Any(OUString("baz"))); + + // When getting the zotero properties: + tools::JsonWriter aJsonWriter; + std::string_view aCommand(".uno:SetDocumentProperties?namePrefix=ZOTERO_PREF_"); + auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + pXTextDocument->getCommandValues(aJsonWriter, aCommand); + + // Then make sure we find the 2 properties and ignore the other one: + std::unique_ptr<char[], o3tl::free_delete> pJSON(aJsonWriter.extractData()); + std::stringstream aStream(pJSON.get()); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + // Without the accompanying fix in place, this test would have failed with: + // - No such node (userDefinedProperties) + // i.e. the returned JSON was just empty. + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), + aTree.get_child("userDefinedProperties").count("")); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx index 3405a1631727..8d8a7ca0f882 100644 --- a/sw/source/uibase/uno/unotxdoc.cxx +++ b/sw/source/uibase/uno/unotxdoc.cxx @@ -84,6 +84,7 @@ #include <com/sun/star/lang/NoSupportException.hpp> #include <com/sun/star/beans/PropertyAttribute.hpp> #include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> #include <com/sun/star/document/RedlineDisplayType.hpp> #include <com/sun/star/document/XDocumentEventBroadcaster.hpp> #include <com/sun/star/frame/XController.hpp> @@ -163,6 +164,7 @@ #include <svx/svdpage.hxx> #include <o3tl/string_view.hxx> +#include <comphelper/sequenceashashmap.hxx> #include <IDocumentOutlineNodes.hxx> #include <SearchResultLocator.hxx> @@ -3568,6 +3570,48 @@ void GetTextFormFields(tools::JsonWriter& rJsonWriter, SwDocShell* pDocShell, rJsonWriter.put("command", aCommand); } } + +/// Implements getCommandValues(".uno:SetDocumentProperties"). +/// +/// Parameters: +/// +/// - namePrefix: field name prefix not not return all user-defined properties +void GetDocumentProperties(tools::JsonWriter& rJsonWriter, SwDocShell* pDocShell, + const std::map<OUString, OUString>& rArguments) +{ + OUString aNamePrefix; + auto it = rArguments.find("namePrefix"); + if (it != rArguments.end()) + { + aNamePrefix = it->second; + } + + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(), uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDP = xDPS->getDocumentProperties(); + uno::Reference<beans::XPropertyAccess> xUDP(xDP->getUserDefinedProperties(), uno::UNO_QUERY); + auto aUDPs = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(xUDP->getPropertyValues()); + tools::ScopedJsonWriterArray aProperties = rJsonWriter.startArray("userDefinedProperties"); + for (const auto& rUDP : aUDPs) + { + if (!rUDP.Name.startsWith(aNamePrefix)) + { + continue; + } + + if (rUDP.Value.getValueTypeClass() != TypeClass_STRING) + { + continue; + } + + OUString aValue; + rUDP.Value >>= aValue; + + tools::ScopedJsonWriterStruct aProperty = rJsonWriter.startStruct(); + rJsonWriter.put("name", rUDP.Name); + rJsonWriter.put("type", "string"); + rJsonWriter.put("value", aValue); + } +} } void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand) @@ -3575,34 +3619,38 @@ void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, std::stri std::map<OUString, OUString> aMap; static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields"); + static constexpr OStringLiteral aSetDocumentProperties(".uno:SetDocumentProperties"); + + INetURLObject aParser(OUString::fromUtf8(rCommand)); + OUString aArguments = aParser.GetParam(); + sal_Int32 nParamIndex = 0; + do + { + std::u16string_view aParam = o3tl::getToken(aArguments, 0, '&', nParamIndex); + sal_Int32 nIndex = 0; + OUString aKey; + OUString aValue; + do + { + std::u16string_view aToken = o3tl::getToken(aParam, 0, '=', nIndex); + if (aKey.isEmpty()) + aKey = aToken; + else + aValue = aToken; + } while (nIndex >= 0); + OUString aDecodedValue + = INetURLObject::decode(aValue, INetURLObject::DecodeMechanism::WithCharset); + aMap[aKey] = aDecodedValue; + } while (nParamIndex >= 0); if (o3tl::starts_with(rCommand, aTextFormFields)) { - if (rCommand.size() > o3tl::make_unsigned(aTextFormFields.getLength())) - { - std::string_view aArguments = rCommand.substr(aTextFormFields.getLength() + 1); - sal_Int32 nParamIndex = 0; - do - { - std::string_view aParamToken = o3tl::getToken(aArguments, 0, '&', nParamIndex); - sal_Int32 nIndex = 0; - OUString aKey; - OUString aValue; - do - { - std::string_view aToken = o3tl::getToken(aParamToken, 0, '=', nIndex); - if (aKey.isEmpty()) - aKey = OUString::fromUtf8(aToken); - else - aValue = OUString::fromUtf8(aToken); - } while (nIndex >= 0); - OUString aDecodedValue - = INetURLObject::decode(aValue, INetURLObject::DecodeMechanism::WithCharset); - aMap[aKey] = aDecodedValue; - } while (nParamIndex >= 0); - } GetTextFormFields(rJsonWriter, m_pDocShell, aMap); } + if (o3tl::starts_with(rCommand, aSetDocumentProperties)) + { + GetDocumentProperties(rJsonWriter, m_pDocShell, aMap); + } } int SwXTextDocument::getPart()