LibreOffice Module sw (master)  1
mmoutputtypepage.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 "mmoutputtypepage.hxx"
21 #include <mailmergewizard.hxx>
22 #include <mmconfigitem.hxx>
23 #include <strings.hrc>
24 #include <bitmaps.hlst>
25 #include <swtypes.hxx>
26 
27 #include <rtl/ref.hxx>
28 #include <com/sun/star/mail/XSmtpService.hpp>
29 #include <vcl/idle.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
32 
33 #include <swunohelper.hxx>
34 #include <mmresultdialogs.hxx>
35 #include <maildispatcher.hxx>
36 #include <imaildsplistener.hxx>
37 #include <mutex>
38 
39 using namespace ::com::sun::star;
40 
42  : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage")
43  , m_pWizard(pWizard)
44  , m_xLetterRB(m_xBuilder->weld_radio_button("letter"))
45  , m_xMailRB(m_xBuilder->weld_radio_button("email"))
46  , m_xLetterHint(m_xBuilder->weld_label("letterft"))
47  , m_xMailHint(m_xBuilder->weld_label("emailft"))
48 {
49  Link<weld::Toggleable&,void> aLink = LINK(this, SwMailMergeOutputTypePage, TypeHdl_Impl);
50  m_xLetterRB->connect_toggled(aLink);
51  m_xMailRB->connect_toggled(aLink);
52 
54  if(rConfigItem.IsOutputToLetter())
55  m_xLetterRB->set_active(true);
56  else
57  m_xMailRB->set_active(true);
58  TypeHdl_Impl(*m_xLetterRB);
59 }
60 
62 {
63 }
64 
66 {
67  bool bLetter = m_xLetterRB->get_active();
68  m_xLetterHint->set_visible(bLetter);
69  m_xMailHint->set_visible(!bLetter);
70  m_pWizard->GetConfigItem().SetOutputToLetter(bLetter);
71  m_pWizard->UpdateRoadmap();
72 }
73 
75 {
76  friend class SwSendMailDialog;
77  std::mutex aDescriptorMutex;
78 
79  std::vector< SwMailDescriptor > aDescriptors;
80  sal_uInt32 nCurrentDescriptor;
83  uno::Reference< mail::XMailService > xConnectedInMailService;
85 
87  nCurrentDescriptor(0), aRemoveIdle("SwSendMailDialog_Impl aRemoveIdle")
88  {
89  aRemoveIdle.SetPriority(TaskPriority::LOWEST);
90  }
91 
93  {
94  // Shutdown must be called when the last reference to the
95  // mail dispatcher will be released in order to force a
96  // shutdown of the mail dispatcher thread.
97  // 'join' with the mail dispatcher thread leads to a
98  // deadlock (SolarMutex).
99  if( xMailDispatcher.is() && !xMailDispatcher->isShutdownRequested() )
100  xMailDispatcher->shutdown();
101  }
103 };
104 
106 {
107  std::scoped_lock aGuard(aDescriptorMutex);
108  if(nCurrentDescriptor < aDescriptors.size())
109  {
111  return &aDescriptors[nCurrentDescriptor - 1];
112  }
113  return nullptr;
114 }
115 
116 namespace {
117 
118 class SwMailDispatcherListener_Impl : public IMailDispatcherListener
119 {
120  SwSendMailDialog& m_rSendMailDialog;
121 
122 public:
123  explicit SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg);
124 
125  virtual void idle() override;
126  virtual void mailDelivered(uno::Reference< mail::XMailMessage> xMailMessage) override;
127  virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher,
128  uno::Reference< mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) override;
129 
130  static void DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage );
131 };
132 
133 }
134 
135 SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg)
136  : m_rSendMailDialog(rParentDlg)
137 {
138 }
139 
140 void SwMailDispatcherListener_Impl::idle()
141 {
142  SolarMutexGuard aGuard;
143  m_rSendMailDialog.AllMailsSent();
144 }
145 
146 void SwMailDispatcherListener_Impl::mailDelivered(
147  uno::Reference< mail::XMailMessage> xMailMessage)
148 {
149  SolarMutexGuard aGuard;
150  m_rSendMailDialog.DocumentSent( xMailMessage, true, nullptr );
151  DeleteAttachments( xMailMessage );
152 }
153 
154 void SwMailDispatcherListener_Impl::mailDeliveryError(
155  ::rtl::Reference<MailDispatcher> /*xMailDispatcher*/,
156  uno::Reference< mail::XMailMessage> xMailMessage,
157  const OUString& sErrorMessage)
158 {
159  SolarMutexGuard aGuard;
160  m_rSendMailDialog.DocumentSent( xMailMessage, false, &sErrorMessage );
161  DeleteAttachments( xMailMessage );
162 }
163 
164 void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage )
165 {
166  const uno::Sequence< mail::MailAttachment > aAttachments = xMessage->getAttachments();
167 
168  for(const auto& rAttachment : aAttachments)
169  {
170  try
171  {
172  uno::Reference< beans::XPropertySet > xTransferableProperties( rAttachment.Data, uno::UNO_QUERY_THROW);
173  OUString sURL;
174  xTransferableProperties->getPropertyValue("URL") >>= sURL;
175  if(!sURL.isEmpty())
177  }
178  catch (const uno::Exception&)
179  {
180  }
181  }
182 }
183 
184 namespace {
185 
186 class SwSendWarningBox_Impl : public weld::MessageDialogController
187 {
188  std::unique_ptr<weld::TextView> m_xDetailED;
189 public:
190  SwSendWarningBox_Impl(weld::Window* pParent, const OUString& rDetails)
191  : MessageDialogController(pParent, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid")
192  , m_xDetailED(m_xBuilder->weld_text_view("errors"))
193  {
194  m_xDetailED->set_size_request(80 * m_xDetailED->get_approximate_digit_width(),
195  8 * m_xDetailED->get_text_height());
196  m_xDetailED->set_text(rDetails);
197  }
198 };
199 
200 }
201 
203  : GenericDialogController(pParent, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog")
204  , m_sContinue(SwResId( ST_CONTINUE ))
205  , m_sSendingTo( SwResId(ST_SENDINGTO ))
206  , m_sCompleted( SwResId(ST_COMPLETED ))
207  , m_sFailed( SwResId(ST_FAILED ))
208  , m_bCancel(false)
209  , m_bDestructionEnabled(false)
211  , m_pConfigItem(&rConfigItem)
212  , m_nExpectedCount(0)
213  , m_nSendCount(0)
214  , m_nErrorCount(0)
215  , m_xTransferStatus(m_xBuilder->weld_label("transferstatus"))
216  , m_xPaused(m_xBuilder->weld_label("paused"))
217  , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
218  , m_xErrorStatus(m_xBuilder->weld_label("errorstatus"))
219  , m_xStatus(m_xBuilder->weld_tree_view("container"))
220  , m_xStop(m_xBuilder->weld_button("stop"))
221  , m_xCancel(m_xBuilder->weld_button("cancel"))
222  , m_xExpander(m_xBuilder->weld_expander("details"))
223 {
224  m_sStop = m_xStop->get_label();
225  m_sTransferStatus = m_xTransferStatus->get_label();
226  m_sErrorStatus = m_xErrorStatus->get_label();
227 
228  Size aSize(m_xStatus->get_approximate_digit_width() * 28,
229  m_xStatus->get_height_rows(20));
230  m_xStatus->set_size_request(aSize.Width(), aSize.Height());
231 
232  m_xStop->connect_clicked(LINK( this, SwSendMailDialog, StopHdl_Impl));
233  m_xCancel->connect_clicked(LINK( this, SwSendMailDialog, CancelHdl_Impl));
234 
235  std::vector<int> aWidths
236  {
237  o3tl::narrowing<int>(m_xStatus->get_checkbox_column_width()),
238  o3tl::narrowing<int>(aSize.Width()/3 * 2)
239  };
240  m_xStatus->set_column_fixed_widths(aWidths);
241 
242  m_xPaused->set_visible(false);
244 }
245 
247 {
248  if(!m_pImpl->xMailDispatcher.is())
249  return;
250 
251  try
252  {
253  if(m_pImpl->xMailDispatcher->isStarted())
254  m_pImpl->xMailDispatcher->stop();
255  if(m_pImpl->xConnectedInMailService.is() && m_pImpl->xConnectedInMailService->isConnected())
256  m_pImpl->xConnectedInMailService->disconnect();
257 
258  uno::Reference<mail::XMailMessage> xMessage =
259  m_pImpl->xMailDispatcher->dequeueMailMessage();
260  while(xMessage.is())
261  {
262  SwMailDispatcherListener_Impl::DeleteAttachments( xMessage );
263  xMessage = m_pImpl->xMailDispatcher->dequeueMailMessage();
264  }
265  }
266  catch (const uno::Exception&)
267  {
268  }
269 }
270 
272 {
273  std::scoped_lock aGuard(m_pImpl->aDescriptorMutex);
274  m_pImpl->aDescriptors.push_back(rDesc);
275  // if the dialog is already running then continue sending of documents
276  if(m_pImpl->xMailDispatcher.is())
277  {
278  IterateMails();
279  }
280 }
281 
282 IMPL_LINK( SwSendMailDialog, StopHdl_Impl, weld::Button&, rButton, void )
283 {
284  m_bCancel = true;
285  if(!m_pImpl->xMailDispatcher.is())
286  return;
287 
288  if(m_pImpl->xMailDispatcher->isStarted())
289  {
290  m_pImpl->xMailDispatcher->stop();
291  rButton.set_label(m_sContinue);
292  m_xPaused->show();
293  }
294  else
295  {
296  m_pImpl->xMailDispatcher->start();
297  rButton.set_label(m_sStop);
298  m_xPaused->hide();
299  }
300 }
301 
303 {
304  m_xDialog->hide();
305 
307  m_xDialog->response(RET_CANCEL);
308  else
309  {
310  m_pImpl->aRemoveIdle.SetInvokeHandler( LINK( this, SwSendMailDialog, RemoveThis ) );
311  m_pImpl->aRemoveIdle.Start();
312  }
313 }
314 
315 IMPL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, pDialog, void )
316 {
317  static_cast<SwSendMailDialog*>(pDialog)->SendMails();
318 }
319 
320 IMPL_LINK( SwSendMailDialog, RemoveThis, Timer*, pTimer, void )
321 {
322  if( m_pImpl->xMailDispatcher.is() )
323  {
324  if(m_pImpl->xMailDispatcher->isStarted())
325  m_pImpl->xMailDispatcher->stop();
326  if(!m_pImpl->xMailDispatcher->isShutdownRequested())
327  m_pImpl->xMailDispatcher->shutdown();
328  }
329 
330  if( m_bDestructionEnabled &&
331  (!m_pImpl->xMailDispatcher.is() ||
332  !m_pImpl->xMailDispatcher->isRunning()))
333  {
334  m_xDialog->response(RET_CANCEL);
335  }
336  else
337  {
338  pTimer->Start();
339  }
340 }
341 
342 IMPL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, p, void )
343 {
344  SwSendMailDialog* pDialog = static_cast<SwSendMailDialog*>(p);
345  if(pDialog->m_pImpl->xMailDispatcher.is() &&
346  pDialog->m_pImpl->xMailDispatcher->isStarted())
347  {
348  pDialog->m_pImpl->xMailDispatcher->stop();
349  pDialog->m_xStop->set_label(pDialog->m_sContinue);
350  pDialog->m_xPaused->show();
351  }
352 }
353 
355 {
356  if(!m_pConfigItem)
357  {
358  OSL_FAIL("config item not set");
359  return;
360  }
361  auto xWait(std::make_unique<weld::WaitObject>(m_xDialog.get()));
362  //get a mail server connection
363  uno::Reference< mail::XSmtpService > xSmtpServer =
365  m_pImpl->xConnectedInMailService,
366  OUString(), OUString(), m_xDialog.get());
367  bool bIsLoggedIn = xSmtpServer.is() && xSmtpServer->isConnected();
368  xWait.reset();
369  if(!bIsLoggedIn)
370  {
371  OSL_FAIL("create error message");
372  return;
373  }
374  m_pImpl->xMailDispatcher.set( new MailDispatcher(xSmtpServer));
375  IterateMails();
376  m_pImpl->xMailListener = new SwMailDispatcherListener_Impl(*this);
377  m_pImpl->xMailDispatcher->addListener(m_pImpl->xMailListener);
378  if(!m_bCancel)
379  {
380  m_pImpl->xMailDispatcher->start();
381  }
382 }
383 
385 {
386  const SwMailDescriptor* pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
387  while( pCurrentMailDescriptor )
388  {
389  if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor->sEMail))
390  {
391  OUString sMessage = m_sSendingTo;
392  m_xStatus->append();
393  m_xStatus->set_image(m_nSendCount, RID_BMP_FORMULA_CANCEL, 0);
394  m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", pCurrentMailDescriptor->sEMail), 1);
395  m_xStatus->set_text(m_nSendCount, m_sFailed, 1);
396  ++m_nSendCount;
397  ++m_nErrorCount;
399  pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
400  continue;
401  }
404  pMessage->setReplyToAddress(m_pConfigItem->GetMailReplyTo());
405  pMessage->addRecipient( pCurrentMailDescriptor->sEMail );
406  pMessage->SetSenderName( m_pConfigItem->GetMailDisplayName() );
407  pMessage->SetSenderAddress( m_pConfigItem->GetMailAddress() );
408  if(!pCurrentMailDescriptor->sAttachmentURL.isEmpty())
409  {
410  mail::MailAttachment aAttach;
411  aAttach.Data =
412  new SwMailTransferable(
413  pCurrentMailDescriptor->sAttachmentURL,
414  pCurrentMailDescriptor->sAttachmentName,
415  pCurrentMailDescriptor->sMimeType );
416  aAttach.ReadableName = pCurrentMailDescriptor->sAttachmentName;
417  pMessage->addAttachment( aAttach );
418  }
419  pMessage->setSubject( pCurrentMailDescriptor->sSubject );
420  uno::Reference< datatransfer::XTransferable> xBody =
421  new SwMailTransferable(
422  pCurrentMailDescriptor->sBodyContent,
423  pCurrentMailDescriptor->sBodyMimeType);
424  pMessage->setBody( xBody );
425 
426  //CC and BCC are tokenized by ';'
427  if(!pCurrentMailDescriptor->sCC.isEmpty())
428  {
429  sal_Int32 nPos = 0;
430  do
431  {
432  OUString sTmp = pCurrentMailDescriptor->sCC.getToken( 0, ';', nPos );
433  if( !sTmp.isEmpty() )
434  pMessage->addCcRecipient( sTmp );
435  }
436  while (nPos >= 0);
437  }
438  if(!pCurrentMailDescriptor->sBCC.isEmpty())
439  {
440  sal_Int32 nPos = 0;
441  do
442  {
443  OUString sTmp = pCurrentMailDescriptor->sBCC.getToken( 0, ';', nPos );
444  if( !sTmp.isEmpty() )
445  pMessage->addBccRecipient( sTmp );
446  }
447  while (nPos >= 0);
448  }
449  m_pImpl->xMailDispatcher->enqueueMailMessage( pMessage );
450  pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
451  }
453 }
454 
455 void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount)
456 {
458  StartSendMails ), this );
459  m_nExpectedCount = nExpectedCount > 0 ? nExpectedCount : 1;
460 }
461 
462 void SwSendMailDialog::DocumentSent( uno::Reference< mail::XMailMessage> const & xMessage,
463  bool bResult,
464  const OUString* pError )
465 {
466  //sending should stop on send errors
467  if(pError &&
468  m_pImpl->xMailDispatcher.is() && m_pImpl->xMailDispatcher->isStarted())
469  {
471  StopSendMails ), this );
472  }
473  OUString sInsertImg(bResult ? OUString(RID_BMP_FORMULA_APPLY) : OUString(RID_BMP_FORMULA_CANCEL));
474 
475  OUString sMessage = m_sSendingTo;
476  m_xStatus->append();
477  m_xStatus->set_image(m_nSendCount, sInsertImg, 0);
478  m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", xMessage->getRecipients()[0]), 1);
479  m_xStatus->set_text(m_nSendCount, bResult ? m_sCompleted : m_sFailed, 1);
480  ++m_nSendCount;
481  if(!bResult)
482  ++m_nErrorCount;
483 
485 
486  if (pError)
487  {
488  SwSendWarningBox_Impl aDlg(m_xDialog.get(), *pError);
489  aDlg.run();
490  }
491 }
492 
494 {
495  OUString sStatus( m_sTransferStatus );
496  sStatus = sStatus.replaceFirst("%1", OUString::number(m_nSendCount) );
497  sStatus = sStatus.replaceFirst("%2", OUString::number(m_nExpectedCount));
498  m_xTransferStatus->set_label(sStatus);
499 
500  sStatus = m_sErrorStatus.replaceFirst("%1", OUString::number(m_nErrorCount) );
501  m_xErrorStatus->set_label(sStatus);
502 
503  if (!m_pImpl->aDescriptors.empty())
504  {
505  assert(m_nExpectedCount && "div-by-zero");
506  m_xProgressBar->set_percentage(m_nSendCount * 100 / m_nExpectedCount);
507  }
508  else
509  m_xProgressBar->set_percentage(0);
510 }
511 
513 {
514  // Leave open if some kind of error occurred
516  {
517  m_xStop->set_sensitive(false);
518  m_xDialog->hide();
519  m_xDialog->response(RET_CANCEL);
520  }
521 }
522 
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::shared_ptr< weld::Dialog > m_xDialog
OUString sMessage
std::unique_ptr< weld::TreeView > m_xStatus
std::unique_ptr< weld::Button > m_xStop
uno::Reference< mail::XSmtpService > ConnectToSmtpServer(SwMailMergeConfigItem const &rConfigItem, uno::Reference< mail::XMailService > &rxInMailService, const OUString &rInMailServerPassword, const OUString &rOutMailServerPassword, weld::Window *pDialogParentWindow)
SwMailMergeConfigItem & GetConfigItem()
OUString const & GetMailAddress() const
IMPL_LINK(SwSendMailDialog, StopHdl_Impl, weld::Button &, rButton, void)
std::unique_ptr< SwSendMailDialog_Impl > m_pImpl
MailDispatcher listener interface.
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
sal_Int32 m_nExpectedCount
bool UCB_DeleteFile(const OUString &rURL)
Definition: swunohelper.cxx:59
RET_CANCEL
::rtl::Reference< IMailDispatcherListener > xMailListener
SwSendMailDialog(weld::Window *pParent, SwMailMergeConfigItem &)
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:165
SwMailMergeOutputTypePage(weld::Container *pPage, SwMailMergeWizard *pWizard)
virtual ~SwMailMergeOutputTypePage() override
std::unique_ptr< weld::Button > m_xCancel
std::vector< SwMailDescriptor > aDescriptors
void AddDocument(SwMailDescriptor const &rDesc)
void DocumentSent(css::uno::Reference< css::mail::XMailMessage > const &xMessage, bool bResult, const OUString *pError)
void StartSend(sal_Int32 nExpectedCount)
SwMailMergeConfigItem * m_pConfigItem
OUString const & GetMailReplyTo() const
std::unique_ptr< weld::RadioButton > m_xMailRB
const SwMailDescriptor * GetNextDescriptor()
IMPL_STATIC_LINK(SwSendMailDialog, StartSendMails, void *, pDialog, void)
SwMailMergeWizard * m_pWizard
OUString const & GetMailDisplayName() const
std::unique_ptr< weld::Label > m_xTransferStatus
std::unique_ptr< weld::Label > m_xPaused
void * p
IMPL_LINK_NOARG(SwMailMergeOutputTypePage, TypeHdl_Impl, weld::Toggleable &, void)
std::unique_ptr< weld::ProgressBar > m_xProgressBar
bool CheckMailAddress(const OUString &rMailAddress)
void SetPriority(TaskPriority ePriority)
std::unique_ptr< weld::RadioButton > m_xLetterRB
A MailDispatcher should be used for sending a bunch a mail messages asynchronously.
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
bool IsMailReplyTo() const
::rtl::Reference< MailDispatcher > xMailDispatcher
virtual ~SwSendMailDialog() override
uno::Reference< mail::XMailService > xConnectedInMailService
sal_uInt16 nPos
std::unique_ptr< weld::Label > m_xErrorStatus
bool IsOutputToLetter() const