Coverage Report

Created: 2026-05-16 09:25

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