2 Copyright (C) 2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include <SOGoUI/UIxComponent.h>
29 @class SOGoAppointment;
31 @interface UIxAppointmentEditor : UIxComponent
37 /* individual values */
38 NSCalendarDate *startDate;
39 NSCalendarDate *endDate;
43 NSArray *participants; /* array of iCalPerson's */
44 NSArray *resources; /* array of iCalPerson's */
49 - (NSString *)iCalStringTemplate;
50 - (NSString *)iCalString;
52 - (NSString *)_completeURIForMethod:(NSString *)_method;
54 - (iCalPerson *)getOrganizer;
55 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
56 treatAsResource:(BOOL)_isResource;
58 - (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters;
59 - (NSString *)iCalParticipantsStringFromQueryParameters;
60 - (NSString *)iCalResourcesStringFromQueryParameters;
61 - (NSString *)iCalStringFromQueryParameter:(NSString *)_qp
62 format:(NSString *)_format;
66 #include <SOGoUI/SOGoDateFormatter.h>
67 #include <SOGoLogic/SOGoAppointment.h>
68 #include <Appointments/SOGoAppointmentFolder.h>
69 #include <Appointments/SOGoAppointmentObject.h>
70 #include <NGiCal/NGiCal.h>
71 #include <SOGoLogic/AgenorUserManager.h>
72 #include "iCalPerson+UIx.h"
73 #include "UIxComponent+Agenor.h"
75 @interface NSDate(UsedPrivates)
76 - (NSString *)icalString; // TODO: this is in NGiCal
79 @implementation UIxAppointmentEditor
82 [self->errorText release];
83 [self->participants release];
84 [self->resources release];
85 [self->startDate release];
86 [self->endDate release];
87 [self->title release];
88 [self->location release];
89 [self->comment release];
90 [self->priority release];
91 [self->categories release];
92 [self->iCalString release];
99 - (void)setItem:(id)_item {
100 ASSIGN(self->item, _item);
106 - (void)setErrorText:(NSString *)_txt {
107 ASSIGNCOPY(self->errorText, _txt);
109 - (NSString *)errorText {
110 return self->errorText;
112 - (BOOL)hasErrorText {
113 return [self->errorText length] > 0 ? YES : NO;
116 - (NSFormatter *)titleDateFormatter {
117 SOGoDateFormatter *fmt;
119 fmt = [[[SOGoDateFormatter alloc] initWithLocale:[self locale]] autorelease];
120 [fmt setFullWeekdayNameAndDetails];
124 - (void)setAptStartDate:(NSCalendarDate *)_date {
125 ASSIGN(self->startDate, _date);
127 - (NSCalendarDate *)aptStartDate {
128 return self->startDate;
130 - (void)setAptEndDate:(NSCalendarDate *)_date {
131 ASSIGN(self->endDate, _date);
133 - (NSCalendarDate *)aptEndDate {
134 return self->endDate;
137 - (void)setTitle:(NSString *)_value {
138 ASSIGNCOPY(self->title, _value);
140 - (NSString *)title {
143 - (void)setLocation:(NSString *)_value {
144 ASSIGNCOPY(self->location, _value);
146 - (NSString *)location {
147 return self->location;
149 - (void)setComment:(NSString *)_value {
150 ASSIGNCOPY(self->comment, _value);
152 - (NSString *)comment {
153 return self->comment;
156 - (void)setParticipants:(NSArray *)_parts {
157 ASSIGN(self->participants, _parts);
159 - (NSArray *)participants {
160 return self->participants;
162 - (void)setResources:(NSArray *)_res {
163 ASSIGN(self->resources, _res);
165 - (NSArray *)resources {
166 return self->resources;
171 - (NSArray *)priorities {
176 return [NSArray arrayWithObjects:
177 @"0", @"5", @"1", nil];
180 - (NSString *)itemPriorityText {
183 key = [NSString stringWithFormat:@"prio_%@", self->item];
184 return [self labelForKey:key];
187 - (void)setPriority:(NSString *)_priority {
188 ASSIGN(self->priority, _priority);
190 - (NSString *)priority {
191 return self->priority;
197 - (NSArray *)categoryItems {
198 // TODO: make this configurable?
200 Tasks categories will be modified as follow :
201 – by default (a simple logo or no logo at all),
208 return [NSArray arrayWithObjects:
217 - (NSString *)itemCategoryText {
218 return [self labelForKey:self->item];
221 - (void)setCategories:(NSArray *)_categories {
222 ASSIGN(self->categories, _categories);
224 - (NSArray *)categories {
225 return self->categories;
231 - (void)setICalString:(NSString *)_s {
232 ASSIGNCOPY(self->iCalString, _s);
234 - (NSString *)iCalString {
235 return self->iCalString;
238 - (NSString *)iCalStringTemplate {
239 static NSString *iCalStringTemplate = \
242 @"PRODID:OpenGroupware.org SOGo 0.9\n"
247 @"STATUS:CONFIRMED\n"
258 NSCalendarDate *lStartDate, *lEndDate;
259 NSString *template, *s;
262 s = [self queryParameterForKey:@"dur"];
263 if(s && [s length] > 0) {
264 minutes = [s intValue];
269 lStartDate = [self selectedDate];
270 lEndDate = [lStartDate dateByAddingYears:0 months:0 days:0
271 hours:0 minutes:minutes seconds:0];
273 s = [self iCalParticipantsAndResourcesStringFromQueryParameters];
274 template = [NSString stringWithFormat:iCalStringTemplate,
275 [[self clientObject] nameInContainer],
276 [[NSCalendarDate date] icalString],
277 [lStartDate icalString],
278 [lEndDate icalString],
283 - (NSString *)iCalParticipantsAndResourcesStringFromQueryParameters {
286 s = [self iCalParticipantsStringFromQueryParameters];
287 return [s stringByAppendingString:
288 [self iCalResourcesStringFromQueryParameters]];
291 - (NSString *)iCalParticipantsStringFromQueryParameters {
292 static NSString *iCalParticipantString = \
293 @"ATTENDEE;ROLE=REQ-PARTICIPANT;CN=\"%@\":mailto:%@\n";
295 return [self iCalStringFromQueryParameter:@"ps"
296 format:iCalParticipantString];
299 - (NSString *)iCalResourcesStringFromQueryParameters {
300 static NSString *iCalResourceString = \
301 @"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":mailto:%@\n";
303 return [self iCalStringFromQueryParameter:@"rs"
304 format:iCalResourceString];
307 - (NSString *)iCalStringFromQueryParameter:(NSString *)_qp
308 format:(NSString *)_format
310 AgenorUserManager *um;
311 NSMutableString *iCalRep;
314 um = [AgenorUserManager sharedUserManager];
315 iCalRep = (NSMutableString *)[NSMutableString string];
316 s = [self queryParameterForKey:_qp];
317 if(s && [s length] > 0) {
321 es = [s componentsSeparatedByString:@","];
323 for(i = 0; i < count; i++) {
324 NSString *email, *cn;
326 email = [es objectAtIndex:i];
327 cn = [um getCNForUID:[um getUIDForEmail:email]];
328 [iCalRep appendFormat:_format, cn, email];
336 - (NSString *)_completeURIForMethod:(NSString *)_method {
340 uri = [[[self context] request] uri];
342 /* first: identify query parameters */
343 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
345 uri = [uri substringToIndex:r.location];
347 /* next: append trailing slash */
348 if (![uri hasSuffix:@"/"])
349 uri = [uri stringByAppendingString:@"/"];
351 /* next: append method */
352 uri = [uri stringByAppendingString:_method];
354 /* next: append query parameters */
355 return [self completeHrefForMethod:uri];
362 This method creates a unique ID and redirects to the "edit" method on the
364 It is actually a folder method and should be defined on the folder.
366 Note: 'clientObject' is the SOGoAppointmentFolder!
367 Update: remember that there are group folders as well.
369 NSString *uri, *objectId, *nextMethod;
371 objectId = [NSClassFromString(@"SOGoAppointmentFolder")
372 globallyUniqueObjectId];
373 if ([objectId length] == 0) {
374 return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
375 reason:@"could not create a unique ID"];
378 nextMethod = [NSString stringWithFormat:@"../%@/edit", objectId];
379 uri = [self _completeURIForMethod:nextMethod];
380 return [self redirectToLocation:uri];
385 /* returned dates are in GMT */
386 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
387 NSCalendarDate *date;
389 date = [NSCalendarDate dateWithString:_str
390 calendarFormat:@"%Y-%m-%d %H:%M %Z"];
391 [date setTimeZone:[self backendTimeZone]];
395 - (iCalPerson *)getOrganizer {
399 emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
400 p = [[[iCalPerson alloc] init] autorelease];
401 [p setEmail:emailProp];
402 [p setCn:[self cnForUser]];
406 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
407 treatAsResource:(BOOL)_isResource
410 NSMutableArray *result;
412 count = [_values count];
413 result = [[NSMutableArray alloc] initWithCapacity:count];
414 for (i = 0; i < count; i++) {
415 NSString *pString, *email, *cn;
419 pString = [_values objectAtIndex:i];
420 if ([pString length] == 0)
423 /* delimiter between email and cn */
424 r = [pString rangeOfString:@";"];
426 email = [pString substringToIndex:r.location];
427 cn = (r.location + 1 < [pString length])
428 ? [pString substringFromIndex:r.location + 1]
437 AgenorUserManager *um = [AgenorUserManager sharedUserManager];
438 cn = [um getCNForUID:[um getUIDForEmail:email]];
441 p = [[iCalPerson alloc] init];
442 [p setEmail:[@"mailto:" stringByAppendingString:email]];
443 if ([cn isNotNull]) [p setCn:cn];
445 /* see RFC2445, sect. 4.2.16 for details */
446 [p setRole:_isResource ? @"NON-PARTICIPANT" : @"REQ-PARTICIPANT"];
447 [result addObject:p];
450 return [result autorelease];
453 - (BOOL)isWriteableClientObject {
454 return [[self clientObject]
455 respondsToSelector:@selector(saveContentString:)];
458 - (void)loadValuesFromAppointment:(SOGoAppointment *)_appointment {
459 if ((self->startDate = [[_appointment startDate] copy]) == nil)
460 self->startDate = [[[[NSCalendarDate date] date] hour:11 minute:0] copy];
461 if ((self->endDate = [[_appointment endDate] copy]) == nil) {
463 [[self->startDate hour:[self->startDate hourOfDay]+1 minute:0] copy];
465 [self->startDate setTimeZone:[self viewTimeZone]];
466 [self->endDate setTimeZone:[self viewTimeZone]];
468 self->title = [[_appointment summary] copy];
469 self->location = [[_appointment location] copy];
470 self->comment = [[_appointment comment] copy];
471 self->priority = [[_appointment priority] copy];
472 self->categories = [[_appointment categories] retain];
473 self->participants = [[_appointment participants] retain];
474 self->resources = [[_appointment resources] retain];
477 - (void)saveValuesIntoAppointment:(SOGoAppointment *)_appointment {
478 /* merge in form values */
479 NSArray *attendees, *lResources;
481 [_appointment setStartDate:[self aptStartDate]];
482 [_appointment setEndDate:[self aptEndDate]];
484 [_appointment setSummary:[self title]];
485 [_appointment setLocation:[self location]];
486 [_appointment setComment:[self comment]];
487 [_appointment setPriority:[self priority]];
488 [_appointment setCategories:[self categories]];
490 attendees = [self participants];
491 lResources = [self resources];
492 if ([lResources count] > 0) {
493 attendees = ([attendees count] > 0)
494 ? [attendees arrayByAddingObjectsFromArray:lResources]
497 [_appointment setAttendees:attendees];
499 [_appointment setOrganizer:[self getOrganizer]];
502 - (void)loadValuesFromICalString:(NSString *)_ical {
503 SOGoAppointment *apt;
505 apt = [[SOGoAppointment alloc] initWithICalString:_ical];
506 [self loadValuesFromAppointment:apt];
510 /* contact editor compatibility */
512 - (void)setContentString:(NSString *)_s {
513 [self setICalString:_s];
515 - (NSString *)contentStringTemplate {
516 return [self iCalStringTemplate];
519 - (void)loadValuesFromContentString:(NSString *)_s {
520 [self loadValuesFromICalString:_s];
525 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
530 /* for testing only */
533 NSLog(@"%s BEEN HERE!", __PRETTY_FUNCTION__);
535 req = [[self context] request];
536 NSLog(@"%@", [req formValues]);
537 [self logWithFormat:@"aptStartDate:%@ aptEndDate:%@",
543 - (id)defaultAction {
546 /* load iCalendar file */
548 // TODO: can't we use [clientObject contentAsString]?
549 ical = [[self clientObject] valueForKey:@"iCalString"];
550 if ([ical length] == 0) /* a new appointment */
551 ical = [self contentStringTemplate];
553 [self setContentString:ical];
554 [self loadValuesFromContentString:ical];
560 SOGoAppointment *apt;
564 if (![self isWriteableClientObject]) {
565 /* return 400 == Bad Request */
566 return [NSException exceptionWithHTTPStatus:400
567 reason:@"method cannot be invoked on "
568 @"the specified object"];
571 apt = [[SOGoAppointment alloc] initWithICalString:[self iCalString]];
573 [self setErrorText:@"Invalid iCalendar data ..."]; // localize
577 [self saveValuesIntoAppointment:apt];
578 content = [apt iCalString];
579 [apt release]; apt = nil;
581 if (content == nil) {
582 [self setErrorText:@"Could not create iCalendar data ..."]; // localize
586 ex = [[self clientObject] saveContentString:content];
588 [self setErrorText:[ex reason]];
592 return [self redirectToLocation:[self _completeURIForMethod:@".."]];
595 @end /* UIxAppointmentEditor */