/* -*- 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/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <cassert>
#include <cstdarg>
#include <unotxdoc.hxx>
#include <sfx2/app.hxx>
#include <sfx2/printer.hxx>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/XDocumentDataSource.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/XEventListener.hpp>
#include <com/sun/star/uri/UriReferenceFactory.hpp>
#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdb/TextConnectionSettings.hpp>
#include <com/sun/star/sdb/XCompletedConnection.hpp>
#include <com/sun/star/sdb/XCompletedExecution.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/text/MailMergeEvent.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <vcl/errinf.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/filedlghelper.hxx>
#include <sfx2/viewfrm.hxx>
#include <dbconfig.hxx>
#include <pagedesc.hxx>
#include <vcl/lstbox.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/pathoptions.hxx>
#include <svl/urihelper.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <svl/stritem.hxx>
#include <svl/eitem.hxx>
#include <vcl/oldprintadaptor.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/progress.hxx>
#include <sfx2/dispatch.hxx>
#include <cmdid.h>
#include <swmodule.hxx>
#include <view.hxx>
#include <docsh.hxx>
#include <edtwin.hxx>
#include <wrtsh.hxx>
#include <fldbas.hxx>
#include <initui.hxx>
#include <swundo.hxx>
#include <flddat.hxx>
#include <modcfg.hxx>
#include <shellio.hxx>
#include <dbui.hxx>
#include <dbmgr.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentContentOperations.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentUndoRedo.hxx>
#include <swwait.hxx>
#include <swunohelper.hxx>
#include <dbui.hrc>
#include <globals.hrc>
#include <strings.hrc>
#include <mmconfigitem.hxx>
#include <sfx2/request.hxx>
#include <hintids.hxx>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/XColumn.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>
#include <com/sun/star/mail/MailAttachment.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/property.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/string.hxx>
#include <comphelper/types.hxx>
#include <mailmergehelper.hxx>
#include <maildispatcher.hxx>
#include <svtools/htmlcfg.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <com/sun/star/util/XNumberFormatTypes.hpp>
#include <editeng/langitem.hxx>
#include <svl/numuno.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/dbconversion.hxx>
#include <o3tl/make_unique.hxx>
#include <unomailmerge.hxx>
#include <sfx2/event.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <osl/mutex.hxx>
#include <rtl/textenc.h>
#include <rtl/tencinfo.h>
#include <cppuhelper/implbase.hxx>
#include <ndindex.hxx>
#include <pam.hxx>
#include <swcrsr.hxx>
#include <swevent.hxx>
#include <osl/file.hxx>
#include <sal/log.hxx>
#include <swabstdlg.hxx>
#include <fmthdft.hxx>
#include <vector>
#include <unomid.h>
#include <section.hxx>
#include <rootfrm.hxx>
#include <fmtpdsc.hxx>
#include <ndtxt.hxx>
#include <calc.hxx>
#include <dbfld.hxx>
#include <IDocumentState.hxx>
#include <imaildsplistener.hxx>
#include <iodetect.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <memory>
#include <comphelper/propertysequence.hxx>
#include <officecfg/Office/Common.hxx>
using namespace ::com::sun::star;
using namespace sw;
#define DB_SEP_SPACE 0
#define DB_SEP_TAB 1
#define DB_SEP_RETURN 2
#define DB_SEP_NEWLINE 3
namespace {
void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell)
{
SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId,
SwDocShell::GetEventName(nStrId),
pDocShell));
}
}
std::vector<std::pair<SwDocShell*, OUString>> SwDBManager::m_aUncommitedRegistrations;
enum class SwDBNextRecord { NEXT, FIRST };
static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action = SwDBNextRecord::NEXT );
enum class WorkingDocType { SOURCE, TARGET, COPY };
static SfxObjectShell* lcl_CreateWorkingDocument(
const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
const vcl::Window *pSourceWindow,
SwDBManager** const ppDBManager,
SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc );
static bool lcl_getCountFromResultSet( sal_Int32& rCount, const SwDSParam* pParam )
{
rCount = pParam->aSelection.getLength();
if ( rCount > 0 )
return true;
uno::Reference<beans::XPropertySet> xPrSet(pParam->xResultSet, uno::UNO_QUERY);
if ( xPrSet.is() )
{
try
{
bool bFinal = false;
uno::Any aFinal = xPrSet->getPropertyValue("IsRowCountFinal");
aFinal >>= bFinal;
if(!bFinal)
{
pParam->xResultSet->last();
pParam->xResultSet->first();
}
uno::Any aCount = xPrSet->getPropertyValue("RowCount");
if( aCount >>= rCount )
return true;
}
catch(const uno::Exception&)
{
}
}
return false;
}
class SwDBManager::ConnectionDisposedListener_Impl
: public cppu::WeakImplHelper< lang::XEventListener >
{
private:
SwDBManager * m_pDBManager;
virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
public:
explicit ConnectionDisposedListener_Impl(SwDBManager& rMgr);
void Dispose() { m_pDBManager = nullptr; }
};
/// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener>
{
uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext;
SwDBManager* m_pDBManager;
public:
explicit SwDataSourceRemovedListener(SwDBManager& rDBManager);
virtual ~SwDataSourceRemovedListener() override;
virtual void SAL_CALL registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
virtual void SAL_CALL revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
virtual void SAL_CALL changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
virtual void SAL_CALL disposing(const lang::EventObject& rObject) override;
void Dispose();
};
SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager& rDBManager)
: m_pDBManager(&rDBManager)
{
uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
m_xDatabaseContext = sdb::DatabaseContext::create(xComponentContext);
m_xDatabaseContext->addDatabaseRegistrationsListener(this);
}
SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
{
if (m_xDatabaseContext.is())
m_xDatabaseContext->removeDatabaseRegistrationsListener(this);
}
void SAL_CALL SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& /*rEvent*/)
{
}
void SAL_CALL SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
{
if (!m_pDBManager || m_pDBManager->getEmbeddedName().isEmpty())
return;
SwDoc* pDoc = m_pDBManager->getDoc();
if (!pDoc)
return;
SwDocShell* pDocShell = pDoc->GetDocShell();
if (!pDocShell)
return;
OUString aOwnURL = pDocShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
OUString sTmpName = "vnd.sun.star.pkg://";
sTmpName += INetURLObject::encode(aOwnURL, INetURLObject::PART_AUTHORITY, INetURLObject::EncodeMechanism::All);
sTmpName += "/" + m_pDBManager->getEmbeddedName();
if (sTmpName != rEvent.OldLocation)
return;
// The revoked database location is inside this document, then remove the
// embedding, as otherwise it would be back on the next reload of the
// document.
pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName());
m_pDBManager->setEmbeddedName(OUString(), *pDocShell);
}
void SAL_CALL SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
{
if (rEvent.OldLocation != rEvent.NewLocation)
revokedDatabaseLocation(rEvent);
}
void SwDataSourceRemovedListener::disposing(const lang::EventObject& /*rObject*/)
{
m_xDatabaseContext.clear();
}
void SwDataSourceRemovedListener::Dispose()
{
m_pDBManager = nullptr;
}
struct SwDBManager::SwDBManager_Impl
{
SwDSParam *pMergeData;
VclPtr<AbstractMailMergeDlg> pMergeDialog;
rtl::Reference<SwDBManager::ConnectionDisposedListener_Impl> m_xDisposeListener;
rtl::Reference<SwDataSourceRemovedListener> m_xDataSourceRemovedListener;
osl::Mutex m_aAllEmailSendMutex;
uno::Reference< mail::XMailMessage> m_xLastMessage;
explicit SwDBManager_Impl(SwDBManager& rDBManager)
: pMergeData( nullptr )
, m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager))
{}
~SwDBManager_Impl()
{
m_xDisposeListener->Dispose();
if (m_xDataSourceRemovedListener.is())
m_xDataSourceRemovedListener->Dispose();
}
};
static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<sdbc::XDataSource> const & xSource)
{
uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
rParam.xFormatter.set(util::NumberFormatter::create(xContext), uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xSourceProps(
(xSource.is()
? xSource
: SwDBManager::getDataSourceAsParent(
rParam.xConnection, rParam.sDataSource)),
uno::UNO_QUERY);
if(xSourceProps.is())
{
uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
if(aFormats.hasValue())
{
uno::Reference<util::XNumberFormatsSupplier> xSuppl;
aFormats >>= xSuppl;
if(xSuppl.is())
{
uno::Reference< beans::XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
uno::Any aNull = xSettings->getPropertyValue("NullDate");
aNull >>= rParam.aNullDate;
if(rParam.xFormatter.is())
rParam.xFormatter->attachNumberFormatsSupplier(xSuppl);
}
}
}
}
static bool lcl_MoveAbsolute(SwDSParam* pParam, long nAbsPos)
{
bool bRet = false;
try
{
if(pParam->aSelection.getLength())
{
if(pParam->aSelection.getLength() <= nAbsPos)
{
pParam->bEndOfDB = true;
bRet = false;
}
else
{
pParam->nSelectionIndex = nAbsPos;
sal_Int32 nPos = 0;
pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
bRet = !pParam->bEndOfDB;
}
}
else if(pParam->bScrollable)
{
bRet = pParam->xResultSet->absolute( nAbsPos );
}
else
{
OSL_FAIL("no absolute positioning available");
}
}
catch(const uno::Exception&)
{
}
return bRet;
}
static void lcl_GetColumnCnt(SwDSParam *pParam,
const uno::Reference< beans::XPropertySet > &rColumnProps,
LanguageType nLanguage, OUString &rResult, double* pNumber)
{
SwDBFormatData aFormatData;
if(!pParam->xFormatter.is())
{
uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(
pParam->xConnection,pParam->sDataSource);
lcl_InitNumberFormatter(*pParam, xSource );
}
aFormatData.aNullDate = pParam->aNullDate;
aFormatData.xFormatter = pParam->xFormatter;
aFormatData.aLocale = LanguageTag( nLanguage ).getLocale();
rResult = SwDBManager::GetDBField( rColumnProps, aFormatData, pNumber);
}
static bool lcl_GetColumnCnt(SwDSParam* pParam, const OUString& rColumnName,
LanguageType nLanguage, OUString& rResult, double* pNumber)
{
uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pParam->xResultSet, uno::UNO_QUERY );
uno::Reference<container::XNameAccess> xCols;
try
{
xCols = xColsSupp->getColumns();
}
catch(const lang::DisposedException&)
{
}
if(!xCols.is() || !xCols->hasByName(rColumnName))
return false;
uno::Any aCol = xCols->getByName(rColumnName);
uno::Reference< beans::XPropertySet > xColumnProps;
aCol >>= xColumnProps;
lcl_GetColumnCnt( pParam, xColumnProps, nLanguage, rResult, pNumber );
return true;
};
// import data
bool SwDBManager::Merge( const SwMergeDescriptor& rMergeDesc )
{
assert( !bInMerge && !pImpl->pMergeData && "merge already activated!" );
SfxObjectShellLock xWorkObjSh;
SwWrtShell *pWorkShell = nullptr;
SwDoc *pWorkDoc = nullptr;
SwDBManager *pWorkDocOrigDBManager = nullptr;
switch( rMergeDesc.nMergeType )
{
case DBMGR_MERGE_PRINTER:
case DBMGR_MERGE_EMAIL:
case DBMGR_MERGE_FILE:
case DBMGR_MERGE_SHELL:
{
SwDocShell *pSourceDocSh = rMergeDesc.rSh.GetView().GetDocShell();
if( pSourceDocSh->IsModified() )
{
pWorkDocOrigDBManager = this;
xWorkObjSh = lcl_CreateWorkingDocument(
WorkingDocType::SOURCE, rMergeDesc.rSh, nullptr,
&pWorkDocOrigDBManager, nullptr, &pWorkShell, &pWorkDoc );
}
SAL_FALLTHROUGH;
}
default:
if( !xWorkObjSh.Is() )
pWorkShell = &rMergeDesc.rSh;
break;
}
SwDBData aData;
aData.nCommandType = sdb::CommandType::TABLE;
uno::Reference<sdbc::XResultSet> xResSet;
uno::Sequence<uno::Any> aSelection;
uno::Reference< sdbc::XConnection> xConnection;
aData.sDataSource = rMergeDesc.rDescriptor.getDataSource();
rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Command] >>= aData.sCommand;
rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= aData.nCommandType;
if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Cursor) )
rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Cursor] >>= xResSet;
if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Selection) )
rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Connection) )
rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
if(aData.sDataSource.isEmpty() || aData.sCommand.isEmpty() || !xResSet.is())
{
return false;
}
pImpl->pMergeData = new SwDSParam(aData, xResSet, aSelection);
SwDSParam* pTemp = FindDSData(aData, false);
if(pTemp)
*pTemp = *pImpl->pMergeData;
else
{
// calls from the calculator may have added a connection with an invalid commandtype
//"real" data base connections added here have to re-use the already available
//DSData and set the correct CommandType
SwDBData aTempData(aData);
aData.nCommandType = -1;
pTemp = FindDSData(aData, false);
if(pTemp)
*pTemp = *pImpl->pMergeData;
else
{
m_DataSourceParams.push_back(o3tl::make_unique<SwDSParam>(*pImpl->pMergeData));
try
{
uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY);
if(xComponent.is())
xComponent->addEventListener(pImpl->m_xDisposeListener.get());
}
catch(const uno::Exception&)
{
}
}
}
if(!pImpl->pMergeData->xConnection.is())
pImpl->pMergeData->xConnection = xConnection;
// add an XEventListener
lcl_ToNextRecord(pImpl->pMergeData, SwDBNextRecord::FIRST);
uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,aData.sDataSource);
lcl_InitNumberFormatter(*pImpl->pMergeData, xSource);
pWorkShell->ChgDBData(aData);
bInMerge = true;
if (IsInitDBFields())
{
// with database fields without DB-Name, use DB-Name from Doc
std::vector<OUString> aDBNames;
aDBNames.emplace_back();
SwDBData aInsertData = pWorkShell->GetDBData();
OUString sDBName = aInsertData.sDataSource
+ OUStringLiteral1(DB_DELIM) + aInsertData.sCommand
+ OUStringLiteral1(DB_DELIM)
+ OUString::number(aInsertData.nCommandType);
pWorkShell->ChangeDBFields( aDBNames, sDBName);
SetInitDBFields(false);
}
bool bRet = true;
switch(rMergeDesc.nMergeType)
{
case DBMGR_MERGE:
pWorkShell->StartAllAction();
pWorkShell->SwViewShell::UpdateFields( true );
pWorkShell->SetModified();
pWorkShell->EndAllAction();
break;
case DBMGR_MERGE_PRINTER:
case DBMGR_MERGE_EMAIL:
case DBMGR_MERGE_FILE:
case DBMGR_MERGE_SHELL:
// save files and send them as e-Mail if required
bRet = MergeMailFiles(pWorkShell, rMergeDesc);
break;
default:
// insert selected entries
// (was: InsertRecord)
ImportFromConnection(pWorkShell);
break;
}
DELETEZ( pImpl->pMergeData );
if( xWorkObjSh.Is() )
{
pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
xWorkObjSh->DoClose();
}
bInMerge = false;
return bRet;
}
void SwDBManager::ImportFromConnection( SwWrtShell* pSh )
{
if(pImpl->pMergeData && !pImpl->pMergeData->bEndOfDB)
{
pSh->StartAllAction();
pSh->StartUndo();
bool bGroupUndo(pSh->DoesGroupUndo());
pSh->DoGroupUndo(false);
if( pSh->HasSelection() )
pSh->DelRight();
std::unique_ptr<SwWait> pWait;
{
sal_uLong i = 0;
do {
ImportDBEntry(pSh);
if( 10 == ++i )
pWait.reset(new SwWait( *pSh->GetView().GetDocShell(), true));
} while(ToNextMergeRecord());
}
pSh->DoGroupUndo(bGroupUndo);
pSh->EndUndo();
pSh->EndAllAction();
}
}
static OUString lcl_FindColumn(const OUString& sFormatStr,sal_uInt16 &nUsedPos, sal_uInt8 &nSeparator)
{
OUStringBuffer sReturn;
sal_uInt16 nLen = sFormatStr.getLength();
nSeparator = 0xff;
while(nUsedPos < nLen && nSeparator == 0xff)
{
sal_Unicode cCurrent = sFormatStr[nUsedPos];
switch(cCurrent)
{
case ',':
nSeparator = DB_SEP_SPACE;
break;
case ';':
nSeparator = DB_SEP_RETURN;
break;
case ':':
nSeparator = DB_SEP_TAB;
break;
case '#':
nSeparator = DB_SEP_NEWLINE;
break;
default:
sReturn.append(cCurrent);
}
nUsedPos++;
}
return sReturn.makeStringAndClear();
}
void SwDBManager::ImportDBEntry(SwWrtShell* pSh)
{
if(pImpl->pMergeData && !pImpl->pMergeData->bEndOfDB)
{
uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
OUString sFormatStr;
sal_uInt16 nFormatLen = sFormatStr.getLength();
if( nFormatLen )
{
const char cSpace = ' ';
const char cTab = '\t';
sal_uInt16 nUsedPos = 0;
sal_uInt8 nSeparator;
OUString sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator);
while( !sColumn.isEmpty() )
{
if(!xCols->hasByName(sColumn))
return;
uno::Any aCol = xCols->getByName(sColumn);
uno::Reference< beans::XPropertySet > xColumnProp;
aCol >>= xColumnProp;
if(xColumnProp.is())
{
SwDBFormatData aDBFormat;
OUString sInsert = GetDBField( xColumnProp, aDBFormat);
if( DB_SEP_SPACE == nSeparator )
sInsert += OUStringLiteral1(cSpace);
else if( DB_SEP_TAB == nSeparator)
sInsert += OUStringLiteral1(cTab);
pSh->Insert(sInsert);
if( DB_SEP_RETURN == nSeparator)
pSh->SplitNode();
else if(DB_SEP_NEWLINE == nSeparator)
pSh->InsertLineBreak();
}
else
{
// column not found -> show error
OUStringBuffer sInsert;
sInsert.append('?').append(sColumn).append('?');
pSh->Insert(sInsert.makeStringAndClear());
}
sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator);
}
pSh->SplitNode();
}
else
{
OUStringBuffer sStr;
uno::Sequence<OUString> aColNames = xCols->getElementNames();
const OUString* pColNames = aColNames.getConstArray();
long nLength = aColNames.getLength();
for(long i = 0; i < nLength; i++)
{
uno::Any aCol = xCols->getByName(pColNames[i]);
uno::Reference< beans::XPropertySet > xColumnProp;
aCol >>= xColumnProp;
SwDBFormatData aDBFormat;
sStr.append(GetDBField( xColumnProp, aDBFormat));
if (i < nLength - 1)
sStr.append("\t");
}
pSh->SwEditShell::Insert2(sStr.makeStringAndClear());
pSh->SwFEShell::SplitNode(); // line feed
}
}
}
bool SwDBManager::GetTableNames(weld::ComboBoxText& rBox, const OUString& rDBName)
{
bool bRet = false;
OUString sOldTableName(rBox.get_active_text());
rBox.clear();
SwDSParam* pParam = FindDSConnection(rDBName, false);
uno::Reference< sdbc::XConnection> xConnection;
if (pParam && pParam->xConnection.is())
xConnection = pParam->xConnection;
else
{
if ( !rDBName.isEmpty() )
xConnection = RegisterConnection( rDBName );
}
if (xConnection.is())
{
uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
if(xTSupplier.is())
{
uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
uno::Sequence<OUString> aTables = xTables->getElementNames();
const OUString* pTables = aTables.getConstArray();
for (sal_Int32 i = 0; i < aTables.getLength(); ++i)
rBox.append("0", pTables[i]);
}
uno::Reference<sdb::XQueriesSupplier> xQSupplier(xConnection, uno::UNO_QUERY);
if(xQSupplier.is())
{
uno::Reference<container::XNameAccess> xQueries = xQSupplier->getQueries();
uno::Sequence<OUString> aQueries = xQueries->getElementNames();
const OUString* pQueries = aQueries.getConstArray();
for (sal_Int32 i = 0; i < aQueries.getLength(); i++)
rBox.append("1", pQueries[i]);
}
if (!sOldTableName.isEmpty())
rBox.set_active_text(sOldTableName);
bRet = true;
}
return bRet;
}
// fill Listbox with column names of a database
void SwDBManager::GetColumnNames(ListBox* pListBox,
const OUString& rDBName, const OUString& rTableName)
{
SwDBData aData;
aData.sDataSource = rDBName;
aData.sCommand = rTableName;
aData.nCommandType = -1;
SwDSParam* pParam = FindDSData(aData, false);
uno::Reference< sdbc::XConnection> xConnection;
if(pParam && pParam->xConnection.is())
xConnection = pParam->xConnection;
else
{
xConnection = RegisterConnection( rDBName );
}
GetColumnNames(pListBox, xConnection, rTableName);
}
void SwDBManager::GetColumnNames(weld::ComboBoxText& rBox,
const OUString& rDBName, const OUString& rTableName)
{
SwDBData aData;
aData.sDataSource = rDBName;
aData.sCommand = rTableName;
aData.nCommandType = -1;
SwDSParam* pParam = FindDSData(aData, false);
uno::Reference< sdbc::XConnection> xConnection;
if(pParam && pParam->xConnection.is())
xConnection = pParam->xConnection;
else
{
xConnection = RegisterConnection( rDBName );
}
GetColumnNames(rBox, xConnection, rTableName);
}
void SwDBManager::GetColumnNames(ListBox* pListBox,
uno::Reference< sdbc::XConnection> const & xConnection,
const OUString& rTableName)
{
pListBox->Clear();
uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
if(xColsSupp.is())
{
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
const uno::Sequence<OUString> aColNames = xCols->getElementNames();
const OUString* pColNames = aColNames.getConstArray();
for(int nCol = 0; nCol < aColNames.getLength(); nCol++)
{
pListBox->InsertEntry(pColNames[nCol]);
}
::comphelper::disposeComponent( xColsSupp );
}
}
void SwDBManager::GetColumnNames(weld::ComboBoxText& rBox,
uno::Reference< sdbc::XConnection> const & xConnection,
const OUString& rTableName)
{
rBox.clear();
uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
if(xColsSupp.is())
{
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
const uno::Sequence<OUString> aColNames = xCols->getElementNames();
const OUString* pColNames = aColNames.getConstArray();
for (sal_Int32 nCol = 0; nCol < aColNames.getLength(); ++nCol)
{
rBox.append_text(pColNames[nCol]);
}
::comphelper::disposeComponent( xColsSupp );
}
}
SwDBManager::SwDBManager(SwDoc* pDoc)
: m_aMergeStatus( MergeStatus::Ok )
, bInitDBFields(false)
, bInMerge(false)
, bMergeSilent(false)
, pImpl(new SwDBManager_Impl(*this))
, pMergeEvtSrc(nullptr)
, m_pDoc(pDoc)
{
}
SwDBManager::~SwDBManager() COVERITY_NOEXCEPT_FALSE
{
RevokeLastRegistrations();
// copy required, m_DataSourceParams can be modified while disposing components
std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections;
for (auto & pParam : m_DataSourceParams)
{
if(pParam->xConnection.is())
{
aCopiedConnections.push_back(pParam->xConnection);
}
}
for (auto & xConnection : aCopiedConnections)
{
try
{
uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY);
if(xComp.is())
xComp->dispose();
}
catch(const uno::RuntimeException&)
{
//may be disposed already since multiple entries may have used the same connection
}
}
}
static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell )
{
//reset all links of the sections of synchronized labels
size_t nSections = rWorkShell.GetSectionFormatCount();
for (size_t nSection = 0; nSection < nSections; ++nSection)
{
SwSectionData aSectionData( *rWorkShell.GetSectionFormat( nSection ).GetSection() );
if( aSectionData.GetType() == FILE_LINK_SECTION )
{
aSectionData.SetType( CONTENT_SECTION );
aSectionData.SetLinkFileName( OUString() );
rWorkShell.UpdateSection( nSection, aSectionData );
}
}
rWorkShell.SetLabelDoc( false );
}
static void lcl_SaveDebugDoc( SfxObjectShell *xTargetDocShell,
const char *name, int no = 0 )
{
static OUString sTempDirURL;
if( sTempDirURL.isEmpty() )
{
SvtPathOptions aPathOpt;
utl::TempFile aTempDir( &aPathOpt.GetTempPath(), true );
if( aTempDir.IsValid() )
{
INetURLObject aTempDirURL( aTempDir.GetURL() );
sTempDirURL = aTempDirURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL );
}
}
if( sTempDirURL.isEmpty() )
return;
const OUString sExt( ".odt" );
OUString basename = OUString::createFromAscii( name );
if (no > 0)
basename += OUString::number(no) + "-";
// aTempFile is not deleted, but that seems to be intentional
utl::TempFile aTempFile( basename, true, &sExt, &sTempDirURL );
INetURLObject aTempFileURL( aTempFile.GetURL() );
auto pDstMed = o3tl::make_unique<SfxMedium>(
aTempFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
StreamMode::STD_READWRITE );
bool bAnyError = !xTargetDocShell->DoSaveAs( *pDstMed );
// xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
bAnyError |= (ERRCODE_NONE != xTargetDocShell->GetError());
if( bAnyError )
SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile.GetURL() );
else
SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile.GetURL() );
}
static bool lcl_SaveDoc(
const INetURLObject* pFileURL,
const std::shared_ptr<const SfxFilter>& pStoreToFilter,
const OUString* pStoreToFilterOptions,
const uno::Sequence< beans::PropertyValue >* pSaveToFilterData,
const bool bIsPDFexport,
SfxObjectShell* xObjectShell,
SwWrtShell& rWorkShell,
OUString * const decodedURL = nullptr )
{
OUString url = pFileURL->GetMainURL( INetURLObject::DecodeMechanism::NONE );
if( decodedURL )
(*decodedURL) = url;
SfxMedium* pDstMed = new SfxMedium( url, StreamMode::STD_READWRITE );
pDstMed->SetFilter( pStoreToFilter );
if( pDstMed->GetItemSet() )
{
if( pStoreToFilterOptions )
pDstMed->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS,
*pStoreToFilterOptions));
if( pSaveToFilterData->getLength() )
pDstMed->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA,
uno::makeAny(*pSaveToFilterData)));
}
// convert fields to text if we are exporting to PDF.
// this prevents a second merge while updating the fields
// in SwXTextDocument::getRendererCount()
if( bIsPDFexport )
rWorkShell.ConvertFieldsToText();
bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed);
// Actually this should be a bool... so in case of email and individual
// files, where this is set, we skip the recently used handling
bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL );
bAnyError |= (ERRCODE_NONE != xObjectShell->GetError());
if( bAnyError )
{
// error message ??
ErrorHandler::HandleError( xObjectShell->GetError() );
}
return !bAnyError;
}
static void lcl_PreparePrinterOptions(
const uno::Sequence< beans::PropertyValue >& rInPrintOptions,
uno::Sequence< beans::PropertyValue >& rOutPrintOptions)
{
// printing should be done synchronously otherwise the document
// might already become invalid during the process
const sal_Int32 nOffset = 1;
rOutPrintOptions.realloc( nOffset );
rOutPrintOptions[ 0 ].Name = "Wait";
rOutPrintOptions[ 0 ].Value <<= true;
// copy print options
const beans::PropertyValue* pOptions = rInPrintOptions.getConstArray();
for( sal_Int32 n = 0, nIndex = nOffset ; n < rInPrintOptions.getLength(); ++n)
{
if( pOptions[n].Name == "CopyCount" || pOptions[n].Name == "FileName"
|| pOptions[n].Name == "Collate" || pOptions[n].Name == "Pages"
|| pOptions[n].Name == "Wait" || pOptions[n].Name == "PrinterName" )
{
// add an option
rOutPrintOptions.realloc( nIndex + 1 );
rOutPrintOptions[ nIndex ].Name = pOptions[n].Name;
rOutPrintOptions[ nIndex++ ].Value = pOptions[n].Value ;
}
}
}
static SfxObjectShell* lcl_CreateWorkingDocument(
// input
const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
// optional input
const vcl::Window *pSourceWindow,
// optional in and output to swap the DB manager
SwDBManager** const ppDBManager,
// optional output
SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc )
{
const SwDoc *pSourceDoc = rSourceWrtShell.GetDoc();
SfxObjectShellRef xWorkObjectShell = pSourceDoc->CreateCopy( true, (aType == WorkingDocType::TARGET) );
SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell, SFX_INTERFACE_NONE );
if( pSourceWindow )
{
// the created window has to be located at the same position as the source window
vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow();
rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() );
}
SwView* pWorkView = static_cast< SwView* >( pWorkFrame->GetViewShell() );
SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr();
pWorkWrtShell->GetViewOptions()->SetIdle( false );
pWorkView->AttrChangedNotify( pWorkWrtShell );// in order for SelectShell to be called
SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );
if( aType == WorkingDocType::TARGET )
{
assert( !ppDBManager );
pWorkDoc->SetInMailMerge( true );
pWorkWrtShell->SetLabelDoc( false );
}
else
{
// We have to swap the DBmanager of the new doc, so we also need input
assert(ppDBManager && *ppDBManager);
SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
pWorkDoc->SetDBManager( *ppDBManager );
*ppDBManager = pWorkDBManager;
if( aType == WorkingDocType::SOURCE )
{
// the GetDBData call constructs the data, if it's missing - kind of const...
pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() );
// some DocumentSettings are currently not copied by SwDoc::CreateCopy
pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
pWorkDoc->getIDocumentState().ResetModified();
}
else
pWorkDoc->getIDocumentLinksAdministration().EmbedAllLinks();
}
if( pView ) *pView = pWorkView;
if( pWrtShell ) *pWrtShell = pWorkWrtShell;
if( pDoc ) *pDoc = pWorkDoc;
return xWorkObjectShell.get();
}
static SwMailMessage* lcl_CreateMailFromDoc(
const SwMergeDescriptor &rMergeDescriptor,
const OUString &sFileURL, const OUString &sMailRecipient,
const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding,
const OUString &sAttachmentMimeType )
{
SwMailMessage* pMessage = new SwMailMessage;
if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() )
pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
pMessage->addRecipient( sMailRecipient );
pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );
OUStringBuffer sBody;
if( rMergeDescriptor.bSendAsAttachment )
{
sBody = rMergeDescriptor.sMailBody;
mail::MailAttachment aAttach;
aAttach.Data = new SwMailTransferable( sFileURL,
rMergeDescriptor.sAttachmentName, sAttachmentMimeType );
aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
pMessage->addAttachment( aAttach );
}
else
{
//read in the temporary file and use it as mail body
SfxMedium aMedium( sFileURL, StreamMode::READ );
SvStream* pInStream = aMedium.GetInStream();
assert( pInStream && "no output file created?" );
if( !pInStream )
return pMessage;
pInStream->SetStreamCharSet( sMailEncoding );
OString sLine;
while ( pInStream->ReadLine( sLine ) )
{
sBody.append(OStringToOUString( sLine, sMailEncoding ));
sBody.append("\n");
}
}
pMessage->setSubject( rMergeDescriptor.sSubject );
uno::Reference< datatransfer::XTransferable> xBody =
new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType );
pMessage->setBody( xBody );
for( const OUString& sCcRecipient : rMergeDescriptor.aCopiesTo )
pMessage->addCcRecipient( sCcRecipient );
for( const OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo )
pMessage->addBccRecipient( sBccRecipient );
return pMessage;
}
class SwDBManager::MailDispatcherListener_Impl : public IMailDispatcherListener
{
SwDBManager &m_rDBManager;
public:
explicit MailDispatcherListener_Impl( SwDBManager &rDBManager )
: m_rDBManager( rDBManager ) {}
virtual void started( ::rtl::Reference<MailDispatcher> ) override {};
virtual void stopped( ::rtl::Reference<MailDispatcher> ) override {};
virtual void idle( ::rtl::Reference<MailDispatcher> ) override {};
virtual void mailDelivered( ::rtl::Reference<MailDispatcher>,
uno::Reference< mail::XMailMessage> xMessage ) override
{
osl::MutexGuard aGuard( m_rDBManager.pImpl->m_aAllEmailSendMutex );
if ( m_rDBManager.pImpl->m_xLastMessage == xMessage )
m_rDBManager.pImpl->m_xLastMessage.clear();
}
virtual void mailDeliveryError( ::rtl::Reference<MailDispatcher> xMailDispatcher,
uno::Reference< mail::XMailMessage>, const OUString& ) override
{
osl::MutexGuard aGuard( m_rDBManager.pImpl->m_aAllEmailSendMutex );
m_rDBManager.m_aMergeStatus = MergeStatus::Error;
m_rDBManager.pImpl->m_xLastMessage.clear();
xMailDispatcher->stop();
}
};
/**
* Please have a look at the README in the same directory, before you make
* larger changes in this function!
*/
bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
const SwMergeDescriptor& rMergeDescriptor)
{
// deconstruct mail merge type for better readability.
// uppercase naming is intentional!
const bool bMT_EMAIL = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
const bool bMT_SHELL = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER;
const bool bMT_FILE = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;
//check if the doc is synchronized and contains at least one linked section
const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1;
const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE );
const bool bIsMergeSilent = IsMergeSilent();
bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile;
OUString sPrefix_ = rMergeDescriptor.sPrefix;
if( bMT_EMAIL )
{
assert( !rMergeDescriptor.bPrefixIsFilename );
assert(!bCheckSingleFile_);
bCheckSingleFile_ = false;
}
else if( bMT_SHELL || bMT_PRINTER )
{
assert(bCheckSingleFile_);
bCheckSingleFile_ = true;
assert(sPrefix_.isEmpty());
sPrefix_.clear();
}
const bool bCreateSingleFile = bCheckSingleFile_;
const OUString sDescriptorPrefix = sPrefix_;
// Setup for dumping debugging documents
static const char *sMaxDumpDocs = nullptr;
static sal_Int32 nMaxDumpDocs = 0;
if (!sMaxDumpDocs)
{
sMaxDumpDocs = getenv("SW_DEBUG_MAILMERGE_DOCS");
if (!sMaxDumpDocs)
sMaxDumpDocs = "";
else
nMaxDumpDocs = OUString(sMaxDumpDocs, strlen(sMaxDumpDocs), osl_getThreadTextEncoding()).toInt32();
}
::rtl::Reference< MailDispatcher > xMailDispatcher;
::rtl::Reference< IMailDispatcherListener > xMailListener;
OUString sMailBodyMimeType;
rtl_TextEncoding sMailEncoding = ::osl_getThreadTextEncoding();
uno::Reference< beans::XPropertySet > xColumnProp;
// Check for (mandatory) email or (optional) filename column
SwDBFormatData aColumnDBFormat;
bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty();
if( ! bColumnName )
{
if( bMT_EMAIL )
return false;
}
else
{
uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) )
return false;
uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn );
aCol >>= xColumnProp;
aColumnDBFormat.xFormatter = pImpl->pMergeData->xFormatter;
aColumnDBFormat.aNullDate = pImpl->pMergeData->aNullDate;
if( bMT_EMAIL )
{
// Reset internal mail accounting data
pImpl->m_xLastMessage.clear();
xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) );
xMailListener = new MailDispatcherListener_Impl( *this );
xMailDispatcher->addListener( xMailListener );
if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
{
sMailBodyMimeType = "text/html; charset=";
sMailBodyMimeType += OUString::createFromAscii(
rtl_getBestMimeCharsetFromTextEncoding( sMailEncoding ));
SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
sMailEncoding = rHtmlOptions.GetTextEncoding();
}
else
sMailBodyMimeType = "text/plain; charset=UTF-8; format=flowed";
}
}
SwDocShell *pSourceDocSh = pSourceShell->GetView().GetDocShell();
// setup the output format
std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
pSourceDocSh->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
const OUString* pStoreToFilterOptions = nullptr;
// if a save_to filter is set then use it - otherwise use the default
if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
{
OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt");
pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
}
else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
{
std::shared_ptr<const SfxFilter> pFilter =
pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
if(pFilter)
{
pStoreToFilter = pFilter;
if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
}
}
const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export";
m_aMergeStatus = MergeStatus::Ok;
// in case of creating a single resulting file this has to be created here
SwView* pTargetView = rMergeDescriptor.pMailMergeConfigItem ?
rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
SwWrtShell* pTargetShell = nullptr;
SwDoc* pTargetDoc = nullptr;
SfxObjectShellRef xTargetDocShell;
std::unique_ptr< utl::TempFile > aTempFile;
sal_uInt16 nStartingPageNo = 0;
vcl::Window *pSourceWindow = nullptr;
std::shared_ptr<weld::GenericDialogController> xProgressDlg;
if( !bIsMergeSilent )
{
// construct the process dialog
pSourceWindow = &pSourceShell->GetView().GetEditWin();
if (!bMT_PRINTER)
xProgressDlg.reset(new CreateMonitor(pSourceWindow->GetFrameWeld()));
else
{
xProgressDlg.reset(new PrintMonitor(pSourceWindow->GetFrameWeld()));
static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
pSourceDocSh->GetTitle(22));
}
weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
if (nResult == RET_CANCEL)
MergeCancel();
xProgressDlg.reset();
});
Application::Reschedule( true );
}
if( bCreateSingleFile && !pTargetView )
{
// create a target docshell to put the merged document into
xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
*pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
nullptr, &pTargetView, &pTargetShell, &pTargetDoc );
// import current print settings
const SwPrintData &rPrintData = pSourceShell->getIDocumentDeviceAccess().getPrintData();
pTargetDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);
if (nMaxDumpDocs)
lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
}
else if( pTargetView )
{
pTargetShell = pTargetView->GetWrtShellPtr();
pTargetDoc = pTargetShell->GetDoc();
xTargetDocShell = pTargetView->GetDocShell();
}
if( bCreateSingleFile )
{
// determine the page style and number used at the start of the source document
pSourceShell->SttEndDoc(true);
nStartingPageNo = pSourceShell->GetVirtPageNum();
}
// Progress, to prohibit KeyInputs
SfxProgress aProgress(pSourceDocSh, ::aEmptyOUStr, 1);
// lock all dispatchers
SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
while (pViewFrame)
{
pViewFrame->GetDispatcher()->Lock(true);
pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
}
sal_Int32 nDocNo = 1;
// For single file mode, the number of pages in the target document so far, which is used
// by AppendDoc() to adjust position of page-bound objects. Getting this information directly
// from the target doc would require repeated layouts of the doc, which is expensive, but
// it can be manually computed from the source documents (for which we do layouts, so the page
// count is known, and there is a blank page between each of them in the target document).
int targetDocPageCount = 0;
if( !bIsMergeSilent && !bMT_PRINTER )
{
sal_Int32 nRecordCount = 1;
lcl_getCountFromResultSet( nRecordCount, pImpl->pMergeData );
// Synchronized docs don't auto-advance the record set, but there is a
// "security" check, which will always advance the record set, if there
// is no "next record" field in a synchronized doc => nRecordPerDoc > 0
sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
->getIDocumentFieldsAccess().GetRecordsPerDocument();
if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
--nRecordPerDoc;
assert( nRecordPerDoc > 0 );
sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
if ( 0 != nRecordCount % nRecordPerDoc )
nMaxDocs += 1;
static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
}
long nStartRow, nEndRow;
bool bFreezedLayouts = false;
// to collect temporary email files
std::vector< OUString> aFilesToRemove;
// The SfxObjectShell will be closed explicitly later but
// it is more safe to use SfxObjectShellLock here
SfxObjectShellLock xWorkDocSh;
SwView* pWorkView = nullptr;
SwDoc* pWorkDoc = nullptr;
SwDBManager* pWorkDocOrigDBManager = nullptr;
SwWrtShell* pWorkShell = nullptr;
bool bWorkDocInitialized = false;
do
{
nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
OUString sColumnData;
// Read the indicated data column, which should contain a valid mail
// address or an optional file name
if( bMT_EMAIL || bColumnName )
{
sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
}
// create a new temporary file name - only done once in case of bCreateSingleFile
if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
{
OUString sPrefix = sDescriptorPrefix;
OUString sLeading;
//#i97667# if the name is from a database field then it will be used _as is_
if( bColumnName && !bMT_EMAIL )
{
if (!sColumnData.isEmpty())
sLeading = sColumnData;
else
sLeading = "_";
}
else
{
INetURLObject aEntry( sPrefix );
sLeading = aEntry.GetBase();
aEntry.removeSegment();
sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
}
OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPrefix, true) );
if( !aTempFile->IsValid() )
{
ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
m_aMergeStatus = MergeStatus::Error;
}
}
if( IsMergeOk() )
{
std::unique_ptr< INetURLObject > aTempFileURL;
if( bNeedsTempFiles )
aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
if( !bIsMergeSilent ) {
if( !bMT_PRINTER )
static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
else {
PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
OUString sStat( SwResId(STR_STATSTR_LETTER) );
sStat += " " + OUString::number( nDocNo );
pPrintMonDlg->m_xPrintInfo->set_label(sStat);
}
//TODO xProgressDlg->queue_draw();
}
Application::Reschedule( true );
// Create a copy of the source document and work with that one instead of the source.
// If we're not in the single file mode (which requires modifying the document for the merging),
// it is enough to do this just once. Currently PDF also has to be treated special.
if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport )
{
assert( !xWorkDocSh.Is());
pWorkDocOrigDBManager = this;
xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
*pSourceShell, nullptr, &pWorkDocOrigDBManager,
&pWorkView, &pWorkShell, &pWorkDoc );
if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
// #i69458# lock fields to prevent access to the result set while calculating layout
// tdf#92324: and do not unlock: keep document locked during printing to avoid
// ExpFields update during printing, generation of preview, etc.
pWorkShell->LockExpFields();
pWorkShell->CalcLayout();
}
lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);
// tdf#92324: Allow ExpFields update only by explicit instruction to avoid
// database cursor movement on any other fields update, for example during
// print preview and other operations
if ( pWorkShell->IsExpFieldsLocked() )
pWorkShell->UnlockExpFields();
pWorkShell->SwViewShell::UpdateFields();
pWorkShell->LockExpFields();
lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);
// also emit MailMergeEvent on XInterface if possible
const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
if(pEvtSrc)
{
uno::Reference< uno::XInterface > xRef(
static_cast<text::XMailMergeBroadcaster*>(const_cast<SwXMailMerge*>(pEvtSrc)) );
text::MailMergeEvent aEvt( xRef, xWorkDocSh->GetModel() );
pEvtSrc->LaunchMailMergeEvent( aEvt );
}
// working copy is merged - prepare final steps depending on merge options
if( bCreateSingleFile )
{
assert( pTargetShell && "no target shell available!" );
// prepare working copy and target to append
pWorkDoc->RemoveInvisibleContent();
pWorkShell->ConvertFieldsToText();
pWorkShell->SetNumberingRestart();
if( bSynchronizedDoc )
{
lcl_RemoveSectionLinks( *pWorkShell );
}
if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
// append the working document to the target document
if( targetDocPageCount % 2 == 1 )
++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
// ensure layout is up to date in order to get correct page count
pWorkShell->CalcLayout();
targetDocPageCount += pWorkShell->GetPageCnt();
if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
if (bMT_SHELL)
{
SwDocMergeInfo aMergeInfo;
// Name of the mark is actually irrelevant, UNO bookmarks have internals names.
aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
appendedDocStart, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
::sw::mark::InsertMode::New);
aMergeInfo.nDBRow = nStartRow;
rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
}
}
else
{
assert( bNeedsTempFiles );
assert( pWorkShell->IsExpFieldsLocked() );
// fields are locked, so it's fine to
// restore the old / empty DB manager for save
pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
// save merged document
OUString sFileURL;
if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
&rMergeDescriptor.aSaveToFilterData, bIsPDFexport,
xWorkDocSh, *pWorkShell, &sFileURL ) )
{
m_aMergeStatus = MergeStatus::Error;
}
// back to the MM DB manager
pWorkDoc->SetDBManager( this );
if( bMT_EMAIL && !IsMergeError() )
{
// schedule file for later removal
aFilesToRemove.push_back( sFileURL );
if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
{
OSL_FAIL("invalid e-Mail address in database column");
}
else
{
uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
sMailEncoding, pStoreToFilter->GetMimeType() );
if( xMessage.is() )
{
osl::MutexGuard aGuard( pImpl->m_aAllEmailSendMutex );
pImpl->m_xLastMessage.set( xMessage );
xMailDispatcher->enqueueMailMessage( xMessage );
if( !xMailDispatcher->isStarted() )
xMailDispatcher->start();
}
}
}
}
if( bCreateSingleFile || bIsPDFexport )
{
pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
xWorkDocSh->DoClose();
xWorkDocSh = nullptr;
}
}
bWorkDocInitialized = true;
nDocNo++;
nEndRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
// Freeze the layouts of the target document after the first inserted
// sub-document, to get the correct PageDesc.
if(!bFreezedLayouts && bCreateSingleFile)
{
for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
aLayout->FreezeLayout(true);
bFreezedLayouts = true;
}
} while( IsMergeOk() &&
((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));
if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
{
// Unlock document fields after merge complete
pWorkView->GetWrtShell().UnlockExpFields();
}
if( !bCreateSingleFile )
{
if( bMT_PRINTER )
Printer::FinishPrintJob( pWorkView->GetPrinterController());
if( !bIsPDFexport )
{
pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
xWorkDocSh->DoClose();
}
}
else if( IsMergeOk() ) // && bCreateSingleFile
{
Application::Reschedule( true );
// sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
// unique fly names, do it here once.
pTargetDoc->SetInMailMerge(false);
pTargetDoc->SetAllUniqueFlyNames();
// Unfreeze target document layouts and correct all PageDescs.
SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
pTargetShell->CalcLayout();
SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
pTargetShell->GetViewOptions()->SetIdle( true );
pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
{
aLayout->FreezeLayout(false);
aLayout->AllCheckPageDescs();
}
Application::Reschedule( true );
if( IsMergeOk() && bMT_FILE )
{
// save merged document
assert( aTempFile.get() );
INetURLObject aTempFileURL;
if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
aTempFileURL.SetURL( aTempFile->GetURL() );
else
{
aTempFileURL.SetURL(sDescriptorPrefix);
// remove the unneeded temporary file
aTempFile->EnableKillingFile();
}
if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
{
m_aMergeStatus = MergeStatus::Error;
}
}
else if( IsMergeOk() && bMT_PRINTER )
{
// print the target document
uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
}
}
// we also show canceled documents, as long as there was no error
if( !IsMergeError() && bMT_SHELL )
// leave docshell available for caller (e.g. MM wizard)
rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
else if( xTargetDocShell.is() )
xTargetDocShell->DoClose();
Application::Reschedule( true );
if (xProgressDlg)
{
xProgressDlg->response(RET_OK);
}
// unlock all dispatchers
pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
while (pViewFrame)
{
pViewFrame->GetDispatcher()->Lock(false);
pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
}
SW_MOD()->SetView(&pSourceShell->GetView());
if( xMailDispatcher.is() )
{
if( IsMergeOk() )
{
// TODO: Instead of polling via an AutoTimer, post an Idle event,
// if the main loop has been made thread-safe.
AutoTimer aEmailDispatcherPollTimer;
aEmailDispatcherPollTimer.SetDebugName(
"sw::SwDBManager aEmailDispatcherPollTimer" );
aEmailDispatcherPollTimer.SetTimeout( 500 );
aEmailDispatcherPollTimer.Start();
while( IsMergeOk() && pImpl->m_xLastMessage.is() )
Application::Yield();
aEmailDispatcherPollTimer.Stop();
}
xMailDispatcher->stop();
xMailDispatcher->shutdown();
}
// remove the temporary files
// has to be done after xMailDispatcher is finished, as mails may be
// delivered as message attachments!
for( const OUString &sFileURL : aFilesToRemove )
SWUnoHelper::UCB_DeleteFile( sFileURL );
return !IsMergeError();
}
void SwDBManager::MergeCancel()
{
if (m_aMergeStatus < MergeStatus::Cancel)
m_aMergeStatus = MergeStatus::Cancel;
}
// determine the column's Numberformat and transfer to the forwarded Formatter,
// if applicable.
sal_uLong SwDBManager::GetColumnFormat( const OUString& rDBName,
const OUString& rTableName,
const OUString& rColNm,
SvNumberFormatter* pNFormatr,
LanguageType nLanguage )
{
sal_uLong nRet = 0;
if(pNFormatr)
{
uno::Reference< sdbc::XDataSource> xSource;
uno::Reference< sdbc::XConnection> xConnection;
bool bUseMergeData = false;
uno::Reference< sdbcx::XColumnsSupplier> xColsSupp;
bool bDisposeConnection = false;
if(pImpl->pMergeData &&
((pImpl->pMergeData->sDataSource == rDBName && pImpl->pMergeData->sCommand == rTableName) ||
(rDBName.isEmpty() && rTableName.isEmpty())))
{
xConnection = pImpl->pMergeData->xConnection;
xSource = SwDBManager::getDataSourceAsParent(xConnection,rDBName);
bUseMergeData = true;
xColsSupp.set(pImpl->pMergeData->xResultSet, css::uno::UNO_QUERY);
}
if(!xConnection.is())
{
SwDBData aData;
aData.sDataSource = rDBName;
aData.sCommand = rTableName;
aData.nCommandType = -1;
SwDSParam* pParam = FindDSData(aData, false);
if(pParam && pParam->xConnection.is())
{
xConnection = pParam->xConnection;
xColsSupp.set(pParam->xResultSet, css::uno::UNO_QUERY);
}
else
{
xConnection = RegisterConnection( rDBName );
bDisposeConnection = true;
}
if(bUseMergeData)
pImpl->pMergeData->xConnection = xConnection;
}
bool bDispose = !xColsSupp.is();
if(bDispose)
{
xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
}
if(xColsSupp.is())
{
uno::Reference<container::XNameAccess> xCols;
try
{
xCols = xColsSupp->getColumns();
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "Exception in getColumns(): " << e);
}
if(!xCols.is() || !xCols->hasByName(rColNm))
return nRet;
uno::Any aCol = xCols->getByName(rColNm);
uno::Reference< beans::XPropertySet > xColumn;
aCol >>= xColumn;
nRet = GetColumnFormat(xSource, xConnection, xColumn, pNFormatr, nLanguage);
if(bDispose)
{
::comphelper::disposeComponent( xColsSupp );
}
if(bDisposeConnection)
{
::comphelper::disposeComponent( xConnection );
}
}
else
nRet = pNFormatr->GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_SYSTEM );
}
return nRet;
}
sal_uLong SwDBManager::GetColumnFormat( uno::Reference< sdbc::XDataSource> const & xSource_in,
uno::Reference< sdbc::XConnection> const & xConnection,
uno::Reference< beans::XPropertySet> const & xColumn,
SvNumberFormatter* pNFormatr,
LanguageType nLanguage )
{
auto xSource = xSource_in;
// set the NumberFormat in the doc if applicable
sal_uLong nRet = 0;
if(!xSource.is())
{
uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
if ( xChild.is() )
xSource.set(xChild->getParent(), uno::UNO_QUERY);
}
if(xSource.is() && xConnection.is() && xColumn.is() && pNFormatr)
{
SvNumberFormatsSupplierObj* pNumFormat = new SvNumberFormatsSupplierObj( pNFormatr );
uno::Reference< util::XNumberFormatsSupplier > xDocNumFormatsSupplier = pNumFormat;
uno::Reference< util::XNumberFormats > xDocNumberFormats = xDocNumFormatsSupplier->getNumberFormats();
uno::Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, uno::UNO_QUERY);
css::lang::Locale aLocale( LanguageTag( nLanguage ).getLocale());
//get the number formatter of the data source
uno::Reference<beans::XPropertySet> xSourceProps(xSource, uno::UNO_QUERY);
uno::Reference< util::XNumberFormats > xNumberFormats;
if(xSourceProps.is())
{
uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
if(aFormats.hasValue())
{
uno::Reference<util::XNumberFormatsSupplier> xSuppl;
aFormats >>= xSuppl;
if(xSuppl.is())
{
xNumberFormats = xSuppl->getNumberFormats();
}
}
}
bool bUseDefault = true;
try
{
uno::Any aFormatKey = xColumn->getPropertyValue("FormatKey");
if(aFormatKey.hasValue())
{
sal_Int32 nFormat = 0;
aFormatKey >>= nFormat;
if(xNumberFormats.is())
{
try
{
uno::Reference<beans::XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat );
uno::Any aFormatString = xNumProps->getPropertyValue("FormatString");
uno::Any aLocaleVal = xNumProps->getPropertyValue("Locale");
OUString sFormat;
aFormatString >>= sFormat;
lang::Locale aLoc;
aLocaleVal >>= aLoc;
nFormat = xDocNumberFormats->queryKey( sFormat, aLoc, false );
if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFormat))
nFormat = xDocNumberFormats->addNew( sFormat, aLoc );
nRet = nFormat;
bUseDefault = false;
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "illegal number format key: " << e);
}
}
}
}
catch(const uno::Exception&)
{
SAL_WARN("sw.mailmerge", "no FormatKey property found");
}
if(bUseDefault)
nRet = dbtools::getDefaultNumberFormat(xColumn, xDocNumberFormatTypes, aLocale);
}
return nRet;
}
sal_Int32 SwDBManager::GetColumnType( const OUString& rDBName,
const OUString& rTableName,
const OUString& rColNm )
{
sal_Int32 nRet = sdbc::DataType::SQLNULL;
SwDBData aData;
aData.sDataSource = rDBName;
aData.sCommand = rTableName;
aData.nCommandType = -1;
SwDSParam* pParam = FindDSData(aData, false);
uno::Reference< sdbc::XConnection> xConnection;
uno::Reference< sdbcx::XColumnsSupplier > xColsSupp;
bool bDispose = false;
if(pParam && pParam->xConnection.is())
{
xConnection = pParam->xConnection;
xColsSupp.set( pParam->xResultSet, uno::UNO_QUERY );
}
else
{
xConnection = RegisterConnection( rDBName );
}
if( !xColsSupp.is() )
{
xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
bDispose = true;
}
if(xColsSupp.is())
{
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
if(xCols->hasByName(rColNm))
{
uno::Any aCol = xCols->getByName(rColNm);
uno::Reference<beans::XPropertySet> xCol;
aCol >>= xCol;
uno::Any aType = xCol->getPropertyValue("Type");
aType >>= nRet;
}
if(bDispose)
::comphelper::disposeComponent( xColsSupp );
}
return nRet;
}
uno::Reference< sdbc::XConnection> SwDBManager::GetConnection(const OUString& rDataSource,
uno::Reference<sdbc::XDataSource>& rxSource)
{
uno::Reference< sdbc::XConnection> xConnection;
uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
try
{
uno::Reference<sdb::XCompletedConnection> xComplConnection(dbtools::getDataSource(rDataSource, xContext), uno::UNO_QUERY);
if ( xComplConnection.is() )
{
rxSource.set(xComplConnection, uno::UNO_QUERY);
uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
xConnection = xComplConnection->connectWithCompletion( xHandler );
}
}
catch(const uno::Exception&)
{
}
return xConnection;
}
uno::Reference< sdbcx::XColumnsSupplier> SwDBManager::GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection,
const OUString& rTableOrQuery,
SwDBSelect eTableOrQuery)
{
uno::Reference< sdbcx::XColumnsSupplier> xRet;
try
{
if(eTableOrQuery == SwDBSelect::UNKNOWN)
{
//search for a table with the given command name
uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
if(xTSupplier.is())
{
uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
eTableOrQuery = xTables->hasByName(rTableOrQuery) ?
SwDBSelect::TABLE : SwDBSelect::QUERY;
}
}
sal_Int32 nCommandType = SwDBSelect::TABLE == eTableOrQuery ?
sdb::CommandType::TABLE : sdb::CommandType::QUERY;
uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
uno::Reference<sdbc::XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY);
OUString sDataSource;
uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection, sDataSource);
uno::Reference<beans::XPropertySet> xSourceProperties(xSource, uno::UNO_QUERY);
if(xSourceProperties.is())
{
xSourceProperties->getPropertyValue("Name") >>= sDataSource;
}
uno::Reference<beans::XPropertySet> xRowProperties(xRowSet, uno::UNO_QUERY);
xRowProperties->setPropertyValue("DataSourceName", uno::makeAny(sDataSource));
xRowProperties->setPropertyValue("Command", uno::makeAny(rTableOrQuery));
xRowProperties->setPropertyValue("CommandType", uno::makeAny(nCommandType));
xRowProperties->setPropertyValue("FetchSize", uno::makeAny(sal_Int32(10)));
xRowProperties->setPropertyValue("ActiveConnection", uno::makeAny(xConnection));
xRowSet->execute();
xRet.set( xRowSet, uno::UNO_QUERY );
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier: " << e);
}
return xRet;
}
OUString SwDBManager::GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps,
const SwDBFormatData& rDBFormatData,
double* pNumber)
{
uno::Reference< sdb::XColumn > xColumn(xColumnProps, uno::UNO_QUERY);
OUString sRet;
assert( xColumn.is() && "SwDBManager::::ImportDBField: illegal arguments" );
if(!xColumn.is())
return sRet;
uno::Any aType = xColumnProps->getPropertyValue("Type");
sal_Int32 eDataType = sdbc::DataType::SQLNULL;
aType >>= eDataType;
switch(eDataType)
{
case sdbc::DataType::CHAR:
case sdbc::DataType::VARCHAR:
case sdbc::DataType::LONGVARCHAR:
try
{
sRet = xColumn->getString();
sRet = sRet.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
}
catch(const sdbc::SQLException&)
{
}
break;
case sdbc::DataType::BIT:
case sdbc::DataType::BOOLEAN:
case sdbc::DataType::TINYINT:
case sdbc::DataType::SMALLINT:
case sdbc::DataType::INTEGER:
case sdbc::DataType::BIGINT:
case sdbc::DataType::FLOAT:
case sdbc::DataType::REAL:
case sdbc::DataType::DOUBLE:
case sdbc::DataType::NUMERIC:
case sdbc::DataType::DECIMAL:
case sdbc::DataType::DATE:
case sdbc::DataType::TIME:
case sdbc::DataType::TIMESTAMP:
{
try
{
sRet = dbtools::DBTypeConversion::getFormattedValue(
xColumnProps,
rDBFormatData.xFormatter,
rDBFormatData.aLocale,
rDBFormatData.aNullDate);
if (pNumber)
{
double fVal = xColumn->getDouble();
if(!xColumn->wasNull())
{
*pNumber = fVal;
}
}
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "exception caught: " << e);
}
}
break;
}
return sRet;
}
// checks if a desired data source table or query is open
bool SwDBManager::IsDataSourceOpen(const OUString& rDataSource,
const OUString& rTableOrQuery, bool bMergeShell)
{
if(pImpl->pMergeData)
{
return ((rDataSource == pImpl->pMergeData->sDataSource
&& rTableOrQuery == pImpl->pMergeData->sCommand)
|| (rDataSource.isEmpty() && rTableOrQuery.isEmpty()))
&& pImpl->pMergeData->xResultSet.is();
}
else if(!bMergeShell)
{
SwDBData aData;
aData.sDataSource = rDataSource;
aData.sCommand = rTableOrQuery;
aData.nCommandType = -1;
SwDSParam* pFound = FindDSData(aData, false);
return (pFound && pFound->xResultSet.is());
}
return false;
}
// read column data at a specified position
bool SwDBManager::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName,
const OUString& rColumnName, sal_uInt32 nAbsRecordId,
LanguageType nLanguage,
OUString& rResult, double* pNumber)
{
bool bRet = false;
SwDSParam* pFound = nullptr;
//check if it's the merge data source
if(pImpl->pMergeData &&
rSourceName == pImpl->pMergeData->sDataSource &&
rTableName == pImpl->pMergeData->sCommand)
{
pFound = pImpl->pMergeData;
}
else
{
SwDBData aData;
aData.sDataSource = rSourceName;
aData.sCommand = rTableName;
aData.nCommandType = -1;
pFound = FindDSData(aData, false);
}
if (!pFound)
return false;
//check validity of supplied record Id
if(pFound->aSelection.getLength())
{
//the destination has to be an element of the selection
const uno::Any* pSelection = pFound->aSelection.getConstArray();
bool bFound = false;
for(sal_Int32 nPos = 0; !bFound && nPos < pFound->aSelection.getLength(); nPos++)
{
sal_Int32 nSelection = 0;
pSelection[nPos] >>= nSelection;
if(nSelection == static_cast<sal_Int32>(nAbsRecordId))
bFound = true;
}
if(!bFound)
return false;
}
if( pFound->HasValidRecord() )
{
sal_Int32 nOldRow = 0;
try
{
nOldRow = pFound->xResultSet->getRow();
}
catch(const uno::Exception&)
{
return false;
}
//position to the desired index
bool bMove = true;
if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
bMove = lcl_MoveAbsolute(pFound, nAbsRecordId);
if(bMove)
bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber);
if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
lcl_MoveAbsolute(pFound, nOldRow);
}
return bRet;
}
// reads the column data at the current position
bool SwDBManager::GetMergeColumnCnt(const OUString& rColumnName, LanguageType nLanguage,
OUString &rResult, double *pNumber)
{
if( !IsValidMergeRecord() )
{
rResult.clear();
return false;
}
bool bRet = lcl_GetColumnCnt(pImpl->pMergeData, rColumnName, nLanguage, rResult, pNumber);
return bRet;
}
bool SwDBManager::ToNextMergeRecord()
{
assert( pImpl->pMergeData && pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
return lcl_ToNextRecord( pImpl->pMergeData );
}
bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter *pDocFormatter,
LanguageType nLanguage, SwCalc &rCalc )
{
if( !IsValidMergeRecord() )
return false;
uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
if( !xColsSupp.is() )
return false;
{
uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
const uno::Sequence<OUString> aColNames = xCols->getElementNames();
const OUString* pColNames = aColNames.getConstArray();
OUString aString;
// add the "record number" variable, as SwCalc::VarLook would.
rCalc.VarChange( GetAppCharClass().lowercase(
SwFieldType::GetTypeStr(TYP_DBSETNUMBERFLD) ), GetSelectedRecordId() );
for( int nCol = 0; nCol < aColNames.getLength(); nCol++ )
{
// get the column type
sal_Int32 nColumnType = sdbc::DataType::SQLNULL;
uno::Any aCol = xCols->getByName( pColNames[nCol] );
uno::Reference<beans::XPropertySet> xColumnProps;
aCol >>= xColumnProps;
uno::Any aType = xColumnProps->getPropertyValue( "Type" );
aType >>= nColumnType;
double aNumber = DBL_MAX;
lcl_GetColumnCnt( pImpl->pMergeData, xColumnProps, nLanguage, aString, &aNumber );
sal_uInt32 nFormat = GetColumnFormat( pImpl->pMergeData->sDataSource,
pImpl->pMergeData->sCommand,
pColNames[nCol], pDocFormatter, nLanguage );
// aNumber is overwritten by SwDBField::FormatValue, so store initial status
bool colIsNumber = aNumber != DBL_MAX;
bool bValidValue = SwDBField::FormatValue( pDocFormatter, aString, nFormat,
aNumber, nColumnType );
if( colIsNumber )
{
if( bValidValue )
{
SwSbxValue aValue;
aValue.PutDouble( aNumber );
aValue.SetDBvalue( true );
SAL_INFO( "sw.ui", "'" << pColNames[nCol] << "': " << aNumber << " / " << aString );
rCalc.VarChange( pColNames[nCol], aValue );
}
}
else
{
SwSbxValue aValue;
aValue.PutString( aString );
aValue.SetDBvalue( true );
SAL_INFO( "sw.ui", "'" << pColNames[nCol] << "': " << aString );
rCalc.VarChange( pColNames[nCol], aValue );
}
}
}
return true;
}
void SwDBManager::ToNextRecord(
const OUString& rDataSource, const OUString& rCommand)
{
SwDSParam* pFound = nullptr;
if(pImpl->pMergeData &&
rDataSource == pImpl->pMergeData->sDataSource &&
rCommand == pImpl->pMergeData->sCommand)
{
pFound = pImpl->pMergeData;
}
else
{
SwDBData aData;
aData.sDataSource = rDataSource;
aData.sCommand = rCommand;
aData.nCommandType = -1;
pFound = FindDSData(aData, false);
}
lcl_ToNextRecord( pFound );
}
static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action )
{
bool bRet = true;
assert( SwDBNextRecord::NEXT == action ||
(SwDBNextRecord::FIRST == action && pParam) );
if( nullptr == pParam )
return false;
if( action == SwDBNextRecord::FIRST )
{
pParam->nSelectionIndex = 0;
pParam->bEndOfDB = false;
}
if( !pParam->HasValidRecord() )
return false;
try
{
if( pParam->aSelection.getLength() )
{
if( pParam->nSelectionIndex >= pParam->aSelection.getLength() )
pParam->bEndOfDB = true;
else
{
sal_Int32 nPos = 0;
pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
}
}
else if( action == SwDBNextRecord::FIRST )
{
pParam->bEndOfDB = !pParam->xResultSet->first();
}
else
{
sal_Int32 nBefore = pParam->xResultSet->getRow();
pParam->bEndOfDB = !pParam->xResultSet->next();
if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow() )
{
// next returned true but it didn't move
::dbtools::throwFunctionSequenceException( pParam->xResultSet );
}
}
++pParam->nSelectionIndex;
bRet = !pParam->bEndOfDB;
}
catch( const uno::Exception &e )
{
pParam->bEndOfDB = true;
bRet = false;
// we allow merging with empty databases, so don't warn on init
SAL_WARN_IF(action == SwDBNextRecord::NEXT,
"sw.mailmerge", "exception in ToNextRecord(): " << e);
}
return bRet;
}
// synchronized labels contain a next record field at their end
// to assure that the next page can be created in mail merge
// the cursor position must be validated
bool SwDBManager::IsValidMergeRecord() const
{
return( pImpl->pMergeData && pImpl->pMergeData->HasValidRecord() );
}
sal_uInt32 SwDBManager::GetSelectedRecordId()
{
sal_uInt32 nRet = 0;
assert( pImpl->pMergeData &&
pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
if(!pImpl->pMergeData || !pImpl->pMergeData->xResultSet.is())
return 0;
try
{
nRet = pImpl->pMergeData->xResultSet->getRow();
}
catch(const uno::Exception&)
{
}
return nRet;
}
bool SwDBManager::ToRecordId(sal_Int32 nSet)
{
assert( pImpl->pMergeData &&
pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
if(!pImpl->pMergeData || !pImpl->pMergeData->xResultSet.is()|| nSet < 0)
return false;
bool bRet = false;
sal_Int32 nAbsPos = nSet;
if(nAbsPos >= 0)
{
bRet = lcl_MoveAbsolute(pImpl->pMergeData, nAbsPos);
pImpl->pMergeData->bEndOfDB = !bRet;
}
return bRet;
}
bool SwDBManager::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery)
{
SwDBData aData;
aData.sDataSource = rDataSource;
aData.sCommand = rTableOrQuery;
aData.nCommandType = -1;
SwDSParam* pFound = FindDSData(aData, true);
if(pFound->xResultSet.is())
return true;
SwDSParam* pParam = FindDSConnection(rDataSource, false);
if(pParam && pParam->xConnection.is())
pFound->xConnection = pParam->xConnection;
if(pFound->xConnection.is())
{
try
{
uno::Reference< sdbc::XDatabaseMetaData > xMetaData = pFound->xConnection->getMetaData();
try
{
pFound->bScrollable = xMetaData
->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE));
}
catch(const uno::Exception&)
{
// DB driver may not be ODBC 3.0 compliant
pFound->bScrollable = true;
}
pFound->xStatement = pFound->xConnection->createStatement();
OUString aQuoteChar = xMetaData->getIdentifierQuoteString();
OUString sStatement("SELECT * FROM ");
sStatement = "SELECT * FROM ";
sStatement += aQuoteChar;
sStatement += rTableOrQuery;
sStatement += aQuoteChar;
pFound->xResultSet = pFound->xStatement->executeQuery( sStatement );
//after executeQuery the cursor must be positioned
pFound->bEndOfDB = !pFound->xResultSet->next();
++pFound->nSelectionIndex;
}
catch (const uno::Exception&)
{
pFound->xResultSet = nullptr;
pFound->xStatement = nullptr;
pFound->xConnection = nullptr;
}
}
return pFound->xResultSet.is();
}
uno::Reference< sdbc::XConnection> const & SwDBManager::RegisterConnection(OUString const& rDataSource)
{
SwDSParam* pFound = SwDBManager::FindDSConnection(rDataSource, true);
uno::Reference< sdbc::XDataSource> xSource;
if(!pFound->xConnection.is())
{
pFound->xConnection = SwDBManager::GetConnection(rDataSource, xSource );
try
{
uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
if(xComponent.is())
xComponent->addEventListener(pImpl->m_xDisposeListener.get());
}
catch(const uno::Exception&)
{
}
}
return pFound->xConnection;
}
sal_uInt32 SwDBManager::GetSelectedRecordId(
const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType)
{
sal_uInt32 nRet = 0xffffffff;
//check for merge data source first
if(pImpl->pMergeData &&
((rDataSource == pImpl->pMergeData->sDataSource &&
rTableOrQuery == pImpl->pMergeData->sCommand) ||
(rDataSource.isEmpty() && rTableOrQuery.isEmpty())) &&
(nCommandType == -1 || nCommandType == pImpl->pMergeData->nCommandType) &&
pImpl->pMergeData->xResultSet.is())
{
nRet = GetSelectedRecordId();
}
else
{
SwDBData aData;
aData.sDataSource = rDataSource;
aData.sCommand = rTableOrQuery;
aData.nCommandType = nCommandType;
SwDSParam* pFound = FindDSData(aData, false);
if(pFound && pFound->xResultSet.is())
{
try
{ //if a selection array is set the current row at the result set may not be set yet
if(pFound->aSelection.getLength())
{
sal_Int32 nSelIndex = pFound->nSelectionIndex;
if(nSelIndex >= pFound->aSelection.getLength())
nSelIndex = pFound->aSelection.getLength() -1;
pFound->aSelection.getConstArray()[nSelIndex] >>= nRet;
}
else
nRet = pFound->xResultSet->getRow();
}
catch(const uno::Exception&)
{
}
}
}
return nRet;
}
// close all data sources - after fields were updated
void SwDBManager::CloseAll(bool bIncludingMerge)
{
//the only thing done here is to reset the selection index
//all connections stay open
for (auto & pParam : m_DataSourceParams)
{
if(bIncludingMerge || pParam.get() != pImpl->pMergeData)
{
pParam->nSelectionIndex = 0;
pParam->bEndOfDB = false;
try
{
if(!bInMerge && pParam->xResultSet.is())
pParam->xResultSet->first();
}
catch(const uno::Exception&)
{}
}
}
}
SwDSParam* SwDBManager::FindDSData(const SwDBData& rData, bool bCreate)
{
//prefer merge data if available
if(pImpl->pMergeData &&
((rData.sDataSource == pImpl->pMergeData->sDataSource &&
rData.sCommand == pImpl->pMergeData->sCommand) ||
(rData.sDataSource.isEmpty() && rData.sCommand.isEmpty())) &&
(rData.nCommandType == -1 || rData.nCommandType == pImpl->pMergeData->nCommandType ||
(bCreate && pImpl->pMergeData->nCommandType == -1)))
{
return pImpl->pMergeData;
}
SwDSParam* pFound = nullptr;
for (size_t nPos = m_DataSourceParams.size(); nPos; nPos--)
{
SwDSParam* pParam = m_DataSourceParams[nPos - 1].get();
if(rData.sDataSource == pParam->sDataSource &&
rData.sCommand == pParam->sCommand &&
(rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType ||
(bCreate && pParam->nCommandType == -1)))
{
// calls from the calculator may add a connection with an invalid commandtype
//later added "real" data base connections have to re-use the already available
//DSData and set the correct CommandType
if(bCreate && pParam->nCommandType == -1)
pParam->nCommandType = rData.nCommandType;
pFound = pParam;
break;
}
}
if(bCreate)
{
if(!pFound)
{
pFound = new SwDSParam(rData);
m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
try
{
uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
if(xComponent.is())
xComponent->addEventListener(pImpl->m_xDisposeListener.get());
}
catch(const uno::Exception&)
{
}
}
}
return pFound;
}
SwDSParam* SwDBManager::FindDSConnection(const OUString& rDataSource, bool bCreate)
{
//prefer merge data if available
if(pImpl->pMergeData && rDataSource == pImpl->pMergeData->sDataSource )
{
SetAsUsed(rDataSource);
return pImpl->pMergeData;
}
SwDSParam* pFound = nullptr;
for (auto & pParam : m_DataSourceParams)
{
if(rDataSource == pParam->sDataSource)
{
SetAsUsed(rDataSource);
pFound = pParam.get();
break;
}
}
if(bCreate && !pFound)
{
SwDBData aData;
aData.sDataSource = rDataSource;
pFound = new SwDSParam(aData);
SetAsUsed(rDataSource);
m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
try
{
uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
if(xComponent.is())
xComponent->addEventListener(pImpl->m_xDisposeListener.get());
}
catch(const uno::Exception&)
{
}
}
return pFound;
}
const SwDBData& SwDBManager::GetAddressDBName()
{
return SW_MOD()->GetDBConfig()->GetAddressSource();
}
uno::Sequence<OUString> SwDBManager::GetExistingDatabaseNames()
{
uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
return xDBContext->getElementNames();
}
namespace sw
{
DBConnURIType GetDBunoType(const INetURLObject &rURL)
{
OUString sExt(rURL.GetExtension());
DBConnURIType type = DBConnURIType::UNKNOWN;
if (sExt == "odb")
{
type = DBConnURIType::ODB;
}
else if (sExt.equalsIgnoreAsciiCase("sxc")
|| sExt.equalsIgnoreAsciiCase("ods")
|| sExt.equalsIgnoreAsciiCase("xls")
|| sExt.equalsIgnoreAsciiCase("xlsx"))
{
type = DBConnURIType::CALC;
}
else if (sExt.equalsIgnoreAsciiCase("sxw") || sExt.equalsIgnoreAsciiCase("odt") || sExt.equalsIgnoreAsciiCase("doc") || sExt.equalsIgnoreAsciiCase("docx"))
{
type = DBConnURIType::WRITER;
}
else if (sExt.equalsIgnoreAsciiCase("dbf"))
{
type = DBConnURIType::DBASE;
}
else if (sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt"))
{
type = DBConnURIType::FLAT;
}
#ifdef _WIN32
else if (sExt.equalsIgnoreAsciiCase("mdb") || sExt.equalsIgnoreAsciiCase("mde"))
{
type = DBConnURIType::MSJET;
}
else if (sExt.equalsIgnoreAsciiCase("accdb") || sExt.equalsIgnoreAsciiCase("accde"))
{
type = DBConnURIType::MSACE;
}
#endif
return type;
}
}
namespace
{
uno::Any GetDBunoURI(const INetURLObject &rURL, DBConnURIType& rType)
{
uno::Any aURLAny;
if (rType == DBConnURIType::UNKNOWN)
rType = GetDBunoType(rURL);
switch (rType) {
case DBConnURIType::UNKNOWN:
case DBConnURIType::ODB:
break;
case DBConnURIType::CALC:
{
OUString sDBURL("sdbc:calc:");
sDBURL += rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
aURLAny <<= sDBURL;
}
break;
case DBConnURIType::WRITER:
{
OUString sDBURL("sdbc:writer:");
sDBURL += rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
aURLAny <<= sDBURL;
}
break;
case DBConnURIType::DBASE:
{
INetURLObject aUrlTmp(rURL);
aUrlTmp.removeSegment();
aUrlTmp.removeFinalSlash();
OUString sDBURL("sdbc:dbase:");
sDBURL += aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
aURLAny <<= sDBURL;
}
break;
case DBConnURIType::FLAT:
{
INetURLObject aUrlTmp(rURL);
aUrlTmp.removeSegment();
aUrlTmp.removeFinalSlash();
OUString sDBURL("sdbc:flat:");
//only the 'path' has to be added
sDBURL += aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
aURLAny <<= sDBURL;
}
break;
case DBConnURIType::MSJET:
#ifdef _WIN32
{
OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=");
sDBURL += rURL.PathToFileName();
aURLAny <<= sDBURL;
}
#endif
break;
case DBConnURIType::MSACE:
#ifdef _WIN32
{
OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=");
sDBURL += rURL.PathToFileName();
aURLAny <<= sDBURL;
}
#endif
break;
}
return aURLAny;
}
/// Returns the URL of this SwDoc.
OUString getOwnURL(SfxObjectShell const * pDocShell)
{
OUString aRet;
if (!pDocShell)
return aRet;
const INetURLObject& rURLObject = pDocShell->GetMedium()->GetURLObject();
aRet = rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE);
return aRet;
}
/**
Loads a data source from file and registers it.
In case of success it returns the registered name, otherwise an empty string.
Optionally add a prefix to the registered DB name.
*/
OUString LoadAndRegisterDataSource_Impl(DBConnURIType type, const uno::Reference< beans::XPropertySet > *pSettings,
const INetURLObject &rURL, const OUString *pDestDir, SfxObjectShell* pDocShell)
{
OUString sExt(rURL.GetExtension());
uno::Any aTableFilterAny;
uno::Any aSuppressVersionsAny;
uno::Any aInfoAny;
bool bStore = true;
OUString sFind;
uno::Sequence<OUString> aFilters(1);
uno::Any aURLAny = GetDBunoURI(rURL, type);
switch (type) {
case DBConnURIType::UNKNOWN:
case DBConnURIType::CALC:
case DBConnURIType::WRITER:
break;
case DBConnURIType::ODB:
bStore = false;
break;
case DBConnURIType::FLAT:
case DBConnURIType::DBASE:
//set the filter to the file name without extension
aFilters[0] = rURL.getBase();
aTableFilterAny <<= aFilters;
break;
case DBConnURIType::MSJET:
case DBConnURIType::MSACE:
aSuppressVersionsAny <<= true;
break;
}
try
{
uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
OUString sNewName = INetURLObject::decode(rURL.getName(),
INetURLObject::DecodeMechanism::Unambiguous);
sal_Int32 nExtLen = sExt.getLength();
sNewName = sNewName.replaceAt(sNewName.getLength() - nExtLen - 1, nExtLen + 1, "");
//find a unique name if sNewName already exists
sFind = sNewName;
sal_Int32 nIndex = 0;
while (xDBContext->hasByName(sFind))
sFind = sNewName + OUString::number(++nIndex);
uno::Reference<uno::XInterface> xNewInstance;
if (!bStore)
{
//odb-file
uno::Any aDataSource = xDBContext->getByName(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
aDataSource >>= xNewInstance;
}
else
{
xNewInstance = xDBContext->createInstance();
uno::Reference<beans::XPropertySet> xDataProperties(xNewInstance, uno::UNO_QUERY);
if (aURLAny.hasValue())
xDataProperties->setPropertyValue("URL", aURLAny);
if (aTableFilterAny.hasValue())
xDataProperties->setPropertyValue("TableFilter", aTableFilterAny);
if (aSuppressVersionsAny.hasValue())
xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny);
if (aInfoAny.hasValue())
xDataProperties->setPropertyValue("Info", aInfoAny);
if (DBConnURIType::FLAT == type && pSettings)
{
uno::Any aSettings = xDataProperties->getPropertyValue("Settings");
uno::Reference < beans::XPropertySet > xDSSettings;
aSettings >>= xDSSettings;
::comphelper::copyProperties(*pSettings, xDSSettings);
xDSSettings->setPropertyValue("Extension", uno::makeAny(sExt));
}
uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, uno::UNO_QUERY_THROW);
uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), uno::UNO_QUERY_THROW);
OUString aOwnURL = getOwnURL(pDocShell);
if (aOwnURL.isEmpty())
{
// Cannot embed, as embedded data source would need the URL of the parent document.
OUString const sOutputExt = ".odb";
OUString sHomePath(SvtPathOptions().GetWorkPath());
utl::TempFile aTempFile(sNewName, true, &sOutputExt, pDestDir ? pDestDir : &sHomePath);
OUString sTmpName = aTempFile.GetURL();
xStore->storeAsURL(sTmpName, uno::Sequence<beans::PropertyValue>());
}
else
{
// Embed.
OUString aStreamRelPath = "EmbeddedDatabase";
uno::Reference<embed::XStorage> xStorage = pDocShell->GetStorage();
// Refer to the sub-storage name in the document settings, so
// we can load it again next time the file is imported.
uno::Reference<lang::XMultiServiceFactory> xFactory(pDocShell->GetModel(), uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::makeAny(aStreamRelPath));
// Store it only after setting the above property, so that only one data source gets registered.
SwDBManager::StoreEmbeddedDataSource(xStore, xStorage, aStreamRelPath, aOwnURL);
}
}
xDBContext->registerObject(sFind, xNewInstance);
}
catch (const uno::Exception&)
{
sFind.clear();
}
return sFind;
}
// Construct vnd.sun.star.pkg:// URL
OUString ConstructVndSunStarPkgUrl(const OUString& rMainURL, const OUString& rStreamRelPath)
{
auto xContext(comphelper::getProcessComponentContext());
auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(rMainURL);
assert(xUri.is());
xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)
->createVndSunStarPkgUrlReference(xUri);
assert(xUri.is());
return xUri->getUriReference() + "/"
+ INetURLObject::encode(
rStreamRelPath, INetURLObject::PART_FPATH,
INetURLObject::EncodeMechanism::All);
}
}
OUString SwDBManager::LoadAndRegisterDataSource(weld::Window* pParent, SwDocShell* pDocShell)
{
sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, pParent);
uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
OUString sHomePath(SvtPathOptions().GetWorkPath());
aDlgHelper.SetDisplayDirectory( sHomePath );
uno::Reference<ui::dialogs::XFilterManager> xFltMgr(xFP, uno::UNO_QUERY);
OUString sFilterAll(SwResId(STR_FILTER_ALL));
OUString sFilterAllData(SwResId(STR_FILTER_ALL_DATA));
OUString sFilterSXB(SwResId(STR_FILTER_SXB));
OUString sFilterSXC(SwResId(STR_FILTER_SXC));
OUString sFilterSXW(SwResId(STR_FILTER_SXW));
OUString sFilterDBF(SwResId(STR_FILTER_DBF));
OUString sFilterXLS(SwResId(STR_FILTER_XLS));
OUString sFilterDOC(SwResId(STR_FILTER_DOC));
OUString sFilterTXT(SwResId(STR_FILTER_TXT));
OUString sFilterCSV(SwResId(STR_FILTER_CSV));
#ifdef _WIN32
OUString sFilterMDB(SwResId(STR_FILTER_MDB));
OUString sFilterACCDB(SwResId(STR_FILTER_ACCDB));
#endif
xFltMgr->appendFilter( sFilterAll, "*" );
xFltMgr->appendFilter( sFilterAllData, "*.ods;*.sxc;*.odt;*.sxw;*.dbf;*.xls;*.xlsx;*.doc;*.docx;*.txt;*.csv");
xFltMgr->appendFilter( sFilterSXB, "*.odb" );
xFltMgr->appendFilter( sFilterSXC, "*.ods;*.sxc" );
xFltMgr->appendFilter( sFilterSXW, "*.odt;*.sxw" );
xFltMgr->appendFilter( sFilterDBF, "*.dbf" );
xFltMgr->appendFilter( sFilterXLS, "*.xls;*.xlsx" );
xFltMgr->appendFilter( sFilterDOC, "*.doc;*.docx" );
xFltMgr->appendFilter( sFilterTXT, "*.txt" );
xFltMgr->appendFilter( sFilterCSV, "*.csv" );
#ifdef _WIN32
xFltMgr->appendFilter(sFilterMDB, "*.mdb;*.mde");
xFltMgr->appendFilter(sFilterACCDB, "*.accdb;*.accde");
#endif
xFltMgr->setCurrentFilter( sFilterAll ) ;
OUString sFind;
if( ERRCODE_NONE == aDlgHelper.Execute() )
{
uno::Reference< beans::XPropertySet > aSettings;
const INetURLObject aURL( xFP->getSelectedFiles().getConstArray()[0] );
const DBConnURIType type = GetDBunoType( aURL );
if( DBConnURIType::FLAT == type )
{
uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext);
if( xSettingsDlg->execute() )
aSettings.set( uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ) );
}
sFind = LoadAndRegisterDataSource_Impl( type, DBConnURIType::FLAT == type ? &aSettings : nullptr, aURL, nullptr, pDocShell );
m_aUncommitedRegistrations.push_back(std::pair<SwDocShell*, OUString>(pDocShell, sFind));
}
return sFind;
}
void SwDBManager::StoreEmbeddedDataSource(const uno::Reference<frame::XStorable>& xStorable,
const uno::Reference<embed::XStorage>& xStorage,
const OUString& rStreamRelPath,
const OUString& rOwnURL, bool bCopyTo)
{
// Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
OUString const sTmpName = ConstructVndSunStarPkgUrl(rOwnURL, rStreamRelPath);
uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
{
{"TargetStorage", uno::makeAny(xStorage)},
{"StreamRelPath", uno::makeAny(rStreamRelPath)},
{"BaseURI", uno::makeAny(rOwnURL)}
});
if (bCopyTo)
xStorable->storeToURL(sTmpName, aSequence);
else
xStorable->storeAsURL(sTmpName, aSequence);
}
OUString SwDBManager::LoadAndRegisterDataSource(const OUString &rURI, const OUString *pDestDir)
{
return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN, nullptr, INetURLObject(rURI), pDestDir, nullptr );
}
namespace
{
// tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
// which allows the original storage to be deleted
void switchEmbeddedDatabaseStorage(uno::Reference<sdb::XDatabaseContext>& rDatabaseContext, const OUString& rName)
{
uno::Reference<sdb::XDocumentDataSource> xDS(rDatabaseContext->getByName(rName), uno::UNO_QUERY);
if (!xDS)
return;
uno::Reference<document::XStorageBasedDocument> xStorageDoc(xDS->getDatabaseDocument(), uno::UNO_QUERY);
if (!xStorageDoc)
return;
xStorageDoc->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
}
}
void SwDBManager::RevokeDataSource(const OUString& rName)
{
uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
if (xDatabaseContext->hasByName(rName))
{
switchEmbeddedDatabaseStorage(xDatabaseContext, rName);
xDatabaseContext->revokeObject(rName);
}
}
void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData& rData, const SwDocShell& rDocShell)
{
uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
OUString sDataSource = rData.sDataSource;
// Fallback, just in case the document would contain an embedded data source, but no DB fields.
if (sDataSource.isEmpty())
sDataSource = "EmbeddedDatabase";
SwDBManager::RevokeDataSource( sDataSource );
// Encode the stream name and the real path into a single URL.
const INetURLObject& rURLObject = rDocShell.GetMedium()->GetURLObject();
OUString const aURL = ConstructVndSunStarPkgUrl(
rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE),
m_sEmbeddedName);
uno::Reference<uno::XInterface> xDataSource(xDatabaseContext->getByName(aURL), uno::UNO_QUERY);
xDatabaseContext->registerObject( sDataSource, xDataSource );
// temp file - don't remember connection
if (rData.sDataSource.isEmpty())
m_aUncommitedRegistrations.push_back(std::pair<SwDocShell*, OUString>(nullptr, sDataSource));
}
void SwDBManager::ExecuteFormLetter( SwWrtShell& rSh,
const uno::Sequence<beans::PropertyValue>& rProperties)
{
//prevent second call
if(pImpl->pMergeDialog)
return ;
OUString sDataSource, sDataTableOrQuery;
uno::Sequence<uno::Any> aSelection;
sal_Int32 nCmdType = sdb::CommandType::TABLE;
uno::Reference< sdbc::XConnection> xConnection;
svx::ODataAccessDescriptor aDescriptor(rProperties);
sDataSource = aDescriptor.getDataSource();
OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDataTableOrQuery);
OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCmdType);
if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Selection) )
aDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Connection) )
aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty())
{
OSL_FAIL("PropertyValues missing or unset");
return;
}
//always create a connection for the dialog and dispose it after the dialog has been closed
SwDSParam* pFound = nullptr;
if(!xConnection.is())
{
xConnection = SwDBManager::RegisterConnection(sDataSource);
pFound = FindDSConnection(sDataSource, true);
}
SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
pImpl->pMergeDialog = pFact->CreateMailMergeDlg( &rSh.GetView().GetViewFrame()->GetWindow(), rSh,
sDataSource,
sDataTableOrQuery,
nCmdType,
xConnection);
if(pImpl->pMergeDialog->Execute() == RET_OK)
{
aDescriptor[svx::DataAccessDescriptorProperty::Selection] <<= pImpl->pMergeDialog->GetSelection();
uno::Reference<sdbc::XResultSet> xResSet = pImpl->pMergeDialog->GetResultSet();
if(xResSet.is())
aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
// SfxObjectShellRef is ok, since there should be no control over the document lifetime here
SfxObjectShellRef xDocShell = rSh.GetView().GetViewFrame()->GetObjectShell();
lcl_emitEvent(SfxEventHintId::SwMailMerge, STR_SW_EVENT_MAIL_MERGE, xDocShell.get());
// prepare mail merge descriptor
SwMergeDescriptor aMergeDesc( pImpl->pMergeDialog->GetMergeType(), rSh, aDescriptor );
aMergeDesc.sSaveToFilter = pImpl->pMergeDialog->GetSaveFilter();
aMergeDesc.bCreateSingleFile = pImpl->pMergeDialog->IsSaveSingleDoc();
aMergeDesc.bPrefixIsFilename = aMergeDesc.bCreateSingleFile;
aMergeDesc.sPrefix = pImpl->pMergeDialog->GetTargetURL();
if( !aMergeDesc.bCreateSingleFile && pImpl->pMergeDialog->IsGenerateFromDataBase() )
{
aMergeDesc.sDBcolumn = pImpl->pMergeDialog->GetColumnName();
}
Merge( aMergeDesc );
lcl_emitEvent(SfxEventHintId::SwMailMergeEnd, STR_SW_EVENT_MAIL_MERGE_END, xDocShell.get());
// reset the cursor inside
xResSet = nullptr;
aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
}
if(pFound)
{
for (auto & pParam : m_DataSourceParams)
{
if (pParam.get() == pFound)
{
try
{
uno::Reference<lang::XComponent> xComp(pParam->xConnection, uno::UNO_QUERY);
if(xComp.is())
xComp->dispose();
}
catch(const uno::RuntimeException&)
{
//may be disposed already since multiple entries may have used the same connection
}
break;
}
//pFound doesn't need to be removed/deleted -
//this has been done by the SwConnectionDisposedListener_Impl already
}
}
pImpl->pMergeDialog.disposeAndClear();
}
void SwDBManager::InsertText(SwWrtShell& rSh,
const uno::Sequence< beans::PropertyValue>& rProperties)
{
OUString sDataSource, sDataTableOrQuery;
uno::Reference<sdbc::XResultSet> xResSet;
uno::Sequence<uno::Any> aSelection;
sal_Int16 nCmdType = sdb::CommandType::TABLE;
const beans::PropertyValue* pValues = rProperties.getConstArray();
uno::Reference< sdbc::XConnection> xConnection;
for(sal_Int32 nPos = 0; nPos < rProperties.getLength(); nPos++)
{
if ( pValues[nPos].Name == "DataSourceName" )
pValues[nPos].Value >>= sDataSource;
else if ( pValues[nPos].Name == "Command" )
pValues[nPos].Value >>= sDataTableOrQuery;
else if ( pValues[nPos].Name == "Cursor" )
pValues[nPos].Value >>= xResSet;
else if ( pValues[nPos].Name == "Selection" )
pValues[nPos].Value >>= aSelection;
else if ( pValues[nPos].Name == "CommandType" )
pValues[nPos].Value >>= nCmdType;
else if ( pValues[nPos].Name == "ActiveConnection" )
pValues[nPos].Value >>= xConnection;
}
if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is())
{
OSL_FAIL("PropertyValues missing or unset");
return;
}
uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
uno::Reference<sdbc::XDataSource> xSource;
uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
if(xChild.is())
xSource.set(xChild->getParent(), uno::UNO_QUERY);
if(!xSource.is())
xSource = dbtools::getDataSource(sDataSource, xContext);
uno::Reference< sdbcx::XColumnsSupplier > xColSupp( xResSet, uno::UNO_QUERY );
SwDBData aDBData;
aDBData.sDataSource = sDataSource;
aDBData.sCommand = sDataTableOrQuery;
aDBData.nCommandType = nCmdType;
SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
ScopedVclPtr<AbstractSwInsertDBColAutoPilot> pDlg(pFact->CreateSwInsertDBColAutoPilot( rSh.GetView(),
xSource,
xColSupp,
aDBData ));
if( RET_OK == pDlg->Execute() )
{
OUString sDummy;
if(!xConnection.is())
xConnection = xSource->getConnection(sDummy, sDummy);
try
{
pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet);
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "exception caught: " << e);
}
}
}
uno::Reference<sdbc::XDataSource> SwDBManager::getDataSourceAsParent(const uno::Reference< sdbc::XConnection>& _xConnection,const OUString& _sDataSourceName)
{
uno::Reference<sdbc::XDataSource> xSource;
try
{
uno::Reference<container::XChild> xChild(_xConnection, uno::UNO_QUERY);
if ( xChild.is() )
xSource.set(xChild->getParent(), uno::UNO_QUERY);
if ( !xSource.is() )
xSource = dbtools::getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext());
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "exception caught in getDataSourceAsParent(): " << e);
}
return xSource;
}
uno::Reference<sdbc::XResultSet> SwDBManager::createCursor(const OUString& _sDataSourceName,
const OUString& _sCommand,
sal_Int32 _nCommandType,
const uno::Reference<sdbc::XConnection>& _xConnection
)
{
uno::Reference<sdbc::XResultSet> xResultSet;
try
{
uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
if( xMgr.is() )
{
uno::Reference<uno::XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet");
uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, uno::UNO_QUERY);
if(xRowSetPropSet.is())
{
xRowSetPropSet->setPropertyValue("DataSourceName", uno::makeAny(_sDataSourceName));
xRowSetPropSet->setPropertyValue("ActiveConnection", uno::makeAny(_xConnection));
xRowSetPropSet->setPropertyValue("Command", uno::makeAny(_sCommand));
xRowSetPropSet->setPropertyValue("CommandType", uno::makeAny(_nCommandType));
uno::Reference< sdb::XCompletedExecution > xRowSet(xInstance, uno::UNO_QUERY);
if ( xRowSet.is() )
{
uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), nullptr), uno::UNO_QUERY_THROW );
xRowSet->executeWithCompletion(xHandler);
}
xResultSet.set(xRowSet, uno::UNO_QUERY);
}
}
}
catch (const uno::Exception& e)
{
SAL_WARN("sw.mailmerge", "Caught exception while creating a new RowSet: " << e);
}
return xResultSet;
}
void SwDBManager::setEmbeddedName(const OUString& rEmbeddedName, SwDocShell& rDocShell)
{
bool bLoad = m_sEmbeddedName != rEmbeddedName && !rEmbeddedName.isEmpty();
bool bRegisterListener = m_sEmbeddedName.isEmpty() && !rEmbeddedName.isEmpty();
m_sEmbeddedName = rEmbeddedName;
if (bLoad)
{
uno::Reference<embed::XStorage> xStorage = rDocShell.GetStorage();
// It's OK that we don't have the named sub-storage yet, in case
// we're in the process of creating it.
if (xStorage->hasByName(rEmbeddedName))
LoadAndRegisterEmbeddedDataSource(rDocShell.GetDoc()->GetDBData(), rDocShell);
}
if (bRegisterListener)
// Register a remove listener, so we know when the embedded data source is removed.
pImpl->m_xDataSourceRemovedListener = new SwDataSourceRemovedListener(*this);
}
const OUString& SwDBManager::getEmbeddedName() const
{
return m_sEmbeddedName;
}
SwDoc* SwDBManager::getDoc() const
{
return m_pDoc;
}
void SwDBManager::releaseRevokeListener()
{
if (pImpl->m_xDataSourceRemovedListener.is())
{
pImpl->m_xDataSourceRemovedListener->Dispose();
pImpl->m_xDataSourceRemovedListener.clear();
}
}
SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager& rManager)
: m_pDBManager(&rManager)
{
}
void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject& rSource )
{
::SolarMutexGuard aGuard;
if (!m_pDBManager) return; // we're disposed too!
uno::Reference<sdbc::XConnection> xSource(rSource.Source, uno::UNO_QUERY);
for (size_t nPos = m_pDBManager->m_DataSourceParams.size(); nPos; nPos--)
{
SwDSParam* pParam = m_pDBManager->m_DataSourceParams[nPos - 1].get();
if(pParam->xConnection.is() &&
(xSource == pParam->xConnection))
{
m_pDBManager->m_DataSourceParams.erase(
m_pDBManager->m_DataSourceParams.begin() + nPos - 1);
}
}
}
std::shared_ptr<SwMailMergeConfigItem> SwDBManager::PerformMailMerge(SwView const * pView)
{
std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
if (!xConfigItem)
return xConfigItem;
svx::ODataAccessDescriptor aDescriptor;
aDescriptor.setDataSource(xConfigItem->GetCurrentDBData().sDataSource);
aDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= xConfigItem->GetConnection().getTyped();
aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ] <<= xConfigItem->GetResultSet();
aDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= xConfigItem->GetCurrentDBData().sCommand;
aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= xConfigItem->GetCurrentDBData().nCommandType;
aDescriptor[ svx::DataAccessDescriptorProperty::Selection ] <<= xConfigItem->GetSelection();
SwWrtShell& rSh = pView->GetWrtShell();
xConfigItem->SetTargetView(nullptr);
SwMergeDescriptor aMergeDesc(DBMGR_MERGE_SHELL, rSh, aDescriptor);
aMergeDesc.pMailMergeConfigItem = xConfigItem.get();
aMergeDesc.bCreateSingleFile = true;
rSh.GetDBManager()->Merge(aMergeDesc);
return xConfigItem;
}
void SwDBManager::RevokeLastRegistrations()
{
if (m_aUncommitedRegistrations.size())
{
SwView* pView = ( m_pDoc && m_pDoc->GetDocShell() ) ? m_pDoc->GetDocShell()->GetView() : nullptr;
if (pView)
{
std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
if (xConfigItem)
{
xConfigItem->DisposeResultSet();
xConfigItem->DocumentReloaded();
}
}
for (auto it = m_aUncommitedRegistrations.begin(); it != m_aUncommitedRegistrations.end();)
{
if ((m_pDoc && it->first == m_pDoc->GetDocShell()) || it->first == nullptr)
{
RevokeDataSource(it->second);
it = m_aUncommitedRegistrations.erase(it);
}
else
++it;
}
}
}
void SwDBManager::CommitLastRegistrations()
{
for (auto aIt = m_aUncommitedRegistrations.begin(); aIt != m_aUncommitedRegistrations.end();)
{
if (aIt->first == m_pDoc->GetDocShell() || aIt->first == nullptr)
{
m_aNotUsedConnections.push_back(aIt->second);
aIt = m_aUncommitedRegistrations.erase(aIt);
}
else
aIt++;
}
}
void SwDBManager::SetAsUsed(const OUString& rName)
{
auto aFound = std::find(m_aNotUsedConnections.begin(), m_aNotUsedConnections.end(), rName);
if (aFound != m_aNotUsedConnections.end())
m_aNotUsedConnections.erase(aFound);
}
void SwDBManager::RevokeNotUsedConnections()
{
for (auto aIt = m_aNotUsedConnections.begin(); aIt != m_aNotUsedConnections.end();)
{
RevokeDataSource(*aIt);
aIt = m_aNotUsedConnections.erase(aIt);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression 'nAbsPos >= 0' is always true.