From 35c5d496645e64463ff4ba1e7b0b78f6b55741d9 Mon Sep 17 00:00:00 2001 From: Regina Henschel Date: Wed, 18 Sep 2024 10:43:32 +0200 Subject: tdf#162963 export property TotalsRow to ODF DatabaseRange has got the property TotalsRow with UI and UNO. But this property was not written to ODF. The patch adds this as attribute calcext:contains-footer to element. Related is issue OFFICE-4169 at OASIS. Change-Id: I99a51198585b9b7dfb840217bdf1312f2462c9bc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173612 Tested-by: Jenkins Reviewed-by: Regina Henschel --- sc/CppunitTest_sc_subsequent_filters_test5.mk | 12 + sc/Module_sc.mk | 1 + sc/inc/queryparam.hxx | 2 + sc/qa/unit/data/fods/tdf162963_DatabaseRange.fods | 959 +++++++++++++++++++++ .../xlsx/tdf162963_TableWithTotalsEnabled.xlsx | Bin 0 -> 10394 bytes sc/qa/unit/subsequent_filters_test5.cxx | 119 +++ sc/source/core/tool/dbdata.cxx | 2 +- sc/source/core/tool/queryparam.cxx | 11 +- sc/source/filter/xml/XMLExportDatabaseRanges.cxx | 3 + sc/source/filter/xml/xmldrani.cxx | 10 +- sc/source/filter/xml/xmldrani.hxx | 1 + sc/source/ui/unoobj/datauno.cxx | 1 + 12 files changed, 1115 insertions(+), 6 deletions(-) create mode 100644 sc/CppunitTest_sc_subsequent_filters_test5.mk create mode 100644 sc/qa/unit/data/fods/tdf162963_DatabaseRange.fods create mode 100644 sc/qa/unit/data/xlsx/tdf162963_TableWithTotalsEnabled.xlsx create mode 100644 sc/qa/unit/subsequent_filters_test5.cxx (limited to 'sc') diff --git a/sc/CppunitTest_sc_subsequent_filters_test5.mk b/sc/CppunitTest_sc_subsequent_filters_test5.mk new file mode 100644 index 000000000000..2bf8e2e5f998 --- /dev/null +++ b/sc/CppunitTest_sc_subsequent_filters_test5.mk @@ -0,0 +1,12 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call sc_subsequent_test,filters_test5)) + +# vim: set noet sw=4 ts=4: diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk index ab9dada68aa7..ee4f23eeab0a 100644 --- a/sc/Module_sc.mk +++ b/sc/Module_sc.mk @@ -92,6 +92,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sc, \ CppunitTest_sc_subsequent_filters_test2 \ CppunitTest_sc_subsequent_filters_test3 \ CppunitTest_sc_subsequent_filters_test4 \ + CppunitTest_sc_subsequent_filters_test5 \ CppunitTest_sc_subsequent_export_test \ CppunitTest_sc_subsequent_export_test2 \ CppunitTest_sc_subsequent_export_test3 \ diff --git a/sc/inc/queryparam.hxx b/sc/inc/queryparam.hxx index dc38638075af..5b27bce3bf77 100644 --- a/sc/inc/queryparam.hxx +++ b/sc/inc/queryparam.hxx @@ -42,6 +42,7 @@ struct SAL_DLLPUBLIC_RTTI ScQueryParamBase { utl::SearchParam::SearchType eSearchType; bool bHasHeader; + bool bHasTotals; bool bByRow; bool bInplace; bool bCaseSens; @@ -88,6 +89,7 @@ inline std::basic_ostream & operator <<(std::basic_ostream + + + + LODev_daily_installed/25.2.0.0.alpha0$Windows_X86_64 LibreOffice_project/976567aee323afd09629b6adf13537908f43d2a8 + + 5mm Grid + Regina Henschel + 2024-07-29T22:39:46 + Regina Henschel + 2024-09-18T20:23:43 + 16.0300 + + + + + 0 + 0 + 4198 + 8731 + + + view1 + + + 3 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 100 + 60 + true + false + false + false + false + + + Vertical + 1666 + 0 + 100 + 60 + false + true + true + true + false + true + -1 + 1 + true + true + true + false + false + false + 500 + 500 + 4 + 4 + true + false + false + false + false + + + + + true + true + true + true + false + true + -1 + false + true + 3 + true + true + true + true + false + false + 500 + 500 + 4 + 4 + true + true + true + 0 + false + true + 0 + false + true + true + false + false + false + false + true + 2 + + + Sheet1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + . + + + + + + + . + + + + + + + + + + : + + + + + + + : + + : + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + + - € + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + : + + + + + : + + : + + + + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ??? + + + + + Page 1 + + + + + + + + ???(???) + + + 00.00.0000, 00:00:00 + + + + + + Page 1/ 99 + + + + + + + ??? + + + + + Page 1 + + + + + + + + + + + + + + + + + + + + Name + + + Sales + + + + + + Alice + + + 21 + + + + + + Bärbel + + + 23 + + + + + + Barbie + + + 9 + + + + + + Inés + + + 34 + + + + + + Inge + + + 23 + + + + + + John + + + 12 + + + + + + Judy + + + 15 + + + + + + Mary + + + 17 + + + + + + Tom + + + 31 + + + + + + + 185 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sc/qa/unit/data/xlsx/tdf162963_TableWithTotalsEnabled.xlsx b/sc/qa/unit/data/xlsx/tdf162963_TableWithTotalsEnabled.xlsx new file mode 100644 index 000000000000..7411f05e8dea Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf162963_TableWithTotalsEnabled.xlsx differ diff --git a/sc/qa/unit/subsequent_filters_test5.cxx b/sc/qa/unit/subsequent_filters_test5.cxx new file mode 100644 index 000000000000..f22b2ff82c83 --- /dev/null +++ b/sc/qa/unit/subsequent_filters_test5.cxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// core, please keep it alphabetically ordered +#include +#include "helper/qahelper.hxx" +#include +#include +#include + +// API, please keep it alphabetically ordered +#include +#include +#include +#include + +using namespace css; +using namespace css::uno; + +/* Implementation of Filters test, volume 5*/ + +class ScFiltersTest5 : public ScModelTestBase +{ +public: + ScFiltersTest5() + : ScModelTestBase(u"sc/qa/unit/data"_ustr) + { + } +}; + +CPPUNIT_TEST_FIXTURE(ScFiltersTest5, testTdf162963) +{ + //tests xlsx -> ods -> ods of property "TotalsRow" + createScDoc("xlsx/tdf162963_TableWithTotalsEnabled.xlsx"); + + constexpr OUString sDBName(u"myData"_ustr); + constexpr OUString sPropName(u"TotalsRow"_ustr); + + // Make sure the database range "myData" has TotalsRow TRUE after import from xlsx. + { + uno::Reference xDoc(mxComponent, UNO_QUERY_THROW); + uno::Reference xDocPropSet(xDoc, UNO_QUERY_THROW); + uno::Reference xNameAccess( + xDocPropSet->getPropertyValue(u"DatabaseRanges"_ustr), UNO_QUERY_THROW); + uno::Reference xDBRangePropSet(xNameAccess->getByName(sDBName), + UNO_QUERY_THROW); + bool bTotalsRow = false; + xDBRangePropSet->getPropertyValue(sPropName) >>= bTotalsRow; + CPPUNIT_ASSERT_MESSAGE("xlsx-import", bTotalsRow); + } + // Make sure TotalsRow is still TRUE after save to ods and reload. + // The error was, that the property "TotalsRow" was not written to ods at all. + // With fix it is written as calcext:contains-footer. + { + saveAndReload(u"calc8"_ustr); + uno::Reference xDoc(mxComponent, UNO_QUERY_THROW); + uno::Reference xDocPropSet(xDoc, UNO_QUERY_THROW); + uno::Reference xNameAccess( + xDocPropSet->getPropertyValue(u"DatabaseRanges"_ustr), UNO_QUERY_THROW); + uno::Reference xDBRangePropSet(xNameAccess->getByName(sDBName), + UNO_QUERY_THROW); + bool bTotalsRow = true; + xDBRangePropSet->getPropertyValue(sPropName) >>= bTotalsRow; + CPPUNIT_ASSERT_MESSAGE("save ods, reload", bTotalsRow); + } +} + +CPPUNIT_TEST_FIXTURE(ScFiltersTest5, testTdf162963_ODF) +{ + // Verify, that calcext:contains-footer is only written in extended file format versions. + // The parameter in DefaultVersion::set need to be adapted, when attribute contains-footer + // is included in ODF strict, see issue OFFICE-4169 at OASIS. + createScDoc("fods/tdf162963_DatabaseRange.fods"); + + // enable TotalsRow + uno::Reference xDoc(mxComponent, UNO_QUERY_THROW); + uno::Reference xDocPropSet(xDoc, UNO_QUERY_THROW); + uno::Reference xNameAccess( + xDocPropSet->getPropertyValue(u"DatabaseRanges"_ustr), UNO_QUERY_THROW); + uno::Reference xDBRangePropSet(xNameAccess->getByName(u"myData"_ustr), + UNO_QUERY_THROW); + xDBRangePropSet->setPropertyValue(u"TotalsRow"_ustr, uno::Any(true)); + + // Backup original ODF default version + const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion(GetODFDefaultVersion()); + + // Save to ODF 1.3 strict. Make sure attribute is not written. + // Adapt to ODF 1.4 strict, when it is available. + SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_013); + save(u"calc8"_ustr); // this saves to .ods not to .fods + xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:spreadsheet/" + "table:database-ranges/table:database-range/contains-footer"_ostr, + 0); + + // Save to ODF_LATEST which is currently (Sep 2024) ODF 1.3 extended. + // Adapt to a concrete version when attribute contains-footer is availabe in ODF strict. + // Make sure attribute is written in calcext namespace + SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_LATEST); + save(u"calc8"_ustr); + pXmlDoc = parseExport(u"content.xml"_ustr); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:spreadsheet/" + "table:database-ranges/table:database-range[@calcext:contains-footer='true']"_ostr); + + // Set back to original ODF default version. + SetODFDefaultVersion(nCurrentODFVersion); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx index 823849554693..24009dfbe4e2 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -423,7 +423,7 @@ void ScDBData::GetQueryParam( ScQueryParam& rQueryParam ) const rQueryParam.nTab = nTable; rQueryParam.bByRow = bByRow; rQueryParam.bHasHeader = bHasHeader; - /* TODO: add Totals to ScQueryParam? */ + rQueryParam.bHasTotals = bHasTotals; } void ScDBData::SetQueryParam(const ScQueryParam& rQueryParam) diff --git a/sc/source/core/tool/queryparam.cxx b/sc/source/core/tool/queryparam.cxx index f270488c7e72..aafe0d891951 100644 --- a/sc/source/core/tool/queryparam.cxx +++ b/sc/source/core/tool/queryparam.cxx @@ -68,6 +68,7 @@ ScQueryParamBase::const_iterator ScQueryParamBase::end() const ScQueryParamBase::ScQueryParamBase() : eSearchType(utl::SearchParam::SearchType::Normal), bHasHeader(true), + bHasTotals(false), bByRow(true), bInplace(true), bCaseSens(false), @@ -78,9 +79,9 @@ ScQueryParamBase::ScQueryParamBase() : } ScQueryParamBase::ScQueryParamBase(const ScQueryParamBase& r) : - eSearchType(r.eSearchType), bHasHeader(r.bHasHeader), bByRow(r.bByRow), bInplace(r.bInplace), - bCaseSens(r.bCaseSens), bDuplicate(r.bDuplicate), mbRangeLookup(r.mbRangeLookup), - m_Entries(r.m_Entries) + eSearchType(r.eSearchType), bHasHeader(r.bHasHeader), bHasTotals(r.bHasTotals), bByRow(r.bByRow), + bInplace(r.bInplace), bCaseSens(r.bCaseSens), bDuplicate(r.bDuplicate), + mbRangeLookup(r.mbRangeLookup), m_Entries(r.m_Entries) { } @@ -90,6 +91,7 @@ ScQueryParamBase& ScQueryParamBase::operator=(const ScQueryParamBase& r) { eSearchType = r.eSearchType; bHasHeader = r.bHasHeader; + bHasTotals = r.bHasTotals; bByRow = r.bByRow; bInplace = r.bInplace; bCaseSens = r.bCaseSens; @@ -337,7 +339,7 @@ void ScQueryParam::Clear() nRow1=nRow2 = 0; nTab = SCTAB_MAX; eSearchType = utl::SearchParam::SearchType::Normal; - bHasHeader = bCaseSens = false; + bHasHeader = bHasTotals = bCaseSens = false; bInplace = bByRow = bDuplicate = true; for (auto & itr : m_Entries) @@ -379,6 +381,7 @@ bool ScQueryParam::operator==( const ScQueryParam& rOther ) const && (nRow2 == rOther.nRow2) && (nTab == rOther.nTab) && (bHasHeader == rOther.bHasHeader) + && (bHasTotals == rOther.bHasTotals) && (bByRow == rOther.bByRow) && (bInplace == rOther.bInplace) && (bCaseSens == rOther.bCaseSens) diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx index 89ebcc27373c..9328b796a81b 100644 --- a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx +++ b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx @@ -246,6 +246,9 @@ private: rData.GetQueryParam(aQueryParam); if (!aQueryParam.bHasHeader) mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONTAINS_HEADER, XML_FALSE); + if (mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + if (aQueryParam.bHasTotals) + mrExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CONTAINS_FOOTER, XML_TRUE); ScSortParam aSortParam; rData.GetSortParam(aSortParam); diff --git a/sc/source/filter/xml/xmldrani.cxx b/sc/source/filter/xml/xmldrani.cxx index cf6cd7d3e651..0e6c400c30bb 100644 --- a/sc/source/filter/xml/xmldrani.cxx +++ b/sc/source/filter/xml/xmldrani.cxx @@ -104,6 +104,7 @@ ScXMLDatabaseRangeContext::ScXMLDatabaseRangeContext( ScXMLImport& rImport, bSubTotalsAscending(true), bFilterConditionSourceRange(false), bHasHeader(true), + bHasFooter(false), bByRow(true), meRangeType(ScDBCollection::GlobalNamed) { @@ -150,6 +151,13 @@ ScXMLDatabaseRangeContext::ScXMLDatabaseRangeContext( ScXMLImport& rImport, mpQueryParam->bHasHeader = bHasHeader; } break; + case XML_ELEMENT( TABLE, XML_CONTAINS_FOOTER ): + case XML_ELEMENT( CALC_EXT, XML_CONTAINS_FOOTER ): + { + bHasFooter = IsXMLToken( aIter, XML_TRUE ); + mpQueryParam->bHasTotals = bHasFooter; + } + break; case XML_ELEMENT( TABLE, XML_DISPLAY_FILTER_BUTTONS ): { bAutoFilter = IsXMLToken( aIter, XML_TRUE ); @@ -247,7 +255,7 @@ std::unique_ptr ScXMLDatabaseRangeContext::ConvertToDBData(const OUStr ScDocument* pDoc = GetScImport().GetDocument(); ::std::unique_ptr pData( - new ScDBData(rName, maRange.aStart.Tab(), maRange.aStart.Col(), maRange.aStart.Row(), maRange.aEnd.Col(), maRange.aEnd.Row(), bByRow, bHasHeader)); + new ScDBData(rName, maRange.aStart.Tab(), maRange.aStart.Col(), maRange.aStart.Row(), maRange.aEnd.Col(), maRange.aEnd.Row(), bByRow, bHasHeader, bHasFooter)); pData->SetAutoFilter(bAutoFilter); pData->SetKeepFmt(bKeepFormats); diff --git a/sc/source/filter/xml/xmldrani.hxx b/sc/source/filter/xml/xmldrani.hxx index a35074117c28..037d5710d52c 100644 --- a/sc/source/filter/xml/xmldrani.hxx +++ b/sc/source/filter/xml/xmldrani.hxx @@ -81,6 +81,7 @@ class ScXMLDatabaseRangeContext : public ScXMLImportContext bool bSubTotalsAscending; bool bFilterConditionSourceRange; bool bHasHeader; + bool bHasFooter; // UNO TotalsRow, ODF contains-footer bool bByRow; ScDBCollection::RangeType meRangeType; diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx index bb31d5f9e7fc..170f1a58993d 100644 --- a/sc/source/ui/unoobj/datauno.cxx +++ b/sc/source/ui/unoobj/datauno.cxx @@ -1750,6 +1750,7 @@ void ScDatabaseRangeObj::SetQueryParam(const ScQueryParam& rQueryParam) ScDBData aNewData( *pData ); aNewData.SetQueryParam(aParam); aNewData.SetHeader(aParam.bHasHeader); // not in ScDBData::SetQueryParam + aNewData.SetTotals(aParam.bHasTotals); // not in ScDBData::SetQueryParam ScDBDocFunc aFunc(*pDocShell); aFunc.ModifyDBData(aNewData); } -- cgit