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