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>
24 #include <unotxdoc.hxx>
25 #include <sfx2/app.hxx>
26 #include <com/sun/star/sdb/CommandType.hpp>
27 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
28 #include <com/sun/star/lang/DisposedException.hpp>
29 #include <com/sun/star/lang/XEventListener.hpp>
30 #include <com/sun/star/uri/UriReferenceFactory.hpp>
31 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
32 #include <com/sun/star/util/NumberFormatter.hpp>
33 #include <com/sun/star/sdb/DatabaseContext.hpp>
34 #include <com/sun/star/sdb/TextConnectionSettings.hpp>
35 #include <com/sun/star/sdb/XCompletedConnection.hpp>
36 #include <com/sun/star/sdb/XCompletedExecution.hpp>
37 #include <com/sun/star/container/XChild.hpp>
38 #include <com/sun/star/text/MailMergeEvent.hpp>
39 #include <com/sun/star/frame/XStorable.hpp>
40 #include <com/sun/star/task/InteractionHandler.hpp>
41 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
42 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <vcl/errinf.hxx>
45 #include <vcl/print.hxx>
46 #include <vcl/scheduler.hxx>
47 #include <sfx2/fcontnr.hxx>
48 #include <sfx2/filedlghelper.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <dbconfig.hxx>
51 #include <unotools/tempfile.hxx>
52 #include <unotools/pathoptions.hxx>
53 #include <svl/zforlist.hxx>
54 #include <svl/stritem.hxx>
55 #include <sfx2/docfile.hxx>
56 #include <sfx2/docfilt.hxx>
57 #include <sfx2/progress.hxx>
58 #include <sfx2/dispatch.hxx>
60 #include <swmodule.hxx>
69 #include <IDocumentLinksAdministration.hxx>
70 #include <IDocumentFieldsAccess.hxx>
71 #include <IDocumentUndoRedo.hxx>
73 #include <swunohelper.hxx>
74 #include <strings.hrc>
75 #include <mmconfigitem.hxx>
76 #include <com/sun/star/sdbc/XRowSet.hpp>
77 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
78 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
79 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
80 #include <com/sun/star/sdb/XColumn.hpp>
81 #include <com/sun/star/sdbc/DataType.hpp>
82 #include <com/sun/star/sdbc/ResultSetType.hpp>
83 #include <com/sun/star/sdbc/SQLException.hpp>
84 #include <com/sun/star/mail/MailAttachment.hpp>
85 #include <comphelper/processfactory.hxx>
86 #include <comphelper/property.hxx>
87 #include <comphelper/storagehelper.hxx>
88 #include <comphelper/string.hxx>
89 #include <comphelper/types.hxx>
90 #include <mailmergehelper.hxx>
91 #include <maildispatcher.hxx>
92 #include <svtools/htmlcfg.hxx>
93 #include <i18nlangtag/languagetag.hxx>
94 #include <com/sun/star/util/XNumberFormatTypes.hpp>
95 #include <svl/numuno.hxx>
96 #include <connectivity/dbtools.hxx>
97 #include <connectivity/dbconversion.hxx>
98 #include <unotools/charclass.hxx>
99 #include <tools/diagnose_ex.h>
101 #include <unomailmerge.hxx>
102 #include <sfx2/event.hxx>
103 #include <svx/dataaccessdescriptor.hxx>
104 #include <osl/mutex.hxx>
105 #include <rtl/textenc.h>
106 #include <rtl/tencinfo.h>
107 #include <cppuhelper/implbase.hxx>
108 #include <ndindex.hxx>
109 #include <swevent.hxx>
110 #include <sal/log.hxx>
111 #include <swabstdlg.hxx>
113 #include <section.hxx>
114 #include <rootfrm.hxx>
117 #include <IDocumentState.hxx>
118 #include <imaildsplistener.hxx>
119 #include <iodetect.hxx>
120 #include <IDocumentDeviceAccess.hxx>
123 #include <comphelper/propertysequence.hxx>
125 using namespace ::com::sun::star
;
130 void lcl_emitEvent(SfxEventHintId nEventId
, sal_Int32 nStrId
, SfxObjectShell
* pDocShell
)
132 SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId
,
133 SwDocShell::GetEventName(nStrId
),
139 std::vector
<std::pair
<SwDocShell
*, OUString
>> SwDBManager::m_aUncommittedRegistrations
;
143 enum class SwDBNextRecord
{ NEXT
, FIRST
};
147 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
= SwDBNextRecord::NEXT
);
151 enum class WorkingDocType
{ SOURCE
, TARGET
, COPY
};
155 static SfxObjectShell
* lcl_CreateWorkingDocument(
156 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
157 const vcl::Window
*pSourceWindow
,
158 SwDBManager
** const ppDBManager
,
159 SwView
** const pView
, SwWrtShell
** const pWrtShell
, SwDoc
** const pDoc
);
161 static bool lcl_getCountFromResultSet( sal_Int32
& rCount
, const SwDSParam
* pParam
)
163 rCount
= pParam
->aSelection
.getLength();
167 uno::Reference
<beans::XPropertySet
> xPrSet(pParam
->xResultSet
, uno::UNO_QUERY
);
173 uno::Any aFinal
= xPrSet
->getPropertyValue("IsRowCountFinal");
177 pParam
->xResultSet
->last();
178 pParam
->xResultSet
->first();
180 uno::Any aCount
= xPrSet
->getPropertyValue("RowCount");
181 if( aCount
>>= rCount
)
184 catch(const uno::Exception
&)
191 class SwDBManager::ConnectionDisposedListener_Impl
192 : public cppu::WeakImplHelper
< lang::XEventListener
>
195 SwDBManager
* m_pDBManager
;
197 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
) override
;
200 explicit ConnectionDisposedListener_Impl(SwDBManager
& rMgr
);
202 void Dispose() { m_pDBManager
= nullptr; }
208 /// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
209 class SwDataSourceRemovedListener
: public cppu::WeakImplHelper
<sdb::XDatabaseRegistrationsListener
>
211 uno::Reference
<sdb::XDatabaseContext
> m_xDatabaseContext
;
212 SwDBManager
* m_pDBManager
;
215 explicit SwDataSourceRemovedListener(SwDBManager
& rDBManager
);
216 virtual ~SwDataSourceRemovedListener() override
;
217 virtual void SAL_CALL
registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
218 virtual void SAL_CALL
revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
219 virtual void SAL_CALL
changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
220 virtual void SAL_CALL
disposing(const lang::EventObject
& rObject
) override
;
226 SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager
& rDBManager
)
227 : m_pDBManager(&rDBManager
)
229 uno::Reference
<uno::XComponentContext
> xComponentContext(comphelper::getProcessComponentContext());
230 m_xDatabaseContext
= sdb::DatabaseContext::create(xComponentContext
);
231 m_xDatabaseContext
->addDatabaseRegistrationsListener(this);
234 SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
236 if (m_xDatabaseContext
.is())
237 m_xDatabaseContext
->removeDatabaseRegistrationsListener(this);
240 void SAL_CALL
SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& /*rEvent*/)
244 void SAL_CALL
SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
246 if (!m_pDBManager
|| m_pDBManager
->getEmbeddedName().isEmpty())
249 SwDoc
* pDoc
= m_pDBManager
->getDoc();
253 SwDocShell
* pDocShell
= pDoc
->GetDocShell();
257 OUString aOwnURL
= pDocShell
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset
);
258 OUString sTmpName
= "vnd.sun.star.pkg://" +
259 INetURLObject::encode(aOwnURL
, INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
260 sTmpName
+= "/" + m_pDBManager
->getEmbeddedName();
262 if (sTmpName
!= rEvent
.OldLocation
)
265 // The revoked database location is inside this document, then remove the
266 // embedding, as otherwise it would be back on the next reload of the
268 pDocShell
->GetStorage()->removeElement(m_pDBManager
->getEmbeddedName());
269 m_pDBManager
->setEmbeddedName(OUString(), *pDocShell
);
272 void SAL_CALL
SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
274 if (rEvent
.OldLocation
!= rEvent
.NewLocation
)
275 revokedDatabaseLocation(rEvent
);
278 void SwDataSourceRemovedListener::disposing(const lang::EventObject
& /*rObject*/)
280 m_xDatabaseContext
.clear();
283 void SwDataSourceRemovedListener::Dispose()
285 m_pDBManager
= nullptr;
288 struct SwDBManager::SwDBManager_Impl
290 std::unique_ptr
<SwDSParam
> pMergeData
;
291 VclPtr
<AbstractMailMergeDlg
> pMergeDialog
;
292 rtl::Reference
<SwDBManager::ConnectionDisposedListener_Impl
> m_xDisposeListener
;
293 rtl::Reference
<SwDataSourceRemovedListener
> m_xDataSourceRemovedListener
;
294 osl::Mutex m_aAllEmailSendMutex
;
295 uno::Reference
< mail::XMailMessage
> m_xLastMessage
;
297 explicit SwDBManager_Impl(SwDBManager
& rDBManager
)
298 : m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager
))
303 m_xDisposeListener
->Dispose();
304 if (m_xDataSourceRemovedListener
.is())
305 m_xDataSourceRemovedListener
->Dispose();
309 static void lcl_InitNumberFormatter(SwDSParam
& rParam
, uno::Reference
<sdbc::XDataSource
> const & xSource
)
311 uno::Reference
<uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
312 rParam
.xFormatter
= util::NumberFormatter::create(xContext
);
313 uno::Reference
<beans::XPropertySet
> xSourceProps(
316 : SwDBManager::getDataSourceAsParent(
317 rParam
.xConnection
, rParam
.sDataSource
)),
319 if(!xSourceProps
.is())
322 uno::Any aFormats
= xSourceProps
->getPropertyValue("NumberFormatsSupplier");
323 if(!aFormats
.hasValue())
326 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
330 uno::Reference
< beans::XPropertySet
> xSettings
= xSuppl
->getNumberFormatSettings();
331 uno::Any aNull
= xSettings
->getPropertyValue("NullDate");
332 aNull
>>= rParam
.aNullDate
;
333 if(rParam
.xFormatter
.is())
334 rParam
.xFormatter
->attachNumberFormatsSupplier(xSuppl
);
338 static bool lcl_MoveAbsolute(SwDSParam
* pParam
, tools::Long nAbsPos
)
343 if(pParam
->aSelection
.hasElements())
345 if(pParam
->aSelection
.getLength() <= nAbsPos
)
347 pParam
->bEndOfDB
= true;
352 pParam
->nSelectionIndex
= nAbsPos
;
354 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
355 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
356 bRet
= !pParam
->bEndOfDB
;
359 else if(pParam
->bScrollable
)
361 bRet
= pParam
->xResultSet
->absolute( nAbsPos
);
365 OSL_FAIL("no absolute positioning available");
368 catch(const uno::Exception
&)
374 static void lcl_GetColumnCnt(SwDSParam
*pParam
,
375 const uno::Reference
< beans::XPropertySet
> &rColumnProps
,
376 LanguageType nLanguage
, OUString
&rResult
, double* pNumber
)
378 SwDBFormatData aFormatData
;
379 if(!pParam
->xFormatter
.is())
381 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(
382 pParam
->xConnection
,pParam
->sDataSource
);
383 lcl_InitNumberFormatter(*pParam
, xSource
);
385 aFormatData
.aNullDate
= pParam
->aNullDate
;
386 aFormatData
.xFormatter
= pParam
->xFormatter
;
388 aFormatData
.aLocale
= LanguageTag( nLanguage
).getLocale();
390 rResult
= SwDBManager::GetDBField( rColumnProps
, aFormatData
, pNumber
);
393 static bool lcl_GetColumnCnt(SwDSParam
* pParam
, const OUString
& rColumnName
,
394 LanguageType nLanguage
, OUString
& rResult
, double* pNumber
)
396 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pParam
->xResultSet
, uno::UNO_QUERY
);
397 uno::Reference
<container::XNameAccess
> xCols
;
400 xCols
= xColsSupp
->getColumns();
402 catch(const lang::DisposedException
&)
405 if(!xCols
.is() || !xCols
->hasByName(rColumnName
))
407 uno::Any aCol
= xCols
->getByName(rColumnName
);
408 uno::Reference
< beans::XPropertySet
> xColumnProps
;
409 aCol
>>= xColumnProps
;
410 lcl_GetColumnCnt( pParam
, xColumnProps
, nLanguage
, rResult
, pNumber
);
415 bool SwDBManager::Merge( const SwMergeDescriptor
& rMergeDesc
)
417 assert( !m_bInMerge
&& !m_pImpl
->pMergeData
&& "merge already activated!" );
419 SfxObjectShellLock xWorkObjSh
;
420 SwWrtShell
*pWorkShell
= nullptr;
421 SwDoc
*pWorkDoc
= nullptr;
422 SwDBManager
*pWorkDocOrigDBManager
= nullptr;
424 switch( rMergeDesc
.nMergeType
)
426 case DBMGR_MERGE_PRINTER
:
427 case DBMGR_MERGE_EMAIL
:
428 case DBMGR_MERGE_FILE
:
429 case DBMGR_MERGE_SHELL
:
431 SwDocShell
*pSourceDocSh
= rMergeDesc
.rSh
.GetView().GetDocShell();
432 if( pSourceDocSh
->IsModified() )
434 pWorkDocOrigDBManager
= this;
435 xWorkObjSh
= lcl_CreateWorkingDocument(
436 WorkingDocType::SOURCE
, rMergeDesc
.rSh
, nullptr,
437 &pWorkDocOrigDBManager
, nullptr, &pWorkShell
, &pWorkDoc
);
443 if( !xWorkObjSh
.Is() )
444 pWorkShell
= &rMergeDesc
.rSh
;
449 aData
.nCommandType
= sdb::CommandType::TABLE
;
450 uno::Reference
<sdbc::XResultSet
> xResSet
;
451 uno::Sequence
<uno::Any
> aSelection
;
452 uno::Reference
< sdbc::XConnection
> xConnection
;
454 aData
.sDataSource
= rMergeDesc
.rDescriptor
.getDataSource();
455 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= aData
.sCommand
;
456 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= aData
.nCommandType
;
458 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Cursor
) )
459 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] >>= xResSet
;
460 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
461 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
462 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
463 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
465 if((aData
.sDataSource
.isEmpty() || aData
.sCommand
.isEmpty()) && !xResSet
.is())
470 m_pImpl
->pMergeData
.reset(new SwDSParam(aData
, xResSet
, aSelection
));
471 SwDSParam
* pTemp
= FindDSData(aData
, false);
473 *pTemp
= *m_pImpl
->pMergeData
;
476 // calls from the calculator may have added a connection with an invalid commandtype
477 //"real" data base connections added here have to re-use the already available
478 //DSData and set the correct CommandType
479 aData
.nCommandType
= -1;
480 pTemp
= FindDSData(aData
, false);
482 *pTemp
= *m_pImpl
->pMergeData
;
485 m_DataSourceParams
.push_back(std::make_unique
<SwDSParam
>(*m_pImpl
->pMergeData
));
488 uno::Reference
<lang::XComponent
> xComponent(m_DataSourceParams
.back()->xConnection
, uno::UNO_QUERY
);
490 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
.get());
492 catch(const uno::Exception
&)
497 if(!m_pImpl
->pMergeData
->xConnection
.is())
498 m_pImpl
->pMergeData
->xConnection
= xConnection
;
499 // add an XEventListener
501 lcl_ToNextRecord(m_pImpl
->pMergeData
.get(), SwDBNextRecord::FIRST
);
503 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
,aData
.sDataSource
);
505 lcl_InitNumberFormatter(*m_pImpl
->pMergeData
, xSource
);
507 pWorkShell
->ChgDBData(aData
);
510 if (IsInitDBFields())
512 // with database fields without DB-Name, use DB-Name from Doc
513 std::vector
<OUString
> aDBNames
;
514 aDBNames
.emplace_back();
515 SwDBData aInsertData
= pWorkShell
->GetDBData();
516 OUString sDBName
= aInsertData
.sDataSource
517 + OUStringChar(DB_DELIM
) + aInsertData
.sCommand
518 + OUStringChar(DB_DELIM
)
519 + OUString::number(aInsertData
.nCommandType
);
520 pWorkShell
->ChangeDBFields( aDBNames
, sDBName
);
521 SetInitDBFields(false);
525 switch(rMergeDesc
.nMergeType
)
528 pWorkShell
->StartAllAction();
529 pWorkShell
->SwViewShell::UpdateFields( true );
530 pWorkShell
->SetModified();
531 pWorkShell
->EndAllAction();
534 case DBMGR_MERGE_PRINTER
:
535 case DBMGR_MERGE_EMAIL
:
536 case DBMGR_MERGE_FILE
:
537 case DBMGR_MERGE_SHELL
:
538 // save files and send them as e-Mail if required
539 bRet
= MergeMailFiles(pWorkShell
, rMergeDesc
);
543 // insert selected entries
544 // (was: InsertRecord)
545 ImportFromConnection(pWorkShell
);
549 m_pImpl
->pMergeData
.reset();
551 if( xWorkObjSh
.Is() )
553 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
554 xWorkObjSh
->DoClose();
562 void SwDBManager::ImportFromConnection( SwWrtShell
* pSh
)
564 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
567 pSh
->StartAllAction();
569 bool bGroupUndo(pSh
->DoesGroupUndo());
570 pSh
->DoGroupUndo(false);
572 if( pSh
->HasSelection() )
575 std::unique_ptr
<SwWait
> pWait
;
583 pWait
.reset(new SwWait( *pSh
->GetView().GetDocShell(), true));
585 } while(ToNextMergeRecord());
588 pSh
->DoGroupUndo(bGroupUndo
);
593 void SwDBManager::ImportDBEntry(SwWrtShell
* pSh
)
595 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
598 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
599 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
601 uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
602 const OUString
* pColNames
= aColNames
.getConstArray();
603 tools::Long nLength
= aColNames
.getLength();
604 for(tools::Long i
= 0; i
< nLength
; i
++)
606 uno::Any aCol
= xCols
->getByName(pColNames
[i
]);
607 uno::Reference
< beans::XPropertySet
> xColumnProp
;
608 aCol
>>= xColumnProp
;
609 SwDBFormatData aDBFormat
;
610 sStr
.append(GetDBField( xColumnProp
, aDBFormat
));
614 pSh
->SwEditShell::Insert2(sStr
.makeStringAndClear());
615 pSh
->SwFEShell::SplitNode(); // line feed
618 bool SwDBManager::GetTableNames(weld::ComboBox
& rBox
, const OUString
& rDBName
)
621 OUString
sOldTableName(rBox
.get_active_text());
623 SwDSParam
* pParam
= FindDSConnection(rDBName
, false);
624 uno::Reference
< sdbc::XConnection
> xConnection
;
625 if (pParam
&& pParam
->xConnection
.is())
626 xConnection
= pParam
->xConnection
;
629 if ( !rDBName
.isEmpty() )
630 xConnection
= RegisterConnection( rDBName
);
632 if (xConnection
.is())
634 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
637 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
638 const uno::Sequence
<OUString
> aTables
= xTables
->getElementNames();
639 for (const OUString
& rTable
: aTables
)
640 rBox
.append("0", rTable
);
642 uno::Reference
<sdb::XQueriesSupplier
> xQSupplier(xConnection
, uno::UNO_QUERY
);
645 uno::Reference
<container::XNameAccess
> xQueries
= xQSupplier
->getQueries();
646 const uno::Sequence
<OUString
> aQueries
= xQueries
->getElementNames();
647 for (const OUString
& rQuery
: aQueries
)
648 rBox
.append("1", rQuery
);
650 if (!sOldTableName
.isEmpty())
651 rBox
.set_active_text(sOldTableName
);
657 // fill Listbox with column names of a database
658 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
659 const OUString
& rDBName
, const OUString
& rTableName
)
662 aData
.sDataSource
= rDBName
;
663 aData
.sCommand
= rTableName
;
664 aData
.nCommandType
= -1;
665 SwDSParam
* pParam
= FindDSData(aData
, false);
666 uno::Reference
< sdbc::XConnection
> xConnection
;
667 if(pParam
&& pParam
->xConnection
.is())
668 xConnection
= pParam
->xConnection
;
671 xConnection
= RegisterConnection( rDBName
);
673 GetColumnNames(rBox
, xConnection
, rTableName
);
676 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
677 uno::Reference
< sdbc::XConnection
> const & xConnection
,
678 const OUString
& rTableName
)
681 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
684 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
685 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
686 for (const OUString
& rColName
: aColNames
)
688 rBox
.append_text(rColName
);
690 ::comphelper::disposeComponent( xColsSupp
);
694 SwDBManager::SwDBManager(SwDoc
* pDoc
)
695 : m_aMergeStatus( MergeStatus::Ok
)
696 , m_bInitDBFields(false)
698 , m_bMergeSilent(false)
699 , m_pImpl(new SwDBManager_Impl(*this))
700 , m_pMergeEvtSrc(nullptr)
705 SwDBManager::~SwDBManager() COVERITY_NOEXCEPT_FALSE
707 RevokeLastRegistrations();
709 // copy required, m_DataSourceParams can be modified while disposing components
710 std::vector
<uno::Reference
<sdbc::XConnection
>> aCopiedConnections
;
711 for (const auto & pParam
: m_DataSourceParams
)
713 if(pParam
->xConnection
.is())
715 aCopiedConnections
.push_back(pParam
->xConnection
);
718 for (const auto & xConnection
: aCopiedConnections
)
722 uno::Reference
<lang::XComponent
> xComp(xConnection
, uno::UNO_QUERY
);
726 catch(const uno::RuntimeException
&)
728 //may be disposed already since multiple entries may have used the same connection
733 static void lcl_RemoveSectionLinks( SwWrtShell
& rWorkShell
)
735 //reset all links of the sections of synchronized labels
736 size_t nSections
= rWorkShell
.GetSectionFormatCount();
737 for (size_t nSection
= 0; nSection
< nSections
; ++nSection
)
739 SwSectionData
aSectionData( *rWorkShell
.GetSectionFormat( nSection
).GetSection() );
740 if( aSectionData
.GetType() == SectionType::FileLink
)
742 aSectionData
.SetType( SectionType::Content
);
743 aSectionData
.SetLinkFileName( OUString() );
744 rWorkShell
.UpdateSection( nSection
, aSectionData
);
747 rWorkShell
.SetLabelDoc( false );
750 static void lcl_SaveDebugDoc( SfxObjectShell
*xTargetDocShell
,
751 const char *name
, int no
= 0 )
753 static OUString sTempDirURL
;
754 if( sTempDirURL
.isEmpty() )
756 SvtPathOptions aPathOpt
;
757 utl::TempFile
aTempDir( &aPathOpt
.GetTempPath(), true );
758 if( aTempDir
.IsValid() )
760 INetURLObject
aTempDirURL( aTempDir
.GetURL() );
761 sTempDirURL
= aTempDirURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
762 SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL
);
765 if( sTempDirURL
.isEmpty() )
768 const OUString
sExt( ".odt" );
769 OUString basename
= OUString::createFromAscii( name
);
771 basename
+= OUString::number(no
) + "-";
772 // aTempFile is not deleted, but that seems to be intentional
773 utl::TempFile
aTempFile( basename
, true, &sExt
, &sTempDirURL
);
774 INetURLObject
aTempFileURL( aTempFile
.GetURL() );
775 auto pDstMed
= std::make_unique
<SfxMedium
>(
776 aTempFileURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
777 StreamMode::STD_READWRITE
);
778 bool bAnyError
= !xTargetDocShell
->DoSaveAs( *pDstMed
);
779 // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
780 bAnyError
|= (ERRCODE_NONE
!= xTargetDocShell
->GetError());
782 SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile
.GetURL() );
784 SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile
.GetURL() );
787 static bool lcl_SaveDoc(
788 const INetURLObject
* pFileURL
,
789 const std::shared_ptr
<const SfxFilter
>& pStoreToFilter
,
790 const OUString
* pStoreToFilterOptions
,
791 const uno::Sequence
< beans::PropertyValue
>* pSaveToFilterData
,
792 const bool bIsPDFexport
,
793 SfxObjectShell
* xObjectShell
,
794 SwWrtShell
& rWorkShell
,
795 OUString
* const decodedURL
= nullptr )
797 OUString url
= pFileURL
->GetMainURL( INetURLObject::DecodeMechanism::NONE
);
801 SfxMedium
* pDstMed
= new SfxMedium( url
, StreamMode::STD_READWRITE
);
802 pDstMed
->SetFilter( pStoreToFilter
);
803 if( pDstMed
->GetItemSet() )
805 if( pStoreToFilterOptions
)
806 pDstMed
->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS
,
807 *pStoreToFilterOptions
));
808 if( pSaveToFilterData
->hasElements() )
809 pDstMed
->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA
,
810 uno::makeAny(*pSaveToFilterData
)));
813 // convert fields to text if we are exporting to PDF.
814 // this prevents a second merge while updating the fields
815 // in SwXTextDocument::getRendererCount()
817 rWorkShell
.ConvertFieldsToText();
819 bool bAnyError
= !xObjectShell
->DoSaveAs(*pDstMed
);
820 // Actually this should be a bool... so in case of email and individual
821 // files, where this is set, we skip the recently used handling
822 bAnyError
|= !xObjectShell
->DoSaveCompleted( pDstMed
, !decodedURL
);
823 bAnyError
|= (ERRCODE_NONE
!= xObjectShell
->GetError());
827 ErrorHandler::HandleError( xObjectShell
->GetError() );
832 static void lcl_PreparePrinterOptions(
833 const uno::Sequence
< beans::PropertyValue
>& rInPrintOptions
,
834 uno::Sequence
< beans::PropertyValue
>& rOutPrintOptions
)
836 // printing should be done synchronously otherwise the document
837 // might already become invalid during the process
839 const sal_Int32 nOffset
= 1;
840 rOutPrintOptions
.realloc( nOffset
);
841 rOutPrintOptions
[ 0 ].Name
= "Wait";
842 rOutPrintOptions
[ 0 ].Value
<<= true;
844 // copy print options
845 sal_Int32 nIndex
= nOffset
;
846 for( const beans::PropertyValue
& rOption
: rInPrintOptions
)
848 if( rOption
.Name
== "CopyCount" || rOption
.Name
== "FileName"
849 || rOption
.Name
== "Collate" || rOption
.Name
== "Pages"
850 || rOption
.Name
== "Wait" || rOption
.Name
== "PrinterName" )
853 rOutPrintOptions
.realloc( nIndex
+ 1 );
854 rOutPrintOptions
[ nIndex
].Name
= rOption
.Name
;
855 rOutPrintOptions
[ nIndex
++ ].Value
= rOption
.Value
;
860 static void lcl_PrepareSaveFilterDataOptions(
861 const uno::Sequence
< beans::PropertyValue
>& rInSaveFilterDataptions
,
862 uno::Sequence
< beans::PropertyValue
>& rOutSaveFilterDataOptions
,
863 const OUString
& sPassword
)
865 const sal_Int32 nOffset
= 2;
866 rOutSaveFilterDataOptions
.realloc( nOffset
);
867 rOutSaveFilterDataOptions
[ 0 ].Name
= "EncryptFile";
868 rOutSaveFilterDataOptions
[ 0 ].Value
<<= true;
869 rOutSaveFilterDataOptions
[ 1 ].Name
= "DocumentOpenPassword";
870 rOutSaveFilterDataOptions
[ 1 ].Value
<<= sPassword
;
872 // copy other options
873 sal_Int32 nIndex
= nOffset
;
874 for( const beans::PropertyValue
& rOption
: rInSaveFilterDataptions
)
876 rOutSaveFilterDataOptions
.realloc( nIndex
+ 1 );
877 rOutSaveFilterDataOptions
[ nIndex
].Name
= rOption
.Name
;
878 rOutSaveFilterDataOptions
[ nIndex
++ ].Value
= rOption
.Value
;
884 static SfxObjectShell
* lcl_CreateWorkingDocument(
886 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
888 const vcl::Window
*pSourceWindow
,
889 // optional in and output to swap the DB manager
890 SwDBManager
** const ppDBManager
,
892 SwView
** const pView
, SwWrtShell
** const pWrtShell
, SwDoc
** const pDoc
)
894 const SwDoc
*pSourceDoc
= rSourceWrtShell
.GetDoc();
895 SfxObjectShellRef xWorkObjectShell
= pSourceDoc
->CreateCopy( true, (aType
== WorkingDocType::TARGET
) );
896 SfxViewFrame
* pWorkFrame
= SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell
, SFX_INTERFACE_NONE
);
900 // the created window has to be located at the same position as the source window
901 vcl::Window
& rTargetWindow
= pWorkFrame
->GetFrame().GetWindow();
902 rTargetWindow
.SetPosPixel( pSourceWindow
->GetPosPixel() );
905 SwView
* pWorkView
= static_cast< SwView
* >( pWorkFrame
->GetViewShell() );
906 SwWrtShell
* pWorkWrtShell
= pWorkView
->GetWrtShellPtr();
907 pWorkWrtShell
->GetViewOptions()->SetIdle( false );
908 pWorkView
->AttrChangedNotify(nullptr);// in order for SelectShell to be called
909 SwDoc
* pWorkDoc
= pWorkWrtShell
->GetDoc();
910 pWorkDoc
->GetIDocumentUndoRedo().DoUndo( false );
911 pWorkDoc
->ReplaceDocumentProperties( *pSourceDoc
);
913 // import print settings
914 const SwPrintData
&rPrintData
= pSourceDoc
->getIDocumentDeviceAccess().getPrintData();
915 pWorkDoc
->getIDocumentDeviceAccess().setPrintData(rPrintData
);
916 const JobSetup
*pJobSetup
= pSourceDoc
->getIDocumentDeviceAccess().getJobsetup();
918 pWorkDoc
->getIDocumentDeviceAccess().setJobsetup(*pJobSetup
);
920 if( aType
== WorkingDocType::TARGET
)
922 assert( !ppDBManager
);
923 pWorkDoc
->SetInMailMerge( true );
924 pWorkWrtShell
->SetLabelDoc( false );
928 // We have to swap the DBmanager of the new doc, so we also need input
929 assert(ppDBManager
&& *ppDBManager
);
930 SwDBManager
*pWorkDBManager
= pWorkDoc
->GetDBManager();
931 pWorkDoc
->SetDBManager( *ppDBManager
);
932 *ppDBManager
= pWorkDBManager
;
934 if( aType
== WorkingDocType::SOURCE
)
936 // the GetDBData call constructs the data, if it's missing - kind of const...
937 pWorkWrtShell
->ChgDBData( const_cast<SwDoc
*>(pSourceDoc
)->GetDBData() );
938 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
939 pWorkWrtShell
->SetLabelDoc( rSourceWrtShell
.IsLabelDoc() );
940 pWorkDoc
->getIDocumentState().ResetModified();
943 pWorkDoc
->getIDocumentLinksAdministration().EmbedAllLinks();
946 if( pView
) *pView
= pWorkView
;
947 if( pWrtShell
) *pWrtShell
= pWorkWrtShell
;
948 if( pDoc
) *pDoc
= pWorkDoc
;
950 return xWorkObjectShell
.get();
953 static SwMailMessage
* lcl_CreateMailFromDoc(
954 const SwMergeDescriptor
&rMergeDescriptor
,
955 const OUString
&sFileURL
, const OUString
&sMailRecipient
,
956 const OUString
&sMailBodyMimeType
, rtl_TextEncoding sMailEncoding
,
957 const OUString
&sAttachmentMimeType
)
959 SwMailMessage
* pMessage
= new SwMailMessage
;
960 if( rMergeDescriptor
.pMailMergeConfigItem
->IsMailReplyTo() )
961 pMessage
->setReplyToAddress(rMergeDescriptor
.pMailMergeConfigItem
->GetMailReplyTo());
962 pMessage
->addRecipient( sMailRecipient
);
963 pMessage
->SetSenderAddress( rMergeDescriptor
.pMailMergeConfigItem
->GetMailAddress() );
965 OUStringBuffer sBody
;
966 if( rMergeDescriptor
.bSendAsAttachment
)
968 sBody
= rMergeDescriptor
.sMailBody
;
969 mail::MailAttachment aAttach
;
970 aAttach
.Data
= new SwMailTransferable( sFileURL
,
971 rMergeDescriptor
.sAttachmentName
, sAttachmentMimeType
);
972 aAttach
.ReadableName
= rMergeDescriptor
.sAttachmentName
;
973 pMessage
->addAttachment( aAttach
);
977 //read in the temporary file and use it as mail body
978 SfxMedium
aMedium( sFileURL
, StreamMode::READ
);
979 SvStream
* pInStream
= aMedium
.GetInStream();
980 assert( pInStream
&& "no output file created?" );
984 pInStream
->SetStreamCharSet( sMailEncoding
);
986 while ( pInStream
->ReadLine( sLine
) )
988 sBody
.append(OStringToOUString( sLine
, sMailEncoding
));
992 pMessage
->setSubject( rMergeDescriptor
.sSubject
);
993 uno::Reference
< datatransfer::XTransferable
> xBody
=
994 new SwMailTransferable( sBody
.makeStringAndClear(), sMailBodyMimeType
);
995 pMessage
->setBody( xBody
);
997 for( const OUString
& sCcRecipient
: rMergeDescriptor
.aCopiesTo
)
998 pMessage
->addCcRecipient( sCcRecipient
);
999 for( const OUString
& sBccRecipient
: rMergeDescriptor
.aBlindCopiesTo
)
1000 pMessage
->addBccRecipient( sBccRecipient
);
1005 class SwDBManager::MailDispatcherListener_Impl
: public IMailDispatcherListener
1007 SwDBManager
&m_rDBManager
;
1010 explicit MailDispatcherListener_Impl( SwDBManager
&rDBManager
)
1011 : m_rDBManager( rDBManager
) {}
1013 virtual void idle() override
{}
1015 virtual void mailDelivered( uno::Reference
< mail::XMailMessage
> xMessage
) override
1017 osl::MutexGuard
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1018 if ( m_rDBManager
.m_pImpl
->m_xLastMessage
== xMessage
)
1019 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1022 virtual void mailDeliveryError( ::rtl::Reference
<MailDispatcher
> xMailDispatcher
,
1023 uno::Reference
< mail::XMailMessage
>, const OUString
& ) override
1025 osl::MutexGuard
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1026 m_rDBManager
.m_aMergeStatus
= MergeStatus::Error
;
1027 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1028 xMailDispatcher
->stop();
1033 * Please have a look at the README in the same directory, before you make
1034 * larger changes in this function!
1036 bool SwDBManager::MergeMailFiles(SwWrtShell
* pSourceShell
,
1037 const SwMergeDescriptor
& rMergeDescriptor
)
1039 // deconstruct mail merge type for better readability.
1040 // uppercase naming is intentional!
1041 const bool bMT_EMAIL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_EMAIL
;
1042 const bool bMT_SHELL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_SHELL
;
1043 const bool bMT_PRINTER
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_PRINTER
;
1044 const bool bMT_FILE
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_FILE
;
1046 //check if the doc is synchronized and contains at least one linked section
1047 const bool bSynchronizedDoc
= pSourceShell
->IsLabelDoc() && pSourceShell
->GetSectionFormatCount() > 1;
1048 const bool bNeedsTempFiles
= ( bMT_EMAIL
|| bMT_FILE
);
1049 const bool bIsMergeSilent
= IsMergeSilent();
1051 bool bCheckSingleFile_
= rMergeDescriptor
.bCreateSingleFile
;
1052 OUString sPrefix_
= rMergeDescriptor
.sPrefix
;
1055 assert( !rMergeDescriptor
.bPrefixIsFilename
);
1056 assert(!bCheckSingleFile_
);
1057 bCheckSingleFile_
= false;
1059 else if( bMT_SHELL
|| bMT_PRINTER
)
1061 assert(bCheckSingleFile_
);
1062 bCheckSingleFile_
= true;
1063 assert(sPrefix_
.isEmpty());
1066 const bool bCreateSingleFile
= bCheckSingleFile_
;
1067 const OUString sDescriptorPrefix
= sPrefix_
;
1069 // Setup for dumping debugging documents
1070 static const sal_Int32 nMaxDumpDocs
= []() {
1071 if (const char* sEnv
= getenv("SW_DEBUG_MAILMERGE_DOCS"))
1072 return OUString(sEnv
, strlen(sEnv
), osl_getThreadTextEncoding()).toInt32();
1074 return sal_Int32(0);
1077 ::rtl::Reference
< MailDispatcher
> xMailDispatcher
;
1078 ::rtl::Reference
< IMailDispatcherListener
> xMailListener
;
1079 OUString sMailBodyMimeType
;
1080 rtl_TextEncoding sMailEncoding
= ::osl_getThreadTextEncoding();
1082 uno::Reference
< beans::XPropertySet
> xColumnProp
;
1083 uno::Reference
< beans::XPropertySet
> xPasswordColumnProp
;
1085 // Check for (mandatory) email or (optional) filename column
1086 SwDBFormatData aColumnDBFormat
;
1087 bool bColumnName
= !rMergeDescriptor
.sDBcolumn
.isEmpty();
1088 bool bPasswordColumnName
= !rMergeDescriptor
.sDBPasswordColumn
.isEmpty();
1097 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
1098 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1099 if( !xCols
->hasByName( rMergeDescriptor
.sDBcolumn
) )
1101 uno::Any aCol
= xCols
->getByName( rMergeDescriptor
.sDBcolumn
);
1102 aCol
>>= xColumnProp
;
1104 if(bPasswordColumnName
)
1106 aCol
= xCols
->getByName( rMergeDescriptor
.sDBPasswordColumn
);
1107 aCol
>>= xPasswordColumnProp
;
1110 aColumnDBFormat
.xFormatter
= m_pImpl
->pMergeData
->xFormatter
;
1111 aColumnDBFormat
.aNullDate
= m_pImpl
->pMergeData
->aNullDate
;
1115 // Reset internal mail accounting data
1116 m_pImpl
->m_xLastMessage
.clear();
1118 xMailDispatcher
.set( new MailDispatcher(rMergeDescriptor
.xSmtpServer
) );
1119 xMailListener
= new MailDispatcherListener_Impl( *this );
1120 xMailDispatcher
->addListener( xMailListener
);
1121 if(!rMergeDescriptor
.bSendAsAttachment
&& rMergeDescriptor
.bSendAsHTML
)
1123 sMailBodyMimeType
= "text/html; charset=" + OUString::createFromAscii(
1124 rtl_getBestMimeCharsetFromTextEncoding( sMailEncoding
));
1125 SvxHtmlOptions
& rHtmlOptions
= SvxHtmlOptions::Get();
1126 sMailEncoding
= rHtmlOptions
.GetTextEncoding();
1129 sMailBodyMimeType
= "text/plain; charset=UTF-8; format=flowed";
1133 SwDocShell
*pSourceDocSh
= pSourceShell
->GetView().GetDocShell();
1135 // setup the output format
1136 std::shared_ptr
<const SfxFilter
> pStoreToFilter
= SwIoSystem::GetFileFilter(
1137 pSourceDocSh
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1138 SfxFilterContainer
* pFilterContainer
= SwDocShell::Factory().GetFilterContainer();
1139 const OUString
* pStoreToFilterOptions
= nullptr;
1141 // if a save_to filter is set then use it - otherwise use the default
1142 if( bMT_EMAIL
&& !rMergeDescriptor
.bSendAsAttachment
)
1144 OUString sExtension
= rMergeDescriptor
.bSendAsHTML
? OUString("html") : OUString("txt");
1145 pStoreToFilter
= pFilterContainer
->GetFilter4Extension(sExtension
, SfxFilterFlags::EXPORT
);
1147 else if( !rMergeDescriptor
.sSaveToFilter
.isEmpty())
1149 std::shared_ptr
<const SfxFilter
> pFilter
=
1150 pFilterContainer
->GetFilter4FilterName( rMergeDescriptor
.sSaveToFilter
);
1153 pStoreToFilter
= pFilter
;
1154 if(!rMergeDescriptor
.sSaveToFilterOptions
.isEmpty())
1155 pStoreToFilterOptions
= &rMergeDescriptor
.sSaveToFilterOptions
;
1158 const bool bIsPDFexport
= pStoreToFilter
&& pStoreToFilter
->GetFilterName() == "writer_pdf_Export";
1160 m_aMergeStatus
= MergeStatus::Ok
;
1162 // in case of creating a single resulting file this has to be created here
1163 SwView
* pTargetView
= rMergeDescriptor
.pMailMergeConfigItem
?
1164 rMergeDescriptor
.pMailMergeConfigItem
->GetTargetView() : nullptr;
1165 SwWrtShell
* pTargetShell
= nullptr;
1166 SwDoc
* pTargetDoc
= nullptr;
1167 SfxObjectShellRef xTargetDocShell
;
1169 std::unique_ptr
< utl::TempFile
> aTempFile
;
1170 sal_uInt16 nStartingPageNo
= 0;
1172 std::shared_ptr
<weld::GenericDialogController
> xProgressDlg
;
1176 vcl::Window
*pSourceWindow
= nullptr;
1177 if( !bIsMergeSilent
)
1179 // construct the process dialog
1180 pSourceWindow
= &pSourceShell
->GetView().GetEditWin();
1182 xProgressDlg
= std::make_shared
<CreateMonitor
>(pSourceWindow
->GetFrameWeld());
1185 xProgressDlg
= std::make_shared
<PrintMonitor
>(pSourceWindow
->GetFrameWeld());
1186 static_cast<PrintMonitor
*>(xProgressDlg
.get())->set_title(
1187 pSourceDocSh
->GetTitle(22));
1189 weld::DialogController::runAsync(xProgressDlg
, [this, &xProgressDlg
](sal_Int32 nResult
){
1190 if (nResult
== RET_CANCEL
)
1192 xProgressDlg
.reset();
1195 Application::Reschedule( true );
1198 if( bCreateSingleFile
&& !pTargetView
)
1200 // create a target docshell to put the merged document into
1201 xTargetDocShell
= lcl_CreateWorkingDocument( WorkingDocType::TARGET
,
1202 *pSourceShell
, bMT_SHELL
? pSourceWindow
: nullptr,
1203 nullptr, &pTargetView
, &pTargetShell
, &pTargetDoc
);
1206 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1208 else if( pTargetView
)
1210 pTargetShell
= pTargetView
->GetWrtShellPtr();
1211 pTargetDoc
= pTargetShell
->GetDoc();
1212 xTargetDocShell
= pTargetView
->GetDocShell();
1215 if( bCreateSingleFile
)
1217 // determine the page style and number used at the start of the source document
1218 pSourceShell
->SttEndDoc(true);
1219 nStartingPageNo
= pSourceShell
->GetVirtPageNum();
1222 // Progress, to prohibit KeyInputs
1223 SfxProgress
aProgress(pSourceDocSh
, OUString(), 1);
1225 // lock all dispatchers
1226 SfxViewFrame
* pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1229 pViewFrame
->GetDispatcher()->Lock(true);
1230 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1233 sal_Int32 nDocNo
= 1;
1235 // For single file mode, the number of pages in the target document so far, which is used
1236 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1237 // from the target doc would require repeated layouts of the doc, which is expensive, but
1238 // it can be manually computed from the source documents (for which we do layouts, so the page
1239 // count is known, and there is a blank page between each of them in the target document).
1240 int targetDocPageCount
= 0;
1242 if( !bIsMergeSilent
&& !bMT_PRINTER
)
1244 sal_Int32 nRecordCount
= 1;
1245 lcl_getCountFromResultSet( nRecordCount
, m_pImpl
->pMergeData
.get() );
1247 // Synchronized docs don't auto-advance the record set, but there is a
1248 // "security" check, which will always advance the record set, if there
1249 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1250 sal_Int32 nRecordPerDoc
= pSourceShell
->GetDoc()
1251 ->getIDocumentFieldsAccess().GetRecordsPerDocument();
1252 if ( bSynchronizedDoc
&& (nRecordPerDoc
> 1) )
1254 assert( nRecordPerDoc
> 0 );
1256 sal_Int32 nMaxDocs
= nRecordCount
/ nRecordPerDoc
;
1257 if ( 0 != nRecordCount
% nRecordPerDoc
)
1259 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetTotalCount(nMaxDocs
);
1262 sal_Int32 nStartRow
, nEndRow
;
1263 bool bFreezedLayouts
= false;
1264 // to collect temporary email files
1265 std::vector
< OUString
> aFilesToRemove
;
1267 // The SfxObjectShell will be closed explicitly later but
1268 // it is more safe to use SfxObjectShellLock here
1269 SfxObjectShellLock xWorkDocSh
;
1270 SwView
* pWorkView
= nullptr;
1271 SwDoc
* pWorkDoc
= nullptr;
1272 SwDBManager
* pWorkDocOrigDBManager
= nullptr;
1273 SwWrtShell
* pWorkShell
= nullptr;
1274 bool bWorkDocInitialized
= false;
1278 nStartRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1280 OUString sColumnData
;
1282 // Read the indicated data column, which should contain a valid mail
1283 // address or an optional file name
1284 if( bMT_EMAIL
|| bColumnName
)
1286 sColumnData
= GetDBField( xColumnProp
, aColumnDBFormat
);
1289 // create a new temporary file name - only done once in case of bCreateSingleFile
1290 if( bNeedsTempFiles
&& ( !bWorkDocInitialized
|| !bCreateSingleFile
))
1292 OUString sPrefix
= sDescriptorPrefix
;
1295 //#i97667# if the name is from a database field then it will be used _as is_
1296 if( bColumnName
&& !bMT_EMAIL
)
1298 if (!sColumnData
.isEmpty())
1299 sLeading
= sColumnData
;
1305 INetURLObject
aEntry( sPrefix
);
1306 sLeading
= aEntry
.GetBase();
1307 aEntry
.removeSegment();
1308 sPrefix
= aEntry
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1311 OUString
sExt(comphelper::string::stripStart(pStoreToFilter
->GetDefaultExtension(), '*'));
1312 aTempFile
.reset( new utl::TempFile(sLeading
, sColumnData
.isEmpty(), &sExt
, &sPrefix
, true) );
1313 if( !aTempFile
->IsValid() )
1315 ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED
);
1316 m_aMergeStatus
= MergeStatus::Error
;
1320 uno::Sequence
< beans::PropertyValue
> aSaveToFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
);
1322 if( bMT_EMAIL
|| bPasswordColumnName
)
1324 OUString sPasswordColumnData
= GetDBField( xPasswordColumnProp
, aColumnDBFormat
);
1325 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
, aSaveToFilterDataOptions
, sPasswordColumnData
);
1330 std::unique_ptr
< INetURLObject
> aTempFileURL
;
1331 if( bNeedsTempFiles
)
1332 aTempFileURL
.reset( new INetURLObject(aTempFile
->GetURL()));
1333 if( !bIsMergeSilent
) {
1335 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetCurrentPosition(nDocNo
);
1337 PrintMonitor
*pPrintMonDlg
= static_cast<PrintMonitor
*>(xProgressDlg
.get());
1338 pPrintMonDlg
->m_xPrinter
->set_label(bNeedsTempFiles
1339 ? aTempFileURL
->GetBase() : pSourceDocSh
->GetTitle( 2));
1340 OUString sStat
= SwResId(STR_STATSTR_LETTER
) + " " + OUString::number( nDocNo
);
1341 pPrintMonDlg
->m_xPrintInfo
->set_label(sStat
);
1343 //TODO xProgressDlg->queue_draw();
1346 Scheduler::ProcessEventsToIdle();
1348 // Create a copy of the source document and work with that one instead of the source.
1349 // If we're not in the single file mode (which requires modifying the document for the merging),
1350 // it is enough to do this just once. Currently PDF also has to be treated special.
1351 if( !bWorkDocInitialized
|| bCreateSingleFile
|| bIsPDFexport
)
1353 assert( !xWorkDocSh
.Is());
1354 pWorkDocOrigDBManager
= this;
1355 xWorkDocSh
= lcl_CreateWorkingDocument( WorkingDocType::COPY
,
1356 *pSourceShell
, nullptr, &pWorkDocOrigDBManager
,
1357 &pWorkView
, &pWorkShell
, &pWorkDoc
);
1358 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1359 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1361 // #i69458# lock fields to prevent access to the result set while calculating layout
1362 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1363 // ExpFields update during printing, generation of preview, etc.
1364 pWorkShell
->LockExpFields();
1365 pWorkShell
->CalcLayout();
1366 // tdf#121168: Now force correct page descriptions applied to page frames. Without
1367 // this, e.g., page frames starting with sections could have page descriptions set
1368 // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1369 pWorkShell
->GetViewOptions()->SetIdle(true);
1370 for (auto aLayout
: pWorkShell
->GetDoc()->GetAllLayouts())
1372 aLayout
->FreezeLayout(false);
1373 aLayout
->AllCheckPageDescs();
1377 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge
, STR_SW_EVENT_FIELD_MERGE
, xWorkDocSh
);
1379 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1380 // database cursor movement on any other fields update, for example during
1381 // print preview and other operations
1382 if ( pWorkShell
->IsExpFieldsLocked() )
1383 pWorkShell
->UnlockExpFields();
1384 pWorkShell
->SwViewShell::UpdateFields();
1385 pWorkShell
->LockExpFields();
1387 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished
, STR_SW_EVENT_FIELD_MERGE_FINISHED
, xWorkDocSh
);
1389 // also emit MailMergeEvent on XInterface if possible
1390 const SwXMailMerge
*pEvtSrc
= GetMailMergeEvtSrc();
1393 rtl::Reference
< SwXMailMerge
> xRef(
1394 const_cast<SwXMailMerge
*>(pEvtSrc
) );
1395 text::MailMergeEvent
aEvt( static_cast<text::XMailMergeBroadcaster
*>(xRef
.get()), xWorkDocSh
->GetModel() );
1396 SolarMutexReleaser rel
;
1397 xRef
->LaunchMailMergeEvent( aEvt
);
1400 // working copy is merged - prepare final steps depending on merge options
1402 if( bCreateSingleFile
)
1404 assert( pTargetShell
&& "no target shell available!" );
1406 // prepare working copy and target to append
1408 pWorkDoc
->RemoveInvisibleContent();
1409 // remove of invisible content has influence on page count and so on fields for page count,
1410 // therefore layout has to be updated before fields are converted to text
1411 pWorkShell
->CalcLayout();
1412 pWorkShell
->ConvertFieldsToText();
1413 pWorkShell
->SetNumberingRestart();
1414 if( bSynchronizedDoc
)
1416 lcl_RemoveSectionLinks( *pWorkShell
);
1419 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1420 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1422 // append the working document to the target document
1423 if( targetDocPageCount
% 2 == 1 )
1424 ++targetDocPageCount
; // Docs always start on odd pages (so offset must be even).
1425 SwNodeIndex appendedDocStart
= pTargetDoc
->AppendDoc( *pWorkDoc
,
1426 nStartingPageNo
, !bWorkDocInitialized
, targetDocPageCount
, nDocNo
);
1427 targetDocPageCount
+= pWorkShell
->GetPageCnt();
1429 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1430 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1434 SwDocMergeInfo aMergeInfo
;
1435 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1436 aMergeInfo
.startPageInTarget
= pTargetDoc
->getIDocumentMarkAccess()->makeMark(
1437 appendedDocStart
, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
1438 ::sw::mark::InsertMode::New
);
1439 aMergeInfo
.nDBRow
= nStartRow
;
1440 rMergeDescriptor
.pMailMergeConfigItem
->AddMergedDocument( aMergeInfo
);
1445 assert( bNeedsTempFiles
);
1446 assert( pWorkShell
->IsExpFieldsLocked() );
1448 // fields are locked, so it's fine to
1449 // restore the old / empty DB manager for save
1450 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1452 // save merged document
1454 if( !lcl_SaveDoc( aTempFileURL
.get(), pStoreToFilter
, pStoreToFilterOptions
,
1455 &aSaveToFilterDataOptions
, bIsPDFexport
,
1456 xWorkDocSh
, *pWorkShell
, &sFileURL
) )
1458 m_aMergeStatus
= MergeStatus::Error
;
1461 // back to the MM DB manager
1462 pWorkDoc
->SetDBManager( this );
1464 if( bMT_EMAIL
&& !IsMergeError() )
1466 // schedule file for later removal
1467 aFilesToRemove
.push_back( sFileURL
);
1469 if( !SwMailMergeHelper::CheckMailAddress( sColumnData
) )
1471 OSL_FAIL("invalid e-Mail address in database column");
1475 uno::Reference
< mail::XMailMessage
> xMessage
= lcl_CreateMailFromDoc(
1476 rMergeDescriptor
, sFileURL
, sColumnData
, sMailBodyMimeType
,
1477 sMailEncoding
, pStoreToFilter
->GetMimeType() );
1480 osl::MutexGuard
aGuard( m_pImpl
->m_aAllEmailSendMutex
);
1481 m_pImpl
->m_xLastMessage
.set( xMessage
);
1482 xMailDispatcher
->enqueueMailMessage( xMessage
);
1483 if( !xMailDispatcher
->isStarted() )
1484 xMailDispatcher
->start();
1489 if( bCreateSingleFile
|| bIsPDFexport
)
1491 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1492 xWorkDocSh
->DoClose();
1493 xWorkDocSh
= nullptr;
1497 bWorkDocInitialized
= true;
1499 nEndRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1501 // Freeze the layouts of the target document after the first inserted
1502 // sub-document, to get the correct PageDesc.
1503 if(!bFreezedLayouts
&& bCreateSingleFile
)
1505 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1506 aLayout
->FreezeLayout(true);
1507 bFreezedLayouts
= true;
1509 } while( IsMergeOk() &&
1510 ((bSynchronizedDoc
&& (nStartRow
!= nEndRow
)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1512 if ( xWorkDocSh
.Is() && pWorkView
->GetWrtShell().IsExpFieldsLocked() )
1514 // Unlock document fields after merge complete
1515 pWorkView
->GetWrtShell().UnlockExpFields();
1518 if( !bCreateSingleFile
)
1521 Printer::FinishPrintJob( pWorkView
->GetPrinterController());
1524 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1525 xWorkDocSh
->DoClose();
1528 else if( IsMergeOk() ) // && bCreateSingleFile
1530 Application::Reschedule( true );
1532 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1533 // unique fly names, do it here once.
1534 pTargetDoc
->SetInMailMerge(false);
1535 pTargetDoc
->SetAllUniqueFlyNames();
1537 // Unfreeze target document layouts and correct all PageDescs.
1538 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1539 pTargetShell
->CalcLayout();
1540 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1541 pTargetShell
->GetViewOptions()->SetIdle( true );
1542 pTargetDoc
->GetIDocumentUndoRedo().DoUndo( true );
1543 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1545 aLayout
->FreezeLayout(false);
1546 aLayout
->AllCheckPageDescs();
1549 Application::Reschedule( true );
1551 if( IsMergeOk() && bMT_FILE
)
1553 // save merged document
1554 assert( aTempFile
);
1555 INetURLObject aTempFileURL
;
1556 if (sDescriptorPrefix
.isEmpty() || !rMergeDescriptor
.bPrefixIsFilename
)
1557 aTempFileURL
.SetURL( aTempFile
->GetURL() );
1560 aTempFileURL
.SetURL(sDescriptorPrefix
);
1561 // remove the unneeded temporary file
1562 aTempFile
->EnableKillingFile();
1564 if( !lcl_SaveDoc( &aTempFileURL
, pStoreToFilter
,
1565 pStoreToFilterOptions
, &rMergeDescriptor
.aSaveToFilterData
,
1566 bIsPDFexport
, xTargetDocShell
.get(), *pTargetShell
) )
1568 m_aMergeStatus
= MergeStatus::Error
;
1571 else if( IsMergeOk() && bMT_PRINTER
)
1573 // print the target document
1574 uno::Sequence
< beans::PropertyValue
> aOptions( rMergeDescriptor
.aPrintOptions
);
1575 lcl_PreparePrinterOptions( rMergeDescriptor
.aPrintOptions
, aOptions
);
1576 pTargetView
->ExecPrint( aOptions
, bIsMergeSilent
, false/*bPrintAsync*/ );
1580 // we also show canceled documents, as long as there was no error
1581 if( !IsMergeError() && bMT_SHELL
)
1582 // leave docshell available for caller (e.g. MM wizard)
1583 rMergeDescriptor
.pMailMergeConfigItem
->SetTargetView( pTargetView
);
1584 else if( xTargetDocShell
.is() )
1585 xTargetDocShell
->DoClose();
1587 Application::Reschedule( true );
1591 xProgressDlg
->response(RET_OK
);
1594 // unlock all dispatchers
1595 pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1598 pViewFrame
->GetDispatcher()->Lock(false);
1599 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1602 SW_MOD()->SetView(&pSourceShell
->GetView());
1604 if( xMailDispatcher
.is() )
1608 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1609 // if the main loop has been made thread-safe.
1610 AutoTimer aEmailDispatcherPollTimer
;
1611 aEmailDispatcherPollTimer
.SetDebugName(
1612 "sw::SwDBManager aEmailDispatcherPollTimer" );
1613 aEmailDispatcherPollTimer
.SetTimeout( 500 );
1614 aEmailDispatcherPollTimer
.Start();
1615 while( IsMergeOk() && m_pImpl
->m_xLastMessage
.is() )
1616 Application::Yield();
1617 aEmailDispatcherPollTimer
.Stop();
1619 xMailDispatcher
->stop();
1620 xMailDispatcher
->shutdown();
1623 // remove the temporary files
1624 // has to be done after xMailDispatcher is finished, as mails may be
1625 // delivered as message attachments!
1626 for( const OUString
&sFileURL
: aFilesToRemove
)
1627 SWUnoHelper::UCB_DeleteFile( sFileURL
);
1629 catch (const uno::Exception
&)
1633 xProgressDlg
->response(RET_CANCEL
);
1637 return !IsMergeError();
1640 void SwDBManager::MergeCancel()
1642 if (m_aMergeStatus
< MergeStatus::Cancel
)
1643 m_aMergeStatus
= MergeStatus::Cancel
;
1646 // determine the column's Numberformat and transfer to the forwarded Formatter,
1648 sal_uLong
SwDBManager::GetColumnFormat( const OUString
& rDBName
,
1649 const OUString
& rTableName
,
1650 const OUString
& rColNm
,
1651 SvNumberFormatter
* pNFormatr
,
1652 LanguageType nLanguage
)
1657 uno::Reference
< sdbc::XDataSource
> xSource
;
1658 uno::Reference
< sdbc::XConnection
> xConnection
;
1659 bool bUseMergeData
= false;
1660 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1661 bool bDisposeConnection
= false;
1662 if(m_pImpl
->pMergeData
&&
1663 ((m_pImpl
->pMergeData
->sDataSource
== rDBName
&& m_pImpl
->pMergeData
->sCommand
== rTableName
) ||
1664 (rDBName
.isEmpty() && rTableName
.isEmpty())))
1666 xConnection
= m_pImpl
->pMergeData
->xConnection
;
1667 xSource
= SwDBManager::getDataSourceAsParent(xConnection
,rDBName
);
1668 bUseMergeData
= true;
1669 xColsSupp
.set(m_pImpl
->pMergeData
->xResultSet
, css::uno::UNO_QUERY
);
1671 if(!xConnection
.is())
1674 aData
.sDataSource
= rDBName
;
1675 aData
.sCommand
= rTableName
;
1676 aData
.nCommandType
= -1;
1677 SwDSParam
* pParam
= FindDSData(aData
, false);
1678 if(pParam
&& pParam
->xConnection
.is())
1680 xConnection
= pParam
->xConnection
;
1681 xColsSupp
.set(pParam
->xResultSet
, css::uno::UNO_QUERY
);
1685 xConnection
= RegisterConnection( rDBName
);
1686 bDisposeConnection
= true;
1689 m_pImpl
->pMergeData
->xConnection
= xConnection
;
1691 bool bDispose
= !xColsSupp
.is();
1694 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1698 uno::Reference
<container::XNameAccess
> xCols
;
1701 xCols
= xColsSupp
->getColumns();
1703 catch (const uno::Exception
&)
1705 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1707 if(!xCols
.is() || !xCols
->hasByName(rColNm
))
1709 uno::Any aCol
= xCols
->getByName(rColNm
);
1710 uno::Reference
< beans::XPropertySet
> xColumn
;
1712 nRet
= GetColumnFormat(xSource
, xConnection
, xColumn
, pNFormatr
, nLanguage
);
1715 ::comphelper::disposeComponent( xColsSupp
);
1717 if(bDisposeConnection
)
1719 ::comphelper::disposeComponent( xConnection
);
1723 nRet
= pNFormatr
->GetFormatIndex( NF_NUMBER_STANDARD
, LANGUAGE_SYSTEM
);
1728 sal_uLong
SwDBManager::GetColumnFormat( uno::Reference
< sdbc::XDataSource
> const & xSource_in
,
1729 uno::Reference
< sdbc::XConnection
> const & xConnection
,
1730 uno::Reference
< beans::XPropertySet
> const & xColumn
,
1731 SvNumberFormatter
* pNFormatr
,
1732 LanguageType nLanguage
)
1734 auto xSource
= xSource_in
;
1736 // set the NumberFormat in the doc if applicable
1741 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
1743 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
1745 if(xSource
.is() && xConnection
.is() && xColumn
.is() && pNFormatr
)
1747 SvNumberFormatsSupplierObj
* pNumFormat
= new SvNumberFormatsSupplierObj( pNFormatr
);
1748 uno::Reference
< util::XNumberFormatsSupplier
> xDocNumFormatsSupplier
= pNumFormat
;
1749 uno::Reference
< util::XNumberFormats
> xDocNumberFormats
= xDocNumFormatsSupplier
->getNumberFormats();
1750 uno::Reference
< util::XNumberFormatTypes
> xDocNumberFormatTypes(xDocNumberFormats
, uno::UNO_QUERY
);
1752 css::lang::Locale
aLocale( LanguageTag( nLanguage
).getLocale());
1754 //get the number formatter of the data source
1755 uno::Reference
<beans::XPropertySet
> xSourceProps(xSource
, uno::UNO_QUERY
);
1756 uno::Reference
< util::XNumberFormats
> xNumberFormats
;
1757 if(xSourceProps
.is())
1759 uno::Any aFormats
= xSourceProps
->getPropertyValue("NumberFormatsSupplier");
1760 if(aFormats
.hasValue())
1762 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
1763 aFormats
>>= xSuppl
;
1766 xNumberFormats
= xSuppl
->getNumberFormats();
1770 bool bUseDefault
= true;
1773 uno::Any aFormatKey
= xColumn
->getPropertyValue("FormatKey");
1774 if(aFormatKey
.hasValue())
1776 sal_Int32 nFormat
= 0;
1777 aFormatKey
>>= nFormat
;
1778 if(xNumberFormats
.is())
1782 uno::Reference
<beans::XPropertySet
> xNumProps
= xNumberFormats
->getByKey( nFormat
);
1783 uno::Any aFormatString
= xNumProps
->getPropertyValue("FormatString");
1784 uno::Any aLocaleVal
= xNumProps
->getPropertyValue("Locale");
1786 aFormatString
>>= sFormat
;
1788 aLocaleVal
>>= aLoc
;
1789 nFormat
= xDocNumberFormats
->queryKey( sFormat
, aLoc
, false );
1790 if(NUMBERFORMAT_ENTRY_NOT_FOUND
== sal::static_int_cast
< sal_uInt32
, sal_Int32
>(nFormat
))
1791 nFormat
= xDocNumberFormats
->addNew( sFormat
, aLoc
);
1793 bUseDefault
= false;
1795 catch (const uno::Exception
&)
1797 TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1802 catch(const uno::Exception
&)
1804 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1807 nRet
= dbtools::getDefaultNumberFormat(xColumn
, xDocNumberFormatTypes
, aLocale
);
1812 sal_Int32
SwDBManager::GetColumnType( const OUString
& rDBName
,
1813 const OUString
& rTableName
,
1814 const OUString
& rColNm
)
1816 sal_Int32 nRet
= sdbc::DataType::SQLNULL
;
1818 aData
.sDataSource
= rDBName
;
1819 aData
.sCommand
= rTableName
;
1820 aData
.nCommandType
= -1;
1821 SwDSParam
* pParam
= FindDSData(aData
, false);
1822 uno::Reference
< sdbc::XConnection
> xConnection
;
1823 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1824 bool bDispose
= false;
1825 if(pParam
&& pParam
->xConnection
.is())
1827 xConnection
= pParam
->xConnection
;
1828 xColsSupp
.set( pParam
->xResultSet
, uno::UNO_QUERY
);
1832 xConnection
= RegisterConnection( rDBName
);
1834 if( !xColsSupp
.is() )
1836 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1841 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1842 if(xCols
->hasByName(rColNm
))
1844 uno::Any aCol
= xCols
->getByName(rColNm
);
1845 uno::Reference
<beans::XPropertySet
> xCol
;
1847 uno::Any aType
= xCol
->getPropertyValue("Type");
1851 ::comphelper::disposeComponent( xColsSupp
);
1856 uno::Reference
< sdbc::XConnection
> SwDBManager::GetConnection(const OUString
& rDataSource
,
1857 uno::Reference
<sdbc::XDataSource
>& rxSource
, const SwView
*pView
)
1859 uno::Reference
< sdbc::XConnection
> xConnection
;
1860 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1863 uno::Reference
<sdb::XCompletedConnection
> xComplConnection(dbtools::getDataSource(rDataSource
, xContext
), uno::UNO_QUERY
);
1864 if ( xComplConnection
.is() )
1866 rxSource
.set(xComplConnection
, uno::UNO_QUERY
);
1867 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
1868 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(xContext
, pWindow
? pWindow
->GetXWindow() : nullptr), uno::UNO_QUERY_THROW
);
1869 xConnection
= xComplConnection
->connectWithCompletion( xHandler
);
1872 catch(const uno::Exception
&)
1879 uno::Reference
< sdbcx::XColumnsSupplier
> SwDBManager::GetColumnSupplier(uno::Reference
<sdbc::XConnection
> const & xConnection
,
1880 const OUString
& rTableOrQuery
,
1881 SwDBSelect eTableOrQuery
)
1883 uno::Reference
< sdbcx::XColumnsSupplier
> xRet
;
1886 if(eTableOrQuery
== SwDBSelect::UNKNOWN
)
1888 //search for a table with the given command name
1889 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
1892 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
1893 eTableOrQuery
= xTables
->hasByName(rTableOrQuery
) ?
1894 SwDBSelect::TABLE
: SwDBSelect::QUERY
;
1897 sal_Int32 nCommandType
= SwDBSelect::TABLE
== eTableOrQuery
?
1898 sdb::CommandType::TABLE
: sdb::CommandType::QUERY
;
1899 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
1900 uno::Reference
<sdbc::XRowSet
> xRowSet(xMgr
->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY
);
1902 OUString sDataSource
;
1903 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
, sDataSource
);
1904 uno::Reference
<beans::XPropertySet
> xSourceProperties(xSource
, uno::UNO_QUERY
);
1905 if(xSourceProperties
.is())
1907 xSourceProperties
->getPropertyValue("Name") >>= sDataSource
;
1910 uno::Reference
<beans::XPropertySet
> xRowProperties(xRowSet
, uno::UNO_QUERY
);
1911 xRowProperties
->setPropertyValue("DataSourceName", uno::makeAny(sDataSource
));
1912 xRowProperties
->setPropertyValue("Command", uno::makeAny(rTableOrQuery
));
1913 xRowProperties
->setPropertyValue("CommandType", uno::makeAny(nCommandType
));
1914 xRowProperties
->setPropertyValue("FetchSize", uno::makeAny(sal_Int32(10)));
1915 xRowProperties
->setPropertyValue("ActiveConnection", uno::makeAny(xConnection
));
1917 xRet
.set( xRowSet
, uno::UNO_QUERY
);
1919 catch (const uno::Exception
&)
1921 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1927 OUString
SwDBManager::GetDBField(uno::Reference
<beans::XPropertySet
> const & xColumnProps
,
1928 const SwDBFormatData
& rDBFormatData
,
1931 uno::Reference
< sdb::XColumn
> xColumn(xColumnProps
, uno::UNO_QUERY
);
1933 assert( xColumn
.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1937 uno::Any aType
= xColumnProps
->getPropertyValue("Type");
1938 sal_Int32 eDataType
= sdbc::DataType::SQLNULL
;
1939 aType
>>= eDataType
;
1942 case sdbc::DataType::CHAR
:
1943 case sdbc::DataType::VARCHAR
:
1944 case sdbc::DataType::LONGVARCHAR
:
1947 sRet
= xColumn
->getString();
1948 sRet
= sRet
.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1950 catch(const sdbc::SQLException
&)
1954 case sdbc::DataType::BIT
:
1955 case sdbc::DataType::BOOLEAN
:
1956 case sdbc::DataType::TINYINT
:
1957 case sdbc::DataType::SMALLINT
:
1958 case sdbc::DataType::INTEGER
:
1959 case sdbc::DataType::BIGINT
:
1960 case sdbc::DataType::FLOAT
:
1961 case sdbc::DataType::REAL
:
1962 case sdbc::DataType::DOUBLE
:
1963 case sdbc::DataType::NUMERIC
:
1964 case sdbc::DataType::DECIMAL
:
1965 case sdbc::DataType::DATE
:
1966 case sdbc::DataType::TIME
:
1967 case sdbc::DataType::TIMESTAMP
:
1972 sRet
= dbtools::DBTypeConversion::getFormattedValue(
1974 rDBFormatData
.xFormatter
,
1975 rDBFormatData
.aLocale
,
1976 rDBFormatData
.aNullDate
);
1979 double fVal
= xColumn
->getDouble();
1980 if(!xColumn
->wasNull())
1986 catch (const uno::Exception
&)
1988 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
1998 // checks if a desired data source table or query is open
1999 bool SwDBManager::IsDataSourceOpen(const OUString
& rDataSource
,
2000 const OUString
& rTableOrQuery
, bool bMergeShell
)
2002 if(m_pImpl
->pMergeData
)
2004 return ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
2005 && rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
)
2006 || (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty()))
2007 && m_pImpl
->pMergeData
->xResultSet
.is();
2009 else if(!bMergeShell
)
2012 aData
.sDataSource
= rDataSource
;
2013 aData
.sCommand
= rTableOrQuery
;
2014 aData
.nCommandType
= -1;
2015 SwDSParam
* pFound
= FindDSData(aData
, false);
2016 return (pFound
&& pFound
->xResultSet
.is());
2021 // read column data at a specified position
2022 bool SwDBManager::GetColumnCnt(const OUString
& rSourceName
, const OUString
& rTableName
,
2023 const OUString
& rColumnName
, sal_uInt32 nAbsRecordId
,
2024 LanguageType nLanguage
,
2025 OUString
& rResult
, double* pNumber
)
2028 SwDSParam
* pFound
= nullptr;
2029 //check if it's the merge data source
2030 if(m_pImpl
->pMergeData
&&
2031 rSourceName
== m_pImpl
->pMergeData
->sDataSource
&&
2032 rTableName
== m_pImpl
->pMergeData
->sCommand
)
2034 pFound
= m_pImpl
->pMergeData
.get();
2039 aData
.sDataSource
= rSourceName
;
2040 aData
.sCommand
= rTableName
;
2041 aData
.nCommandType
= -1;
2042 pFound
= FindDSData(aData
, false);
2046 //check validity of supplied record Id
2047 if(pFound
->aSelection
.hasElements())
2049 //the destination has to be an element of the selection
2050 bool bFound
= std::any_of(pFound
->aSelection
.begin(), pFound
->aSelection
.end(),
2051 [nAbsRecordId
](const uno::Any
& rSelection
) {
2052 sal_Int32 nSelection
= 0;
2053 rSelection
>>= nSelection
;
2054 return nSelection
== static_cast<sal_Int32
>(nAbsRecordId
);
2059 if( pFound
->HasValidRecord() )
2061 sal_Int32 nOldRow
= 0;
2064 nOldRow
= pFound
->xResultSet
->getRow();
2066 catch(const uno::Exception
&)
2070 //position to the desired index
2072 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2073 bMove
= lcl_MoveAbsolute(pFound
, nAbsRecordId
);
2075 bRet
= lcl_GetColumnCnt(pFound
, rColumnName
, nLanguage
, rResult
, pNumber
);
2076 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2077 lcl_MoveAbsolute(pFound
, nOldRow
);
2082 // reads the column data at the current position
2083 bool SwDBManager::GetMergeColumnCnt(const OUString
& rColumnName
, LanguageType nLanguage
,
2084 OUString
&rResult
, double *pNumber
)
2086 if( !IsValidMergeRecord() )
2092 bool bRet
= lcl_GetColumnCnt(m_pImpl
->pMergeData
.get(), rColumnName
, nLanguage
, rResult
, pNumber
);
2096 bool SwDBManager::ToNextMergeRecord()
2098 assert( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2099 return lcl_ToNextRecord( m_pImpl
->pMergeData
.get() );
2102 bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter
*pDocFormatter
,
2103 LanguageType nLanguage
, SwCalc
&rCalc
)
2105 if( !IsValidMergeRecord() )
2108 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
2109 if( !xColsSupp
.is() )
2113 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
2114 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
2117 // add the "record number" variable, as SwCalc::VarLook would.
2118 rCalc
.VarChange( GetAppCharClass().lowercase(
2119 SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber
) ), GetSelectedRecordId() );
2121 for( const OUString
& rColName
: aColNames
)
2123 // get the column type
2124 sal_Int32 nColumnType
= sdbc::DataType::SQLNULL
;
2125 uno::Any aCol
= xCols
->getByName( rColName
);
2126 uno::Reference
<beans::XPropertySet
> xColumnProps
;
2127 aCol
>>= xColumnProps
;
2128 uno::Any aType
= xColumnProps
->getPropertyValue( "Type" );
2129 aType
>>= nColumnType
;
2130 double aNumber
= DBL_MAX
;
2132 lcl_GetColumnCnt( m_pImpl
->pMergeData
.get(), xColumnProps
, nLanguage
, aString
, &aNumber
);
2134 sal_uInt32 nFormat
= GetColumnFormat( m_pImpl
->pMergeData
->sDataSource
,
2135 m_pImpl
->pMergeData
->sCommand
,
2136 rColName
, pDocFormatter
, nLanguage
);
2137 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2138 bool colIsNumber
= aNumber
!= DBL_MAX
;
2139 bool bValidValue
= SwDBField::FormatValue( pDocFormatter
, aString
, nFormat
,
2140 aNumber
, nColumnType
);
2146 aValue
.PutDouble( aNumber
);
2147 aValue
.SetDBvalue( true );
2148 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aNumber
<< " / " << aString
);
2149 rCalc
.VarChange( rColName
, aValue
);
2155 aValue
.PutString( aString
);
2156 aValue
.SetDBvalue( true );
2157 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aString
);
2158 rCalc
.VarChange( rColName
, aValue
);
2166 void SwDBManager::ToNextRecord(
2167 const OUString
& rDataSource
, const OUString
& rCommand
)
2169 SwDSParam
* pFound
= nullptr;
2170 if(m_pImpl
->pMergeData
&&
2171 rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2172 rCommand
== m_pImpl
->pMergeData
->sCommand
)
2174 pFound
= m_pImpl
->pMergeData
.get();
2179 aData
.sDataSource
= rDataSource
;
2180 aData
.sCommand
= rCommand
;
2181 aData
.nCommandType
= -1;
2182 pFound
= FindDSData(aData
, false);
2184 lcl_ToNextRecord( pFound
);
2187 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
)
2191 assert( SwDBNextRecord::NEXT
== action
||
2192 (SwDBNextRecord::FIRST
== action
&& pParam
) );
2193 if( nullptr == pParam
)
2196 if( action
== SwDBNextRecord::FIRST
)
2198 pParam
->nSelectionIndex
= 0;
2199 pParam
->bEndOfDB
= false;
2202 if( !pParam
->HasValidRecord() )
2207 if( pParam
->aSelection
.hasElements() )
2209 if( pParam
->nSelectionIndex
>= pParam
->aSelection
.getLength() )
2210 pParam
->bEndOfDB
= true;
2214 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
2215 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
2218 else if( action
== SwDBNextRecord::FIRST
)
2220 pParam
->bEndOfDB
= !pParam
->xResultSet
->first();
2224 sal_Int32 nBefore
= pParam
->xResultSet
->getRow();
2225 pParam
->bEndOfDB
= !pParam
->xResultSet
->next();
2226 if( !pParam
->bEndOfDB
&& nBefore
== pParam
->xResultSet
->getRow() )
2228 // next returned true but it didn't move
2229 ::dbtools::throwFunctionSequenceException( pParam
->xResultSet
);
2233 ++pParam
->nSelectionIndex
;
2234 bRet
= !pParam
->bEndOfDB
;
2236 catch( const uno::Exception
& )
2238 // we allow merging with empty databases, so don't warn on init
2239 TOOLS_WARN_EXCEPTION_IF(action
== SwDBNextRecord::NEXT
,
2240 "sw.mailmerge", "exception in ToNextRecord()");
2241 pParam
->bEndOfDB
= true;
2247 // synchronized labels contain a next record field at their end
2248 // to assure that the next page can be created in mail merge
2249 // the cursor position must be validated
2250 bool SwDBManager::IsValidMergeRecord() const
2252 return( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->HasValidRecord() );
2255 sal_uInt32
SwDBManager::GetSelectedRecordId()
2257 sal_uInt32 nRet
= 0;
2258 assert( m_pImpl
->pMergeData
&&
2259 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2260 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is())
2264 nRet
= m_pImpl
->pMergeData
->xResultSet
->getRow();
2266 catch(const uno::Exception
&)
2272 bool SwDBManager::ToRecordId(sal_Int32 nSet
)
2274 assert( m_pImpl
->pMergeData
&&
2275 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2276 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is()|| nSet
< 0)
2279 sal_Int32 nAbsPos
= nSet
;
2280 assert(nAbsPos
>= 0);
2281 bRet
= lcl_MoveAbsolute(m_pImpl
->pMergeData
.get(), nAbsPos
);
2282 m_pImpl
->pMergeData
->bEndOfDB
= !bRet
;
2286 bool SwDBManager::OpenDataSource(const OUString
& rDataSource
, const OUString
& rTableOrQuery
)
2289 aData
.sDataSource
= rDataSource
;
2290 aData
.sCommand
= rTableOrQuery
;
2291 aData
.nCommandType
= -1;
2293 SwDSParam
* pFound
= FindDSData(aData
, true);
2294 if(pFound
->xResultSet
.is())
2296 SwDSParam
* pParam
= FindDSConnection(rDataSource
, false);
2297 if(pParam
&& pParam
->xConnection
.is())
2298 pFound
->xConnection
= pParam
->xConnection
;
2299 if(pFound
->xConnection
.is())
2303 uno::Reference
< sdbc::XDatabaseMetaData
> xMetaData
= pFound
->xConnection
->getMetaData();
2306 pFound
->bScrollable
= xMetaData
2307 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE
));
2309 catch(const uno::Exception
&)
2311 // DB driver may not be ODBC 3.0 compliant
2312 pFound
->bScrollable
= true;
2314 pFound
->xStatement
= pFound
->xConnection
->createStatement();
2315 OUString aQuoteChar
= xMetaData
->getIdentifierQuoteString();
2316 OUString sStatement
= "SELECT * FROM " + aQuoteChar
+ rTableOrQuery
+ aQuoteChar
;
2317 pFound
->xResultSet
= pFound
->xStatement
->executeQuery( sStatement
);
2319 //after executeQuery the cursor must be positioned
2320 pFound
->bEndOfDB
= !pFound
->xResultSet
->next();
2321 ++pFound
->nSelectionIndex
;
2323 catch (const uno::Exception
&)
2325 pFound
->xResultSet
= nullptr;
2326 pFound
->xStatement
= nullptr;
2327 pFound
->xConnection
= nullptr;
2330 return pFound
->xResultSet
.is();
2333 uno::Reference
< sdbc::XConnection
> const & SwDBManager::RegisterConnection(OUString
const& rDataSource
)
2335 SwDSParam
* pFound
= SwDBManager::FindDSConnection(rDataSource
, true);
2336 uno::Reference
< sdbc::XDataSource
> xSource
;
2337 if(!pFound
->xConnection
.is())
2339 SwView
* pView
= (m_pDoc
&& m_pDoc
->GetDocShell()) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
2340 pFound
->xConnection
= SwDBManager::GetConnection(rDataSource
, xSource
, pView
);
2343 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2345 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
.get());
2347 catch(const uno::Exception
&)
2351 return pFound
->xConnection
;
2354 sal_uInt32
SwDBManager::GetSelectedRecordId(
2355 const OUString
& rDataSource
, const OUString
& rTableOrQuery
, sal_Int32 nCommandType
)
2357 sal_uInt32 nRet
= 0xffffffff;
2358 //check for merge data source first
2359 if(m_pImpl
->pMergeData
&&
2360 ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2361 rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
) ||
2362 (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty())) &&
2363 (nCommandType
== -1 || nCommandType
== m_pImpl
->pMergeData
->nCommandType
) &&
2364 m_pImpl
->pMergeData
->xResultSet
.is())
2366 nRet
= GetSelectedRecordId();
2371 aData
.sDataSource
= rDataSource
;
2372 aData
.sCommand
= rTableOrQuery
;
2373 aData
.nCommandType
= nCommandType
;
2374 SwDSParam
* pFound
= FindDSData(aData
, false);
2375 if(pFound
&& pFound
->xResultSet
.is())
2378 { //if a selection array is set the current row at the result set may not be set yet
2379 if(pFound
->aSelection
.hasElements())
2381 sal_Int32 nSelIndex
= pFound
->nSelectionIndex
;
2382 if(nSelIndex
>= pFound
->aSelection
.getLength())
2383 nSelIndex
= pFound
->aSelection
.getLength() -1;
2384 pFound
->aSelection
.getConstArray()[nSelIndex
] >>= nRet
;
2388 nRet
= pFound
->xResultSet
->getRow();
2390 catch(const uno::Exception
&)
2398 // close all data sources - after fields were updated
2399 void SwDBManager::CloseAll(bool bIncludingMerge
)
2401 //the only thing done here is to reset the selection index
2402 //all connections stay open
2403 for (auto & pParam
: m_DataSourceParams
)
2405 if (bIncludingMerge
|| pParam
.get() != m_pImpl
->pMergeData
.get())
2407 pParam
->nSelectionIndex
= 0;
2408 pParam
->bEndOfDB
= false;
2411 if(!m_bInMerge
&& pParam
->xResultSet
.is())
2412 pParam
->xResultSet
->first();
2414 catch(const uno::Exception
&)
2420 SwDSParam
* SwDBManager::FindDSData(const SwDBData
& rData
, bool bCreate
)
2422 //prefer merge data if available
2423 if(m_pImpl
->pMergeData
&&
2424 ((rData
.sDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2425 rData
.sCommand
== m_pImpl
->pMergeData
->sCommand
) ||
2426 (rData
.sDataSource
.isEmpty() && rData
.sCommand
.isEmpty())) &&
2427 (rData
.nCommandType
== -1 || rData
.nCommandType
== m_pImpl
->pMergeData
->nCommandType
||
2428 (bCreate
&& m_pImpl
->pMergeData
->nCommandType
== -1)))
2430 return m_pImpl
->pMergeData
.get();
2433 SwDSParam
* pFound
= nullptr;
2434 for (size_t nPos
= m_DataSourceParams
.size(); nPos
; nPos
--)
2436 SwDSParam
* pParam
= m_DataSourceParams
[nPos
- 1].get();
2437 if(rData
.sDataSource
== pParam
->sDataSource
&&
2438 rData
.sCommand
== pParam
->sCommand
&&
2439 (rData
.nCommandType
== -1 || rData
.nCommandType
== pParam
->nCommandType
||
2440 (bCreate
&& pParam
->nCommandType
== -1)))
2442 // calls from the calculator may add a connection with an invalid commandtype
2443 //later added "real" data base connections have to re-use the already available
2444 //DSData and set the correct CommandType
2445 if(bCreate
&& pParam
->nCommandType
== -1)
2446 pParam
->nCommandType
= rData
.nCommandType
;
2451 if(bCreate
&& !pFound
)
2453 pFound
= new SwDSParam(rData
);
2454 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2457 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2459 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
.get());
2461 catch(const uno::Exception
&)
2468 SwDSParam
* SwDBManager::FindDSConnection(const OUString
& rDataSource
, bool bCreate
)
2470 //prefer merge data if available
2471 if(m_pImpl
->pMergeData
&& rDataSource
== m_pImpl
->pMergeData
->sDataSource
)
2473 SetAsUsed(rDataSource
);
2474 return m_pImpl
->pMergeData
.get();
2476 SwDSParam
* pFound
= nullptr;
2477 for (const auto & pParam
: m_DataSourceParams
)
2479 if(rDataSource
== pParam
->sDataSource
)
2481 SetAsUsed(rDataSource
);
2482 pFound
= pParam
.get();
2486 if(bCreate
&& !pFound
)
2489 aData
.sDataSource
= rDataSource
;
2490 pFound
= new SwDSParam(aData
);
2491 SetAsUsed(rDataSource
);
2492 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2495 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2497 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
.get());
2499 catch(const uno::Exception
&)
2506 const SwDBData
& SwDBManager::GetAddressDBName()
2508 return SW_MOD()->GetDBConfig()->GetAddressSource();
2511 uno::Sequence
<OUString
> SwDBManager::GetExistingDatabaseNames()
2513 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2514 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2515 return xDBContext
->getElementNames();
2520 DBConnURIType
GetDBunoType(const INetURLObject
&rURL
)
2522 OUString
sExt(rURL
.GetFileExtension());
2523 DBConnURIType type
= DBConnURIType::UNKNOWN
;
2527 type
= DBConnURIType::ODB
;
2529 else if (sExt
.equalsIgnoreAsciiCase("sxc")
2530 || sExt
.equalsIgnoreAsciiCase("ods")
2531 || sExt
.equalsIgnoreAsciiCase("xls")
2532 || sExt
.equalsIgnoreAsciiCase("xlsx"))
2534 type
= DBConnURIType::CALC
;
2536 else if (sExt
.equalsIgnoreAsciiCase("sxw") || sExt
.equalsIgnoreAsciiCase("odt") || sExt
.equalsIgnoreAsciiCase("doc") || sExt
.equalsIgnoreAsciiCase("docx"))
2538 type
= DBConnURIType::WRITER
;
2540 else if (sExt
.equalsIgnoreAsciiCase("dbf"))
2542 type
= DBConnURIType::DBASE
;
2544 else if (sExt
.equalsIgnoreAsciiCase("csv") || sExt
.equalsIgnoreAsciiCase("txt"))
2546 type
= DBConnURIType::FLAT
;
2549 else if (sExt
.equalsIgnoreAsciiCase("mdb") || sExt
.equalsIgnoreAsciiCase("mde"))
2551 type
= DBConnURIType::MSJET
;
2553 else if (sExt
.equalsIgnoreAsciiCase("accdb") || sExt
.equalsIgnoreAsciiCase("accde"))
2555 type
= DBConnURIType::MSACE
;
2564 uno::Any
GetDBunoURI(const INetURLObject
&rURL
, DBConnURIType
& rType
)
2568 if (rType
== DBConnURIType::UNKNOWN
)
2569 rType
= GetDBunoType(rURL
);
2572 case DBConnURIType::UNKNOWN
:
2573 case DBConnURIType::ODB
:
2575 case DBConnURIType::CALC
:
2577 OUString sDBURL
= "sdbc:calc:" +
2578 rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2582 case DBConnURIType::WRITER
:
2584 OUString sDBURL
= "sdbc:writer:" +
2585 rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2589 case DBConnURIType::DBASE
:
2591 INetURLObject
aUrlTmp(rURL
);
2592 aUrlTmp
.removeSegment();
2593 aUrlTmp
.removeFinalSlash();
2594 OUString sDBURL
= "sdbc:dbase:" +
2595 aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2599 case DBConnURIType::FLAT
:
2601 INetURLObject
aUrlTmp(rURL
);
2602 aUrlTmp
.removeSegment();
2603 aUrlTmp
.removeFinalSlash();
2604 OUString sDBURL
= "sdbc:flat:" +
2605 //only the 'path' has to be added
2606 aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2610 case DBConnURIType::MSJET
:
2613 OUString
sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL
.PathToFileName());
2618 case DBConnURIType::MSACE
:
2621 OUString
sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL
.PathToFileName());
2630 /// Returns the URL of this SwDoc.
2631 OUString
getOwnURL(SfxObjectShell
const * pDocShell
)
2638 const INetURLObject
& rURLObject
= pDocShell
->GetMedium()->GetURLObject();
2639 aRet
= rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2644 Loads a data source from file and registers it.
2646 In case of success it returns the registered name, otherwise an empty string.
2647 Optionally add a prefix to the registered DB name.
2649 OUString
LoadAndRegisterDataSource_Impl(DBConnURIType type
, const uno::Reference
< beans::XPropertySet
> *pSettings
,
2650 const INetURLObject
&rURL
, const OUString
*pDestDir
, SfxObjectShell
* pDocShell
)
2652 OUString
sExt(rURL
.GetFileExtension());
2653 uno::Any aTableFilterAny
;
2654 uno::Any aSuppressVersionsAny
;
2658 uno::Sequence
<OUString
> aFilters(1);
2660 uno::Any aURLAny
= GetDBunoURI(rURL
, type
);
2662 case DBConnURIType::UNKNOWN
:
2663 case DBConnURIType::CALC
:
2664 case DBConnURIType::WRITER
:
2666 case DBConnURIType::ODB
:
2669 case DBConnURIType::FLAT
:
2670 case DBConnURIType::DBASE
:
2671 //set the filter to the file name without extension
2672 aFilters
[0] = rURL
.getBase(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
2673 aTableFilterAny
<<= aFilters
;
2675 case DBConnURIType::MSJET
:
2676 case DBConnURIType::MSACE
:
2677 aSuppressVersionsAny
<<= true;
2683 uno::Reference
<uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext());
2684 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2686 OUString sNewName
= rURL
.getName(
2687 INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::Unambiguous
);
2688 sal_Int32 nExtLen
= sExt
.getLength();
2689 sNewName
= sNewName
.replaceAt(sNewName
.getLength() - nExtLen
- 1, nExtLen
+ 1, "");
2691 //find a unique name if sNewName already exists
2693 sal_Int32 nIndex
= 0;
2694 while (xDBContext
->hasByName(sFind
))
2695 sFind
= sNewName
+ OUString::number(++nIndex
);
2697 uno::Reference
<uno::XInterface
> xNewInstance
;
2701 uno::Any aDataSource
= xDBContext
->getByName(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
2702 aDataSource
>>= xNewInstance
;
2706 xNewInstance
= xDBContext
->createInstance();
2707 uno::Reference
<beans::XPropertySet
> xDataProperties(xNewInstance
, uno::UNO_QUERY
);
2709 if (aURLAny
.hasValue())
2710 xDataProperties
->setPropertyValue("URL", aURLAny
);
2711 if (aTableFilterAny
.hasValue())
2712 xDataProperties
->setPropertyValue("TableFilter", aTableFilterAny
);
2713 if (aSuppressVersionsAny
.hasValue())
2714 xDataProperties
->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny
);
2715 if (aInfoAny
.hasValue())
2716 xDataProperties
->setPropertyValue("Info", aInfoAny
);
2718 if (DBConnURIType::FLAT
== type
&& pSettings
)
2720 uno::Any aSettings
= xDataProperties
->getPropertyValue("Settings");
2721 uno::Reference
< beans::XPropertySet
> xDSSettings
;
2722 aSettings
>>= xDSSettings
;
2723 ::comphelper::copyProperties(*pSettings
, xDSSettings
);
2724 xDSSettings
->setPropertyValue("Extension", uno::makeAny(sExt
));
2727 uno::Reference
<sdb::XDocumentDataSource
> xDS(xNewInstance
, uno::UNO_QUERY_THROW
);
2728 uno::Reference
<frame::XStorable
> xStore(xDS
->getDatabaseDocument(), uno::UNO_QUERY_THROW
);
2729 OUString aOwnURL
= getOwnURL(pDocShell
);
2730 if (aOwnURL
.isEmpty())
2732 // Cannot embed, as embedded data source would need the URL of the parent document.
2733 OUString
const sOutputExt
= ".odb";
2734 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2735 utl::TempFile
aTempFile(sNewName
, true, &sOutputExt
, pDestDir
? pDestDir
: &sHomePath
);
2736 const OUString
& sTmpName
= aTempFile
.GetURL();
2737 xStore
->storeAsURL(sTmpName
, uno::Sequence
<beans::PropertyValue
>());
2742 OUString aStreamRelPath
= "EmbeddedDatabase";
2743 uno::Reference
<embed::XStorage
> xStorage
= pDocShell
->GetStorage();
2745 // Refer to the sub-storage name in the document settings, so
2746 // we can load it again next time the file is imported.
2747 uno::Reference
<lang::XMultiServiceFactory
> xFactory(pDocShell
->GetModel(), uno::UNO_QUERY
);
2748 uno::Reference
<beans::XPropertySet
> xPropertySet(xFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
2749 xPropertySet
->setPropertyValue("EmbeddedDatabaseName", uno::makeAny(aStreamRelPath
));
2751 // Store it only after setting the above property, so that only one data source gets registered.
2752 SwDBManager::StoreEmbeddedDataSource(xStore
, xStorage
, aStreamRelPath
, aOwnURL
);
2755 xDBContext
->registerObject(sFind
, xNewInstance
);
2757 catch (const uno::Exception
&)
2764 // Construct vnd.sun.star.pkg:// URL
2765 OUString
ConstructVndSunStarPkgUrl(const OUString
& rMainURL
, const OUString
& rStreamRelPath
)
2767 auto xContext(comphelper::getProcessComponentContext());
2768 auto xUri
= css::uri::UriReferenceFactory::create(xContext
)->parse(rMainURL
);
2770 xUri
= css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext
)
2771 ->createVndSunStarPkgUrlReference(xUri
);
2773 return xUri
->getUriReference() + "/"
2774 + INetURLObject::encode(
2775 rStreamRelPath
, INetURLObject::PART_FPATH
,
2776 INetURLObject::EncodeMechanism::All
);
2780 OUString
SwDBManager::LoadAndRegisterDataSource(weld::Window
* pParent
, SwDocShell
* pDocShell
)
2782 sfx2::FileDialogHelper
aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
, FileDialogFlags::NONE
, pParent
);
2783 uno::Reference
< ui::dialogs::XFilePicker3
> xFP
= aDlgHelper
.GetFilePicker();
2785 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2786 aDlgHelper
.SetDisplayDirectory( sHomePath
);
2788 OUString
sFilterAll(SwResId(STR_FILTER_ALL
));
2789 OUString
sFilterAllData(SwResId(STR_FILTER_ALL_DATA
));
2790 OUString
sFilterSXB(SwResId(STR_FILTER_SXB
));
2791 OUString
sFilterSXC(SwResId(STR_FILTER_SXC
));
2792 OUString
sFilterSXW(SwResId(STR_FILTER_SXW
));
2793 OUString
sFilterDBF(SwResId(STR_FILTER_DBF
));
2794 OUString
sFilterXLS(SwResId(STR_FILTER_XLS
));
2795 OUString
sFilterDOC(SwResId(STR_FILTER_DOC
));
2796 OUString
sFilterTXT(SwResId(STR_FILTER_TXT
));
2797 OUString
sFilterCSV(SwResId(STR_FILTER_CSV
));
2799 OUString
sFilterMDB(SwResId(STR_FILTER_MDB
));
2800 OUString
sFilterACCDB(SwResId(STR_FILTER_ACCDB
));
2802 xFP
->appendFilter( sFilterAll
, "*" );
2803 xFP
->appendFilter( sFilterAllData
, "*.ods;*.sxc;*.odt;*.sxw;*.dbf;*.xls;*.xlsx;*.doc;*.docx;*.txt;*.csv");
2805 xFP
->appendFilter( sFilterSXB
, "*.odb" );
2806 xFP
->appendFilter( sFilterSXC
, "*.ods;*.sxc" );
2807 xFP
->appendFilter( sFilterSXW
, "*.odt;*.sxw" );
2808 xFP
->appendFilter( sFilterDBF
, "*.dbf" );
2809 xFP
->appendFilter( sFilterXLS
, "*.xls;*.xlsx" );
2810 xFP
->appendFilter( sFilterDOC
, "*.doc;*.docx" );
2811 xFP
->appendFilter( sFilterTXT
, "*.txt" );
2812 xFP
->appendFilter( sFilterCSV
, "*.csv" );
2814 xFP
->appendFilter(sFilterMDB
, "*.mdb;*.mde");
2815 xFP
->appendFilter(sFilterACCDB
, "*.accdb;*.accde");
2818 xFP
->setCurrentFilter( sFilterAll
) ;
2820 if( ERRCODE_NONE
== aDlgHelper
.Execute() )
2822 uno::Reference
< beans::XPropertySet
> aSettings
;
2823 const INetURLObject
aURL( xFP
->getSelectedFiles().getConstArray()[0] );
2824 const DBConnURIType type
= GetDBunoType( aURL
);
2826 if( DBConnURIType::FLAT
== type
)
2828 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2829 uno::Reference
< sdb::XTextConnectionSettings
> xSettingsDlg
= sdb::TextConnectionSettings::create(xContext
);
2830 if( xSettingsDlg
->execute() )
2831 aSettings
.set( uno::Reference
< beans::XPropertySet
>( xSettingsDlg
, uno::UNO_QUERY_THROW
) );
2833 sFind
= LoadAndRegisterDataSource_Impl( type
, DBConnURIType::FLAT
== type
? &aSettings
: nullptr, aURL
, nullptr, pDocShell
);
2835 m_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(pDocShell
, sFind
));
2840 void SwDBManager::StoreEmbeddedDataSource(const uno::Reference
<frame::XStorable
>& xStorable
,
2841 const uno::Reference
<embed::XStorage
>& xStorage
,
2842 const OUString
& rStreamRelPath
,
2843 const OUString
& rOwnURL
, bool bCopyTo
)
2845 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2846 OUString
const sTmpName
= ConstructVndSunStarPkgUrl(rOwnURL
, rStreamRelPath
);
2848 uno::Sequence
<beans::PropertyValue
> aSequence
= comphelper::InitPropertySequence(
2850 {"TargetStorage", uno::makeAny(xStorage
)},
2851 {"StreamRelPath", uno::makeAny(rStreamRelPath
)},
2852 {"BaseURI", uno::makeAny(rOwnURL
)}
2855 xStorable
->storeToURL(sTmpName
, aSequence
);
2857 xStorable
->storeAsURL(sTmpName
, aSequence
);
2860 OUString
SwDBManager::LoadAndRegisterDataSource(const OUString
&rURI
, const OUString
*pDestDir
)
2862 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN
, nullptr, INetURLObject(rURI
), pDestDir
, nullptr );
2867 // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2868 // which allows the original storage to be deleted
2869 void switchEmbeddedDatabaseStorage(const uno::Reference
<sdb::XDatabaseContext
>& rDatabaseContext
, const OUString
& rName
)
2871 uno::Reference
<sdb::XDocumentDataSource
> xDS(rDatabaseContext
->getByName(rName
), uno::UNO_QUERY
);
2874 uno::Reference
<document::XStorageBasedDocument
> xStorageDoc(xDS
->getDatabaseDocument(), uno::UNO_QUERY
);
2877 xStorageDoc
->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2881 void SwDBManager::RevokeDataSource(const OUString
& rName
)
2883 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2884 if (xDatabaseContext
->hasByName(rName
))
2886 switchEmbeddedDatabaseStorage(xDatabaseContext
, rName
);
2887 xDatabaseContext
->revokeObject(rName
);
2891 void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData
& rData
, const SwDocShell
& rDocShell
)
2893 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2895 OUString sDataSource
= rData
.sDataSource
;
2897 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2898 if (sDataSource
.isEmpty())
2899 sDataSource
= "EmbeddedDatabase";
2901 SwDBManager::RevokeDataSource( sDataSource
);
2903 // Encode the stream name and the real path into a single URL.
2904 const INetURLObject
& rURLObject
= rDocShell
.GetMedium()->GetURLObject();
2905 OUString
const aURL
= ConstructVndSunStarPkgUrl(
2906 rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
2909 uno::Reference
<uno::XInterface
> xDataSource(xDatabaseContext
->getByName(aURL
), uno::UNO_QUERY
);
2910 xDatabaseContext
->registerObject( sDataSource
, xDataSource
);
2912 // temp file - don't remember connection
2913 if (rData
.sDataSource
.isEmpty())
2914 m_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(nullptr, sDataSource
));
2917 void SwDBManager::ExecuteFormLetter( SwWrtShell
& rSh
,
2918 const uno::Sequence
<beans::PropertyValue
>& rProperties
)
2920 //prevent second call
2921 if(m_pImpl
->pMergeDialog
)
2923 OUString sDataSource
, sDataTableOrQuery
;
2924 uno::Sequence
<uno::Any
> aSelection
;
2926 sal_Int32 nCmdType
= sdb::CommandType::TABLE
;
2927 uno::Reference
< sdbc::XConnection
> xConnection
;
2929 svx::ODataAccessDescriptor
aDescriptor(rProperties
);
2930 sDataSource
= aDescriptor
.getDataSource();
2931 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= sDataTableOrQuery
);
2932 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= nCmdType
);
2934 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
2935 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
2936 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
2937 aDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
2939 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty())
2941 OSL_FAIL("PropertyValues missing or unset");
2945 //always create a connection for the dialog and dispose it after the dialog has been closed
2946 SwDSParam
* pFound
= nullptr;
2947 if(!xConnection
.is())
2949 xConnection
= SwDBManager::RegisterConnection(sDataSource
);
2950 pFound
= FindDSConnection(sDataSource
, true);
2952 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
2953 m_pImpl
->pMergeDialog
= pFact
->CreateMailMergeDlg(rSh
.GetView().GetViewFrame()->GetWindow().GetFrameWeld(), rSh
,
2958 if(m_pImpl
->pMergeDialog
->Execute() == RET_OK
)
2960 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] <<= m_pImpl
->pMergeDialog
->GetSelection();
2962 uno::Reference
<sdbc::XResultSet
> xResSet
= m_pImpl
->pMergeDialog
->GetResultSet();
2964 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
2966 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2967 SfxObjectShellRef xDocShell
= rSh
.GetView().GetViewFrame()->GetObjectShell();
2969 lcl_emitEvent(SfxEventHintId::SwMailMerge
, STR_SW_EVENT_MAIL_MERGE
, xDocShell
.get());
2971 // prepare mail merge descriptor
2972 SwMergeDescriptor
aMergeDesc( m_pImpl
->pMergeDialog
->GetMergeType(), rSh
, aDescriptor
);
2973 aMergeDesc
.sSaveToFilter
= m_pImpl
->pMergeDialog
->GetSaveFilter();
2974 aMergeDesc
.bCreateSingleFile
= m_pImpl
->pMergeDialog
->IsSaveSingleDoc();
2975 aMergeDesc
.bPrefixIsFilename
= aMergeDesc
.bCreateSingleFile
;
2976 aMergeDesc
.sPrefix
= m_pImpl
->pMergeDialog
->GetTargetURL();
2978 if(!aMergeDesc
.bCreateSingleFile
)
2980 if(m_pImpl
->pMergeDialog
->IsGenerateFromDataBase())
2981 aMergeDesc
.sDBcolumn
= m_pImpl
->pMergeDialog
->GetColumnName();
2983 if(m_pImpl
->pMergeDialog
->IsFileEncryptedFromDataBase())
2984 aMergeDesc
.sDBPasswordColumn
= m_pImpl
->pMergeDialog
->GetPasswordColumnName();
2987 Merge( aMergeDesc
);
2989 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd
, STR_SW_EVENT_MAIL_MERGE_END
, xDocShell
.get());
2991 // reset the cursor inside
2993 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
2997 for (const auto & pParam
: m_DataSourceParams
)
2999 if (pParam
.get() == pFound
)
3003 uno::Reference
<lang::XComponent
> xComp(pParam
->xConnection
, uno::UNO_QUERY
);
3007 catch(const uno::RuntimeException
&)
3009 //may be disposed already since multiple entries may have used the same connection
3013 //pFound doesn't need to be removed/deleted -
3014 //this has been done by the SwConnectionDisposedListener_Impl already
3017 m_pImpl
->pMergeDialog
.disposeAndClear();
3020 void SwDBManager::InsertText(SwWrtShell
& rSh
,
3021 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
3023 OUString sDataSource
, sDataTableOrQuery
;
3024 uno::Reference
<sdbc::XResultSet
> xResSet
;
3025 uno::Sequence
<uno::Any
> aSelection
;
3026 sal_Int16 nCmdType
= sdb::CommandType::TABLE
;
3027 uno::Reference
< sdbc::XConnection
> xConnection
;
3028 for(const beans::PropertyValue
& rValue
: rProperties
)
3030 if ( rValue
.Name
== "DataSourceName" )
3031 rValue
.Value
>>= sDataSource
;
3032 else if ( rValue
.Name
== "Command" )
3033 rValue
.Value
>>= sDataTableOrQuery
;
3034 else if ( rValue
.Name
== "Cursor" )
3035 rValue
.Value
>>= xResSet
;
3036 else if ( rValue
.Name
== "Selection" )
3037 rValue
.Value
>>= aSelection
;
3038 else if ( rValue
.Name
== "CommandType" )
3039 rValue
.Value
>>= nCmdType
;
3040 else if ( rValue
.Name
== "ActiveConnection" )
3041 rValue
.Value
>>= xConnection
;
3043 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty() || !xResSet
.is())
3045 OSL_FAIL("PropertyValues missing or unset");
3048 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
3049 uno::Reference
<sdbc::XDataSource
> xSource
;
3050 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
3052 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3054 xSource
= dbtools::getDataSource(sDataSource
, xContext
);
3055 uno::Reference
< sdbcx::XColumnsSupplier
> xColSupp( xResSet
, uno::UNO_QUERY
);
3057 aDBData
.sDataSource
= sDataSource
;
3058 aDBData
.sCommand
= sDataTableOrQuery
;
3059 aDBData
.nCommandType
= nCmdType
;
3061 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
3062 ScopedVclPtr
<AbstractSwInsertDBColAutoPilot
> pDlg(pFact
->CreateSwInsertDBColAutoPilot( rSh
.GetView(),
3066 if( RET_OK
!= pDlg
->Execute() )
3070 if(!xConnection
.is())
3071 xConnection
= xSource
->getConnection(sDummy
, sDummy
);
3074 pDlg
->DataToDoc( aSelection
, xSource
, xConnection
, xResSet
);
3076 catch (const uno::Exception
&)
3078 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3082 uno::Reference
<sdbc::XDataSource
> SwDBManager::getDataSourceAsParent(const uno::Reference
< sdbc::XConnection
>& _xConnection
,const OUString
& _sDataSourceName
)
3084 uno::Reference
<sdbc::XDataSource
> xSource
;
3087 uno::Reference
<container::XChild
> xChild(_xConnection
, uno::UNO_QUERY
);
3089 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3090 if ( !xSource
.is() )
3091 xSource
= dbtools::getDataSource(_sDataSourceName
, ::comphelper::getProcessComponentContext());
3093 catch (const uno::Exception
&)
3095 TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3100 uno::Reference
<sdbc::XResultSet
> SwDBManager::createCursor(const OUString
& _sDataSourceName
,
3101 const OUString
& _sCommand
,
3102 sal_Int32 _nCommandType
,
3103 const uno::Reference
<sdbc::XConnection
>& _xConnection
,
3104 const SwView
* pView
)
3106 uno::Reference
<sdbc::XResultSet
> xResultSet
;
3109 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
3112 uno::Reference
<uno::XInterface
> xInstance
= xMgr
->createInstance("com.sun.star.sdb.RowSet");
3113 uno::Reference
<beans::XPropertySet
> xRowSetPropSet(xInstance
, uno::UNO_QUERY
);
3114 if(xRowSetPropSet
.is())
3116 xRowSetPropSet
->setPropertyValue("DataSourceName", uno::makeAny(_sDataSourceName
));
3117 xRowSetPropSet
->setPropertyValue("ActiveConnection", uno::makeAny(_xConnection
));
3118 xRowSetPropSet
->setPropertyValue("Command", uno::makeAny(_sCommand
));
3119 xRowSetPropSet
->setPropertyValue("CommandType", uno::makeAny(_nCommandType
));
3121 uno::Reference
< sdb::XCompletedExecution
> xRowSet(xInstance
, uno::UNO_QUERY
);
3125 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
3126 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr
), pWindow
? pWindow
->GetXWindow() : nullptr), uno::UNO_QUERY_THROW
);
3127 xRowSet
->executeWithCompletion(xHandler
);
3129 xResultSet
.set(xRowSet
, uno::UNO_QUERY
);
3133 catch (const uno::Exception
&)
3135 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3140 void SwDBManager::setEmbeddedName(const OUString
& rEmbeddedName
, SwDocShell
& rDocShell
)
3142 bool bLoad
= m_sEmbeddedName
!= rEmbeddedName
&& !rEmbeddedName
.isEmpty();
3143 bool bRegisterListener
= m_sEmbeddedName
.isEmpty() && !rEmbeddedName
.isEmpty();
3145 m_sEmbeddedName
= rEmbeddedName
;
3149 uno::Reference
<embed::XStorage
> xStorage
= rDocShell
.GetStorage();
3150 // It's OK that we don't have the named sub-storage yet, in case
3151 // we're in the process of creating it.
3152 if (xStorage
->hasByName(rEmbeddedName
))
3153 LoadAndRegisterEmbeddedDataSource(rDocShell
.GetDoc()->GetDBData(), rDocShell
);
3156 if (bRegisterListener
)
3157 // Register a remove listener, so we know when the embedded data source is removed.
3158 m_pImpl
->m_xDataSourceRemovedListener
= new SwDataSourceRemovedListener(*this);
3161 const OUString
& SwDBManager::getEmbeddedName() const
3163 return m_sEmbeddedName
;
3166 SwDoc
* SwDBManager::getDoc() const
3171 void SwDBManager::releaseRevokeListener()
3173 if (m_pImpl
->m_xDataSourceRemovedListener
.is())
3175 m_pImpl
->m_xDataSourceRemovedListener
->Dispose();
3176 m_pImpl
->m_xDataSourceRemovedListener
.clear();
3180 SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager
& rManager
)
3181 : m_pDBManager(&rManager
)
3185 void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject
& rSource
)
3187 ::SolarMutexGuard aGuard
;
3189 if (!m_pDBManager
) return; // we're disposed too!
3191 uno::Reference
<sdbc::XConnection
> xSource(rSource
.Source
, uno::UNO_QUERY
);
3192 for (size_t nPos
= m_pDBManager
->m_DataSourceParams
.size(); nPos
; nPos
--)
3194 SwDSParam
* pParam
= m_pDBManager
->m_DataSourceParams
[nPos
- 1].get();
3195 if(pParam
->xConnection
.is() &&
3196 (xSource
== pParam
->xConnection
))
3198 m_pDBManager
->m_DataSourceParams
.erase(
3199 m_pDBManager
->m_DataSourceParams
.begin() + nPos
- 1);
3204 std::shared_ptr
<SwMailMergeConfigItem
> SwDBManager::PerformMailMerge(SwView
const * pView
)
3206 std::shared_ptr
<SwMailMergeConfigItem
> xConfigItem
= pView
->GetMailMergeConfigItem();
3210 svx::ODataAccessDescriptor aDescriptor
;
3211 aDescriptor
.setDataSource(xConfigItem
->GetCurrentDBData().sDataSource
);
3212 aDescriptor
[ svx::DataAccessDescriptorProperty::Connection
] <<= xConfigItem
->GetConnection().getTyped();
3213 aDescriptor
[ svx::DataAccessDescriptorProperty::Cursor
] <<= xConfigItem
->GetResultSet();
3214 aDescriptor
[ svx::DataAccessDescriptorProperty::Command
] <<= xConfigItem
->GetCurrentDBData().sCommand
;
3215 aDescriptor
[ svx::DataAccessDescriptorProperty::CommandType
] <<= xConfigItem
->GetCurrentDBData().nCommandType
;
3216 aDescriptor
[ svx::DataAccessDescriptorProperty::Selection
] <<= xConfigItem
->GetSelection();
3218 SwWrtShell
& rSh
= pView
->GetWrtShell();
3219 xConfigItem
->SetTargetView(nullptr);
3221 SwMergeDescriptor
aMergeDesc(DBMGR_MERGE_SHELL
, rSh
, aDescriptor
);
3222 aMergeDesc
.pMailMergeConfigItem
= xConfigItem
.get();
3223 aMergeDesc
.bCreateSingleFile
= true;
3224 rSh
.GetDBManager()->Merge(aMergeDesc
);
3229 void SwDBManager::RevokeLastRegistrations()
3231 if (m_aUncommittedRegistrations
.empty())
3234 SwView
* pView
= ( m_pDoc
&& m_pDoc
->GetDocShell() ) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
3237 const std::shared_ptr
<SwMailMergeConfigItem
>& xConfigItem
= pView
->GetMailMergeConfigItem();
3240 xConfigItem
->DisposeResultSet();
3241 xConfigItem
->DocumentReloaded();
3245 for (auto it
= m_aUncommittedRegistrations
.begin(); it
!= m_aUncommittedRegistrations
.end();)
3247 if ((m_pDoc
&& it
->first
== m_pDoc
->GetDocShell()) || it
->first
== nullptr)
3249 RevokeDataSource(it
->second
);
3250 it
= m_aUncommittedRegistrations
.erase(it
);
3257 void SwDBManager::CommitLastRegistrations()
3259 for (auto aIt
= m_aUncommittedRegistrations
.begin(); aIt
!= m_aUncommittedRegistrations
.end();)
3261 if (aIt
->first
== m_pDoc
->GetDocShell() || aIt
->first
== nullptr)
3263 m_aNotUsedConnections
.push_back(aIt
->second
);
3264 aIt
= m_aUncommittedRegistrations
.erase(aIt
);
3271 void SwDBManager::SetAsUsed(const OUString
& rName
)
3273 auto aFound
= std::find(m_aNotUsedConnections
.begin(), m_aNotUsedConnections
.end(), rName
);
3274 if (aFound
!= m_aNotUsedConnections
.end())
3275 m_aNotUsedConnections
.erase(aFound
);
3278 void SwDBManager::RevokeNotUsedConnections()
3280 for (auto aIt
= m_aNotUsedConnections
.begin(); aIt
!= m_aNotUsedConnections
.end();)
3282 RevokeDataSource(*aIt
);
3283 aIt
= m_aNotUsedConnections
.erase(aIt
);
3287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */