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