LibreOffice Module sw (master) 1
dbmgr.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <cassert>
23
24#include <unotxdoc.hxx>
25#include <sfx2/app.hxx>
26#include <com/sun/star/sdb/CommandType.hpp>
27#include <com/sun/star/sdb/XDocumentDataSource.hpp>
28#include <com/sun/star/lang/DisposedException.hpp>
29#include <com/sun/star/lang/XEventListener.hpp>
30#include <com/sun/star/uri/UriReferenceFactory.hpp>
31#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
32#include <com/sun/star/util/NumberFormatter.hpp>
33#include <com/sun/star/sdb/DatabaseContext.hpp>
34#include <com/sun/star/sdb/TextConnectionSettings.hpp>
35#include <com/sun/star/sdb/XCompletedConnection.hpp>
36#include <com/sun/star/sdb/XCompletedExecution.hpp>
37#include <com/sun/star/container/XChild.hpp>
38#include <com/sun/star/text/MailMergeEvent.hpp>
39#include <com/sun/star/frame/XStorable.hpp>
40#include <com/sun/star/task/InteractionHandler.hpp>
41#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
42#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
43#include <com/sun/star/beans/XPropertySet.hpp>
44#include <vcl/errinf.hxx>
45#include <vcl/print.hxx>
46#include <vcl/scheduler.hxx>
47#include <sfx2/fcontnr.hxx>
49#include <sfx2/viewfrm.hxx>
50#include <dbconfig.hxx>
51#include <unotools/tempfile.hxx>
53#include <svl/numformat.hxx>
54#include <svl/zforlist.hxx>
55#include <svl/stritem.hxx>
56#include <sfx2/docfile.hxx>
57#include <sfx2/docfilt.hxx>
58#include <sfx2/progress.hxx>
59#include <sfx2/dispatch.hxx>
60#include <cmdid.h>
61#include <swmodule.hxx>
62#include <view.hxx>
63#include <docsh.hxx>
64#include <edtwin.hxx>
65#include <wrtsh.hxx>
66#include <fldbas.hxx>
67#include <dbui.hxx>
68#include <dbmgr.hxx>
69#include <doc.hxx>
72#include <IDocumentUndoRedo.hxx>
73#include <swwait.hxx>
74#include <swunohelper.hxx>
75#include <strings.hrc>
76#include <mmconfigitem.hxx>
77#include <com/sun/star/sdbc/XRowSet.hpp>
78#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
79#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
80#include <com/sun/star/sdb/XQueriesSupplier.hpp>
81#include <com/sun/star/sdb/XColumn.hpp>
82#include <com/sun/star/sdbc/DataType.hpp>
83#include <com/sun/star/sdbc/ResultSetType.hpp>
84#include <com/sun/star/sdbc/SQLException.hpp>
85#include <com/sun/star/mail/MailAttachment.hpp>
90#include <comphelper/string.hxx>
91#include <comphelper/types.hxx>
92#include <mailmergehelper.hxx>
93#include <maildispatcher.hxx>
94#include <svtools/htmlcfg.hxx>
96#include <com/sun/star/util/XNumberFormatTypes.hpp>
97#include <svl/numuno.hxx>
100#include <unotools/charclass.hxx>
102
103#include <unomailmerge.hxx>
104#include <sfx2/event.hxx>
106#include <rtl/textenc.h>
107#include <rtl/tencinfo.h>
109#include <ndindex.hxx>
110#include <swevent.hxx>
111#include <sal/log.hxx>
112#include <swabstdlg.hxx>
113#include <vector>
114#include <section.hxx>
115#include <rootfrm.hxx>
116#include <calc.hxx>
117#include <dbfld.hxx>
118#include <IDocumentState.hxx>
119#include <imaildsplistener.hxx>
120#include <iodetect.hxx>
122
123#include <memory>
124#include <mutex>
126
127using namespace ::com::sun::star;
128using namespace sw;
129
130namespace {
131
132void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell)
133{
136 pDocShell));
137}
138
139// Construct vnd.sun.star.pkg:// URL
140OUString ConstructVndSunStarPkgUrl(const OUString& rMainURL, std::u16string_view rStreamRelPath)
141{
143 auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(rMainURL);
144 assert(xUri.is());
145 xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)
146 ->createVndSunStarPkgUrlReference(xUri);
147 assert(xUri.is());
148 return xUri->getUriReference() + "/"
150 rStreamRelPath, INetURLObject::PART_FPATH,
152}
153
154}
155
156std::vector<std::pair<SwDocShell*, OUString>> SwDBManager::s_aUncommittedRegistrations;
157
158namespace {
159
160enum class SwDBNextRecord { NEXT, FIRST };
161
162}
163
164static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action = SwDBNextRecord::NEXT );
165
166namespace {
167
168enum class WorkingDocType { SOURCE, TARGET, COPY };
169
170}
171
173 const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
174 const vcl::Window *pSourceWindow,
175 SwDBManager** const ppDBManager,
176 SwView** const pView, SwWrtShell** const pWrtShell, rtl::Reference<SwDoc>* const pDoc );
177
178static bool lcl_getCountFromResultSet( sal_Int32& rCount, const SwDSParam* pParam )
179{
180 rCount = pParam->aSelection.getLength();
181 if ( rCount > 0 )
182 return true;
183
184 uno::Reference<beans::XPropertySet> xPrSet(pParam->xResultSet, uno::UNO_QUERY);
185 if ( xPrSet.is() )
186 {
187 try
188 {
189 bool bFinal = false;
190 uno::Any aFinal = xPrSet->getPropertyValue("IsRowCountFinal");
191 aFinal >>= bFinal;
192 if(!bFinal)
193 {
194 pParam->xResultSet->last();
195 pParam->xResultSet->first();
196 }
197 uno::Any aCount = xPrSet->getPropertyValue("RowCount");
198 if( aCount >>= rCount )
199 return true;
200 }
201 catch(const uno::Exception&)
202 {
203 }
204 }
205 return false;
206}
207
209 : public cppu::WeakImplHelper< lang::XEventListener >
210{
211private:
213
214 virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
215
216public:
218
219 void Dispose() { m_pDBManager = nullptr; }
220
221};
222
223namespace {
224
226class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener>
227{
228 uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext;
229 SwDBManager* m_pDBManager;
230
231public:
232 explicit SwDataSourceRemovedListener(SwDBManager& rDBManager);
233 virtual ~SwDataSourceRemovedListener() override;
234 virtual void SAL_CALL registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
235 virtual void SAL_CALL revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
236 virtual void SAL_CALL changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
237 virtual void SAL_CALL disposing(const lang::EventObject& rObject) override;
238 void Dispose();
239};
240
241}
242
243SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager& rDBManager)
244 : m_pDBManager(&rDBManager)
245{
246 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
247 m_xDatabaseContext = sdb::DatabaseContext::create(xComponentContext);
248 m_xDatabaseContext->addDatabaseRegistrationsListener(this);
249}
250
251SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
252{
253 if (m_xDatabaseContext.is())
254 m_xDatabaseContext->removeDatabaseRegistrationsListener(this);
255}
256
257void SAL_CALL SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& /*rEvent*/)
258{
259}
260
261void SAL_CALL SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
262{
263 if (!m_pDBManager || m_pDBManager->getEmbeddedName().isEmpty())
264 return;
265
266 SwDoc* pDoc = m_pDBManager->getDoc();
267 if (!pDoc)
268 return;
269
270 SwDocShell* pDocShell = pDoc->GetDocShell();
271 if (!pDocShell)
272 return;
273
274 const OUString sTmpName = ConstructVndSunStarPkgUrl(
276 m_pDBManager->getEmbeddedName());
277
278 if (sTmpName != rEvent.OldLocation)
279 return;
280
281 // The revoked database location is inside this document, then remove the
282 // embedding, as otherwise it would be back on the next reload of the
283 // document.
284 pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName());
285 m_pDBManager->setEmbeddedName(OUString(), *pDocShell);
286}
287
288void SAL_CALL SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
289{
290 if (rEvent.OldLocation != rEvent.NewLocation)
291 revokedDatabaseLocation(rEvent);
292}
293
294void SwDataSourceRemovedListener::disposing(const lang::EventObject& /*rObject*/)
295{
296 m_xDatabaseContext.clear();
297}
298
299void SwDataSourceRemovedListener::Dispose()
300{
301 m_pDBManager = nullptr;
302}
303
305{
306 std::unique_ptr<SwDSParam> pMergeData;
311 uno::Reference< mail::XMailMessage> m_xLastMessage;
312
313 explicit SwDBManager_Impl(SwDBManager& rDBManager)
315 {}
316
318 {
319 m_xDisposeListener->Dispose();
322 }
323};
324
325static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<sdbc::XDataSource> const & xSource)
326{
327 uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
328 rParam.xFormatter = util::NumberFormatter::create(xContext);
329 uno::Reference<beans::XPropertySet> xSourceProps(
330 (xSource.is()
331 ? xSource
333 rParam.xConnection, rParam.sDataSource)),
334 uno::UNO_QUERY);
335 if(!xSourceProps.is())
336 return;
337
338 uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
339 if(!aFormats.hasValue())
340 return;
341
342 uno::Reference<util::XNumberFormatsSupplier> xSuppl;
343 aFormats >>= xSuppl;
344 if(xSuppl.is())
345 {
346 uno::Reference< beans::XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
347 uno::Any aNull = xSettings->getPropertyValue("NullDate");
348 aNull >>= rParam.aNullDate;
349 if(rParam.xFormatter.is())
350 rParam.xFormatter->attachNumberFormatsSupplier(xSuppl);
351 }
352}
353
354static bool lcl_MoveAbsolute(SwDSParam* pParam, tools::Long nAbsPos)
355{
356 bool bRet = false;
357 try
358 {
359 if(pParam->aSelection.hasElements())
360 {
361 if(pParam->aSelection.getLength() <= nAbsPos)
362 {
363 pParam->bEndOfDB = true;
364 bRet = false;
365 }
366 else
367 {
368 pParam->nSelectionIndex = nAbsPos;
369 sal_Int32 nPos = 0;
370 pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
371 pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
372 bRet = !pParam->bEndOfDB;
373 }
374 }
375 else if(pParam->bScrollable)
376 {
377 bRet = pParam->xResultSet->absolute( nAbsPos );
378 }
379 else
380 {
381 OSL_FAIL("no absolute positioning available");
382 }
383 }
384 catch(const uno::Exception&)
385 {
386 }
387 return bRet;
388}
389
390static void lcl_GetColumnCnt(SwDSParam *pParam,
391 const uno::Reference< beans::XPropertySet > &rColumnProps,
392 LanguageType nLanguage, OUString &rResult, double* pNumber)
393{
394 SwDBFormatData aFormatData;
395 if(!pParam->xFormatter.is())
396 {
397 uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(
398 pParam->xConnection,pParam->sDataSource);
399 lcl_InitNumberFormatter(*pParam, xSource );
400 }
401 aFormatData.aNullDate = pParam->aNullDate;
402 aFormatData.xFormatter = pParam->xFormatter;
403
404 aFormatData.aLocale = LanguageTag( nLanguage ).getLocale();
405
406 rResult = SwDBManager::GetDBField( rColumnProps, aFormatData, pNumber);
407}
408
409static bool lcl_GetColumnCnt(SwDSParam* pParam, const OUString& rColumnName,
410 LanguageType nLanguage, OUString& rResult, double* pNumber)
411{
412 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pParam->xResultSet, uno::UNO_QUERY );
413 uno::Reference<container::XNameAccess> xCols;
414 try
415 {
416 xCols = xColsSupp->getColumns();
417 }
418 catch(const lang::DisposedException&)
419 {
420 }
421 if(!xCols.is() || !xCols->hasByName(rColumnName))
422 return false;
423 uno::Any aCol = xCols->getByName(rColumnName);
424 uno::Reference< beans::XPropertySet > xColumnProps;
425 aCol >>= xColumnProps;
426 lcl_GetColumnCnt( pParam, xColumnProps, nLanguage, rResult, pNumber );
427 return true;
428};
429
430// import data
431bool SwDBManager::Merge( const SwMergeDescriptor& rMergeDesc )
432{
433 assert( !m_bInMerge && !m_pImpl->pMergeData && "merge already activated!" );
434
435 SfxObjectShellLock xWorkObjSh;
436 SwWrtShell *pWorkShell = nullptr;
437 rtl::Reference<SwDoc> pWorkDoc;
438 SwDBManager *pWorkDocOrigDBManager = nullptr;
439
440 switch( rMergeDesc.nMergeType )
441 {
444 case DBMGR_MERGE_FILE:
446 {
447 SwDocShell *pSourceDocSh = rMergeDesc.rSh.GetView().GetDocShell();
448 if( pSourceDocSh->IsModified() )
449 {
450 pWorkDocOrigDBManager = this;
451 xWorkObjSh = lcl_CreateWorkingDocument(
452 WorkingDocType::SOURCE, rMergeDesc.rSh, nullptr,
453 &pWorkDocOrigDBManager, nullptr, &pWorkShell, &pWorkDoc );
454 }
455 [[fallthrough]];
456 }
457
458 default:
459 if( !xWorkObjSh.Is() )
460 pWorkShell = &rMergeDesc.rSh;
461 break;
462 }
463
465 aData.nCommandType = sdb::CommandType::TABLE;
466 uno::Reference<sdbc::XResultSet> xResSet;
467 uno::Sequence<uno::Any> aSelection;
468 uno::Reference< sdbc::XConnection> xConnection;
469
470 aData.sDataSource = rMergeDesc.rDescriptor.getDataSource();
473
480
481 if((aData.sDataSource.isEmpty() || aData.sCommand.isEmpty()) && !xResSet.is())
482 {
483 return false;
484 }
485
486 m_pImpl->pMergeData.reset(new SwDSParam(aData, xResSet, aSelection));
487 SwDSParam* pTemp = FindDSData(aData, false);
488 if(pTemp)
489 *pTemp = *m_pImpl->pMergeData;
490 else
491 {
492 // calls from the calculator may have added a connection with an invalid commandtype
493 //"real" data base connections added here have to re-use the already available
494 //DSData and set the correct CommandType
495 aData.nCommandType = -1;
496 pTemp = FindDSData(aData, false);
497 if(pTemp)
498 *pTemp = *m_pImpl->pMergeData;
499 else
500 {
501 m_DataSourceParams.push_back(std::make_unique<SwDSParam>(*m_pImpl->pMergeData));
502 try
503 {
504 uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY);
505 if(xComponent.is())
506 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
507 }
508 catch(const uno::Exception&)
509 {
510 }
511 }
512 }
513 if(!m_pImpl->pMergeData->xConnection.is())
514 m_pImpl->pMergeData->xConnection = xConnection;
515 // add an XEventListener
516
517 lcl_ToNextRecord(m_pImpl->pMergeData.get(), SwDBNextRecord::FIRST);
518
519 uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,aData.sDataSource);
520
521 lcl_InitNumberFormatter(*m_pImpl->pMergeData, xSource);
522
523 pWorkShell->ChgDBData(aData);
524 m_bInMerge = true;
525
526 if (IsInitDBFields())
527 {
528 // with database fields without DB-Name, use DB-Name from Doc
529 std::vector<OUString> aDBNames;
530 aDBNames.emplace_back();
531 SwDBData aInsertData = pWorkShell->GetDBData();
532 OUString sDBName = aInsertData.sDataSource
533 + OUStringChar(DB_DELIM) + aInsertData.sCommand
534 + OUStringChar(DB_DELIM)
535 + OUString::number(aInsertData.nCommandType);
536 pWorkShell->ChangeDBFields( aDBNames, sDBName);
537 SetInitDBFields(false);
538 }
539
540 bool bRet = true;
541 switch(rMergeDesc.nMergeType)
542 {
543 case DBMGR_MERGE:
544 pWorkShell->StartAllAction();
545 pWorkShell->SwViewShell::UpdateFields( true );
546 pWorkShell->SetModified();
547 pWorkShell->EndAllAction();
548 break;
549
552 case DBMGR_MERGE_FILE:
554 // save files and send them as e-Mail if required
555 bRet = MergeMailFiles(pWorkShell, rMergeDesc);
556 break;
557
558 default:
559 // insert selected entries
560 // (was: InsertRecord)
561 ImportFromConnection(pWorkShell);
562 break;
563 }
564
565 m_pImpl->pMergeData.reset();
566
567 if( xWorkObjSh.Is() )
568 {
569 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
570 xWorkObjSh->DoClose();
571 }
572
573 m_bInMerge = false;
574
575 return bRet;
576}
577
579{
580 if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
581 return;
582
583 pSh->StartAllAction();
584 pSh->StartUndo();
585 bool bGroupUndo(pSh->DoesGroupUndo());
586 pSh->DoGroupUndo(false);
587
588 if( pSh->HasSelection() )
589 pSh->DelRight();
590
591 std::optional<SwWait> oWait;
592
593 {
594 sal_uLong i = 0;
595 do {
596
597 ImportDBEntry(pSh);
598 if( 10 == ++i )
599 oWait.emplace( *pSh->GetView().GetDocShell(), true);
600
601 } while(ToNextMergeRecord());
602 }
603
604 pSh->DoGroupUndo(bGroupUndo);
605 pSh->EndUndo();
606 pSh->EndAllAction();
607}
608
610{
611 if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
612 return;
613
614 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
615 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
616 OUStringBuffer sStr;
617 uno::Sequence<OUString> aColNames = xCols->getElementNames();
618 const OUString* pColNames = aColNames.getConstArray();
619 tools::Long nLength = aColNames.getLength();
620 for(tools::Long i = 0; i < nLength; i++)
621 {
622 uno::Any aCol = xCols->getByName(pColNames[i]);
623 uno::Reference< beans::XPropertySet > xColumnProp;
624 aCol >>= xColumnProp;
625 SwDBFormatData aDBFormat;
626 sStr.append(GetDBField( xColumnProp, aDBFormat));
627 if (i < nLength - 1)
628 sStr.append("\t");
629 }
630 pSh->SwEditShell::Insert2(sStr.makeStringAndClear());
631 pSh->SwFEShell::SplitNode(); // line feed
632}
633
634bool SwDBManager::GetTableNames(weld::ComboBox& rBox, const OUString& rDBName)
635{
636 bool bRet = false;
637 OUString sOldTableName(rBox.get_active_text());
638 rBox.clear();
639 SwDSParam* pParam = FindDSConnection(rDBName, false);
640 uno::Reference< sdbc::XConnection> xConnection;
641 if (pParam && pParam->xConnection.is())
642 xConnection = pParam->xConnection;
643 else
644 {
645 if ( !rDBName.isEmpty() )
646 xConnection = RegisterConnection( rDBName );
647 }
648 if (xConnection.is())
649 {
650 uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
651 if(xTSupplier.is())
652 {
653 uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
654 const uno::Sequence<OUString> aTables = xTables->getElementNames();
655 for (const OUString& rTable : aTables)
656 rBox.append("0", rTable);
657 }
658 uno::Reference<sdb::XQueriesSupplier> xQSupplier(xConnection, uno::UNO_QUERY);
659 if(xQSupplier.is())
660 {
661 uno::Reference<container::XNameAccess> xQueries = xQSupplier->getQueries();
662 const uno::Sequence<OUString> aQueries = xQueries->getElementNames();
663 for (const OUString& rQuery : aQueries)
664 rBox.append("1", rQuery);
665 }
666 if (!sOldTableName.isEmpty())
667 rBox.set_active_text(sOldTableName);
668 bRet = true;
669 }
670 return bRet;
671}
672
673// fill Listbox with column names of a database
675 const OUString& rDBName, const OUString& rTableName)
676{
678 aData.sDataSource = rDBName;
679 aData.sCommand = rTableName;
680 aData.nCommandType = -1;
681 SwDSParam* pParam = FindDSData(aData, false);
682 uno::Reference< sdbc::XConnection> xConnection;
683 if(pParam && pParam->xConnection.is())
684 xConnection = pParam->xConnection;
685 else
686 {
687 xConnection = RegisterConnection( rDBName );
688 }
689 GetColumnNames(rBox, xConnection, rTableName);
690}
691
693 uno::Reference< sdbc::XConnection> const & xConnection,
694 const OUString& rTableName)
695{
696 rBox.clear();
697 uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
698 if(xColsSupp.is())
699 {
700 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
701 const uno::Sequence<OUString> aColNames = xCols->getElementNames();
702 for (const OUString& rColName : aColNames)
703 {
704 rBox.append_text(rColName);
705 }
706 ::comphelper::disposeComponent( xColsSupp );
707 }
708}
709
712 , m_bInitDBFields(false)
713 , m_bInMerge(false)
714 , m_bMergeSilent(false)
715 , m_pImpl(new SwDBManager_Impl(*this))
716 , m_pMergeEvtSrc(nullptr)
717 , m_pDoc(pDoc)
718{
719}
720
722{
724
725 // copy required, m_DataSourceParams can be modified while disposing components
726 std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections;
727 for (const auto & pParam : m_DataSourceParams)
728 {
729 if(pParam->xConnection.is())
730 {
731 aCopiedConnections.push_back(pParam->xConnection);
732 }
733 }
734 for (const auto & xConnection : aCopiedConnections)
735 {
736 try
737 {
738 uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY);
739 if(xComp.is())
740 xComp->dispose();
741 }
742 catch(const uno::RuntimeException&)
743 {
744 //may be disposed already since multiple entries may have used the same connection
745 }
746 }
747}
748
750{
752}
753
754static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell )
755{
756 //reset all links of the sections of synchronized labels
757 size_t nSections = rWorkShell.GetSectionFormatCount();
758 for (size_t nSection = 0; nSection < nSections; ++nSection)
759 {
760 SwSectionData aSectionData( *rWorkShell.GetSectionFormat( nSection ).GetSection() );
761 if( aSectionData.GetType() == SectionType::FileLink )
762 {
763 aSectionData.SetType( SectionType::Content );
764 aSectionData.SetLinkFileName( OUString() );
765 rWorkShell.UpdateSection( nSection, aSectionData );
766 }
767 }
768 rWorkShell.SetLabelDoc( false );
769}
770
771static void lcl_SaveDebugDoc( SfxObjectShell *xTargetDocShell,
772 const char *name, int no = 0 )
773{
774 static OUString sTempDirURL;
775 if( sTempDirURL.isEmpty() )
776 {
777 SvtPathOptions aPathOpt;
778 utl::TempFileNamed aTempDir( &aPathOpt.GetTempPath(), true );
779 if( aTempDir.IsValid() )
780 {
781 INetURLObject aTempDirURL( aTempDir.GetURL() );
782 sTempDirURL = aTempDirURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
783 SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL );
784 }
785 }
786 if( sTempDirURL.isEmpty() )
787 return;
788
789 OUString basename = OUString::createFromAscii( name );
790 if (no > 0)
791 basename += OUString::number(no) + "-";
792 // aTempFile is not deleted, but that seems to be intentional
793 utl::TempFileNamed aTempFile( basename, true, u".odt", &sTempDirURL );
794 INetURLObject aTempFileURL( aTempFile.GetURL() );
795 auto pDstMed = std::make_unique<SfxMedium>(
797 StreamMode::STD_READWRITE );
798 bool bAnyError = !xTargetDocShell->DoSaveAs( *pDstMed );
799 // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
800 bAnyError |= (ERRCODE_NONE != xTargetDocShell->GetError());
801 if( bAnyError )
802 SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile.GetURL() );
803 else
804 SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile.GetURL() );
805}
806
807static bool lcl_SaveDoc(
808 const INetURLObject* pFileURL,
809 const std::shared_ptr<const SfxFilter>& pStoreToFilter,
810 const OUString* pStoreToFilterOptions,
811 const uno::Sequence< beans::PropertyValue >* pSaveToFilterData,
812 const bool bIsPDFexport,
813 SfxObjectShell* xObjectShell,
814 SwWrtShell& rWorkShell,
815 OUString * const decodedURL = nullptr )
816{
817 OUString url = pFileURL->GetMainURL( INetURLObject::DecodeMechanism::NONE );
818 if( decodedURL )
819 (*decodedURL) = url;
820
821 SfxMedium* pDstMed = new SfxMedium( url, StreamMode::STD_READWRITE );
822 pDstMed->SetFilter( pStoreToFilter );
823 if( pDstMed->GetItemSet() )
824 {
825 if( pStoreToFilterOptions )
826 pDstMed->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS,
827 *pStoreToFilterOptions));
828 if( pSaveToFilterData->hasElements() )
829 pDstMed->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA,
830 uno::Any(*pSaveToFilterData)));
831 }
832
833 // convert fields to text if we are exporting to PDF.
834 // this prevents a second merge while updating the fields
835 // in SwXTextDocument::getRendererCount()
836 if( bIsPDFexport )
837 rWorkShell.ConvertFieldsToText();
838
839 bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed);
840 // Actually this should be a bool... so in case of email and individual
841 // files, where this is set, we skip the recently used handling
842 bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL );
843 bAnyError |= (ERRCODE_NONE != xObjectShell->GetError());
844 if( bAnyError )
845 {
846 // error message ??
847 ErrorHandler::HandleError( xObjectShell->GetError() );
848 }
849 return !bAnyError;
850}
851
853 const uno::Sequence< beans::PropertyValue >& rInPrintOptions,
854 uno::Sequence< beans::PropertyValue >& rOutPrintOptions)
855{
856 // printing should be done synchronously otherwise the document
857 // might already become invalid during the process
858
859 rOutPrintOptions = { comphelper::makePropertyValue("Wait", true) };
860
861 // copy print options
862 sal_Int32 nIndex = 1;
863 for( const beans::PropertyValue& rOption : rInPrintOptions)
864 {
865 if( rOption.Name == "CopyCount" || rOption.Name == "FileName"
866 || rOption.Name == "Collate" || rOption.Name == "Pages"
867 || rOption.Name == "Wait" || rOption.Name == "PrinterName" )
868 {
869 // add an option
870 rOutPrintOptions.realloc( nIndex + 1 );
871 auto pOutPrintOptions = rOutPrintOptions.getArray();
872 pOutPrintOptions[ nIndex ].Name = rOption.Name;
873 pOutPrintOptions[ nIndex++ ].Value = rOption.Value ;
874 }
875 }
876}
877
879 const uno::Sequence< beans::PropertyValue >& rInSaveFilterDataptions,
880 uno::Sequence< beans::PropertyValue >& rOutSaveFilterDataOptions,
881 const OUString& sPassword)
882{
883 rOutSaveFilterDataOptions
884 = { comphelper::makePropertyValue("EncryptFile", true),
885 comphelper::makePropertyValue("DocumentOpenPassword", sPassword) };
886
887 // copy other options
888 sal_Int32 nIndex = 2;
889 for( const beans::PropertyValue& rOption : rInSaveFilterDataptions)
890 {
891 rOutSaveFilterDataOptions.realloc( nIndex + 1 );
892 auto pOutSaveFilterDataOptions = rOutSaveFilterDataOptions.getArray();
893 pOutSaveFilterDataOptions[ nIndex ].Name = rOption.Name;
894 pOutSaveFilterDataOptions[ nIndex++ ].Value = rOption.Value ;
895 }
896
897}
898
899
901 // input
902 const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
903 // optional input
904 const vcl::Window *pSourceWindow,
905 // optional in and output to swap the DB manager
906 SwDBManager** const ppDBManager,
907 // optional output
908 SwView** const pView, SwWrtShell** const pWrtShell, rtl::Reference<SwDoc>* const pDoc )
909{
910 const SwDoc *pSourceDoc = rSourceWrtShell.GetDoc();
911 SfxObjectShellRef xWorkObjectShell = pSourceDoc->CreateCopy( true, (aType == WorkingDocType::TARGET) );
912 SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell, SFX_INTERFACE_NONE );
913
914 if( pSourceWindow )
915 {
916 // the created window has to be located at the same position as the source window
917 vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow();
918 rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() );
919 }
920
921 SwView* pWorkView = static_cast< SwView* >( pWorkFrame->GetViewShell() );
922 SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr();
923 pWorkWrtShell->GetViewOptions()->SetIdle( false );
924 pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called
925 SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
926 pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
927 pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );
928
929 // import print settings
930 const SwPrintData &rPrintData = pSourceDoc->getIDocumentDeviceAccess().getPrintData();
931 pWorkDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);
932 const JobSetup *pJobSetup = pSourceDoc->getIDocumentDeviceAccess().getJobsetup();
933 if (pJobSetup)
934 pWorkDoc->getIDocumentDeviceAccess().setJobsetup(*pJobSetup);
935
936 if( aType == WorkingDocType::TARGET )
937 {
938 assert( !ppDBManager );
939 pWorkDoc->SetInMailMerge( true );
940 pWorkWrtShell->SetLabelDoc( false );
941 }
942 else
943 {
944 // We have to swap the DBmanager of the new doc, so we also need input
945 assert(ppDBManager && *ppDBManager);
946 SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
947 pWorkDoc->SetDBManager( *ppDBManager );
948 *ppDBManager = pWorkDBManager;
949
950 if( aType == WorkingDocType::SOURCE )
951 {
952 // the GetDBData call constructs the data, if it's missing - kind of const...
953 pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() );
954 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
955 pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
956 pWorkDoc->getIDocumentState().ResetModified();
957 }
958 else
960 }
961
962 if( pView ) *pView = pWorkView;
963 if( pWrtShell ) *pWrtShell = pWorkWrtShell;
964 if( pDoc ) *pDoc = pWorkDoc;
965
966 return xWorkObjectShell.get();
967}
968
970 const SwMergeDescriptor &rMergeDescriptor,
971 const OUString &sFileURL, const OUString &sMailRecipient,
972 const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding,
973 const OUString &sAttachmentMimeType )
974{
976 if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() )
977 pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
978 pMessage->addRecipient( sMailRecipient );
979 pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );
980
981 OUStringBuffer sBody;
982 if( rMergeDescriptor.bSendAsAttachment )
983 {
984 sBody = rMergeDescriptor.sMailBody;
985 mail::MailAttachment aAttach;
986 aAttach.Data = new SwMailTransferable( sFileURL,
987 rMergeDescriptor.sAttachmentName, sAttachmentMimeType );
988 aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
989 pMessage->addAttachment( aAttach );
990 }
991 else
992 {
993 //read in the temporary file and use it as mail body
994 SfxMedium aMedium( sFileURL, StreamMode::READ );
995 SvStream* pInStream = aMedium.GetInStream();
996 assert( pInStream && "no output file created?" );
997 if( !pInStream )
998 return pMessage;
999
1000 pInStream->SetStreamCharSet( sMailEncoding );
1001 OStringBuffer sLine;
1002 while ( pInStream->ReadLine( sLine ) )
1003 {
1004 sBody.append(OStringToOUString( sLine, sMailEncoding ));
1005 sBody.append("\n");
1006 }
1007 }
1008 pMessage->setSubject( rMergeDescriptor.sSubject );
1009 uno::Reference< datatransfer::XTransferable> xBody =
1010 new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType );
1011 pMessage->setBody( xBody );
1012
1013 for( const OUString& sCcRecipient : rMergeDescriptor.aCopiesTo )
1014 pMessage->addCcRecipient( sCcRecipient );
1015 for( const OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo )
1016 pMessage->addBccRecipient( sBccRecipient );
1017
1018 return pMessage;
1019}
1020
1022{
1024
1025public:
1027 : m_rDBManager( rDBManager ) {}
1028
1029 virtual void idle() override {}
1030
1031 virtual void mailDelivered( uno::Reference< mail::XMailMessage> xMessage ) override
1032 {
1033 std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1034 if ( m_rDBManager.m_pImpl->m_xLastMessage == xMessage )
1035 m_rDBManager.m_pImpl->m_xLastMessage.clear();
1036 }
1037
1039 uno::Reference< mail::XMailMessage>, const OUString& ) override
1040 {
1041 std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1043 m_rDBManager.m_pImpl->m_xLastMessage.clear();
1044 xMailDispatcher->stop();
1045 }
1046};
1047
1053 const SwMergeDescriptor& rMergeDescriptor)
1054{
1055 // deconstruct mail merge type for better readability.
1056 // uppercase naming is intentional!
1057 const bool bMT_EMAIL = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
1058 const bool bMT_SHELL = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
1059 const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER;
1060 const bool bMT_FILE = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;
1061
1062 //check if the doc is synchronized and contains at least one linked section
1063 const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1;
1064 const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE );
1065 const bool bIsMergeSilent = IsMergeSilent();
1066
1067 bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile;
1068 OUString sPrefix_ = rMergeDescriptor.sPrefix;
1069 if( bMT_EMAIL )
1070 {
1071 assert( !rMergeDescriptor.bPrefixIsFilename );
1072 assert(!bCheckSingleFile_);
1073 bCheckSingleFile_ = false;
1074 }
1075 else if( bMT_SHELL || bMT_PRINTER )
1076 {
1077 assert(bCheckSingleFile_);
1078 bCheckSingleFile_ = true;
1079 assert(sPrefix_.isEmpty());
1080 sPrefix_.clear();
1081 }
1082 const bool bCreateSingleFile = bCheckSingleFile_;
1083 const OUString sDescriptorPrefix = sPrefix_;
1084
1085 // Setup for dumping debugging documents
1086 static const sal_Int32 nMaxDumpDocs = []() {
1087 if (const char* sEnv = getenv("SW_DEBUG_MAILMERGE_DOCS"))
1088 return OUString(sEnv, strlen(sEnv), osl_getThreadTextEncoding()).toInt32();
1089 else
1090 return sal_Int32(0);
1091 }();
1092
1093 ::rtl::Reference< MailDispatcher > xMailDispatcher;
1095 OUString sMailBodyMimeType;
1096 rtl_TextEncoding sMailEncoding = ::osl_getThreadTextEncoding();
1097
1098 uno::Reference< beans::XPropertySet > xColumnProp;
1099 uno::Reference< beans::XPropertySet > xPasswordColumnProp;
1100
1101 // Check for (mandatory) email or (optional) filename column
1102 SwDBFormatData aColumnDBFormat;
1103 bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty();
1104 bool bPasswordColumnName = !rMergeDescriptor.sDBPasswordColumn.isEmpty();
1105
1106 if( ! bColumnName )
1107 {
1108 if( bMT_EMAIL )
1109 return false;
1110 }
1111 else
1112 {
1113 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
1114 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1115 if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) )
1116 return false;
1117 uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn );
1118 aCol >>= xColumnProp;
1119
1120 if(bPasswordColumnName)
1121 {
1122 aCol = xCols->getByName( rMergeDescriptor.sDBPasswordColumn );
1123 aCol >>= xPasswordColumnProp;
1124 }
1125
1126 aColumnDBFormat.xFormatter = m_pImpl->pMergeData->xFormatter;
1127 aColumnDBFormat.aNullDate = m_pImpl->pMergeData->aNullDate;
1128
1129 if( bMT_EMAIL )
1130 {
1131 // Reset internal mail accounting data
1132 m_pImpl->m_xLastMessage.clear();
1133
1134 xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) );
1135 xMailListener = new MailDispatcherListener_Impl( *this );
1136 xMailDispatcher->addListener( xMailListener );
1137 if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
1138 {
1139 sMailBodyMimeType = "text/html; charset=utf-8";
1140 sMailEncoding = RTL_TEXTENCODING_UTF8;
1141 }
1142 else
1143 sMailBodyMimeType = "text/plain; charset=UTF-8; format=flowed";
1144 }
1145 }
1146
1147 SwDocShell *pSourceDocSh = pSourceShell->GetView().GetDocShell();
1148
1149 // setup the output format
1150 std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
1152 SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
1153 const OUString* pStoreToFilterOptions = nullptr;
1154
1155 // if a save_to filter is set then use it - otherwise use the default
1156 if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
1157 {
1158 OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt");
1159 pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
1160 }
1161 else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
1162 {
1163 std::shared_ptr<const SfxFilter> pFilter =
1164 pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
1165 if(pFilter)
1166 {
1167 pStoreToFilter = pFilter;
1168 if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
1169 pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
1170 }
1171 }
1172 const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export";
1173 const bool bIsMultiFile = bMT_FILE && !bCreateSingleFile;
1174
1176
1177 // in case of creating a single resulting file this has to be created here
1178 SwView* pTargetView = rMergeDescriptor.pMailMergeConfigItem ?
1179 rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
1180 SwWrtShell* pTargetShell = nullptr;
1181 SfxObjectShellRef xTargetDocShell;
1182 rtl::Reference<SwDoc> pTargetDoc;
1183
1184 std::unique_ptr< utl::TempFileNamed > aTempFile;
1185 sal_uInt16 nStartingPageNo = 0;
1186
1187 std::shared_ptr<weld::GenericDialogController> xProgressDlg;
1188
1189 try
1190 {
1191 vcl::Window *pSourceWindow = nullptr;
1192 if( !bIsMergeSilent )
1193 {
1194 // construct the process dialog
1195 pSourceWindow = &pSourceShell->GetView().GetEditWin();
1196 if (!bMT_PRINTER)
1197 xProgressDlg = std::make_shared<CreateMonitor>(pSourceWindow->GetFrameWeld());
1198 else
1199 {
1200 xProgressDlg = std::make_shared<PrintMonitor>(pSourceWindow->GetFrameWeld());
1201 static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
1202 pSourceDocSh->GetTitle(22));
1203 }
1204 weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
1205 if (nResult == RET_CANCEL)
1206 MergeCancel();
1207 xProgressDlg.reset();
1208 });
1209
1211 }
1212
1213 if( bCreateSingleFile && !pTargetView )
1214 {
1215 // create a target docshell to put the merged document into
1216 xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
1217 *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
1218 nullptr, &pTargetView, &pTargetShell, &pTargetDoc );
1219
1220 if (nMaxDumpDocs)
1221 lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1222 }
1223 else if( pTargetView )
1224 {
1225 pTargetShell = pTargetView->GetWrtShellPtr();
1226 pTargetDoc = pTargetShell->GetDoc();
1227 xTargetDocShell = pTargetView->GetDocShell();
1228 }
1229
1230 if( bCreateSingleFile )
1231 {
1232 // determine the page style and number used at the start of the source document
1233 pSourceShell->SttEndDoc(true);
1234 nStartingPageNo = pSourceShell->GetVirtPageNum();
1235 }
1236
1237 // Progress, to prohibit KeyInputs
1238 SfxProgress aProgress(pSourceDocSh, OUString(), 1);
1239
1240 // lock all dispatchers
1241 SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1242 while (pViewFrame)
1243 {
1244 pViewFrame->GetDispatcher()->Lock(true);
1245 pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1246 }
1247
1248 sal_Int32 nDocNo = 1;
1249
1250 // For single file mode, the number of pages in the target document so far, which is used
1251 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1252 // from the target doc would require repeated layouts of the doc, which is expensive, but
1253 // it can be manually computed from the source documents (for which we do layouts, so the page
1254 // count is known, and there is a blank page between each of them in the target document).
1255 int targetDocPageCount = 0;
1256
1257 if( !bIsMergeSilent && !bMT_PRINTER )
1258 {
1259 sal_Int32 nRecordCount = 1;
1260 lcl_getCountFromResultSet( nRecordCount, m_pImpl->pMergeData.get() );
1261
1262 // Synchronized docs don't auto-advance the record set, but there is a
1263 // "security" check, which will always advance the record set, if there
1264 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1265 sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
1267 if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
1268 --nRecordPerDoc;
1269 assert( nRecordPerDoc > 0 );
1270
1271 sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
1272 if ( 0 != nRecordCount % nRecordPerDoc )
1273 nMaxDocs += 1;
1274 static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
1275 }
1276
1277 sal_Int32 nStartRow, nEndRow;
1278 bool bFreezedLayouts = false;
1279 // to collect temporary email files
1280 std::vector< OUString> aFilesToRemove;
1281
1282 // The SfxObjectShell will be closed explicitly later but
1283 // it is more safe to use SfxObjectShellLock here
1284 SfxObjectShellLock xWorkDocSh;
1285 SwView* pWorkView = nullptr;
1286 rtl::Reference<SwDoc> pWorkDoc;
1287 SwDBManager* pWorkDocOrigDBManager = nullptr;
1288 SwWrtShell* pWorkShell = nullptr;
1289 bool bWorkDocInitialized = false;
1290
1291 do
1292 {
1293 nStartRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1294
1295 OUString sColumnData;
1296
1297 // Read the indicated data column, which should contain a valid mail
1298 // address or an optional file name
1299 if( bMT_EMAIL || bColumnName )
1300 {
1301 sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
1302 }
1303
1304 // create a new temporary file name - only done once in case of bCreateSingleFile
1305 if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
1306 {
1307 OUString sPrefix = sDescriptorPrefix;
1308 OUString sLeading;
1309
1310 //#i97667# if the name is from a database field then it will be used _as is_
1311 if( bColumnName && !bMT_EMAIL )
1312 {
1313 if (!sColumnData.isEmpty())
1314 sLeading = sColumnData;
1315 else
1316 sLeading = "_";
1317 }
1318 else
1319 {
1320 INetURLObject aEntry( sPrefix );
1321 sLeading = aEntry.GetBase();
1322 aEntry.removeSegment();
1324 }
1325
1326 OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
1327 aTempFile.reset( new utl::TempFileNamed(sLeading, sColumnData.isEmpty(), sExt, &sPrefix, true) );
1328 if( !aTempFile->IsValid() )
1329 {
1332 }
1333 }
1334
1335 uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions( rMergeDescriptor.aSaveToFilterData );
1336
1337 if( bMT_EMAIL || bPasswordColumnName )
1338 {
1339 OUString sPasswordColumnData = GetDBField( xPasswordColumnProp, aColumnDBFormat );
1340 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor.aSaveToFilterData, aSaveToFilterDataOptions, sPasswordColumnData );
1341 }
1342
1343 if( IsMergeOk() )
1344 {
1345 std::unique_ptr< INetURLObject > aTempFileURL;
1346 if( bNeedsTempFiles )
1347 aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
1348 if( !bIsMergeSilent ) {
1349 if( !bMT_PRINTER )
1350 static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
1351 else {
1352 PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
1353 pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
1354 ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
1355 OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number( nDocNo );
1356 pPrintMonDlg->m_xPrintInfo->set_label(sStat);
1357 }
1358 //TODO xProgressDlg->queue_draw();
1359 }
1360
1362
1363 // Create a copy of the source document and work with that one instead of the source.
1364 // If we're not in the single file mode (which requires modifying the document for the merging),
1365 // it is enough to do this just once. Currently PDF also has to be treated special.
1366 if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport || bIsMultiFile )
1367 {
1368 assert( !xWorkDocSh.Is());
1369 pWorkDocOrigDBManager = this;
1370 xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
1371 *pSourceShell, nullptr, &pWorkDocOrigDBManager,
1372 &pWorkView, &pWorkShell, &pWorkDoc );
1373 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1374 lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1375
1376 // #i69458# lock fields to prevent access to the result set while calculating layout
1377 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1378 // ExpFields update during printing, generation of preview, etc.
1379 pWorkShell->LockExpFields();
1380 pWorkShell->CalcLayout();
1381 // tdf#121168: Now force correct page descriptions applied to page frames. Without
1382 // this, e.g., page frames starting with sections could have page descriptions set
1383 // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1384 pWorkShell->GetViewOptions()->SetIdle(true);
1385 for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
1386 {
1387 aLayout->FreezeLayout(false);
1388 aLayout->AllCheckPageDescs();
1389 }
1390 }
1391
1392 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);
1393
1394 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1395 // database cursor movement on any other fields update, for example during
1396 // print preview and other operations
1397 if ( pWorkShell->IsExpFieldsLocked() )
1398 pWorkShell->UnlockExpFields();
1399 pWorkShell->SwViewShell::UpdateFields();
1400 pWorkShell->LockExpFields();
1401
1402 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);
1403
1404 // also emit MailMergeEvent on XInterface if possible
1405 const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
1406 if(pEvtSrc)
1407 {
1409 const_cast<SwXMailMerge*>(pEvtSrc) );
1410 text::MailMergeEvent aEvt( static_cast<text::XMailMergeBroadcaster*>(xRef.get()), xWorkDocSh->GetModel() );
1412 xRef->LaunchMailMergeEvent( aEvt );
1413 }
1414
1415 // working copy is merged - prepare final steps depending on merge options
1416
1417 if( bCreateSingleFile )
1418 {
1419 assert( pTargetShell && "no target shell available!" );
1420
1421 // prepare working copy and target to append
1422
1423 pWorkDoc->RemoveInvisibleContent();
1424 // remove of invisible content has influence on page count and so on fields for page count,
1425 // therefore layout has to be updated before fields are converted to text
1426 pWorkShell->CalcLayout();
1427 pWorkShell->ConvertFieldsToText();
1428 pWorkShell->SetNumberingRestart();
1429 if( bSynchronizedDoc )
1430 {
1431 lcl_RemoveSectionLinks( *pWorkShell );
1432 }
1433
1434 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1435 lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1436
1437 // append the working document to the target document
1438 if( targetDocPageCount % 2 == 1 )
1439 ++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
1440 SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
1441 nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
1442 targetDocPageCount += pWorkShell->GetPageCnt();
1443
1444 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1445 lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1446
1447 if (bMT_SHELL)
1448 {
1449 SwDocMergeInfo aMergeInfo;
1450 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1451 aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
1453 ::sw::mark::InsertMode::New);
1454 aMergeInfo.nDBRow = nStartRow;
1455 rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
1456 }
1457 }
1458 else
1459 {
1460 assert( bNeedsTempFiles );
1461 assert( pWorkShell->IsExpFieldsLocked() );
1462
1463 if (bIsMultiFile && pWorkDoc->HasInvisibleContent())
1464 {
1465 pWorkDoc->RemoveInvisibleContent();
1466 pWorkShell->CalcLayout();
1467 pWorkShell->ConvertFieldsToText();
1468 }
1469
1470 // fields are locked, so it's fine to
1471 // restore the old / empty DB manager for save
1472 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1473
1474 // save merged document
1475 OUString sFileURL;
1476 if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
1477 &aSaveToFilterDataOptions, bIsPDFexport,
1478 xWorkDocSh, *pWorkShell, &sFileURL ) )
1479 {
1481 }
1482
1483 // back to the MM DB manager
1484 pWorkDoc->SetDBManager( this );
1485
1486 if( bMT_EMAIL && !IsMergeError() )
1487 {
1488 // schedule file for later removal
1489 aFilesToRemove.push_back( sFileURL );
1490
1491 if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
1492 {
1493 OSL_FAIL("invalid e-Mail address in database column");
1494 }
1495 else
1496 {
1497 uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
1498 rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
1499 sMailEncoding, pStoreToFilter->GetMimeType() );
1500 if( xMessage.is() )
1501 {
1502 std::unique_lock aGuard( m_pImpl->m_aAllEmailSendMutex );
1503 m_pImpl->m_xLastMessage.set( xMessage );
1504 xMailDispatcher->enqueueMailMessage( xMessage );
1505 if( !xMailDispatcher->isStarted() )
1506 xMailDispatcher->start();
1507 }
1508 }
1509 }
1510 }
1511 if( bCreateSingleFile || bIsPDFexport || bIsMultiFile)
1512 {
1513 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1514 pWorkDoc.clear();
1515 xWorkDocSh->DoClose();
1516 xWorkDocSh = nullptr;
1517 }
1518 }
1519
1520 bWorkDocInitialized = true;
1521 nDocNo++;
1522 nEndRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1523
1524 // Freeze the layouts of the target document after the first inserted
1525 // sub-document, to get the correct PageDesc.
1526 if(!bFreezedLayouts && bCreateSingleFile)
1527 {
1528 for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1529 aLayout->FreezeLayout(true);
1530 bFreezedLayouts = true;
1531 }
1532 } while( IsMergeOk() &&
1533 ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1534
1535 if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
1536 {
1537 // Unlock document fields after merge complete
1538 pWorkView->GetWrtShell().UnlockExpFields();
1539 }
1540
1541 if( !bCreateSingleFile )
1542 {
1543 if( bMT_PRINTER )
1545 if( !bIsPDFexport )
1546 {
1547 if (pWorkDoc)
1548 pWorkDoc->SetDBManager(pWorkDocOrigDBManager);
1549 if (xWorkDocSh.Is())
1550 xWorkDocSh->DoClose();
1551 }
1552 }
1553 else if( IsMergeOk() ) // && bCreateSingleFile
1554 {
1556
1557 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1558 // unique fly names, do it here once.
1559 pTargetDoc->SetInMailMerge(false);
1560 pTargetDoc->SetAllUniqueFlyNames();
1561
1562 // Unfreeze target document layouts and correct all PageDescs.
1563 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1564 pTargetShell->CalcLayout();
1565 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1566 pTargetShell->GetViewOptions()->SetIdle( true );
1567 pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
1568 for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1569 {
1570 aLayout->FreezeLayout(false);
1571 aLayout->AllCheckPageDescs();
1572 }
1573
1575
1576 if( IsMergeOk() && bMT_FILE )
1577 {
1578 // save merged document
1579 assert( aTempFile );
1580 INetURLObject aTempFileURL;
1581 if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
1582 aTempFileURL.SetURL( aTempFile->GetURL() );
1583 else
1584 {
1585 aTempFileURL.SetURL(sDescriptorPrefix);
1586 // remove the unneeded temporary file
1587 aTempFile->EnableKillingFile();
1588 }
1589 if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
1590 pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
1591 bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
1592 {
1594 }
1595 }
1596 else if( IsMergeOk() && bMT_PRINTER )
1597 {
1598 // print the target document
1599 uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
1600 lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
1601 pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
1602 }
1603 }
1604
1605 // we also show canceled documents, as long as there was no error
1606 if( !IsMergeError() && bMT_SHELL )
1607 // leave docshell available for caller (e.g. MM wizard)
1608 rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
1609 else if( xTargetDocShell.is() )
1610 xTargetDocShell->DoClose();
1611
1613
1614 if (xProgressDlg)
1615 {
1616 xProgressDlg->response(RET_OK);
1617 }
1618
1619 // unlock all dispatchers
1620 pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1621 while (pViewFrame)
1622 {
1623 pViewFrame->GetDispatcher()->Lock(false);
1624 pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1625 }
1626
1627 SW_MOD()->SetView(&pSourceShell->GetView());
1628
1629 if( xMailDispatcher.is() )
1630 {
1631 if( IsMergeOk() )
1632 {
1633 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1634 // if the main loop has been made thread-safe.
1635 AutoTimer aEmailDispatcherPollTimer("sw::SwDBManager aEmailDispatcherPollTimer");
1636 aEmailDispatcherPollTimer.SetTimeout( 500 );
1637 aEmailDispatcherPollTimer.Start();
1638 while( IsMergeOk() && m_pImpl->m_xLastMessage.is() && !Application::IsQuit())
1640 aEmailDispatcherPollTimer.Stop();
1641 }
1642 xMailDispatcher->stop();
1643 xMailDispatcher->shutdown();
1644 }
1645
1646 // remove the temporary files
1647 // has to be done after xMailDispatcher is finished, as mails may be
1648 // delivered as message attachments!
1649 for( const OUString &sFileURL : aFilesToRemove )
1650 SWUnoHelper::UCB_DeleteFile( sFileURL );
1651 }
1652 catch (const uno::Exception&)
1653 {
1654 if (xProgressDlg)
1655 {
1656 xProgressDlg->response(RET_CANCEL);
1657 }
1658 }
1659
1660 return !IsMergeError();
1661}
1662
1664{
1667}
1668
1669// determine the column's Numberformat and transfer to the forwarded Formatter,
1670// if applicable.
1672 const OUString& rTableName,
1673 const OUString& rColNm,
1674 SvNumberFormatter* pNFormatr,
1675 LanguageType nLanguage )
1676{
1677 sal_uLong nRet = 0;
1678 if(pNFormatr)
1679 {
1680 uno::Reference< sdbc::XDataSource> xSource;
1681 uno::Reference< sdbc::XConnection> xConnection;
1682 bool bUseMergeData = false;
1683 uno::Reference< sdbcx::XColumnsSupplier> xColsSupp;
1684 bool bDisposeConnection = false;
1685 if(m_pImpl->pMergeData &&
1686 ((m_pImpl->pMergeData->sDataSource == rDBName && m_pImpl->pMergeData->sCommand == rTableName) ||
1687 (rDBName.isEmpty() && rTableName.isEmpty())))
1688 {
1689 xConnection = m_pImpl->pMergeData->xConnection;
1690 xSource = SwDBManager::getDataSourceAsParent(xConnection,rDBName);
1691 bUseMergeData = true;
1692 xColsSupp.set(m_pImpl->pMergeData->xResultSet, css::uno::UNO_QUERY);
1693 }
1694 if(!xConnection.is())
1695 {
1697 aData.sDataSource = rDBName;
1698 aData.sCommand = rTableName;
1699 aData.nCommandType = -1;
1700 SwDSParam* pParam = FindDSData(aData, false);
1701 if(pParam && pParam->xConnection.is())
1702 {
1703 xConnection = pParam->xConnection;
1704 xColsSupp.set(pParam->xResultSet, css::uno::UNO_QUERY);
1705 }
1706 else
1707 {
1708 xConnection = RegisterConnection( rDBName );
1709 bDisposeConnection = true;
1710 }
1711 if(bUseMergeData)
1712 m_pImpl->pMergeData->xConnection = xConnection;
1713 }
1714 bool bDispose = !xColsSupp.is();
1715 if(bDispose)
1716 {
1717 xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1718 }
1719 if(xColsSupp.is())
1720 {
1721 uno::Reference<container::XNameAccess> xCols;
1722 try
1723 {
1724 xCols = xColsSupp->getColumns();
1725 }
1726 catch (const uno::Exception&)
1727 {
1728 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1729 }
1730 if(!xCols.is() || !xCols->hasByName(rColNm))
1731 return nRet;
1732 uno::Any aCol = xCols->getByName(rColNm);
1733 uno::Reference< beans::XPropertySet > xColumn;
1734 aCol >>= xColumn;
1735 nRet = GetColumnFormat(xSource, xConnection, xColumn, pNFormatr, nLanguage);
1736 if(bDispose)
1737 {
1738 ::comphelper::disposeComponent( xColsSupp );
1739 }
1740 if(bDisposeConnection)
1741 {
1742 ::comphelper::disposeComponent( xConnection );
1743 }
1744 }
1745 else
1747 }
1748 return nRet;
1749}
1750
1751sal_uLong SwDBManager::GetColumnFormat( uno::Reference< sdbc::XDataSource> const & xSource_in,
1752 uno::Reference< sdbc::XConnection> const & xConnection,
1753 uno::Reference< beans::XPropertySet> const & xColumn,
1754 SvNumberFormatter* pNFormatr,
1755 LanguageType nLanguage )
1756{
1757 auto xSource = xSource_in;
1758
1759 // set the NumberFormat in the doc if applicable
1760 sal_uLong nRet = 0;
1761
1762 if(!xSource.is())
1763 {
1764 uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
1765 if ( xChild.is() )
1766 xSource.set(xChild->getParent(), uno::UNO_QUERY);
1767 }
1768 if(xSource.is() && xConnection.is() && xColumn.is() && pNFormatr)
1769 {
1771 uno::Reference< util::XNumberFormats > xDocNumberFormats = pNumFormat->getNumberFormats();
1772 uno::Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, uno::UNO_QUERY);
1773
1774 css::lang::Locale aLocale( LanguageTag( nLanguage ).getLocale());
1775
1776 //get the number formatter of the data source
1777 uno::Reference<beans::XPropertySet> xSourceProps(xSource, uno::UNO_QUERY);
1778 uno::Reference< util::XNumberFormats > xNumberFormats;
1779 if(xSourceProps.is())
1780 {
1781 uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
1782 if(aFormats.hasValue())
1783 {
1784 uno::Reference<util::XNumberFormatsSupplier> xSuppl;
1785 aFormats >>= xSuppl;
1786 if(xSuppl.is())
1787 {
1788 xNumberFormats = xSuppl->getNumberFormats();
1789 }
1790 }
1791 }
1792 bool bUseDefault = true;
1793 try
1794 {
1795 uno::Any aFormatKey = xColumn->getPropertyValue("FormatKey");
1796 if(aFormatKey.hasValue())
1797 {
1798 sal_Int32 nFormat = 0;
1799 aFormatKey >>= nFormat;
1800 if(xNumberFormats.is())
1801 {
1802 try
1803 {
1804 uno::Reference<beans::XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat );
1805 uno::Any aFormatString = xNumProps->getPropertyValue("FormatString");
1806 uno::Any aLocaleVal = xNumProps->getPropertyValue("Locale");
1807 OUString sFormat;
1808 aFormatString >>= sFormat;
1809 lang::Locale aLoc;
1810 aLocaleVal >>= aLoc;
1811 nFormat = xDocNumberFormats->queryKey( sFormat, aLoc, false );
1812 if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFormat))
1813 nFormat = xDocNumberFormats->addNew( sFormat, aLoc );
1814 nRet = nFormat;
1815 bUseDefault = false;
1816 }
1817 catch (const uno::Exception&)
1818 {
1819 TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1820 }
1821 }
1822 }
1823 }
1824 catch(const uno::Exception&)
1825 {
1826 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1827 }
1828 if(bUseDefault)
1829 nRet = dbtools::getDefaultNumberFormat(xColumn, xDocNumberFormatTypes, aLocale);
1830 }
1831 return nRet;
1832}
1833
1834sal_Int32 SwDBManager::GetColumnType( const OUString& rDBName,
1835 const OUString& rTableName,
1836 const OUString& rColNm )
1837{
1838 sal_Int32 nRet = sdbc::DataType::SQLNULL;
1840 aData.sDataSource = rDBName;
1841 aData.sCommand = rTableName;
1842 aData.nCommandType = -1;
1843 SwDSParam* pParam = FindDSData(aData, false);
1844 uno::Reference< sdbc::XConnection> xConnection;
1845 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp;
1846 bool bDispose = false;
1847 if(pParam && pParam->xConnection.is())
1848 {
1849 xConnection = pParam->xConnection;
1850 xColsSupp.set( pParam->xResultSet, uno::UNO_QUERY );
1851 }
1852 else
1853 {
1854 xConnection = RegisterConnection( rDBName );
1855 }
1856 if( !xColsSupp.is() )
1857 {
1858 xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1859 bDispose = true;
1860 }
1861 if(xColsSupp.is())
1862 {
1863 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1864 if(xCols->hasByName(rColNm))
1865 {
1866 uno::Any aCol = xCols->getByName(rColNm);
1867 uno::Reference<beans::XPropertySet> xCol;
1868 aCol >>= xCol;
1869 uno::Any aType = xCol->getPropertyValue("Type");
1870 aType >>= nRet;
1871 }
1872 if(bDispose)
1873 ::comphelper::disposeComponent( xColsSupp );
1874 }
1875 return nRet;
1876}
1877
1878uno::Reference< sdbc::XConnection> SwDBManager::GetConnection(const OUString& rDataSource,
1879 uno::Reference<sdbc::XDataSource>& rxSource, const SwView *pView)
1880{
1881 uno::Reference< sdbc::XConnection> xConnection;
1882 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1883 try
1884 {
1885 uno::Reference<sdb::XCompletedConnection> xComplConnection(dbtools::getDataSource(rDataSource, xContext), uno::UNO_QUERY);
1886 if ( xComplConnection.is() )
1887 {
1888 rxSource.set(xComplConnection, uno::UNO_QUERY);
1889 weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
1890 uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(xContext, pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
1891 xConnection = xComplConnection->connectWithCompletion( xHandler );
1892 }
1893 }
1894 catch(const uno::Exception&)
1895 {
1896 }
1897
1898 return xConnection;
1899}
1900
1901uno::Reference< sdbcx::XColumnsSupplier> SwDBManager::GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection,
1902 const OUString& rTableOrQuery,
1903 SwDBSelect eTableOrQuery)
1904{
1905 uno::Reference< sdbcx::XColumnsSupplier> xRet;
1906 try
1907 {
1908 if(eTableOrQuery == SwDBSelect::UNKNOWN)
1909 {
1910 //search for a table with the given command name
1911 uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
1912 if(xTSupplier.is())
1913 {
1914 uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
1915 eTableOrQuery = xTables->hasByName(rTableOrQuery) ?
1917 }
1918 }
1919 sal_Int32 nCommandType = SwDBSelect::TABLE == eTableOrQuery ?
1920 sdb::CommandType::TABLE : sdb::CommandType::QUERY;
1921 uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
1922 uno::Reference<sdbc::XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY);
1923
1924 OUString sDataSource;
1925 uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection, sDataSource);
1926 uno::Reference<beans::XPropertySet> xSourceProperties(xSource, uno::UNO_QUERY);
1927 if(xSourceProperties.is())
1928 {
1929 xSourceProperties->getPropertyValue("Name") >>= sDataSource;
1930 }
1931
1932 uno::Reference<beans::XPropertySet> xRowProperties(xRowSet, uno::UNO_QUERY);
1933 xRowProperties->setPropertyValue("DataSourceName", uno::Any(sDataSource));
1934 xRowProperties->setPropertyValue("Command", uno::Any(rTableOrQuery));
1935 xRowProperties->setPropertyValue("CommandType", uno::Any(nCommandType));
1936 xRowProperties->setPropertyValue("FetchSize", uno::Any(sal_Int32(10)));
1937 xRowProperties->setPropertyValue("ActiveConnection", uno::Any(xConnection));
1938 xRowSet->execute();
1939 xRet.set( xRowSet, uno::UNO_QUERY );
1940 }
1941 catch (const uno::Exception&)
1942 {
1943 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1944 }
1945
1946 return xRet;
1947}
1948
1949OUString SwDBManager::GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps,
1950 const SwDBFormatData& rDBFormatData,
1951 double* pNumber)
1952{
1953 uno::Reference< sdb::XColumn > xColumn(xColumnProps, uno::UNO_QUERY);
1954 OUString sRet;
1955 assert( xColumn.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1956 if(!xColumn.is())
1957 return sRet;
1958
1959 uno::Any aType = xColumnProps->getPropertyValue("Type");
1960 sal_Int32 eDataType = sdbc::DataType::SQLNULL;
1961 aType >>= eDataType;
1962 switch(eDataType)
1963 {
1964 case sdbc::DataType::CHAR:
1965 case sdbc::DataType::VARCHAR:
1966 case sdbc::DataType::LONGVARCHAR:
1967 try
1968 {
1969 sRet = xColumn->getString();
1970 sRet = sRet.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1971 }
1972 catch(const sdbc::SQLException&)
1973 {
1974 }
1975 break;
1976 case sdbc::DataType::BIT:
1977 case sdbc::DataType::BOOLEAN:
1978 case sdbc::DataType::TINYINT:
1979 case sdbc::DataType::SMALLINT:
1980 case sdbc::DataType::INTEGER:
1981 case sdbc::DataType::BIGINT:
1982 case sdbc::DataType::FLOAT:
1983 case sdbc::DataType::REAL:
1984 case sdbc::DataType::DOUBLE:
1985 case sdbc::DataType::NUMERIC:
1986 case sdbc::DataType::DECIMAL:
1987 case sdbc::DataType::DATE:
1988 case sdbc::DataType::TIME:
1989 case sdbc::DataType::TIMESTAMP:
1990 {
1991
1992 try
1993 {
1995 xColumnProps,
1996 rDBFormatData.xFormatter,
1997 rDBFormatData.aLocale,
1998 rDBFormatData.aNullDate);
1999 if (pNumber)
2000 {
2001 double fVal = xColumn->getDouble();
2002 if(!xColumn->wasNull())
2003 {
2004 *pNumber = fVal;
2005 }
2006 }
2007 }
2008 catch (const uno::Exception&)
2009 {
2010 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
2011 }
2012
2013 }
2014 break;
2015 }
2016
2017 return sRet;
2018}
2019
2020// checks if a desired data source table or query is open
2021bool SwDBManager::IsDataSourceOpen(const OUString& rDataSource,
2022 const OUString& rTableOrQuery, bool bMergeShell)
2023{
2024 if(m_pImpl->pMergeData)
2025 {
2026 return ((rDataSource == m_pImpl->pMergeData->sDataSource
2027 && rTableOrQuery == m_pImpl->pMergeData->sCommand)
2028 || (rDataSource.isEmpty() && rTableOrQuery.isEmpty()))
2029 && m_pImpl->pMergeData->xResultSet.is();
2030 }
2031 else if(!bMergeShell)
2032 {
2034 aData.sDataSource = rDataSource;
2035 aData.sCommand = rTableOrQuery;
2036 aData.nCommandType = -1;
2037 SwDSParam* pFound = FindDSData(aData, false);
2038 return (pFound && pFound->xResultSet.is());
2039 }
2040 return false;
2041}
2042
2043// read column data at a specified position
2044bool SwDBManager::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName,
2045 const OUString& rColumnName, sal_uInt32 nAbsRecordId,
2046 LanguageType nLanguage,
2047 OUString& rResult, double* pNumber)
2048{
2049 bool bRet = false;
2050 SwDSParam* pFound = nullptr;
2051 //check if it's the merge data source
2052 if(m_pImpl->pMergeData &&
2053 rSourceName == m_pImpl->pMergeData->sDataSource &&
2054 rTableName == m_pImpl->pMergeData->sCommand)
2055 {
2056 pFound = m_pImpl->pMergeData.get();
2057 }
2058 else
2059 {
2061 aData.sDataSource = rSourceName;
2062 aData.sCommand = rTableName;
2063 aData.nCommandType = -1;
2064 pFound = FindDSData(aData, false);
2065 }
2066 if (!pFound)
2067 return false;
2068 //check validity of supplied record Id
2069 if(pFound->aSelection.hasElements())
2070 {
2071 //the destination has to be an element of the selection
2072 bool bFound = std::any_of(std::cbegin(pFound->aSelection), std::cend(pFound->aSelection),
2073 [nAbsRecordId](const uno::Any& rSelection) {
2074 sal_Int32 nSelection = 0;
2075 rSelection >>= nSelection;
2076 return nSelection == static_cast<sal_Int32>(nAbsRecordId);
2077 });
2078 if(!bFound)
2079 return false;
2080 }
2081 if( pFound->HasValidRecord() )
2082 {
2083 sal_Int32 nOldRow = 0;
2084 try
2085 {
2086 nOldRow = pFound->xResultSet->getRow();
2087 }
2088 catch(const uno::Exception&)
2089 {
2090 return false;
2091 }
2092 //position to the desired index
2093 bool bMove = true;
2094 if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2095 bMove = lcl_MoveAbsolute(pFound, nAbsRecordId);
2096 if(bMove)
2097 bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber);
2098 if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2099 lcl_MoveAbsolute(pFound, nOldRow);
2100 }
2101 return bRet;
2102}
2103
2104// reads the column data at the current position
2105bool SwDBManager::GetMergeColumnCnt(const OUString& rColumnName, LanguageType nLanguage,
2106 OUString &rResult, double *pNumber)
2107{
2108 if( !IsValidMergeRecord() )
2109 {
2110 rResult.clear();
2111 return false;
2112 }
2113
2114 bool bRet = lcl_GetColumnCnt(m_pImpl->pMergeData.get(), rColumnName, nLanguage, rResult, pNumber);
2115 return bRet;
2116}
2117
2119{
2120 assert( m_pImpl->pMergeData && m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2121 return lcl_ToNextRecord( m_pImpl->pMergeData.get() );
2122}
2123
2125 LanguageType nLanguage, SwCalc &rCalc )
2126{
2127 if( !IsValidMergeRecord() )
2128 return false;
2129
2130 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
2131 if( !xColsSupp.is() )
2132 return false;
2133
2134 {
2135 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
2136 const uno::Sequence<OUString> aColNames = xCols->getElementNames();
2137 OUString aString;
2138
2139 // add the "record number" variable, as SwCalc::VarLook would.
2140 rCalc.VarChange( GetAppCharClass().lowercase(
2142
2143 for( const OUString& rColName : aColNames )
2144 {
2145 // get the column type
2146 sal_Int32 nColumnType = sdbc::DataType::SQLNULL;
2147 uno::Any aCol = xCols->getByName( rColName );
2148 uno::Reference<beans::XPropertySet> xColumnProps;
2149 aCol >>= xColumnProps;
2150 uno::Any aType = xColumnProps->getPropertyValue( "Type" );
2151 aType >>= nColumnType;
2152 double aNumber = DBL_MAX;
2153
2154 lcl_GetColumnCnt( m_pImpl->pMergeData.get(), xColumnProps, nLanguage, aString, &aNumber );
2155
2156 sal_uInt32 nFormat = GetColumnFormat( m_pImpl->pMergeData->sDataSource,
2157 m_pImpl->pMergeData->sCommand,
2158 rColName, pDocFormatter, nLanguage );
2159 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2160 bool colIsNumber = aNumber != DBL_MAX;
2161 bool bValidValue = SwDBField::FormatValue( pDocFormatter, aString, nFormat,
2162 aNumber, nColumnType );
2163 if( colIsNumber )
2164 {
2165 if( bValidValue )
2166 {
2167 SwSbxValue aValue;
2168 aValue.PutDouble( aNumber );
2169 aValue.SetDBvalue( true );
2170 SAL_INFO( "sw.ui", "'" << rColName << "': " << aNumber << " / " << aString );
2171 rCalc.VarChange( rColName, aValue );
2172 }
2173 }
2174 else
2175 {
2176 SwSbxValue aValue;
2177 aValue.PutString( aString );
2178 aValue.SetDBvalue( true );
2179 SAL_INFO( "sw.ui", "'" << rColName << "': " << aString );
2180 rCalc.VarChange( rColName, aValue );
2181 }
2182 }
2183 }
2184
2185 return true;
2186}
2187
2189 const OUString& rDataSource, const OUString& rCommand)
2190{
2191 SwDSParam* pFound = nullptr;
2192 if(m_pImpl->pMergeData &&
2193 rDataSource == m_pImpl->pMergeData->sDataSource &&
2194 rCommand == m_pImpl->pMergeData->sCommand)
2195 {
2196 pFound = m_pImpl->pMergeData.get();
2197 }
2198 else
2199 {
2201 aData.sDataSource = rDataSource;
2202 aData.sCommand = rCommand;
2203 aData.nCommandType = -1;
2204 pFound = FindDSData(aData, false);
2205 }
2206 lcl_ToNextRecord( pFound );
2207}
2208
2209static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action )
2210{
2211 bool bRet = true;
2212
2213 assert( SwDBNextRecord::NEXT == action ||
2214 (SwDBNextRecord::FIRST == action && pParam) );
2215 if( nullptr == pParam )
2216 return false;
2217
2218 if( action == SwDBNextRecord::FIRST )
2219 {
2220 pParam->nSelectionIndex = 0;
2221 pParam->bEndOfDB = false;
2222 }
2223
2224 if( !pParam->HasValidRecord() )
2225 return false;
2226
2227 try
2228 {
2229 if( pParam->aSelection.hasElements() )
2230 {
2231 if( pParam->nSelectionIndex >= pParam->aSelection.getLength() )
2232 pParam->bEndOfDB = true;
2233 else
2234 {
2235 sal_Int32 nPos = 0;
2236 pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
2237 pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
2238 }
2239 }
2240 else if( action == SwDBNextRecord::FIRST )
2241 {
2242 pParam->bEndOfDB = !pParam->xResultSet->first();
2243 }
2244 else
2245 {
2246 sal_Int32 nBefore = pParam->xResultSet->getRow();
2247 pParam->bEndOfDB = !pParam->xResultSet->next();
2248 if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow() )
2249 {
2250 // next returned true but it didn't move
2251 ::dbtools::throwFunctionSequenceException( pParam->xResultSet );
2252 }
2253 }
2254
2255 ++pParam->nSelectionIndex;
2256 bRet = !pParam->bEndOfDB;
2257 }
2258 catch( const uno::Exception & )
2259 {
2260 // we allow merging with empty databases, so don't warn on init
2261 TOOLS_WARN_EXCEPTION_IF(action == SwDBNextRecord::NEXT,
2262 "sw.mailmerge", "exception in ToNextRecord()");
2263 pParam->bEndOfDB = true;
2264 bRet = false;
2265 }
2266 return bRet;
2267}
2268
2269// synchronized labels contain a next record field at their end
2270// to assure that the next page can be created in mail merge
2271// the cursor position must be validated
2273{
2274 return( m_pImpl->pMergeData && m_pImpl->pMergeData->HasValidRecord() );
2275}
2276
2278{
2279 sal_uInt32 nRet = 0;
2280 assert( m_pImpl->pMergeData &&
2281 m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2282 if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is())
2283 return 0;
2284 try
2285 {
2286 nRet = m_pImpl->pMergeData->xResultSet->getRow();
2287 }
2288 catch(const uno::Exception&)
2289 {
2290 }
2291 return nRet;
2292}
2293
2294bool SwDBManager::ToRecordId(sal_Int32 nSet)
2295{
2296 assert( m_pImpl->pMergeData &&
2297 m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2298 if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is()|| nSet < 0)
2299 return false;
2300 bool bRet = false;
2301 sal_Int32 nAbsPos = nSet;
2302 assert(nAbsPos >= 0);
2303 bRet = lcl_MoveAbsolute(m_pImpl->pMergeData.get(), nAbsPos);
2304 m_pImpl->pMergeData->bEndOfDB = !bRet;
2305 return bRet;
2306}
2307
2308bool SwDBManager::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery)
2309{
2311 aData.sDataSource = rDataSource;
2312 aData.sCommand = rTableOrQuery;
2313 aData.nCommandType = -1;
2314
2315 SwDSParam* pFound = FindDSData(aData, true);
2316 if(pFound->xResultSet.is())
2317 return true;
2318 SwDSParam* pParam = FindDSConnection(rDataSource, false);
2319 if(pParam && pParam->xConnection.is())
2320 pFound->xConnection = pParam->xConnection;
2321 if(pFound->xConnection.is())
2322 {
2323 try
2324 {
2325 uno::Reference< sdbc::XDatabaseMetaData > xMetaData = pFound->xConnection->getMetaData();
2326 try
2327 {
2328 pFound->bScrollable = xMetaData
2329 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE));
2330 }
2331 catch(const uno::Exception&)
2332 {
2333 // DB driver may not be ODBC 3.0 compliant
2334 pFound->bScrollable = true;
2335 }
2336 pFound->xStatement = pFound->xConnection->createStatement();
2337 OUString aQuoteChar = xMetaData->getIdentifierQuoteString();
2338 OUString sStatement = "SELECT * FROM " + aQuoteChar + rTableOrQuery + aQuoteChar;
2339 pFound->xResultSet = pFound->xStatement->executeQuery( sStatement );
2340
2341 //after executeQuery the cursor must be positioned
2342 pFound->bEndOfDB = !pFound->xResultSet->next();
2343 ++pFound->nSelectionIndex;
2344 }
2345 catch (const uno::Exception&)
2346 {
2347 pFound->xResultSet = nullptr;
2348 pFound->xStatement = nullptr;
2349 pFound->xConnection = nullptr;
2350 }
2351 }
2352 return pFound->xResultSet.is();
2353}
2354
2355uno::Reference< sdbc::XConnection> const & SwDBManager::RegisterConnection(OUString const& rDataSource)
2356{
2357 SwDSParam* pFound = SwDBManager::FindDSConnection(rDataSource, true);
2358 uno::Reference< sdbc::XDataSource> xSource;
2359 if(!pFound->xConnection.is())
2360 {
2361 SwView* pView = (m_pDoc && m_pDoc->GetDocShell()) ? m_pDoc->GetDocShell()->GetView() : nullptr;
2362 pFound->xConnection = SwDBManager::GetConnection(rDataSource, xSource, pView);
2363 try
2364 {
2365 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2366 if(xComponent.is())
2367 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2368 }
2369 catch(const uno::Exception&)
2370 {
2371 }
2372 }
2373 return pFound->xConnection;
2374}
2375
2377 const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType)
2378{
2379 sal_uInt32 nRet = 0xffffffff;
2380 //check for merge data source first
2381 if(m_pImpl->pMergeData &&
2382 ((rDataSource == m_pImpl->pMergeData->sDataSource &&
2383 rTableOrQuery == m_pImpl->pMergeData->sCommand) ||
2384 (rDataSource.isEmpty() && rTableOrQuery.isEmpty())) &&
2385 (nCommandType == -1 || nCommandType == m_pImpl->pMergeData->nCommandType) &&
2386 m_pImpl->pMergeData->xResultSet.is())
2387 {
2388 nRet = GetSelectedRecordId();
2389 }
2390 else
2391 {
2393 aData.sDataSource = rDataSource;
2394 aData.sCommand = rTableOrQuery;
2395 aData.nCommandType = nCommandType;
2396 SwDSParam* pFound = FindDSData(aData, false);
2397 if(pFound && pFound->xResultSet.is())
2398 {
2399 try
2400 { //if a selection array is set the current row at the result set may not be set yet
2401 if(pFound->aSelection.hasElements())
2402 {
2403 sal_Int32 nSelIndex = pFound->nSelectionIndex;
2404 if(nSelIndex >= pFound->aSelection.getLength())
2405 nSelIndex = pFound->aSelection.getLength() -1;
2406 pFound->aSelection.getConstArray()[nSelIndex] >>= nRet;
2407
2408 }
2409 else
2410 nRet = pFound->xResultSet->getRow();
2411 }
2412 catch(const uno::Exception&)
2413 {
2414 }
2415 }
2416 }
2417 return nRet;
2418}
2419
2420// close all data sources - after fields were updated
2421void SwDBManager::CloseAll(bool bIncludingMerge)
2422{
2423 //the only thing done here is to reset the selection index
2424 //all connections stay open
2425 for (auto & pParam : m_DataSourceParams)
2426 {
2427 if (bIncludingMerge || pParam.get() != m_pImpl->pMergeData.get())
2428 {
2429 pParam->nSelectionIndex = 0;
2430 pParam->bEndOfDB = false;
2431 try
2432 {
2433 if(!m_bInMerge && pParam->xResultSet.is())
2434 pParam->xResultSet->first();
2435 }
2436 catch(const uno::Exception&)
2437 {}
2438 }
2439 }
2440}
2441
2442SwDSParam* SwDBManager::FindDSData(const SwDBData& rData, bool bCreate)
2443{
2444 //prefer merge data if available
2445 if(m_pImpl->pMergeData &&
2446 ((rData.sDataSource == m_pImpl->pMergeData->sDataSource &&
2447 rData.sCommand == m_pImpl->pMergeData->sCommand) ||
2448 (rData.sDataSource.isEmpty() && rData.sCommand.isEmpty())) &&
2449 (rData.nCommandType == -1 || rData.nCommandType == m_pImpl->pMergeData->nCommandType ||
2450 (bCreate && m_pImpl->pMergeData->nCommandType == -1)))
2451 {
2452 return m_pImpl->pMergeData.get();
2453 }
2454
2455 SwDSParam* pFound = nullptr;
2456 for (size_t nPos = m_DataSourceParams.size(); nPos; nPos--)
2457 {
2458 SwDSParam* pParam = m_DataSourceParams[nPos - 1].get();
2459 if(rData.sDataSource == pParam->sDataSource &&
2460 rData.sCommand == pParam->sCommand &&
2461 (rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType ||
2462 (bCreate && pParam->nCommandType == -1)))
2463 {
2464 // calls from the calculator may add a connection with an invalid commandtype
2465 //later added "real" data base connections have to re-use the already available
2466 //DSData and set the correct CommandType
2467 if(bCreate && pParam->nCommandType == -1)
2468 pParam->nCommandType = rData.nCommandType;
2469 pFound = pParam;
2470 break;
2471 }
2472 }
2473 if(bCreate && !pFound)
2474 {
2475 pFound = new SwDSParam(rData);
2476 m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2477 try
2478 {
2479 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2480 if(xComponent.is())
2481 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2482 }
2483 catch(const uno::Exception&)
2484 {
2485 }
2486 }
2487 return pFound;
2488}
2489
2490SwDSParam* SwDBManager::FindDSConnection(const OUString& rDataSource, bool bCreate)
2491{
2492 //prefer merge data if available
2493 if(m_pImpl->pMergeData && rDataSource == m_pImpl->pMergeData->sDataSource )
2494 {
2495 SetAsUsed(rDataSource);
2496 return m_pImpl->pMergeData.get();
2497 }
2498 SwDSParam* pFound = nullptr;
2499 for (const auto & pParam : m_DataSourceParams)
2500 {
2501 if(rDataSource == pParam->sDataSource)
2502 {
2503 SetAsUsed(rDataSource);
2504 pFound = pParam.get();
2505 break;
2506 }
2507 }
2508 if(bCreate && !pFound)
2509 {
2511 aData.sDataSource = rDataSource;
2512 pFound = new SwDSParam(aData);
2513 SetAsUsed(rDataSource);
2514 m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2515 try
2516 {
2517 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2518 if(xComponent.is())
2519 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2520 }
2521 catch(const uno::Exception&)
2522 {
2523 }
2524 }
2525 return pFound;
2526}
2527
2529{
2530 return SW_MOD()->GetDBConfig()->GetAddressSource();
2531}
2532
2534{
2535 uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2536 uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2537 return xDBContext->getElementNames();
2538}
2539
2540namespace sw
2541{
2543{
2544 OUString sExt(rURL.GetFileExtension());
2546
2547 if (sExt == "odb")
2548 {
2550 }
2551 else if (sExt.equalsIgnoreAsciiCase("sxc")
2552 || sExt.equalsIgnoreAsciiCase("ods")
2553 || sExt.equalsIgnoreAsciiCase("xls")
2554 || sExt.equalsIgnoreAsciiCase("xlsx"))
2555 {
2557 }
2558 else if (sExt.equalsIgnoreAsciiCase("sxw") || sExt.equalsIgnoreAsciiCase("odt") || sExt.equalsIgnoreAsciiCase("doc") || sExt.equalsIgnoreAsciiCase("docx"))
2559 {
2561 }
2562 else if (sExt.equalsIgnoreAsciiCase("dbf"))
2563 {
2565 }
2566 else if (sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt"))
2567 {
2569 }
2570#ifdef _WIN32
2571 else if (sExt.equalsIgnoreAsciiCase("mdb") || sExt.equalsIgnoreAsciiCase("mde"))
2572 {
2574 }
2575 else if (sExt.equalsIgnoreAsciiCase("accdb") || sExt.equalsIgnoreAsciiCase("accde"))
2576 {
2578 }
2579#endif
2580 return type;
2581}
2582}
2583
2584namespace
2585{
2586uno::Any GetDBunoURI(const INetURLObject &rURL, DBConnURIType& rType)
2587{
2588 uno::Any aURLAny;
2589
2590 if (rType == DBConnURIType::UNKNOWN)
2591 rType = GetDBunoType(rURL);
2592
2593 switch (rType) {
2594 case DBConnURIType::UNKNOWN:
2595 case DBConnURIType::ODB:
2596 break;
2597 case DBConnURIType::CALC:
2598 {
2599 OUString sDBURL = "sdbc:calc:" +
2601 aURLAny <<= sDBURL;
2602 }
2603 break;
2604 case DBConnURIType::WRITER:
2605 {
2606 OUString sDBURL = "sdbc:writer:" +
2608 aURLAny <<= sDBURL;
2609 }
2610 break;
2611 case DBConnURIType::DBASE:
2612 {
2613 INetURLObject aUrlTmp(rURL);
2614 aUrlTmp.removeSegment();
2615 aUrlTmp.removeFinalSlash();
2616 OUString sDBURL = "sdbc:dbase:" +
2617 aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2618 aURLAny <<= sDBURL;
2619 }
2620 break;
2621 case DBConnURIType::FLAT:
2622 {
2623 INetURLObject aUrlTmp(rURL);
2624 aUrlTmp.removeSegment();
2625 aUrlTmp.removeFinalSlash();
2626 OUString sDBURL = "sdbc:flat:" +
2627 //only the 'path' has to be added
2628 aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2629 aURLAny <<= sDBURL;
2630 }
2631 break;
2632 case DBConnURIType::MSJET:
2633#ifdef _WIN32
2634 {
2635 OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL.PathToFileName());
2636 aURLAny <<= sDBURL;
2637 }
2638#endif
2639 break;
2640 case DBConnURIType::MSACE:
2641#ifdef _WIN32
2642 {
2643 OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL.PathToFileName());
2644 aURLAny <<= sDBURL;
2645 }
2646#endif
2647 break;
2648 }
2649 return aURLAny;
2650}
2651
2653OUString getOwnURL(SfxObjectShell const * pDocShell)
2654{
2655 OUString aRet;
2656
2657 if (!pDocShell)
2658 return aRet;
2659
2660 const INetURLObject& rURLObject = pDocShell->GetMedium()->GetURLObject();
2662 return aRet;
2663}
2664
2671OUString LoadAndRegisterDataSource_Impl(DBConnURIType type, const uno::Reference< beans::XPropertySet > *pSettings,
2672 const INetURLObject &rURL, const OUString *pDestDir, SfxObjectShell* pDocShell)
2673{
2674 OUString sExt(rURL.GetFileExtension());
2675 uno::Any aTableFilterAny;
2676 uno::Any aSuppressVersionsAny;
2677 uno::Any aInfoAny;
2678 bool bStore = true;
2679 OUString sFind;
2680
2681 uno::Any aURLAny = GetDBunoURI(rURL, type);
2682 switch (type) {
2683 case DBConnURIType::UNKNOWN:
2684 case DBConnURIType::CALC:
2685 case DBConnURIType::WRITER:
2686 break;
2687 case DBConnURIType::ODB:
2688 bStore = false;
2689 break;
2690 case DBConnURIType::FLAT:
2691 case DBConnURIType::DBASE:
2692 //set the filter to the file name without extension
2693 {
2694 uno::Sequence<OUString> aFilters { rURL.getBase(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset) };
2695 aTableFilterAny <<= aFilters;
2696 }
2697 break;
2698 case DBConnURIType::MSJET:
2699 case DBConnURIType::MSACE:
2700 aSuppressVersionsAny <<= true;
2701 break;
2702 }
2703
2704 try
2705 {
2706 uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
2707 uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2708
2709 OUString sNewName = rURL.getName(
2711 sal_Int32 nExtLen = sExt.getLength();
2712 sNewName = sNewName.replaceAt(sNewName.getLength() - nExtLen - 1, nExtLen + 1, u"");
2713
2714 //find a unique name if sNewName already exists
2715 sFind = sNewName;
2716 sal_Int32 nIndex = 0;
2717 while (xDBContext->hasByName(sFind))
2718 sFind = sNewName + OUString::number(++nIndex);
2719
2720 uno::Reference<uno::XInterface> xNewInstance;
2721 if (!bStore)
2722 {
2723 //odb-file
2724 uno::Any aDataSource = xDBContext->getByName(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
2725 aDataSource >>= xNewInstance;
2726 }
2727 else
2728 {
2729 xNewInstance = xDBContext->createInstance();
2730 uno::Reference<beans::XPropertySet> xDataProperties(xNewInstance, uno::UNO_QUERY);
2731
2732 if (aURLAny.hasValue())
2733 xDataProperties->setPropertyValue("URL", aURLAny);
2734 if (aTableFilterAny.hasValue())
2735 xDataProperties->setPropertyValue("TableFilter", aTableFilterAny);
2736 if (aSuppressVersionsAny.hasValue())
2737 xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny);
2738 if (aInfoAny.hasValue())
2739 xDataProperties->setPropertyValue("Info", aInfoAny);
2740
2741 if (DBConnURIType::FLAT == type && pSettings)
2742 {
2743 uno::Any aSettings = xDataProperties->getPropertyValue("Settings");
2744 uno::Reference < beans::XPropertySet > xDSSettings;
2745 aSettings >>= xDSSettings;
2746 ::comphelper::copyProperties(*pSettings, xDSSettings);
2747 xDSSettings->setPropertyValue("Extension", uno::Any(sExt));
2748 }
2749
2750 uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, uno::UNO_QUERY_THROW);
2751 uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), uno::UNO_QUERY_THROW);
2752 OUString aOwnURL = getOwnURL(pDocShell);
2753 if (aOwnURL.isEmpty())
2754 {
2755 // Cannot embed, as embedded data source would need the URL of the parent document.
2756 OUString sHomePath(SvtPathOptions().GetWorkPath());
2757 const OUString sTmpName = utl::CreateTempURL(sNewName, true, u".odb", pDestDir ? pDestDir : &sHomePath);
2758 xStore->storeAsURL(sTmpName, uno::Sequence<beans::PropertyValue>());
2759 }
2760 else
2761 {
2762 // Embed.
2763 OUString aStreamRelPath = "EmbeddedDatabase";
2764 uno::Reference<embed::XStorage> xStorage = pDocShell->GetStorage();
2765
2766 // Refer to the sub-storage name in the document settings, so
2767 // we can load it again next time the file is imported.
2768 uno::Reference<lang::XMultiServiceFactory> xFactory(pDocShell->GetModel(), uno::UNO_QUERY);
2769 uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
2770 xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath));
2771
2772 // Store it only after setting the above property, so that only one data source gets registered.
2773 SwDBManager::StoreEmbeddedDataSource(xStore, xStorage, aStreamRelPath, aOwnURL);
2774 }
2775 }
2776 xDBContext->registerObject(sFind, xNewInstance);
2777 }
2778 catch (const uno::Exception&)
2779 {
2780 sFind.clear();
2781 }
2782 return sFind;
2783}
2784}
2785
2787{
2788 sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, pParent);
2790 uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
2791
2792 OUString sFilterAll(SwResId(STR_FILTER_ALL));
2793 OUString sFilterAllData(SwResId(STR_FILTER_ALL_DATA));
2794
2795 const std::vector<std::pair<OUString, OUString>> filters{
2796 { SwResId(STR_FILTER_SXB), "*.odb" },
2797 { SwResId(STR_FILTER_SXC), "*.ods;*.sxc" },
2798 { SwResId(STR_FILTER_SXW), "*.odt;*.sxw" },
2799 { SwResId(STR_FILTER_DBF), "*.dbf" },
2800 { SwResId(STR_FILTER_XLS), "*.xls;*.xlsx" },
2801 { SwResId(STR_FILTER_DOC), "*.doc;*.docx" },
2802 { SwResId(STR_FILTER_TXT), "*.txt" },
2803 { SwResId(STR_FILTER_CSV), "*.csv" },
2804#ifdef _WIN32
2805 { SwResId(STR_FILTER_MDB), "*.mdb;*.mde" },
2806 { SwResId(STR_FILTER_ACCDB), "*.accdb;*.accde" },
2807#endif
2808 };
2809
2810 OUStringBuffer sAllDataFilter;
2811 for (const auto& [name, filter] : filters)
2812 {
2813 (void)name;
2814 if (!sAllDataFilter.isEmpty())
2815 sAllDataFilter.append(';');
2816 sAllDataFilter.append(filter);
2817 }
2818
2819 xFP->appendFilter( sFilterAll, "*" );
2820 xFP->appendFilter( sFilterAllData, sAllDataFilter.makeStringAndClear());
2821
2822 // Similar to sfx2::addExtension from sfx2/source/dialog/filtergrouping.cxx
2823 for (const auto& [name, filter] : filters)
2824 xFP->appendFilter(name + " (" + filter + ")", filter);
2825
2826 xFP->setCurrentFilter( sFilterAll ) ;
2827 OUString sFind;
2828 if( ERRCODE_NONE == aDlgHelper.Execute() )
2829 {
2830 uno::Reference< beans::XPropertySet > aSettings;
2831 const INetURLObject aURL( xFP->getSelectedFiles()[0] );
2833
2834 if( DBConnURIType::FLAT == type )
2835 {
2836 uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2837 uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext);
2838 if( xSettingsDlg->execute() )
2839 aSettings.set( uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ) );
2840 }
2841 sFind = LoadAndRegisterDataSource_Impl( type, DBConnURIType::FLAT == type ? &aSettings : nullptr, aURL, nullptr, pDocShell );
2842
2843 s_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(pDocShell, sFind));
2844 }
2845 return sFind;
2846}
2847
2848void SwDBManager::StoreEmbeddedDataSource(const uno::Reference<frame::XStorable>& xStorable,
2849 const uno::Reference<embed::XStorage>& xStorage,
2850 const OUString& rStreamRelPath,
2851 const OUString& rOwnURL, bool bCopyTo)
2852{
2853 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2854 OUString const sTmpName = ConstructVndSunStarPkgUrl(rOwnURL, rStreamRelPath);
2855
2856 uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
2857 {
2858 {"TargetStorage", uno::Any(xStorage)},
2859 {"StreamRelPath", uno::Any(rStreamRelPath)},
2860 {"BaseURI", uno::Any(rOwnURL)}
2861 });
2862 if (bCopyTo)
2863 xStorable->storeToURL(sTmpName, aSequence);
2864 else
2865 xStorable->storeAsURL(sTmpName, aSequence);
2866}
2867
2868OUString SwDBManager::LoadAndRegisterDataSource(std::u16string_view rURI, const OUString *pDestDir)
2869{
2870 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN, nullptr, INetURLObject(rURI), pDestDir, nullptr );
2871}
2872
2873namespace
2874{
2875 // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2876 // which allows the original storage to be deleted
2877 void switchEmbeddedDatabaseStorage(const uno::Reference<sdb::XDatabaseContext>& rDatabaseContext, const OUString& rName)
2878 {
2879 uno::Reference<sdb::XDocumentDataSource> xDS(rDatabaseContext->getByName(rName), uno::UNO_QUERY);
2880 if (!xDS)
2881 return;
2882 uno::Reference<document::XStorageBasedDocument> xStorageDoc(xDS->getDatabaseDocument(), uno::UNO_QUERY);
2883 if (!xStorageDoc)
2884 return;
2885 xStorageDoc->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2886 }
2887}
2888
2889void SwDBManager::RevokeDataSource(const OUString& rName)
2890{
2891 uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2892 if (xDatabaseContext->hasByName(rName))
2893 {
2894 switchEmbeddedDatabaseStorage(xDatabaseContext, rName);
2895 xDatabaseContext->revokeObject(rName);
2896 }
2897}
2898
2900{
2901 uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2902
2903 OUString sDataSource = rData.sDataSource;
2904
2905 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2906 if (sDataSource.isEmpty())
2907 sDataSource = "EmbeddedDatabase";
2908
2909 SwDBManager::RevokeDataSource( sDataSource );
2910
2911 // Encode the stream name and the real path into a single URL.
2912 const INetURLObject& rURLObject = rDocShell.GetMedium()->GetURLObject();
2913 OUString const aURL = ConstructVndSunStarPkgUrl(
2916
2917 uno::Reference<uno::XInterface> xDataSource(xDatabaseContext->getByName(aURL), uno::UNO_QUERY);
2918 xDatabaseContext->registerObject( sDataSource, xDataSource );
2919
2920 // temp file - don't remember connection
2921 if (rData.sDataSource.isEmpty())
2922 s_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(nullptr, sDataSource));
2923}
2924
2926 const uno::Sequence<beans::PropertyValue>& rProperties)
2927{
2928 //prevent second call
2929 if(m_pImpl->pMergeDialog)
2930 return ;
2931 OUString sDataSource, sDataTableOrQuery;
2932 uno::Sequence<uno::Any> aSelection;
2933
2934 sal_Int32 nCmdType = sdb::CommandType::TABLE;
2935 uno::Reference< sdbc::XConnection> xConnection;
2936
2937 svx::ODataAccessDescriptor aDescriptor(rProperties);
2938 sDataSource = aDescriptor.getDataSource();
2939 OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDataTableOrQuery);
2940 OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCmdType);
2941
2943 aDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
2945 aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
2946
2947 if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty())
2948 {
2949 OSL_FAIL("PropertyValues missing or unset");
2950 return;
2951 }
2952
2953 //always create a connection for the dialog and dispose it after the dialog has been closed
2954 SwDSParam* pFound = nullptr;
2955 if(!xConnection.is())
2956 {
2957 xConnection = SwDBManager::RegisterConnection(sDataSource);
2958 pFound = FindDSConnection(sDataSource, true);
2959 }
2961 m_pImpl->pMergeDialog = pFact->CreateMailMergeDlg(rSh.GetView().GetViewFrame()->GetFrameWeld(), rSh,
2962 sDataSource,
2963 sDataTableOrQuery,
2964 nCmdType,
2965 xConnection);
2966 if(m_pImpl->pMergeDialog->Execute() == RET_OK)
2967 {
2968 aDescriptor[svx::DataAccessDescriptorProperty::Selection] <<= m_pImpl->pMergeDialog->GetSelection();
2969
2970 uno::Reference<sdbc::XResultSet> xResSet = m_pImpl->pMergeDialog->GetResultSet();
2971 if(xResSet.is())
2972 aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
2973
2974 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2976
2977 lcl_emitEvent(SfxEventHintId::SwMailMerge, STR_SW_EVENT_MAIL_MERGE, xDocShell.get());
2978
2979 // prepare mail merge descriptor
2980 SwMergeDescriptor aMergeDesc( m_pImpl->pMergeDialog->GetMergeType(), rSh, aDescriptor );
2981 aMergeDesc.sSaveToFilter = m_pImpl->pMergeDialog->GetSaveFilter();
2982 aMergeDesc.bCreateSingleFile = m_pImpl->pMergeDialog->IsSaveSingleDoc();
2983 aMergeDesc.bPrefixIsFilename = aMergeDesc.bCreateSingleFile;
2984 aMergeDesc.sPrefix = m_pImpl->pMergeDialog->GetTargetURL();
2985
2986 if(!aMergeDesc.bCreateSingleFile)
2987 {
2988 if(m_pImpl->pMergeDialog->IsGenerateFromDataBase())
2989 aMergeDesc.sDBcolumn = m_pImpl->pMergeDialog->GetColumnName();
2990
2991 if(m_pImpl->pMergeDialog->IsFileEncryptedFromDataBase())
2992 aMergeDesc.sDBPasswordColumn = m_pImpl->pMergeDialog->GetPasswordColumnName();
2993 }
2994
2995 Merge( aMergeDesc );
2996
2997 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd, STR_SW_EVENT_MAIL_MERGE_END, xDocShell.get());
2998
2999 // reset the cursor inside
3000 xResSet = nullptr;
3001 aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
3002 }
3003 if(pFound)
3004 {
3005 for (const auto & pParam : m_DataSourceParams)
3006 {
3007 if (pParam.get() == pFound)
3008 {
3009 try
3010 {
3011 uno::Reference<lang::XComponent> xComp(pParam->xConnection, uno::UNO_QUERY);
3012 if(xComp.is())
3013 xComp->dispose();
3014 }
3015 catch(const uno::RuntimeException&)
3016 {
3017 //may be disposed already since multiple entries may have used the same connection
3018 }
3019 break;
3020 }
3021 //pFound doesn't need to be removed/deleted -
3022 //this has been done by the SwConnectionDisposedListener_Impl already
3023 }
3024 }
3025 m_pImpl->pMergeDialog.disposeAndClear();
3026}
3027
3029 const uno::Sequence< beans::PropertyValue>& rProperties)
3030{
3031 OUString sDataSource, sDataTableOrQuery;
3032 uno::Reference<sdbc::XResultSet> xResSet;
3033 uno::Sequence<uno::Any> aSelection;
3034 sal_Int16 nCmdType = sdb::CommandType::TABLE;
3035 uno::Reference< sdbc::XConnection> xConnection;
3036 for(const beans::PropertyValue& rValue : rProperties)
3037 {
3038 if ( rValue.Name == "DataSourceName" )
3039 rValue.Value >>= sDataSource;
3040 else if ( rValue.Name == "Command" )
3041 rValue.Value >>= sDataTableOrQuery;
3042 else if ( rValue.Name == "Cursor" )
3043 rValue.Value >>= xResSet;
3044 else if ( rValue.Name == "Selection" )
3045 rValue.Value >>= aSelection;
3046 else if ( rValue.Name == "CommandType" )
3047 rValue.Value >>= nCmdType;
3048 else if ( rValue.Name == "ActiveConnection" )
3049 rValue.Value >>= xConnection;
3050 }
3051 if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is())
3052 {
3053 OSL_FAIL("PropertyValues missing or unset");
3054 return;
3055 }
3056 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
3057 uno::Reference<sdbc::XDataSource> xSource;
3058 uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
3059 if(xChild.is())
3060 xSource.set(xChild->getParent(), uno::UNO_QUERY);
3061 if(!xSource.is())
3062 xSource = dbtools::getDataSource(sDataSource, xContext);
3063 uno::Reference< sdbcx::XColumnsSupplier > xColSupp( xResSet, uno::UNO_QUERY );
3064 SwDBData aDBData;
3065 aDBData.sDataSource = sDataSource;
3066 aDBData.sCommand = sDataTableOrQuery;
3067 aDBData.nCommandType = nCmdType;
3068
3071 xSource,
3072 xColSupp,
3073 aDBData ));
3074 if( RET_OK != pDlg->Execute() )
3075 return;
3076
3077 OUString sDummy;
3078 if(!xConnection.is())
3079 xConnection = xSource->getConnection(sDummy, sDummy);
3080 try
3081 {
3082 pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet);
3083 }
3084 catch (const uno::Exception&)
3085 {
3086 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3087 }
3088}
3089
3090uno::Reference<sdbc::XDataSource> SwDBManager::getDataSourceAsParent(const uno::Reference< sdbc::XConnection>& _xConnection,const OUString& _sDataSourceName)
3091{
3092 uno::Reference<sdbc::XDataSource> xSource;
3093 try
3094 {
3095 uno::Reference<container::XChild> xChild(_xConnection, uno::UNO_QUERY);
3096 if ( xChild.is() )
3097 xSource.set(xChild->getParent(), uno::UNO_QUERY);
3098 if ( !xSource.is() )
3099 xSource = dbtools::getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext());
3100 }
3101 catch (const uno::Exception&)
3102 {
3103 TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3104 }
3105 return xSource;
3106}
3107
3108uno::Reference<sdbc::XResultSet> SwDBManager::createCursor(const OUString& _sDataSourceName,
3109 const OUString& _sCommand,
3110 sal_Int32 _nCommandType,
3111 const uno::Reference<sdbc::XConnection>& _xConnection,
3112 const SwView* pView)
3113{
3114 uno::Reference<sdbc::XResultSet> xResultSet;
3115 try
3116 {
3117 uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
3118 if( xMgr.is() )
3119 {
3120 uno::Reference<uno::XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet");
3121 uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, uno::UNO_QUERY);
3122 if(xRowSetPropSet.is())
3123 {
3124 xRowSetPropSet->setPropertyValue("DataSourceName", uno::Any(_sDataSourceName));
3125 xRowSetPropSet->setPropertyValue("ActiveConnection", uno::Any(_xConnection));
3126 xRowSetPropSet->setPropertyValue("Command", uno::Any(_sCommand));
3127 xRowSetPropSet->setPropertyValue("CommandType", uno::Any(_nCommandType));
3128
3129 uno::Reference< sdb::XCompletedExecution > xRowSet(xInstance, uno::UNO_QUERY);
3130
3131 if ( xRowSet.is() )
3132 {
3133 weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
3134 uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
3135 xRowSet->executeWithCompletion(xHandler);
3136 }
3137 xResultSet.set(xRowSet, uno::UNO_QUERY);
3138 }
3139 }
3140 }
3141 catch (const uno::Exception&)
3142 {
3143 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3144 }
3145 return xResultSet;
3146}
3147
3148void SwDBManager::setEmbeddedName(const OUString& rEmbeddedName, SwDocShell& rDocShell)
3149{
3150 bool bLoad = m_sEmbeddedName != rEmbeddedName && !rEmbeddedName.isEmpty();
3151 bool bRegisterListener = m_sEmbeddedName.isEmpty() && !rEmbeddedName.isEmpty();
3152
3153 m_sEmbeddedName = rEmbeddedName;
3154
3155 if (bLoad)
3156 {
3157 uno::Reference<embed::XStorage> xStorage = rDocShell.GetStorage();
3158 // It's OK that we don't have the named sub-storage yet, in case
3159 // we're in the process of creating it.
3160 if (xStorage->hasByName(rEmbeddedName))
3161 LoadAndRegisterEmbeddedDataSource(rDocShell.GetDoc()->GetDBData(), rDocShell);
3162 }
3163
3164 if (bRegisterListener)
3165 // Register a remove listener, so we know when the embedded data source is removed.
3166 m_pImpl->m_xDataSourceRemovedListener = new SwDataSourceRemovedListener(*this);
3167}
3168
3169const OUString& SwDBManager::getEmbeddedName() const
3170{
3171 return m_sEmbeddedName;
3172}
3173
3175{
3176 return m_pDoc;
3177}
3178
3180{
3181 if (m_pImpl->m_xDataSourceRemovedListener.is())
3182 {
3183 m_pImpl->m_xDataSourceRemovedListener->Dispose();
3184 m_pImpl->m_xDataSourceRemovedListener.clear();
3185 }
3186}
3187
3189 : m_pDBManager(&rManager)
3190{
3191}
3192
3193void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject& rSource )
3194{
3195 ::SolarMutexGuard aGuard;
3196
3197 if (!m_pDBManager) return; // we're disposed too!
3198
3199 uno::Reference<sdbc::XConnection> xSource(rSource.Source, uno::UNO_QUERY);
3200 for (size_t nPos = m_pDBManager->m_DataSourceParams.size(); nPos; nPos--)
3201 {
3202 SwDSParam* pParam = m_pDBManager->m_DataSourceParams[nPos - 1].get();
3203 if(pParam->xConnection.is() &&
3204 (xSource == pParam->xConnection))
3205 {
3206 m_pDBManager->m_DataSourceParams.erase(
3207 m_pDBManager->m_DataSourceParams.begin() + nPos - 1);
3208 }
3209 }
3210}
3211
3212std::shared_ptr<SwMailMergeConfigItem> SwDBManager::PerformMailMerge(SwView const * pView)
3213{
3214 std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
3215 if (!xConfigItem)
3216 return xConfigItem;
3217
3218 svx::ODataAccessDescriptor aDescriptor;
3219 aDescriptor.setDataSource(xConfigItem->GetCurrentDBData().sDataSource);
3220 aDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= xConfigItem->GetConnection().getTyped();
3221 aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ] <<= xConfigItem->GetResultSet();
3222 aDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= xConfigItem->GetCurrentDBData().sCommand;
3223 aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= xConfigItem->GetCurrentDBData().nCommandType;
3224 aDescriptor[ svx::DataAccessDescriptorProperty::Selection ] <<= xConfigItem->GetSelection();
3225
3226 SwWrtShell& rSh = pView->GetWrtShell();
3227 xConfigItem->SetTargetView(nullptr);
3228
3229 SwMergeDescriptor aMergeDesc(DBMGR_MERGE_SHELL, rSh, aDescriptor);
3230 aMergeDesc.pMailMergeConfigItem = xConfigItem.get();
3231 aMergeDesc.bCreateSingleFile = true;
3232 rSh.GetDBManager()->Merge(aMergeDesc);
3233
3234 return xConfigItem;
3235}
3236
3238{
3239 if (s_aUncommittedRegistrations.empty())
3240 return;
3241
3242 SwView* pView = ( m_pDoc && m_pDoc->GetDocShell() ) ? m_pDoc->GetDocShell()->GetView() : nullptr;
3243 if (pView)
3244 {
3245 const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
3246 if (xConfigItem)
3247 {
3248 xConfigItem->DisposeResultSet();
3249 xConfigItem->DocumentReloaded();
3250 }
3251 }
3252
3253 for (auto it = s_aUncommittedRegistrations.begin(); it != s_aUncommittedRegistrations.end();)
3254 {
3255 if ((m_pDoc && it->first == m_pDoc->GetDocShell()) || it->first == nullptr)
3256 {
3257 RevokeDataSource(it->second);
3258 it = s_aUncommittedRegistrations.erase(it);
3259 }
3260 else
3261 ++it;
3262 }
3263}
3264
3266{
3267 for (auto aIt = s_aUncommittedRegistrations.begin(); aIt != s_aUncommittedRegistrations.end();)
3268 {
3269 if (aIt->first == m_pDoc->GetDocShell() || aIt->first == nullptr)
3270 {
3271 m_aNotUsedConnections.push_back(aIt->second);
3272 aIt = s_aUncommittedRegistrations.erase(aIt);
3273 }
3274 else
3275 aIt++;
3276 }
3277}
3278
3279void SwDBManager::SetAsUsed(const OUString& rName)
3280{
3281 auto aFound = std::find(m_aNotUsedConnections.begin(), m_aNotUsedConnections.end(), rName);
3282 if (aFound != m_aNotUsedConnections.end())
3283 m_aNotUsedConnections.erase(aFound);
3284}
3285
3287{
3288 for (auto aIt = m_aNotUsedConnections.begin(); aIt != m_aNotUsedConnections.end();)
3289 {
3290 RevokeDataSource(*aIt);
3291 aIt = m_aNotUsedConnections.erase(aIt);
3292 }
3293}
3294
3295/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const short COPY
SfxApplication * SfxGetpApp()
static void Yield()
static bool Reschedule(bool bHandleAllCurrentEvents=false)
static bool IsQuit()
static DialogMask HandleError(ErrCode nId, weld::Window *pParent=nullptr, DialogMask nMask=DialogMask::MAX)
virtual const JobSetup * getJobsetup() const =0
Returns the Jobsetup.
virtual void setPrintData(const SwPrintData &rPrtData)=0
Sets the PrintData.
virtual void setJobsetup(const JobSetup &rJobSetup)=0
Sets the Jobsetup.
virtual const SwPrintData & getPrintData() const =0
Returns the PrintData.
virtual sal_Int32 GetRecordsPerDocument() const =0
virtual bool EmbedAllLinks()=0
Embed all local links (ranges/graphics).
virtual void ResetModified()=0
virtual void DoUndo(bool const bDoUndo)=0
Enable/Disable Undo.
MailDispatcher listener interface.
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetFileExtension() const
bool removeSegment(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true)
OUString getBase(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetBase() const
OUString PathToFileName() const
bool SetURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
static OUString encode(std::u16string_view rText, Part ePart, EncodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
A MailDispatcher should be used for sending a bunch a mail messages asynchronously.
std::unique_ptr< weld::Label > m_xPrintInfo
Definition: dbui.hxx:30
std::unique_ptr< weld::Label > m_xPrinter
Definition: dbui.hxx:29
static void FinishPrintJob(const std::shared_ptr< vcl::PrinterController > &i_pController)
bool PutString(const OUString &)
bool PutDouble(double)
static void ProcessEventsToIdle()
void NotifyEvent(const SfxEventHint &rEvent, bool bSynchron=true)
void Lock(bool bLock)
std::shared_ptr< const SfxFilter > GetFilter4FilterName(const OUString &rName, SfxFilterFlags nMust=SfxFilterFlags::NONE, SfxFilterFlags nDont=SFX_FILTER_NOTINSTALLED) const
std::shared_ptr< const SfxFilter > GetFilter4Extension(const OUString &rExt, SfxFilterFlags nMust=SfxFilterFlags::IMPORT, SfxFilterFlags nDont=SFX_FILTER_NOTINSTALLED) const
vcl::Window & GetWindow() const
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
const INetURLObject & GetURLObject() const
SfxItemSet * GetItemSet() const
void SetFilter(const std::shared_ptr< const SfxFilter > &pFilter)
SvStream * GetInStream()
bool Is() const
bool DoSaveAs(SfxMedium &rNewStor)
virtual bool DoSaveCompleted(SfxMedium *pNewStor=nullptr, bool bRegisterRecent=true)
bool IsModified() const
ErrCode GetError() const
SfxMedium * GetMedium() const
css::uno::Reference< css::frame::XModel3 > GetModel() const
OUString GetTitle(sal_uInt16 nMaxLen=0) const
css::uno::Reference< css::embed::XStorage > const & GetStorage()
SfxViewShell * GetViewShell() const
static SfxViewFrame * GetNext(const SfxViewFrame &rPrev, const SfxObjectShell *pDoc=nullptr, bool bOnlyVisible=true)
SfxDispatcher * GetDispatcher()
static SfxViewFrame * GetFirst(const SfxObjectShell *pDoc=nullptr, bool bOnlyVisible=true)
SfxFrame & GetFrame() const
virtual SfxObjectShell * GetObjectShell() override
weld::Window * GetFrameWeld() const
static SfxViewFrame * LoadHiddenDocument(SfxObjectShell const &i_rDoc, SfxInterfaceId i_nViewId)
weld::Window * GetFrameWeld() const
const std::shared_ptr< vcl::PrinterController > & GetPrinterController() const
SfxViewFrame * GetViewFrame() const
void ExecPrint(const css::uno::Sequence< css::beans::PropertyValue > &, bool, bool)
sal_uInt32 GetFormatIndex(NfIndexTableOffset, LanguageType eLnge=LANGUAGE_DONTKNOW)
bool ReadLine(OStringBuffer &rStr, sal_Int32 nMaxBytesToRead=0xFFFE)
void SetStreamCharSet(rtl_TextEncoding eCharSet)
const OUString & GetTempPath() const
virtual VclPtr< AbstractMailMergeDlg > CreateMailMergeDlg(weld::Window *pParent, SwWrtShell &rSh, const OUString &rSourceName, const OUString &rTableName, sal_Int32 nCommandType, const css::uno::Reference< css::sdbc::XConnection > &xConnection)=0
virtual VclPtr< AbstractSwInsertDBColAutoPilot > CreateSwInsertDBColAutoPilot(SwView &rView, css::uno::Reference< css::sdbc::XDataSource > rxSource, css::uno::Reference< css::sdbcx::XColumnsSupplier > xColSupp, const SwDBData &rData)=0
static SwAbstractDialogFactory * Create()
Definition: swabstdlg.cxx:36
Definition: calc.hxx:199
void VarChange(const OUString &rStr, const SwSbxValue &rValue)
Definition: calc.cxx:596
bool SttEndDoc(bool bStt)
Definition: crsrsh.cxx:573
sal_uInt16 GetPageCnt()
Definition: crsrsh.cxx:1291
static bool FormatValue(SvNumberFormatter const *pDocFormatter, OUString const &aString, sal_uInt32 nFormat, double &aNumber, sal_Int32 nColumnType, SwDBField *pField=nullptr)
Definition: dbfld.cxx:257
virtual void SAL_CALL disposing(const lang::EventObject &Source) override
Definition: dbmgr.cxx:3193
ConnectionDisposedListener_Impl(SwDBManager &rMgr)
Definition: dbmgr.cxx:3188
virtual void mailDeliveryError(::rtl::Reference< MailDispatcher > xMailDispatcher, uno::Reference< mail::XMailMessage >, const OUString &) override
Definition: dbmgr.cxx:1038
MailDispatcherListener_Impl(SwDBManager &rDBManager)
Definition: dbmgr.cxx:1026
virtual void mailDelivered(uno::Reference< mail::XMailMessage > xMessage) override
Definition: dbmgr.cxx:1031
virtual void idle() override
Called when there are no more mail messages to deliver.
Definition: dbmgr.cxx:1029
static css::uno::Reference< css::sdbc::XConnection > GetConnection(const OUString &rDataSource, css::uno::Reference< css::sdbc::XDataSource > &rxSource, const SwView *pView)
Definition: dbmgr.cxx:1878
static css::uno::Reference< css::sdbcx::XColumnsSupplier > GetColumnSupplier(css::uno::Reference< css::sdbc::XConnection > const &xConnection, const OUString &rTableOrQuery, SwDBSelect eTableOrQuery=SwDBSelect::UNKNOWN)
Definition: dbmgr.cxx:1901
std::vector< OUString > m_aNotUsedConnections
Not used connections.
Definition: dbmgr.hxx:266
void RevokeLastRegistrations()
Revoke not committed registrations in case of mail merge cancel.
Definition: dbmgr.cxx:3237
css::uno::Reference< css::sdbc::XConnection > const & RegisterConnection(OUString const &rSource)
create and store or find an already stored connection to a data source for use in SwFieldMgr and SwDB...
Definition: dbmgr.cxx:2355
SAL_DLLPRIVATE bool ToNextMergeRecord()
Definition: dbmgr.cxx:2118
SwDoc * getDoc() const
Definition: dbmgr.cxx:3174
const SwXMailMerge * m_pMergeEvtSrc
!= 0 if mail merge events are to be send
Definition: dbmgr.hxx:258
SAL_DLLPRIVATE SwDSParam * FindDSConnection(const OUString &rSource, bool bCreate)
Definition: dbmgr.cxx:2490
static std::vector< std::pair< SwDocShell *, OUString > > s_aUncommittedRegistrations
Store last registrations to revoke or commit.
Definition: dbmgr.hxx:263
void releaseRevokeListener()
Stop reacting to removed database registrations.
Definition: dbmgr.cxx:3179
bool Merge(const SwMergeDescriptor &rMergeDesc)
Merging of data records into fields.
Definition: dbmgr.cxx:431
bool m_bInMerge
merge process active
Definition: dbmgr.hxx:254
void MergeCancel()
Definition: dbmgr.cxx:1663
static OUString GetDBField(css::uno::Reference< css::beans::XPropertySet > const &xColumnProp, const SwDBFormatData &rDBFormatData, double *pNumber=nullptr)
Definition: dbmgr.cxx:1949
SAL_DLLPRIVATE void ImportFromConnection(SwWrtShell *pSh)
Insert data record as text into document.
Definition: dbmgr.cxx:578
bool IsDataSourceOpen(const OUString &rDataSource, const OUString &rTableOrQuery, bool bMergeShell)
check if a data source is open
Definition: dbmgr.cxx:2021
void CloseAll(bool bIncludingMerge=true)
close all data sources - after fields were updated
Definition: dbmgr.cxx:2421
bool FillCalcWithMergeData(SvNumberFormatter *pDocFormatter, LanguageType nLanguage, SwCalc &aCalc)
Definition: dbmgr.cxx:2124
static const SwDBData & GetAddressDBName()
Definition: dbmgr.cxx:2528
static sal_uLong GetColumnFormat(css::uno::Reference< css::sdbc::XDataSource > const &xSource, css::uno::Reference< css::sdbc::XConnection > const &xConnection, css::uno::Reference< css::beans::XPropertySet > const &xColumn, SvNumberFormatter *pNFormatr, LanguageType nLanguage)
static css::uno::Sequence< OUString > GetExistingDatabaseNames()
Definition: dbmgr.cxx:2533
const OUString & getEmbeddedName() const
Definition: dbmgr.cxx:3169
bool GetTableNames(weld::ComboBox &rBox, const OUString &rDBName)
Fill listbox with all table names of a database.
Definition: dbmgr.cxx:634
const SwXMailMerge * GetMailMergeEvtSrc() const
MailMergeEvent source.
Definition: dbmgr.hxx:299
SAL_DLLPRIVATE void ImplDestroy()
Definition: dbmgr.cxx:721
SwDBManager(SwDBManager const &)=delete
static std::shared_ptr< SwMailMergeConfigItem > PerformMailMerge(SwView const *pView)
Definition: dbmgr.cxx:3212
void setEmbeddedName(const OUString &rEmbeddedName, SwDocShell &rDocShell)
Definition: dbmgr.cxx:3148
void GetColumnNames(weld::ComboBox &rBox, const OUString &rDBName, const OUString &rTableName)
Fill listbox with all column names of a database table.
Definition: dbmgr.cxx:674
bool IsMergeOk() const
Definition: dbmgr.hxx:309
SAL_DLLPRIVATE bool IsValidMergeRecord() const
Definition: dbmgr.cxx:2272
bool ToRecordId(sal_Int32 nSet)
Definition: dbmgr.cxx:2294
void RevokeNotUsedConnections()
Remove not used connections.
Definition: dbmgr.cxx:3286
bool m_bMergeSilent
suppress display of dialogs/boxes (used when called over API)
Definition: dbmgr.hxx:255
void SetAsUsed(const OUString &rName)
Set connection as used.
Definition: dbmgr.cxx:3279
sal_Int32 GetColumnType(const OUString &rDBName, const OUString &rTableName, const OUString &rColNm)
Definition: dbmgr.cxx:1834
static css::uno::Reference< css::sdbc::XDataSource > getDataSourceAsParent(const css::uno::Reference< css::sdbc::XConnection > &_xConnection, const OUString &_sDataSourceName)
try to get the data source from the given connection through the XChild interface.
Definition: dbmgr.cxx:3090
SAL_DLLPRIVATE void ImportDBEntry(SwWrtShell *pSh)
Insert a single data record as text into document.
Definition: dbmgr.cxx:609
OUString m_sEmbeddedName
Name of the embedded database that's included in the current document.
Definition: dbmgr.hxx:260
bool IsMergeSilent() const
Definition: dbmgr.hxx:302
void ExecuteFormLetter(SwWrtShell &rSh, const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
Definition: dbmgr.cxx:2925
static void InsertText(SwWrtShell &rSh, const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
Definition: dbmgr.cxx:3028
SwDSParams_t m_DataSourceParams
Definition: dbmgr.hxx:256
bool GetMergeColumnCnt(const OUString &rColumnName, LanguageType nLanguage, OUString &rResult, double *pNumber)
Definition: dbmgr.cxx:2105
~SwDBManager()
Definition: dbmgr.cxx:749
bool OpenDataSource(const OUString &rDataSource, const OUString &rTableOrQuery)
open the source while fields are updated - for the calculator only!
Definition: dbmgr.cxx:2308
static OUString LoadAndRegisterDataSource(weld::Window *pParent, SwDocShell *pDocShell=nullptr)
Loads a data source from file and registers it.
Definition: dbmgr.cxx:2786
bool m_bInitDBFields
Definition: dbmgr.hxx:253
void LoadAndRegisterEmbeddedDataSource(const SwDBData &rData, const SwDocShell &rDocShell)
Load the embedded data source of the document and also register it.
Definition: dbmgr.cxx:2899
bool IsInitDBFields() const
Initialize data fields that lack name of database.
Definition: dbmgr.hxx:315
bool GetColumnCnt(const OUString &rSourceName, const OUString &rTableName, const OUString &rColumnName, sal_uInt32 nAbsRecordId, LanguageType nLanguage, OUString &rResult, double *pNumber)
Definition: dbmgr.cxx:2044
SwDoc * m_pDoc
The document that owns this manager.
Definition: dbmgr.hxx:272
void CommitLastRegistrations()
Accept not committed registrations.
Definition: dbmgr.cxx:3265
void ToNextRecord(const OUString &rDataSource, const OUString &rTableOrQuery)
Definition: dbmgr.cxx:2188
static css::uno::Reference< css::sdbc::XResultSet > createCursor(const OUString &_sDataSourceName, const OUString &_sCommand, sal_Int32 _nCommandType, const css::uno::Reference< css::sdbc::XConnection > &_xConnection, const SwView *pView)
creates a RowSet, which must be disposed after use.
Definition: dbmgr.cxx:3108
static void StoreEmbeddedDataSource(const css::uno::Reference< css::frame::XStorable > &xStorable, const css::uno::Reference< css::embed::XStorage > &xStorage, const OUString &rStreamRelPath, const OUString &rOwnURL, bool bCopyTo=false)
Definition: dbmgr.cxx:2848
static void RevokeDataSource(const OUString &rName)
Unregister a data source.
Definition: dbmgr.cxx:2889
SAL_DLLPRIVATE SwDSParam * FindDSData(const SwDBData &rData, bool bCreate)
Definition: dbmgr.cxx:2442
MergeStatus m_aMergeStatus
current / last merge status
Definition: dbmgr.hxx:252
SAL_DLLPRIVATE bool MergeMailFiles(SwWrtShell *pSh, const SwMergeDescriptor &rMergeDescriptor)
Run the mail merge for defined modes, except DBMGR_MERGE.
Definition: dbmgr.cxx:1052
void SetInitDBFields(bool b)
Definition: dbmgr.hxx:316
bool IsMergeError() const
Definition: dbmgr.hxx:310
sal_uInt32 GetSelectedRecordId()
Definition: dbmgr.cxx:2277
std::unique_ptr< SwDBManager_Impl > m_pImpl
Definition: dbmgr.hxx:257
static OUString GetEventName(sal_Int32 nId)
Definition: docsh.cxx:1329
const SwView * GetView() const
Definition: docsh.hxx:221
SwDoc * GetDoc()
returns Doc. But be careful!
Definition: docsh.hxx:204
Definition: doc.hxx:194
SwDBData const & GetDBData()
Definition: docfld.cxx:383
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:400
IDocumentDeviceAccess const & getIDocumentDeviceAccess() const
Definition: doc.cxx:244
o3tl::sorted_vector< SwRootFrame * > GetAllLayouts()
Definition: doclay.cxx:1687
SfxObjectShell * CreateCopy(bool bCallInitNew, bool bEmpty) const
Definition: docnew.cxx:889
void SetInMailMerge(bool bNew)
Definition: doc.hxx:969
SwDBManager * GetDBManager() const
Definition: doc.hxx:678
IDocumentLinksAdministration const & getIDocumentLinksAdministration() const
Definition: doc.cxx:266
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:150
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:363
void SetDBManager(SwDBManager *pNewMgr)
Definition: doc.hxx:677
void ReplaceDocumentProperties(const SwDoc &rSource, bool mailMerge=false)
Replace document properties with those from rSource.
Definition: docglos.cxx:83
SwDocShell * GetDocShell()
Definition: doc.hxx:1359
virtual void CalcLayout() override
To enable set up of StartActions and EndActions.
Definition: edws.cxx:108
bool DoesGroupUndo() const
Definition: edws.cxx:209
void StartAllAction()
For all views of this document.
Definition: edws.cxx:86
size_t GetSectionFormatCount() const
Definition: edsect.cxx:114
SwUndoId StartUndo(SwUndoId eUndoId=SwUndoId::EMPTY, const SwRewriter *pRewriter=nullptr)
Undo: set up Undo parenthesis, return nUndoId of this parenthesis.
Definition: edws.cxx:223
SwDBManager * GetDBManager() const
For evaluation of DB fields (new DB-manager).
Definition: edfld.cxx:328
bool IsExpFieldsLocked() const
Definition: edfld.cxx:353
void ChangeDBFields(const std::vector< OUString > &rOldNames, const OUString &rNewName)
Definition: edfld.cxx:307
bool ConvertFieldsToText()
Replace fields by text - mailmerge support.
Definition: editsh.cxx:761
void UpdateSection(size_t const nSect, SwSectionData &, SfxItemSet const *const =nullptr)
Definition: edsect.cxx:156
void LockExpFields()
Definition: edfld.cxx:343
SwDBData const & GetDBData() const
Database information.
Definition: edfld.cxx:291
void SetNumberingRestart()
Set all numbering start points to a fixed value - mailmerge support.
Definition: editsh.cxx:769
void SetModified()
Definition: edws.cxx:70
void DoGroupUndo(bool bUn)
Definition: edws.cxx:206
bool IsLabelDoc() const
Definition: edfld.cxx:373
const SwSectionFormat & GetSectionFormat(size_t nFormat) const
Definition: edsect.cxx:142
void UnlockExpFields()
Definition: edfld.cxx:348
SwUndoId EndUndo(SwUndoId eUndoId=SwUndoId::EMPTY, const SwRewriter *pRewriter=nullptr)
Closes parenthesis of nUndoId, not used by UI.
Definition: edws.cxx:234
void SetLabelDoc(bool bFlag)
Labels: Synchronize ranges.
Definition: edfld.cxx:368
void EndAllAction()
Definition: edws.cxx:97
sal_uInt16 GetVirtPageNum() const
Definition: fews.cxx:343
static const OUString & GetTypeStr(SwFieldTypesEnum nTypeId)
Definition: fldbas.cxx:122
static std::shared_ptr< const SfxFilter > GetFileFilter(const OUString &rFileName)
Detect for the given file which filter should be used.
Definition: iodetect.cxx:149
void AddMergedDocument(SwDocMergeInfo const &rInfo)
OUString const & GetMailAddress() const
void SetTargetView(SwView *pView)
bool IsMailReplyTo() const
OUString const & GetMailReplyTo() const
Marks a node in the document model.
Definition: ndindex.hxx:31
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:187
void SetDBvalue(bool bSet)
Definition: calc.hxx:130
void SetType(SectionType const eNew)
Definition: section.hxx:96
SectionType GetType() const
Definition: section.hxx:95
void SetLinkFileName(OUString const &rNew)
Definition: section.hxx:118
SwSection * GetSection() const
Definition: section.cxx:638
void SetIdle(bool b) const
Definition: viewopt.hxx:283
const SwViewOption * GetViewOptions() const
Definition: viewsh.hxx:433
SwDoc * GetDoc() const
Definition: viewsh.hxx:290
Definition: view.hxx:146
SwWrtShell & GetWrtShell() const
Definition: view.hxx:416
std::shared_ptr< SwMailMergeConfigItem > const & GetMailMergeConfigItem() const
Definition: view0.cxx:136
SwWrtShell * GetWrtShellPtr() const
Definition: view.hxx:417
SwEditWin & GetEditWin()
Definition: view.hxx:419
SwDocShell * GetDocShell()
Definition: view.cxx:1160
Used by the UI to modify the document model.
Definition: wrtsh.hxx:97
void ChgDBData(const SwDBData &SwDBData)
Definition: wrtsh1.cxx:2069
bool DelRight(bool isReplaceHeuristic=false)
Definition: delete.cxx:291
bool HasSelection() const
Definition: wrtsh.hxx:147
const SwView & GetView() const
Definition: wrtsh.hxx:438
Gives UNO access to the global mail merge functionality, via the com.sun.star.text....
void Stop()
void SetTimeout(sal_uInt64 nTimeoutMs)
virtual void Start(bool bStartTimer=true) override
static css::uno::Reference< css::embed::XStorage > GetTemporaryStorage(const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
const css::uno::Reference< css::ui::dialogs::XFilePicker3 > & GetFilePicker() const
void SetContext(Context _eNewContext)
bool has(DataAccessDescriptorProperty _eWhich) const
OUString getDataSource() const
void setDataSource(const OUString &_sDataSourceNameOrLocation)
T * get() const
bool is() const
bool IsValid() const
OUString const & GetURL() const
virtual Point GetPosPixel() const
virtual void SetPosPixel(const Point &rNewPos)
weld::Window * GetFrameWeld() const
void append(const OUString &rId, const OUString &rStr)
virtual OUString get_active_text() const=0
virtual void clear()=0
void append_text(const OUString &rStr)
void set_active_text(const OUString &rStr)
static bool runAsync(const std::shared_ptr< DialogController > &rController, const std::function< void(sal_Int32)> &)
virtual css::uno::Reference< css::awt::XWindow > GetXWindow()=0
static bool lcl_MoveAbsolute(SwDSParam *pParam, tools::Long nAbsPos)
Definition: dbmgr.cxx:354
static bool lcl_getCountFromResultSet(sal_Int32 &rCount, const SwDSParam *pParam)
Definition: dbmgr.cxx:178
static void lcl_SaveDebugDoc(SfxObjectShell *xTargetDocShell, const char *name, int no=0)
Definition: dbmgr.cxx:771
static bool lcl_ToNextRecord(SwDSParam *pParam, const SwDBNextRecord action=SwDBNextRecord::NEXT)
Definition: dbmgr.cxx:2209
static SfxObjectShell * lcl_CreateWorkingDocument(const WorkingDocType aType, const SwWrtShell &rSourceWrtShell, const vcl::Window *pSourceWindow, SwDBManager **const ppDBManager, SwView **const pView, SwWrtShell **const pWrtShell, rtl::Reference< SwDoc > *const pDoc)
Definition: dbmgr.cxx:900
static void lcl_PreparePrinterOptions(const uno::Sequence< beans::PropertyValue > &rInPrintOptions, uno::Sequence< beans::PropertyValue > &rOutPrintOptions)
Definition: dbmgr.cxx:852
static rtl::Reference< SwMailMessage > lcl_CreateMailFromDoc(const SwMergeDescriptor &rMergeDescriptor, const OUString &sFileURL, const OUString &sMailRecipient, const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding, const OUString &sAttachmentMimeType)
Definition: dbmgr.cxx:969
static bool lcl_SaveDoc(const INetURLObject *pFileURL, const std::shared_ptr< const SfxFilter > &pStoreToFilter, const OUString *pStoreToFilterOptions, const uno::Sequence< beans::PropertyValue > *pSaveToFilterData, const bool bIsPDFexport, SfxObjectShell *xObjectShell, SwWrtShell &rWorkShell, OUString *const decodedURL=nullptr)
Definition: dbmgr.cxx:807
static void lcl_InitNumberFormatter(SwDSParam &rParam, uno::Reference< sdbc::XDataSource > const &xSource)
Definition: dbmgr.cxx:325
static void lcl_RemoveSectionLinks(SwWrtShell &rWorkShell)
Definition: dbmgr.cxx:754
static void lcl_PrepareSaveFilterDataOptions(const uno::Sequence< beans::PropertyValue > &rInSaveFilterDataptions, uno::Sequence< beans::PropertyValue > &rOutSaveFilterDataOptions, const OUString &sPassword)
Definition: dbmgr.cxx:878
static void lcl_GetColumnCnt(SwDSParam *pParam, const uno::Reference< beans::XPropertySet > &rColumnProps, LanguageType nLanguage, OUString &rResult, double *pNumber)
Definition: dbmgr.cxx:390
@ DBMGR_MERGE_EMAIL
Send mail merge as email.
Definition: dbmgr.hxx:91
@ DBMGR_MERGE
Data records in fields.
Definition: dbmgr.hxx:89
@ DBMGR_MERGE_SHELL
Create merge doc and keep the doc shell.
Definition: dbmgr.hxx:93
@ DBMGR_MERGE_PRINTER
Print mail merge.
Definition: dbmgr.hxx:90
@ DBMGR_MERGE_FILE
Save mail merge as files.
Definition: dbmgr.hxx:92
SwDBSelect
Definition: dbmgr.hxx:98
#define suppress_fun_call_w_exception(expr)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define TOOLS_WARN_EXCEPTION_IF(cond, area, stream)
URL aURL
float u
#define ERRCODE_IO_NOTSUPPORTED
#define ERRCODE_NONE
SfxEventHintId
Reference< XSingleServiceFactory > xFactory
Reference< XColumn > xColumn
OUString sAttachmentName
Definition: dbmgr.hxx:191
css::uno::Sequence< OUString > aBlindCopiesTo
Definition: dbmgr.hxx:193
bool bSendAsAttachment
Definition: dbmgr.hxx:196
OUString sSubject
Definition: dbmgr.hxx:189
css::uno::Sequence< OUString > aCopiesTo
Definition: dbmgr.hxx:192
css::uno::Reference< css::mail::XSmtpService > xSmtpServer
Definition: dbmgr.hxx:194
OUString sMailBody
Definition: dbmgr.hxx:190
OUString sPrefix
Basename incl.
Definition: dbmgr.hxx:174
OUString sDBPasswordColumn
DB column to fetch password.
Definition: dbmgr.hxx:209
bool bPrefixIsFilename
Use the sPrefix as the target filename also overwriting an existing target file.
Definition: dbmgr.hxx:182
OUString sDBcolumn
DB column to fetch EMail of Filename from.
Definition: dbmgr.hxx:205
css::uno::Sequence< css::beans::PropertyValue > aPrintOptions
Definition: dbmgr.hxx:217
OUString sSaveToFilter
Definition: dbmgr.hxx:157
css::uno::Sequence< css::beans::PropertyValue > aSaveToFilterData
Definition: dbmgr.hxx:159
OUString sSaveToFilterOptions
Definition: dbmgr.hxx:158
const char * name
CharClass & GetAppCharClass()
Definition: init.cxx:705
sal_Int32 nIndex
#define LANGUAGE_SYSTEM
sal_uInt16 nPos
OUString sPrefix
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
constexpr OUStringLiteral aData
Definition: ww8scan.hxx:48
bool UCB_DeleteFile(const OUString &rURL)
Definition: swunohelper.cxx:59
bool CheckMailAddress(std::u16string_view aMailAddress)
void Dispose(const T &xInterface)
const LanguageTag & getLocale()
OString stripStart(const OString &rIn, char c)
Reference< XComponentContext > getProcessComponentContext()
css::uno::Sequence< css::beans::PropertyValue > InitPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
OOO_DLLPUBLIC_DBTOOLS OUString getFormattedValue(const css::uno::Reference< css::beans::XPropertySet > &_xColumn, const css::uno::Reference< css::util::XNumberFormatter > &xFormatter, const css::lang::Locale &_rLocale, const css::util::Date &rNullDate)
sal_Int32 getDefaultNumberFormat(const Reference< XPropertySet > &_xColumn, const Reference< XNumberFormatTypes > &_xTypes, const Locale &_rLocale)
Reference< XDataSource > getDataSource(const OUString &_rsTitleOrPath, const Reference< XComponentContext > &_rxContext)
int i
action
Dialog to specify the properties of date form field.
DBConnURIType GetDBunoType(const INetURLObject &rURL)
Definition: dbmgr.cxx:2542
DBConnURIType
Definition: dbmgr.hxx:482
long Long
OUString CreateTempURL(const OUString *pParent, bool bDirectory)
sal_Int32 toInt32(std::u16string_view rStr)
const Color aColNames[SC_RANGECOLORS]
constexpr auto SFX_INTERFACE_NONE
sal_uIntPtr sal_uLong
sal_Int32 nCommandType
Definition: swdbdata.hxx:32
OUString sDataSource
Definition: swdbdata.hxx:30
OUString sCommand
Definition: swdbdata.hxx:31
css::util::Date aNullDate
Definition: dbmgr.hxx:67
css::uno::Reference< css::util::XNumberFormatter > xFormatter
Definition: dbmgr.hxx:68
css::lang::Locale aLocale
Definition: dbmgr.hxx:69
std::unique_ptr< SwDSParam > pMergeData
Definition: dbmgr.cxx:306
std::mutex m_aAllEmailSendMutex
Definition: dbmgr.cxx:310
rtl::Reference< SwDBManager::ConnectionDisposedListener_Impl > m_xDisposeListener
Definition: dbmgr.cxx:308
rtl::Reference< SwDataSourceRemovedListener > m_xDataSourceRemovedListener
Definition: dbmgr.cxx:309
uno::Reference< mail::XMailMessage > m_xLastMessage
Definition: dbmgr.cxx:311
SwDBManager_Impl(SwDBManager &rDBManager)
Definition: dbmgr.cxx:313
VclPtr< AbstractMailMergeDlg > pMergeDialog
Definition: dbmgr.cxx:307
css::uno::Reference< css::util::XNumberFormatter > xFormatter
Definition: dbmgr.hxx:106
css::uno::Reference< css::sdbc::XResultSet > xResultSet
Definition: dbmgr.hxx:109
css::uno::Reference< css::sdbc::XStatement > xStatement
Definition: dbmgr.hxx:108
bool bEndOfDB
Definition: dbmgr.hxx:112
css::util::Date aNullDate
Definition: dbmgr.hxx:104
css::uno::Reference< css::sdbc::XConnection > xConnection
Definition: dbmgr.hxx:107
bool bScrollable
Definition: dbmgr.hxx:111
css::uno::Sequence< css::uno::Any > aSelection
Definition: dbmgr.hxx:110
bool HasValidRecord() const
Definition: dbmgr.hxx:133
tools::Long nSelectionIndex
Definition: dbmgr.hxx:113
sal_Int32 nDBRow
sw::mark::IMark * startPageInTarget
const svx::ODataAccessDescriptor & rDescriptor
Definition: dbmgr.hxx:143
const DBManagerOptions nMergeType
Definition: dbmgr.hxx:141
SwMailMergeConfigItem * pMailMergeConfigItem
Definition: dbmgr.hxx:220
bool bCreateSingleFile
Create a single or multiple results.
Definition: dbmgr.hxx:151
SwWrtShell & rSh
Definition: dbmgr.hxx:142
bool hasValue()
#define STR_SW_EVENT_FIELD_MERGE
Definition: swevent.hxx:30
#define STR_SW_EVENT_MAIL_MERGE
Definition: swevent.hxx:28
#define STR_SW_EVENT_MAIL_MERGE_END
Definition: swevent.hxx:29
#define STR_SW_EVENT_FIELD_MERGE_FINISHED
Definition: swevent.hxx:31
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:165
#define SW_MOD()
Definition: swmodule.hxx:256
#define DB_DELIM
Definition: swtypes.hxx:130
NEXT
ResultType type
RET_OK
RET_CANCEL
sal_Int32 nLength
NF_NUMBER_STANDARD