framework/source/uiconfiguration/uiconfigurationmanager.cxx |   33 ++++++++++--
 sw/qa/extras/odfexport/odfexport.cxx                        |   32 +++++++++++
 2 files changed, 60 insertions(+), 5 deletions(-)

New commits:
commit 5c74b979eac620dc1a435fcdf9e88488fd4217d9
Author:     David Hashe <[email protected]>
AuthorDate: Fri Apr 25 20:30:57 2025 -0400
Commit:     Miklos Vajna <[email protected]>
CommitDate: Wed May 21 09:20:33 2025 +0200

    tdf#60700 stop creating empty UIConfiguration subdirs in ODFs
    
    This change stops the creation of various subdirectories of the
    Configurations2/ directory on new documents that do not have anything
    that needs to be stored in those subdirectories. Previously, LibreOffice
    would always create a bunch of empty subdirectories that were not
    necessary.
    
    Whenever an XStorage is opened readwrite on a path, it will immediately
    create that path. LibreOffice was opening all of these paths readwrite.
    So, the solution is to always open these paths as readonly, and then
    when we need to write data to them we close them and re-open them as
    readwrite. If we never need to write data to a path, then it won't be
    created.
    
    This patch specifically stops the creation of subdirectories that were
    being created directly by UIConfigurationManager. There are other paths
    that were being created by ImageManager and
    DocumentAcceleratorConfiguration, and those paths will be handled by
    other patches.
    
    Test coverage was added in aaed16eef8b2d32ac02c8cb3a42ca25246cb0d33
    
    $ CPPUNIT_TEST_NAME="Test testTdf69500" make CppunitTest_sw_odfexport
    
    I also manually tested with the following procedure:
    
    1. Open a new document in writer.
    2. Tools > Customize > Toolbars.
    3. Set scope to "Untitled 1".
    4. Add a new toolbar "New Toolbar 1".
    5. Add any command from Available Commands to the new toolbar.
    6. OK.
    7. Visually verify that the toolbar is present.
    8. Save the document.
    9. Verify that the toolbar is saved correctly inside the file.
    
    $ unzip -l Untitled\ 1.odt
    Archive:  Untitled 1.odt
      Length      Date    Time    Name
    ---------  ---------- -----   ----
           39  2025-04-25 23:35   mimetype
          353  2025-04-25 23:35   
Configurations2/toolbar/custom_toolbar_2a773c3.xml
            0  2025-04-25 23:35   Configurations2/accelerator/
            0  2025-04-25 23:35   Configurations2/images/Bitmaps/
        12645  2025-04-25 23:35   styles.xml
          899  2025-04-25 23:35   manifest.rdf
         3563  2025-04-25 23:35   content.xml
         1012  2025-04-25 23:35   meta.xml
        14748  2025-04-25 23:35   settings.xml
          318  2025-04-25 23:35   Thumbnails/thumbnail.png
         1180  2025-04-25 23:35   META-INF/manifest.xml
    ---------                     -------
        34757                     11 files
    
    10. Close and reopen the document.
    11. Visually verify that the toolbar is present.
    
    Change-Id: If0a93675204576b3b6f7fe9113cada398e4da2ea
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184655
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/framework/source/uiconfiguration/uiconfigurationmanager.cxx 
b/framework/source/uiconfiguration/uiconfigurationmanager.cxx
index 29be73cc0a72..7a2af67a45b0 100644
--- a/framework/source/uiconfiguration/uiconfigurationmanager.cxx
+++ b/framework/source/uiconfiguration/uiconfigurationmanager.cxx
@@ -163,10 +163,12 @@ private:
     {
         UIElementType() : bModified( false ),
                           bLoaded( false ),
+                          bShouldReloadRWOnStore( false ),
                           nElementType( css::ui::UIElementType::UNKNOWN ) {}
 
         bool                                                              
bModified;
         bool                                                              
bLoaded;
+        bool                                                              
bShouldReloadRWOnStore;
         sal_Int16                                                         
nElementType;
         UIElementDataHashMap                                              
aElementsHashMap;
         css::uno::Reference< css::embed::XStorage > xStorage;
@@ -631,8 +633,6 @@ void UIConfigurationManager::impl_Initialize()
     // Initialize the top-level structures with the storage data
     if ( m_xDocConfigStorage.is() )
     {
-        tools::Long nModes = m_bReadOnly ? ElementModes::READ : 
ElementModes::READWRITE;
-
         // Try to access our module sub folder
         for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
               i++ )
@@ -640,7 +640,7 @@ void UIConfigurationManager::impl_Initialize()
             Reference< XStorage > xElementTypeStorage;
             try
             {
-                xElementTypeStorage = m_xDocConfigStorage->openStorageElement( 
OUString(UIELEMENTTYPENAMES[i]), nModes );
+                xElementTypeStorage = m_xDocConfigStorage->openStorageElement( 
OUString(UIELEMENTTYPENAMES[i]), ElementModes::READ );
             }
             catch ( const css::container::NoSuchElementException& )
             {
@@ -660,6 +660,7 @@ void UIConfigurationManager::impl_Initialize()
 
             m_aUIElements[i].nElementType = i;
             m_aUIElements[i].bModified = false;
+            m_aUIElements[i].bShouldReloadRWOnStore = !m_bReadOnly;
             m_aUIElements[i].xStorage = std::move(xElementTypeStorage);
         }
     }
@@ -1286,8 +1287,30 @@ void SAL_CALL UIConfigurationManager::store()
         {
             UIElementType& rElementType = m_aUIElements[i];
 
-            if ( rElementType.bModified && rElementType.xStorage.is() )
-                impl_storeElementTypeData( rElementType.xStorage, rElementType 
);
+            if ( rElementType.bModified )
+            {
+                if ( rElementType.bShouldReloadRWOnStore )
+                {
+                    rElementType.bShouldReloadRWOnStore = false;
+                    rElementType.xStorage.clear();
+                    try
+                    {
+                        rElementType.xStorage = 
m_xDocConfigStorage->openStorageElement( OUString(UIELEMENTTYPENAMES[i]),
+                                                                               
          ElementModes::READWRITE );
+                    }
+                    catch ( const Exception& )
+                    {
+                        rElementType.xStorage = 
m_xDocConfigStorage->openStorageElement( OUString(UIELEMENTTYPENAMES[i]),
+                                                                               
         ElementModes::READ );
+                        throw;
+                    }
+                }
+                if ( rElementType.xStorage.is() )
+                {
+                    impl_storeElementTypeData( rElementType.xStorage, 
rElementType );
+                }
+
+            }
         }
         catch ( const Exception& )
         {
diff --git a/sw/qa/extras/odfexport/odfexport.cxx 
b/sw/qa/extras/odfexport/odfexport.cxx
index 058d8adac6ba..ca4efa7076be 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -1527,6 +1527,38 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf60700_accelerators)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf60700_directories)
+{
+    createSwDoc();
+    save(mpFilter);
+
+    // There is no way to read the contents of Configurations2/ from a loaded 
document,
+    // because only UIConfigurationManager has a reference to it and there is 
no method
+    // to access that reference from outside the class. So, we inspect the 
zipfile
+    // directly.
+
+    uno::Sequence<uno::Any> aArgs{ uno::Any(maTempFile.GetURL()) };
+    uno::Reference<container::XNameAccess> xNameAccess(
+        
m_xSFactory->createInstanceWithArguments(u"com.sun.star.packages.zip.ZipFileAccess"_ustr,
+                                                 aArgs),
+        uno::UNO_QUERY);
+    const css::uno::Sequence<OUString> aNames(xNameAccess->getElementNames());
+
+    int nMatches = 0;
+    for (const OUString& sName : aNames)
+    {
+        OUString sRest;
+        if (sName.startsWith("Configurations2/", &sRest) && !sRest.isEmpty())
+        {
+            ++nMatches;
+        }
+    }
+
+    // There should be two elements ("accelerator", "images") within 
Configurations2/ on a fresh document.
+    CPPUNIT_ASSERT_EQUAL(2, nMatches);
+}
+
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to