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