put calc external data mappings under link update control

register each mapping as a LinkManager link, so its refresh goes through
the same update control as sheet links and area links. Wait until after
the infobar-controlled update mode is known to update.

Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: I6710d4bdf3de6e002365d75c911b69076a054da8
Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/7058
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit b72d760465c857eb5dd10cdc45b107c49bb0caec)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208319
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Caolán McNamara
2026-07-21 08:59:06 +02:00
committed by Xisco Fauli
parent a435d56867
commit 4aba7e41c1
15 changed files with 147 additions and 16 deletions
+1 -1
View File
@@ -94,7 +94,7 @@ public:
class SC_DLLPUBLIC ExternalDataMapper
{
//ScDocument& mrDoc;
ScDocument& mrDoc;
std::vector<ExternalDataSource> maDataSources;
public:
+4
View File
@@ -954,6 +954,10 @@ public:
SCROW nRow2, SCTAB nTab) const;
void RefreshDirtyTableColumnNames();
SC_DLLPUBLIC sc::ExternalDataMapper& GetExternalDataMapper();
/** True when the document holds at least one external data mapping. Checks
without creating the mapper, so it stays cheap for documents that have
none. */
SC_DLLPUBLIC bool HasDataProviderMappings() const;
SC_DLLPUBLIC const ScRangeData* GetRangeAtBlock( const ScRange& rBlock, OUString& rName,
bool* pSheetLocal = nullptr ) const;
+2
View File
@@ -150,6 +150,8 @@ class DataProvider(UITestCase):
xFileName.executeAction('TYPE', mkPropertyValues({'TEXT': xFilePath}))
with self.ui_test.load_file(systemPathToFileUrl(xFilePath)) as doc2:
xAllow = self.ui_test.wait_until_child_is_available("allowupdating")
xAllow.executeAction("CLICK", tuple())
# Without the fix in place, this test would have failed with
# AssertionError: 'Rank' != 'Rank|Males|Females\r'
self.assertEqual("Rank", get_cell_by_position(doc2, 0, 0, 0).getString())
@@ -0,0 +1 @@
changed
1 changed
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
<office:body>
<office:spreadsheet>
<table:table table:name="Sheet1">
<table:table-column/>
<table:table-row>
<table:table-cell office:value-type="string"><text:p>unchanged</text:p></table:table-cell>
</table:table-row>
</table:table>
<table:database-ranges>
<table:database-range table:name="myImport" table:target-range-address="Sheet1.A1:Sheet1.A1"/>
</table:database-ranges>
<calcext:data-mappings>
<calcext:data-mapping xlink:href="dummy.csv" calcext:provider="org.libreoffice.calc.csv" calcext:frequency="0" calcext:id="" calcext:database-name="myImport"/>
</calcext:data-mappings>
</office:spreadsheet>
</office:body>
</office:document>
+40
View File
@@ -10,6 +10,7 @@
#include "helper/qahelper.hxx"
#include <document.hxx>
#include <docsh.hxx>
#include <address.hxx>
#include <dbdata.hxx>
#include <datamapper.hxx>
@@ -17,6 +18,8 @@
#include <orcusxml.hxx>
#include <sortparam.hxx>
#include <datatransformation.hxx>
#include <comphelper/embeddedobjectcontainer.hxx>
#include <sfx2/linkmgr.hxx>
#include <memory>
@@ -34,6 +37,7 @@ public:
void testBaseImport();
void testTdf169541_TwoDataMapping();
void testTdf169610_SortTransform();
void testLinkUpdateGate();
CPPUNIT_TEST_SUITE(ScDataProvidersTest);
CPPUNIT_TEST(testCSVImport);
@@ -43,6 +47,7 @@ public:
CPPUNIT_TEST(testBaseImport);
CPPUNIT_TEST(testTdf169541_TwoDataMapping);
CPPUNIT_TEST(testTdf169610_SortTransform);
CPPUNIT_TEST(testLinkUpdateGate);
CPPUNIT_TEST_SUITE_END();
};
@@ -305,6 +310,41 @@ void ScDataProvidersTest::testTdf169610_SortTransform()
CPPUNIT_ASSERT_EQUAL(14.0, pDoc->GetValue(5, 11, 0));
}
void ScDataProvidersTest::testLinkUpdateGate()
{
// A data mapping loaded from a document is an external link. Its data must
// not refresh from the source file until the user allows link updating,
// the same gate that governs sheet links and area links.
createScDoc("mappinggate.fods");
ScDocument* pDoc = getScDoc();
CPPUNIT_ASSERT(pDoc);
ScDocShell* pDocShell = getScDocShell();
CPPUNIT_ASSERT(pDocShell);
// Loading the mapping registered it as a link in the LinkManager.
auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
CPPUNIT_ASSERT_EQUAL(size_t(1), rDataSources.size());
sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
CPPUNIT_ASSERT(pLinkManager);
CPPUNIT_ASSERT_EQUAL(size_t(1), pLinkManager->GetLinks().size());
// The saved document holds "unchanged" in A1; the source file holds
// "changed". The href stored in the document is not portable, so point the
// source at the real test file before asking for an update.
rDataSources[0].setURL(createFileURL(u"csv/mappinggate.csv"));
CPPUNIT_ASSERT_EQUAL(u"unchanged"_ustr, pDoc->GetString(0, 0, 0));
// With updating not allowed, updating the links leaves the saved value.
pDocShell->GetEmbeddedObjectContainer().setUserAllowsLinkUpdate(false);
pLinkManager->UpdateAllLinks(false, nullptr, OUString());
CPPUNIT_ASSERT_EQUAL(u"unchanged"_ustr, pDoc->GetString(0, 0, 0));
// Once the user allows updating, the same update brings in the live value.
pDocShell->GetEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
pLinkManager->UpdateAllLinks(false, nullptr, OUString());
CPPUNIT_ASSERT_EQUAL(u"changed"_ustr, pDoc->GetString(0, 0, 0));
}
ScDataProvidersTest::ScDataProvidersTest()
: ScModelTestBase(u"sc/qa/unit/data/dataprovider"_ustr)
{
+5
View File
@@ -1042,6 +1042,11 @@ sc::ExternalDataMapper& ScDocument::GetExternalDataMapper()
return *mpDataMapper;
}
bool ScDocument::HasDataProviderMappings() const
{
return mpDataMapper && !mpDataMapper->getDataSources().empty();
}
void ScDocument::StoreTabToCache(SCTAB nTab, SvStream& rStrm) const
{
const ScTable* pTab = FetchTable(nTab);
+6
View File
@@ -17,6 +17,7 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <comphelper/embeddedobjectcontainer.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/string.hxx>
#include <osl/thread.h>
@@ -29,6 +30,7 @@
#include <ddelink.hxx>
#include <brdcst.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <scmatrix.hxx>
#include <patattr.hxx>
@@ -123,6 +125,10 @@ void ScDdeLink::Store( SvStream& rStream, ScMultipleWriteHeader& rHdr ) const
sfx2::SvBaseLink::UpdateResult ScDdeLink::DataChanged(
const OUString& rMimeType, const css::uno::Any & rValue )
{
if (rDoc.GetDocumentShell()
&& !rDoc.GetDocumentShell()->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate())
return SUCCESS;
// we only master strings...
if ( SotClipboardFormatId::STRING != SotExchange::GetFormatIdFromMimeType( rMimeType ))
return SUCCESS;
+6
View File
@@ -7,6 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <comphelper/embeddedobjectcontainer.hxx>
#include <comphelper/processfactory.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/bindings.hxx>
@@ -21,6 +22,7 @@
#include <utility>
#include <webservicelink.hxx>
#include <brdcst.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <sc.hrc>
@@ -36,6 +38,10 @@ ScWebServiceLink::~ScWebServiceLink() {}
sfx2::SvBaseLink::UpdateResult ScWebServiceLink::DataChanged(const OUString&, const css::uno::Any&)
{
if (pDoc && pDoc->GetDocumentShell()
&& !pDoc->GetDocumentShell()->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate())
return SUCCESS;
aResult.clear();
bHasResult = false;
-10
View File
@@ -122,16 +122,6 @@ ScXMLMappingContext::ScXMLMappingContext( ScXMLImport& rImport,
}
}
ScXMLMappingContext::~ScXMLMappingContext()
{
ScDocument* pDoc = GetScImport().GetDocument();
auto& rDataMapper = pDoc->GetExternalDataMapper();
auto& rDataSources = rDataMapper.getDataSources();
if(!rDataSources.empty())
rDataSources.back().refresh(pDoc, true);
}
uno::Reference<xml::sax::XFastContextHandler>
SAL_CALL ScXMLMappingContext::createFastChildContext(
sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
-2
View File
@@ -34,8 +34,6 @@ public:
ScXMLMappingContext( ScXMLImport& rImport,
const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
virtual ~ScXMLMappingContext() override;
virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
sal_Int32 nElement,
const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+57 -2
View File
@@ -26,6 +26,10 @@
#include <datamapper.hxx>
#include <dbdata.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <comphelper/embeddedobjectcontainer.hxx>
#include <sfx2/lnkbase.hxx>
#include <sfx2/linkmgr.hxx>
#include <utility>
using namespace com::sun::star;
@@ -194,8 +198,46 @@ const std::vector<std::shared_ptr<sc::DataTransformation>>& ExternalDataSource::
return maDataTransformations;
}
ExternalDataMapper::ExternalDataMapper(ScDocument& /*rDoc*/)
//mrDoc(rDoc)
namespace {
// A data mapping fetches external data (CSV, HTML, XML) into a sheet. It is
// registered in the LinkManager so it passes through the same link update
// control as sheet links, area links, and external references. The link holds
// the index of its data source in the mapper. That index is stable because
// data sources are only ever appended, never removed.
class ScDataProviderLink final : public sfx2::SvBaseLink
{
ScDocShell& mrDocShell;
size_t mnSourceIndex;
public:
ScDataProviderLink(ScDocShell& rDocShell, size_t nSourceIndex)
: sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE)
, mrDocShell(rDocShell)
, mnSourceIndex(nSourceIndex)
{
}
virtual sfx2::SvBaseLink::UpdateResult DataChanged(const OUString&,
const css::uno::Any&) override
{
if (!mrDocShell.GetEmbeddedObjectContainer().getUserAllowsLinkUpdate())
return SUCCESS;
ScDocument& rDoc = mrDocShell.GetDocument();
std::vector<sc::ExternalDataSource>& rSources
= rDoc.GetExternalDataMapper().getDataSources();
if (mnSourceIndex < rSources.size())
rSources[mnSourceIndex].refresh(&rDoc, true);
return SUCCESS;
}
};
}
ExternalDataMapper::ExternalDataMapper(ScDocument& rDoc)
: mrDoc(rDoc)
{
}
@@ -206,6 +248,19 @@ ExternalDataMapper::~ExternalDataMapper()
void ExternalDataMapper::insertDataSource(const sc::ExternalDataSource& rSource)
{
maDataSources.push_back(rSource);
ScDocShell* pDocShell = mrDoc.GetDocumentShell();
if (!pDocShell)
return;
sfx2::LinkManager* pLinkManager = mrDoc.GetLinkManager();
if (!pLinkManager)
return;
ScDataProviderLink* pLink = new ScDataProviderLink(*pDocShell, maDataSources.size() - 1);
const OUString& rProvider = rSource.getProvider();
pLinkManager->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile,
rSource.getURL(), &rProvider, nullptr);
}
const std::vector<sc::ExternalDataSource>& ExternalDataMapper::getDataSources() const
+4
View File
@@ -36,6 +36,7 @@
#include <tools/urlobj.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <comphelper/configuration.hxx>
#include <comphelper/embeddedobjectcontainer.hxx>
#include <comphelper/processfactory.hxx>
#include <tablink.hxx>
@@ -140,6 +141,9 @@ bool ScTableLink::Refresh(const OUString& rNewFile, const OUString& rNewFilter,
if (rNewFile.isEmpty() || rNewFilter.isEmpty())
return false;
if (!m_rDocSh.GetEmbeddedObjectContainer().getUserAllowsLinkUpdate())
return false;
OUString aNewUrl = ScGlobal::GetAbsDocName(rNewFile, &m_rDocSh);
bool bNewUrlName = aFileName != aNewUrl;
+1 -1
View File
@@ -1695,7 +1695,7 @@ void ScTabViewShell::Construct( TriState nForceDesignMode )
const sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
const ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks()
|| rMgr.hasExternalLinks()
|| rDoc.HasDataProviderMappings() || rMgr.hasExternalLinks()
|| (pDrawLayer && hasDeferredFillBitmapLinks(pDrawLayer->GetItemPool()))
|| !pDocSh->GetDeferredFormControlImages().empty())
bLink = true;
+1
View File
@@ -1756,6 +1756,7 @@ void SfxObjectShell::ShowLinkUpdateInfobar()
rHelpBtn.connect_clicked(LINK(nullptr, LinkUpdateHelp, DispatchHelpLinksHdl));
weld::Button& rBtn = pInfoBar->addButton();
rBtn.set_buildable_name(u"allowupdating"_ustr);
rBtn.set_label(SfxResId(STR_INFOBAR_ALLOW_UPDATING));
rBtn.set_tooltip_text(SfxResId(STR_INFOBAR_ALLOW_UPDATING_TOOLTIP));
rBtn.connect_clicked(LINK(this, SfxObjectShell, AllowLinksUpdateHdl));