Kaydet (Commit) 071c3309 authored tarafından László Németh's avatar László Németh

tdf#67207 DOCX mail merge: fix export/import of database fields

to support the registered databases (containing ODS, XLSX
sheet or ODT text table data sources).

Now database fields don't lose their database connection,
and File->Print can merge mails after DOCX export/import, if
the LO instance has got a registered database with the same
name and table, as in saved in w:settings/w:mailMerge/w:query
element of the DOCX document in the form of

SELECT * FROM [databaseName].dbo.[tableName]$

query.

Notes:

– This fix supports only single table usage.

– The exported DOCX document is editable in MSO, too,
  without losing the database connection in LO later.

Change-Id: I97826b7ee7defd0243dbaffa0325c5b11dd2c0d1
Reviewed-on: https://gerrit.libreoffice.org/71228
Tested-by: Jenkins
Reviewed-by: 's avatarLászló Németh <nemeth@numbertext.org>
üst e1a75c1c
...@@ -988,6 +988,38 @@ DECLARE_OOXMLIMPORT_TEST(testTdf123054, "tdf123054.docx") ...@@ -988,6 +988,38 @@ DECLARE_OOXMLIMPORT_TEST(testTdf123054, "tdf123054.docx")
getProperty<OUString>(getParagraph(20), "ParaStyleName")); getProperty<OUString>(getParagraph(20), "ParaStyleName"));
} }
DECLARE_OOXMLEXPORT_TEST(testTdf67207_MERGEFIELD_DATABASE, "tdf67207.docx")
{
// database fields use the database "database" and its table "Sheet1"
uno::Reference<beans::XPropertySet> xTextField = getProperty< uno::Reference<beans::XPropertySet> >(getRun(getParagraph(2), 2), "TextField");
CPPUNIT_ASSERT(xTextField.is());
uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY_THROW);
uno::Reference<text::XDependentTextField> xDependent(xTextField, uno::UNO_QUERY_THROW);
CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.Database"));
OUString sValue;
xTextField->getPropertyValue("Content") >>= sValue;
CPPUNIT_ASSERT_EQUAL(OUString::fromUtf8("<c1>"), sValue);
uno::Reference<beans::XPropertySet> xFiledMaster = xDependent->getTextFieldMaster();
uno::Reference<lang::XServiceInfo> xFiledMasterServiceInfo(xFiledMaster, uno::UNO_QUERY_THROW);
CPPUNIT_ASSERT(xFiledMasterServiceInfo->supportsService("com.sun.star.text.fieldmaster.Database"));
// Defined properties: DataBaseName, Name, DataTableName, DataColumnName, DependentTextFields, DataCommandType, InstanceName, DataBaseURL
CPPUNIT_ASSERT(xFiledMaster->getPropertyValue("DataBaseName") >>= sValue);
CPPUNIT_ASSERT_EQUAL(OUString("database"), sValue);
sal_Int32 nCommandType;
CPPUNIT_ASSERT(xFiledMaster->getPropertyValue("DataCommandType") >>= nCommandType);
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nCommandType); // css::sdb::CommandType::TABLE
CPPUNIT_ASSERT(xFiledMaster->getPropertyValue("DataTableName") >>= sValue);
CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), sValue);
CPPUNIT_ASSERT(xFiledMaster->getPropertyValue("DataColumnName") >>= sValue);
CPPUNIT_ASSERT_EQUAL(OUString("c1"), sValue);
CPPUNIT_ASSERT(xFiledMaster->getPropertyValue("InstanceName") >>= sValue);
CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.text.fieldmaster.DataBase.database.Sheet1.c1"), sValue);
}
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <com/sun/star/xml/sax/XSAXSerializable.hpp> #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
#include <com/sun/star/xml/sax/Writer.hpp> #include <com/sun/star/xml/sax/Writer.hpp>
#include <com/sun/star/awt/XControlModel.hpp> #include <com/sun/star/awt/XControlModel.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <oox/token/namespaces.hxx> #include <oox/token/namespaces.hxx>
#include <oox/token/tokens.hxx> #include <oox/token/tokens.hxx>
...@@ -63,6 +64,7 @@ ...@@ -63,6 +64,7 @@
#include <section.hxx> #include <section.hxx>
#include <ftninfo.hxx> #include <ftninfo.hxx>
#include <pagedesc.hxx> #include <pagedesc.hxx>
#include <swdbdata.hxx>
#include <editeng/unoprnms.hxx> #include <editeng/unoprnms.hxx>
#include <editeng/editobj.hxx> #include <editeng/editobj.hxx>
...@@ -979,6 +981,27 @@ void DocxExport::WriteSettings() ...@@ -979,6 +981,27 @@ void DocxExport::WriteSettings()
pFS->endElementNS( XML_w, XML_compat ); pFS->endElementNS( XML_w, XML_compat );
} }
// export current mail merge database and table names
SwDBData aData = m_pDoc->GetDBData();
if ( !aData.sDataSource.isEmpty() && aData.nCommandType == css::sdb::CommandType::TABLE && !aData.sCommand.isEmpty() )
{
OUStringBuffer aDataSource;
aDataSource.append("SELECT * FROM ");
aDataSource.append(aData.sDataSource); // current database
aDataSource.append(".dbo."); // default database owner
aDataSource.append(aData.sCommand); // sheet name
aDataSource.append("$"); // sheet identifier
const OUString sDataSource = aDataSource.makeStringAndClear();
pFS->startElementNS( XML_w, XML_mailMerge );
pFS->singleElementNS(XML_w, XML_mainDocumentType,
FSNS( XML_w, XML_val ), "formLetters" );
pFS->singleElementNS(XML_w, XML_dataType,
FSNS( XML_w, XML_val ), "textFile" );
pFS->singleElementNS( XML_w, XML_query,
FSNS( XML_w, XML_val ), OUStringToOString( sDataSource, RTL_TEXTENCODING_UTF8 ).getStr() );
pFS->endElementNS( XML_w, XML_mailMerge );
}
// Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer. // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer.
// Use the setting from the default style. // Use the setting from the default style.
SwTextFormatColl* pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, /*bRegardLanguage=*/false); SwTextFormatColl* pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, /*bRegardLanguage=*/false);
......
...@@ -3080,8 +3080,15 @@ uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(c ...@@ -3080,8 +3080,15 @@ uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(c
uno::Reference< beans::XPropertySet > xMaster; uno::Reference< beans::XPropertySet > xMaster;
OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) ); OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) );
OUStringBuffer aFieldMasterName; OUStringBuffer aFieldMasterName;
OUString sDatabaseDataSourceName = GetSettingsTable()->GetCurrentDatabaseDataSource();
bool bIsMergeField = sFieldMasterService.endsWith("Database");
aFieldMasterName.appendAscii( pFieldMasterService ); aFieldMasterName.appendAscii( pFieldMasterService );
aFieldMasterName.append('.'); aFieldMasterName.append('.');
if ( bIsMergeField && !sDatabaseDataSourceName.isEmpty() )
{
aFieldMasterName.append(sDatabaseDataSourceName);
aFieldMasterName.append('.');
}
aFieldMasterName.append(rFieldMasterName); aFieldMasterName.append(rFieldMasterName);
OUString sFieldMasterName = aFieldMasterName.makeStringAndClear(); OUString sFieldMasterName = aFieldMasterName.makeStringAndClear();
if(xFieldMasterAccess->hasByName(sFieldMasterName)) if(xFieldMasterAccess->hasByName(sFieldMasterName))
...@@ -3093,10 +3100,27 @@ uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(c ...@@ -3093,10 +3100,27 @@ uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(c
{ {
//create the master //create the master
xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW); xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW);
//set the master's name if ( !bIsMergeField || sDatabaseDataSourceName.isEmpty() )
xMaster->setPropertyValue( {
//set the master's name
xMaster->setPropertyValue(
getPropertyName(PROP_NAME), getPropertyName(PROP_NAME),
uno::makeAny(rFieldMasterName)); uno::makeAny(rFieldMasterName));
} else {
// set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
xMaster->setPropertyValue(
getPropertyName(PROP_DATABASE_NAME),
uno::makeAny(sDatabaseDataSourceName.copy(0, sDatabaseDataSourceName.indexOf('.'))));
xMaster->setPropertyValue(
getPropertyName(PROP_COMMAND_TYPE),
uno::makeAny(sal_Int32(0)));
xMaster->setPropertyValue(
getPropertyName(PROP_DATATABLE_NAME),
uno::makeAny(sDatabaseDataSourceName.copy(sDatabaseDataSourceName.indexOf('.') + 1)));
xMaster->setPropertyValue(
getPropertyName(PROP_DATACOLUMN_NAME),
uno::makeAny(rFieldMasterName));
}
} }
return xMaster; return xMaster;
} }
......
...@@ -350,6 +350,10 @@ OUString getPropertyName( PropertyIds eId ) ...@@ -350,6 +350,10 @@ OUString getPropertyName( PropertyIds eId )
case PROP_RUBY_ADJUST: sName = "RubyAdjust"; break; case PROP_RUBY_ADJUST: sName = "RubyAdjust"; break;
case PROP_RUBY_POSITION: sName = "RubyPosition"; break; case PROP_RUBY_POSITION: sName = "RubyPosition"; break;
case PROP_LAYOUT_IN_CELL: sName = "IsLayoutInCell"; break; case PROP_LAYOUT_IN_CELL: sName = "IsLayoutInCell"; break;
case PROP_DATABASE_NAME: sName = "DataBaseName"; break;
case PROP_COMMAND_TYPE: sName = "DataCommandType"; break;
case PROP_DATATABLE_NAME: sName = "DataTableName"; break;
case PROP_DATACOLUMN_NAME: sName = "DataColumnName"; break;
} }
assert(sName.getLength()>0); assert(sName.getLength()>0);
return sName; return sName;
......
...@@ -348,6 +348,10 @@ enum PropertyIds ...@@ -348,6 +348,10 @@ enum PropertyIds
,PROP_RUBY_ADJUST ,PROP_RUBY_ADJUST
,PROP_RUBY_POSITION ,PROP_RUBY_POSITION
,PROP_LAYOUT_IN_CELL ,PROP_LAYOUT_IN_CELL
,PROP_DATABASE_NAME
,PROP_COMMAND_TYPE
,PROP_DATATABLE_NAME
,PROP_DATACOLUMN_NAME
}; };
//Returns the UNO string equivalent to eId. //Returns the UNO string equivalent to eId.
......
...@@ -256,6 +256,7 @@ struct SettingsTable_Impl ...@@ -256,6 +256,7 @@ struct SettingsTable_Impl
std::vector<beans::PropertyValue> m_aCompatSettings; std::vector<beans::PropertyValue> m_aCompatSettings;
uno::Sequence<beans::PropertyValue> m_pCurrentCompatSetting; uno::Sequence<beans::PropertyValue> m_pCurrentCompatSetting;
OUString m_sCurrentDatabaseDataSource;
DocumentProtection_Impl m_DocumentProtection; DocumentProtection_Impl m_DocumentProtection;
...@@ -460,6 +461,29 @@ void SettingsTable::lcl_sprm(Sprm& rSprm) ...@@ -460,6 +461,29 @@ void SettingsTable::lcl_sprm(Sprm& rSprm)
case NS_ooxml::LN_CT_Settings_mirrorMargins: case NS_ooxml::LN_CT_Settings_mirrorMargins:
m_pImpl->m_bMirrorMargin = nIntValue; m_pImpl->m_bMirrorMargin = nIntValue;
break; break;
case NS_ooxml::LN_CT_Settings_mailMerge:
{
writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
if (pProperties.get())
pProperties->resolve(*this);
}
break;
case NS_ooxml::LN_CT_MailMerge_query:
{
// try to get the "database.table" name from the query saved previously
OUString sVal = pValue->getString();
if ( sVal.endsWith("$") && sVal.indexOf(".dbo.") > 0 )
{
sal_Int32 nSpace = sVal.lastIndexOf(' ');
sal_Int32 nDbo = sVal.lastIndexOf(".dbo.");
if ( nSpace > 0 && nSpace < nDbo - 1 )
{
m_pImpl->m_sCurrentDatabaseDataSource = sVal.copy(nSpace + 1, nDbo - nSpace - 1) +
sVal.copy(nDbo + 4, sVal.getLength() - nDbo - 5);
}
}
}
break;
case NS_ooxml::LN_CT_Compat_compatSetting: case NS_ooxml::LN_CT_Compat_compatSetting:
{ {
writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
...@@ -595,6 +619,11 @@ css::uno::Sequence<css::beans::PropertyValue> SettingsTable::GetDocumentProtecti ...@@ -595,6 +619,11 @@ css::uno::Sequence<css::beans::PropertyValue> SettingsTable::GetDocumentProtecti
return m_pImpl->m_DocumentProtection.toSequence(); return m_pImpl->m_DocumentProtection.toSequence();
} }
OUString SettingsTable::GetCurrentDatabaseDataSource() const
{
return m_pImpl->m_sCurrentDatabaseDataSource;
}
static bool lcl_isDefault(const uno::Reference<beans::XPropertyState>& xPropertyState, const OUString& rPropertyName) static bool lcl_isDefault(const uno::Reference<beans::XPropertyState>& xPropertyState, const OUString& rPropertyName)
{ {
return xPropertyState->getPropertyState(rPropertyName) == beans::PropertyState_DEFAULT_VALUE; return xPropertyState->getPropertyState(rPropertyName) == beans::PropertyState_DEFAULT_VALUE;
......
...@@ -88,6 +88,8 @@ class SettingsTable : public LoggedProperties, public LoggedTable ...@@ -88,6 +88,8 @@ class SettingsTable : public LoggedProperties, public LoggedTable
sal_Int32 GetWordCompatibilityMode() const; sal_Int32 GetWordCompatibilityMode() const;
OUString GetCurrentDatabaseDataSource() const;
private: private:
// Properties // Properties
virtual void lcl_attribute(Id Name, Value & val) override; virtual void lcl_attribute(Id Name, Value & val) override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment