diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-11-30 13:58:39 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-12-06 16:03:28 +0000 |
commit | 6004550c5a5fefe532dd73cceb163055fa475217 (patch) | |
tree | 661e44c8adafbed7fe3a87ae0c4ea1bb495fbf3a | |
parent | a61a4da16ef2fea05787fa57c45cde11eb72e11e (diff) |
sfx2: extend .uno:SetDocumentProperties to update custom doc props
Scripting clients (like the LOK API) had a way to get all custom
properties where the name matches a certain prefix, but setting such
properties was not possible.
.uno:SetDocumentProperties can already show a dialog to edit properties
interactively and had a parameter to set some properties in a
non-interactive way, but there doesn't seem to be a way to influence
custom properties there without using the internal API.
Fix the problem by adding a new UpdatedProperties parameter that allows
removing all old custom properties matching the prefix and adding new
ones with a single UNO command dispatch.
This is meant to be the write side of the reading commit
5e8f6dcb8ce00d2d5e35b3cf5654187b3068276c (sw lok,
.uno:SetDocumentProperties: expose value of custom document properties,
2022-11-29).
(cherry picked from commit afb362c60a18243621834dcf2b30672be6eed76f)
Change-Id: Ib7450d4d21285d9a73758e1c172543521fc07cef
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143708
Tested-by: Miklos Vajna <vmiklos@collabora.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r-- | comphelper/source/misc/sequenceashashmap.cxx | 8 | ||||
-rw-r--r-- | sfx2/qa/cppunit/doc.cxx | 60 | ||||
-rw-r--r-- | sfx2/sdi/sfx.sdi | 2 | ||||
-rw-r--r-- | sfx2/source/doc/objserv.cxx | 57 |
4 files changed, 126 insertions, 1 deletions
diff --git a/comphelper/source/misc/sequenceashashmap.cxx b/comphelper/source/misc/sequenceashashmap.cxx index 957c76feceac..6d2379de5abb 100644 --- a/comphelper/source/misc/sequenceashashmap.cxx +++ b/comphelper/source/misc/sequenceashashmap.cxx @@ -365,6 +365,14 @@ std::vector<css::beans::PropertyValue> JsonToPropertyValues(const OString& rJson aValue.Value <<= aSeq; } } + else if (rType == "[]com.sun.star.beans.PropertyValue") + { + aNodeValue = rPair.second.get_child("value", aNodeNull); + std::stringstream s; + boost::property_tree::write_json(s, aNodeValue); + std::vector<beans::PropertyValue> aPropertyValues = JsonToPropertyValues(s.str().c_str()); + aValue.Value <<= comphelper::containerToSequence(aPropertyValues); + } else if (rType == "[][]com.sun.star.beans.PropertyValue") { aNodeValue = rPair.second.get_child("value", aNodeNull); diff --git a/sfx2/qa/cppunit/doc.cxx b/sfx2/qa/cppunit/doc.cxx index 7e62652b5bbb..b706c2476508 100644 --- a/sfx2/qa/cppunit/doc.cxx +++ b/sfx2/qa/cppunit/doc.cxx @@ -13,11 +13,16 @@ #include <com/sun/star/frame/Desktop.hpp> #include <com/sun/star/view/XSelectionSupplier.hpp> #include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> #include <comphelper/propertyvalue.hxx> #include <sfx2/objsh.hxx> #include <sfx2/sfxbasemodel.hxx> #include <osl/file.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> using namespace com::sun::star; @@ -103,6 +108,61 @@ CPPUNIT_TEST_FIXTURE(Test, testTempFilePath) // "test%25C3%25Bf" instead of a directory named "test%C3%Bf". pBaseModel->storeToURL(aPdfTarget, aPdfArgs); } + +CPPUNIT_TEST_FIXTURE(Test, testSetDocumentPropertiesUpdate) +{ + // Given a document with 3 custom props, 2 Zotero ones and an other: + getComponent() = loadFromDesktop("private:factory/swriter"); + auto pBaseModel = dynamic_cast<SfxBaseModel*>(getComponent().get()); + CPPUNIT_ASSERT(pBaseModel); + uno::Reference<document::XDocumentProperties> xDP = pBaseModel->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 updating the Zotero ones (1 update, 1 removal): + std::vector<beans::PropertyValue> aArgsVec = comphelper::JsonToPropertyValues(R"json( +{ + "UpdatedProperties": { + "type": "[]com.sun.star.beans.PropertyValue", + "value": { + "NamePrefix": { + "type": "string", + "value": "ZOTERO_PREF_" + }, + "UserDefinedProperties": { + "type": "[]com.sun.star.beans.PropertyValue", + "value": { + "ZOTERO_PREF_1": { + "type": "string", + "value": "test" + } + } + } + } + } +} +)json"); + uno::Sequence<beans::PropertyValue> aArgs = comphelper::containerToSequence(aArgsVec); + dispatchCommand(getComponent(), ".uno:SetDocumentProperties", aArgs); + + // Then make sure that OTHER is still there and that ZOTERO_PREF_1 + ZOTERO_PREF_2 gets updated + // to the new value of a single ZOTERO_PREF_1: + uno::Reference<beans::XPropertyAccess> xUDPAccess(xUDP, uno::UNO_QUERY); + comphelper::SequenceAsHashMap aMap(xUDPAccess->getPropertyValues()); + auto it = aMap.find("ZOTERO_PREF_1"); + CPPUNIT_ASSERT(it != aMap.end()); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: test + // - Actual : foo + // i.e. ZOTERO_PREF_1 was not updated. + CPPUNIT_ASSERT_EQUAL(OUString("test"), it->second.get<OUString>()); + CPPUNIT_ASSERT(bool(aMap.find("ZOTERO_PREF_2") == aMap.end())); + CPPUNIT_ASSERT(aMap.find("OTHER") != aMap.end()); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sfx2/sdi/sfx.sdi b/sfx2/sdi/sfx.sdi index 49888d5a2a73..1cfa7483f315 100644 --- a/sfx2/sdi/sfx.sdi +++ b/sfx2/sdi/sfx.sdi @@ -3325,7 +3325,7 @@ SfxBoolItem PrintPreview SID_PRINTPREVIEW SfxVoidItem SetDocumentProperties SID_DOCINFO -(SfxDocumentInfoItem Properties SID_DOCINFO) +(SfxDocumentInfoItem Properties SID_DOCINFO, SfxUnoAnyItem UpdatedProperties FN_PARAM_1) [ AutoUpdate = FALSE, FastCall = FALSE, diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx index d3db98802433..a003fb2d73e6 100644 --- a/sfx2/source/doc/objserv.cxx +++ b/sfx2/source/doc/objserv.cxx @@ -24,6 +24,7 @@ #include <com/sun/star/util/CloseVetoException.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> #include <com/sun/star/document/XCmisDocument.hpp> #include <com/sun/star/drawing/LineStyle.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> @@ -466,6 +467,56 @@ static void sendErrorToLOK(ErrCode error) SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_CALLBACK_ERROR, aStream.str().c_str()); } +namespace +{ +void SetDocProperties(const uno::Reference<document::XDocumentProperties>& xDP, + const uno::Sequence<beans::PropertyValue>& rUpdatedProperties) +{ + comphelper::SequenceAsHashMap aMap(rUpdatedProperties); + OUString aNamePrefix; + auto it = aMap.find("NamePrefix"); + if (it != aMap.end()) + { + it->second >>= aNamePrefix; + } + + uno::Sequence<beans::PropertyValue> aUserDefinedProperties; + it = aMap.find("UserDefinedProperties"); + if (it != aMap.end()) + { + it->second >>= aUserDefinedProperties; + } + + uno::Reference<beans::XPropertyContainer> xUDP = xDP->getUserDefinedProperties(); + if (!aNamePrefix.isEmpty()) + { + uno::Reference<beans::XPropertySet> xSet(xUDP, UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xSetInfo = xSet->getPropertySetInfo(); + const uno::Sequence<beans::Property> aProperties = xSetInfo->getProperties(); + for (const auto& rProperty : aProperties) + { + if (!rProperty.Name.startsWith(aNamePrefix)) + { + continue; + } + + if (!(rProperty.Attributes & beans::PropertyAttribute::REMOVABLE)) + { + continue; + } + + xUDP->removeProperty(rProperty.Name); + } + } + + for (const auto& rUserDefinedProperty : aUserDefinedProperties) + { + xUDP->addProperty(rUserDefinedProperty.Name, beans::PropertyAttribute::REMOVABLE, + rUserDefinedProperty.Value); + } +} +} + void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) { weld::Window* pDialogParent = rReq.GetFrameWeld(); @@ -570,6 +621,12 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) SetUseUserData( pDocInfItem->IsUseUserData() ); SetUseThumbnailSave( pDocInfItem->IsUseThumbnailSave() ); } + else if (const SfxUnoAnyItem* pItem = rReq.GetArg<SfxUnoAnyItem>(FN_PARAM_1)) + { + uno::Sequence<beans::PropertyValue> aUpdatedProperties; + pItem->GetValue() >>= aUpdatedProperties; + SetDocProperties(getDocProperties(), aUpdatedProperties); + } else { // no argument containing DocInfo; check optional arguments |