1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
25 #include <unotxdoc.hxx>
26 #include <sfx2/app.hxx>
27 #include <sfx2/printer.hxx>
28 #include <com/sun/star/sdb/CommandType.hpp>
29 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
30 #include <com/sun/star/lang/DisposedException.hpp>
31 #include <com/sun/star/lang/XEventListener.hpp>
32 #include <com/sun/star/uri/UriReferenceFactory.hpp>
33 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
34 #include <com/sun/star/util/NumberFormatter.hpp>
35 #include <com/sun/star/sdb/DatabaseContext.hpp>
36 #include <com/sun/star/sdb/TextConnectionSettings.hpp>
37 #include <com/sun/star/sdb/XCompletedConnection.hpp>
38 #include <com/sun/star/sdb/XCompletedExecution.hpp>
39 #include <com/sun/star/container/XChild.hpp>
40 #include <com/sun/star/text/MailMergeEvent.hpp>
41 #include <com/sun/star/frame/XStorable.hpp>
42 #include <com/sun/star/task/InteractionHandler.hpp>
43 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
44 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
45 #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <vcl/errinf.hxx>
48 #include <sfx2/fcontnr.hxx>
49 #include <sfx2/filedlghelper.hxx>
50 #include <sfx2/viewfrm.hxx>
51 #include <dbconfig.hxx>
52 #include <pagedesc.hxx>
53 #include <vcl/lstbox.hxx>
54 #include <unotools/tempfile.hxx>
55 #include <unotools/pathoptions.hxx>
56 #include <svl/urihelper.hxx>
57 #include <svl/zforlist.hxx>
58 #include <svl/zformat.hxx>
59 #include <svl/stritem.hxx>
60 #include <svl/eitem.hxx>
61 #include <vcl/oldprintadaptor.hxx>
62 #include <sfx2/docfile.hxx>
63 #include <sfx2/progress.hxx>
64 #include <sfx2/dispatch.hxx>
66 #include <swmodule.hxx>
76 #include <shellio.hxx>
80 #include <IDocumentSettingAccess.hxx>
81 #include <IDocumentLinksAdministration.hxx>
82 #include <IDocumentContentOperations.hxx>
83 #include <IDocumentFieldsAccess.hxx>
84 #include <IDocumentUndoRedo.hxx>
86 #include <swunohelper.hxx>
88 #include <globals.hrc>
89 #include <strings.hrc>
90 #include <mmconfigitem.hxx>
91 #include <sfx2/request.hxx>
92 #include <hintids.hxx>
93 #include <com/sun/star/sdbc/XRowSet.hpp>
94 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
95 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
96 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
97 #include <com/sun/star/sdb/XColumn.hpp>
98 #include <com/sun/star/sdbc/DataType.hpp>
99 #include <com/sun/star/sdbc/ResultSetType.hpp>
100 #include <com/sun/star/mail/MailAttachment.hpp>
101 #include <comphelper/processfactory.hxx>
102 #include <comphelper/property.hxx>
103 #include <comphelper/string.hxx>
104 #include <comphelper/types.hxx>
105 #include <mailmergehelper.hxx>
106 #include <maildispatcher.hxx>
107 #include <svtools/htmlcfg.hxx>
108 #include <i18nlangtag/languagetag.hxx>
109 #include <com/sun/star/util/XNumberFormatTypes.hpp>
110 #include <editeng/langitem.hxx>
111 #include <svl/numuno.hxx>
112 #include <connectivity/dbtools.hxx>
113 #include <connectivity/dbconversion.hxx>
114 #include <o3tl/make_unique.hxx>
116 #include <unomailmerge.hxx>
117 #include <sfx2/event.hxx>
118 #include <vcl/msgbox.hxx>
119 #include <svx/dataaccessdescriptor.hxx>
120 #include <osl/mutex.hxx>
121 #include <rtl/textenc.h>
122 #include <cppuhelper/implbase.hxx>
123 #include <ndindex.hxx>
125 #include <swcrsr.hxx>
126 #include <swevent.hxx>
127 #include <osl/file.hxx>
128 #include <sal/log.hxx>
129 #include <swabstdlg.hxx>
130 #include <fmthdft.hxx>
133 #include <section.hxx>
134 #include <rootfrm.hxx>
135 #include <fmtpdsc.hxx>
139 #include <IDocumentState.hxx>
140 #include <imaildsplistener.hxx>
143 #include <comphelper/propertysequence.hxx>
144 #include <officecfg/Office/Common.hxx>
146 using namespace ::com::sun::star
;
149 #define DB_SEP_SPACE 0
151 #define DB_SEP_RETURN 2
152 #define DB_SEP_NEWLINE 3
156 void lcl_emitEvent(SfxEventHintId nEventId
, sal_Int32 nStrId
, SfxObjectShell
* pDocShell
)
158 SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId
,
159 SwDocShell::GetEventName(nStrId
),
165 std::vector
<std::pair
<SwDocShell
*, OUString
>> SwDBManager::m_aUncommitedRegistrations
;
167 enum class SwDBNextRecord
{ NEXT
, FIRST
};
168 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
= SwDBNextRecord::NEXT
);
170 enum class WorkingDocType
{ SOURCE
, TARGET
, COPY
};
171 static SfxObjectShell
* lcl_CreateWorkingDocument(
172 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
173 const vcl::Window
*pSourceWindow
,
174 SwDBManager
** const pDBManager
,
175 SwView
** const pView
, SwWrtShell
** const pWrtShell
, SwDoc
** const pDoc
);
177 static bool lcl_getCountFromResultSet( sal_Int32
& rCount
, const SwDSParam
* pParam
)
179 rCount
= pParam
->aSelection
.getLength();
183 uno::Reference
<beans::XPropertySet
> xPrSet(pParam
->xResultSet
, uno::UNO_QUERY
);
189 uno::Any aFinal
= xPrSet
->getPropertyValue("IsRowCountFinal");
193 pParam
->xResultSet
->last();
194 pParam
->xResultSet
->first();
196 uno::Any aCount
= xPrSet
->getPropertyValue("RowCount");
197 if( aCount
>>= rCount
)
200 catch(const uno::Exception
&)
207 class SwDBManager::ConnectionDisposedListener_Impl
208 : public cppu::WeakImplHelper
< lang::XEventListener
>
211 SwDBManager
* m_pDBManager
;
213 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
) override
;
216 explicit ConnectionDisposedListener_Impl(SwDBManager
& rMgr
);
218 void Dispose() { m_pDBManager
= nullptr; }
222 /// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
223 class SwDataSourceRemovedListener
: public cppu::WeakImplHelper
<sdb::XDatabaseRegistrationsListener
>
225 uno::Reference
<sdb::XDatabaseContext
> m_xDatabaseContext
;
226 SwDBManager
* m_pDBManager
;
229 explicit SwDataSourceRemovedListener(SwDBManager
& rDBManager
);
230 virtual ~SwDataSourceRemovedListener() override
;
231 virtual void SAL_CALL
registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
232 virtual void SAL_CALL
revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
233 virtual void SAL_CALL
changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
234 virtual void SAL_CALL
disposing(const lang::EventObject
& rObject
) override
;
238 SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager
& rDBManager
)
239 : m_pDBManager(&rDBManager
)
241 uno::Reference
<uno::XComponentContext
> xComponentContext(comphelper::getProcessComponentContext());
242 m_xDatabaseContext
= sdb::DatabaseContext::create(xComponentContext
);
243 m_xDatabaseContext
->addDatabaseRegistrationsListener(this);
246 SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
248 if (m_xDatabaseContext
.is())
249 m_xDatabaseContext
->removeDatabaseRegistrationsListener(this);
252 void SAL_CALL
SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& /*rEvent*/)
256 void SAL_CALL
SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
258 if (!m_pDBManager
|| m_pDBManager
->getEmbeddedName().isEmpty())
261 SwDoc
* pDoc
= m_pDBManager
->getDoc();
265 SwDocShell
* pDocShell
= pDoc
->GetDocShell();
269 OUString aOwnURL
= pDocShell
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset
);
270 OUString sTmpName
= "vnd.sun.star.pkg://";
271 sTmpName
+= INetURLObject::encode(aOwnURL
, INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
272 sTmpName
+= "/" + m_pDBManager
->getEmbeddedName();
274 if (sTmpName
!= rEvent
.OldLocation
)
277 // The revoked database location is inside this document, then remove the
278 // embedding, as otherwise it would be back on the next reload of the
280 pDocShell
->GetStorage()->removeElement(m_pDBManager
->getEmbeddedName());
281 m_pDBManager
->setEmbeddedName(OUString(), *pDocShell
);
284 void SAL_CALL
SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
286 if (rEvent
.OldLocation
!= rEvent
.NewLocation
)
287 revokedDatabaseLocation(rEvent
);
290 void SwDataSourceRemovedListener::disposing(const lang::EventObject
& /*rObject*/)
292 m_xDatabaseContext
.clear();
295 void SwDataSourceRemovedListener::Dispose()
297 m_pDBManager
= nullptr;
300 struct SwDBManager::SwDBManager_Impl
302 SwDSParam
*pMergeData
;
303 VclPtr
<AbstractMailMergeDlg
> pMergeDialog
;
304 rtl::Reference
<SwDBManager::ConnectionDisposedListener_Impl
> m_xDisposeListener
;
305 rtl::Reference
<SwDataSourceRemovedListener
> m_xDataSourceRemovedListener
;
306 osl::Mutex m_aAllEmailSendMutex
;
307 uno::Reference
< mail::XMailMessage
> m_xLastMessage
;
309 explicit SwDBManager_Impl(SwDBManager
& rDBManager
)
310 : pMergeData( nullptr )
311 , m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager
))
316 m_xDisposeListener
->Dispose();
317 if (m_xDataSourceRemovedListener
.is())
318 m_xDataSourceRemovedListener
->Dispose();
322 static void lcl_InitNumberFormatter(SwDSParam
& rParam
, uno::Reference
<sdbc::XDataSource
> const & xSource
)
324 uno::Reference
<uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
325 rParam
.xFormatter
.set(util::NumberFormatter::create(xContext
), uno::UNO_QUERY
);
326 uno::Reference
<beans::XPropertySet
> xSourceProps(
329 : SwDBManager::getDataSourceAsParent(
330 rParam
.xConnection
, rParam
.sDataSource
)),
332 if(xSourceProps
.is())
334 uno::Any aFormats
= xSourceProps
->getPropertyValue("NumberFormatsSupplier");
335 if(aFormats
.hasValue())
337 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
341 uno::Reference
< beans::XPropertySet
> xSettings
= xSuppl
->getNumberFormatSettings();
342 uno::Any aNull
= xSettings
->getPropertyValue("NullDate");
343 aNull
>>= rParam
.aNullDate
;
344 if(rParam
.xFormatter
.is())
345 rParam
.xFormatter
->attachNumberFormatsSupplier(xSuppl
);
351 static bool lcl_MoveAbsolute(SwDSParam
* pParam
, long nAbsPos
)
356 if(pParam
->aSelection
.getLength())
358 if(pParam
->aSelection
.getLength() <= nAbsPos
)
360 pParam
->bEndOfDB
= true;
365 pParam
->nSelectionIndex
= nAbsPos
;
367 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
368 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
369 bRet
= !pParam
->bEndOfDB
;
372 else if(pParam
->bScrollable
)
374 bRet
= pParam
->xResultSet
->absolute( nAbsPos
);
378 OSL_FAIL("no absolute positioning available");
381 catch(const uno::Exception
&)
387 static void lcl_GetColumnCnt(SwDSParam
*pParam
,
388 const uno::Reference
< beans::XPropertySet
> &rColumnProps
,
389 LanguageType nLanguage
, OUString
&rResult
, double* pNumber
)
391 SwDBFormatData aFormatData
;
392 if(!pParam
->xFormatter
.is())
394 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(
395 pParam
->xConnection
,pParam
->sDataSource
);
396 lcl_InitNumberFormatter(*pParam
, xSource
);
398 aFormatData
.aNullDate
= pParam
->aNullDate
;
399 aFormatData
.xFormatter
= pParam
->xFormatter
;
401 aFormatData
.aLocale
= LanguageTag( nLanguage
).getLocale();
403 rResult
= SwDBManager::GetDBField( rColumnProps
, aFormatData
, pNumber
);
406 static bool lcl_GetColumnCnt(SwDSParam
* pParam
, const OUString
& rColumnName
,
407 LanguageType nLanguage
, OUString
& rResult
, double* pNumber
)
409 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pParam
->xResultSet
, uno::UNO_QUERY
);
410 uno::Reference
<container::XNameAccess
> xCols
;
413 xCols
= xColsSupp
->getColumns();
415 catch(const lang::DisposedException
&)
418 if(!xCols
.is() || !xCols
->hasByName(rColumnName
))
420 uno::Any aCol
= xCols
->getByName(rColumnName
);
421 uno::Reference
< beans::XPropertySet
> xColumnProps
;
422 aCol
>>= xColumnProps
;
423 lcl_GetColumnCnt( pParam
, xColumnProps
, nLanguage
, rResult
, pNumber
);
428 bool SwDBManager::Merge( const SwMergeDescriptor
& rMergeDesc
)
430 assert( !bInMerge
&& !pImpl
->pMergeData
&& "merge already activated!" );
432 SfxObjectShellLock xWorkObjSh
;
433 SwWrtShell
*pWorkShell
= nullptr;
434 SwDoc
*pWorkDoc
= nullptr;
435 SwDBManager
*pWorkDocOrigDBManager
= nullptr;
437 switch( rMergeDesc
.nMergeType
)
439 case DBMGR_MERGE_PRINTER
:
440 case DBMGR_MERGE_EMAIL
:
441 case DBMGR_MERGE_FILE
:
442 case DBMGR_MERGE_SHELL
:
444 SwDocShell
*pSourceDocSh
= rMergeDesc
.rSh
.GetView().GetDocShell();
445 if( pSourceDocSh
->IsModified() )
447 pWorkDocOrigDBManager
= this;
448 xWorkObjSh
= lcl_CreateWorkingDocument(
449 WorkingDocType::SOURCE
, rMergeDesc
.rSh
, nullptr,
450 &pWorkDocOrigDBManager
, nullptr, &pWorkShell
, &pWorkDoc
);
456 if( !xWorkObjSh
.Is() )
457 pWorkShell
= &rMergeDesc
.rSh
;
462 aData
.nCommandType
= sdb::CommandType::TABLE
;
463 uno::Reference
<sdbc::XResultSet
> xResSet
;
464 uno::Sequence
<uno::Any
> aSelection
;
465 uno::Reference
< sdbc::XConnection
> xConnection
;
467 aData
.sDataSource
= rMergeDesc
.rDescriptor
.getDataSource();
468 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= aData
.sCommand
;
469 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= aData
.nCommandType
;
471 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Cursor
) )
472 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] >>= xResSet
;
473 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
474 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
475 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
476 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
478 if(aData
.sDataSource
.isEmpty() || aData
.sCommand
.isEmpty() || !xResSet
.is())
483 pImpl
->pMergeData
= new SwDSParam(aData
, xResSet
, aSelection
);
484 SwDSParam
* pTemp
= FindDSData(aData
, false);
486 *pTemp
= *pImpl
->pMergeData
;
489 // calls from the calculator may have added a connection with an invalid commandtype
490 //"real" data base connections added here have to re-use the already available
491 //DSData and set the correct CommandType
492 SwDBData
aTempData(aData
);
493 aData
.nCommandType
= -1;
494 pTemp
= FindDSData(aData
, false);
496 *pTemp
= *pImpl
->pMergeData
;
499 m_DataSourceParams
.push_back(o3tl::make_unique
<SwDSParam
>(*pImpl
->pMergeData
));
502 uno::Reference
<lang::XComponent
> xComponent(m_DataSourceParams
.back()->xConnection
, uno::UNO_QUERY
);
504 xComponent
->addEventListener(pImpl
->m_xDisposeListener
.get());
506 catch(const uno::Exception
&)
511 if(!pImpl
->pMergeData
->xConnection
.is())
512 pImpl
->pMergeData
->xConnection
= xConnection
;
513 // add an XEventListener
515 lcl_ToNextRecord(pImpl
->pMergeData
, SwDBNextRecord::FIRST
);
517 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
,aData
.sDataSource
);
519 lcl_InitNumberFormatter(*pImpl
->pMergeData
, xSource
);
521 pWorkShell
->ChgDBData(aData
);
524 if (IsInitDBFields())
526 // with database fields without DB-Name, use DB-Name from Doc
527 std::vector
<OUString
> aDBNames
;
528 aDBNames
.emplace_back();
529 SwDBData aInsertData
= pWorkShell
->GetDBData();
530 OUString sDBName
= aInsertData
.sDataSource
531 + OUStringLiteral1(DB_DELIM
) + aInsertData
.sCommand
532 + OUStringLiteral1(DB_DELIM
)
533 + OUString::number(aInsertData
.nCommandType
);
534 pWorkShell
->ChangeDBFields( aDBNames
, sDBName
);
535 SetInitDBFields(false);
539 switch(rMergeDesc
.nMergeType
)
542 pWorkShell
->StartAllAction();
543 pWorkShell
->SwViewShell::UpdateFields( true );
544 pWorkShell
->SetModified();
545 pWorkShell
->EndAllAction();
548 case DBMGR_MERGE_PRINTER
:
549 case DBMGR_MERGE_EMAIL
:
550 case DBMGR_MERGE_FILE
:
551 case DBMGR_MERGE_SHELL
:
552 // save files and send them as e-Mail if required
553 bRet
= MergeMailFiles(pWorkShell
, rMergeDesc
);
557 // insert selected entries
558 // (was: InsertRecord)
559 ImportFromConnection(pWorkShell
);
563 DELETEZ( pImpl
->pMergeData
);
565 if( xWorkObjSh
.Is() )
567 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
568 xWorkObjSh
->DoClose();
576 void SwDBManager::ImportFromConnection( SwWrtShell
* pSh
)
578 if(pImpl
->pMergeData
&& !pImpl
->pMergeData
->bEndOfDB
)
580 pSh
->StartAllAction();
582 bool bGroupUndo(pSh
->DoesGroupUndo());
583 pSh
->DoGroupUndo(false);
585 if( pSh
->HasSelection() )
588 std::unique_ptr
<SwWait
> pWait
;
596 pWait
.reset(new SwWait( *pSh
->GetView().GetDocShell(), true));
598 } while(ToNextMergeRecord());
601 pSh
->DoGroupUndo(bGroupUndo
);
607 static OUString
lcl_FindColumn(const OUString
& sFormatStr
,sal_uInt16
&nUsedPos
, sal_uInt8
&nSeparator
)
610 sal_uInt16 nLen
= sFormatStr
.getLength();
612 while(nUsedPos
< nLen
&& nSeparator
== 0xff)
614 sal_Unicode cAkt
= sFormatStr
[nUsedPos
];
618 nSeparator
= DB_SEP_SPACE
;
621 nSeparator
= DB_SEP_RETURN
;
624 nSeparator
= DB_SEP_TAB
;
627 nSeparator
= DB_SEP_NEWLINE
;
630 sReturn
+= OUStringLiteral1(cAkt
);
638 void SwDBManager::ImportDBEntry(SwWrtShell
* pSh
)
640 if(pImpl
->pMergeData
&& !pImpl
->pMergeData
->bEndOfDB
)
642 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
643 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
645 sal_uInt16 nFormatLen
= sFormatStr
.getLength();
648 const char cSpace
= ' ';
649 const char cTab
= '\t';
650 sal_uInt16 nUsedPos
= 0;
651 sal_uInt8 nSeparator
;
652 OUString sColumn
= lcl_FindColumn(sFormatStr
, nUsedPos
, nSeparator
);
653 while( !sColumn
.isEmpty() )
655 if(!xCols
->hasByName(sColumn
))
657 uno::Any aCol
= xCols
->getByName(sColumn
);
658 uno::Reference
< beans::XPropertySet
> xColumnProp
;
659 aCol
>>= xColumnProp
;
662 SwDBFormatData aDBFormat
;
663 OUString sInsert
= GetDBField( xColumnProp
, aDBFormat
);
664 if( DB_SEP_SPACE
== nSeparator
)
665 sInsert
+= OUStringLiteral1(cSpace
);
666 else if( DB_SEP_TAB
== nSeparator
)
667 sInsert
+= OUStringLiteral1(cTab
);
668 pSh
->Insert(sInsert
);
669 if( DB_SEP_RETURN
== nSeparator
)
671 else if(DB_SEP_NEWLINE
== nSeparator
)
672 pSh
->InsertLineBreak();
676 // column not found -> show error
677 OUStringBuffer sInsert
;
678 sInsert
.append('?').append(sColumn
).append('?');
679 pSh
->Insert(sInsert
.makeStringAndClear());
681 sColumn
= lcl_FindColumn(sFormatStr
, nUsedPos
, nSeparator
);
688 uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
689 const OUString
* pColNames
= aColNames
.getConstArray();
690 long nLength
= aColNames
.getLength();
691 for(long i
= 0; i
< nLength
; i
++)
693 uno::Any aCol
= xCols
->getByName(pColNames
[i
]);
694 uno::Reference
< beans::XPropertySet
> xColumnProp
;
695 aCol
>>= xColumnProp
;
696 SwDBFormatData aDBFormat
;
697 sStr
+= GetDBField( xColumnProp
, aDBFormat
);
701 pSh
->SwEditShell::Insert2(sStr
);
702 pSh
->SwFEShell::SplitNode(); // line feed
707 // fill Listbox with tablelist
708 bool SwDBManager::GetTableNames(ListBox
* pListBox
, const OUString
& rDBName
)
711 OUString
sOldTableName(pListBox
->GetSelectedEntry());
713 SwDSParam
* pParam
= FindDSConnection(rDBName
, false);
714 uno::Reference
< sdbc::XConnection
> xConnection
;
715 if(pParam
&& pParam
->xConnection
.is())
716 xConnection
= pParam
->xConnection
;
719 if ( !rDBName
.isEmpty() )
720 xConnection
= RegisterConnection( rDBName
);
724 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
727 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
728 uno::Sequence
<OUString
> aTables
= xTables
->getElementNames();
729 const OUString
* pTables
= aTables
.getConstArray();
730 for(long i
= 0; i
< aTables
.getLength(); i
++)
732 const sal_Int32 nEntry
= pListBox
->InsertEntry(pTables
[i
]);
733 pListBox
->SetEntryData(nEntry
, nullptr);
736 uno::Reference
<sdb::XQueriesSupplier
> xQSupplier(xConnection
, uno::UNO_QUERY
);
739 uno::Reference
<container::XNameAccess
> xQueries
= xQSupplier
->getQueries();
740 uno::Sequence
<OUString
> aQueries
= xQueries
->getElementNames();
741 const OUString
* pQueries
= aQueries
.getConstArray();
742 for(long i
= 0; i
< aQueries
.getLength(); i
++)
744 const sal_Int32 nEntry
= pListBox
->InsertEntry(pQueries
[i
]);
745 pListBox
->SetEntryData(nEntry
, reinterpret_cast<void*>(1));
748 if (!sOldTableName
.isEmpty())
749 pListBox
->SelectEntry(sOldTableName
);
755 // fill Listbox with column names of a database
756 void SwDBManager::GetColumnNames(ListBox
* pListBox
,
757 const OUString
& rDBName
, const OUString
& rTableName
)
760 aData
.sDataSource
= rDBName
;
761 aData
.sCommand
= rTableName
;
762 aData
.nCommandType
= -1;
763 SwDSParam
* pParam
= FindDSData(aData
, false);
764 uno::Reference
< sdbc::XConnection
> xConnection
;
765 if(pParam
&& pParam
->xConnection
.is())
766 xConnection
= pParam
->xConnection
;
769 xConnection
= RegisterConnection( rDBName
);
771 GetColumnNames(pListBox
, xConnection
, rTableName
);
774 void SwDBManager::GetColumnNames(ListBox
* pListBox
,
775 uno::Reference
< sdbc::XConnection
> const & xConnection
,
776 const OUString
& rTableName
)
779 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
782 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
783 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
784 const OUString
* pColNames
= aColNames
.getConstArray();
785 for(int nCol
= 0; nCol
< aColNames
.getLength(); nCol
++)
787 pListBox
->InsertEntry(pColNames
[nCol
]);
789 ::comphelper::disposeComponent( xColsSupp
);
793 SwDBManager::SwDBManager(SwDoc
* pDoc
)
794 : m_aMergeStatus( MergeStatus::Ok
)
795 , bInitDBFields(false)
797 , bMergeSilent(false)
798 , pImpl(new SwDBManager_Impl(*this))
799 , pMergeEvtSrc(nullptr)
804 SwDBManager::~SwDBManager() COVERITY_NOEXCEPT_FALSE
806 RevokeLastRegistrations();
808 // copy required, m_DataSourceParams can be modified while disposing components
809 std::vector
<uno::Reference
<sdbc::XConnection
>> aCopiedConnections
;
810 for (auto & pParam
: m_DataSourceParams
)
812 if(pParam
->xConnection
.is())
814 aCopiedConnections
.push_back(pParam
->xConnection
);
817 for (auto & xConnection
: aCopiedConnections
)
821 uno::Reference
<lang::XComponent
> xComp(xConnection
, uno::UNO_QUERY
);
825 catch(const uno::RuntimeException
&)
827 //may be disposed already since multiple entries may have used the same connection
832 static void lcl_RemoveSectionLinks( SwWrtShell
& rWorkShell
)
834 //reset all links of the sections of synchronized labels
835 size_t nSections
= rWorkShell
.GetSectionFormatCount();
836 for (size_t nSection
= 0; nSection
< nSections
; ++nSection
)
838 SwSectionData
aSectionData( *rWorkShell
.GetSectionFormat( nSection
).GetSection() );
839 if( aSectionData
.GetType() == FILE_LINK_SECTION
)
841 aSectionData
.SetType( CONTENT_SECTION
);
842 aSectionData
.SetLinkFileName( OUString() );
843 rWorkShell
.UpdateSection( nSection
, aSectionData
);
846 rWorkShell
.SetLabelDoc( false );
849 static void lcl_SaveDebugDoc( SfxObjectShell
*xTargetDocShell
,
850 const char *name
, int no
= 0 )
852 static OUString sTempDirURL
;
853 if( sTempDirURL
.isEmpty() )
855 SvtPathOptions aPathOpt
;
856 utl::TempFile
aTempDir( &aPathOpt
.GetTempPath(), true );
857 if( aTempDir
.IsValid() )
859 INetURLObject
aTempDirURL( aTempDir
.GetURL() );
860 sTempDirURL
= aTempDirURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
861 SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL
);
864 if( sTempDirURL
.isEmpty() )
867 const OUString
sExt( ".odt" );
868 OUString basename
= OUString::createFromAscii( name
);
870 basename
+= OUString::number(no
) + "-";
871 // aTempFile is not deleted, but that seems to be intentional
872 utl::TempFile
aTempFile( basename
, true, &sExt
, &sTempDirURL
);
873 INetURLObject
aTempFileURL( aTempFile
.GetURL() );
874 auto pDstMed
= o3tl::make_unique
<SfxMedium
>(
875 aTempFileURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
876 StreamMode::STD_READWRITE
);
877 bool bAnyError
= !xTargetDocShell
->DoSaveAs( *pDstMed
);
878 // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
879 bAnyError
|= (ERRCODE_NONE
!= xTargetDocShell
->GetError());
881 SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile
.GetURL() );
883 SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile
.GetURL() );
886 static bool lcl_SaveDoc(
887 const INetURLObject
* pFileURL
,
888 const std::shared_ptr
<const SfxFilter
>& pStoreToFilter
,
889 const OUString
* pStoreToFilterOptions
,
890 const uno::Sequence
< beans::PropertyValue
>* pSaveToFilterData
,
891 const bool bIsPDFexport
,
892 SfxObjectShell
* xObjectShell
,
893 SwWrtShell
& rWorkShell
,
894 OUString
* const decodedURL
= nullptr )
896 OUString url
= pFileURL
->GetMainURL( INetURLObject::DecodeMechanism::NONE
);
900 SfxMedium
* pDstMed
= new SfxMedium( url
, StreamMode::STD_READWRITE
);
901 pDstMed
->SetFilter( pStoreToFilter
);
902 if( pDstMed
->GetItemSet() )
904 if( pStoreToFilterOptions
)
905 pDstMed
->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS
,
906 *pStoreToFilterOptions
));
907 if( pSaveToFilterData
->getLength() )
908 pDstMed
->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA
,
909 uno::makeAny(*pSaveToFilterData
)));
912 // convert fields to text if we are exporting to PDF.
913 // this prevents a second merge while updating the fields
914 // in SwXTextDocument::getRendererCount()
916 rWorkShell
.ConvertFieldsToText();
918 bool bAnyError
= !xObjectShell
->DoSaveAs(*pDstMed
);
919 // Actually this should be a bool... so in case of email and individual
920 // files, where this is set, we skip the recently used handling
921 bAnyError
|= !xObjectShell
->DoSaveCompleted( pDstMed
, !decodedURL
);
922 bAnyError
|= (ERRCODE_NONE
!= xObjectShell
->GetError());
926 ErrorHandler::HandleError( xObjectShell
->GetError() );
931 static void lcl_PreparePrinterOptions(
932 const uno::Sequence
< beans::PropertyValue
>& rInPrintOptions
,
933 uno::Sequence
< beans::PropertyValue
>& rOutPrintOptions
)
935 // printing should be done synchronously otherwise the document
936 // might already become invalid during the process
938 const sal_Int32 nOffset
= 1;
939 rOutPrintOptions
.realloc( nOffset
);
940 rOutPrintOptions
[ 0 ].Name
= "Wait";
941 rOutPrintOptions
[ 0 ].Value
<<= true;
943 // copy print options
944 const beans::PropertyValue
* pOptions
= rInPrintOptions
.getConstArray();
945 for( sal_Int32 n
= 0, nIndex
= nOffset
; n
< rInPrintOptions
.getLength(); ++n
)
947 if( pOptions
[n
].Name
== "CopyCount" || pOptions
[n
].Name
== "FileName"
948 || pOptions
[n
].Name
== "Collate" || pOptions
[n
].Name
== "Pages"
949 || pOptions
[n
].Name
== "Wait" || pOptions
[n
].Name
== "PrinterName" )
952 rOutPrintOptions
.realloc( nIndex
+ 1 );
953 rOutPrintOptions
[ nIndex
].Name
= pOptions
[n
].Name
;
954 rOutPrintOptions
[ nIndex
++ ].Value
= pOptions
[n
].Value
;
959 static SfxObjectShell
* lcl_CreateWorkingDocument(
961 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
963 const vcl::Window
*pSourceWindow
,
964 // optional in and output to swap the DB manager
965 SwDBManager
** const pDBManager
,
967 SwView
** const pView
, SwWrtShell
** const pWrtShell
, SwDoc
** const pDoc
)
969 const SwDoc
*pSourceDoc
= rSourceWrtShell
.GetDoc();
970 SfxObjectShellRef xWorkObjectShell
= pSourceDoc
->CreateCopy( true, (aType
== WorkingDocType::TARGET
) );
971 SfxViewFrame
* pWorkFrame
= SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell
, SFX_INTERFACE_NONE
);
975 // the created window has to be located at the same position as the source window
976 vcl::Window
& rTargetWindow
= pWorkFrame
->GetFrame().GetWindow();
977 rTargetWindow
.SetPosPixel( pSourceWindow
->GetPosPixel() );
980 SwView
* pWorkView
= static_cast< SwView
* >( pWorkFrame
->GetViewShell() );
981 SwWrtShell
* pWorkWrtShell
= pWorkView
->GetWrtShellPtr();
982 pWorkWrtShell
->GetViewOptions()->SetIdle( false );
983 pWorkView
->AttrChangedNotify( pWorkWrtShell
);// in order for SelectShell to be called
984 SwDoc
* pWorkDoc
= pWorkWrtShell
->GetDoc();
985 pWorkDoc
->GetIDocumentUndoRedo().DoUndo( false );
986 pWorkDoc
->ReplaceDocumentProperties( *pSourceDoc
);
988 if( aType
== WorkingDocType::TARGET
)
990 assert( !pDBManager
);
991 pWorkDoc
->SetInMailMerge( true );
992 pWorkWrtShell
->SetLabelDoc( false );
996 // We have to swap the DBmanager of the new doc, so we also need input
997 assert( pDBManager
&& *pDBManager
);
998 SwDBManager
*pWorkDBManager
= pWorkDoc
->GetDBManager();
999 pWorkDoc
->SetDBManager( *pDBManager
);
1000 *pDBManager
= pWorkDBManager
;
1002 if( aType
== WorkingDocType::SOURCE
)
1004 // the GetDBData call constructs the data, if it's missing - kind of const...
1005 pWorkWrtShell
->ChgDBData( const_cast<SwDoc
*>(pSourceDoc
)->GetDBData() );
1006 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
1007 pWorkWrtShell
->SetLabelDoc( rSourceWrtShell
.IsLabelDoc() );
1008 pWorkDoc
->getIDocumentState().ResetModified();
1011 pWorkDoc
->getIDocumentLinksAdministration().EmbedAllLinks();
1014 if( pView
) *pView
= pWorkView
;
1015 if( pWrtShell
) *pWrtShell
= pWorkWrtShell
;
1016 if( pDoc
) *pDoc
= pWorkDoc
;
1018 return xWorkObjectShell
.get();
1021 static SwMailMessage
* lcl_CreateMailFromDoc(
1022 const SwMergeDescriptor
&rMergeDescriptor
,
1023 const OUString
&sFileURL
, const OUString
&sMailRecipient
,
1024 const OUString
&sMailBodyMimeType
, rtl_TextEncoding sMailEncoding
,
1025 const OUString
&sAttachmentMimeType
)
1027 SwMailMessage
* pMessage
= new SwMailMessage
;
1028 if( rMergeDescriptor
.pMailMergeConfigItem
->IsMailReplyTo() )
1029 pMessage
->setReplyToAddress(rMergeDescriptor
.pMailMergeConfigItem
->GetMailReplyTo());
1030 pMessage
->addRecipient( sMailRecipient
);
1031 pMessage
->SetSenderAddress( rMergeDescriptor
.pMailMergeConfigItem
->GetMailAddress() );
1034 if( rMergeDescriptor
.bSendAsAttachment
)
1036 sBody
= rMergeDescriptor
.sMailBody
;
1037 mail::MailAttachment aAttach
;
1038 aAttach
.Data
= new SwMailTransferable( sFileURL
,
1039 rMergeDescriptor
.sAttachmentName
, sAttachmentMimeType
);
1040 aAttach
.ReadableName
= rMergeDescriptor
.sAttachmentName
;
1041 pMessage
->addAttachment( aAttach
);
1045 //read in the temporary file and use it as mail body
1046 SfxMedium
aMedium( sFileURL
, StreamMode::READ
);
1047 SvStream
* pInStream
= aMedium
.GetInStream();
1048 assert( pInStream
&& "no output file created?" );
1052 pInStream
->SetStreamCharSet( sMailEncoding
);
1054 while ( pInStream
->ReadLine( sLine
) )
1056 sBody
+= OStringToOUString( sLine
, sMailEncoding
);
1060 pMessage
->setSubject( rMergeDescriptor
.sSubject
);
1061 uno::Reference
< datatransfer::XTransferable
> xBody
=
1062 new SwMailTransferable( sBody
, sMailBodyMimeType
);
1063 pMessage
->setBody( xBody
);
1065 for( const OUString
& sCcRecipient
: rMergeDescriptor
.aCopiesTo
)
1066 pMessage
->addCcRecipient( sCcRecipient
);
1067 for( const OUString
& sBccRecipient
: rMergeDescriptor
.aBlindCopiesTo
)
1068 pMessage
->addBccRecipient( sBccRecipient
);
1073 class SwDBManager::MailDispatcherListener_Impl
: public IMailDispatcherListener
1075 SwDBManager
&m_rDBManager
;
1078 explicit MailDispatcherListener_Impl( SwDBManager
&rDBManager
)
1079 : m_rDBManager( rDBManager
) {}
1081 virtual void started( ::rtl::Reference
<MailDispatcher
> ) override
{};
1082 virtual void stopped( ::rtl::Reference
<MailDispatcher
> ) override
{};
1083 virtual void idle( ::rtl::Reference
<MailDispatcher
> ) override
{};
1085 virtual void mailDelivered( ::rtl::Reference
<MailDispatcher
>,
1086 uno::Reference
< mail::XMailMessage
> xMessage
) override
1088 osl::MutexGuard
aGuard( m_rDBManager
.pImpl
->m_aAllEmailSendMutex
);
1089 if ( m_rDBManager
.pImpl
->m_xLastMessage
== xMessage
)
1090 m_rDBManager
.pImpl
->m_xLastMessage
.clear();
1093 virtual void mailDeliveryError( ::rtl::Reference
<MailDispatcher
> xMailDispatcher
,
1094 uno::Reference
< mail::XMailMessage
>, const OUString
& ) override
1096 osl::MutexGuard
aGuard( m_rDBManager
.pImpl
->m_aAllEmailSendMutex
);
1097 m_rDBManager
.m_aMergeStatus
= MergeStatus::Error
;
1098 m_rDBManager
.pImpl
->m_xLastMessage
.clear();
1099 xMailDispatcher
->stop();
1104 * Please have a look at the README in the same directory, before you make
1105 * larger changes in this function!
1107 bool SwDBManager::MergeMailFiles(SwWrtShell
* pSourceShell
,
1108 const SwMergeDescriptor
& rMergeDescriptor
)
1110 // deconstruct mail merge type for better readability.
1111 // uppercase naming is intentional!
1112 const bool bMT_EMAIL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_EMAIL
;
1113 const bool bMT_SHELL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_SHELL
;
1114 const bool bMT_PRINTER
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_PRINTER
;
1115 const bool bMT_FILE
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_FILE
;
1117 //check if the doc is synchronized and contains at least one linked section
1118 const bool bSynchronizedDoc
= pSourceShell
->IsLabelDoc() && pSourceShell
->GetSectionFormatCount() > 1;
1119 const bool bNeedsTempFiles
= ( bMT_EMAIL
|| bMT_FILE
);
1120 const bool bIsMergeSilent
= IsMergeSilent();
1122 bool bCheckSingleFile_
= rMergeDescriptor
.bCreateSingleFile
;
1125 assert( !rMergeDescriptor
.bPrefixIsFilename
);
1126 assert( bMT_EMAIL
&& !bCheckSingleFile_
);
1127 bCheckSingleFile_
= false;
1129 else if( bMT_SHELL
|| bMT_PRINTER
)
1131 assert( !rMergeDescriptor
.bPrefixIsFilename
);
1132 assert( (bMT_SHELL
|| bMT_PRINTER
) && bCheckSingleFile_
);
1133 bCheckSingleFile_
= true;
1135 const bool bCreateSingleFile
= bCheckSingleFile_
;
1137 // Setup for dumping debugging documents
1138 static const char *sMaxDumpDocs
= nullptr;
1139 static sal_Int32 nMaxDumpDocs
= 0;
1142 sMaxDumpDocs
= getenv("SW_DEBUG_MAILMERGE_DOCS");
1146 nMaxDumpDocs
= OUString(sMaxDumpDocs
, strlen(sMaxDumpDocs
), osl_getThreadTextEncoding()).toInt32();
1149 ::rtl::Reference
< MailDispatcher
> xMailDispatcher
;
1150 ::rtl::Reference
< IMailDispatcherListener
> xMailListener
;
1151 OUString sMailBodyMimeType
;
1152 rtl_TextEncoding sMailEncoding
= ::osl_getThreadTextEncoding();
1154 uno::Reference
< beans::XPropertySet
> xColumnProp
;
1156 // Check for (mandatory) email or (optional) filename column
1157 SwDBFormatData aColumnDBFormat
;
1158 bool bColumnName
= !rMergeDescriptor
.sDBcolumn
.isEmpty();
1166 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
1167 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1168 if( !xCols
->hasByName( rMergeDescriptor
.sDBcolumn
) )
1170 uno::Any aCol
= xCols
->getByName( rMergeDescriptor
.sDBcolumn
);
1171 aCol
>>= xColumnProp
;
1173 aColumnDBFormat
.xFormatter
= pImpl
->pMergeData
->xFormatter
;
1174 aColumnDBFormat
.aNullDate
= pImpl
->pMergeData
->aNullDate
;
1178 // Reset internal mail accounting data
1179 pImpl
->m_xLastMessage
.clear();
1181 xMailDispatcher
.set( new MailDispatcher(rMergeDescriptor
.xSmtpServer
) );
1182 xMailListener
= new MailDispatcherListener_Impl( *this );
1183 xMailDispatcher
->addListener( xMailListener
);
1184 if(!rMergeDescriptor
.bSendAsAttachment
&& rMergeDescriptor
.bSendAsHTML
)
1186 sMailBodyMimeType
= "text/html; charset=";
1187 sMailBodyMimeType
+= OUString::createFromAscii(
1188 rtl_getBestMimeCharsetFromTextEncoding( sMailEncoding
));
1189 SvxHtmlOptions
& rHtmlOptions
= SvxHtmlOptions::Get();
1190 sMailEncoding
= rHtmlOptions
.GetTextEncoding();
1193 sMailBodyMimeType
= "text/plain; charset=UTF-8; format=flowed";
1197 SwDocShell
*pSourceDocSh
= pSourceShell
->GetView().GetDocShell();
1199 // setup the output format
1200 std::shared_ptr
<const SfxFilter
> pStoreToFilter
= SwIoSystem::GetFileFilter(
1201 pSourceDocSh
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1202 SfxFilterContainer
* pFilterContainer
= SwDocShell::Factory().GetFilterContainer();
1203 const OUString
* pStoreToFilterOptions
= nullptr;
1205 // if a save_to filter is set then use it - otherwise use the default
1206 if( bMT_EMAIL
&& !rMergeDescriptor
.bSendAsAttachment
)
1208 OUString sExtension
= rMergeDescriptor
.bSendAsHTML
? OUString("html") : OUString("txt");
1209 pStoreToFilter
= pFilterContainer
->GetFilter4Extension(sExtension
, SfxFilterFlags::EXPORT
);
1211 else if( !rMergeDescriptor
.sSaveToFilter
.isEmpty())
1213 std::shared_ptr
<const SfxFilter
> pFilter
=
1214 pFilterContainer
->GetFilter4FilterName( rMergeDescriptor
.sSaveToFilter
);
1217 pStoreToFilter
= pFilter
;
1218 if(!rMergeDescriptor
.sSaveToFilterOptions
.isEmpty())
1219 pStoreToFilterOptions
= &rMergeDescriptor
.sSaveToFilterOptions
;
1222 const bool bIsPDFexport
= pStoreToFilter
&& pStoreToFilter
->GetFilterName() == "writer_pdf_Export";
1224 m_aMergeStatus
= MergeStatus::Ok
;
1226 // in case of creating a single resulting file this has to be created here
1227 SwView
* pTargetView
= rMergeDescriptor
.pMailMergeConfigItem
?
1228 rMergeDescriptor
.pMailMergeConfigItem
->GetTargetView() : nullptr;
1229 SwWrtShell
* pTargetShell
= nullptr;
1230 SwDoc
* pTargetDoc
= nullptr;
1231 SfxObjectShellRef xTargetDocShell
= nullptr;
1233 std::unique_ptr
< utl::TempFile
> aTempFile
;
1234 sal_uInt16 nStartingPageNo
= 0;
1236 vcl::Window
*pSourceWindow
= nullptr;
1237 VclPtr
<CancelableDialog
> pProgressDlg
;
1239 if( !bIsMergeSilent
)
1241 // construct the process dialog
1242 pSourceWindow
= &pSourceShell
->GetView().GetEditWin();
1243 vcl::Window
* pParent
= pSourceWindow
;
1245 pProgressDlg
= VclPtr
<CreateMonitor
>::Create(
1246 pParent
, pParent
!= pSourceWindow
);
1248 pProgressDlg
= VclPtr
<PrintMonitor
>::Create(
1249 pParent
, pParent
!= pSourceWindow
,
1250 PrintMonitor::MONITOR_TYPE_PRINT
);
1251 static_cast<PrintMonitor
*>( pProgressDlg
.get() )->SetText(
1252 pSourceDocSh
->GetTitle(22) );
1254 pProgressDlg
->SetCancelHdl( LINK(this, SwDBManager
, PrtCancelHdl
) );
1255 pProgressDlg
->Show();
1257 Application::Reschedule( true );
1260 if( bCreateSingleFile
&& !pTargetView
)
1262 // create a target docshell to put the merged document into
1263 xTargetDocShell
= lcl_CreateWorkingDocument( WorkingDocType::TARGET
,
1264 *pSourceShell
, bMT_SHELL
? pSourceWindow
: nullptr,
1265 nullptr, &pTargetView
, &pTargetShell
, &pTargetDoc
);
1267 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1269 else if( pTargetView
)
1271 pTargetShell
= pTargetView
->GetWrtShellPtr();
1272 pTargetDoc
= pTargetShell
->GetDoc();
1273 xTargetDocShell
= pTargetView
->GetDocShell();
1276 if( bCreateSingleFile
)
1278 // determine the page style and number used at the start of the source document
1279 pSourceShell
->SttEndDoc(true);
1280 nStartingPageNo
= pSourceShell
->GetVirtPageNum();
1283 // Progress, to prohibit KeyInputs
1284 SfxProgress
aProgress(pSourceDocSh
, ::aEmptyOUStr
, 1);
1286 // lock all dispatchers
1287 SfxViewFrame
* pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1290 pViewFrame
->GetDispatcher()->Lock(true);
1291 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1294 sal_Int32 nDocNo
= 1;
1296 // For single file mode, the number of pages in the target document so far, which is used
1297 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1298 // from the target doc would require repeated layouts of the doc, which is expensive, but
1299 // it can be manually computed from the source documents (for which we do layouts, so the page
1300 // count is known, and there is a blank page between each of them in the target document).
1301 int targetDocPageCount
= 0;
1303 if( !bIsMergeSilent
&& !bMT_PRINTER
)
1305 sal_Int32 nRecordCount
= 1;
1306 lcl_getCountFromResultSet( nRecordCount
, pImpl
->pMergeData
);
1308 // Synchronized docs don't auto-advance the record set, but there is a
1309 // "security" check, which will always advance the record set, if there
1310 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1311 sal_Int32 nRecordPerDoc
= pSourceShell
->GetDoc()
1312 ->getIDocumentFieldsAccess().GetRecordsPerDocument();
1313 if ( bSynchronizedDoc
&& (nRecordPerDoc
> 1) )
1315 assert( nRecordPerDoc
> 0 );
1317 sal_Int32 nMaxDocs
= nRecordCount
/ nRecordPerDoc
;
1318 if ( 0 != nRecordCount
% nRecordPerDoc
)
1320 static_cast<CreateMonitor
*>( pProgressDlg
.get() )->SetTotalCount( nMaxDocs
);
1323 long nStartRow
, nEndRow
;
1324 bool bFreezedLayouts
= false;
1325 // to collect temporary email files
1326 std::vector
< OUString
> aFilesToRemove
;
1328 // The SfxObjectShell will be closed explicitly later but
1329 // it is more safe to use SfxObjectShellLock here
1330 SfxObjectShellLock xWorkDocSh
;
1331 SwView
* pWorkView
= nullptr;
1332 SwDoc
* pWorkDoc
= nullptr;
1333 SwDBManager
* pWorkDocOrigDBManager
= nullptr;
1334 SwWrtShell
* pWorkShell
= nullptr;
1335 bool bWorkDocInitialized
= false;
1339 nStartRow
= pImpl
->pMergeData
? pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1341 OUString sColumnData
;
1343 // Read the indicated data column, which should contain a valid mail
1344 // address or an optional file name
1345 if( bMT_EMAIL
|| bColumnName
)
1347 sColumnData
= GetDBField( xColumnProp
, aColumnDBFormat
);
1350 // create a new temporary file name - only done once in case of bCreateSingleFile
1351 if( bNeedsTempFiles
&& ( !bWorkDocInitialized
|| !bCreateSingleFile
))
1353 OUString sPrefix
= rMergeDescriptor
.sPrefix
;
1356 //#i97667# if the name is from a database field then it will be used _as is_
1357 if( bColumnName
&& !bMT_EMAIL
)
1359 if (!sColumnData
.isEmpty())
1360 sLeading
= sColumnData
;
1366 INetURLObject
aEntry( sPrefix
);
1367 sLeading
= aEntry
.GetBase();
1368 aEntry
.removeSegment();
1369 sPrefix
= aEntry
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1372 OUString
sExt(comphelper::string::stripStart(pStoreToFilter
->GetDefaultExtension(), '*'));
1373 aTempFile
.reset( new utl::TempFile(sLeading
, sColumnData
.isEmpty(), &sExt
, &sPrefix
, true) );
1374 if( !aTempFile
->IsValid() )
1376 ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED
);
1377 m_aMergeStatus
= MergeStatus::Error
;
1383 std::unique_ptr
< INetURLObject
> aTempFileURL
;
1384 if( bNeedsTempFiles
)
1385 aTempFileURL
.reset( new INetURLObject(aTempFile
->GetURL()));
1386 if( !bIsMergeSilent
) {
1388 static_cast<CreateMonitor
*>( pProgressDlg
.get() )->SetCurrentPosition( nDocNo
);
1390 PrintMonitor
*pPrintMonDlg
= static_cast<PrintMonitor
*>( pProgressDlg
.get() );
1391 pPrintMonDlg
->m_pPrinter
->SetText( bNeedsTempFiles
1392 ? aTempFileURL
->GetBase() : pSourceDocSh
->GetTitle( 22 ) );
1393 OUString
sStat( SwResId(STR_STATSTR_LETTER
) );
1394 sStat
+= " " + OUString::number( nDocNo
);
1395 pPrintMonDlg
->m_pPrintInfo
->SetText( sStat
);
1397 pProgressDlg
->Update();
1400 Application::Reschedule( true );
1402 // Create a copy of the source document and work with that one instead of the source.
1403 // If we're not in the single file mode (which requires modifying the document for the merging),
1404 // it is enough to do this just once. Currently PDF also has to be treated special.
1405 if( !bWorkDocInitialized
|| bCreateSingleFile
|| bIsPDFexport
)
1407 assert( !xWorkDocSh
.Is());
1408 pWorkDocOrigDBManager
= this;
1409 xWorkDocSh
= lcl_CreateWorkingDocument( WorkingDocType::COPY
,
1410 *pSourceShell
, nullptr, &pWorkDocOrigDBManager
,
1411 &pWorkView
, &pWorkShell
, &pWorkDoc
);
1412 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1413 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1415 // #i69458# lock fields to prevent access to the result set while calculating layout
1416 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1417 // ExpFields update during printing, generation of preview, etc.
1418 pWorkShell
->LockExpFields();
1419 pWorkShell
->CalcLayout();
1422 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge
, STR_SW_EVENT_FIELD_MERGE
, xWorkDocSh
);
1424 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1425 // database cursor movement on any other fields update, for example during
1426 // print preview and other operations
1427 if ( pWorkShell
->IsExpFieldsLocked() )
1428 pWorkShell
->UnlockExpFields();
1429 pWorkShell
->SwViewShell::UpdateFields();
1430 pWorkShell
->LockExpFields();
1432 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished
, STR_SW_EVENT_FIELD_MERGE_FINISHED
, xWorkDocSh
);
1434 // also emit MailMergeEvent on XInterface if possible
1435 const SwXMailMerge
*pEvtSrc
= GetMailMergeEvtSrc();
1438 uno::Reference
< uno::XInterface
> xRef(
1439 static_cast<text::XMailMergeBroadcaster
*>(const_cast<SwXMailMerge
*>(pEvtSrc
)) );
1440 text::MailMergeEvent
aEvt( xRef
, xWorkDocSh
->GetModel() );
1441 pEvtSrc
->LaunchMailMergeEvent( aEvt
);
1444 // working copy is merged - prepare final steps depending on merge options
1446 if( bCreateSingleFile
)
1448 assert( pTargetShell
&& "no target shell available!" );
1450 // prepare working copy and target to append
1452 pWorkDoc
->RemoveInvisibleContent();
1453 pWorkShell
->ConvertFieldsToText();
1454 pWorkShell
->SetNumberingRestart();
1455 if( bSynchronizedDoc
)
1457 lcl_RemoveSectionLinks( *pWorkShell
);
1460 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1461 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1463 // append the working document to the target document
1464 if( targetDocPageCount
% 2 == 1 )
1465 ++targetDocPageCount
; // Docs always start on odd pages (so offset must be even).
1466 SwNodeIndex appendedDocStart
= pTargetDoc
->AppendDoc( *pWorkDoc
,
1467 nStartingPageNo
, !bWorkDocInitialized
, targetDocPageCount
, nDocNo
);
1468 targetDocPageCount
+= pWorkShell
->GetPageCnt();
1470 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1471 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1475 SwDocMergeInfo aMergeInfo
;
1476 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1477 aMergeInfo
.startPageInTarget
= pTargetDoc
->getIDocumentMarkAccess()->makeMark(
1478 appendedDocStart
, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
1479 ::sw::mark::InsertMode::New
);
1480 aMergeInfo
.nDBRow
= nStartRow
;
1481 rMergeDescriptor
.pMailMergeConfigItem
->AddMergedDocument( aMergeInfo
);
1486 assert( bNeedsTempFiles
);
1487 assert( pWorkShell
->IsExpFieldsLocked() );
1489 // fields are locked, so it's fine to
1490 // restore the old / empty DB manager for save
1491 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1493 // save merged document
1495 if( !lcl_SaveDoc( aTempFileURL
.get(), pStoreToFilter
, pStoreToFilterOptions
,
1496 &rMergeDescriptor
.aSaveToFilterData
, bIsPDFexport
,
1497 xWorkDocSh
, *pWorkShell
, &sFileURL
) )
1499 m_aMergeStatus
= MergeStatus::Error
;
1502 // back to the MM DB manager
1503 pWorkDoc
->SetDBManager( this );
1505 if( bMT_EMAIL
&& !IsMergeError() )
1507 // schedule file for later removal
1508 aFilesToRemove
.push_back( sFileURL
);
1510 if( !SwMailMergeHelper::CheckMailAddress( sColumnData
) )
1512 OSL_FAIL("invalid e-Mail address in database column");
1516 uno::Reference
< mail::XMailMessage
> xMessage
= lcl_CreateMailFromDoc(
1517 rMergeDescriptor
, sFileURL
, sColumnData
, sMailBodyMimeType
,
1518 sMailEncoding
, pStoreToFilter
->GetMimeType() );
1521 osl::MutexGuard
aGuard( pImpl
->m_aAllEmailSendMutex
);
1522 pImpl
->m_xLastMessage
.set( xMessage
);
1523 xMailDispatcher
->enqueueMailMessage( xMessage
);
1524 if( !xMailDispatcher
->isStarted() )
1525 xMailDispatcher
->start();
1530 if( bCreateSingleFile
|| bIsPDFexport
)
1532 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1533 xWorkDocSh
->DoClose();
1534 xWorkDocSh
= nullptr;
1538 bWorkDocInitialized
= true;
1540 nEndRow
= pImpl
->pMergeData
? pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1542 // Freeze the layouts of the target document after the first inserted
1543 // sub-document, to get the correct PageDesc.
1544 if(!bFreezedLayouts
&& bCreateSingleFile
)
1546 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1547 aLayout
->FreezeLayout(true);
1548 bFreezedLayouts
= true;
1550 } while( IsMergeOk() &&
1551 ((bSynchronizedDoc
&& (nStartRow
!= nEndRow
)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1553 if ( xWorkDocSh
.Is() && pWorkView
->GetWrtShell().IsExpFieldsLocked() )
1555 // Unlock document fields after merge complete
1556 pWorkView
->GetWrtShell().UnlockExpFields();
1559 if( !bCreateSingleFile
)
1562 Printer::FinishPrintJob( pWorkView
->GetPrinterController());
1565 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1566 xWorkDocSh
->DoClose();
1569 else if( IsMergeOk() ) // && bCreateSingleFile
1571 Application::Reschedule( true );
1573 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1574 // unique fly names, do it here once.
1575 pTargetDoc
->SetInMailMerge(false);
1576 pTargetDoc
->SetAllUniqueFlyNames();
1578 // Unfreeze target document layouts and correct all PageDescs.
1579 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1580 pTargetShell
->CalcLayout();
1581 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1582 pTargetShell
->GetViewOptions()->SetIdle( true );
1583 pTargetDoc
->GetIDocumentUndoRedo().DoUndo( true );
1584 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1586 aLayout
->FreezeLayout(false);
1587 aLayout
->AllCheckPageDescs();
1590 Application::Reschedule( true );
1592 if( IsMergeOk() && bMT_FILE
)
1594 // save merged document
1595 assert( aTempFile
.get() );
1596 INetURLObject aTempFileURL
;
1597 if( rMergeDescriptor
.sPrefix
.isEmpty() || !rMergeDescriptor
.bPrefixIsFilename
)
1598 aTempFileURL
.SetURL( aTempFile
->GetURL() );
1601 aTempFileURL
.SetURL( rMergeDescriptor
.sPrefix
);
1602 // remove the unneeded temporary file
1603 aTempFile
->EnableKillingFile();
1605 if( !lcl_SaveDoc( &aTempFileURL
, pStoreToFilter
,
1606 pStoreToFilterOptions
, &rMergeDescriptor
.aSaveToFilterData
,
1607 bIsPDFexport
, xTargetDocShell
.get(), *pTargetShell
) )
1609 m_aMergeStatus
= MergeStatus::Error
;
1612 else if( IsMergeOk() && bMT_PRINTER
)
1614 // print the target document
1615 uno::Sequence
< beans::PropertyValue
> aOptions( rMergeDescriptor
.aPrintOptions
);
1616 lcl_PreparePrinterOptions( rMergeDescriptor
.aPrintOptions
, aOptions
);
1617 pTargetView
->ExecPrint( aOptions
, bIsMergeSilent
, false/*bPrintAsync*/ );
1621 // we also show canceled documents, as long as there was no error
1622 if( !IsMergeError() && bMT_SHELL
)
1623 // leave docshell available for caller (e.g. MM wizard)
1624 rMergeDescriptor
.pMailMergeConfigItem
->SetTargetView( pTargetView
);
1625 else if( xTargetDocShell
.is() )
1626 xTargetDocShell
->DoClose();
1628 Application::Reschedule( true );
1630 pProgressDlg
.disposeAndClear();
1632 // unlock all dispatchers
1633 pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1636 pViewFrame
->GetDispatcher()->Lock(false);
1637 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1640 SW_MOD()->SetView(&pSourceShell
->GetView());
1642 if( xMailDispatcher
.is() )
1646 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1647 // if the main loop has been made thread-safe.
1648 AutoTimer aEmailDispatcherPollTimer
;
1649 aEmailDispatcherPollTimer
.SetDebugName(
1650 "sw::SwDBManager aEmailDispatcherPollTimer" );
1651 aEmailDispatcherPollTimer
.SetTimeout( 500 );
1652 aEmailDispatcherPollTimer
.Start();
1653 while( IsMergeOk() && pImpl
->m_xLastMessage
.is() )
1654 Application::Yield();
1655 aEmailDispatcherPollTimer
.Stop();
1657 xMailDispatcher
->stop();
1658 xMailDispatcher
->shutdown();
1661 // remove the temporary files
1662 // has to be done after xMailDispatcher is finished, as mails may be
1663 // delivered as message attachments!
1664 for( const OUString
&sFileURL
: aFilesToRemove
)
1665 SWUnoHelper::UCB_DeleteFile( sFileURL
);
1667 return !IsMergeError();
1670 void SwDBManager::MergeCancel()
1672 if (m_aMergeStatus
< MergeStatus::Cancel
)
1673 m_aMergeStatus
= MergeStatus::Cancel
;
1676 IMPL_LINK( SwDBManager
, PrtCancelHdl
, Button
*, pButton
, void )
1678 pButton
->GetParent()->Hide();
1682 // determine the column's Numberformat and transfer to the forwarded Formatter,
1684 sal_uLong
SwDBManager::GetColumnFormat( const OUString
& rDBName
,
1685 const OUString
& rTableName
,
1686 const OUString
& rColNm
,
1687 SvNumberFormatter
* pNFormatr
,
1688 LanguageType nLanguage
)
1693 uno::Reference
< sdbc::XDataSource
> xSource
;
1694 uno::Reference
< sdbc::XConnection
> xConnection
;
1695 bool bUseMergeData
= false;
1696 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1697 bool bDisposeConnection
= false;
1698 if(pImpl
->pMergeData
&&
1699 ((pImpl
->pMergeData
->sDataSource
== rDBName
&& pImpl
->pMergeData
->sCommand
== rTableName
) ||
1700 (rDBName
.isEmpty() && rTableName
.isEmpty())))
1702 xConnection
= pImpl
->pMergeData
->xConnection
;
1703 xSource
= SwDBManager::getDataSourceAsParent(xConnection
,rDBName
);
1704 bUseMergeData
= true;
1705 xColsSupp
.set(pImpl
->pMergeData
->xResultSet
, css::uno::UNO_QUERY
);
1707 if(!xConnection
.is())
1710 aData
.sDataSource
= rDBName
;
1711 aData
.sCommand
= rTableName
;
1712 aData
.nCommandType
= -1;
1713 SwDSParam
* pParam
= FindDSData(aData
, false);
1714 if(pParam
&& pParam
->xConnection
.is())
1716 xConnection
= pParam
->xConnection
;
1717 xColsSupp
.set(pParam
->xResultSet
, css::uno::UNO_QUERY
);
1721 xConnection
= RegisterConnection( rDBName
);
1722 bDisposeConnection
= true;
1725 pImpl
->pMergeData
->xConnection
= xConnection
;
1727 bool bDispose
= !xColsSupp
.is();
1730 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1734 uno::Reference
<container::XNameAccess
> xCols
;
1737 xCols
= xColsSupp
->getColumns();
1739 catch (const uno::Exception
& e
)
1741 SAL_WARN("sw.mailmerge", "Exception in getColumns(): " << e
);
1743 if(!xCols
.is() || !xCols
->hasByName(rColNm
))
1745 uno::Any aCol
= xCols
->getByName(rColNm
);
1746 uno::Reference
< beans::XPropertySet
> xColumn
;
1748 nRet
= GetColumnFormat(xSource
, xConnection
, xColumn
, pNFormatr
, nLanguage
);
1751 ::comphelper::disposeComponent( xColsSupp
);
1753 if(bDisposeConnection
)
1755 ::comphelper::disposeComponent( xConnection
);
1759 nRet
= pNFormatr
->GetFormatIndex( NF_NUMBER_STANDARD
, LANGUAGE_SYSTEM
);
1764 sal_uLong
SwDBManager::GetColumnFormat( uno::Reference
< sdbc::XDataSource
> const & xSource_in
,
1765 uno::Reference
< sdbc::XConnection
> const & xConnection
,
1766 uno::Reference
< beans::XPropertySet
> const & xColumn
,
1767 SvNumberFormatter
* pNFormatr
,
1768 LanguageType nLanguage
)
1770 auto xSource
= xSource_in
;
1772 // set the NumberFormat in the doc if applicable
1777 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
1779 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
1781 if(xSource
.is() && xConnection
.is() && xColumn
.is() && pNFormatr
)
1783 SvNumberFormatsSupplierObj
* pNumFormat
= new SvNumberFormatsSupplierObj( pNFormatr
);
1784 uno::Reference
< util::XNumberFormatsSupplier
> xDocNumFormatsSupplier
= pNumFormat
;
1785 uno::Reference
< util::XNumberFormats
> xDocNumberFormats
= xDocNumFormatsSupplier
->getNumberFormats();
1786 uno::Reference
< util::XNumberFormatTypes
> xDocNumberFormatTypes(xDocNumberFormats
, uno::UNO_QUERY
);
1788 css::lang::Locale
aLocale( LanguageTag( nLanguage
).getLocale());
1790 //get the number formatter of the data source
1791 uno::Reference
<beans::XPropertySet
> xSourceProps(xSource
, uno::UNO_QUERY
);
1792 uno::Reference
< util::XNumberFormats
> xNumberFormats
;
1793 if(xSourceProps
.is())
1795 uno::Any aFormats
= xSourceProps
->getPropertyValue("NumberFormatsSupplier");
1796 if(aFormats
.hasValue())
1798 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
1799 aFormats
>>= xSuppl
;
1802 xNumberFormats
= xSuppl
->getNumberFormats();
1806 bool bUseDefault
= true;
1809 uno::Any aFormatKey
= xColumn
->getPropertyValue("FormatKey");
1810 if(aFormatKey
.hasValue())
1812 sal_Int32 nFormat
= 0;
1813 aFormatKey
>>= nFormat
;
1814 if(xNumberFormats
.is())
1818 uno::Reference
<beans::XPropertySet
> xNumProps
= xNumberFormats
->getByKey( nFormat
);
1819 uno::Any aFormatString
= xNumProps
->getPropertyValue("FormatString");
1820 uno::Any aLocaleVal
= xNumProps
->getPropertyValue("Locale");
1822 aFormatString
>>= sFormat
;
1824 aLocaleVal
>>= aLoc
;
1825 nFormat
= xDocNumberFormats
->queryKey( sFormat
, aLoc
, false );
1826 if(NUMBERFORMAT_ENTRY_NOT_FOUND
== sal::static_int_cast
< sal_uInt32
, sal_Int32
>(nFormat
))
1827 nFormat
= xDocNumberFormats
->addNew( sFormat
, aLoc
);
1829 bUseDefault
= false;
1831 catch (const uno::Exception
& e
)
1833 SAL_WARN("sw.mailmerge", "illegal number format key: " << e
);
1838 catch(const uno::Exception
&)
1840 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1843 nRet
= dbtools::getDefaultNumberFormat(xColumn
, xDocNumberFormatTypes
, aLocale
);
1848 sal_Int32
SwDBManager::GetColumnType( const OUString
& rDBName
,
1849 const OUString
& rTableName
,
1850 const OUString
& rColNm
)
1852 sal_Int32 nRet
= sdbc::DataType::SQLNULL
;
1854 aData
.sDataSource
= rDBName
;
1855 aData
.sCommand
= rTableName
;
1856 aData
.nCommandType
= -1;
1857 SwDSParam
* pParam
= FindDSData(aData
, false);
1858 uno::Reference
< sdbc::XConnection
> xConnection
;
1859 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1860 bool bDispose
= false;
1861 if(pParam
&& pParam
->xConnection
.is())
1863 xConnection
= pParam
->xConnection
;
1864 xColsSupp
.set( pParam
->xResultSet
, uno::UNO_QUERY
);
1868 xConnection
= RegisterConnection( rDBName
);
1870 if( !xColsSupp
.is() )
1872 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1877 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1878 if(xCols
->hasByName(rColNm
))
1880 uno::Any aCol
= xCols
->getByName(rColNm
);
1881 uno::Reference
<beans::XPropertySet
> xCol
;
1883 uno::Any aType
= xCol
->getPropertyValue("Type");
1887 ::comphelper::disposeComponent( xColsSupp
);
1892 uno::Reference
< sdbc::XConnection
> SwDBManager::GetConnection(const OUString
& rDataSource
,
1893 uno::Reference
<sdbc::XDataSource
>& rxSource
)
1895 uno::Reference
< sdbc::XConnection
> xConnection
;
1896 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1899 uno::Reference
<sdb::XCompletedConnection
> xComplConnection(dbtools::getDataSource(rDataSource
, xContext
), uno::UNO_QUERY
);
1900 if ( xComplConnection
.is() )
1902 rxSource
.set(xComplConnection
, uno::UNO_QUERY
);
1903 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1904 xConnection
= xComplConnection
->connectWithCompletion( xHandler
);
1907 catch(const uno::Exception
&)
1914 uno::Reference
< sdbcx::XColumnsSupplier
> SwDBManager::GetColumnSupplier(uno::Reference
<sdbc::XConnection
> const & xConnection
,
1915 const OUString
& rTableOrQuery
,
1916 SwDBSelect eTableOrQuery
)
1918 uno::Reference
< sdbcx::XColumnsSupplier
> xRet
;
1921 if(eTableOrQuery
== SwDBSelect::UNKNOWN
)
1923 //search for a table with the given command name
1924 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
1927 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
1928 eTableOrQuery
= xTables
->hasByName(rTableOrQuery
) ?
1929 SwDBSelect::TABLE
: SwDBSelect::QUERY
;
1932 sal_Int32 nCommandType
= SwDBSelect::TABLE
== eTableOrQuery
?
1933 sdb::CommandType::TABLE
: sdb::CommandType::QUERY
;
1934 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
1935 uno::Reference
<sdbc::XRowSet
> xRowSet(xMgr
->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY
);
1937 OUString sDataSource
;
1938 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
, sDataSource
);
1939 uno::Reference
<beans::XPropertySet
> xSourceProperties(xSource
, uno::UNO_QUERY
);
1940 if(xSourceProperties
.is())
1942 xSourceProperties
->getPropertyValue("Name") >>= sDataSource
;
1945 uno::Reference
<beans::XPropertySet
> xRowProperties(xRowSet
, uno::UNO_QUERY
);
1946 xRowProperties
->setPropertyValue("DataSourceName", uno::makeAny(sDataSource
));
1947 xRowProperties
->setPropertyValue("Command", uno::makeAny(rTableOrQuery
));
1948 xRowProperties
->setPropertyValue("CommandType", uno::makeAny(nCommandType
));
1949 xRowProperties
->setPropertyValue("FetchSize", uno::makeAny(sal_Int32(10)));
1950 xRowProperties
->setPropertyValue("ActiveConnection", uno::makeAny(xConnection
));
1952 xRet
.set( xRowSet
, uno::UNO_QUERY
);
1954 catch (const uno::Exception
& e
)
1956 SAL_WARN("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier: " << e
);
1962 OUString
SwDBManager::GetDBField(uno::Reference
<beans::XPropertySet
> const & xColumnProps
,
1963 const SwDBFormatData
& rDBFormatData
,
1966 uno::Reference
< sdb::XColumn
> xColumn(xColumnProps
, uno::UNO_QUERY
);
1968 assert( xColumn
.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1972 uno::Any aType
= xColumnProps
->getPropertyValue("Type");
1973 sal_Int32 eDataType
= sdbc::DataType::SQLNULL
;
1974 aType
>>= eDataType
;
1977 case sdbc::DataType::CHAR
:
1978 case sdbc::DataType::VARCHAR
:
1979 case sdbc::DataType::LONGVARCHAR
:
1982 sRet
= xColumn
->getString();
1983 sRet
= sRet
.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1985 catch(const sdbc::SQLException
&)
1989 case sdbc::DataType::BIT
:
1990 case sdbc::DataType::BOOLEAN
:
1991 case sdbc::DataType::TINYINT
:
1992 case sdbc::DataType::SMALLINT
:
1993 case sdbc::DataType::INTEGER
:
1994 case sdbc::DataType::BIGINT
:
1995 case sdbc::DataType::FLOAT
:
1996 case sdbc::DataType::REAL
:
1997 case sdbc::DataType::DOUBLE
:
1998 case sdbc::DataType::NUMERIC
:
1999 case sdbc::DataType::DECIMAL
:
2000 case sdbc::DataType::DATE
:
2001 case sdbc::DataType::TIME
:
2002 case sdbc::DataType::TIMESTAMP
:
2007 sRet
= dbtools::DBTypeConversion::getFormattedValue(
2009 rDBFormatData
.xFormatter
,
2010 rDBFormatData
.aLocale
,
2011 rDBFormatData
.aNullDate
);
2014 double fVal
= xColumn
->getDouble();
2015 if(!xColumn
->wasNull())
2021 catch (const uno::Exception
& e
)
2023 SAL_WARN("sw.mailmerge", "exception caught: " << e
);
2033 // checks if a desired data source table or query is open
2034 bool SwDBManager::IsDataSourceOpen(const OUString
& rDataSource
,
2035 const OUString
& rTableOrQuery
, bool bMergeShell
)
2037 if(pImpl
->pMergeData
)
2039 return ((rDataSource
== pImpl
->pMergeData
->sDataSource
2040 && rTableOrQuery
== pImpl
->pMergeData
->sCommand
)
2041 || (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty()))
2042 && pImpl
->pMergeData
->xResultSet
.is();
2044 else if(!bMergeShell
)
2047 aData
.sDataSource
= rDataSource
;
2048 aData
.sCommand
= rTableOrQuery
;
2049 aData
.nCommandType
= -1;
2050 SwDSParam
* pFound
= FindDSData(aData
, false);
2051 return (pFound
&& pFound
->xResultSet
.is());
2056 // read column data at a specified position
2057 bool SwDBManager::GetColumnCnt(const OUString
& rSourceName
, const OUString
& rTableName
,
2058 const OUString
& rColumnName
, sal_uInt32 nAbsRecordId
,
2059 LanguageType nLanguage
,
2060 OUString
& rResult
, double* pNumber
)
2063 SwDSParam
* pFound
= nullptr;
2064 //check if it's the merge data source
2065 if(pImpl
->pMergeData
&&
2066 rSourceName
== pImpl
->pMergeData
->sDataSource
&&
2067 rTableName
== pImpl
->pMergeData
->sCommand
)
2069 pFound
= pImpl
->pMergeData
;
2074 aData
.sDataSource
= rSourceName
;
2075 aData
.sCommand
= rTableName
;
2076 aData
.nCommandType
= -1;
2077 pFound
= FindDSData(aData
, false);
2081 //check validity of supplied record Id
2082 if(pFound
->aSelection
.getLength())
2084 //the destination has to be an element of the selection
2085 const uno::Any
* pSelection
= pFound
->aSelection
.getConstArray();
2086 bool bFound
= false;
2087 for(sal_Int32 nPos
= 0; !bFound
&& nPos
< pFound
->aSelection
.getLength(); nPos
++)
2089 sal_Int32 nSelection
= 0;
2090 pSelection
[nPos
] >>= nSelection
;
2091 if(nSelection
== static_cast<sal_Int32
>(nAbsRecordId
))
2097 if( pFound
->HasValidRecord() )
2099 sal_Int32 nOldRow
= 0;
2102 nOldRow
= pFound
->xResultSet
->getRow();
2104 catch(const uno::Exception
&)
2108 //position to the desired index
2110 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2111 bMove
= lcl_MoveAbsolute(pFound
, nAbsRecordId
);
2113 bRet
= lcl_GetColumnCnt(pFound
, rColumnName
, nLanguage
, rResult
, pNumber
);
2114 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2115 lcl_MoveAbsolute(pFound
, nOldRow
);
2120 // reads the column data at the current position
2121 bool SwDBManager::GetMergeColumnCnt(const OUString
& rColumnName
, LanguageType nLanguage
,
2122 OUString
&rResult
, double *pNumber
)
2124 if( !IsValidMergeRecord() )
2130 bool bRet
= lcl_GetColumnCnt(pImpl
->pMergeData
, rColumnName
, nLanguage
, rResult
, pNumber
);
2134 bool SwDBManager::ToNextMergeRecord()
2136 assert( pImpl
->pMergeData
&& pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2137 return lcl_ToNextRecord( pImpl
->pMergeData
);
2140 bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter
*pDocFormatter
,
2141 LanguageType nLanguage
, SwCalc
&rCalc
)
2143 if( !IsValidMergeRecord() )
2146 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
2147 if( !xColsSupp
.is() )
2151 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
2152 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
2153 const OUString
* pColNames
= aColNames
.getConstArray();
2156 // add the "record number" variable, as SwCalc::VarLook would.
2157 rCalc
.VarChange( GetAppCharClass().lowercase(
2158 SwFieldType::GetTypeStr(TYP_DBSETNUMBERFLD
) ), GetSelectedRecordId() );
2160 for( int nCol
= 0; nCol
< aColNames
.getLength(); nCol
++ )
2162 // get the column type
2163 sal_Int32 nColumnType
= sdbc::DataType::SQLNULL
;
2164 uno::Any aCol
= xCols
->getByName( pColNames
[nCol
] );
2165 uno::Reference
<beans::XPropertySet
> xColumnProps
;
2166 aCol
>>= xColumnProps
;
2167 uno::Any aType
= xColumnProps
->getPropertyValue( "Type" );
2168 aType
>>= nColumnType
;
2169 double aNumber
= DBL_MAX
;
2171 lcl_GetColumnCnt( pImpl
->pMergeData
, xColumnProps
, nLanguage
, aString
, &aNumber
);
2173 sal_uInt32 nFormat
= GetColumnFormat( pImpl
->pMergeData
->sDataSource
,
2174 pImpl
->pMergeData
->sCommand
,
2175 pColNames
[nCol
], pDocFormatter
, nLanguage
);
2176 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2177 bool colIsNumber
= aNumber
!= DBL_MAX
;
2178 bool bValidValue
= SwDBField::FormatValue( pDocFormatter
, aString
, nFormat
,
2179 aNumber
, nColumnType
);
2185 aValue
.PutDouble( aNumber
);
2186 aValue
.SetDBvalue( true );
2187 SAL_INFO( "sw.ui", "'" << pColNames
[nCol
] << "': " << aNumber
<< " / " << aString
);
2188 rCalc
.VarChange( pColNames
[nCol
], aValue
);
2194 aValue
.PutString( aString
);
2195 aValue
.SetDBvalue( true );
2196 SAL_INFO( "sw.ui", "'" << pColNames
[nCol
] << "': " << aString
);
2197 rCalc
.VarChange( pColNames
[nCol
], aValue
);
2205 bool SwDBManager::ToNextRecord(
2206 const OUString
& rDataSource
, const OUString
& rCommand
)
2208 SwDSParam
* pFound
= nullptr;
2209 if(pImpl
->pMergeData
&&
2210 rDataSource
== pImpl
->pMergeData
->sDataSource
&&
2211 rCommand
== pImpl
->pMergeData
->sCommand
)
2213 pFound
= pImpl
->pMergeData
;
2218 aData
.sDataSource
= rDataSource
;
2219 aData
.sCommand
= rCommand
;
2220 aData
.nCommandType
= -1;
2221 pFound
= FindDSData(aData
, false);
2223 return lcl_ToNextRecord( pFound
);
2226 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
)
2230 assert( SwDBNextRecord::NEXT
== action
||
2231 (SwDBNextRecord::FIRST
== action
&& pParam
) );
2232 if( nullptr == pParam
)
2235 if( action
== SwDBNextRecord::FIRST
)
2237 pParam
->nSelectionIndex
= 0;
2238 pParam
->bEndOfDB
= false;
2241 if( !pParam
->HasValidRecord() )
2246 if( pParam
->aSelection
.getLength() )
2248 if( pParam
->nSelectionIndex
>= pParam
->aSelection
.getLength() )
2249 pParam
->bEndOfDB
= true;
2253 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
2254 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
2257 else if( action
== SwDBNextRecord::FIRST
)
2259 pParam
->bEndOfDB
= !pParam
->xResultSet
->first();
2263 sal_Int32 nBefore
= pParam
->xResultSet
->getRow();
2264 pParam
->bEndOfDB
= !pParam
->xResultSet
->next();
2265 if( !pParam
->bEndOfDB
&& nBefore
== pParam
->xResultSet
->getRow() )
2267 // next returned true but it didn't move
2268 ::dbtools::throwFunctionSequenceException( pParam
->xResultSet
);
2272 ++pParam
->nSelectionIndex
;
2273 bRet
= !pParam
->bEndOfDB
;
2275 catch( const uno::Exception
&e
)
2277 pParam
->bEndOfDB
= true;
2279 // we allow merging with empty databases, so don't warn on init
2280 SAL_WARN_IF(action
== SwDBNextRecord::NEXT
,
2281 "sw.mailmerge", "exception in ToNextRecord(): " << e
);
2286 // synchronized labels contain a next record field at their end
2287 // to assure that the next page can be created in mail merge
2288 // the cursor position must be validated
2289 bool SwDBManager::IsValidMergeRecord() const
2291 return( pImpl
->pMergeData
&& pImpl
->pMergeData
->HasValidRecord() );
2294 sal_uInt32
SwDBManager::GetSelectedRecordId()
2296 sal_uInt32 nRet
= 0;
2297 assert( pImpl
->pMergeData
&&
2298 pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2299 if(!pImpl
->pMergeData
|| !pImpl
->pMergeData
->xResultSet
.is())
2303 nRet
= pImpl
->pMergeData
->xResultSet
->getRow();
2305 catch(const uno::Exception
&)
2311 bool SwDBManager::ToRecordId(sal_Int32 nSet
)
2313 assert( pImpl
->pMergeData
&&
2314 pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2315 if(!pImpl
->pMergeData
|| !pImpl
->pMergeData
->xResultSet
.is()|| nSet
< 0)
2318 sal_Int32 nAbsPos
= nSet
;
2322 bRet
= lcl_MoveAbsolute(pImpl
->pMergeData
, nAbsPos
);
2323 pImpl
->pMergeData
->bEndOfDB
= !bRet
;
2328 bool SwDBManager::OpenDataSource(const OUString
& rDataSource
, const OUString
& rTableOrQuery
)
2331 aData
.sDataSource
= rDataSource
;
2332 aData
.sCommand
= rTableOrQuery
;
2333 aData
.nCommandType
= -1;
2335 SwDSParam
* pFound
= FindDSData(aData
, true);
2336 if(pFound
->xResultSet
.is())
2338 SwDSParam
* pParam
= FindDSConnection(rDataSource
, false);
2339 if(pParam
&& pParam
->xConnection
.is())
2340 pFound
->xConnection
= pParam
->xConnection
;
2341 if(pFound
->xConnection
.is())
2345 uno::Reference
< sdbc::XDatabaseMetaData
> xMetaData
= pFound
->xConnection
->getMetaData();
2348 pFound
->bScrollable
= xMetaData
2349 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE
));
2351 catch(const uno::Exception
&)
2353 // DB driver may not be ODBC 3.0 compliant
2354 pFound
->bScrollable
= true;
2356 pFound
->xStatement
= pFound
->xConnection
->createStatement();
2357 OUString aQuoteChar
= xMetaData
->getIdentifierQuoteString();
2358 OUString
sStatement("SELECT * FROM ");
2359 sStatement
= "SELECT * FROM ";
2360 sStatement
+= aQuoteChar
;
2361 sStatement
+= rTableOrQuery
;
2362 sStatement
+= aQuoteChar
;
2363 pFound
->xResultSet
= pFound
->xStatement
->executeQuery( sStatement
);
2365 //after executeQuery the cursor must be positioned
2366 pFound
->bEndOfDB
= !pFound
->xResultSet
->next();
2367 ++pFound
->nSelectionIndex
;
2369 catch (const uno::Exception
&)
2371 pFound
->xResultSet
= nullptr;
2372 pFound
->xStatement
= nullptr;
2373 pFound
->xConnection
= nullptr;
2376 return pFound
->xResultSet
.is();
2379 uno::Reference
< sdbc::XConnection
> const & SwDBManager::RegisterConnection(OUString
const& rDataSource
)
2381 SwDSParam
* pFound
= SwDBManager::FindDSConnection(rDataSource
, true);
2382 uno::Reference
< sdbc::XDataSource
> xSource
;
2383 if(!pFound
->xConnection
.is())
2385 pFound
->xConnection
= SwDBManager::GetConnection(rDataSource
, xSource
);
2388 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2390 xComponent
->addEventListener(pImpl
->m_xDisposeListener
.get());
2392 catch(const uno::Exception
&)
2396 return pFound
->xConnection
;
2399 sal_uInt32
SwDBManager::GetSelectedRecordId(
2400 const OUString
& rDataSource
, const OUString
& rTableOrQuery
, sal_Int32 nCommandType
)
2402 sal_uInt32 nRet
= 0xffffffff;
2403 //check for merge data source first
2404 if(pImpl
->pMergeData
&&
2405 ((rDataSource
== pImpl
->pMergeData
->sDataSource
&&
2406 rTableOrQuery
== pImpl
->pMergeData
->sCommand
) ||
2407 (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty())) &&
2408 (nCommandType
== -1 || nCommandType
== pImpl
->pMergeData
->nCommandType
) &&
2409 pImpl
->pMergeData
->xResultSet
.is())
2411 nRet
= GetSelectedRecordId();
2416 aData
.sDataSource
= rDataSource
;
2417 aData
.sCommand
= rTableOrQuery
;
2418 aData
.nCommandType
= nCommandType
;
2419 SwDSParam
* pFound
= FindDSData(aData
, false);
2420 if(pFound
&& pFound
->xResultSet
.is())
2423 { //if a selection array is set the current row at the result set may not be set yet
2424 if(pFound
->aSelection
.getLength())
2426 sal_Int32 nSelIndex
= pFound
->nSelectionIndex
;
2427 if(nSelIndex
>= pFound
->aSelection
.getLength())
2428 nSelIndex
= pFound
->aSelection
.getLength() -1;
2429 pFound
->aSelection
.getConstArray()[nSelIndex
] >>= nRet
;
2433 nRet
= pFound
->xResultSet
->getRow();
2435 catch(const uno::Exception
&)
2443 // close all data sources - after fields were updated
2444 void SwDBManager::CloseAll(bool bIncludingMerge
)
2446 //the only thing done here is to reset the selection index
2447 //all connections stay open
2448 for (auto & pParam
: m_DataSourceParams
)
2450 if(bIncludingMerge
|| pParam
.get() != pImpl
->pMergeData
)
2452 pParam
->nSelectionIndex
= 0;
2453 pParam
->bEndOfDB
= false;
2456 if(!bInMerge
&& pParam
->xResultSet
.is())
2457 pParam
->xResultSet
->first();
2459 catch(const uno::Exception
&)
2465 SwDSParam
* SwDBManager::FindDSData(const SwDBData
& rData
, bool bCreate
)
2467 //prefer merge data if available
2468 if(pImpl
->pMergeData
&&
2469 ((rData
.sDataSource
== pImpl
->pMergeData
->sDataSource
&&
2470 rData
.sCommand
== pImpl
->pMergeData
->sCommand
) ||
2471 (rData
.sDataSource
.isEmpty() && rData
.sCommand
.isEmpty())) &&
2472 (rData
.nCommandType
== -1 || rData
.nCommandType
== pImpl
->pMergeData
->nCommandType
||
2473 (bCreate
&& pImpl
->pMergeData
->nCommandType
== -1)))
2475 return pImpl
->pMergeData
;
2478 SwDSParam
* pFound
= nullptr;
2479 for (size_t nPos
= m_DataSourceParams
.size(); nPos
; nPos
--)
2481 SwDSParam
* pParam
= m_DataSourceParams
[nPos
- 1].get();
2482 if(rData
.sDataSource
== pParam
->sDataSource
&&
2483 rData
.sCommand
== pParam
->sCommand
&&
2484 (rData
.nCommandType
== -1 || rData
.nCommandType
== pParam
->nCommandType
||
2485 (bCreate
&& pParam
->nCommandType
== -1)))
2487 // calls from the calculator may add a connection with an invalid commandtype
2488 //later added "real" data base connections have to re-use the already available
2489 //DSData and set the correct CommandType
2490 if(bCreate
&& pParam
->nCommandType
== -1)
2491 pParam
->nCommandType
= rData
.nCommandType
;
2500 pFound
= new SwDSParam(rData
);
2501 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2504 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2506 xComponent
->addEventListener(pImpl
->m_xDisposeListener
.get());
2508 catch(const uno::Exception
&)
2516 SwDSParam
* SwDBManager::FindDSConnection(const OUString
& rDataSource
, bool bCreate
)
2518 //prefer merge data if available
2519 if(pImpl
->pMergeData
&& rDataSource
== pImpl
->pMergeData
->sDataSource
)
2521 SetAsUsed(rDataSource
);
2522 return pImpl
->pMergeData
;
2524 SwDSParam
* pFound
= nullptr;
2525 for (auto & pParam
: m_DataSourceParams
)
2527 if(rDataSource
== pParam
->sDataSource
)
2529 SetAsUsed(rDataSource
);
2530 pFound
= pParam
.get();
2534 if(bCreate
&& !pFound
)
2537 aData
.sDataSource
= rDataSource
;
2538 pFound
= new SwDSParam(aData
);
2539 SetAsUsed(rDataSource
);
2540 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2543 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2545 xComponent
->addEventListener(pImpl
->m_xDisposeListener
.get());
2547 catch(const uno::Exception
&)
2554 const SwDBData
& SwDBManager::GetAddressDBName()
2556 return SW_MOD()->GetDBConfig()->GetAddressSource();
2559 uno::Sequence
<OUString
> SwDBManager::GetExistingDatabaseNames()
2561 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2562 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2563 return xDBContext
->getElementNames();
2568 DBConnURIType
GetDBunoType(const INetURLObject
&rURL
)
2570 OUString
sExt(rURL
.GetExtension());
2571 DBConnURIType type
= DBConnURIType::UNKNOWN
;
2575 type
= DBConnURIType::ODB
;
2577 else if (sExt
.equalsIgnoreAsciiCase("sxc")
2578 || sExt
.equalsIgnoreAsciiCase("ods")
2579 || sExt
.equalsIgnoreAsciiCase("xls")
2580 || sExt
.equalsIgnoreAsciiCase("xlsx"))
2582 type
= DBConnURIType::CALC
;
2584 else if (sExt
.equalsIgnoreAsciiCase("sxw") || sExt
.equalsIgnoreAsciiCase("odt") || sExt
.equalsIgnoreAsciiCase("doc") || sExt
.equalsIgnoreAsciiCase("docx"))
2586 type
= DBConnURIType::WRITER
;
2588 else if (sExt
.equalsIgnoreAsciiCase("dbf"))
2590 type
= DBConnURIType::DBASE
;
2592 else if (sExt
.equalsIgnoreAsciiCase("csv") || sExt
.equalsIgnoreAsciiCase("txt"))
2594 type
= DBConnURIType::FLAT
;
2597 else if (sExt
.equalsIgnoreAsciiCase("mdb") || sExt
.equalsIgnoreAsciiCase("mde"))
2599 type
= DBConnURIType::MSJET
;
2601 else if (sExt
.equalsIgnoreAsciiCase("accdb") || sExt
.equalsIgnoreAsciiCase("accde"))
2603 type
= DBConnURIType::MSACE
;
2612 uno::Any
GetDBunoURI(const INetURLObject
&rURL
, DBConnURIType
& rType
)
2616 if (rType
== DBConnURIType::UNKNOWN
)
2617 rType
= GetDBunoType(rURL
);
2620 case DBConnURIType::UNKNOWN
:
2621 case DBConnURIType::ODB
:
2623 case DBConnURIType::CALC
:
2625 OUString
sDBURL("sdbc:calc:");
2626 sDBURL
+= rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2630 case DBConnURIType::WRITER
:
2632 OUString
sDBURL("sdbc:writer:");
2633 sDBURL
+= rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2637 case DBConnURIType::DBASE
:
2639 INetURLObject
aUrlTmp(rURL
);
2640 aUrlTmp
.removeSegment();
2641 aUrlTmp
.removeFinalSlash();
2642 OUString
sDBURL("sdbc:dbase:");
2643 sDBURL
+= aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2647 case DBConnURIType::FLAT
:
2649 INetURLObject
aUrlTmp(rURL
);
2650 aUrlTmp
.removeSegment();
2651 aUrlTmp
.removeFinalSlash();
2652 OUString
sDBURL("sdbc:flat:");
2653 //only the 'path' has to be added
2654 sDBURL
+= aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2658 case DBConnURIType::MSJET
:
2661 OUString
sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=");
2662 sDBURL
+= rURL
.PathToFileName();
2667 case DBConnURIType::MSACE
:
2670 OUString
sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=");
2671 sDBURL
+= rURL
.PathToFileName();
2680 /// Returns the URL of this SwDoc.
2681 OUString
getOwnURL(SfxObjectShell
const * pDocShell
)
2688 const INetURLObject
& rURLObject
= pDocShell
->GetMedium()->GetURLObject();
2689 aRet
= rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2694 Loads a data source from file and registers it.
2696 In case of success it returns the registered name, otherwise an empty string.
2697 Optionally add a prefix to the registered DB name.
2699 OUString
LoadAndRegisterDataSource_Impl(DBConnURIType type
, const uno::Reference
< beans::XPropertySet
> *pSettings
,
2700 const INetURLObject
&rURL
, const OUString
*pPrefix
, const OUString
*pDestDir
, SfxObjectShell
* pDocShell
)
2702 OUString
sExt(rURL
.GetExtension());
2703 uno::Any aTableFilterAny
;
2704 uno::Any aSuppressVersionsAny
;
2708 uno::Sequence
<OUString
> aFilters(1);
2710 uno::Any aURLAny
= GetDBunoURI(rURL
, type
);
2712 case DBConnURIType::UNKNOWN
:
2713 case DBConnURIType::CALC
:
2714 case DBConnURIType::WRITER
:
2716 case DBConnURIType::ODB
:
2719 case DBConnURIType::FLAT
:
2720 case DBConnURIType::DBASE
:
2721 //set the filter to the file name without extension
2722 aFilters
[0] = rURL
.getBase();
2723 aTableFilterAny
<<= aFilters
;
2725 case DBConnURIType::MSJET
:
2726 case DBConnURIType::MSACE
:
2727 aSuppressVersionsAny
<<= true;
2733 uno::Reference
<uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext());
2734 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2736 OUString sNewName
= INetURLObject::decode(rURL
.getName(),
2737 INetURLObject::DecodeMechanism::Unambiguous
);
2738 sal_Int32 nExtLen
= sExt
.getLength();
2739 sNewName
= sNewName
.replaceAt(sNewName
.getLength() - nExtLen
- 1, nExtLen
+ 1, "");
2741 sNewName
= *pPrefix
+ sNewName
;
2743 //find a unique name if sNewName already exists
2745 sal_Int32 nIndex
= 0;
2746 while (xDBContext
->hasByName(sFind
))
2749 sFind
+= OUString::number(++nIndex
);
2752 uno::Reference
<uno::XInterface
> xNewInstance
;
2756 uno::Any aDataSource
= xDBContext
->getByName(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
2757 aDataSource
>>= xNewInstance
;
2761 xNewInstance
= xDBContext
->createInstance();
2762 uno::Reference
<beans::XPropertySet
> xDataProperties(xNewInstance
, uno::UNO_QUERY
);
2764 if (aURLAny
.hasValue())
2765 xDataProperties
->setPropertyValue("URL", aURLAny
);
2766 if (aTableFilterAny
.hasValue())
2767 xDataProperties
->setPropertyValue("TableFilter", aTableFilterAny
);
2768 if (aSuppressVersionsAny
.hasValue())
2769 xDataProperties
->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny
);
2770 if (aInfoAny
.hasValue())
2771 xDataProperties
->setPropertyValue("Info", aInfoAny
);
2773 if (DBConnURIType::FLAT
== type
&& pSettings
)
2775 uno::Any aSettings
= xDataProperties
->getPropertyValue("Settings");
2776 uno::Reference
< beans::XPropertySet
> xDSSettings
;
2777 aSettings
>>= xDSSettings
;
2778 ::comphelper::copyProperties(*pSettings
, xDSSettings
);
2779 xDSSettings
->setPropertyValue("Extension", uno::makeAny(sExt
));
2782 uno::Reference
<sdb::XDocumentDataSource
> xDS(xNewInstance
, uno::UNO_QUERY_THROW
);
2783 uno::Reference
<frame::XStorable
> xStore(xDS
->getDatabaseDocument(), uno::UNO_QUERY_THROW
);
2784 OUString aOwnURL
= getOwnURL(pDocShell
);
2785 if (aOwnURL
.isEmpty())
2787 // Cannot embed, as embedded data source would need the URL of the parent document.
2788 OUString
const sOutputExt
= ".odb";
2789 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2790 utl::TempFile
aTempFile(sNewName
, true, &sOutputExt
, pDestDir
? pDestDir
: &sHomePath
);
2791 OUString sTmpName
= aTempFile
.GetURL();
2792 xStore
->storeAsURL(sTmpName
, uno::Sequence
<beans::PropertyValue
>());
2797 OUString aStreamRelPath
= "EmbeddedDatabase";
2798 uno::Reference
<embed::XStorage
> xStorage
= pDocShell
->GetStorage();
2800 // Refer to the sub-storage name in the document settings, so
2801 // we can load it again next time the file is imported.
2802 uno::Reference
<lang::XMultiServiceFactory
> xFactory(pDocShell
->GetModel(), uno::UNO_QUERY
);
2803 uno::Reference
<beans::XPropertySet
> xPropertySet(xFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
2804 xPropertySet
->setPropertyValue("EmbeddedDatabaseName", uno::makeAny(aStreamRelPath
));
2806 // Store it only after setting the above property, so that only one data source gets registered.
2807 SwDBManager::StoreEmbeddedDataSource(xStore
, xStorage
, aStreamRelPath
, aOwnURL
);
2810 xDBContext
->registerObject(sFind
, xNewInstance
);
2812 catch (const uno::Exception
&)
2819 // Construct vnd.sun.star.pkg:// URL
2820 OUString
ConstructVndSunStarPkgUrl(const OUString
& rMainURL
, const OUString
& rStreamRelPath
)
2822 auto xContext(comphelper::getProcessComponentContext());
2823 auto xUri
= css::uri::UriReferenceFactory::create(xContext
)->parse(rMainURL
);
2825 xUri
= css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext
)
2826 ->createVndSunStarPkgUrlReference(xUri
);
2828 return xUri
->getUriReference() + "/"
2829 + INetURLObject::encode(
2830 rStreamRelPath
, INetURLObject::PART_FPATH
,
2831 INetURLObject::EncodeMechanism::All
);
2835 OUString
SwDBManager::LoadAndRegisterDataSource(const vcl::Window
* pParent
, SwDocShell
* pDocShell
)
2837 sfx2::FileDialogHelper
aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
, FileDialogFlags::NONE
, pParent
);
2838 uno::Reference
< ui::dialogs::XFilePicker3
> xFP
= aDlgHelper
.GetFilePicker();
2840 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2841 aDlgHelper
.SetDisplayDirectory( sHomePath
);
2843 uno::Reference
<ui::dialogs::XFilterManager
> xFltMgr(xFP
, uno::UNO_QUERY
);
2845 OUString
sFilterAll(SwResId(STR_FILTER_ALL
));
2846 OUString
sFilterAllData(SwResId(STR_FILTER_ALL_DATA
));
2847 OUString
sFilterSXB(SwResId(STR_FILTER_SXB
));
2848 OUString
sFilterSXC(SwResId(STR_FILTER_SXC
));
2849 OUString
sFilterSXW(SwResId(STR_FILTER_SXW
));
2850 OUString
sFilterDBF(SwResId(STR_FILTER_DBF
));
2851 OUString
sFilterXLS(SwResId(STR_FILTER_XLS
));
2852 OUString
sFilterDOC(SwResId(STR_FILTER_DOC
));
2853 OUString
sFilterTXT(SwResId(STR_FILTER_TXT
));
2854 OUString
sFilterCSV(SwResId(STR_FILTER_CSV
));
2856 OUString
sFilterMDB(SwResId(STR_FILTER_MDB
));
2857 OUString
sFilterACCDB(SwResId(STR_FILTER_ACCDB
));
2859 xFltMgr
->appendFilter( sFilterAll
, "*" );
2860 xFltMgr
->appendFilter( sFilterAllData
, "*.ods;*.sxc;*.odt;*.sxw;*.dbf;*.xls;*.xlsx;*.doc;*.docx;*.txt;*.csv");
2862 xFltMgr
->appendFilter( sFilterSXB
, "*.odb" );
2863 xFltMgr
->appendFilter( sFilterSXC
, "*.ods;*.sxc" );
2864 xFltMgr
->appendFilter( sFilterSXW
, "*.odt;*.sxw" );
2865 xFltMgr
->appendFilter( sFilterDBF
, "*.dbf" );
2866 xFltMgr
->appendFilter( sFilterXLS
, "*.xls;*.xlsx" );
2867 xFltMgr
->appendFilter( sFilterDOC
, "*.doc;*.docx" );
2868 xFltMgr
->appendFilter( sFilterTXT
, "*.txt" );
2869 xFltMgr
->appendFilter( sFilterCSV
, "*.csv" );
2871 xFltMgr
->appendFilter(sFilterMDB
, "*.mdb;*.mde");
2872 xFltMgr
->appendFilter(sFilterACCDB
, "*.accdb;*.accde");
2875 xFltMgr
->setCurrentFilter( sFilterAll
) ;
2877 if( ERRCODE_NONE
== aDlgHelper
.Execute() )
2879 uno::Reference
< beans::XPropertySet
> aSettings
;
2880 const INetURLObject
aURL( xFP
->getSelectedFiles().getConstArray()[0] );
2881 const DBConnURIType type
= GetDBunoType( aURL
);
2883 if( DBConnURIType::FLAT
== type
)
2885 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2886 uno::Reference
< sdb::XTextConnectionSettings
> xSettingsDlg
= sdb::TextConnectionSettings::create(xContext
);
2887 if( xSettingsDlg
->execute() )
2888 aSettings
.set( uno::Reference
< beans::XPropertySet
>( xSettingsDlg
, uno::UNO_QUERY_THROW
) );
2890 sFind
= LoadAndRegisterDataSource_Impl( type
, DBConnURIType::FLAT
== type
? &aSettings
: nullptr, aURL
, nullptr, nullptr, pDocShell
);
2892 m_aUncommitedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(pDocShell
, sFind
));
2897 void SwDBManager::StoreEmbeddedDataSource(const uno::Reference
<frame::XStorable
>& xStorable
,
2898 const uno::Reference
<embed::XStorage
>& xStorage
,
2899 const OUString
& rStreamRelPath
,
2900 const OUString
& rOwnURL
)
2902 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2903 OUString
const sTmpName
= ConstructVndSunStarPkgUrl(rOwnURL
, rStreamRelPath
);
2905 uno::Sequence
<beans::PropertyValue
> aSequence
= comphelper::InitPropertySequence(
2907 {"TargetStorage", uno::makeAny(xStorage
)},
2908 {"StreamRelPath", uno::makeAny(rStreamRelPath
)},
2909 {"BaseURI", uno::makeAny(rOwnURL
)}
2911 xStorable
->storeAsURL(sTmpName
, aSequence
);
2914 OUString
SwDBManager::LoadAndRegisterDataSource(const OUString
&rURI
, const OUString
*pDestDir
)
2916 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN
, nullptr, INetURLObject(rURI
), nullptr, pDestDir
, nullptr );
2919 void SwDBManager::RevokeDataSource(const OUString
& rName
)
2921 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2922 if (xDatabaseContext
->hasByName(rName
))
2923 xDatabaseContext
->revokeObject(rName
);
2926 void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData
& rData
, const SwDocShell
& rDocShell
)
2928 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2930 OUString sDataSource
= rData
.sDataSource
;
2932 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2933 if (sDataSource
.isEmpty())
2934 sDataSource
= "EmbeddedDatabase";
2936 SwDBManager::RevokeDataSource( sDataSource
);
2938 // Encode the stream name and the real path into a single URL.
2939 const INetURLObject
& rURLObject
= rDocShell
.GetMedium()->GetURLObject();
2940 OUString
const aURL
= ConstructVndSunStarPkgUrl(
2941 rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
2944 uno::Reference
<uno::XInterface
> xDataSource(xDatabaseContext
->getByName(aURL
), uno::UNO_QUERY
);
2945 xDatabaseContext
->registerObject( sDataSource
, xDataSource
);
2947 // temp file - don't remember connection
2948 if (rData
.sDataSource
.isEmpty())
2949 m_aUncommitedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(nullptr, sDataSource
));
2952 void SwDBManager::ExecuteFormLetter( SwWrtShell
& rSh
,
2953 const uno::Sequence
<beans::PropertyValue
>& rProperties
)
2955 //prevent second call
2956 if(pImpl
->pMergeDialog
)
2958 OUString sDataSource
, sDataTableOrQuery
;
2959 uno::Sequence
<uno::Any
> aSelection
;
2961 sal_Int32 nCmdType
= sdb::CommandType::TABLE
;
2962 uno::Reference
< sdbc::XConnection
> xConnection
;
2964 svx::ODataAccessDescriptor
aDescriptor(rProperties
);
2965 sDataSource
= aDescriptor
.getDataSource();
2966 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= sDataTableOrQuery
);
2967 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= nCmdType
);
2969 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
2970 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
2971 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
2972 aDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
2974 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty())
2976 OSL_FAIL("PropertyValues missing or unset");
2980 //always create a connection for the dialog and dispose it after the dialog has been closed
2981 SwDSParam
* pFound
= nullptr;
2982 if(!xConnection
.is())
2984 xConnection
= SwDBManager::RegisterConnection(sDataSource
);
2985 pFound
= FindDSConnection(sDataSource
, true);
2987 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
2988 assert( pFact
&& "Factory creation failed!" );
2989 pImpl
->pMergeDialog
= pFact
->CreateMailMergeDlg( &rSh
.GetView().GetViewFrame()->GetWindow(), rSh
,
2994 assert( pImpl
->pMergeDialog
&& "Dialog creation failed!" );
2995 if(pImpl
->pMergeDialog
->Execute() == RET_OK
)
2997 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] <<= pImpl
->pMergeDialog
->GetSelection();
2999 uno::Reference
<sdbc::XResultSet
> xResSet
= pImpl
->pMergeDialog
->GetResultSet();
3001 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
3003 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
3004 SfxObjectShellRef xDocShell
= rSh
.GetView().GetViewFrame()->GetObjectShell();
3006 lcl_emitEvent(SfxEventHintId::SwMailMerge
, STR_SW_EVENT_MAIL_MERGE
, xDocShell
.get());
3008 // prepare mail merge descriptor
3009 SwMergeDescriptor
aMergeDesc( pImpl
->pMergeDialog
->GetMergeType(), rSh
, aDescriptor
);
3010 aMergeDesc
.sSaveToFilter
= pImpl
->pMergeDialog
->GetSaveFilter();
3011 aMergeDesc
.bCreateSingleFile
= pImpl
->pMergeDialog
->IsSaveSingleDoc();
3012 aMergeDesc
.bPrefixIsFilename
= aMergeDesc
.bCreateSingleFile
;
3013 aMergeDesc
.sPrefix
= pImpl
->pMergeDialog
->GetTargetURL();
3014 if( !aMergeDesc
.bCreateSingleFile
&& pImpl
->pMergeDialog
->IsGenerateFromDataBase() )
3016 aMergeDesc
.sDBcolumn
= pImpl
->pMergeDialog
->GetColumnName();
3019 Merge( aMergeDesc
);
3021 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd
, STR_SW_EVENT_MAIL_MERGE_END
, xDocShell
.get());
3023 // reset the cursor inside
3025 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
3029 for (auto & pParam
: m_DataSourceParams
)
3031 if (pParam
.get() == pFound
)
3035 uno::Reference
<lang::XComponent
> xComp(pParam
->xConnection
, uno::UNO_QUERY
);
3039 catch(const uno::RuntimeException
&)
3041 //may be disposed already since multiple entries may have used the same connection
3045 //pFound doesn't need to be removed/deleted -
3046 //this has been done by the SwConnectionDisposedListener_Impl already
3049 pImpl
->pMergeDialog
.disposeAndClear();
3052 void SwDBManager::InsertText(SwWrtShell
& rSh
,
3053 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
3055 OUString sDataSource
, sDataTableOrQuery
;
3056 uno::Reference
<sdbc::XResultSet
> xResSet
;
3057 uno::Sequence
<uno::Any
> aSelection
;
3058 sal_Int16 nCmdType
= sdb::CommandType::TABLE
;
3059 const beans::PropertyValue
* pValues
= rProperties
.getConstArray();
3060 uno::Reference
< sdbc::XConnection
> xConnection
;
3061 for(sal_Int32 nPos
= 0; nPos
< rProperties
.getLength(); nPos
++)
3063 if ( pValues
[nPos
].Name
== "DataSourceName" )
3064 pValues
[nPos
].Value
>>= sDataSource
;
3065 else if ( pValues
[nPos
].Name
== "Command" )
3066 pValues
[nPos
].Value
>>= sDataTableOrQuery
;
3067 else if ( pValues
[nPos
].Name
== "Cursor" )
3068 pValues
[nPos
].Value
>>= xResSet
;
3069 else if ( pValues
[nPos
].Name
== "Selection" )
3070 pValues
[nPos
].Value
>>= aSelection
;
3071 else if ( pValues
[nPos
].Name
== "CommandType" )
3072 pValues
[nPos
].Value
>>= nCmdType
;
3073 else if ( pValues
[nPos
].Name
== "ActiveConnection" )
3074 pValues
[nPos
].Value
>>= xConnection
;
3076 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty() || !xResSet
.is())
3078 OSL_FAIL("PropertyValues missing or unset");
3081 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
3082 uno::Reference
<sdbc::XDataSource
> xSource
;
3083 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
3085 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3087 xSource
= dbtools::getDataSource(sDataSource
, xContext
);
3088 uno::Reference
< sdbcx::XColumnsSupplier
> xColSupp( xResSet
, uno::UNO_QUERY
);
3090 aDBData
.sDataSource
= sDataSource
;
3091 aDBData
.sCommand
= sDataTableOrQuery
;
3092 aDBData
.nCommandType
= nCmdType
;
3094 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
3095 assert( pFact
&& "Factory creation failed!" );
3096 ScopedVclPtr
<AbstractSwInsertDBColAutoPilot
> pDlg(pFact
->CreateSwInsertDBColAutoPilot( rSh
.GetView(),
3100 assert( pDlg
&& "Dialog creation failed!" );
3101 if( RET_OK
== pDlg
->Execute() )
3104 if(!xConnection
.is())
3105 xConnection
= xSource
->getConnection(sDummy
, sDummy
);
3108 pDlg
->DataToDoc( aSelection
, xSource
, xConnection
, xResSet
);
3110 catch (const uno::Exception
& e
)
3112 SAL_WARN("sw.mailmerge", "exception caught: " << e
);
3117 uno::Reference
<sdbc::XDataSource
> SwDBManager::getDataSourceAsParent(const uno::Reference
< sdbc::XConnection
>& _xConnection
,const OUString
& _sDataSourceName
)
3119 uno::Reference
<sdbc::XDataSource
> xSource
;
3122 uno::Reference
<container::XChild
> xChild(_xConnection
, uno::UNO_QUERY
);
3124 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3125 if ( !xSource
.is() )
3126 xSource
= dbtools::getDataSource(_sDataSourceName
, ::comphelper::getProcessComponentContext());
3128 catch (const uno::Exception
& e
)
3130 SAL_WARN("sw.mailmerge", "exception caught in getDataSourceAsParent(): " << e
);
3135 uno::Reference
<sdbc::XResultSet
> SwDBManager::createCursor(const OUString
& _sDataSourceName
,
3136 const OUString
& _sCommand
,
3137 sal_Int32 _nCommandType
,
3138 const uno::Reference
<sdbc::XConnection
>& _xConnection
3141 uno::Reference
<sdbc::XResultSet
> xResultSet
;
3144 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
3147 uno::Reference
<uno::XInterface
> xInstance
= xMgr
->createInstance("com.sun.star.sdb.RowSet");
3148 uno::Reference
<beans::XPropertySet
> xRowSetPropSet(xInstance
, uno::UNO_QUERY
);
3149 if(xRowSetPropSet
.is())
3151 xRowSetPropSet
->setPropertyValue("DataSourceName", uno::makeAny(_sDataSourceName
));
3152 xRowSetPropSet
->setPropertyValue("ActiveConnection", uno::makeAny(_xConnection
));
3153 xRowSetPropSet
->setPropertyValue("Command", uno::makeAny(_sCommand
));
3154 xRowSetPropSet
->setPropertyValue("CommandType", uno::makeAny(_nCommandType
));
3156 uno::Reference
< sdb::XCompletedExecution
> xRowSet(xInstance
, uno::UNO_QUERY
);
3160 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr
), nullptr), uno::UNO_QUERY_THROW
);
3161 xRowSet
->executeWithCompletion(xHandler
);
3163 xResultSet
.set(xRowSet
, uno::UNO_QUERY
);
3167 catch (const uno::Exception
& e
)
3169 SAL_WARN("sw.mailmerge", "Caught exception while creating a new RowSet: " << e
);
3174 void SwDBManager::setEmbeddedName(const OUString
& rEmbeddedName
, SwDocShell
& rDocShell
)
3176 bool bLoad
= m_sEmbeddedName
!= rEmbeddedName
&& !rEmbeddedName
.isEmpty();
3177 bool bRegisterListener
= m_sEmbeddedName
.isEmpty() && !rEmbeddedName
.isEmpty();
3179 m_sEmbeddedName
= rEmbeddedName
;
3183 uno::Reference
<embed::XStorage
> xStorage
= rDocShell
.GetStorage();
3184 // It's OK that we don't have the named sub-storage yet, in case
3185 // we're in the process of creating it.
3186 if (xStorage
->hasByName(rEmbeddedName
))
3187 LoadAndRegisterEmbeddedDataSource(rDocShell
.GetDoc()->GetDBData(), rDocShell
);
3190 if (bRegisterListener
)
3191 // Register a remove listener, so we know when the embedded data source is removed.
3192 pImpl
->m_xDataSourceRemovedListener
= new SwDataSourceRemovedListener(*this);
3195 const OUString
& SwDBManager::getEmbeddedName() const
3197 return m_sEmbeddedName
;
3200 SwDoc
* SwDBManager::getDoc() const
3205 void SwDBManager::releaseRevokeListener()
3207 if (pImpl
->m_xDataSourceRemovedListener
.is())
3209 pImpl
->m_xDataSourceRemovedListener
->Dispose();
3210 pImpl
->m_xDataSourceRemovedListener
.clear();
3214 SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager
& rManager
)
3215 : m_pDBManager(&rManager
)
3219 void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject
& rSource
)
3221 ::SolarMutexGuard aGuard
;
3223 if (!m_pDBManager
) return; // we're disposed too!
3225 uno::Reference
<sdbc::XConnection
> xSource(rSource
.Source
, uno::UNO_QUERY
);
3226 for (size_t nPos
= m_pDBManager
->m_DataSourceParams
.size(); nPos
; nPos
--)
3228 SwDSParam
* pParam
= m_pDBManager
->m_DataSourceParams
[nPos
- 1].get();
3229 if(pParam
->xConnection
.is() &&
3230 (xSource
== pParam
->xConnection
))
3232 m_pDBManager
->m_DataSourceParams
.erase(
3233 m_pDBManager
->m_DataSourceParams
.begin() + nPos
- 1);
3238 std::shared_ptr
<SwMailMergeConfigItem
> SwDBManager::PerformMailMerge(SwView
const * pView
)
3240 std::shared_ptr
<SwMailMergeConfigItem
> xConfigItem
= pView
->GetMailMergeConfigItem();
3244 svx::ODataAccessDescriptor aDescriptor
;
3245 aDescriptor
.setDataSource(xConfigItem
->GetCurrentDBData().sDataSource
);
3246 aDescriptor
[ svx::DataAccessDescriptorProperty::Connection
] <<= xConfigItem
->GetConnection().getTyped();
3247 aDescriptor
[ svx::DataAccessDescriptorProperty::Cursor
] <<= xConfigItem
->GetResultSet();
3248 aDescriptor
[ svx::DataAccessDescriptorProperty::Command
] <<= xConfigItem
->GetCurrentDBData().sCommand
;
3249 aDescriptor
[ svx::DataAccessDescriptorProperty::CommandType
] <<= xConfigItem
->GetCurrentDBData().nCommandType
;
3250 aDescriptor
[ svx::DataAccessDescriptorProperty::Selection
] <<= xConfigItem
->GetSelection();
3252 SwWrtShell
& rSh
= pView
->GetWrtShell();
3253 xConfigItem
->SetTargetView(nullptr);
3255 SwMergeDescriptor
aMergeDesc(DBMGR_MERGE_SHELL
, rSh
, aDescriptor
);
3256 aMergeDesc
.pMailMergeConfigItem
= xConfigItem
.get();
3257 aMergeDesc
.bCreateSingleFile
= true;
3258 rSh
.GetDBManager()->Merge(aMergeDesc
);
3263 void SwDBManager::RevokeLastRegistrations()
3265 if (m_aUncommitedRegistrations
.size())
3267 SwView
* pView
= ( m_pDoc
&& m_pDoc
->GetDocShell() ) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
3270 std::shared_ptr
<SwMailMergeConfigItem
> xConfigItem
= pView
->GetMailMergeConfigItem();
3273 xConfigItem
->DisposeResultSet();
3274 xConfigItem
->DocumentReloaded();
3278 for (auto it
= m_aUncommitedRegistrations
.begin(); it
!= m_aUncommitedRegistrations
.end();)
3280 if ((m_pDoc
&& it
->first
== m_pDoc
->GetDocShell()) || it
->first
== nullptr)
3282 RevokeDataSource(it
->second
);
3283 it
= m_aUncommitedRegistrations
.erase(it
);
3291 void SwDBManager::CommitLastRegistrations()
3293 for (auto aIt
= m_aUncommitedRegistrations
.begin(); aIt
!= m_aUncommitedRegistrations
.end();)
3295 if (aIt
->first
== m_pDoc
->GetDocShell() || aIt
->first
== nullptr)
3297 m_aNotUsedConnections
.push_back(aIt
->second
);
3298 aIt
= m_aUncommitedRegistrations
.erase(aIt
);
3305 void SwDBManager::SetAsUsed(const OUString
& rName
)
3307 auto aFound
= std::find(m_aNotUsedConnections
.begin(), m_aNotUsedConnections
.end(), rName
);
3308 if (aFound
!= m_aNotUsedConnections
.end())
3309 m_aNotUsedConnections
.erase(aFound
);
3312 void SwDBManager::RevokeNotUsedConnections()
3314 for (auto aIt
= m_aNotUsedConnections
.begin(); aIt
!= m_aNotUsedConnections
.end();)
3316 RevokeDataSource(*aIt
);
3317 aIt
= m_aNotUsedConnections
.erase(aIt
);
3321 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */