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
38 - (SOGoAppointment *)appointment;
39 - (NSString *)iCalStringTemplate;
40 - (NSString *)iCalString;
42 - (NSString *)_completeURIForMethod:(NSString *)_method;
44 - (iCalPerson *)getOrganizer;
45 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
46 treatAsResource:(BOOL)_isResource;
51 #include <SOGoUI/SOGoDateFormatter.h>
52 #include <SOGoLogic/SOGoAppointment.h>
53 #include <Appointments/SOGoAppointmentFolder.h>
54 #include <Appointments/SOGoAppointmentObject.h>
55 #include <NGiCal/NGiCal.h>
56 #include <SOGoLogic/AgenorUserManager.h>
57 #include "iCalPerson+UIx.h"
58 #include "UIxComponent+Agenor.h"
60 @interface NSDate(UsedPrivates)
61 - (NSString *)icalString; // TODO: this is in NGiCal
64 @interface NSObject(AppointmentHack)
65 - (BOOL)isAppointment;
68 @implementation NSObject(AppointmentHack)
69 - (BOOL)isAppointment {
70 return [self isKindOfClass:NSClassFromString(@"SOGoAppointmentObject")];
74 @implementation UIxAppointmentEditor
77 [self->appointment release];
78 [self->participants release];
85 - (void)setItem:(id)_item {
86 ASSIGN(self->item, _item);
92 - (NSString *)formattedAptStartTime {
94 SOGoDateFormatter *fmt;
97 date = [[[[self appointment] startDate] copy] autorelease];
98 [date setTimeZone:[self viewTimeZone]];
99 fmt = [[SOGoDateFormatter alloc] initWithLocale:[self locale]];
100 [fmt setFullWeekdayNameAndDetails];
101 s = [fmt stringForObjectValue:date];
106 - (BOOL)isNewAppointment {
107 /* that doesn't work! (TODO: explain why!) */
108 // TODO: is this actually used?
109 return ![[self clientObject] isAppointment];
112 - (NSString *)iCalString {
113 // TODO: improve check for new appointment
116 ical = [[self clientObject] valueForKey:@"iCalString"];
117 if ([ical length] == 0) /* a new appointment */
118 ical = [self iCalStringTemplate];
123 - (NSString *)iCalStringTemplate {
124 static NSString *iCalStringTemplate = \
125 @"BEGIN:VCALENDAR\nMETHOD:REQUEST\nPRODID:OpenGroupware.org SOGo 0.9\n"
130 @"STATUS:CONFIRMED\n"
138 NSCalendarDate *startDate, *endDate;
141 startDate = [self selectedDate];
142 endDate = [startDate dateByAddingYears:0 months:0 days:0
143 hours:1 minutes:0 seconds:0];
145 template = [NSString stringWithFormat:iCalStringTemplate,
146 [[self clientObject] nameInContainer],
147 [[NSCalendarDate date] icalString],
148 [startDate icalString],
149 [endDate icalString]];
156 - (SOGoAppointment *)appointment {
157 if (self->appointment == nil) {
158 self->appointment = [[SOGoAppointment alloc]
159 initWithICalString:[self iCalString]];
161 return self->appointment;
166 - (NSString *)jsCode {
167 static NSString *script = \
168 @"function addToTable(tableId, type, cn, dn, email, uid, sn) {\n"
169 @" var test = document.getElementById(email);"
173 @" var table = document.getElementById(tableId);"
174 @" var tr = document.createElement('tr');"
175 @" var td, checkbox, text;"
177 @" td = document.createElement('td');"
178 @" checkbox = document.createElement('input');"
179 @" checkbox.setAttribute('type', 'checkbox');"
180 @" checkbox.setAttribute('checked', 'checked');"
181 @" checkbox.setAttribute('value', email + ';' + cn);"
182 @" checkbox.setAttribute('id', email);"
183 @" checkbox.setAttribute('name', tableId);"
184 @" td.appendChild(checkbox);"
185 @" tr.appendChild(td);"
186 @" td = document.createElement('td');"
187 @" text = document.createTextNode(cn);"
188 @" td.appendChild(text);"
189 @" tr.appendChild(td);"
190 @" table.appendChild(tr);"
192 @"function addParticipant(type, cn, dn, email, uid, sn) {\n"
193 @" addToTable('participants', type, cn, dn, email, uid, sn);\n"
195 @"function addResource(type, cn, dn, email, uid, sn) {\n"
196 @" addToTable('resources', type, cn, dn, email, uid, sn);\n"
204 - (NSString *)_completeURIForMethod:(NSString *)_method {
208 uri = [[[self context] request] uri];
210 /* first: identify query parameters */
211 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
213 uri = [uri substringToIndex:r.location];
215 /* next: append trailing slash */
216 if (![uri hasSuffix:@"/"])
217 uri = [uri stringByAppendingString:@"/"];
219 /* next: append method */
220 uri = [uri stringByAppendingString:_method];
222 /* next: append query parameters */
223 return [self completeHrefForMethod:uri];
228 - (NSString *)combinedCNAndEmailForUser {
229 return [NSString stringWithFormat:@"%@;%@",
234 - (NSString *)combinedCNAndEmail {
235 return [NSString stringWithFormat:@"%@;%@",
236 [self->item rfc822Email],
237 [self->item cnForDisplay]];
244 This method creates a unique ID and redirects to the "edit" method on the
246 It is actually a folder method and should be defined on the folder.
248 Note: 'clientObject' is the SOGoAppointmentFolder!
249 Update: remember that there are group folders as well.
251 NSString *uri, *objectId, *nextMethod;
253 objectId = [NSClassFromString(@"SOGoAppointmentFolder")
254 globallyUniqueObjectId];
255 if ([objectId length] == 0) {
256 return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
257 reason:@"could not create a unique ID"];
260 nextMethod = [NSString stringWithFormat:@"../%@/edit", objectId];
261 uri = [self _completeURIForMethod:nextMethod];
262 return [self redirectToLocation:uri];
267 /* returned dates are in GMT */
268 - (NSCalendarDate *)_dateFromString:(NSString *)_str {
269 NSCalendarDate *date;
271 date = [NSCalendarDate dateWithString:_str
272 calendarFormat:@"%Y-%m-%d %H:%M %Z"];
273 [date setTimeZone:[self backendTimeZone]];
277 - (iCalPerson *)getOrganizer {
281 emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
282 p = [[[iCalPerson alloc] init] autorelease];
283 [p setEmail:emailProp];
284 [p setCn:[self cnForUser]];
288 - (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
289 treatAsResource:(BOOL)_isResource
292 NSMutableArray *result;
294 count = [_values count];
295 result = [[NSMutableArray alloc] initWithCapacity:count];
296 for (i = 0; i < count; i++) {
297 NSString *pString, *email, *cn;
301 pString = [_values objectAtIndex:i];
302 if ([pString length] == 0)
305 /* delimiter between email and cn */
306 r = [pString rangeOfString:@";"];
308 email = [pString substringToIndex:r.location];
309 cn = (r.location + 1 < [pString length])
310 ? [pString substringFromIndex:r.location + 1]
319 AgenorUserManager *um = [AgenorUserManager sharedUserManager];
320 cn = [um getCNForUID:[um getUIDForEmail:email]];
323 p = [[iCalPerson alloc] init];
324 [p setEmail:[@"mailto:" stringByAppendingString:email]];
325 if ([cn isNotNull]) [p setCn:cn];
327 /* see RFC2445, sect. 4.2.16 for details */
328 [p setRole:_isResource ? @"NON-PARTICIPANT" : @"REQ-PARTICIPANT"];
329 [result addObject:p];
332 return [result autorelease];
338 /* for testing only */
341 NSLog(@"%s BEEN HERE!", __PRETTY_FUNCTION__);
343 req = [[self context] request];
344 NSLog(@"%@", [req formValues]);
349 SOGoAppointment *apt;
350 NSString *iCalString;
352 NSCalendarDate *sd, *ed;
357 if (![[self clientObject] respondsToSelector:@selector(saveContentString:)]){
358 /* return 400 == Bad Request */
359 return [NSException exceptionWithHTTPStatus:400
360 reason:@"method cannot be invoked on "
361 @"the specified object"];
364 req = [[self context] request];
366 /* get iCalString from hidden input */
367 iCalString = [req formValueForKey:@"ical"];
368 if ([iCalString length] == 0) {
369 // TODO: improve user experience ... (eg error panel like Zope)
370 /* return 400 == Bad Request */
371 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
372 reason:@"missing iCalendar form data in request"];
375 apt = [[SOGoAppointment alloc] initWithICalString:iCalString];
377 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
378 reason:@"invalid iCalendar form data in request"];
381 /* merge in form values */
383 sd = [self _dateFromString:[req formValueForKey:@"startDate"]];
384 ed = [self _dateFromString:[req formValueForKey:@"endDate"]];
385 [apt setStartDate:sd];
388 [apt setSummary:[req formValueForKey:@"summary"]];
389 [apt setLocation:[req formValueForKey:@"location"]];
391 attendees = [self getICalPersonsFromFormValues:
392 [req formValuesForKey:@"participants"]
394 [apt setAttendees:attendees];
395 attendees = [self getICalPersonsFromFormValues:
396 [req formValuesForKey:@"resources"]
397 treatAsResource:YES];
398 [apt appendAttendees:attendees];
399 [apt setOrganizer:[self getOrganizer]];
401 /* receive current representation for save operation */
402 iCalString = [apt iCalString];
403 [apt release]; apt = nil;
408 ex = [[self clientObject] saveContentString:iCalString];
410 // TODO: add some error handling in form! (eg like in Zope)
413 uri = [self _completeURIForMethod:@".."];
414 return [self redirectToLocation:uri];
417 @end /* UIxAppointmentEditor */