• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • akonadi
  • calendar
itiphandler.cpp
1 /*
2  Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
3  <info@klaralvdalens-datakonsult.se>
4 
5  Copyright (C) 2010 Bertjan Broeksema <broeksema@kde.org>
6  Copyright (C) 2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
7 
8  Copyright (C) 2012 SĂ©rgio Martins <iamsergio@gmail.com>
9 
10  This library is free software; you can redistribute it and/or modify it
11  under the terms of the GNU Library General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or (at your
13  option) any later version.
14 
15  This library is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
18  License for more details.
19 
20  You should have received a copy of the GNU Library General Public License
21  along with this library; see the file COPYING.LIB. If not, write to the
22  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23  02110-1301, USA.
24 */
25 
26 #include "itiphandler.h"
27 #include "itiphandler_p.h"
28 #include "itiphandlerhelper_p.h"
29 #include "calendarsettings.h"
30 #include "publishdialog.h"
31 #include "utils_p.h"
32 #include "mailclient_p.h"
33 
34 #include <kcalcore/icalformat.h>
35 #include <kcalcore/incidence.h>
36 #include <kcalcore/schedulemessage.h>
37 #include <kcalcore/attendee.h>
38 #include <kcalutils/stringify.h>
39 
40 #include <kpimidentities/identitymanager.h>
41 #include <mailtransport/transportmanager.h>
42 
43 #include <KMessageBox>
44 #include <KLocalizedString>
45 
46 using namespace Akonadi;
47 
48 // async emittion
49 static void emitiTipMessageProcessed(ITIPHandler *handler,
50  ITIPHandler::Result resultCode,
51  const QString &errorString)
52 {
53  QMetaObject::invokeMethod(handler, "iTipMessageProcessed", Qt::QueuedConnection,
54  Q_ARG(Akonadi::ITIPHandler::Result, resultCode),
55  Q_ARG(QString, errorString));
56 }
57 
58 GroupwareUiDelegate::~GroupwareUiDelegate()
59 {
60 }
61 
62 ITIPHandler::ITIPHandler(QObject *parent) : QObject(parent)
63  , d(new Private(this))
64 {
65  qRegisterMetaType<Akonadi::ITIPHandler::Result>("Akonadi::ITIPHandler::Result");
66 }
67 
68 ITIPHandler::~ITIPHandler()
69 {
70  delete d;
71 }
72 
73 void ITIPHandler::processiTIPMessage(const QString &receiver,
74  const QString &iCal,
75  const QString &action)
76 {
77  kDebug() << "processiTIPMessage called with receiver=" << receiver
78  << "; action=" << action;
79 
80  if (d->m_currentOperation != OperationNone) {
81  d->m_currentOperation = OperationNone;
82  kFatal() << "There can't be an operation in progress!" << d->m_currentOperation;
83  return;
84  }
85 
86  d->m_currentOperation = OperationProcessiTIPMessage;
87 
88  if (!d->isLoaded()) {
89  d->m_queuedInvitation.receiver = receiver;
90  d->m_queuedInvitation.iCal = iCal;
91  d->m_queuedInvitation.action = action;
92  return;
93  }
94 
95  if (d->m_calendarLoadError) {
96  d->m_currentOperation = OperationNone;
97  kError() << "Error loading calendar";
98  emitiTipMessageProcessed(this, ResultError, i18n("Error loading calendar."));
99  return;
100  }
101 
102  KCalCore::ICalFormat format;
103  KCalCore::ScheduleMessage::Ptr message = format.parseScheduleMessage(d->calendar(), iCal);
104 
105  if (!message) {
106  const QString errorMessage = format.exception() ? i18n("Error message: %1", KCalUtils::Stringify::errorMessage(*format.exception()))
107  : i18n("Unknown error while parsing iCal invitation");
108 
109  kError() << "Error parsing" << errorMessage;
110 
111  if (d->m_showDialogsOnError) {
112  KMessageBox::detailedError(0, // mParent, TODO
113  i18n("Error while processing an invitation or update."),
114  errorMessage);
115  }
116 
117  d->m_currentOperation = OperationNone;
118  emitiTipMessageProcessed(this, ResultError, errorMessage);
119 
120  return;
121  }
122 
123  d->m_method = static_cast<KCalCore::iTIPMethod>(message->method());
124 
125  KCalCore::ScheduleMessage::Status status = message->status();
126  d->m_incidence = message->event().dynamicCast<KCalCore::Incidence>();
127  if (!d->m_incidence) {
128  kError() << "Invalid incidence";
129  d->m_currentOperation = OperationNone;
130  emitiTipMessageProcessed(this, ResultError, i18n("Invalid incidence"));
131  return;
132  }
133 
134  if (action.startsWith(QLatin1String("accepted")) ||
135  action.startsWith(QLatin1String("tentative")) ||
136  action.startsWith(QLatin1String("delegated")) ||
137  action.startsWith(QLatin1String("counter"))) {
138  // Find myself and set my status. This can't be done in the scheduler,
139  // since this does not know the choice I made in the KMail bpf
140  const KCalCore::Attendee::List attendees = d->m_incidence->attendees();
141  foreach(KCalCore::Attendee::Ptr attendee, attendees) {
142  if (attendee->email() == receiver) {
143  if (action.startsWith(QLatin1String("accepted"))) {
144  attendee->setStatus(KCalCore::Attendee::Accepted);
145  } else if (action.startsWith(QLatin1String("tentative"))) {
146  attendee->setStatus(KCalCore::Attendee::Tentative);
147  } else if (CalendarSettings::self()->outlookCompatCounterProposals() &&
148  action.startsWith(QLatin1String("counter"))) {
149  attendee->setStatus(KCalCore::Attendee::Tentative);
150  } else if (action.startsWith(QLatin1String("delegated"))) {
151  attendee->setStatus(KCalCore::Attendee::Delegated);
152  }
153  break;
154  }
155  }
156  if (CalendarSettings::self()->outlookCompatCounterProposals() ||
157  !action.startsWith(QLatin1String("counter"))) {
158  d->m_scheduler->acceptTransaction(d->m_incidence, d->calendar(), d->m_method, status, receiver);
159  return; // signal emitted in onSchedulerFinished().
160  }
161  //TODO: what happens here? we must emit a signal
162  } else if (action.startsWith(QLatin1String("cancel"))) {
163  // Delete the old incidence, if one is present
164  KCalCore::Incidence::Ptr existingIncidence = d->calendar()->incidenceFromSchedulingID(d->m_incidence->uid());
165  if (existingIncidence) {
166  d->m_scheduler->acceptTransaction(d->m_incidence, d->calendar(), KCalCore::iTIPCancel, status, receiver);
167  return; // signal emitted in onSchedulerFinished().
168  } else {
169  // We don't have the incidence, nothing to cancel
170  kWarning() << "Couldn't find the incidence to delete.\n"
171  << "You deleted it previously or didn't even accept the invitation it in the first place.\n"
172  << "; uid=" << d->m_incidence->uid()
173  << "; identifier=" << d->m_incidence->instanceIdentifier()
174  << "; summary=" << d->m_incidence->summary();
175 
176  kDebug() << "\n Here's what we do have with such a summary:";
177  KCalCore::Incidence::List knownIncidences = calendar()->incidences();
178  foreach(const KCalCore::Incidence::Ptr &knownIncidence, knownIncidences) {
179  if (knownIncidence->summary() == d->m_incidence->summary()) {
180  kDebug() << "\nFound: uid=" << knownIncidence->uid()
181  << "; identifier=" << knownIncidence->instanceIdentifier()
182  << "; schedulingId" << knownIncidence->schedulingID();
183  }
184  }
185 
186  emitiTipMessageProcessed(this, ResultSuccess, QString());
187  }
188  } else if (action.startsWith(QLatin1String("reply"))) {
189  if (d->m_method != KCalCore::iTIPCounter) {
190  d->m_scheduler->acceptTransaction(d->m_incidence, d->calendar(), d->m_method, status, QString());
191  } else {
192  d->m_scheduler->acceptCounterProposal(d->m_incidence, d->calendar());
193  }
194  return; // signal emitted in onSchedulerFinished().
195  } else {
196  kError() << "Unknown incoming action" << action;
197 
198  d->m_currentOperation = OperationNone;
199  emitiTipMessageProcessed(this, ResultError, i18n("Invalid action: %1", action));
200  }
201 
202  if (action.startsWith(QLatin1String("counter"))) {
203  if (d->m_uiDelegate) {
204  Akonadi::Item item;
205  item.setMimeType(d->m_incidence->mimeType());
206  item.setPayload(KCalCore::Incidence::Ptr(d->m_incidence->clone()));
207 
208  // TODO_KDE5: This blocks because m_uiDelegate is not a QObject and can't emit a finished()
209  // signal. Make async in kde5
210  d->m_uiDelegate->requestIncidenceEditor(item);
211  KCalCore::Incidence::Ptr newIncidence;
212  if (item.hasPayload<KCalCore::Incidence::Ptr>()) {
213  newIncidence = item.payload<KCalCore::Incidence::Ptr>();
214  }
215 
216  if (*newIncidence == *d->m_incidence) {
217  emitiTipMessageProcessed(this, ResultCancelled, QString());
218  } else {
219  ITIPHandlerHelper::SendResult result = d->m_helper->sendCounterProposal(d->m_incidence, newIncidence);
220  if (result != ITIPHandlerHelper::ResultSuccess) {
221  // It gives success in all paths, this never happens
222  emitiTipMessageProcessed(this, ResultError, i18n("Error sending counter proposal"));
223  Q_ASSERT(false);
224  }
225  }
226  } else {
227  // This should never happen
228  kWarning() << "No UI delegate is set";
229  emitiTipMessageProcessed(this, ResultError, i18n("Could not start editor to edit counter proposal"));
230  }
231  }
232 }
233 
234 void ITIPHandler::sendiTIPMessage(KCalCore::iTIPMethod method,
235  const KCalCore::Incidence::Ptr &incidence,
236  QWidget *parentWidget)
237 {
238  if (!incidence) {
239  Q_ASSERT(false);
240  kError() << "Invalid incidence";
241  return;
242  }
243 
244  d->m_queuedInvitation.method = method;
245  d->m_queuedInvitation.incidence = incidence;
246  d->m_parentWidget = parentWidget;
247 
248  if (!d->isLoaded()) {
249  // This method will be called again once the calendar is loaded.
250  return;
251  }
252 
253  Q_ASSERT(d->m_currentOperation == OperationNone);
254  if (d->m_currentOperation != OperationNone) {
255  kError() << "There can't be an operation in progress!" << d->m_currentOperation;
256  return;
257  }
258 
259  if (incidence->attendeeCount() == 0 && method != KCalCore::iTIPPublish) {
260  if (d->m_showDialogsOnError) {
261  KMessageBox::information(parentWidget,
262  i18n("The item '%1' has no attendees. "
263  "Therefore no groupware message will be sent.",
264  incidence->summary()),
265  i18n("Message Not Sent"),
266  QLatin1String("ScheduleNoAttendees"));
267  }
268 
269  return;
270  }
271 
272  d->m_currentOperation = OperationSendiTIPMessage;
273 
274  KCalCore::Incidence *incidenceCopy = incidence->clone();
275  incidenceCopy->registerObserver(0);
276  incidenceCopy->clearAttendees();
277 
278  d->m_scheduler->performTransaction(incidence, method);
279 }
280 
281 void ITIPHandler::publishInformation(const KCalCore::Incidence::Ptr &incidence,
282  QWidget *parentWidget)
283 {
284  Q_ASSERT(incidence);
285  if (!incidence) {
286  kError() << "Invalid incidence. Aborting.";
287  return;
288  }
289 
290  Q_ASSERT(d->m_currentOperation == OperationNone);
291  if (d->m_currentOperation != OperationNone) {
292  kError() << "There can't be an operation in progress!" << d->m_currentOperation;
293  return;
294  }
295 
296  d->m_queuedInvitation.incidence = incidence;
297  d->m_parentWidget = parentWidget;
298 
299  d->m_currentOperation = OperationPublishInformation;
300 
301  QPointer<Akonadi::PublishDialog> publishdlg = new Akonadi::PublishDialog();
302  if (incidence->attendeeCount() > 0) {
303  KCalCore::Attendee::List attendees = incidence->attendees();
304  KCalCore::Attendee::List::ConstIterator it;
305  KCalCore::Attendee::List::ConstIterator end(attendees.constEnd());
306  for (it = attendees.constBegin(); it != end; ++it) {
307  publishdlg->addAttendee(*it);
308  }
309  }
310  if (publishdlg->exec() == QDialog::Accepted && publishdlg)
311  d->m_scheduler->publish(incidence, publishdlg->addresses());
312  else
313  emit informationPublished(ResultSuccess, QString()); // Canceled.
314  delete publishdlg;
315 }
316 
317 void ITIPHandler::sendAsICalendar(const KCalCore::Incidence::Ptr &originalIncidence,
318  QWidget *parentWidget)
319 {
320  Q_UNUSED(parentWidget);
321  Q_ASSERT(originalIncidence);
322  if (!originalIncidence) {
323  kError() << "Invalid incidence";
324  return;
325  }
326 
327  // Clone so we can change organizer and recurid
328  KCalCore::Incidence::Ptr incidence = KCalCore::Incidence::Ptr(originalIncidence->clone());
329 
330  KPIMIdentities::IdentityManager identityManager;
331 
332  QPointer<Akonadi::PublishDialog> publishdlg = new Akonadi::PublishDialog;
333  if (publishdlg->exec() == QDialog::Accepted && publishdlg) {
334  const QString recipients = publishdlg->addresses();
335  if (incidence->organizer()->isEmpty()) {
336  incidence->setOrganizer(KCalCore::Person::Ptr(
337  new KCalCore::Person(Akonadi::CalendarUtils::fullName(),
338  Akonadi::CalendarUtils::email())));
339  }
340 
341  if (incidence->hasRecurrenceId()) {
342  // For an individual occurrence, recur id doesn't make sense, since we're not sending the whole recurrence series.
343  incidence->setRecurrenceId(KDateTime());
344  }
345 
346  KCalCore::ICalFormat format;
347  const QString from = Akonadi::CalendarUtils::email();
348  const bool bccMe = Akonadi::CalendarSettings::self()->bcc();
349  const QString messageText = format.createScheduleMessage(incidence, KCalCore::iTIPRequest);
350  MailClient *mailer = new MailClient();
351  d->m_queuedInvitation.incidence = incidence;
352  connect(mailer, SIGNAL(finished(Akonadi::MailClient::Result,QString)),
353  d, SLOT(finishSendAsICalendar(Akonadi::MailScheduler::Result,QString)));
354 
355  mailer->mailTo(incidence, identityManager.identityForAddress(from), from, bccMe,
356  recipients, messageText,
357  MailTransport::TransportManager::self()->defaultTransportName());
358  }
359 }
360 
361 void ITIPHandler::setGroupwareUiDelegate(GroupwareUiDelegate *delegate)
362 {
363  d->m_uiDelegate = delegate;
364 }
365 
366 void ITIPHandler::setCalendar(const Akonadi::CalendarBase::Ptr &calendar)
367 {
368  if (d->m_calendar != calendar) {
369  d->m_calendar = calendar;
370  }
371 }
372 
373 void ITIPHandler::setShowDialogsOnError(bool enable)
374 {
375  d->m_showDialogsOnError = enable;
376 }
377 
378 Akonadi::CalendarBase::Ptr ITIPHandler::calendar() const
379 {
380  return d->m_calendar;
381 }
382 
Akonadi::ITIPHandler::calendar
Akonadi::CalendarBase::Ptr calendar() const
Returns the calendar used by this itip handler.
Definition: itiphandler.cpp:378
Akonadi::ITIPHandler::sendAsICalendar
void sendAsICalendar(const KCalCore::Incidence::Ptr &incidence, QWidget *parentWidget=0)
Sends an e-mail with the incidence attached as iCalendar source.
Definition: itiphandler.cpp:317
Akonadi::ITIPHandler::~ITIPHandler
~ITIPHandler()
Destroys this instance.
Definition: itiphandler.cpp:68
Akonadi::ITIPHandler::ResultError
An unexpected error occurred.
Definition: itiphandler.h:64
Akonadi::ITIPHandler::ResultCancelled
User cancelled the operation.
Definition: itiphandler.h:66
Akonadi::ITIPHandler::informationPublished
void informationPublished(Akonadi::ITIPHandler::Result, const QString &errorMessage)
Signal emitted after an incidence was published with publishInformation()
Akonadi::ITIPHandler::ResultSuccess
The invitation was successfuly handled.
Definition: itiphandler.h:65
Akonadi::ITIPHandler::publishInformation
void publishInformation(const KCalCore::Incidence::Ptr &incidence, QWidget *parentWidget=0)
Publishes incidence incidence.
Definition: itiphandler.cpp:281
Akonadi::ITIPHandler
Handles sending of iTip messages aswell as processing incoming ones.
Definition: itiphandler.h:59
Akonadi::ITIPHandler::processiTIPMessage
void processiTIPMessage(const QString &receiver, const QString &iCal, const QString &type)
Processes a received iTip message.
Definition: itiphandler.cpp:73
Akonadi::ITIPHandler::ITIPHandler
ITIPHandler(QObject *parent=0)
Creates a new ITIPHandler instance.
Definition: itiphandler.cpp:62
Akonadi::ITIPHandler::sendiTIPMessage
void sendiTIPMessage(KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence, QWidget *parentWidget=0)
Sends an iTip message.
Definition: itiphandler.cpp:234
Akonadi::ITIPHandler::setGroupwareUiDelegate
void setGroupwareUiDelegate(GroupwareUiDelegate *)
Sets the UI delegate to edit counter proposals.
Definition: itiphandler.cpp:361
Akonadi::ITIPHandlerHelper::ResultSuccess
The invitation was sent to all attendees.
Definition: itiphandlerhelper_p.h:82
Akonadi::ITIPHandlerHelper::SendResult
SendResult
Definition: itiphandlerhelper_p.h:74
Akonadi::ITIPHandler::setShowDialogsOnError
void setShowDialogsOnError(bool enable)
Sets if the ITIP handler should show dialogs on error.
Definition: itiphandler.cpp:373
Akonadi::ITIPHandler::Result
Result
Definition: itiphandler.h:63
Akonadi::ITIPHandler::setCalendar
void setCalendar(const Akonadi::CalendarBase::Ptr &)
Sets the calendar that the itip handler should use.
Definition: itiphandler.cpp:366
Akonadi::GroupwareUiDelegate
Ui delegate for editing counter proposals.
Definition: itiphandler.h:45
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal