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