korganizer

incidencechanger.cpp
1 /*
2  This file is part of KOrganizer.
3 
4  Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20  As a special exception, permission is given to link this program
21  with any edition of TQt, and distribute the resulting executable,
22  without including the source code for TQt in the source distribution.
23 */
24 
25 #include "incidencechanger.h"
26 #include "koglobals.h"
27 #include "koprefs.h"
28 #include "kogroupware.h"
29 #include "mailscheduler.h"
30 
31 #include <libkcal/freebusy.h>
32 #include <libkcal/dndfactory.h>
33 #include <kdebug.h>
34 #include <tdemessagebox.h>
35 #include <tdelocale.h>
36 
37 bool IncidenceChanger::beginChange( Incidence *incidence,
38  ResourceCalendar *res, const TQString &subRes )
39 {
40  if ( !incidence ) {
41  return false;
42  }
43 
44  kdDebug(5850) << "IncidenceChanger::beginChange for incidence \""
45  << incidence->summary() << "\"" << endl;
46 
47  CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
48  if ( !calRes ) {
49  return false;
50  }
51 
52  return calRes->beginChange( incidence, res, subRes );
53 }
54 
55 bool IncidenceChanger::sendGroupwareMessage( Incidence *incidence,
57  KOGlobals::HowChanged action,
58  TQWidget *parent )
59 {
60  if ( KOPrefs::instance()->thatIsMe( incidence->organizer().email() ) && incidence->attendeeCount()>0
61  && !KOPrefs::instance()->mUseGroupwareCommunication ) {
62  emit schedule( method, incidence );
63  return true;
64  } else if( KOPrefs::instance()->mUseGroupwareCommunication ) {
65  return
66  KOGroupware::instance()->sendICalMessage( parent, method, incidence, action, false );
67  }
68  return true;
69 }
70 
71 void IncidenceChanger::cancelAttendees( Incidence *incidence )
72 {
73  if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
74  if ( KMessageBox::questionYesNo( 0, i18n("Some attendees were removed "
75  "from the incidence. Shall cancel messages be sent to these attendees?"),
76  i18n( "Attendees Removed" ), i18n("Send Messages"), i18n("Do Not Send") ) == KMessageBox::Yes ) {
77  // don't use KOGroupware::sendICalMessage here, because that asks just
78  // a very general question "Other people are involved, send message to
79  // them?", which isn't helpful at all in this situation. Afterwards, it
80  // would only call the MailScheduler::performTransaction, so do this
81  // manually.
82  // FIXME: Groupware scheduling should be factored out to it's own class
83  // anyway
84  KCal::MailScheduler scheduler( mCalendar );
85  scheduler.performTransaction( incidence, Scheduler::Cancel );
86  }
87  }
88 }
89 
90 bool IncidenceChanger::endChange( Incidence *incidence,
91  ResourceCalendar *res, const TQString &subRes )
92 {
93  // FIXME: if that's a groupware incidence, and I'm not the organizer,
94  // send out a mail to the organizer with a counterproposal instead
95  // of actually changing the incidence. Then no locking is needed.
96  // FIXME: if that's a groupware incidence, and the incidence was
97  // never locked, we can't unlock it with endChange().
98 
99  if ( !incidence ) {
100  return false;
101  }
102 
103  kdDebug(5850) << "IncidenceChanger::endChange for incidence \""
104  << incidence->summary() << "\"" << endl;
105 
106  CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
107  if ( !calRes ) {
108  return false;
109  }
110 
111  return calRes->endChange( incidence, res, subRes );
112 }
113 
114 bool IncidenceChanger::deleteIncidence( Incidence *incidence, TQWidget *parent )
115 {
116  if ( !incidence ) return true;
117 kdDebug(5850)<<"IncidenceChanger::deleteIncidence for incidence \""<<incidence->summary()<<"\""<<endl;
118  bool doDelete = sendGroupwareMessage( incidence, KCal::Scheduler::Cancel,
119  KOGlobals::INCIDENCEDELETED, parent );
120  if( doDelete ) {
121  // @TODO: let Calendar::deleteIncidence do the locking...
122  Incidence* tmp = incidence->clone();
123  emit incidenceToBeDeleted( incidence );
124  doDelete = mCalendar->deleteIncidence( incidence );
125  if ( !KOPrefs::instance()->thatIsMe( tmp->organizer().email() ) ) {
126  const TQStringList myEmails = KOPrefs::instance()->allEmails();
127  bool notifyOrganizer = false;
128  for ( TQStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
129  TQString email = *it;
130  Attendee *me = tmp->attendeeByMail(email);
131  if ( me ) {
132  if ( me->status() == KCal::Attendee::Accepted || me->status() == KCal::Attendee::Delegated )
133  notifyOrganizer = true;
134  Attendee *newMe = new Attendee( *me );
135  newMe->setStatus( KCal::Attendee::Declined );
136  tmp->clearAttendees();
137  tmp->addAttendee( newMe );
138  break;
139  }
140  }
141 
142  if ( !KOGroupware::instance()->doNotNotify() && notifyOrganizer ) {
143  KCal::MailScheduler scheduler( mCalendar );
144  scheduler.performTransaction( tmp, Scheduler::Reply );
145  }
146  //reset the doNotNotify flag
147  KOGroupware::instance()->setDoNotNotify( false );
148  }
149  emit incidenceDeleted( incidence );
150  }
151  return doDelete;
152 }
153 
154 bool IncidenceChanger::cutIncidences( const Incidence::List &incidences,
155  TQWidget *parent )
156 {
157  Incidence::List::ConstIterator it;
158  bool doDelete = true;
159  Incidence::List incsToCut;
160  for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
161  if ( *it ) {
162  doDelete = sendGroupwareMessage( *it, KCal::Scheduler::Cancel,
163  KOGlobals::INCIDENCEDELETED, parent );
164  if ( doDelete ) {
165  emit incidenceToBeDeleted( *it );
166  incsToCut.append( *it );
167  }
168  }
169  }
170 
171  DndFactory factory( mCalendar );
172 
173  if ( factory.cutIncidences( incsToCut ) ) {
174  for ( it = incsToCut.constBegin(); it != incsToCut.constEnd(); ++it ) {
175  emit incidenceDeleted( *it );
176  }
177  return !incsToCut.isEmpty();
178  } else {
179  return false;
180  }
181 }
182 
183 bool IncidenceChanger::cutIncidence( Incidence *incidence, TQWidget *parent )
184 {
185  Incidence::List incidences;
186  incidences.append( incidence );
187  return cutIncidences( incidences, parent );
188 }
189 
190 class IncidenceChanger::ComparisonVisitor : public IncidenceBase::Visitor
191 {
192  public:
193  ComparisonVisitor() {}
194  bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
195  {
196  mIncidence2 = inc2;
197  if ( incidence )
198  return incidence->accept( *this );
199  else
200  return (inc2 == 0);
201  }
202  protected:
203  bool visit( Event *event )
204  {
205  Event *ev2 = dynamic_cast<Event*>(mIncidence2);
206  if ( event && ev2 ) {
207  return *event == *ev2;
208  } else {
209  // either both 0, or return false;
210  return ( ev2 == event );
211  }
212  }
213  bool visit( Todo *todo )
214  {
215  Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
216  if ( todo && to2 ) {
217  return *todo == *to2;
218  } else {
219  // either both 0, or return false;
220  return ( todo == to2 );
221  }
222  }
223  bool visit( Journal *journal )
224  {
225  Journal *j2 = dynamic_cast<Journal*>( mIncidence2 );
226  if ( journal && j2 ) {
227  return *journal == *j2;
228  } else {
229  // either both 0, or return false;
230  return ( journal == j2 );
231  }
232  }
233  bool visit( FreeBusy *fb )
234  {
235  FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
236  if ( fb && fb2 ) {
237  return *fb == *fb2;
238  } else {
239  // either both 0, or return false;
240  return ( fb2 == fb );
241  }
242  }
243 
244  protected:
245  IncidenceBase *mIncidence2;
246 };
247 
248 class IncidenceChanger::AssignmentVisitor : public IncidenceBase::Visitor
249 {
250  public:
251  AssignmentVisitor() {}
252  bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
253  {
254  mIncidence2 = inc2;
255  if ( incidence )
256  return incidence->accept( *this );
257  else
258  return false;
259  }
260  protected:
261  bool visit( Event *event )
262  {
263  Event *ev2 = dynamic_cast<Event*>( mIncidence2 );
264  if ( event && ev2 ) {
265  *event = *ev2;
266  return true;
267  } else {
268  return false;
269  }
270  }
271  bool visit( Todo *todo )
272  {
273  Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
274  if ( todo && to2 ) {
275  *todo = *to2;
276  return true;
277  } else {
278  return false;
279  }
280  }
281  bool visit( Journal *journal )
282  {
283  Journal *j2 = dynamic_cast<Journal*>(mIncidence2);
284  if ( journal && j2 ) {
285  *journal = *j2;
286  return true;
287  } else {
288  return false;
289  }
290  }
291  bool visit( FreeBusy *fb )
292  {
293  FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
294  if ( fb && fb2 ) {
295  *fb = *fb2;
296  return true;
297  } else {
298  return false;
299  }
300  }
301 
302  protected:
303  IncidenceBase *mIncidence2;
304 };
305 
306 bool IncidenceChanger::incidencesEqual( Incidence *inc1, Incidence *inc2 )
307 {
309  return ( v.act( inc1, inc2 ) );
310 }
311 
312 bool IncidenceChanger::assignIncidence( Incidence *inc1, Incidence *inc2 )
313 {
315  return v.act( inc1, inc2 );
316 }
317 
318 bool IncidenceChanger::myAttendeeStatusChanged( Incidence *oldInc, Incidence *newInc )
319 {
320  Attendee *oldMe = oldInc->attendeeByMails( KOPrefs::instance()->allEmails() );
321  Attendee *newMe = newInc->attendeeByMails( KOPrefs::instance()->allEmails() );
322  if ( oldMe && newMe && ( oldMe->status() != newMe->status() ) )
323  return true;
324 
325  return false;
326 }
327 
328 bool IncidenceChanger::changeIncidence( Incidence *oldinc, Incidence *newinc,
329  KOGlobals::WhatChanged action,
330  TQWidget *parent )
331 {
332  return changeIncidence( oldinc, newinc, action, parent, 0 );
333 }
334 
335 bool IncidenceChanger::changeIncidence( Incidence *oldinc, Incidence *newinc,
336  KOGlobals::WhatChanged action,
337  TQWidget *parent,
338  int dontAskForGroupware )
339 {
340 kdDebug(5850)<<"IncidenceChanger::changeIncidence for incidence \""<<newinc->summary()<<"\" ( old one was \""<<oldinc->summary()<<"\")"<<endl;
341  if ( incidencesEqual( newinc, oldinc ) ) {
342  // Don't do anything
343  kdDebug(5850) << "Incidence not changed\n";
344  } else {
345  kdDebug(5850) << "Incidence changed\n";
346  bool attendeeStatusChanged = myAttendeeStatusChanged( oldinc, newinc );
347  int revision = newinc->revision();
348  newinc->setRevision( revision + 1 );
349  // FIXME: Use a generic method for this! Ideally, have an interface class
350  // for group scheduling. Each implementation could then just do what
351  // it wants with the event. If no groupware is used,use the null
352  // pattern...
353  bool success = true;
354  if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
355  success = KOGroupware::instance()->sendICalMessage(
356  parent,
357  KCal::Scheduler::Request,
358  newinc, KOGlobals::INCIDENCEEDITED, attendeeStatusChanged, dontAskForGroupware );
359  }
360 
361  if ( success ) {
362  // Accept the event changes
363  emit incidenceChanged( oldinc, newinc, action );
364  } else {
365  // revert changes
366  assignIncidence( newinc, oldinc );
367  return false;
368  }
369  }
370  return true;
371 }
372 
373 bool IncidenceChanger::addIncidence( Incidence *incidence,
374  ResourceCalendar *res, const TQString &subRes,
375  TQWidget *parent )
376 {
377  return addIncidence( incidence, res, subRes, parent, 0 );
378 }
379 
380 bool IncidenceChanger::addIncidence( Incidence *incidence,
381  ResourceCalendar *res, const TQString &subRes,
382  TQWidget *parent, int dontAskForGroupware )
383 {
384  CalendarResources *stdcal = dynamic_cast<CalendarResources *>( mCalendar );
385  if( stdcal && !stdcal->hasCalendarResources() ) {
386  KMessageBox::sorry(
387  parent,
388  i18n( "No calendars found, unable to save %1 \"%2\"." ).
389  arg( i18n( incidence->type() ) ).
390  arg( incidence->summary() ) );
391  kdDebug(5850) << "IncidenceChanger: No calendars found" << endl;
392  return false;
393  }
394 
395  // FIXME: This is a nasty hack, since we need to set a parent for the
396  // resource selection dialog. However, we don't have any UI methods
397  // in the calendar, only in the CalendarResources::DestinationPolicy
398  // So we need to type-cast it and extract it from the CalendarResources
399  TQWidget *tmpparent = 0;
400  if ( stdcal ) {
401  tmpparent = stdcal->dialogParentWidget();
402  stdcal->setDialogParentWidget( parent );
403  }
404 
405  // If a ResourceCalendar isn't provided, then try to compute one
406  // along with any subResource from the incidence.
407  ResourceCalendar *pRes = res;
408  TQString pSubRes = subRes;
409  TQString pResName;
410  if ( !pRes ) {
411  if ( stdcal ) {
412  pRes = stdcal->resource( incidence );
413  if ( pRes ) {
414  pResName = pRes->resourceName();
415  if ( pRes->canHaveSubresources() ) {
416  pSubRes = pRes->subresourceIdentifier( incidence );
417  pResName = pRes->labelForSubresource( pSubRes );
418  }
419  }
420  }
421  }
422 
423  bool success = false;
424  if ( stdcal && pRes && !pRes->readOnly() && pRes->subresourceWritable( pSubRes ) ) {
425  success = stdcal->addIncidence( incidence, pRes, pSubRes );
426  } else {
427  success = mCalendar->addIncidence( incidence );
428  }
429 
430  if ( !success ) {
431  // We can have a failure if the user pressed [cancel] in the resource
432  // selectdialog, so check the exception.
433  ErrorFormat *e = stdcal ? stdcal->exception() : 0;
434  if ( !e ||
435  ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel &&
437  TQString errMessage;
438  if ( pResName.isEmpty() ) {
439  errMessage = i18n( "Unable to save %1 \"%2\"." ).
440  arg( i18n( incidence->type() ) ).
441  arg( incidence->summary() );
442  } else {
443  errMessage = i18n( "Unable to save %1 \"%2\" to calendar %3." ).
444  arg( i18n( incidence->type() ) ).
445  arg( incidence->summary() ).
446  arg( pResName );
447  }
448  KMessageBox::sorry( parent, errMessage );
449  }
450  kdDebug(5850) << "IncidenceChanger: Can't add incidence" << endl;
451  return false;
452  }
453 
454  if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
455  if ( !KOGroupware::instance()->sendICalMessage(
456  parent,
457  KCal::Scheduler::Request,
458  incidence, KOGlobals::INCIDENCEADDED, false, dontAskForGroupware ) ) {
459  KMessageBox::sorry(
460  parent,
461  i18n( "Attempt to send the scheduling message failed. "
462  "Please check your Group Scheduling settings. "
463  "Contact your system administrator for more help.") );
464  }
465  }
466 
467  emit incidenceAdded( incidence );
468  return true;
469 }
470 
471 
472 #include "incidencechanger.moc"
473 #include "incidencechangerbase.moc"
void setStatus(PartStat s)
PartStat status() const
ResourceCalendar * resource(Incidence *incidence)
void setDialogParentWidget(TQWidget *parent)
bool addIncidence(Incidence *incidence)
TDE_DEPRECATED bool beginChange(Incidence *incidence)
TQWidget * dialogParentWidget()
TDE_DEPRECATED bool endChange(Incidence *incidence)
ErrorFormat * exception() const
ErrorCodeFormat errorCode()
virtual bool visit(Event *)
int attendeeCount() const
Attendee * attendeeByMail(const TQString &) const
Attendee * attendeeByMails(const TQStringList &, const TQString &email=TQString()) const
virtual bool accept(Visitor &)
void addAttendee(Attendee *attendee, bool doUpdate=true)
virtual Incidence * clone()=0
int revision() const
TQString summary() const
void setRevision(int rev)
virtual TQString subresourceIdentifier(Incidence *incidence)
virtual bool subresourceWritable(const TQString &) const
virtual bool canHaveSubresources() const
virtual const TQString labelForSubresource(const TQString &resource) const