]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxComponentEditor.m
d81be97153a3106e39ee79ee9b5d0c11e91c23ff
[scalable-opengroupware.org] / UI / Scheduler / UIxComponentEditor.m
1 /* UIxComponentEditor.m - this file is part of SOGo
2  *
3  * Copyright (C) 2006 Inverse groupe conseil
4  *
5  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This file is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #import <Foundation/NSArray.h>
24 #import <Foundation/NSBundle.h>
25 #import <Foundation/NSException.h>
26 #import <Foundation/NSCalendarDate.h>
27 #import <Foundation/NSKeyValueCoding.h>
28 #import <Foundation/NSString.h>
29 #import <Foundation/NSUserDefaults.h>
30 #import <Foundation/NSURL.h>
31
32 #import <NGCards/iCalPerson.h>
33 #import <NGCards/iCalRepeatableEntityObject.h>
34 #import <NGCards/iCalRecurrenceRule.h>
35 #import <NGCards/NSString+NGCards.h>
36 #import <NGCards/NSCalendarDate+NGCards.h>
37 #import <NGObjWeb/SoSecurityManager.h>
38 #import <NGObjWeb/NSException+HTTP.h>
39 #import <NGObjWeb/WORequest.h>
40 #import <NGExtensions/NSCalendarDate+misc.h>
41 #import <NGExtensions/NSObject+Logs.h>
42 #import <NGExtensions/NSString+misc.h>
43
44 #import <UI/SOGoUI/SOGoDateFormatter.h>
45 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
46 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
47 #import <SoObjects/Appointments/SOGoTaskObject.h>
48 #import <SoObjects/SOGo/NSString+Utilities.h>
49 #import <SoObjects/SOGo/SOGoUser.h>
50 #import <SoObjects/SOGo/SOGoPermissions.h>
51
52 #import "UIxComponent+Scheduler.h"
53
54 #import "UIxComponentEditor.h"
55
56 @implementation UIxComponentEditor
57
58 - (id) init
59 {
60   if ((self = [super init]))
61     {
62       component = nil;
63       [self setPrivacy: @"PUBLIC"];
64       [self setIsCycleEndNever];
65       componentOwner = @"";
66       organizer = nil;
67       attendeesNames = nil;
68       attendeesEmails = nil;
69       calendarList = nil;
70     }
71
72   return self;
73 }
74
75 - (void) dealloc
76 {
77   [item release];
78   [cycleUntilDate release];
79   [title release];
80   [location release];
81   [organizer release];
82   [comment release];
83   [priority release];
84   [categories release];
85   [cycle release];
86   [cycleEnd release];
87   [url release];
88   [attendeesNames release];
89   [attendeesEmails release];
90   [calendarList release];
91
92   [super dealloc];
93 }
94
95 - (void) _loadAttendees
96 {
97   NSEnumerator *attendees;
98   iCalPerson *currentAttendee;
99   NSMutableString *names, *emails;
100
101   names = [NSMutableString new];
102   emails = [NSMutableString new];
103
104   attendees = [[component attendees] objectEnumerator];
105   currentAttendee = [attendees nextObject];
106   while (currentAttendee)
107     {
108       NSLog (@"currentCN: %@", [currentAttendee cn]);
109       [names appendFormat: @"%@,", [currentAttendee cn]];
110       [emails appendFormat: @"%@,", [currentAttendee rfc822Email]];
111       currentAttendee = [attendees nextObject];
112     }
113
114   if ([names length] > 0)
115     {
116       ASSIGN (attendeesNames, [names substringToIndex: [names length] - 1]);
117       ASSIGN (attendeesEmails,
118               [emails substringToIndex: [emails length] - 1]);
119     }
120
121   [names release];
122   [emails release];
123 }
124
125 /* warning: we use this method which will be triggered by the template system
126    when the page is instantiated, but we should find another and cleaner way of
127    doing this... for example, when the clientObject is set */
128 - (void) setComponent: (iCalRepeatableEntityObject *) newComponent
129 {
130 //   iCalRecurrenceRule *rrule;
131   SOGoObject *co;
132
133   if (!component)
134     {
135       component = newComponent;
136
137       co = [self clientObject];
138       componentOwner = [co ownerInContext: nil];
139
140       ASSIGN (title, [component summary]);
141       ASSIGN (location, [component location]);
142       ASSIGN (comment, [component comment]);
143       ASSIGN (url, [[component url] absoluteString]);
144       ASSIGN (privacy, [component accessClass]);
145       ASSIGN (priority, [component priority]);
146       ASSIGN (status, [component status]);
147       ASSIGN (categories, [[component categories] commaSeparatedValues]);
148       ASSIGN (organizer, [component organizer]);
149       [self _loadAttendees];
150     }
151 //   /* cycles */
152 //   if ([component isRecurrent])
153 //     {
154 //       rrule = [[component recurrenceRules] objectAtIndex: 0];
155 //       [self adjustCycleControlsForRRule: rrule];
156 //     }
157 }
158
159 - (void) setSaveURL: (NSString *) newSaveURL
160 {
161   saveURL = newSaveURL;
162 }
163
164 - (NSString *) saveURL
165 {
166   return saveURL;
167 }
168
169 /* accessors */
170
171 - (void) setItem: (id) _item
172 {
173   ASSIGN (item, _item);
174 }
175
176 - (id) item
177 {
178   return item;
179 }
180
181 - (NSString *) itemPriorityText
182 {
183   return [self labelForKey: [NSString stringWithFormat: @"prio_%@", item]];
184 }
185
186 - (NSString *) itemPrivacyText
187 {
188   NSString *tag;
189
190   tag = [[self clientObject] componentTag];
191
192   return [self labelForKey: [NSString stringWithFormat: @"%@_%@", item, tag]];
193 }
194
195 - (NSString *) itemStatusText
196 {
197   return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]];
198 }
199
200 - (void) setTitle: (NSString *) _value
201 {
202   ASSIGN (title, _value);
203 }
204
205 - (NSString *) title
206 {
207   return title;
208 }
209
210 - (void) setUrl: (NSString *) _url
211 {
212   ASSIGN (url, _url);
213 }
214
215 - (NSString *) url
216 {
217   return url;
218 }
219
220 - (void) setAttendeesNames: (NSString *) newAttendeesNames
221 {
222   ASSIGN (attendeesNames, newAttendeesNames);
223 }
224
225 - (NSString *) attendeesNames
226 {
227   return attendeesNames;
228 }
229
230 - (void) setAttendeesEmails: (NSString *) newAttendeesEmails
231 {
232   ASSIGN (attendeesEmails, newAttendeesEmails);
233 }
234
235 - (NSString *) attendeesEmails
236 {
237   return attendeesEmails;
238 }
239
240 - (void) setLocation: (NSString *) _value
241 {
242   ASSIGN (location, _value);
243 }
244
245 - (NSString *) location
246 {
247   return location;
248 }
249
250 - (void) setComment: (NSString *) _value
251 {
252   ASSIGN (comment, _value);
253 }
254
255 - (NSString *) comment
256 {
257   return comment;
258 }
259
260 - (NSArray *) categoryList
261 {
262   static NSArray *categoryItems = nil;
263
264   if (!categoryItems)
265     {
266       categoryItems = [NSArray arrayWithObjects: @"ANNIVERSARY",
267                                @"BIRTHDAY",
268                                @"BUSINESS",
269                                @"CALLS", 
270                                @"CLIENTS",
271                                @"COMPETITION",
272                                @"CUSTOMER",
273                                @"FAVORITES",
274                                @"FOLLOW UP",
275                                @"GIFTS",
276                                @"HOLIDAYS",
277                                @"IDEAS",
278                                @"ISSUES",
279                                @"MISCELLANEOUS",
280                                @"PERSONAL",
281                                @"PROJECTS",
282                                @"PUBLIC HOLIDAY",
283                                @"STATUS",
284                                @"SUPPLIERS",
285                                @"TRAVEL",
286                                @"VACATION",
287                               nil];
288       [categoryItems retain];
289     }
290
291   return categoryItems;
292 }
293
294 - (void) setCategories: (NSArray *) _categories
295 {
296   ASSIGN (categories, _categories);
297 }
298
299 - (NSArray *) categories
300 {
301   return categories;
302 }
303
304 - (NSString *) itemCategoryText
305 {
306   return [self labelForKey:
307                  [NSString stringWithFormat: @"category_%@", item]];
308 }
309
310 - (NSArray *) calendarList
311 {
312   SOGoAppointmentFolder *folder;
313   NSEnumerator *allCalendars;
314   NSDictionary *currentCalendar;
315
316   if (!calendarList)
317     {
318       calendarList = [NSMutableArray new];
319       folder = [[self clientObject] container];
320       allCalendars = [[folder calendarFolders] objectEnumerator];
321       currentCalendar = [allCalendars nextObject];
322       while (currentCalendar)
323         {
324           if ([[currentCalendar objectForKey: @"active"] boolValue])
325             [calendarList addObject: currentCalendar];
326           currentCalendar = [allCalendars nextObject];
327         }
328     }
329
330   return calendarList;
331 }
332
333 - (NSString *) calendarsFoldersList
334 {
335   NSArray *calendars;
336
337   calendars = [[self calendarList] valueForKey: @"folder"];
338
339   return [calendars componentsJoinedByString: @","];
340 }
341
342 - (NSString *) componentCalendar
343 {
344   return @"/";
345 }
346
347 /* priorities */
348
349 - (NSArray *) priorities
350 {
351   /* 0 == undefined
352      5 == normal
353      1 == high
354   */
355   static NSArray *priorities = nil;
356
357   if (!priorities)
358     {
359       priorities = [NSArray arrayWithObjects: @"0", @"5", @"1", nil];
360       [priorities retain];
361     }
362
363   return priorities;
364 }
365
366 - (void) setPriority: (NSString *) _priority
367 {
368   ASSIGN (priority, _priority);
369 }
370
371 - (NSString *) priority
372 {
373   return priority;
374 }
375
376 - (NSArray *) privacyClasses
377 {
378   static NSArray *priorities = nil;
379
380   if (!priorities)
381     {
382       priorities = [NSArray arrayWithObjects: @"PUBLIC",
383                             @"CONFIDENTIAL", @"PRIVATE", nil];
384       [priorities retain];
385     }
386
387   return priorities;
388 }
389
390 - (void) setPrivacy: (NSString *) _privacy
391 {
392   ASSIGN (privacy, _privacy);
393 }
394
395 - (NSString *) privacy
396 {
397   return privacy;
398 }
399
400 - (NSArray *) statusTypes
401 {
402   static NSArray *priorities = nil;
403
404   if (!priorities)
405     {
406       priorities = [NSArray arrayWithObjects: @"", @"TENTATIVE", @"CONFIRMED", @"CANCELLED", nil];
407       [priorities retain];
408     }
409
410   return priorities;
411 }
412
413 - (void) setStatus: (NSString *) _status
414 {
415   ASSIGN (status, _status);
416 }
417
418 - (NSString *) status
419 {
420   return status;
421 }
422
423 - (NSArray *) cycles
424 {
425   NSBundle *bundle;
426   NSString *path;
427   static NSArray *cycles = nil;
428   
429   if (!cycles)
430     {
431       bundle = [NSBundle bundleForClass:[self class]];
432       path   = [bundle pathForResource: @"cycles" ofType: @"plist"];
433       NSAssert(path != nil, @"Cannot find cycles.plist!");
434       cycles = [[NSArray arrayWithContentsOfFile:path] retain];
435       NSAssert(cycles != nil, @"Cannot instantiate cycles from cycles.plist!");
436     }
437
438   return cycles;
439 }
440
441 - (void) setCycle: (NSDictionary *) _cycle
442 {
443   ASSIGN (cycle, _cycle);
444 }
445
446 - (NSDictionary *) cycle
447 {
448   return cycle;
449 }
450
451 - (BOOL) hasCycle
452 {
453   return ([cycle objectForKey: @"rule"] != nil);
454 }
455
456 - (NSString *) cycleLabel
457 {
458   NSString *key;
459   
460   key = [(NSDictionary *)item objectForKey: @"label"];
461
462   return [self labelForKey:key];
463 }
464
465 - (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate
466 {
467 //   NSCalendarDate *until;
468
469 //   /* copy hour/minute/second from startDate */
470 //   until = [_cycleUntilDate hour: [startDate hourOfDay]
471 //                            minute: [startDate minuteOfHour]
472 //                            second: [startDate secondOfMinute]];
473 //   [until setTimeZone: [startDate timeZone]];
474 //   ASSIGN (cycleUntilDate, until);
475 }
476
477 - (NSCalendarDate *) cycleUntilDate
478 {
479   return cycleUntilDate;
480 }
481
482 - (iCalRecurrenceRule *) rrule
483 {
484   NSString *ruleRep;
485   iCalRecurrenceRule *rule;
486
487   if (![self hasCycle])
488     return nil;
489   ruleRep = [cycle objectForKey: @"rule"];
490   rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep];
491
492   if (cycleUntilDate && [self isCycleEndUntil])
493     [rule setUntilDate:cycleUntilDate];
494
495   return rule;
496 }
497
498 - (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule
499 {
500 //   NSDictionary *c;
501 //   NSCalendarDate *until;
502   
503 //   c = [self cycleMatchingRRule:_rrule];
504 //   [self setCycle:c];
505
506 //   until = [[[_rrule untilDate] copy] autorelease];
507 //   if (!until)
508 //     until = startDate;
509 //   else
510 //     [self setIsCycleEndUntil];
511
512 //   [until setTimeZone:[[self clientObject] userTimeZone]];
513 //   [self setCycleUntilDate:until];
514 }
515
516 /*
517  This method is necessary, because we have a fixed sets of cycles in the UI.
518  The model is able to represent arbitrary rules, however.
519  There SHOULD be a different UI, similar to iCal.app, to allow modelling
520  of more complex rules.
521  
522  This method obviously cannot map all existing rules back to the fixed list
523  in cycles.plist. This should be fixed in a future version when interop
524  becomes more important.
525  */
526 - (NSDictionary *) cycleMatchingRRule: (iCalRecurrenceRule *) _rrule
527 {
528   NSString *cycleRep;
529   NSArray *cycles;
530   unsigned i, count;
531
532   if (!_rrule)
533     return [[self cycles] objectAtIndex:0];
534
535   cycleRep = [_rrule versitString];
536   cycles   = [self cycles];
537   count    = [cycles count];
538   for (i = 1; i < count; i++) {
539     NSDictionary *c;
540     NSString *cr;
541
542     c  = [cycles objectAtIndex:i];
543     cr = [c objectForKey: @"rule"];
544     if ([cr isEqualToString:cycleRep])
545       return c;
546   }
547   [self warnWithFormat: @"No default cycle for rrule found! -> %@", _rrule];
548   return nil;
549 }
550
551 /* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */
552 - (NSArray *) cycleEnds
553 {
554   static NSArray *ends = nil;
555   
556   if (!ends)
557     {
558       ends = [NSArray arrayWithObjects: @"cycle_end_never",
559                       @"cycle_end_until", nil];
560       [ends retain];
561     }
562
563   return ends;
564 }
565
566 - (void) setCycleEnd: (NSString *) _cycleEnd
567 {
568   ASSIGN (cycleEnd, _cycleEnd);
569 }
570
571 - (NSString *) cycleEnd
572 {
573   return cycleEnd;
574 }
575
576 - (BOOL) isCycleEndUntil
577 {
578   return (cycleEnd && [cycleEnd isEqualToString: @"cycle_end_until"]);
579 }
580
581 - (void) setIsCycleEndUntil
582 {
583   [self setCycleEnd: @"cycle_end_until"];
584 }
585
586 - (void) setIsCycleEndNever
587 {
588   [self setCycleEnd: @"cycle_end_never"];
589 }
590
591 /* helpers */
592 - (NSFormatter *) titleDateFormatter
593 {
594   SOGoDateFormatter *fmt;
595   
596   fmt = [[SOGoDateFormatter alloc] initWithLocale: [self locale]];
597   [fmt autorelease];
598   [fmt setFullWeekdayNameAndDetails];
599
600   return fmt;
601 }
602
603 - (NSString *) completeURIForMethod: (NSString *) _method
604 {
605   NSString *uri;
606   NSRange r;
607     
608   uri = [[[self context] request] uri];
609     
610   /* first: identify query parameters */
611   r = [uri rangeOfString: @"?" options:NSBackwardsSearch];
612   if (r.length > 0)
613     uri = [uri substringToIndex:r.location];
614     
615   /* next: append trailing slash */
616   if (![uri hasSuffix: @"/"])
617     uri = [uri stringByAppendingString: @"/"];
618   
619   /* next: append method */
620   uri = [uri stringByAppendingString:_method];
621     
622   /* next: append query parameters */
623   return [self completeHrefForMethod:uri];
624 }
625
626 - (BOOL) isWriteableClientObject
627 {
628   return [[self clientObject] 
629                 respondsToSelector: @selector(saveContentString:)];
630 }
631
632 - (BOOL) containsConflict: (id) _component
633 {
634   [self subclassResponsibility: _cmd];
635
636   return NO;
637 }
638
639 /* access */
640
641 #if 0
642 - (iCalPerson *) getOrganizer
643 {
644   iCalPerson *p;
645   NSString *emailProp;
646   
647   emailProp = [@"MAILTO:" stringByAppendingString:[self emailForUser]];
648   p = [[[iCalPerson alloc] init] autorelease];
649   [p setEmail:emailProp];
650   [p setCn:[self cnForUser]];
651   return p;
652 }
653 #endif
654
655 - (BOOL) isMyComponent
656 {
657   return ([[context activeUser] hasEmail: [organizer rfc822Email]]);
658 }
659
660 - (BOOL) canEditComponent
661 {
662   return [self isMyComponent];
663 }
664
665 /* response generation */
666
667 - (NSString *) initialCycleVisibility
668 {
669   return ([self hasCycle]
670           ? @"visibility: visible;"
671           : @"visibility: hidden;");
672 }
673
674 - (NSString *) initialCycleEndUntilVisibility {
675   return ([self isCycleEndUntil]
676           ? @"visibility: visible;"
677           : @"visibility: hidden;");
678 }
679
680 // - (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters
681 // {
682 //   NSString *s;
683   
684 //   s = [self iCalParticipantsStringFromQueryParameters];
685 //   return [s stringByAppendingString:
686 //               [self iCalResourcesStringFromQueryParameters]];
687 // }
688
689 // - (NSString *) iCalParticipantsStringFromQueryParameters
690 // {
691 //   static NSString *iCalParticipantString = @"ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=\"%@\":MAILTO:%@\r\n";
692   
693 //   return [self iCalStringFromQueryParameter: @"ps"
694 //                format: iCalParticipantString];
695 // }
696
697 // - (NSString *) iCalResourcesStringFromQueryParameters
698 // {
699 //   static NSString *iCalResourceString = @"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":MAILTO:%@\r\n";
700
701 //   return [self iCalStringFromQueryParameter: @"rs"
702 //                format: iCalResourceString];
703 // }
704
705 // - (NSString *) iCalStringFromQueryParameter: (NSString *) _qp
706 //                                      format: (NSString *) _format
707 // {
708 //   AgenorUserManager *um;
709 //   NSMutableString *iCalRep;
710 //   NSString *s;
711
712 //   um = [AgenorUserManager sharedUserManager];
713 //   iCalRep = (NSMutableString *)[NSMutableString string];
714 //   s = [self queryParameterForKey:_qp];
715 //   if(s && [s length] > 0) {
716 //     NSArray *es;
717 //     unsigned i, count;
718     
719 //     es = [s componentsSeparatedByString: @","];
720 //     count = [es count];
721 //     for(i = 0; i < count; i++) {
722 //       NSString *email, *cn;
723       
724 //       email = [es objectAtIndex:i];
725 //       cn = [um getCNForUID:[um getUIDForEmail:email]];
726 //       [iCalRep appendFormat:_format, cn, email];
727 //     }
728 //   }
729 //   return iCalRep;
730 // }
731
732 - (NSException *) validateObjectForStatusChange
733 {
734   id co;
735
736   co = [self clientObject];
737   if (![co respondsToSelector: @selector(changeParticipationStatus:)])
738     return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
739                         reason:
740                           @"method cannot be invoked on the specified object"];
741
742   return nil;
743 }
744
745 /* contact editor compatibility */
746
747 - (NSString *) urlButtonClasses
748 {
749   NSString *classes;
750
751   if ([url length])
752     classes = @"button";
753   else
754     classes = @"button _disabled";
755
756   return classes;
757 }
758
759 - (void) _handleAttendeesEdition
760 {
761   NSArray *names, *emails;
762   NSMutableArray *newAttendees;
763   unsigned int count, max;
764   NSString *currentEmail;
765   iCalPerson *currentAttendee;
766
767   newAttendees = [NSMutableArray new];
768   if ([attendeesNames length] > 0)
769     {
770       names = [attendeesNames componentsSeparatedByString: @","];
771       emails = [attendeesEmails componentsSeparatedByString: @","];
772       max = [emails count];
773       for (count = 0; count < max; count++)
774         {
775           currentEmail = [emails objectAtIndex: count];
776           currentAttendee = [component findParticipantWithEmail: currentEmail];
777           if (!currentAttendee)
778             {
779               currentAttendee = [iCalPerson elementWithTag: @"attendee"];
780               [currentAttendee setCn: [names objectAtIndex: count]];
781               [currentAttendee setEmail: currentEmail];
782               [currentAttendee setRole: @"REQ-PARTICIPANT"];
783               [currentAttendee
784                 setParticipationStatus: iCalPersonPartStatNeedsAction];
785             }
786           [newAttendees addObject: currentAttendee];
787         }
788     }
789
790   [component setAttendees: newAttendees];
791   [newAttendees release];
792 }
793
794 - (void) _handleOrganizer
795 {
796   NSString *organizerEmail;
797   SOGoUser *activeUser;
798
799   organizerEmail = [[component organizer] email];
800   if ([organizerEmail length] == 0)
801     {
802       if ([[component attendees] count] > 0)
803         {
804           ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]);
805           activeUser = [context activeUser];
806           [organizer setCn: [activeUser cn]];
807           [organizer setEmail: [activeUser primaryEmail]];
808           [component setOrganizer: organizer];
809         }
810     }
811   else
812     {
813       if ([[component attendees] count] == 0)
814         {
815           ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]);
816           [component setOrganizer: organizer];
817         }
818     }
819 }
820
821 - (void) takeValuesFromRequest: (WORequest *) _rq
822                      inContext: (WOContext *) _ctx
823 {
824   NSCalendarDate *now;
825   SOGoCalendarComponent *clientObject;
826
827   [super takeValuesFromRequest: _rq inContext: _ctx];
828
829   now = [NSCalendarDate calendarDate];
830   [component setSummary: title];
831   [component setLocation: location];
832   [component setComment: comment];
833   [component setUrl: url];
834   [component setAccessClass: privacy];
835   [self _handleAttendeesEdition];
836   [self _handleOrganizer];
837   clientObject = [self clientObject];
838   if ([clientObject isNew])
839     {
840       [component setUid: [clientObject nameInContainer]];
841       [component setCreated: now];
842       [component setTimeStampAsDate: now];
843       [component setPriority: @"0"];
844     }
845   [component setLastModified: now];
846 }
847
848 - (NSString *) toolbar
849 {
850   SOGoCalendarComponent *clientObject;
851   NSString *toolbarFilename;
852   iCalPerson *participant;
853   iCalPersonPartStat participationStatus;
854   SoSecurityManager *sm;
855   NSString *owner;
856
857   sm = [SoSecurityManager sharedSecurityManager];
858   clientObject = [self clientObject];
859
860   owner = [clientObject ownerInContext: context];
861   participant = [clientObject findParticipantWithUID: owner];
862
863   if (participant
864       && ![sm validatePermission: SOGoCalendarPerm_RespondToComponent
865               onObject: clientObject
866               inContext: context])
867     {
868       participationStatus = [participant participationStatus];
869       /* Lightning does not manage participation status within tasks */
870       if (participationStatus == iCalPersonPartStatAccepted)
871         toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar";
872       else if (participationStatus == iCalPersonPartStatDeclined)
873         toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar";
874       else
875         toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar";
876     }
877   else if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent
878                 onObject: clientObject
879                 inContext: context])
880     {
881       if ([[clientObject componentTag] isEqualToString: @"vevent"])
882         toolbarFilename = @"SOGoAppointmentObject.toolbar";
883       else
884         toolbarFilename = @"SOGoTaskObject.toolbar";
885     }
886   else
887     toolbarFilename = @"SOGoComponentClose.toolbar";
888
889   return toolbarFilename;
890 }
891
892 @end