Coverage Report

Created: 2025-12-08 09:28

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