comphelper/qa/unit/propertyvalue.cxx         |   71 ++++++++++++++++++++
 comphelper/source/misc/sequenceashashmap.cxx |   14 +++
 desktop/source/lib/init.cxx                  |   14 +++
 include/vcl/ITiledRenderable.hxx             |    5 +
 sw/inc/unotxdoc.hxx                          |    3 
 sw/qa/uibase/uno/uno.cxx                     |   50 ++++++++++++++
 sw/source/uibase/uno/unotxdoc.cxx            |   95 +++++++++++++++++++++++++++
 7 files changed, 252 insertions(+)

New commits:
commit 6650a890673ad66b9d0d5bfde0e06ae2e9c3f909
Author:     Miklos Vajna <[email protected]>
AuthorDate: Fri Nov 25 16:32:37 2022 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Tue Nov 29 08:08:05 2022 +0100

    comphelper: support property values arrays in JsonToPropertyValues()
    
    Needed for an uncoming .uno:TextFormFields uno command where one of the
    parameters has the []com.sun.star.beans.PropertyValues type at an UNO
    level, and we can't provide JSON at the moment that would express that.
    
    (cherry picked from commit 1e83197fdd4263ca4817a6ac16f274aaee3e66fd)
    
    Conflicts:
            comphelper/source/misc/sequenceashashmap.cxx
    
    Change-Id: I288a540b2fcac0e5a4a82bca235199c559ba2d0c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143409
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/comphelper/qa/unit/propertyvalue.cxx 
b/comphelper/qa/unit/propertyvalue.cxx
index 40f60bb0463d..738022917e9d 100644
--- a/comphelper/qa/unit/propertyvalue.cxx
+++ b/comphelper/qa/unit/propertyvalue.cxx
@@ -14,9 +14,12 @@
 #include <cppunit/extensions/HelperMacros.h>
 
 #include <comphelper/propertyvalue.hxx>
+#include <comphelper/propertysequence.hxx>
 #include <cppu/unotype.hxx>
 #include <o3tl/any.hxx>
 
+using namespace com::sun::star;
+
 namespace
 {
 class MakePropertyValueTest : public CppUnit::TestFixture
@@ -25,6 +28,7 @@ class MakePropertyValueTest : public CppUnit::TestFixture
     CPPUNIT_TEST(testLvalue);
     CPPUNIT_TEST(testRvalue);
     CPPUNIT_TEST(testBitField);
+    CPPUNIT_TEST(testJson);
     CPPUNIT_TEST_SUITE_END();
 
     void testLvalue()
@@ -52,6 +56,73 @@ class MakePropertyValueTest : public CppUnit::TestFixture
         CPPUNIT_ASSERT_EQUAL(cppu::UnoType<bool>::get(), 
v.Value.getValueType());
         CPPUNIT_ASSERT_EQUAL(false, *o3tl::doAccess<bool>(v.Value));
     }
+
+    void testJson()
+    {
+        std::vector<beans::PropertyValue> aRet = 
comphelper::JsonToPropertyValues(R"json(
+{
+    "FieldType": {
+        "type": "string",
+        "value": "vnd.oasis.opendocument.field.UNHANDLED"
+    },
+    "FieldCommandPrefix": {
+        "type": "string",
+        "value": "ADDIN ZOTERO_ITEM"
+    },
+    "Fields": {
+        "type": "[][]com.sun.star.beans.PropertyValue",
+        "value": [
+            {
+                "FieldType": {
+                    "type": "string",
+                    "value": "vnd.oasis.opendocument.field.UNHANDLED"
+                },
+                "FieldCommand": {
+                    "type": "string",
+                    "value": "ADDIN ZOTERO_ITEM new command 1"
+                },
+                "Fields": {
+                    "type": "string",
+                    "value": "new result 1"
+                }
+            },
+            {
+                "FieldType": {
+                    "type": "string",
+                    "value": "vnd.oasis.opendocument.field.UNHANDLED"
+                },
+                "FieldCommandPrefix": {
+                    "type": "string",
+                    "value": "ADDIN ZOTERO_ITEM new command 2"
+                },
+                "Fields": {
+                    "type": "string",
+                    "value": "new result 2"
+                }
+            }
+        ]
+    }
+}
+)json");
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aRet.size());
+        beans::PropertyValue aFirst = aRet[0];
+        CPPUNIT_ASSERT_EQUAL(OUString("FieldType"), aFirst.Name);
+        
CPPUNIT_ASSERT_EQUAL(OUString("vnd.oasis.opendocument.field.UNHANDLED"),
+                             aFirst.Value.get<OUString>());
+        beans::PropertyValue aSecond = aRet[1];
+        CPPUNIT_ASSERT_EQUAL(OUString("FieldCommandPrefix"), aSecond.Name);
+        CPPUNIT_ASSERT_EQUAL(OUString("ADDIN ZOTERO_ITEM"), 
aSecond.Value.get<OUString>());
+        beans::PropertyValue aThird = aRet[2];
+        CPPUNIT_ASSERT_EQUAL(OUString("Fields"), aThird.Name);
+        uno::Sequence<uno::Sequence<beans::PropertyValue>> aSeqs;
+        aThird.Value >>= aSeqs;
+        CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), aSeqs.getLength());
+        uno::Sequence<beans::PropertyValue> aFirstSeq = aSeqs[0];
+        CPPUNIT_ASSERT_EQUAL(OUString("FieldType"), aFirstSeq[0].Name);
+        CPPUNIT_ASSERT_EQUAL(OUString("FieldCommand"), aFirstSeq[1].Name);
+        CPPUNIT_ASSERT_EQUAL(OUString("ADDIN ZOTERO_ITEM new command 1"),
+                             aFirstSeq[1].Value.get<OUString>());
+    }
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(MakePropertyValueTest);
diff --git a/comphelper/source/misc/sequenceashashmap.cxx 
b/comphelper/source/misc/sequenceashashmap.cxx
index c6ac57326935..957c76feceac 100644
--- a/comphelper/source/misc/sequenceashashmap.cxx
+++ b/comphelper/source/misc/sequenceashashmap.cxx
@@ -30,6 +30,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
 #include <sal/log.hxx>
+#include <comphelper/sequence.hxx>
 
 using namespace com::sun::star;
 
@@ -364,6 +365,19 @@ 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::vector<uno::Sequence<beans::PropertyValue>> aSeqs;
+            for (const auto& rItem : aNodeValue)
+            {
+                std::stringstream s;
+                boost::property_tree::write_json(s, rItem.second);
+                std::vector<beans::PropertyValue> aPropertyValues = 
JsonToPropertyValues(s.str().c_str());
+                
aSeqs.push_back(comphelper::containerToSequence(aPropertyValues));
+            }
+            aValue.Value <<= comphelper::containerToSequence(aSeqs);
+        }
         else
             SAL_WARN("comphelper", "JsonToPropertyValues: unhandled type '" << 
rType << "'");
         aArguments.push_back(aValue);
commit 429050f77e1e62a1befd1439d891df42eaccdccc
Author:     Miklos Vajna <[email protected]>
AuthorDate: Fri Nov 25 12:30:56 2022 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Tue Nov 29 08:07:50 2022 +0100

    sw lok, .uno:TextFormFields: expose field code of fieldmarks
    
    The fieldmarks in a document were kind of invisible for LOK clients
    previously.
    
    The Zotero use-case requires a way to fetch certain types of fieldmarks
    from a document, to be able to update them and then write back those
    updated versions to the document later.
    
    Fix this by introducing a new .uno:TextFormFields, you can get its value
    using the getCommandValues() LOK API. This allows filtering for a
    certain field command prefix, which is generic, but e.g. in the Zotero
    case allows getting the citations or the bibliography. The returned JSON
    is an array of matching fieldmarks, containing their type and field
    command. It seems there is no way to return the field result, till the
    motivation is to just update that field result. (If there will be need,
    the field result can be added.)
    
    Do this in a way that next time we add a Writer-specific command (to be
    able to return its values), there will be no need to touch
    include/vcl/ITiledRenderable.hxx, causing a large rebuild.
    
    (cherry picked from commit 24219cc1e9829f82a533667aef0f51b6a7df6fc2)
    
    Conflicts:
            sw/qa/uibase/uno/uno.cxx
    
    Change-Id: I2ef1159bec4034bbdd6b4ba00715a69423106edd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143408
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 93552811ab69..8f44e686417c 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -5725,6 +5725,7 @@ 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");
 
     if (!strcmp(pCommand, ".uno:LanguageStatus"))
     {
@@ -5901,6 +5902,19 @@ static char* 
doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
     {
         return getFontSubset(std::string_view(pCommand + 
aFontSubset.getLength()));
     }
+    else if (aCommand.startsWith(aTextFormFields))
+    {
+        ITiledRenderable* pDoc = getTiledRenderable(pThis);
+        if (!pDoc)
+        {
+            SetLastExceptionMsg("Document doesn't support tiled rendering");
+            return nullptr;
+        }
+
+        tools::JsonWriter aJsonWriter;
+        pDoc->getCommandValues(aJsonWriter, aCommand);
+        return aJsonWriter.extractData();
+    }
     else
     {
         SetLastExceptionMsg("Unknown command, no values returned");
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index 618c61060063..3ce1b9042908 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -369,6 +369,11 @@ public:
      *  Allow / disable drawing current text edit (used in Impress for slide 
previews)
      */
     virtual void setPaintTextEdit(bool) {}
+
+    /// Returns a json mapping of the possible values for the given command.
+    virtual void getCommandValues(tools::JsonWriter& /*rJsonWriter*/, const 
OString& /*rCommand*/)
+    {
+    }
 };
 } // namespace vcl
 
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 4e4d3f160eec..fabab635edda 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -458,6 +458,9 @@ public:
     /// @see vcl::ITiledRenderable::executeContentControlEvent().
     void executeContentControlEvent(const StringMap& aArguments) override;
 
+    /// @see vcl::ITiledRenderable::getCommandValues().
+    void getCommandValues(tools::JsonWriter& rJsonWriter, const OString& 
rCommand) override;
+
     void                        Invalidate();
     void                        Reactivate(SwDocShell* pNewDocShell);
     SwXDocumentPropertyHelper * GetPropertyHelper ();
diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx
index f4b337d8f9d2..18390d2c03f6 100644
--- a/sw/qa/uibase/uno/uno.cxx
+++ b/sw/qa/uibase/uno/uno.cxx
@@ -9,11 +9,16 @@
 
 #include <swmodeltestbase.hxx>
 
+#include <boost/property_tree/json_parser.hpp>
+
 #include <com/sun/star/frame/XModel2.hpp>
 #include <com/sun/star/text/XTextViewTextRangeSupplier.hpp>
 #include <com/sun/star/util/XCloseable.hpp>
 
 #include <vcl/scheduler.hxx>
+#include <tools/json_writer.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <xmloff/odffields.hxx>
 
 #include <docsh.hxx>
 #include <edtwin.hxx>
@@ -22,6 +27,7 @@
 #include <wrtsh.hxx>
 
 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/uno/data/";
+#include <unotxdoc.hxx>
 
 /// Covers sw/source/uibase/uno/ fixes.
 class SwUibaseUnoTest : public SwModelTestBase
@@ -90,6 +96,50 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, 
testCreateTextRangeByPixelPosition)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nActual);
 }
 
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetTextFormFields)
+{
+    // Given a document with 3 fieldmarks: 2 zotero items and a zotero
+    // bibliography:
+    createSwDoc();
+    for (int i = 0; i < 2; ++i)
+    {
+        uno::Sequence<css::beans::PropertyValue> aArgs = {
+            comphelper::makePropertyValue("FieldType", 
uno::Any(OUString(ODF_UNHANDLED))),
+            comphelper::makePropertyValue("FieldCommand",
+                                          uno::Any(OUString("ADDIN ZOTERO_ITEM 
foo bar"))),
+            comphelper::makePropertyValue("FieldResult", 
uno::Any(OUString("result"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+    {
+        uno::Sequence<css::beans::PropertyValue> aArgs = {
+            comphelper::makePropertyValue("FieldType", 
uno::Any(OUString(ODF_UNHANDLED))),
+            comphelper::makePropertyValue("FieldCommand",
+                                          uno::Any(OUString("ADDIN ZOTERO_BIBL 
foo bar"))),
+            comphelper::makePropertyValue("FieldResult",
+                                          
uno::Any(OUString("<p>aaa</p><p>bbb</p>"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+
+    // When getting the zotero items:
+    tools::JsonWriter aJsonWriter;
+    OString 
aCommand(".uno:TextFormFields?type=vnd.oasis.opendocument.field.UNHANDLED&"
+                     "commandPrefix=ADDIN%20ZOTERO_ITEM");
+    auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    pXTextDocument->getCommandValues(aJsonWriter, aCommand);
+
+    // Then make sure we find the 2 items and ignore the bibliography:
+    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 needed PixelToLogic() call 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_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 9ab446907fdf..a13bd7b2ab42 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -163,6 +163,7 @@
 #include <tools/json_writer.hxx>
 
 #include <svx/svdpage.hxx>
+#include <o3tl/string_view.hxx>
 
 #include <IDocumentOutlineNodes.hxx>
 #include <SearchResultLocator.hxx>
@@ -3509,6 +3510,100 @@ void SwXTextDocument::executeContentControlEvent(const 
StringMap& rArguments)
     }
 }
 
+namespace
+{
+/// Implements getCommandValues(".uno:TextFormFields").
+///
+/// Parameters:
+///
+/// - type: e.g. ODF_UNHANDLED
+/// - commandPrefix: field comment prefix not not return all fieldmarks
+void GetTextFormFields(tools::JsonWriter& rJsonWriter, SwDocShell* pDocShell,
+                       const std::map<OUString, OUString>& rArguments)
+{
+    OUString aType;
+    OUString aCommandPrefix;
+    {
+        auto it = rArguments.find("type");
+        if (it != rArguments.end())
+        {
+            aType = it->second;
+        }
+
+        it = rArguments.find("commandPrefix");
+        if (it != rArguments.end())
+        {
+            aCommandPrefix = it->second;
+        }
+    }
+
+    SwDoc* pDoc = pDocShell->GetDoc();
+    IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+    tools::ScopedJsonWriterArray aFields = rJsonWriter.startArray("fields");
+    for (auto it = pMarkAccess->getFieldmarksBegin(); it != 
pMarkAccess->getFieldmarksEnd(); ++it)
+    {
+        auto pFieldmark = dynamic_cast<sw::mark::IFieldmark*>(*it);
+        assert(pFieldmark);
+        if (pFieldmark->GetFieldname() != aType)
+        {
+            continue;
+        }
+
+        auto itParam = pFieldmark->GetParameters()->find(ODF_CODE_PARAM);
+        if (itParam == pFieldmark->GetParameters()->end())
+        {
+            continue;
+        }
+
+        OUString aCommand;
+        itParam->second >>= aCommand;
+        if (!aCommand.startsWith(aCommandPrefix))
+        {
+            continue;
+        }
+
+        tools::ScopedJsonWriterStruct aField = rJsonWriter.startStruct();
+        rJsonWriter.put("type", aType);
+        rJsonWriter.put("command", aCommand);
+    }
+}
+}
+
+void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, const 
OString& rCommand)
+{
+    std::map<OUString, OUString> aMap;
+
+    static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields");
+
+    if (o3tl::starts_with(rCommand, aTextFormFields))
+    {
+        if (rCommand.getLength() > aTextFormFields.getLength())
+        {
+            OString aArguments = rCommand.copy(aTextFormFields.getLength() + 
1);
+            sal_Int32 nParamIndex = 0;
+            do
+            {
+                OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
+                sal_Int32 nIndex = 0;
+                OUString aKey;
+                OUString aValue;
+                do
+                {
+                    OString aToken = aParamToken.getToken(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);
+    }
+}
+
 int SwXTextDocument::getPart()
 {
     SolarMutexGuard aGuard;

Reply via email to