sw/qa/extras/ooxmlexport/data/tdf147724.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport18.cxx   |   13 +++++
 writerfilter/source/dmapper/SdtHelper.cxx    |   64 ++++++++++++++++++++++-----
 writerfilter/source/dmapper/SdtHelper.hxx    |    3 -
 4 files changed, 69 insertions(+), 11 deletions(-)

New commits:
commit da100343e5416e1040f8f6d83fd73d8d0577112e
Author:     Vasily Melenchuk <vasily.melenc...@cib.de>
AuthorDate: Sun Sep 11 15:46:09 2022 +0300
Commit:     Vasily Melenchuk <vasily.melenc...@cib.de>
CommitDate: Wed Sep 28 09:58:00 2022 +0200

    tdf#147724: DOCX STD import: use storeid to indetify custom XML
    
    We need to identify XML document to use by storeid in sdt block
    and in properties of external XML file. Otherwise are possible
    content collisions when same xpath can evaluate successfully
    absolutely unrelated custom XML.
    
    Change-Id: I6d201a539130b110046deb1818340513cc47a061
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139771
    Tested-by: Jenkins
    Reviewed-by: Vasily Melenchuk <vasily.melenc...@cib.de>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf147724.docx 
b/sw/qa/extras/ooxmlexport/data/tdf147724.docx
new file mode 100644
index 000000000000..97f05c921b89
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf147724.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 6c4eaa09fa34..b9932f7c199d 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -104,6 +104,19 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149551_mongolianVert)
     assertXPath(pXmlDoc, "//wps:bodyPr", "vert", "mongolianVert");
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx")
+{
+    const auto& pLayout = parseLayoutDump();
+
+    // Ensure we load field value from external XML correctly (it was 
"HERUNTERLADEN")
+    assertXPathContent(pLayout, "/root/page[1]/body/txt[1]", "Placeholder -> 
*ABC*");
+
+    // This SDT has no storage id, it is not an error, but content can be 
taken from any suitable XML
+    // There 2 variants possible, both are acceptable
+    OUString sFieldResult = getXPathContent(pLayout, 
"/root/page[1]/body/txt[2]");
+    CPPUNIT_ASSERT(sFieldResult == "Placeholder -> *HERUNTERLADEN*" || 
sFieldResult == "Placeholder -> *ABC*");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx 
b/writerfilter/source/dmapper/SdtHelper.cxx
index 50ddf4b99b21..8225a6a04bca 100644
--- a/writerfilter/source/dmapper/SdtHelper.cxx
+++ b/writerfilter/source/dmapper/SdtHelper.cxx
@@ -104,13 +104,13 @@ void SdtHelper::loadPropertiesXMLs()
     if (!xDomBuilder.is())
         return;
 
-    std::vector<uno::Reference<xml::dom::XDocument>> aPropDocs;
-
     // Load core properties
     try
     {
         auto xCorePropsStream = 
xImporter->getCorePropertiesStream(m_rDM_Impl.m_xDocumentStorage);
-        aPropDocs.push_back(xDomBuilder->parse(xCorePropsStream));
+        m_xPropertiesXMLs.insert(
+            { OUString("{6C3C8BC8-F283-45AE-878A-BAB7291924A1}"), // hardcoded 
id for core props
+              xDomBuilder->parse(xCorePropsStream) });
     }
     catch (const uno::Exception&)
     {
@@ -123,7 +123,9 @@ void SdtHelper::loadPropertiesXMLs()
     {
         auto xExtPropsStream
             = 
xImporter->getExtendedPropertiesStream(m_rDM_Impl.m_xDocumentStorage);
-        aPropDocs.push_back(xDomBuilder->parse(xExtPropsStream));
+        m_xPropertiesXMLs.insert(
+            { OUString("{6668398D-A668-4E3E-A5EB-62B293D839F1}"), // hardcoded 
id for extended props
+              xDomBuilder->parse(xExtPropsStream) });
     }
     catch (const uno::Exception&)
     {
@@ -136,12 +138,40 @@ void SdtHelper::loadPropertiesXMLs()
     // Add custom XMLs
     uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmls
         = m_rDM_Impl.getDocumentReference()->getCustomXmlDomList();
-    for (const auto& xDoc : aCustomXmls)
+    uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmlProps
+        = m_rDM_Impl.getDocumentReference()->getCustomXmlDomPropsList();
+    if (aCustomXmls.getLength())
     {
-        aPropDocs.push_back(xDoc);
+        uno::Reference<XXPathAPI> xXpathAPI = 
XPathAPI::create(m_xComponentContext);
+        xXpathAPI->registerNS("ds",
+                              
"http://schemas.openxmlformats.org/officeDocument/2006/customXml";);
+        sal_Int32 nItem = 0;
+        // Hereby we assume that items from getCustomXmlDomList() and 
getCustomXmlDomPropsList()
+        // are matching each other:
+        // item1.xml -> itemProps1.xml, item2.xml -> itemProps2.xml
+        // This does works practically, but is it true in general?
+        for (const auto& xDoc : aCustomXmls)
+        {
+            // Retrieve storeid from properties xml
+            OUString aStoreId;
+            uno::Reference<XXPathObject> xResult
+                = xXpathAPI->eval(aCustomXmlProps[nItem], 
"string(/ds:datastoreItem/@ds:itemID)");
+
+            if (xResult.is() && xResult->getString().getLength())
+            {
+                aStoreId = xResult->getString();
+            }
+            else
+            {
+                SAL_WARN("writerfilter",
+                         "SdtHelper::loadPropertiesXMLs: can't fetch storeid 
for custom doc!");
+            }
+
+            m_xPropertiesXMLs.insert({ aStoreId, xDoc });
+            nItem++;
+        }
     }
 
-    m_xPropertiesXMLs = comphelper::containerToSequence(aPropDocs);
     m_bPropertiesXMLsLoaded = true;
 }
 
@@ -191,10 +221,24 @@ std::optional<OUString> 
SdtHelper::getValueFromDataBinding()
 
     lcl_registerNamespaces(m_sDataBindingPrefixMapping, xXpathAPI);
 
-    // Iterate all properties xml documents and try to fetch data
-    for (const auto& xDocument : m_xPropertiesXMLs)
+    // Find storage by store id and eval xpath there
+    const auto& aSourceIt = m_xPropertiesXMLs.find(m_sDataBindingStoreItemID);
+    if (aSourceIt != m_xPropertiesXMLs.end())
+    {
+        uno::Reference<XXPathObject> xResult
+            = xXpathAPI->eval(aSourceIt->second, m_sDataBindingXPath);
+
+        if (xResult.is() && xResult->getNodeList() && 
xResult->getNodeList()->getLength()
+            && xResult->getString().getLength())
+        {
+            return xResult->getString();
+        }
+    }
+
+    // Nothing found? Try to iterate storages and eval xpath
+    for (const auto& aSource : m_xPropertiesXMLs)
     {
-        uno::Reference<XXPathObject> xResult = xXpathAPI->eval(xDocument, 
m_sDataBindingXPath);
+        uno::Reference<XXPathObject> xResult = xXpathAPI->eval(aSource.second, 
m_sDataBindingXPath);
 
         if (xResult.is() && xResult->getNodeList() && 
xResult->getNodeList()->getLength()
             && xResult->getString().getLength())
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx 
b/writerfilter/source/dmapper/SdtHelper.hxx
index 2cfa19f5e95e..689c57503185 100644
--- a/writerfilter/source/dmapper/SdtHelper.hxx
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -11,6 +11,7 @@
 
 #include <vector>
 #include <optional>
+#include <unordered_map>
 
 #include <com/sun/star/beans/PropertyValue.hpp>
 #include <com/sun/star/text/XTextRange.hpp>
@@ -94,7 +95,7 @@ class SdtHelper final : public virtual SvRefBase
     bool m_bOutsideAParagraph;
 
     /// Storage for all properties documents as xml::dom::XDocument for later 
querying xpath for data
-    css::uno::Sequence<css::uno::Reference<css::xml::dom::XDocument>> 
m_xPropertiesXMLs;
+    std::unordered_map<OUString, 
css::uno::Reference<css::xml::dom::XDocument>> m_xPropertiesXMLs;
 
     /// Check if m_xPropertiesXMLs is initialized and loaded (need extra flag 
to distinguish
     /// empty sequence from not yet initialized)

Reply via email to