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
923 if (SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr())
924 {
925 pWorkWrtShell->GetViewOptions()->SetIdle( false );
926 pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called
927 SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
928 pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
929 pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );
930
931 // import print settings
932 const SwPrintData &rPrintData = pSourceDoc->getIDocumentDeviceAccess().getPrintData();
933 pWorkDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);
934 const JobSetup *pJobSetup = pSourceDoc->getIDocumentDeviceAccess().getJobsetup();
935 if (pJobSetup)
936 pWorkDoc->getIDocumentDeviceAccess().setJobsetup(*pJobSetup);
937
938 if( aType == WorkingDocType::TARGET )
939 {
940 assert( !ppDBManager );
941 pWorkDoc->SetInMailMerge( true );
942 pWorkWrtShell->SetLabelDoc( false );
943 }
944 else
945 {
946 // We have to swap the DBmanager of the new doc, so we also need input
947 assert(ppDBManager && *ppDBManager);
948 SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
949 pWorkDoc->SetDBManager( *ppDBManager );
950 *ppDBManager = pWorkDBManager;
951
952 if( aType == WorkingDocType::SOURCE )
953 {
954 // the GetDBData call constructs the data, if it's missing - kind of const...
955 pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() );
956 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
957 pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
958 pWorkDoc->getIDocumentState().ResetModified();
959 }
960 else
962 }
963
964 if( pView ) *pView = pWorkView;
965 if( pWrtShell ) *pWrtShell = pWorkWrtShell;
966 if( pDoc ) *pDoc = pWorkDoc;
967 }
968
969 return xWorkObjectShell.get();
970}
971
973 const SwMergeDescriptor &rMergeDescriptor,
974 const OUString &sFileURL, const OUString &sMailRecipient,
975 const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding,
976 const OUString &sAttachmentMimeType )
977{
979 if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() )
980 pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
981 pMessage->addRecipient( sMailRecipient );
982 pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );
983
984 OUStringBuffer sBody;
985 if( rMergeDescriptor.bSendAsAttachment )
986 {
987 sBody = rMergeDescriptor.sMailBody;
988 mail::MailAttachment aAttach;
989 aAttach.Data = new SwMailTransferable( sFileURL,
990 rMergeDescriptor.sAttachmentName, sAttachmentMimeType );
991 aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
992 pMessage->addAttachment( aAttach );
993 }
994 else
995 {
996 //read in the temporary file and use it as mail body
997 SfxMedium aMedium( sFileURL, StreamMode::READ );
998 SvStream* pInStream = aMedium.GetInStream();
999 assert( pInStream && "no output file created?" );
1000 if( !pInStream )
1001 return pMessage;
1002
1003 pInStream->SetStreamCharSet( sMailEncoding );
1004 OStringBuffer sLine;
1005 while ( pInStream->ReadLine( sLine ) )
1006 {
1007 sBody.append(OStringToOUString( sLine, sMailEncoding ) + "\n");
1008 }
1009 }
1010 pMessage->setSubject( rMergeDescriptor.sSubject );
1011 uno::Reference< datatransfer::XTransferable> xBody =
1012 new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType );
1013 pMessage->setBody( xBody );
1014
1015 for( const OUString& sCcRecipient : rMergeDescriptor.aCopiesTo )
1016 pMessage->addCcRecipient( sCcRecipient );
1017 for( const OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo )
1018 pMessage->addBccRecipient( sBccRecipient );
1019
1020 return pMessage;
1021}
1022
1024{
1026
1027public:
1029 : m_rDBManager( rDBManager ) {}
1030
1031 virtual void idle() override {}
1032
1033 virtual void mailDelivered( uno::Reference< mail::XMailMessage> xMessage ) override
1034 {
1035 std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1036 if ( m_rDBManager.m_pImpl->m_xLastMessage == xMessage )
1037 m_rDBManager.m_pImpl->m_xLastMessage.clear();
1038 }
1039
1041 uno::Reference< mail::XMailMessage>, const OUString& ) override
1042 {
1043 std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1045 m_rDBManager.m_pImpl->m_xLastMessage.clear();
1046 xMailDispatcher->stop();
1047 }
1048};
1049
1055 const SwMergeDescriptor& rMergeDescriptor)
1056{
1057 // deconstruct mail merge type for better readability.
1058 // uppercase naming is intentional!
1059 const bool bMT_EMAIL = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
1060 const bool bMT_SHELL = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
1061 const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER;
1062 const bool bMT_FILE = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;
1063
1064 //check if the doc is synchronized and contains at least one linked section
1065 const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1;
1066 const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE );
1067 const bool bIsMergeSilent = IsMergeSilent();
1068
1069 bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile;
1070 OUString sPrefix_ = rMergeDescriptor.sPrefix;
1071 if( bMT_EMAIL )
1072 {
1073 assert( !rMergeDescriptor.bPrefixIsFilename );
1074 assert(!bCheckSingleFile_);
1075 bCheckSingleFile_ = false;
1076 }
1077 else if( bMT_SHELL || bMT_PRINTER )
1078 {
1079 assert(bCheckSingleFile_);
1080 bCheckSingleFile_ = true;
1081 assert(sPrefix_.isEmpty());
1082 sPrefix_.clear();
1083 }
1084 const bool bCreateSingleFile = bCheckSingleFile_;
1085 const OUString sDescriptorPrefix = sPrefix_;
1086
1087 // Setup for dumping debugging documents
1088 static const sal_Int32 nMaxDumpDocs = []() {
1089 if (const char* sEnv = getenv("SW_DEBUG_MAILMERGE_DOCS"))
1090 return OUString(sEnv, strlen(sEnv), osl_getThreadTextEncoding()).toInt32();
1091 else
1092 return sal_Int32(0);
1093 }();
1094
1095 ::rtl::Reference< MailDispatcher > xMailDispatcher;
1097 OUString sMailBodyMimeType;
1098 rtl_TextEncoding sMailEncoding = ::osl_getThreadTextEncoding();
1099
1100 uno::Reference< beans::XPropertySet > xColumnProp;
1101 uno::Reference< beans::XPropertySet > xPasswordColumnProp;
1102
1103 // Check for (mandatory) email or (optional) filename column
1104 SwDBFormatData aColumnDBFormat;
1105 bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty();
1106 bool bPasswordColumnName = !rMergeDescriptor.sDBPasswordColumn.isEmpty();
1107
1108 if( ! bColumnName )
1109 {
1110 if( bMT_EMAIL )
1111 return false;
1112 }
1113 else
1114 {
1115 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
1116 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1117 if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) )
1118 return false;
1119 uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn );
1120 aCol >>= xColumnProp;
1121
1122 if(bPasswordColumnName)
1123 {
1124 aCol = xCols->getByName( rMergeDescriptor.sDBPasswordColumn );
1125 aCol >>= xPasswordColumnProp;
1126 }
1127
1128 aColumnDBFormat.xFormatter = m_pImpl->pMergeData->xFormatter;
1129 aColumnDBFormat.aNullDate = m_pImpl->pMergeData->aNullDate;
1130
1131 if( bMT_EMAIL )
1132 {
1133 // Reset internal mail accounting data
1134 m_pImpl->m_xLastMessage.clear();
1135
1136 xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) );
1137 xMailListener = new MailDispatcherListener_Impl( *this );
1138 xMailDispatcher->addListener( xMailListener );
1139 if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
1140 {
1141 sMailBodyMimeType = "text/html; charset=utf-8";
1142 sMailEncoding = RTL_TEXTENCODING_UTF8;
1143 }
1144 else
1145 sMailBodyMimeType = "text/plain; charset=UTF-8; format=flowed";
1146 }
1147 }
1148
1149 SwDocShell *pSourceDocSh = pSourceShell->GetView().GetDocShell();
1150
1151 // setup the output format
1152 std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
1154 SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
1155 const OUString* pStoreToFilterOptions = nullptr;
1156
1157 // if a save_to filter is set then use it - otherwise use the default
1158 if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
1159 {
1160 OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt");
1161 pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
1162 }
1163 else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
1164 {
1165 std::shared_ptr<const SfxFilter> pFilter =
1166 pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
1167 if(pFilter)
1168 {
1169 pStoreToFilter = pFilter;
1170 if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
1171 pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
1172 }
1173 }
1174 const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export";
1175 const bool bIsMultiFile = bMT_FILE && !bCreateSingleFile;
1176
1178
1179 // in case of creating a single resulting file this has to be created here
1180 SwView* pTargetView = rMergeDescriptor.pMailMergeConfigItem ?
1181 rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
1182 SwWrtShell* pTargetShell = nullptr;
1183 SfxObjectShellRef xTargetDocShell;
1184 rtl::Reference<SwDoc> pTargetDoc;
1185
1186 std::unique_ptr< utl::TempFileNamed > aTempFile;
1187 sal_uInt16 nStartingPageNo = 0;
1188
1189 std::shared_ptr<weld::GenericDialogController> xProgressDlg;
1190
1191 try
1192 {
1193 vcl::Window *pSourceWindow = nullptr;
1194 if( !bIsMergeSilent )
1195 {
1196 // construct the process dialog
1197 pSourceWindow = &pSourceShell->GetView().GetEditWin();
1198 if (!bMT_PRINTER)
1199 xProgressDlg = std::make_shared<CreateMonitor>(pSourceWindow->GetFrameWeld());
1200 else
1201 {
1202 xProgressDlg = std::make_shared<PrintMonitor>(pSourceWindow->GetFrameWeld());
1203 static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
1204 pSourceDocSh->GetTitle(22));
1205 }
1206 weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
1207 if (nResult == RET_CANCEL)
1208 MergeCancel();
1209 xProgressDlg.reset();
1210 });
1211
1213 }
1214
1215 if( bCreateSingleFile && !pTargetView )
1216 {
1217 // create a target docshell to put the merged document into
1218 xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
1219 *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
1220 nullptr, &pTargetView, &pTargetShell, &pTargetDoc );
1221
1222 if (nMaxDumpDocs)
1223 lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1224 }
1225 else if( pTargetView )
1226 {
1227 pTargetShell = pTargetView->GetWrtShellPtr();
1228 if (pTargetShell)
1229 {
1230 pTargetDoc = pTargetShell->GetDoc();
1231 xTargetDocShell = pTargetView->GetDocShell();
1232 }
1233 }
1234
1235 if( bCreateSingleFile )
1236 {
1237 // determine the page style and number used at the start of the source document
1238 pSourceShell->SttEndDoc(true);
1239 nStartingPageNo = pSourceShell->GetVirtPageNum();
1240 }
1241
1242 // Progress, to prohibit KeyInputs
1243 SfxProgress aProgress(pSourceDocSh, OUString(), 1);
1244
1245 // lock all dispatchers
1246 SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1247 while (pViewFrame)
1248 {
1249 pViewFrame->GetDispatcher()->Lock(true);
1250 pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1251 }
1252
1253 sal_Int32 nDocNo = 1;
1254
1255 // For single file mode, the number of pages in the target document so far, which is used
1256 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1257 // from the target doc would require repeated layouts of the doc, which is expensive, but
1258 // it can be manually computed from the source documents (for which we do layouts, so the page
1259 // count is known, and there is a blank page between each of them in the target document).
1260 int targetDocPageCount = 0;
1261
1262 if( !bIsMergeSilent && !bMT_PRINTER )
1263 {
1264 sal_Int32 nRecordCount = 1;
1265 lcl_getCountFromResultSet( nRecordCount, m_pImpl->pMergeData.get() );
1266
1267 // Synchronized docs don't auto-advance the record set, but there is a
1268 // "security" check, which will always advance the record set, if there
1269 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1270 sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
1272 if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
1273 --nRecordPerDoc;
1274 assert( nRecordPerDoc > 0 );
1275
1276 sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
1277 if ( 0 != nRecordCount % nRecordPerDoc )
1278 nMaxDocs += 1;
1279 static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
1280 }
1281
1282 sal_Int32 nStartRow, nEndRow;
1283 bool bFreezedLayouts = false;
1284 // to collect temporary email files
1285 std::vector< OUString> aFilesToRemove;
1286
1287 // The SfxObjectShell will be closed explicitly later but
1288 // it is more safe to use SfxObjectShellLock here
1289 SfxObjectShellLock xWorkDocSh;
1290 SwView* pWorkView = nullptr;
1291 rtl::Reference<SwDoc> pWorkDoc;
1292 SwDBManager* pWorkDocOrigDBManager = nullptr;
1293 SwWrtShell* pWorkShell = nullptr;
1294 bool bWorkDocInitialized = false;
1295
1296 do
1297 {
1298 nStartRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1299
1300 OUString sColumnData;
1301
1302 // Read the indicated data column, which should contain a valid mail
1303 // address or an optional file name
1304 if( bMT_EMAIL || bColumnName )
1305 {
1306 sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
1307 }
1308
1309 // create a new temporary file name - only done once in case of bCreateSingleFile
1310 if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
1311 {
1312 OUString sPrefix = sDescriptorPrefix;
1313 OUString sLeading;
1314
1315 //#i97667# if the name is from a database field then it will be used _as is_
1316 if( bColumnName && !bMT_EMAIL )
1317 {
1318 if (!sColumnData.isEmpty())
1319 sLeading = sColumnData;
1320 else
1321 sLeading = "_";
1322 }
1323 else
1324 {
1325 INetURLObject aEntry( sPrefix );
1326 sLeading = aEntry.GetBase();
1327 aEntry.removeSegment();
1329 }
1330
1331 OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
1332 aTempFile.reset( new utl::TempFileNamed(sLeading, sColumnData.isEmpty(), sExt, &sPrefix, true) );
1333 if( !aTempFile->IsValid() )
1334 {
1337 }
1338 }
1339
1340 uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions( rMergeDescriptor.aSaveToFilterData );
1341
1342 if( bMT_EMAIL || bPasswordColumnName )
1343 {
1344 OUString sPasswordColumnData = GetDBField( xPasswordColumnProp, aColumnDBFormat );
1345 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor.aSaveToFilterData, aSaveToFilterDataOptions, sPasswordColumnData );
1346 }
1347
1348 if( IsMergeOk() )
1349 {
1350 std::unique_ptr< INetURLObject > aTempFileURL;
1351 if( bNeedsTempFiles )
1352 aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
1353 if( !bIsMergeSilent ) {
1354 if( !bMT_PRINTER )
1355 static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
1356 else {
1357 PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
1358 pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
1359 ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
1360 OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number( nDocNo );
1361 pPrintMonDlg->m_xPrintInfo->set_label(sStat);
1362 }
1363 //TODO xProgressDlg->queue_draw();
1364 }
1365
1367
1368 // Create a copy of the source document and work with that one instead of the source.
1369 // If we're not in the single file mode (which requires modifying the document for the merging),
1370 // it is enough to do this just once. Currently PDF also has to be treated special.
1371 if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport || bIsMultiFile )
1372 {
1373 assert( !xWorkDocSh.Is());
1374 pWorkDocOrigDBManager = this;
1375 xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
1376 *pSourceShell, nullptr, &pWorkDocOrigDBManager,
1377 &pWorkView, &pWorkShell, &pWorkDoc );
1378 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1379 lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1380
1381 // #i69458# lock fields to prevent access to the result set while calculating layout
1382 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1383 // ExpFields update during printing, generation of preview, etc.
1384 pWorkShell->LockExpFields();
1385 pWorkShell->CalcLayout();
1386 // tdf#121168: Now force correct page descriptions applied to page frames. Without
1387 // this, e.g., page frames starting with sections could have page descriptions set
1388 // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1389 pWorkShell->GetViewOptions()->SetIdle(true);
1390 for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
1391 {
1392 aLayout->FreezeLayout(false);
1393 aLayout->AllCheckPageDescs();
1394 }
1395 }
1396
1397 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);
1398
1399 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1400 // database cursor movement on any other fields update, for example during
1401 // print preview and other operations
1402 if ( pWorkShell->IsExpFieldsLocked() )
1403 pWorkShell->UnlockExpFields();
1404 pWorkShell->SwViewShell::UpdateFields();
1405 pWorkShell->LockExpFields();
1406
1407 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);
1408
1409 // also emit MailMergeEvent on XInterface if possible
1410 const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
1411 if(pEvtSrc)
1412 {
1414 const_cast<SwXMailMerge*>(pEvtSrc) );
1415 text::MailMergeEvent aEvt( static_cast<text::XMailMergeBroadcaster*>(xRef.get()), xWorkDocSh->GetModel() );
1417 xRef->LaunchMailMergeEvent( aEvt );
1418 }
1419
1420 // working copy is merged - prepare final steps depending on merge options
1421
1422 if( bCreateSingleFile )
1423 {
1424 assert( pTargetShell && "no target shell available!" );
1425
1426 // prepare working copy and target to append
1427
1428 pWorkDoc->RemoveInvisibleContent();
1429 // remove of invisible content has influence on page count and so on fields for page count,
1430 // therefore layout has to be updated before fields are converted to text
1431 pWorkShell->CalcLayout();
1432 pWorkShell->ConvertFieldsToText();
1433 pWorkShell->SetNumberingRestart();
1434 if( bSynchronizedDoc )
1435 {
1436 lcl_RemoveSectionLinks( *pWorkShell );
1437 }
1438
1439 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1440 lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1441
1442 // append the working document to the target document
1443 if( targetDocPageCount % 2 == 1 )
1444 ++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
1445 SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
1446 nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
1447 targetDocPageCount += pWorkShell->GetPageCnt();
1448
1449 if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1450 lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1451
1452 if (bMT_SHELL)
1453 {
1454 SwDocMergeInfo aMergeInfo;
1455 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1456 aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
1458 ::sw::mark::InsertMode::New);
1459 aMergeInfo.nDBRow = nStartRow;
1460 rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
1461 }
1462 }
1463 else
1464 {
1465 assert( bNeedsTempFiles );
1466 assert( pWorkShell->IsExpFieldsLocked() );
1467
1468 if (bIsMultiFile && pWorkDoc->HasInvisibleContent())
1469 {
1470 pWorkDoc->RemoveInvisibleContent();
1471 pWorkShell->CalcLayout();
1472 pWorkShell->ConvertFieldsToText();
1473 }
1474
1475 // fields are locked, so it's fine to
1476 // restore the old / empty DB manager for save
1477 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1478
1479 // save merged document
1480 OUString sFileURL;
1481 if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
1482 &aSaveToFilterDataOptions, bIsPDFexport,
1483 xWorkDocSh, *pWorkShell, &sFileURL ) )
1484 {
1486 }
1487
1488 // back to the MM DB manager
1489 pWorkDoc->SetDBManager( this );
1490
1491 if( bMT_EMAIL && !IsMergeError() )
1492 {
1493 // schedule file for later removal
1494 aFilesToRemove.push_back( sFileURL );
1495
1496 if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
1497 {
1498 OSL_FAIL("invalid e-Mail address in database column");
1499 }
1500 else
1501 {
1502 uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
1503 rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
1504 sMailEncoding, pStoreToFilter->GetMimeType() );
1505 if( xMessage.is() )
1506 {
1507 std::unique_lock aGuard( m_pImpl->m_aAllEmailSendMutex );
1508 m_pImpl->m_xLastMessage.set( xMessage );
1509 xMailDispatcher->enqueueMailMessage( xMessage );
1510 if( !xMailDispatcher->isStarted() )
1511 xMailDispatcher->start();
1512 }
1513 }
1514 }
1515 }
1516 if( bCreateSingleFile || bIsPDFexport || bIsMultiFile)
1517 {
1518 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1519 pWorkDoc.clear();
1520 xWorkDocSh->DoClose();
1521 xWorkDocSh = nullptr;
1522 }
1523 }
1524
1525 bWorkDocInitialized = true;
1526 nDocNo++;
1527 nEndRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1528
1529 // Freeze the layouts of the target document after the first inserted
1530 // sub-document, to get the correct PageDesc.
1531 if(!bFreezedLayouts && bCreateSingleFile)
1532 {
1533 for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1534 aLayout->FreezeLayout(true);
1535 bFreezedLayouts = true;
1536 }
1537 } while( IsMergeOk() &&
1538 ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1539
1540 if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
1541 {
1542 // Unlock document fields after merge complete
1543 pWorkView->GetWrtShell().UnlockExpFields();
1544 }
1545
1546 if( !bCreateSingleFile )
1547 {
1548 if( bMT_PRINTER )
1550 if( !bIsPDFexport )
1551 {
1552 if (pWorkDoc)
1553 pWorkDoc->SetDBManager(pWorkDocOrigDBManager);
1554 if (xWorkDocSh.Is())
1555 xWorkDocSh->DoClose();
1556 }
1557 }
1558 else if( IsMergeOk() ) // && bCreateSingleFile
1559 {
1561
1562 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1563 // unique fly names, do it here once.
1564 pTargetDoc->SetInMailMerge(false);
1565 pTargetDoc->SetAllUniqueFlyNames();
1566
1567 // Unfreeze target document layouts and correct all PageDescs.
1568 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1569 pTargetShell->CalcLayout();
1570 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1571 pTargetShell->GetViewOptions()->SetIdle( true );
1572 pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
1573 for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1574 {
1575 aLayout->FreezeLayout(false);
1576 aLayout->AllCheckPageDescs();
1577 }
1578
1580
1581 if( IsMergeOk() && bMT_FILE )
1582 {
1583 // save merged document
1584 assert( aTempFile );
1585 INetURLObject aTempFileURL;
1586 if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
1587 aTempFileURL.SetURL( aTempFile->GetURL() );
1588 else
1589 {
1590 aTempFileURL.SetURL(sDescriptorPrefix);
1591 // remove the unneeded temporary file
1592 aTempFile->EnableKillingFile();
1593 }
1594 if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
1595 pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
1596 bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
1597 {
1599 }
1600 }
1601 else if( IsMergeOk() && bMT_PRINTER )
1602 {
1603 // print the target document
1604 uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
1605 lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
1606 pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
1607 }
1608 }
1609
1610 // we also show canceled documents, as long as there was no error
1611 if( !IsMergeError() && bMT_SHELL )
1612 // leave docshell available for caller (e.g. MM wizard)
1613 rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
1614 else if( xTargetDocShell.is() )
1615 xTargetDocShell->DoClose();
1616
1618
1619 if (xProgressDlg)
1620 {
1621 xProgressDlg->response(RET_OK);
1622 }
1623
1624 // unlock all dispatchers
1625 pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1626 while (pViewFrame)
1627 {
1628 pViewFrame->GetDispatcher()->Lock(false);
1629 pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1630 }
1631
1632 SW_MOD()->SetView(&pSourceShell->GetView());
1633
1634 if( xMailDispatcher.is() )
1635 {
1636 if( IsMergeOk() )
1637 {
1638 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1639 // if the main loop has been made thread-safe.
1640 AutoTimer aEmailDispatcherPollTimer("sw::SwDBManager aEmailDispatcherPollTimer");
1641 aEmailDispatcherPollTimer.SetTimeout( 500 );
1642 aEmailDispatcherPollTimer.Start();
1643 while( IsMergeOk() && m_pImpl->m_xLastMessage.is() && !Application::IsQuit())
1645 aEmailDispatcherPollTimer.Stop();
1646 }
1647 xMailDispatcher->stop();
1648 xMailDispatcher->shutdown();
1649 }
1650
1651 // remove the temporary files
1652 // has to be done after xMailDispatcher is finished, as mails may be
1653 // delivered as message attachments!
1654 for( const OUString &sFileURL : aFilesToRemove )
1655 SWUnoHelper::UCB_DeleteFile( sFileURL );
1656 }
1657 catch (const uno::Exception&)
1658 {
1659 if (xProgressDlg)
1660 {
1661 xProgressDlg->response(RET_CANCEL);
1662 }
1663 }
1664
1665 return !IsMergeError();
1666}
1667
1669{
1672}
1673
1674// determine the column's Numberformat and transfer to the forwarded Formatter,
1675// if applicable.
1677 const OUString& rTableName,
1678 const OUString& rColNm,
1679 SvNumberFormatter* pNFormatr,
1680 LanguageType nLanguage )
1681{
1682 sal_uLong nRet = 0;
1683 if(pNFormatr)
1684 {
1685 uno::Reference< sdbc::XDataSource> xSource;
1686 uno::Reference< sdbc::XConnection> xConnection;
1687 bool bUseMergeData = false;
1688 uno::Reference< sdbcx::XColumnsSupplier> xColsSupp;
1689 bool bDisposeConnection = false;
1690 if(m_pImpl->pMergeData &&
1691 ((m_pImpl->pMergeData->sDataSource == rDBName && m_pImpl->pMergeData->sCommand == rTableName) ||
1692 (rDBName.isEmpty() && rTableName.isEmpty())))
1693 {
1694 xConnection = m_pImpl->pMergeData->xConnection;
1695 xSource = SwDBManager::getDataSourceAsParent(xConnection,rDBName);
1696 bUseMergeData = true;
1697 xColsSupp.set(m_pImpl->pMergeData->xResultSet, css::uno::UNO_QUERY);
1698 }
1699 if(!xConnection.is())
1700 {
1702 aData.sDataSource = rDBName;
1703 aData.sCommand = rTableName;
1704 aData.nCommandType = -1;
1705 SwDSParam* pParam = FindDSData(aData, false);
1706 if(pParam && pParam->xConnection.is())
1707 {
1708 xConnection = pParam->xConnection;
1709 xColsSupp.set(pParam->xResultSet, css::uno::UNO_QUERY);
1710 }
1711 else
1712 {
1713 xConnection = RegisterConnection( rDBName );
1714 bDisposeConnection = true;
1715 }
1716 if(bUseMergeData)
1717 m_pImpl->pMergeData->xConnection = xConnection;
1718 }
1719 bool bDispose = !xColsSupp.is();
1720 if(bDispose)
1721 {
1722 xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1723 }
1724 if(xColsSupp.is())
1725 {
1726 uno::Reference<container::XNameAccess> xCols;
1727 try
1728 {
1729 xCols = xColsSupp->getColumns();
1730 }
1731 catch (const uno::Exception&)
1732 {
1733 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1734 }
1735 if(!xCols.is() || !xCols->hasByName(rColNm))
1736 return nRet;
1737 uno::Any aCol = xCols->getByName(rColNm);
1738 uno::Reference< beans::XPropertySet > xColumn;
1739 aCol >>= xColumn;
1740 nRet = GetColumnFormat(xSource, xConnection, xColumn, pNFormatr, nLanguage);
1741 if(bDispose)
1742 {
1743 ::comphelper::disposeComponent( xColsSupp );
1744 }
1745 if(bDisposeConnection)
1746 {
1747 ::comphelper::disposeComponent( xConnection );
1748 }
1749 }
1750 else
1752 }
1753 return nRet;
1754}
1755
1756sal_uLong SwDBManager::GetColumnFormat( uno::Reference< sdbc::XDataSource> const & xSource_in,
1757 uno::Reference< sdbc::XConnection> const & xConnection,
1758 uno::Reference< beans::XPropertySet> const & xColumn,
1759 SvNumberFormatter* pNFormatr,
1760 LanguageType nLanguage )
1761{
1762 auto xSource = xSource_in;
1763
1764 // set the NumberFormat in the doc if applicable
1765 sal_uLong nRet = 0;
1766
1767 if(!xSource.is())
1768 {
1769 uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
1770 if ( xChild.is() )
1771 xSource.set(xChild->getParent(), uno::UNO_QUERY);
1772 }
1773 if(xSource.is() && xConnection.is() && xColumn.is() && pNFormatr)
1774 {
1776 uno::Reference< util::XNumberFormats > xDocNumberFormats = pNumFormat->getNumberFormats();
1777 uno::Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, uno::UNO_QUERY);
1778
1779 css::lang::Locale aLocale( LanguageTag( nLanguage ).getLocale());
1780
1781 //get the number formatter of the data source
1782 uno::Reference<beans::XPropertySet> xSourceProps(xSource, uno::UNO_QUERY);
1783 uno::Reference< util::XNumberFormats > xNumberFormats;
1784 if(xSourceProps.is())
1785 {
1786 uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
1787 if(aFormats.hasValue())
1788 {
1789 uno::Reference<util::XNumberFormatsSupplier> xSuppl;
1790 aFormats >>= xSuppl;
1791 if(xSuppl.is())
1792 {
1793 xNumberFormats = xSuppl->getNumberFormats();
1794 }
1795 }
1796 }
1797 bool bUseDefault = true;
1798 try
1799 {
1800 uno::Any aFormatKey = xColumn->getPropertyValue("FormatKey");
1801 if(aFormatKey.hasValue())
1802 {
1803 sal_Int32 nFormat = 0;
1804 aFormatKey >>= nFormat;
1805 if(xNumberFormats.is())
1806 {
1807 try
1808 {
1809 uno::Reference<beans::XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat );
1810 uno::Any aFormatString = xNumProps->getPropertyValue("FormatString");
1811 uno::Any aLocaleVal = xNumProps->getPropertyValue("Locale");
1812 OUString sFormat;
1813 aFormatString >>= sFormat;
1814 lang::Locale aLoc;
1815 aLocaleVal >>= aLoc;
1816 nFormat = xDocNumberFormats->queryKey( sFormat, aLoc, false );
1817 if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFormat))
1818 nFormat = xDocNumberFormats->addNew( sFormat, aLoc );
1819 nRet = nFormat;
1820 bUseDefault = false;
1821 }
1822 catch (const uno::Exception&)
1823 {
1824 TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1825 }
1826 }
1827 }
1828 }
1829 catch(const uno::Exception&)
1830 {
1831 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1832 }
1833 if(bUseDefault)
1834 nRet = dbtools::getDefaultNumberFormat(xColumn, xDocNumberFormatTypes, aLocale);
1835 }
1836 return nRet;
1837}
1838
1839sal_Int32 SwDBManager::GetColumnType( const OUString& rDBName,
1840 const OUString& rTableName,
1841 const OUString& rColNm )
1842{
1843 sal_Int32 nRet = sdbc::DataType::SQLNULL;
1845 aData.sDataSource = rDBName;
1846 aData.sCommand = rTableName;
1847 aData.nCommandType = -1;
1848 SwDSParam* pParam = FindDSData(aData, false);
1849 uno::Reference< sdbc::XConnection> xConnection;
1850 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp;
1851 bool bDispose = false;
1852 if(pParam && pParam->xConnection.is())
1853 {
1854 xConnection = pParam->xConnection;
1855 xColsSupp.set( pParam->xResultSet, uno::UNO_QUERY );
1856 }
1857 else
1858 {
1859 xConnection = RegisterConnection( rDBName );
1860 }
1861 if( !xColsSupp.is() )
1862 {
1863 xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1864 bDispose = true;
1865 }
1866 if(xColsSupp.is())
1867 {
1868 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1869 if(xCols->hasByName(rColNm))
1870 {
1871 uno::Any aCol = xCols->getByName(rColNm);
1872 uno::Reference<beans::XPropertySet> xCol;
1873 aCol >>= xCol;
1874 uno::Any aType = xCol->getPropertyValue("Type");
1875 aType >>= nRet;
1876 }
1877 if(bDispose)
1878 ::comphelper::disposeComponent( xColsSupp );
1879 }
1880 return nRet;
1881}
1882
1883uno::Reference< sdbc::XConnection> SwDBManager::GetConnection(const OUString& rDataSource,
1884 uno::Reference<sdbc::XDataSource>& rxSource, const SwView *pView)
1885{
1886 uno::Reference< sdbc::XConnection> xConnection;
1887 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1888 try
1889 {
1890 uno::Reference<sdb::XCompletedConnection> xComplConnection(dbtools::getDataSource(rDataSource, xContext), uno::UNO_QUERY);
1891 if ( xComplConnection.is() )
1892 {
1893 rxSource.set(xComplConnection, uno::UNO_QUERY);
1894 weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
1895 uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(xContext, pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
1896 xConnection = xComplConnection->connectWithCompletion( xHandler );
1897 }
1898 }
1899 catch(const uno::Exception&)
1900 {
1901 }
1902
1903 return xConnection;
1904}
1905
1906uno::Reference< sdbcx::XColumnsSupplier> SwDBManager::GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection,
1907 const OUString& rTableOrQuery,
1908 SwDBSelect eTableOrQuery)
1909{
1910 uno::Reference< sdbcx::XColumnsSupplier> xRet;
1911 try
1912 {
1913 if(eTableOrQuery == SwDBSelect::UNKNOWN)
1914 {
1915 //search for a table with the given command name
1916 uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
1917 if(xTSupplier.is())
1918 {
1919 uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
1920 eTableOrQuery = xTables->hasByName(rTableOrQuery) ?
1922 }
1923 }
1924 sal_Int32 nCommandType = SwDBSelect::TABLE == eTableOrQuery ?
1925 sdb::CommandType::TABLE : sdb::CommandType::QUERY;
1926 uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
1927 uno::Reference<sdbc::XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY);
1928
1929 OUString sDataSource;
1930 uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection, sDataSource);
1931 uno::Reference<beans::XPropertySet> xSourceProperties(xSource, uno::UNO_QUERY);
1932 if(xSourceProperties.is())
1933 {
1934 xSourceProperties->getPropertyValue("Name") >>= sDataSource;
1935 }
1936
1937 uno::Reference<beans::XPropertySet> xRowProperties(xRowSet, uno::UNO_QUERY);
1938 xRowProperties->setPropertyValue("DataSourceName", uno::Any(sDataSource));
1939 xRowProperties->setPropertyValue("Command", uno::Any(rTableOrQuery));
1940 xRowProperties->setPropertyValue("CommandType", uno::Any(nCommandType));
1941 xRowProperties->setPropertyValue("FetchSize", uno::Any(sal_Int32(10)));
1942 xRowProperties->setPropertyValue("ActiveConnection", uno::Any(xConnection));
1943 xRowSet->execute();
1944 xRet.set( xRowSet, uno::UNO_QUERY );
1945 }
1946 catch (const uno::Exception&)
1947 {
1948 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1949 }
1950
1951 return xRet;
1952}
1953
1954OUString SwDBManager::GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps,
1955 const SwDBFormatData& rDBFormatData,
1956 double* pNumber)
1957{
1958 uno::Reference< sdb::XColumn > xColumn(xColumnProps, uno::UNO_QUERY);
1959 OUString sRet;
1960 assert( xColumn.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1961 if(!xColumn.is())
1962 return sRet;
1963
1964 uno::Any aType = xColumnProps->getPropertyValue("Type");
1965 sal_Int32 eDataType = sdbc::DataType::SQLNULL;
1966 aType >>= eDataType;
1967 switch(eDataType)
1968 {
1969 case sdbc::DataType::CHAR:
1970 case sdbc::DataType::VARCHAR:
1971 case sdbc::DataType::LONGVARCHAR:
1972 try
1973 {
1974 sRet = xColumn->getString();
1975 sRet = sRet.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1976 }
1977 catch(const sdbc::SQLException&)
1978 {
1979 }
1980 break;
1981 case sdbc::DataType::BIT:
1982 case sdbc::DataType::BOOLEAN:
1983 case sdbc::DataType::TINYINT:
1984 case sdbc::DataType::SMALLINT:
1985 case sdbc::DataType::INTEGER:
1986 case sdbc::DataType::BIGINT:
1987 case sdbc::DataType::FLOAT:
1988 case sdbc::DataType::REAL:
1989 case sdbc::DataType::DOUBLE:
1990 case sdbc::DataType::NUMERIC:
1991 case sdbc::DataType::DECIMAL:
1992 case sdbc::DataType::DATE:
1993 case sdbc::DataType::TIME:
1994 case sdbc::DataType::TIMESTAMP:
1995 {
1996
1997 try
1998 {
2000 xColumnProps,
2001 rDBFormatData.xFormatter,
2002 rDBFormatData.aLocale,
2003 rDBFormatData.aNullDate);
2004 if (pNumber)
2005 {
2006 double fVal = xColumn->getDouble();
2007 if(!xColumn->wasNull())
2008 {
2009 *pNumber = fVal;
2010 }
2011 }
2012 }
2013 catch (const uno::Exception&)
2014 {
2015 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
2016 }
2017
2018 }
2019 break;
2020 }
2021
2022 return sRet;
2023}
2024
2025// checks if a desired data source table or query is open
2026bool SwDBManager::IsDataSourceOpen(const OUString& rDataSource,
2027 const OUString& rTableOrQuery, bool bMergeShell)
2028{
2029 if(m_pImpl->pMergeData)
2030 {
2031 return ((rDataSource == m_pImpl->pMergeData->sDataSource
2032 && rTableOrQuery == m_pImpl->pMergeData->sCommand)
2033 || (rDataSource.isEmpty() && rTableOrQuery.isEmpty()))
2034 && m_pImpl->pMergeData->xResultSet.is();
2035 }
2036 else if(!bMergeShell)
2037 {
2039 aData.sDataSource = rDataSource;
2040 aData.sCommand = rTableOrQuery;
2041 aData.nCommandType = -1;
2042 SwDSParam* pFound = FindDSData(aData, false);
2043 return (pFound && pFound->xResultSet.is());
2044 }
2045 return false;
2046}
2047
2048// read column data at a specified position
2049bool SwDBManager::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName,
2050 const OUString& rColumnName, sal_uInt32 nAbsRecordId,
2051 LanguageType nLanguage,
2052 OUString& rResult, double* pNumber)
2053{
2054 bool bRet = false;
2055 SwDSParam* pFound = nullptr;
2056 //check if it's the merge data source
2057 if(m_pImpl->pMergeData &&
2058 rSourceName == m_pImpl->pMergeData->sDataSource &&
2059 rTableName == m_pImpl->pMergeData->sCommand)
2060 {
2061 pFound = m_pImpl->pMergeData.get();
2062 }
2063 else
2064 {
2066 aData.sDataSource = rSourceName;
2067 aData.sCommand = rTableName;
2068 aData.nCommandType = -1;
2069 pFound = FindDSData(aData, false);
2070 }
2071 if (!pFound)
2072 return false;
2073 //check validity of supplied record Id
2074 if(pFound->aSelection.hasElements())
2075 {
2076 //the destination has to be an element of the selection
2077 bool bFound = std::any_of(std::cbegin(pFound->aSelection), std::cend(pFound->aSelection),
2078 [nAbsRecordId](const uno::Any& rSelection) {
2079 sal_Int32 nSelection = 0;
2080 rSelection >>= nSelection;
2081 return nSelection == static_cast<sal_Int32>(nAbsRecordId);
2082 });
2083 if(!bFound)
2084 return false;
2085 }
2086 if( pFound->HasValidRecord() )
2087 {
2088 sal_Int32 nOldRow = 0;
2089 try
2090 {
2091 nOldRow = pFound->xResultSet->getRow();
2092 }
2093 catch(const uno::Exception&)
2094 {
2095 return false;
2096 }
2097 //position to the desired index
2098 bool bMove = true;
2099 if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2100 bMove = lcl_MoveAbsolute(pFound, nAbsRecordId);
2101 if(bMove)
2102 bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber);
2103 if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2104 lcl_MoveAbsolute(pFound, nOldRow);
2105 }
2106 return bRet;
2107}
2108
2109// reads the column data at the current position
2110bool SwDBManager::GetMergeColumnCnt(const OUString& rColumnName, LanguageType nLanguage,
2111 OUString &rResult, double *pNumber)
2112{
2113 if( !IsValidMergeRecord() )
2114 {
2115 rResult.clear();
2116 return false;
2117 }
2118
2119 bool bRet = lcl_GetColumnCnt(m_pImpl->pMergeData.get(), rColumnName, nLanguage, rResult, pNumber);
2120 return bRet;
2121}
2122
2124{
2125 assert( m_pImpl->pMergeData && m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2126 return lcl_ToNextRecord( m_pImpl->pMergeData.get() );
2127}
2128
2130 LanguageType nLanguage, SwCalc &rCalc )
2131{
2132 if( !IsValidMergeRecord() )
2133 return false;
2134
2135 uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
2136 if( !xColsSupp.is() )
2137 return false;
2138
2139 {
2140 uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
2141 const uno::Sequence<OUString> aColNames = xCols->getElementNames();
2142 OUString aString;
2143
2144 // add the "record number" variable, as SwCalc::VarLook would.
2145 rCalc.VarChange( GetAppCharClass().lowercase(
2147
2148 for( const OUString& rColName : aColNames )
2149 {
2150 // get the column type
2151 sal_Int32 nColumnType = sdbc::DataType::SQLNULL;
2152 uno::Any aCol = xCols->getByName( rColName );
2153 uno::Reference<beans::XPropertySet> xColumnProps;
2154 aCol >>= xColumnProps;
2155 uno::Any aType = xColumnProps->getPropertyValue( "Type" );
2156 aType >>= nColumnType;
2157 double aNumber = DBL_MAX;
2158
2159 lcl_GetColumnCnt( m_pImpl->pMergeData.get(), xColumnProps, nLanguage, aString, &aNumber );
2160
2161 sal_uInt32 nFormat = GetColumnFormat( m_pImpl->pMergeData->sDataSource,
2162 m_pImpl->pMergeData->sCommand,
2163 rColName, pDocFormatter, nLanguage );
2164 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2165 bool colIsNumber = aNumber != DBL_MAX;
2166 bool bValidValue = SwDBField::FormatValue( pDocFormatter, aString, nFormat,
2167 aNumber, nColumnType );
2168 if( colIsNumber )
2169 {
2170 if( bValidValue )
2171 {
2172 SwSbxValue aValue;
2173 aValue.PutDouble( aNumber );
2174 aValue.SetDBvalue( true );
2175 SAL_INFO( "sw.ui", "'" << rColName << "': " << aNumber << " / " << aString );
2176 rCalc.VarChange( rColName, aValue );
2177 }
2178 }
2179 else
2180 {
2181 SwSbxValue aValue;
2182 aValue.PutString( aString );
2183 aValue.SetDBvalue( true );
2184 SAL_INFO( "sw.ui", "'" << rColName << "': " << aString );
2185 rCalc.VarChange( rColName, aValue );
2186 }
2187 }
2188 }
2189
2190 return true;
2191}
2192
2194 const OUString& rDataSource, const OUString& rCommand)
2195{
2196 SwDSParam* pFound = nullptr;
2197 if(m_pImpl->pMergeData &&
2198 rDataSource == m_pImpl->pMergeData->sDataSource &&
2199 rCommand == m_pImpl->pMergeData->sCommand)
2200 {
2201 pFound = m_pImpl->pMergeData.get();
2202 }
2203 else
2204 {
2206 aData.sDataSource = rDataSource;
2207 aData.sCommand = rCommand;
2208 aData.nCommandType = -1;
2209 pFound = FindDSData(aData, false);
2210 }
2211 lcl_ToNextRecord( pFound );
2212}
2213
2214static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action )
2215{
2216 bool bRet = true;
2217
2218 assert( SwDBNextRecord::NEXT == action ||
2219 (SwDBNextRecord::FIRST == action && pParam) );
2220 if( nullptr == pParam )
2221 return false;
2222
2223 if( action == SwDBNextRecord::FIRST )
2224 {
2225 pParam->nSelectionIndex = 0;
2226 pParam->bEndOfDB = false;
2227 }
2228
2229 if( !pParam->HasValidRecord() )
2230 return false;
2231
2232 try
2233 {
2234 if( pParam->aSelection.hasElements() )
2235 {
2236 if( pParam->nSelectionIndex >= pParam->aSelection.getLength() )
2237 pParam->bEndOfDB = true;
2238 else
2239 {
2240 sal_Int32 nPos = 0;
2241 pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
2242 pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
2243 }
2244 }
2245 else if( action == SwDBNextRecord::FIRST )
2246 {
2247 pParam->bEndOfDB = !pParam->xResultSet->first();
2248 }
2249 else
2250 {
2251 sal_Int32 nBefore = pParam->xResultSet->getRow();
2252 pParam->bEndOfDB = !pParam->xResultSet->next();
2253 if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow() )
2254 {
2255 // next returned true but it didn't move
2256 ::dbtools::throwFunctionSequenceException( pParam->xResultSet );
2257 }
2258 }
2259
2260 ++pParam->nSelectionIndex;
2261 bRet = !pParam->bEndOfDB;
2262 }
2263 catch( const uno::Exception & )
2264 {
2265 // we allow merging with empty databases, so don't warn on init
2266 TOOLS_WARN_EXCEPTION_IF(action == SwDBNextRecord::NEXT,
2267 "sw.mailmerge", "exception in ToNextRecord()");
2268 pParam->bEndOfDB = true;
2269 bRet = false;
2270 }
2271 return bRet;
2272}
2273
2274// synchronized labels contain a next record field at their end
2275// to assure that the next page can be created in mail merge
2276// the cursor position must be validated
2278{
2279 return( m_pImpl->pMergeData && m_pImpl->pMergeData->HasValidRecord() );
2280}
2281
2283{
2284 sal_uInt32 nRet = 0;
2285 assert( m_pImpl->pMergeData &&
2286 m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2287 if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is())
2288 return 0;
2289 try
2290 {
2291 nRet = m_pImpl->pMergeData->xResultSet->getRow();
2292 }
2293 catch(const uno::Exception&)
2294 {
2295 }
2296 return nRet;
2297}
2298
2299bool SwDBManager::ToRecordId(sal_Int32 nSet)
2300{
2301 assert( m_pImpl->pMergeData &&
2302 m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2303 if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is()|| nSet < 0)
2304 return false;
2305 bool bRet = false;
2306 sal_Int32 nAbsPos = nSet;
2307 assert(nAbsPos >= 0);
2308 bRet = lcl_MoveAbsolute(m_pImpl->pMergeData.get(), nAbsPos);
2309 m_pImpl->pMergeData->bEndOfDB = !bRet;
2310 return bRet;
2311}
2312
2313bool SwDBManager::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery)
2314{
2316 aData.sDataSource = rDataSource;
2317 aData.sCommand = rTableOrQuery;
2318 aData.nCommandType = -1;
2319
2320 SwDSParam* pFound = FindDSData(aData, true);
2321 if(pFound->xResultSet.is())
2322 return true;
2323 SwDSParam* pParam = FindDSConnection(rDataSource, false);
2324 if(pParam && pParam->xConnection.is())
2325 pFound->xConnection = pParam->xConnection;
2326 if(pFound->xConnection.is())
2327 {
2328 try
2329 {
2330 uno::Reference< sdbc::XDatabaseMetaData > xMetaData = pFound->xConnection->getMetaData();
2331 try
2332 {
2333 pFound->bScrollable = xMetaData
2334 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE));
2335 }
2336 catch(const uno::Exception&)
2337 {
2338 // DB driver may not be ODBC 3.0 compliant
2339 pFound->bScrollable = true;
2340 }
2341 pFound->xStatement = pFound->xConnection->createStatement();
2342 OUString aQuoteChar = xMetaData->getIdentifierQuoteString();
2343 OUString sStatement = "SELECT * FROM " + aQuoteChar + rTableOrQuery + aQuoteChar;
2344 pFound->xResultSet = pFound->xStatement->executeQuery( sStatement );
2345
2346 //after executeQuery the cursor must be positioned
2347 pFound->bEndOfDB = !pFound->xResultSet->next();
2348 ++pFound->nSelectionIndex;
2349 }
2350 catch (const uno::Exception&)
2351 {
2352 pFound->xResultSet = nullptr;
2353 pFound->xStatement = nullptr;
2354 pFound->xConnection = nullptr;
2355 }
2356 }
2357 return pFound->xResultSet.is();
2358}
2359
2360uno::Reference< sdbc::XConnection> const & SwDBManager::RegisterConnection(OUString const& rDataSource)
2361{
2362 SwDSParam* pFound = SwDBManager::FindDSConnection(rDataSource, true);
2363 uno::Reference< sdbc::XDataSource> xSource;
2364 if(!pFound->xConnection.is())
2365 {
2366 SwView* pView = (m_pDoc && m_pDoc->GetDocShell()) ? m_pDoc->GetDocShell()->GetView() : nullptr;
2367 pFound->xConnection = SwDBManager::GetConnection(rDataSource, xSource, pView);
2368 try
2369 {
2370 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2371 if(xComponent.is())
2372 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2373 }
2374 catch(const uno::Exception&)
2375 {
2376 }
2377 }
2378 return pFound->xConnection;
2379}
2380
2382 const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType)
2383{
2384 sal_uInt32 nRet = 0xffffffff;
2385 //check for merge data source first
2386 if(m_pImpl->pMergeData &&
2387 ((rDataSource == m_pImpl->pMergeData->sDataSource &&
2388 rTableOrQuery == m_pImpl->pMergeData->sCommand) ||
2389 (rDataSource.isEmpty() && rTableOrQuery.isEmpty())) &&
2390 (nCommandType == -1 || nCommandType == m_pImpl->pMergeData->nCommandType) &&
2391 m_pImpl->pMergeData->xResultSet.is())
2392 {
2393 nRet = GetSelectedRecordId();
2394 }
2395 else
2396 {
2398 aData.sDataSource = rDataSource;
2399 aData.sCommand = rTableOrQuery;
2400 aData.nCommandType = nCommandType;
2401 SwDSParam* pFound = FindDSData(aData, false);
2402 if(pFound && pFound->xResultSet.is())
2403 {
2404 try
2405 { //if a selection array is set the current row at the result set may not be set yet
2406 if(pFound->aSelection.hasElements())
2407 {
2408 sal_Int32 nSelIndex = pFound->nSelectionIndex;
2409 if(nSelIndex >= pFound->aSelection.getLength())
2410 nSelIndex = pFound->aSelection.getLength() -1;
2411 pFound->aSelection.getConstArray()[nSelIndex] >>= nRet;
2412
2413 }
2414 else
2415 nRet = pFound->xResultSet->getRow();
2416 }
2417 catch(const uno::Exception&)
2418 {
2419 }
2420 }
2421 }
2422 return nRet;
2423}
2424
2425// close all data sources - after fields were updated
2426void SwDBManager::CloseAll(bool bIncludingMerge)
2427{
2428 //the only thing done here is to reset the selection index
2429 //all connections stay open
2430 for (auto & pParam : m_DataSourceParams)
2431 {
2432 if (bIncludingMerge || pParam.get() != m_pImpl->pMergeData.get())
2433 {
2434 pParam->nSelectionIndex = 0;
2435 pParam->bEndOfDB = false;
2436 try
2437 {
2438 if(!m_bInMerge && pParam->xResultSet.is())
2439 pParam->xResultSet->first();
2440 }
2441 catch(const uno::Exception&)
2442 {}
2443 }
2444 }
2445}
2446
2447SwDSParam* SwDBManager::FindDSData(const SwDBData& rData, bool bCreate)
2448{
2449 //prefer merge data if available
2450 if(m_pImpl->pMergeData &&
2451 ((rData.sDataSource == m_pImpl->pMergeData->sDataSource &&
2452 rData.sCommand == m_pImpl->pMergeData->sCommand) ||
2453 (rData.sDataSource.isEmpty() && rData.sCommand.isEmpty())) &&
2454 (rData.nCommandType == -1 || rData.nCommandType == m_pImpl->pMergeData->nCommandType ||
2455 (bCreate && m_pImpl->pMergeData->nCommandType == -1)))
2456 {
2457 return m_pImpl->pMergeData.get();
2458 }
2459
2460 SwDSParam* pFound = nullptr;
2461 for (size_t nPos = m_DataSourceParams.size(); nPos; nPos--)
2462 {
2463 SwDSParam* pParam = m_DataSourceParams[nPos - 1].get();
2464 if(rData.sDataSource == pParam->sDataSource &&
2465 rData.sCommand == pParam->sCommand &&
2466 (rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType ||
2467 (bCreate && pParam->nCommandType == -1)))
2468 {
2469 // calls from the calculator may add a connection with an invalid commandtype
2470 //later added "real" data base connections have to re-use the already available
2471 //DSData and set the correct CommandType
2472 if(bCreate && pParam->nCommandType == -1)
2473 pParam->nCommandType = rData.nCommandType;
2474 pFound = pParam;
2475 break;
2476 }
2477 }
2478 if(bCreate && !pFound)
2479 {
2480 pFound = new SwDSParam(rData);
2481 m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2482 try
2483 {
2484 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2485 if(xComponent.is())
2486 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2487 }
2488 catch(const uno::Exception&)
2489 {
2490 }
2491 }
2492 return pFound;
2493}
2494
2495SwDSParam* SwDBManager::FindDSConnection(const OUString& rDataSource, bool bCreate)
2496{
2497 //prefer merge data if available
2498 if(m_pImpl->pMergeData && rDataSource == m_pImpl->pMergeData->sDataSource )
2499 {
2500 SetAsUsed(rDataSource);
2501 return m_pImpl->pMergeData.get();
2502 }
2503 SwDSParam* pFound = nullptr;
2504 for (const auto & pParam : m_DataSourceParams)
2505 {
2506 if(rDataSource == pParam->sDataSource)
2507 {
2508 SetAsUsed(rDataSource);
2509 pFound = pParam.get();
2510 break;
2511 }
2512 }
2513 if(bCreate && !pFound)
2514 {
2516 aData.sDataSource = rDataSource;
2517 pFound = new SwDSParam(aData);
2518 SetAsUsed(rDataSource);
2519 m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2520 try
2521 {
2522 uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2523 if(xComponent.is())
2524 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2525 }
2526 catch(const uno::Exception&)
2527 {
2528 }
2529 }
2530 return pFound;
2531}
2532
2534{
2535 return SW_MOD()->GetDBConfig()->GetAddressSource();
2536}
2537
2539{
2540 uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2541 uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2542 return xDBContext->getElementNames();
2543}
2544
2545namespace sw
2546{
2548{
2549 OUString sExt(rURL.GetFileExtension());
2551
2552 if (sExt == "odb")
2553 {
2555 }
2556 else if (sExt.equalsIgnoreAsciiCase("sxc")
2557 || sExt.equalsIgnoreAsciiCase("ods")
2558 || sExt.equalsIgnoreAsciiCase("xls")
2559 || sExt.equalsIgnoreAsciiCase("xlsx"))
2560 {
2562 }
2563 else if (sExt.equalsIgnoreAsciiCase("sxw") || sExt.equalsIgnoreAsciiCase("odt") || sExt.equalsIgnoreAsciiCase("doc") || sExt.equalsIgnoreAsciiCase("docx"))
2564 {
2566 }
2567 else if (sExt.equalsIgnoreAsciiCase("dbf"))
2568 {
2570 }
2571 else if (sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt"))
2572 {
2574 }
2575#ifdef _WIN32
2576 else if (sExt.equalsIgnoreAsciiCase("mdb") || sExt.equalsIgnoreAsciiCase("mde"))
2577 {
2579 }
2580 else if (sExt.equalsIgnoreAsciiCase("accdb") || sExt.equalsIgnoreAsciiCase("accde"))
2581 {
2583 }
2584#endif
2585 return type;
2586}
2587}
2588
2589namespace
2590{
2591uno::Any GetDBunoURI(const INetURLObject &rURL, DBConnURIType& rType)
2592{
2593 uno::Any aURLAny;
2594
2595 if (rType == DBConnURIType::UNKNOWN)
2596 rType = GetDBunoType(rURL);
2597
2598 switch (rType) {
2599 case DBConnURIType::UNKNOWN:
2600 case DBConnURIType::ODB:
2601 break;
2602 case DBConnURIType::CALC:
2603 {
2604 OUString sDBURL = "sdbc:calc:" +
2606 aURLAny <<= sDBURL;
2607 }
2608 break;
2609 case DBConnURIType::WRITER:
2610 {
2611 OUString sDBURL = "sdbc:writer:" +
2613 aURLAny <<= sDBURL;
2614 }
2615 break;
2616 case DBConnURIType::DBASE:
2617 {
2618 INetURLObject aUrlTmp(rURL);
2619 aUrlTmp.removeSegment();
2620 aUrlTmp.removeFinalSlash();
2621 OUString sDBURL = "sdbc:dbase:" +
2622 aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2623 aURLAny <<= sDBURL;
2624 }
2625 break;
2626 case DBConnURIType::FLAT:
2627 {
2628 INetURLObject aUrlTmp(rURL);
2629 aUrlTmp.removeSegment();
2630 aUrlTmp.removeFinalSlash();
2631 OUString sDBURL = "sdbc:flat:" +
2632 //only the 'path' has to be added
2633 aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2634 aURLAny <<= sDBURL;
2635 }
2636 break;
2637 case DBConnURIType::MSJET:
2638#ifdef _WIN32
2639 {
2640 OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL.PathToFileName());
2641 aURLAny <<= sDBURL;
2642 }
2643#endif
2644 break;
2645 case DBConnURIType::MSACE:
2646#ifdef _WIN32
2647 {
2648 OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL.PathToFileName());
2649 aURLAny <<= sDBURL;
2650 }
2651#endif
2652 break;
2653 }
2654 return aURLAny;
2655}
2656
2658OUString getOwnURL(SfxObjectShell const * pDocShell)
2659{
2660 OUString aRet;
2661
2662 if (!pDocShell)
2663 return aRet;
2664
2665 const INetURLObject& rURLObject = pDocShell->GetMedium()->GetURLObject();
2667 return aRet;
2668}
2669
2676OUString LoadAndRegisterDataSource_Impl(DBConnURIType type, const uno::Reference< beans::XPropertySet > *pSettings,
2677 const INetURLObject &rURL, const OUString *pDestDir, SfxObjectShell* pDocShell)
2678{
2679 OUString sExt(rURL.GetFileExtension());
2680 uno::Any aTableFilterAny;
2681 uno::Any aSuppressVersionsAny;
2682 uno::Any aInfoAny;
2683 bool bStore = true;
2684 OUString sFind;
2685
2686 uno::Any aURLAny = GetDBunoURI(rURL, type);
2687 switch (type) {
2688 case DBConnURIType::UNKNOWN:
2689 case DBConnURIType::CALC:
2690 case DBConnURIType::WRITER:
2691 break;
2692 case DBConnURIType::ODB:
2693 bStore = false;
2694 break;
2695 case DBConnURIType::FLAT:
2696 case DBConnURIType::DBASE:
2697 //set the filter to the file name without extension
2698 {
2699 uno::Sequence<OUString> aFilters { rURL.getBase(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset) };
2700 aTableFilterAny <<= aFilters;
2701 }
2702 break;
2703 case DBConnURIType::MSJET:
2704 case DBConnURIType::MSACE:
2705 aSuppressVersionsAny <<= true;
2706 break;
2707 }
2708
2709 try
2710 {
2711 uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
2712 uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2713
2714 OUString sNewName = rURL.getName(
2716 sal_Int32 nExtLen = sExt.getLength();
2717 sNewName = sNewName.replaceAt(sNewName.getLength() - nExtLen - 1, nExtLen + 1, u"");
2718
2719 //find a unique name if sNewName already exists
2720 sFind = sNewName;
2721 sal_Int32 nIndex = 0;
2722 while (xDBContext->hasByName(sFind))
2723 sFind = sNewName + OUString::number(++nIndex);
2724
2725 uno::Reference<uno::XInterface> xNewInstance;
2726 if (!bStore)
2727 {
2728 //odb-file
2729 uno::Any aDataSource = xDBContext->getByName(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
2730 aDataSource >>= xNewInstance;
2731 }
2732 else
2733 {
2734 xNewInstance = xDBContext->createInstance();
2735 uno::Reference<beans::XPropertySet> xDataProperties(xNewInstance, uno::UNO_QUERY);
2736
2737 if (aURLAny.hasValue())
2738 xDataProperties->setPropertyValue("URL", aURLAny);
2739 if (aTableFilterAny.hasValue())
2740 xDataProperties->setPropertyValue("TableFilter", aTableFilterAny);
2741 if (aSuppressVersionsAny.hasValue())
2742 xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny);
2743 if (aInfoAny.hasValue())
2744 xDataProperties->setPropertyValue("Info", aInfoAny);
2745
2746 if (DBConnURIType::FLAT == type && pSettings)
2747 {
2748 uno::Any aSettings = xDataProperties->getPropertyValue("Settings");
2749 uno::Reference < beans::XPropertySet > xDSSettings;
2750 aSettings >>= xDSSettings;
2751 ::comphelper::copyProperties(*pSettings, xDSSettings);
2752 xDSSettings->setPropertyValue("Extension", uno::Any(sExt));
2753 }
2754
2755 uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, uno::UNO_QUERY_THROW);
2756 uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), uno::UNO_QUERY_THROW);
2757 OUString aOwnURL = getOwnURL(pDocShell);
2758 if (aOwnURL.isEmpty())
2759 {
2760 // Cannot embed, as embedded data source would need the URL of the parent document.
2761 OUString sHomePath(SvtPathOptions().GetWorkPath());
2762 const OUString sTmpName = utl::CreateTempURL(sNewName, true, u".odb", pDestDir ? pDestDir : &sHomePath);
2763 xStore->storeAsURL(sTmpName, uno::Sequence<beans::PropertyValue>());
2764 }
2765 else
2766 {
2767 // Embed.
2768 OUString aStreamRelPath = "EmbeddedDatabase";
2769 uno::Reference<embed::XStorage> xStorage = pDocShell->GetStorage();
2770
2771 // Refer to the sub-storage name in the document settings, so
2772 // we can load it again next time the file is imported.
2773 uno::Reference<lang::XMultiServiceFactory> xFactory(pDocShell->GetModel(), uno::UNO_QUERY);
2774 uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
2775 xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath));
2776
2777 // Store it only after setting the above property, so that only one data source gets registered.
2778 SwDBManager::StoreEmbeddedDataSource(xStore, xStorage, aStreamRelPath, aOwnURL);
2779 }
2780 }
2781 xDBContext->registerObject(sFind, xNewInstance);
2782 }
2783 catch (const uno::Exception&)
2784 {
2785 sFind.clear();
2786 }
2787 return sFind;
2788}
2789}
2790
2792{
2793 sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, pParent);
2795 uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
2796
2797 OUString sFilterAll(SwResId(STR_FILTER_ALL));
2798 OUString sFilterAllData(SwResId(STR_FILTER_ALL_DATA));
2799
2800 const std::vector<std::pair<OUString, OUString>> filters{
2801 { SwResId(STR_FILTER_SXB), "*.odb" },
2802 { SwResId(STR_FILTER_SXC), "*.ods;*.sxc" },
2803 { SwResId(STR_FILTER_SXW), "*.odt;*.sxw" },
2804 { SwResId(STR_FILTER_DBF), "*.dbf" },
2805 { SwResId(STR_FILTER_XLS), "*.xls;*.xlsx" },
2806 { SwResId(STR_FILTER_DOC), "*.doc;*.docx" },
2807 { SwResId(STR_FILTER_TXT), "*.txt" },
2808 { SwResId(STR_FILTER_CSV), "*.csv" },
2809#ifdef _WIN32
2810 { SwResId(STR_FILTER_MDB), "*.mdb;*.mde" },
2811 { SwResId(STR_FILTER_ACCDB), "*.accdb;*.accde" },
2812#endif
2813 };
2814
2815 OUStringBuffer sAllDataFilter;
2816 for (const auto& [name, filter] : filters)
2817 {
2818 (void)name;
2819 if (!sAllDataFilter.isEmpty())
2820 sAllDataFilter.append(';');
2821 sAllDataFilter.append(filter);
2822 }
2823
2824 xFP->appendFilter( sFilterAll, "*" );
2825 xFP->appendFilter( sFilterAllData, sAllDataFilter.makeStringAndClear());
2826
2827 // Similar to sfx2::addExtension from sfx2/source/dialog/filtergrouping.cxx
2828 for (const auto& [name, filter] : filters)
2829 xFP->appendFilter(name + " (" + filter + ")", filter);
2830
2831 xFP->setCurrentFilter( sFilterAll ) ;
2832 OUString sFind;
2833 if( ERRCODE_NONE == aDlgHelper.Execute() )
2834 {
2835 uno::Reference< beans::XPropertySet > aSettings;
2836 const INetURLObject aURL( xFP->getSelectedFiles()[0] );
2838
2839 if( DBConnURIType::FLAT == type )
2840 {
2841 uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2842 uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext);
2843 if( xSettingsDlg->execute() )
2844 aSettings.set( uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ) );
2845 }
2846 sFind = LoadAndRegisterDataSource_Impl( type, DBConnURIType::FLAT == type ? &aSettings : nullptr, aURL, nullptr, pDocShell );
2847
2848 s_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(pDocShell, sFind));
2849 }
2850 return sFind;
2851}
2852
2853void SwDBManager::StoreEmbeddedDataSource(const uno::Reference<frame::XStorable>& xStorable,
2854 const uno::Reference<embed::XStorage>& xStorage,
2855 const OUString& rStreamRelPath,
2856 const OUString& rOwnURL, bool bCopyTo)
2857{
2858 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2859 OUString const sTmpName = ConstructVndSunStarPkgUrl(rOwnURL, rStreamRelPath);
2860
2861 uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
2862 {
2863 {"TargetStorage", uno::Any(xStorage)},
2864 {"StreamRelPath", uno::Any(rStreamRelPath)},
2865 {"BaseURI", uno::Any(rOwnURL)}
2866 });
2867 if (bCopyTo)
2868 xStorable->storeToURL(sTmpName, aSequence);
2869 else
2870 xStorable->storeAsURL(sTmpName, aSequence);
2871}
2872
2873OUString SwDBManager::LoadAndRegisterDataSource(std::u16string_view rURI, const OUString *pDestDir)
2874{
2875 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN, nullptr, INetURLObject(rURI), pDestDir, nullptr );
2876}
2877
2878namespace
2879{
2880 // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2881 // which allows the original storage to be deleted
2882 void switchEmbeddedDatabaseStorage(const uno::Reference<sdb::XDatabaseContext>& rDatabaseContext, const OUString& rName)
2883 {
2884 uno::Reference<sdb::XDocumentDataSource> xDS(rDatabaseContext->getByName(rName), uno::UNO_QUERY);
2885 if (!xDS)
2886 return;
2887 uno::Reference<document::XStorageBasedDocument> xStorageDoc(xDS->getDatabaseDocument(), uno::UNO_QUERY);
2888 if (!xStorageDoc)
2889 return;
2890 xStorageDoc->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2891 }
2892}
2893
2894void SwDBManager::RevokeDataSource(const OUString& rName)
2895{
2896 uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2897 if (xDatabaseContext->hasByName(rName))
2898 {
2899 switchEmbeddedDatabaseStorage(xDatabaseContext, rName);
2900 xDatabaseContext->revokeObject(rName);
2901 }
2902}
2903
2905{
2906 uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2907
2908 OUString sDataSource = rData.sDataSource;
2909
2910 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2911 if (sDataSource.isEmpty())
2912 sDataSource = "EmbeddedDatabase";
2913
2914 SwDBManager::RevokeDataSource( sDataSource );
2915
2916 // Encode the stream name and the real path into a single URL.
2917 const INetURLObject& rURLObject = rDocShell.GetMedium()->GetURLObject();
2918 OUString const aURL = ConstructVndSunStarPkgUrl(
2921
2922 uno::Reference<uno::XInterface> xDataSource(xDatabaseContext->getByName(aURL), uno::UNO_QUERY);
2923 xDatabaseContext->registerObject( sDataSource, xDataSource );
2924
2925 // temp file - don't remember connection
2926 if (rData.sDataSource.isEmpty())
2927 s_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(nullptr, sDataSource));
2928}
2929
2931 const uno::Sequence<beans::PropertyValue>& rProperties)
2932{
2933 //prevent second call
2934 if(m_pImpl->pMergeDialog)
2935 return ;
2936 OUString sDataSource, sDataTableOrQuery;
2937 uno::Sequence<uno::Any> aSelection;
2938
2939 sal_Int32 nCmdType = sdb::CommandType::TABLE;
2940 uno::Reference< sdbc::XConnection> xConnection;
2941
2942 svx::ODataAccessDescriptor aDescriptor(rProperties);
2943 sDataSource = aDescriptor.getDataSource();
2944 OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDataTableOrQuery);
2945 OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCmdType);
2946
2948 aDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
2950 aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
2951
2952 if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty())
2953 {
2954 OSL_FAIL("PropertyValues missing or unset");
2955 return;
2956 }
2957
2958 //always create a connection for the dialog and dispose it after the dialog has been closed
2959 SwDSParam* pFound = nullptr;
2960 if(!xConnection.is())
2961 {
2962 xConnection = SwDBManager::RegisterConnection(sDataSource);
2963 pFound = FindDSConnection(sDataSource, true);
2964 }
2966 m_pImpl->pMergeDialog = pFact->CreateMailMergeDlg(rSh.GetView().GetViewFrame().GetFrameWeld(), rSh,
2967 sDataSource,
2968 sDataTableOrQuery,
2969 nCmdType,
2970 xConnection);
2971 if(m_pImpl->pMergeDialog->Execute() == RET_OK)
2972 {
2973 aDescriptor[svx::DataAccessDescriptorProperty::Selection] <<= m_pImpl->pMergeDialog->GetSelection();
2974
2975 uno::Reference<sdbc::XResultSet> xResSet = m_pImpl->pMergeDialog->GetResultSet();
2976 if(xResSet.is())
2977 aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
2978
2979 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2981
2982 lcl_emitEvent(SfxEventHintId::SwMailMerge, STR_SW_EVENT_MAIL_MERGE, xDocShell.get());
2983
2984 // prepare mail merge descriptor
2985 SwMergeDescriptor aMergeDesc( m_pImpl->pMergeDialog->GetMergeType(), rSh, aDescriptor );
2986 aMergeDesc.sSaveToFilter = m_pImpl->pMergeDialog->GetSaveFilter();
2987 aMergeDesc.bCreateSingleFile = m_pImpl->pMergeDialog->IsSaveSingleDoc();
2988 aMergeDesc.bPrefixIsFilename = aMergeDesc.bCreateSingleFile;
2989 aMergeDesc.sPrefix = m_pImpl->pMergeDialog->GetTargetURL();
2990
2991 if(!aMergeDesc.bCreateSingleFile)
2992 {
2993 if(m_pImpl->pMergeDialog->IsGenerateFromDataBase())
2994 aMergeDesc.sDBcolumn = m_pImpl->pMergeDialog->GetColumnName();
2995
2996 if(m_pImpl->pMergeDialog->IsFileEncryptedFromDataBase())
2997 aMergeDesc.sDBPasswordColumn = m_pImpl->pMergeDialog->GetPasswordColumnName();
2998 }
2999
3000 Merge( aMergeDesc );
3001
3002 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd, STR_SW_EVENT_MAIL_MERGE_END, xDocShell.get());
3003
3004 // reset the cursor inside
3005 xResSet = nullptr;
3006 aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
3007 }
3008 if(pFound)
3009 {
3010 for (const auto & pParam : m_DataSourceParams)
3011 {
3012 if (pParam.get() == pFound)
3013 {
3014 try
3015 {
3016 uno::Reference<lang::XComponent> xComp(pParam->xConnection, uno::UNO_QUERY);
3017 if(xComp.is())
3018 xComp->dispose();
3019 }
3020 catch(const uno::RuntimeException&)
3021 {
3022 //may be disposed already since multiple entries may have used the same connection
3023 }
3024 break;
3025 }
3026 //pFound doesn't need to be removed/deleted -
3027 //this has been done by the SwConnectionDisposedListener_Impl already
3028 }
3029 }
3030 m_pImpl->pMergeDialog.disposeAndClear();
3031}
3032
3034 const uno::Sequence< beans::PropertyValue>& rProperties)
3035{
3036 OUString sDataSource, sDataTableOrQuery;
3037 uno::Reference<sdbc::XResultSet> xResSet;
3038 uno::Sequence<uno::Any> aSelection;
3039 sal_Int16 nCmdType = sdb::CommandType::TABLE;
3040 uno::Reference< sdbc::XConnection> xConnection;
3041 for(const beans::PropertyValue& rValue : rProperties)
3042 {
3043 if ( rValue.Name == "DataSourceName" )
3044 rValue.Value >>= sDataSource;
3045 else if ( rValue.Name == "Command" )
3046 rValue.Value >>= sDataTableOrQuery;
3047 else if ( rValue.Name == "Cursor" )
3048 rValue.Value >>= xResSet;
3049 else if ( rValue.Name == "Selection" )
3050 rValue.Value >>= aSelection;
3051 else if ( rValue.Name == "CommandType" )
3052 rValue.Value >>= nCmdType;
3053 else if ( rValue.Name == "ActiveConnection" )
3054 rValue.Value >>= xConnection;
3055 }
3056 if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is())
3057 {
3058 OSL_FAIL("PropertyValues missing or unset");
3059 return;
3060 }
3061 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
3062 uno::Reference<sdbc::XDataSource> xSource;
3063 uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
3064 if(xChild.is())
3065 xSource.set(xChild->getParent(), uno::UNO_QUERY);
3066 if(!xSource.is())
3067 xSource = dbtools::getDataSource(sDataSource, xContext);
3068 uno::Reference< sdbcx::XColumnsSupplier > xColSupp( xResSet, uno::UNO_QUERY );
3069 SwDBData aDBData;
3070 aDBData.sDataSource = sDataSource;
3071 aDBData.sCommand = sDataTableOrQuery;
3072 aDBData.nCommandType = nCmdType;
3073
3076 xSource,
3077 xColSupp,
3078 aDBData ));
3079 if( RET_OK != pDlg->Execute() )
3080 return;
3081
3082 OUString sDummy;
3083 if(!xConnection.is())
3084 xConnection = xSource->getConnection(sDummy, sDummy);
3085 try
3086 {
3087 pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet);
3088 }
3089 catch (const uno::Exception&)
3090 {
3091 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3092 }
3093}
3094
3095uno::Reference<sdbc::XDataSource> SwDBManager::getDataSourceAsParent(const uno::Reference< sdbc::XConnection>& _xConnection,const OUString& _sDataSourceName)
3096{
3097 uno::Reference<sdbc::XDataSource> xSource;
3098 try
3099 {
3100 uno::Reference<container::XChild> xChild(_xConnection, uno::UNO_QUERY);
3101 if ( xChild.is() )
3102 xSource.set(xChild->getParent(), uno::UNO_QUERY);
3103 if ( !xSource.is() )
3104 xSource = dbtools::getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext());
3105 }
3106 catch (const uno::Exception&)
3107 {
3108 TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3109 }
3110 return xSource;
3111}
3112
3113uno::Reference<sdbc::XResultSet> SwDBManager::createCursor(const OUString& _sDataSourceName,
3114 const OUString& _sCommand,
3115 sal_Int32 _nCommandType,
3116 const uno::Reference<sdbc::XConnection>& _xConnection,
3117 const SwView* pView)
3118{
3119 uno::Reference<sdbc::XResultSet> xResultSet;
3120 try
3121 {
3122 uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
3123 if( xMgr.is() )
3124 {
3125 uno::Reference<uno::XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet");
3126 uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, uno::UNO_QUERY);
3127 if(xRowSetPropSet.is())
3128 {
3129 xRowSetPropSet->setPropertyValue("DataSourceName", uno::Any(_sDataSourceName));
3130 xRowSetPropSet->setPropertyValue("ActiveConnection", uno::Any(_xConnection));
3131 xRowSetPropSet->setPropertyValue("Command", uno::Any(_sCommand));
3132 xRowSetPropSet->setPropertyValue("CommandType", uno::Any(_nCommandType));
3133
3134 uno::Reference< sdb::XCompletedExecution > xRowSet(xInstance, uno::UNO_QUERY);
3135
3136 if ( xRowSet.is() )
3137 {
3138 weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
3139 uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
3140 xRowSet->executeWithCompletion(xHandler);
3141 }
3142 xResultSet.set(xRowSet, uno::UNO_QUERY);
3143 }
3144 }
3145 }
3146 catch (const uno::Exception&)
3147 {
3148 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3149 }
3150 return xResultSet;
3151}
3152
3153void SwDBManager::setEmbeddedName(const OUString& rEmbeddedName, SwDocShell& rDocShell)
3154{
3155 bool bLoad = m_sEmbeddedName != rEmbeddedName && !rEmbeddedName.isEmpty();
3156 bool bRegisterListener = m_sEmbeddedName.isEmpty() && !rEmbeddedName.isEmpty();
3157
3158 m_sEmbeddedName = rEmbeddedName;
3159
3160 if (bLoad)
3161 {
3162 uno::Reference<embed::XStorage> xStorage = rDocShell.GetStorage();
3163 // It's OK that we don't have the named sub-storage yet, in case
3164 // we're in the process of creating it.
3165 if (xStorage->hasByName(rEmbeddedName))
3166 LoadAndRegisterEmbeddedDataSource(rDocShell.GetDoc()->GetDBData(), rDocShell);
3167 }
3168
3169 if (bRegisterListener)
3170 // Register a remove listener, so we know when the embedded data source is removed.
3171 m_pImpl->m_xDataSourceRemovedListener = new SwDataSourceRemovedListener(*this);
3172}
3173
3174const OUString& SwDBManager::getEmbeddedName() const
3175{
3176 return m_sEmbeddedName;
3177}
3178
3180{
3181 return m_pDoc;
3182}
3183
3185{
3186 if (m_pImpl->m_xDataSourceRemovedListener.is())
3187 {
3188 m_pImpl->m_xDataSourceRemovedListener->Dispose();
3189 m_pImpl->m_xDataSourceRemovedListener.clear();
3190 }
3191}
3192
3194 : m_pDBManager(&rManager)
3195{
3196}
3197
3198void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject& rSource )
3199{
3200 ::SolarMutexGuard aGuard;
3201
3202 if (!m_pDBManager) return; // we're disposed too!
3203
3204 uno::Reference<sdbc::XConnection> xSource(rSource.Source, uno::UNO_QUERY);
3205 for (size_t nPos = m_pDBManager->m_DataSourceParams.size(); nPos; nPos--)
3206 {
3207 SwDSParam* pParam = m_pDBManager->m_DataSourceParams[nPos - 1].get();
3208 if(pParam->xConnection.is() &&
3209 (xSource == pParam->xConnection))
3210 {
3211 m_pDBManager->m_DataSourceParams.erase(
3212 m_pDBManager->m_DataSourceParams.begin() + nPos - 1);
3213 }
3214 }
3215}
3216
3217std::shared_ptr<SwMailMergeConfigItem> SwDBManager::PerformMailMerge(SwView const * pView)
3218{
3219 std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
3220 if (!xConfigItem)
3221 return xConfigItem;
3222
3223 svx::ODataAccessDescriptor aDescriptor;
3224 aDescriptor.setDataSource(xConfigItem->GetCurrentDBData().sDataSource);
3225 aDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= xConfigItem->GetConnection().getTyped();
3226 aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ] <<= xConfigItem->GetResultSet();
3227 aDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= xConfigItem->GetCurrentDBData().sCommand;
3228 aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= xConfigItem->GetCurrentDBData().nCommandType;
3229 aDescriptor[ svx::DataAccessDescriptorProperty::Selection ] <<= xConfigItem->GetSelection();
3230
3231 SwWrtShell& rSh = pView->GetWrtShell();
3232 xConfigItem->SetTargetView(nullptr);
3233
3234 SwMergeDescriptor aMergeDesc(DBMGR_MERGE_SHELL, rSh, aDescriptor);
3235 aMergeDesc.pMailMergeConfigItem = xConfigItem.get();
3236 aMergeDesc.bCreateSingleFile = true;
3237 rSh.GetDBManager()->Merge(aMergeDesc);
3238
3239 return xConfigItem;
3240}
3241
3243{
3244 if (s_aUncommittedRegistrations.empty())
3245 return;
3246
3247 SwView* pView = ( m_pDoc && m_pDoc->GetDocShell() ) ? m_pDoc->GetDocShell()->GetView() : nullptr;
3248 if (pView)
3249 {
3250 const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
3251 if (xConfigItem)
3252 {
3253 xConfigItem->DisposeResultSet();
3254 xConfigItem->DocumentReloaded();
3255 }
3256 }
3257
3258 for (auto it = s_aUncommittedRegistrations.begin(); it != s_aUncommittedRegistrations.end();)
3259 {
3260 if ((m_pDoc && it->first == m_pDoc->GetDocShell()) || it->first == nullptr)
3261 {
3262 RevokeDataSource(it->second);
3263 it = s_aUncommittedRegistrations.erase(it);
3264 }
3265 else
3266 ++it;
3267 }
3268}
3269
3271{
3272 for (auto aIt = s_aUncommittedRegistrations.begin(); aIt != s_aUncommittedRegistrations.end();)
3273 {
3274 if (aIt->first == m_pDoc->GetDocShell() || aIt->first == nullptr)
3275 {
3276 m_aNotUsedConnections.push_back(aIt->second);
3277 aIt = s_aUncommittedRegistrations.erase(aIt);
3278 }
3279 else
3280 aIt++;
3281 }
3282}
3283
3284void SwDBManager::SetAsUsed(const OUString& rName)
3285{
3286 auto aFound = std::find(m_aNotUsedConnections.begin(), m_aNotUsedConnections.end(), rName);
3287 if (aFound != m_aNotUsedConnections.end())
3288 m_aNotUsedConnections.erase(aFound);
3289}
3290
3292{
3293 for (auto aIt = m_aNotUsedConnections.begin(); aIt != m_aNotUsedConnections.end();)
3294 {
3295 RevokeDataSource(*aIt);
3296 aIt = m_aNotUsedConnections.erase(aIt);
3297 }
3298}
3299
3300/* 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
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 SAL_WARN_UNUSED_RESULT SfxViewFrame * GetNext(const SfxViewFrame &rPrev, const SfxObjectShell *pDoc=nullptr, bool bOnlyVisible=true)
SfxDispatcher * GetDispatcher()
static SAL_WARN_UNUSED_RESULT 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
SfxViewFrame & GetViewFrame() const
const std::shared_ptr< vcl::PrinterController > & GetPrinterController() 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:1319
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:3198
ConnectionDisposedListener_Impl(SwDBManager &rMgr)
Definition: dbmgr.cxx:3193
virtual void mailDeliveryError(::rtl::Reference< MailDispatcher > xMailDispatcher, uno::Reference< mail::XMailMessage >, const OUString &) override
Definition: dbmgr.cxx:1040
MailDispatcherListener_Impl(SwDBManager &rDBManager)
Definition: dbmgr.cxx:1028
virtual void mailDelivered(uno::Reference< mail::XMailMessage > xMessage) override
Definition: dbmgr.cxx:1033
virtual void idle() override
Called when there are no more mail messages to deliver.
Definition: dbmgr.cxx:1031
static css::uno::Reference< css::sdbc::XConnection > GetConnection(const OUString &rDataSource, css::uno::Reference< css::sdbc::XDataSource > &rxSource, const SwView *pView)
Definition: dbmgr.cxx:1883
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:1906
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:3242
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:2360
SAL_DLLPRIVATE bool ToNextMergeRecord()
Definition: dbmgr.cxx:2123
SwDoc * getDoc() const
Definition: dbmgr.cxx:3179
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:2495
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:3184
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:1668
static OUString GetDBField(css::uno::Reference< css::beans::XPropertySet > const &xColumnProp, const SwDBFormatData &rDBFormatData, double *pNumber=nullptr)
Definition: dbmgr.cxx:1954
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:2026
void CloseAll(bool bIncludingMerge=true)
close all data sources - after fields were updated
Definition: dbmgr.cxx:2426
bool FillCalcWithMergeData(SvNumberFormatter *pDocFormatter, LanguageType nLanguage, SwCalc &aCalc)
Definition: dbmgr.cxx:2129
static const SwDBData & GetAddressDBName()
Definition: dbmgr.cxx:2533
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:2538
const OUString & getEmbeddedName() const
Definition: dbmgr.cxx:3174
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:3217
void setEmbeddedName(const OUString &rEmbeddedName, SwDocShell &rDocShell)
Definition: dbmgr.cxx:3153
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:2277
bool ToRecordId(sal_Int32 nSet)
Definition: dbmgr.cxx:2299
void RevokeNotUsedConnections()
Remove not used connections.
Definition: dbmgr.cxx:3291
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:3284
sal_Int32 GetColumnType(const OUString &rDBName, const OUString &rTableName, const OUString &rColNm)
Definition: dbmgr.cxx:1839
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:3095
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:2930
static void InsertText(SwWrtShell &rSh, const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
Definition: dbmgr.cxx:3033
SwDSParams_t m_DataSourceParams
Definition: dbmgr.hxx:256
bool GetMergeColumnCnt(const OUString &rColumnName, LanguageType nLanguage, OUString &rResult, double *pNumber)
Definition: dbmgr.cxx:2110
~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:2313
static OUString LoadAndRegisterDataSource(weld::Window *pParent, SwDocShell *pDocShell=nullptr)
Loads a data source from file and registers it.
Definition: dbmgr.cxx:2791
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:2904
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:2049
SwDoc * m_pDoc
The document that owns this manager.
Definition: dbmgr.hxx:272
void CommitLastRegistrations()
Accept not committed registrations.
Definition: dbmgr.cxx:3270
void ToNextRecord(const OUString &rDataSource, const OUString &rTableOrQuery)
Definition: dbmgr.cxx:2193
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:3113
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:2853
static void RevokeDataSource(const OUString &rName)
Unregister a data source.
Definition: dbmgr.cxx:2894
SAL_DLLPRIVATE SwDSParam * FindDSData(const SwDBData &rData, bool bCreate)
Definition: dbmgr.cxx:2447
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:1054
void SetInitDBFields(bool b)
Definition: dbmgr.hxx:316
bool IsMergeError() const
Definition: dbmgr.hxx:310
sal_uInt32 GetSelectedRecordId()
Definition: dbmgr.cxx:2282
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:195
SwDBData const & GetDBData()
Definition: docfld.cxx:386
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:402
IDocumentDeviceAccess const & getIDocumentDeviceAccess() const
Definition: doc.cxx:246
o3tl::sorted_vector< SwRootFrame * > GetAllLayouts()
Definition: doclay.cxx:1683
SfxObjectShell * CreateCopy(bool bCallInitNew, bool bEmpty) const
Definition: docnew.cxx:893
void SetInMailMerge(bool bNew)
Definition: doc.hxx:972
SwDBManager * GetDBManager() const
Definition: doc.hxx:681
IDocumentLinksAdministration const & getIDocumentLinksAdministration() const
Definition: doc.cxx:268
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:152
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:365
void SetDBManager(SwDBManager *pNewMgr)
Definition: doc.hxx:680
void ReplaceDocumentProperties(const SwDoc &rSource, bool mailMerge=false)
Replace document properties with those from rSource.
Definition: docglos.cxx:83
SwDocShell * GetDocShell()
Definition: doc.hxx:1364
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:335
bool IsExpFieldsLocked() const
Definition: edfld.cxx:360
void ChangeDBFields(const std::vector< OUString > &rOldNames, const OUString &rNewName)
Definition: edfld.cxx:314
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:350
SwDBData const & GetDBData() const
Database information.
Definition: edfld.cxx:298
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:380
const SwSectionFormat & GetSectionFormat(size_t nFormat) const
Definition: edsect.cxx:142
void UnlockExpFields()
Definition: edfld.cxx:355
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:375
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:646
void SetIdle(bool b) const
Definition: viewopt.hxx:341
const SwViewOption * GetViewOptions() const
Definition: viewsh.hxx:434
SwDoc * GetDoc() const
Definition: viewsh.hxx:290
Definition: view.hxx:146
SwWrtShell & GetWrtShell() const
Definition: view.hxx:421
std::shared_ptr< SwMailMergeConfigItem > const & GetMailMergeConfigItem() const
Definition: view0.cxx:136
SwWrtShell * GetWrtShellPtr() const
Definition: view.hxx:422
SwEditWin & GetEditWin()
Definition: view.hxx:424
SwDocShell * GetDocShell()
Definition: view.cxx:1163
Used by the UI to modify the document model.
Definition: wrtsh.hxx:97
void ChgDBData(const SwDBData &SwDBData)
Definition: wrtsh1.cxx:2077
bool DelRight(bool isReplaceHeuristic=false)
Definition: delete.cxx:293
bool HasSelection() const
Definition: wrtsh.hxx:147
const SwView & GetView() const
Definition: wrtsh.hxx:443
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:2214
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:972
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:719
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:2547
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:168
#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