From 94105e937a2c7371116b0bdaf111b669d3802c40 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Wed, 18 Feb 2015 16:44:09 -0500 Subject: [PATCH] Improve JSON getter/setter for todos --- SOPE/NGCards/iCalToDo.h | 1 + SOPE/NGCards/iCalToDo.m | 6 + SoObjects/Appointments/iCalToDo+SOGo.m | 202 +++++++- UI/Scheduler/UIxTaskEditor.h | 28 +- UI/Scheduler/UIxTaskEditor.m | 641 +++++++++++++------------ 5 files changed, 530 insertions(+), 348 deletions(-) diff --git a/SOPE/NGCards/iCalToDo.h b/SOPE/NGCards/iCalToDo.h index 1ec312604..e9b799c2b 100644 --- a/SOPE/NGCards/iCalToDo.h +++ b/SOPE/NGCards/iCalToDo.h @@ -39,6 +39,7 @@ - (NSString *) percentComplete; - (void) setDue: (NSCalendarDate *) _date; +- (void) setAllDayDue: (NSCalendarDate *) _date; - (NSCalendarDate *) due; - (void) setCompleted: (NSCalendarDate *) _date; diff --git a/SOPE/NGCards/iCalToDo.m b/SOPE/NGCards/iCalToDo.m index 439bbfe18..a16d906a0 100644 --- a/SOPE/NGCards/iCalToDo.m +++ b/SOPE/NGCards/iCalToDo.m @@ -65,6 +65,12 @@ setDateTime: newDueDate]; } +- (void) setAllDayDue: (NSCalendarDate *) newDueDate +{ + [(iCalDateTime *) [self uniqueChildWithTag: @"due"] + setDate: newDueDate]; +} + - (NSCalendarDate *) due { return [(iCalDateTime *) [self uniqueChildWithTag: @"due"] diff --git a/SoObjects/Appointments/iCalToDo+SOGo.m b/SoObjects/Appointments/iCalToDo+SOGo.m index 79ed39265..dd7a38363 100644 --- a/SoObjects/Appointments/iCalToDo+SOGo.m +++ b/SoObjects/Appointments/iCalToDo+SOGo.m @@ -27,10 +27,19 @@ #import #import -#import -#import -#import #import +#import +#import +#import +#import +#import +#import + +#import + +#import +#import +#import #import "iCalRepeatableEntityObject+SOGo.h" @@ -38,6 +47,179 @@ @implementation iCalToDo (SOGoExtensions) +- (NSDictionary *) attributesInContext: (WOContext *) context +{ + BOOL isAllDayStartDate, isAllDayDueDate; + NSCalendarDate *startDate, *dueDate, *completedDate; + NSMutableDictionary *data; + NSTimeZone *timeZone; + SOGoUserDefaults *ud; + + ud = [[context activeUser] userDefaults]; + timeZone = [ud timeZone]; + + startDate = [self startDate]; + isAllDayStartDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] isAllDay]; + if (!isAllDayStartDate) + [startDate setTimeZone: timeZone]; + + dueDate = [self due]; + isAllDayDueDate = [(iCalDateTime *) [self uniqueChildWithTag: @"due"] isAllDay]; + if (!isAllDayDueDate) + [dueDate setTimeZone: timeZone]; + + completedDate = [self completed]; + [completedDate setTimeZone: timeZone]; + + data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]]; + + if (startDate) + [data setObject: [startDate iso8601DateString] forKey: @"startDate"]; + if (dueDate) + [data setObject: [dueDate iso8601DateString] forKey: @"dueDate"]; + if (completedDate) + [data setObject: [completedDate iso8601DateString] forKey: @"completedDate"]; + + if ([[self percentComplete] length]) + [data setObject: [NSNumber numberWithInt: [[self percentComplete] intValue]] forKey: @"percentComplete"]; + + return data; +} + +/** + * @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:] + * @see [iCalEntityObject+SOGo setAttributes:inContext:] + * @see [UIxAppointmentEditor saveAction] + */ +- (void) setAttributes: (NSDictionary *) data + inContext: (WOContext *) context +{ + BOOL isAllDayStartDate, isAllDayDueDate; + NSCalendarDate *startDate, *dueDate, *completedDate; + NSInteger percent; + SOGoUserDefaults *ud; + iCalDateTime *todoStartDate, *todoDueDate; + iCalTimeZone *tz; + id o; + + [super setAttributes: data inContext: context]; + + startDate = dueDate = completedDate = nil; + + // Handle start date + isAllDayStartDate = YES; + o = [data objectForKey: @"startDate"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + startDate = [self dateFromString: o inContext: context]; + + o = [data objectForKey: @"startTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + { + isAllDayStartDate = NO; + [self adjustDate: &startDate withTimeString: o inContext: context]; + } + + if (startDate) + [self setStartDate: startDate]; + else + { + [self setStartDate: nil]; + [self removeAllAlarms]; + } + + // Handle due date + isAllDayDueDate = YES; + o = [data objectForKey: @"dueDate"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + dueDate = [self dateFromString: o inContext: context]; + + o = [data objectForKey: @"dueTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + { + isAllDayDueDate = NO; + [self adjustDate: &dueDate withTimeString: o inContext: context]; + } + + if (dueDate) + if (isAllDayDueDate) + [self setAllDayDue: dueDate]; + else + [self setDue: dueDate]; + else + [self setDue: nil]; + + // Handle time zone + todoStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + todoDueDate = (iCalDateTime *)[self uniqueChildWithTag: @"due"]; + tz = [todoStartDate timeZone]; + if (!tz) + tz = [todoDueDate timeZone]; + + if (isAllDayStartDate && isAllDayDueDate) + { + if (tz) + [[self parent] removeChild: tz]; + [todoStartDate setTimeZone: nil]; + [todoDueDate setTimeZone: nil]; + } + else + { + if (!tz) + { + ud = [[context activeUser] userDefaults]; + tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; + } + if (tz) + { + [[self parent] addTimeZone: tz]; + if (todoStartDate) + { + if (isAllDayStartDate) + [todoStartDate setTimeZone: nil]; + else if (![todoStartDate timeZone]) + [todoStartDate setTimeZone: tz]; + } + if (todoDueDate) + { + if (isAllDayDueDate) + [todoDueDate setTimeZone: nil]; + else if (![todoDueDate timeZone]) + [todoDueDate setTimeZone: tz]; + } + } + } + + // Handle completed date + o = [data objectForKey: @"completedDate"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + completedDate = [self dateFromString: o inContext: context]; + + o = [data objectForKey: @"completedTime"]; + if ([o isKindOfClass: [NSString class]] && [o length]) + [self adjustDate: &completedDate withTimeString: o inContext: context]; + + o = [self status]; + if ([o length]) + { + if ([o isEqualToString: @"COMPLETED"] && completedDate) + // As specified in RFC5545, the COMPLETED property must use UTC time + [self setCompleted: completedDate]; + else + [self setCompleted: nil]; + } + + // Percent complete + o = [data objectForKey: @"percentComplete"]; + if ([o isKindOfClass: [NSNumber class]]) + { + percent = [o intValue]; + if (percent >= 0 && percent <= 100) + [self setPercentComplete: [NSString stringWithFormat: @"%i", percent]]; + } + else + [self setPercentComplete: @""]; +} + - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent container: (id) theContainer { @@ -53,7 +235,7 @@ iCalAccessClass accessClass; /* extract values */ - + startDate = [self startDate]; dueDate = [self due]; uid = [self uid]; @@ -78,7 +260,7 @@ [row setObject: @"vtodo" forKey: @"c_component"]; - if ([uid isNotNull]) + if ([uid isNotNull]) [row setObject:uid forKey: @"c_uid"]; else [self logWithFormat: @"WARNING: could not extract a uid from event!"]; @@ -96,7 +278,7 @@ [row setObject: title forKey: @"c_title"]; if ([location isNotNull]) [row setObject: location forKey: @"c_location"]; if ([sequence isNotNull]) [row setObject: sequence forKey: @"c_sequence"]; - + if ([startDate isNotNull]) date = [self quickRecordDateAsNumber: startDate withOffset: 0 forAllDay: NO]; @@ -146,12 +328,12 @@ if (organizer) { NSString *email; - + email = [organizer valueForKey: @"rfc822Email"]; if (email) [row setObject:email forKey: @"c_orgmail"]; } - + /* construct partstates */ count = [attendees count]; partstates = [[NSMutableString alloc] initWithCapacity:count * 2]; @@ -159,7 +341,7 @@ { iCalPerson *p; iCalPersonPartStat stat; - + p = [attendees objectAtIndex:i]; stat = [p participationStatus]; if(i != 0) @@ -171,7 +353,7 @@ /* handle alarms */ [self updateNextAlarmDateInRow: row forContainer: theContainer]; - + categories = [self categories]; if ([categories count] > 0) [row setObject: [categories componentsJoinedByString: @","] diff --git a/UI/Scheduler/UIxTaskEditor.h b/UI/Scheduler/UIxTaskEditor.h index 2deeaaf78..2e6935c9c 100644 --- a/UI/Scheduler/UIxTaskEditor.h +++ b/UI/Scheduler/UIxTaskEditor.h @@ -1,8 +1,6 @@ /* UIxTaskEditor.h - this file is part of SOGo * - * Copyright (C) 2007-2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-2015 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,35 +26,13 @@ @class iCalToDo; @class NSString; -@interface UIxTaskEditor : UIxComponent +@interface UIxTaskEditor : UIxComponentEditor { - iCalToDo *todo; - NSCalendarDate *taskStartDate; - NSCalendarDate *taskDueDate; - NSCalendarDate *statusDate; - NSString *status; - NSString *statusPercent; - BOOL hasStartDate; - BOOL hasDueDate; - NSString *item; SOGoDateFormatter *dateFormatter; } -/* template values */ -- (NSString *) saveURL; - (iCalToDo *) todo; -/* icalendar values */ -- (void) setTaskStartDate: (NSCalendarDate *) _date; -- (NSCalendarDate *) taskStartDate; - -- (void) setTaskDueDate: (NSCalendarDate *) _date; -- (NSCalendarDate *) taskDueDate; - -- (NSString *) taskStartDateTimeText; -- (NSString *) taskDueDateTimeText; -- (NSString *) statusDateText; - @end #endif /* UIXAPPOINTMENTEDITOR_H */ diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 34ee2fa6f..7b87258a4 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -37,7 +37,9 @@ #import #import +#import #import +#import #import #import #import @@ -58,15 +60,7 @@ if ((self = [super init])) { - taskStartDate = nil; - taskDueDate = nil; - statusDate = nil; - hasStartDate = NO; - hasDueDate = NO; - status = nil; - statusPercent = nil; item = nil; - todo = nil; user = [[self context] activeUser]; ASSIGN (dateFormatter, [user dateFormatterInContext: context]); @@ -77,26 +71,14 @@ - (void) dealloc { - [taskStartDate release]; - [taskDueDate release]; - [statusDate release]; - [status release]; - [statusPercent release]; [dateFormatter release]; - [[todo parent] release]; [super dealloc]; } /* template values */ - (iCalToDo *) todo { - if (!todo) - { - todo = (iCalToDo *) [[self clientObject] occurence]; - [[todo parent] retain]; - } - - return todo; + return (iCalToDo *) component; } - (NSString *) saveURL @@ -106,150 +88,61 @@ } /* icalendar values */ -- (void) setTaskStartDate: (NSCalendarDate *) newTaskStartDate -{ - ASSIGN (taskStartDate, newTaskStartDate); -} -- (NSCalendarDate *) taskStartDate -{ - return taskStartDate; -} +// - (NSArray *) statusList +// { +// static NSArray *statusItems = nil; -- (void) setHasStartDate: (BOOL) newHasStartDate -{ - hasStartDate = newHasStartDate; -} +// if (!statusItems) +// { +// statusItems = [NSArray arrayWithObjects: @"NEEDS-ACTION", +// @"IN-PROCESS", +// @"COMPLETED", +// @"CANCELLED", +// nil]; +// [statusItems retain]; +// } -- (BOOL) hasStartDate -{ - return hasStartDate; -} +// return statusItems; +// } -- (BOOL) startDateDisabled -{ - return !hasStartDate; -} +// - (NSString *) itemStatusText +// { +// if (!item) +// { +// item = status; +// if (!item) +// item = @"NOT-SPECIFIED"; +// } +// return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; +// } -- (void) setTaskDueDate: (NSCalendarDate *) newTaskDueDate -{ - ASSIGN (taskDueDate, newTaskDueDate); -} +// - (BOOL) statusDateDisabled +// { +// return !([status isEqualToString: @"COMPLETED"] || statusDate); +// } -- (NSCalendarDate *) taskDueDate -{ - return taskDueDate; -} - -- (void) setHasDueDate: (BOOL) newHasDueDate -{ - hasDueDate = newHasDueDate; -} - -- (BOOL) hasDueDate -{ - return hasDueDate; -} - -- (BOOL) dueDateDisabled -{ - return !hasDueDate; -} - -- (NSArray *) statusList -{ - static NSArray *statusItems = nil; - - if (!statusItems) - { - statusItems = [NSArray arrayWithObjects: @"NEEDS-ACTION", - @"IN-PROCESS", - @"COMPLETED", - @"CANCELLED", - nil]; - [statusItems retain]; - } - - return statusItems; -} - -- (NSString *) itemStatusText -{ - if (!item) - { - item = status; - if (!item) - item = @"NOT-SPECIFIED"; - } - return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; -} - -- (void) setItem: (NSString *) newItem -{ - item = newItem; -} - -- (NSString *) item -{ - return item; -} - -- (NSString *) status -{ - return status; -} - -- (void) setStatus: (NSString *) newStatus -{ - status = newStatus; -} - -- (void) setStatusDate: (NSCalendarDate *) newStatusDate -{ - ASSIGN (statusDate, newStatusDate); -} - -- (NSCalendarDate *) statusDate -{ - return statusDate; -} - -- (BOOL) statusDateDisabled -{ - return !([status isEqualToString: @"COMPLETED"] || statusDate); -} - -- (BOOL) statusPercentDisabled -{ - return ([status length] == 0 || [status isEqualToString: @"CANCELLED"]); -} - -- (void) setStatusPercent: (NSString *) newStatusPercent -{ - ASSIGN (statusPercent, newStatusPercent); -} - -- (NSString *) statusPercent -{ - return statusPercent; -} +// - (BOOL) statusPercentDisabled +// { +// return ([status length] == 0 || [status isEqualToString: @"CANCELLED"]); +// } /* viewing read-only tasks */ -- (NSString *) taskStartDateTimeText -{ - return [dateFormatter formattedDateAndTime: taskStartDate]; -} +// - (NSString *) taskStartDateTimeText +// { +// return [dateFormatter formattedDateAndTime: taskStartDate]; +// } -- (NSString *) taskDueDateTimeText -{ - return [dateFormatter formattedDateAndTime: taskDueDate]; -} +// - (NSString *) taskDueDateTimeText +// { +// return [dateFormatter formattedDateAndTime: taskDueDate]; +// } -- (NSString *) statusDateText -{ - return [dateFormatter formattedDate: statusDate]; -} +// - (NSString *) statusDateText +// { +// return [dateFormatter formattedDate: statusDate]; +// } /* actions */ - (NSCalendarDate *) newStartDate @@ -290,114 +183,208 @@ return newStartDate; } -- (id ) defaultAction -{ - NSCalendarDate *startDate, *dueDate; - NSString *duration; - NSTimeZone *timeZone; - SOGoUserDefaults *ud; - unsigned int minutes; +// - (id ) defaultAction +// { +// NSCalendarDate *startDate, *dueDate; +// NSString *duration; +// NSTimeZone *timeZone; +// SOGoUserDefaults *ud; +// iCalToDo *todo; +// unsigned int minutes; - ud = [[context activeUser] userDefaults]; - timeZone = [ud timeZone]; - [self todo]; - if (todo) - { - startDate = [todo startDate]; - dueDate = [todo due]; - if (startDate) - hasStartDate = YES; - else - startDate = [self newStartDate]; - if (dueDate) - hasDueDate = YES; - else - dueDate = [self newStartDate]; +// ud = [[context activeUser] userDefaults]; +// timeZone = [ud timeZone]; +// todo = [self todo]; +// if (todo) +// { +// startDate = [todo startDate]; +// dueDate = [todo due]; +// if (startDate) +// hasStartDate = YES; +// else +// startDate = [self newStartDate]; +// if (dueDate) +// hasDueDate = YES; +// else +// dueDate = [self newStartDate]; - ASSIGN (statusDate, [todo completed]); - [statusDate setTimeZone: timeZone]; +// ASSIGN (statusDate, [todo completed]); +// [statusDate setTimeZone: timeZone]; - ASSIGN (status, [todo status]); +// ASSIGN (status, [todo status]); - if ([status length] == 0 && statusDate) - { - ASSIGN(status, @"COMPLETED"); - } - else - { - ASSIGN (statusDate, [self newStartDate]); - } - ASSIGN (statusPercent, [todo percentComplete]); - } - else - { - startDate = [self newStartDate]; - duration = [self queryParameterForKey:@"dur"]; - if ([duration length] > 0) - minutes = [duration intValue]; - else - minutes = 60; - dueDate = [startDate dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: minutes seconds: 0]; - hasStartDate = NO; - hasDueDate = NO; - ASSIGN (statusDate, [self newStartDate]); - ASSIGN (status, @""); - ASSIGN (statusPercent, @""); - } +// if ([status length] == 0 && statusDate) +// { +// ASSIGN(status, @"COMPLETED"); +// } +// else +// { +// ASSIGN (statusDate, [self newStartDate]); +// } +// ASSIGN (statusPercent, [todo percentComplete]); +// } +// else +// { +// startDate = [self newStartDate]; +// duration = [self queryParameterForKey:@"dur"]; +// if ([duration length] > 0) +// minutes = [duration intValue]; +// else +// minutes = 60; +// dueDate = [startDate dateByAddingYears: 0 months: 0 days: 0 +// hours: 0 minutes: minutes seconds: 0]; +// hasStartDate = NO; +// hasDueDate = NO; +// ASSIGN (statusDate, [self newStartDate]); +// ASSIGN (status, @""); +// ASSIGN (statusPercent, @""); +// } - [startDate setTimeZone: timeZone]; - ASSIGN (taskStartDate, startDate); - - [dueDate setTimeZone: timeZone]; - ASSIGN (taskDueDate, dueDate); +// /* here comes the code for initializing repeat, reminder and isAllDay... */ - /* here comes the code for initializing repeat, reminder and isAllDay... */ +// return self; +// } - return self; -} - -#warning this method could be replaced with a method common with UIxAppointmentEditor... +/** + * @api {post} /so/:username/Calendar/:calendarId/:todoId/save Save todo + * @apiVersion 1.0.0 + * @apiName PostToDoSave + * @apiGroup Calendar + * @apiExample {curl} Example usage: + * curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/2142-54198E00-F-821E450.ics/save \ + * -H 'Content-Type: application/json' \ + * -d '{ "Summary": "Todo", "startDate": "2015-01-28", "startTime": "10:00", \ + * "dueDate": "2015-01-28", "status": "in-process", "percentComplete": 25 }' + * + * @apiParam {String} [startDate] Start date (YYYY-MM-DD) + * @apiParam {String} [startTime] Start time (HH:MM) + * @apiParam {String} [dueDate] End date (YYYY-MM-DD) + * @apiParam {String} [dueTime] End time (HH:MM) + * @apiParam {String} [completedDate] End date (YYYY-MM-DD) + * @apiParam {String} [completedTime] End time (HH:MM) + * @apiParam {Number} percentComplete Percent completion (0-100) + * + * Save in [iCalEntityObject+SOGo setAttributes:inContext:] + * + * @apiParam {Number} [sendAppointmentNotifications] 0 if notifications must not be sent + * @apiParam {String} [summary] Summary + * @apiParam {String} [location] Location + * @apiParam {String} [comment] Comment + * @apiParam {String} [status] Status (needs-action, in-process, completed, or cancelled) + * @apiParam {String} [attachUrl] Attached URL + * @apiParam {Number} [priority] Priority + * @apiParam {NSString} [classification] Either public, confidential or private + * @apiParam {String[]} [categories] Categories + * @apiParam {Object[]} [attendees] List of attendees + * @apiParam {String} [attendees.name] Attendee's name + * @apiParam {String} attendees.email Attendee's email address + * @apiParam {String} [attendees.uid] System user ID + * @apiParam {String} attendees.status Attendee's participation status + * @apiParam {String} [attendees.role] Either CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT, or NON-PARTICIPANT + * @apiParam {String} [attendees.delegatedTo] User that the original request was delegated to + * @apiParam {String} [attendees.delegatedFrom] User the request was delegated from + * @apiParam {Object[]} [alarm] Alarm definition + * @apiParam {String} alarm.action Either display or email + * @apiParam {Number} alarm.quantity Quantity of units + * @apiParam {String} alarm.unit Either MINUTES, HOURS, or DAYS + * @apiParam {String} alarm.reference Either BEFORE or AFTER + * @apiParam {String} alarm.relation Either START or END + * @apiParam {Boolean} [alarm.attendees] Alert attendees by email if true and action is email + * @apiParam {Object} [alarm.organizer] Alert organizer at this email address if action is email + * @apiParam {String} [alarm.organizer.name] Attendee's name + * @apiParam {String} alarm.organizer.email Attendee's email address + * + * Save in [iCalRepeatbleEntityObject+SOGo setAttributes:inContext:] + * + * @apiParam {Object} [repeat] Recurrence rule definition + * @apiParam {String} repeat.frequency Either daily, every weekday, weekly, bi-weekly, monthly, or yearly + * @apiParam {Number} repeat.interval Intervals the recurrence rule repeats + * @apiParam {String} [repeat.count] Number of occurrences at which to range-bound the recurrence + * @apiParam {String} [repeat.until] A date (YYYY-MM-DD) that bounds the recurrence rule in an inclusive manner + * @apiParam {Object[]} [repeat.days] List of days of the week (by day mask) + * @apiParam {String} [repeat.days.day] Day of the week (SU, MO, TU, WE, TH, FR, SA) + * @apiParam {Number} [repeat.days.occurence] Occurrence of a specific day within the monthly or yearly rule (values are -5 to 5) + * @apiParam {Number[]} [repeat.months] List of months of the year (values are 1 to 12) + * @apiParam {Number[]} [repeat.monthdays] Days of the month (values are 1 to 31) + * + * Save in [UIxComponentEditor setAttributes:] + * + * @apiParam {Object} [organizer] Appointment organizer + * @apiParam {String} organizer.name Organizer's name + * @apiParam {String} organizer.email Organizer's email address + * + * @apiError (Error 500) {Object} error The error message + */ - (id ) saveAction { - NSString *newCalendar; + NSDictionary *params; + NSException *ex; + NSString *newCalendar, *jsonResponse; SOGoAppointmentFolder *thisFolder, *newFolder; SOGoTaskObject *co; SoSecurityManager *sm; + WORequest *request; + iCalToDo *todo; + todo = [self todo]; co = [self clientObject]; - [co saveComponent: todo]; - newCalendar = [self queryParameterForKey: @"moveToCalendar"]; - if ([newCalendar length]) + ex = nil; + request = [context request]; + params = [[request contentAsString] objectFromJSONString]; + if (params == nil) { - sm = [SoSecurityManager sharedSecurityManager]; + ex = [NSException exceptionWithName: @"JSONParsingException" + reason: @"Can't parse JSON string" + userInfo: nil]; + } + else + { + [self setAttributes: params]; + ex = [co saveComponent: todo]; - thisFolder = [co container]; - if (![sm validatePermission: SoPerm_DeleteObjects - onObject: thisFolder - inContext: context]) - { - newFolder = [[thisFolder container] lookupName: newCalendar - inContext: context - acquire: NO]; - if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: newFolder - inContext: context]) - [co moveToFolder: newFolder]; - } + newCalendar = [self queryParameterForKey: @"moveToCalendar"]; + if ([newCalendar length]) + { + sm = [SoSecurityManager sharedSecurityManager]; + + thisFolder = [co container]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: thisFolder + inContext: context]) + { + newFolder = [[thisFolder container] lookupName: newCalendar + inContext: context + acquire: NO]; + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: newFolder + inContext: context]) + [co moveToFolder: newFolder]; + } + } } - return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; + if (ex) + jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: + @"failure", @"status", + [ex reason], + @"message", + nil]; + else + jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: + @"success", @"status", nil]; + + return [self responseWithStatus: 200 + andJSONRepresentation: jsonResponse]; } /** - * @api {get} /so/:username/Calendar/:calendarId/:todoId/view Get task + * @api {get} /so/:username/Calendar/:calendarId/:todoId/view Get todo * @apiVersion 1.0.0 - * @apiName GetTaskView + * @apiName GetToDoView * @apiGroup Calendar * @apiExample {curl} Example usage: - * curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/1A5-53489200-2D-6BD99880.ics/view + * curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/2142-54198E00-F-821E450.ics/view * * @apiParam {Number} [resetAlarm] Mark alarm as triggered if set to 1 * @apiParam {Number} [snoozeAlarm] Snooze the alarm for this number of minutes @@ -405,11 +392,16 @@ * @apiSuccess (Success 200) {String} id Todo ID * @apiSuccess (Success 200) {String} pid Calendar ID (todo's folder) * @apiSuccess (Success 200) {String} calendar Human readable name of calendar - * @apiSuccess (Success 200) {String} startDate Formatted start date - * @apiSuccess (Success 200) {String} startTime Formatted start time - * @apiSuccess (Success 200) {String} dueDate Formatted due date - * @apiSuccess (Success 200) {String} dueTime Formatted due time - * @apiSuccess (Success 200) {NSNumber} percentComplete Percent completion + * @apiSuccess (Success 200) {String} startDate Start date (ISO8601) + * @apiSuccess (Success 200) {String} localizedStartDate Formatted start date + * @apiSuccess (Success 200) {String} localizedStartTime Formatted start time + * @apiSuccess (Success 200) {String} dueDate Due date (ISO8601) + * @apiSuccess (Success 200) {String} localizedDueDate Formatted due date + * @apiSuccess (Success 200) {String} localizedDueTime Formatted due time + * @apiSuccess (Success 200) {String} completedDate Completed date (ISO8601) + * @apiSuccess (Success 200) {String} completedTime Formatted completed time + * @apiSuccess (Success 200) {String} status Status (needs-action, in-process, completed, or cancelled) + * @apiSuccess (Success 200) {Number} percentComplete Percent completion * * From [iCalEntityObject+SOGo attributes] * @@ -458,26 +450,36 @@ - (id ) viewAction { NSMutableDictionary *data; - NSCalendarDate *startDate, *dueDate; + NSCalendarDate *startDate, *dueDate, *completedDate; NSTimeZone *timeZone; SOGoCalendarComponent *co; SOGoAppointmentFolder *thisFolder; SOGoUserDefaults *ud; iCalAlarm *anAlarm; - BOOL resetAlarm; - BOOL snoozeAlarm; + iCalToDo *todo; + BOOL resetAlarm, snoozeAlarm; + BOOL isAllDayStartDate, isAllDayDueDate; - [self todo]; + todo = [self todo]; co = [self clientObject]; thisFolder = [co container]; ud = [[context activeUser] userDefaults]; timeZone = [ud timeZone]; + startDate = [todo startDate]; - [startDate setTimeZone: timeZone]; + isAllDayStartDate = [(iCalDateTime *) [todo uniqueChildWithTag: @"dtstart"] isAllDay]; + if (!isAllDayStartDate) + [startDate setTimeZone: timeZone]; + dueDate = [todo due]; - [dueDate setTimeZone: timeZone]; + isAllDayDueDate = [(iCalDateTime *) [todo uniqueChildWithTag: @"due"] isAllDay]; + if (!isAllDayDueDate) + [dueDate setTimeZone: timeZone]; + + completedDate = [todo completed]; + [completedDate setTimeZone: timeZone]; // resetAlarm=yes is set only when we are about to show the alarm popup in the Web // interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the @@ -512,99 +514,114 @@ [co snoozeAlarm: snoozeAlarm]; } } - resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue]; data = [NSMutableDictionary dictionaryWithObjectsAndKeys: [co nameInContainer], @"id", [thisFolder nameInContainer], @"pid", [thisFolder displayName], @"calendar", - (startDate? (id)[dateFormatter formattedDate: startDate] : (id)@""), @"startDate", - (startDate? (id)[dateFormatter formattedTime: startDate] : (id)@""), @"startTime", - (dueDate? (id)[dateFormatter formattedDate: dueDate] : (id)@""), @"dueDate", - (dueDate? (id)[dateFormatter formattedTime: dueDate] : (id)@""), @"dueTime", - [NSNumber numberWithInt: [[todo percentComplete] intValue]], @"percentComplete", nil]; - - // Add attributes from iCalEntityObject+SOGo and iCalRepeatableEntityObject+SOGo - [data addEntriesFromDictionary: [todo attributes]]; + + if (startDate) + { + [data setObject: [dateFormatter formattedDate: startDate] forKey: @"localizedStartDate"]; + if (!isAllDayStartDate) + [data setObject: [dateFormatter formattedTime: startDate] forKey: @"localizedStartTime"]; + } + if (dueDate) + { + [data setObject: [dateFormatter formattedDate: dueDate] forKey: @"localizedDueDate"]; + if (!isAllDayDueDate) + [data setObject: [dateFormatter formattedTime: dueDate] forKey: @"localizedDueTime"]; + } + + if (completedDate) + { + [data setObject: [dateFormatter formattedDate: completedDate] forKey: @"localizedCompletedDate"]; + [data setObject: [dateFormatter formattedTime: completedDate] forKey: @"localizedCompletedTime"]; + } + + // Add attributes from iCalToDo+SOGo, iCalEntityObject+SOGo and iCalRepeatableEntityObject+SOGo + [data addEntriesFromDictionary: [todo attributesInContext: context]]; // Return JSON representation return [self responseWithStatus: 200 andJSONRepresentation: data]; } -- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request - inContext: (WOContext*) context -{ - NSString *actionName; +// - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request +// inContext: (WOContext*) context +// { +// NSString *actionName; - actionName = [[request requestHandlerPath] lastPathComponent]; +// actionName = [[request requestHandlerPath] lastPathComponent]; - return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)] - && [actionName hasPrefix: @"save"]); -} +// return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)] +// && [actionName hasPrefix: @"save"]); +// } -- (void) takeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext *) _ctx -{ - SOGoUserDefaults *ud; - iCalTimeZone *tz; +// - (void) takeValuesFromRequest: (WORequest *) _rq +// inContext: (WOContext *) _ctx +// { +// SOGoUserDefaults *ud; +// iCalTimeZone *tz; +// iCalToDo *todo; - [self todo]; +// todo = [self todo]; - [super takeValuesFromRequest: _rq inContext: _ctx]; +// [super takeValuesFromRequest: _rq inContext: _ctx]; - if (hasStartDate) - [todo setStartDate: taskStartDate]; - else - { - [todo setStartDate: nil]; - [todo removeAllAlarms]; - } +// if (hasStartDate) +// [todo setStartDate: taskStartDate]; +// else +// { +// [todo setStartDate: nil]; +// [todo removeAllAlarms]; +// } - if (hasDueDate) - [todo setDue: taskDueDate]; - else - [todo setDue: nil]; +// if (hasDueDate) +// [todo setDue: taskDueDate]; +// else +// [todo setDue: nil]; - if ([status isEqualToString: @"COMPLETED"]) - [todo setCompleted: statusDate]; - else - [todo setCompleted: nil]; - if ([status length] > 0) - { - [todo setStatus: status]; - [todo setPercentComplete: statusPercent]; - } - else - { - [todo setStatus: @""]; - [todo setPercentComplete: @""]; - } +// if ([status isEqualToString: @"COMPLETED"]) +// [todo setCompleted: statusDate]; +// else +// [todo setCompleted: nil]; +// if ([status length] > 0) +// { +// [todo setStatus: status]; +// [todo setPercentComplete: statusPercent]; +// } +// else +// { +// [todo setStatus: @""]; +// [todo setPercentComplete: @""]; +// } - if ([[self clientObject] isNew]) - { - ud = [[context activeUser] userDefaults]; - tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; +// if ([[self clientObject] isNew]) +// { +// ud = [[context activeUser] userDefaults]; +// tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; - if (hasStartDate || hasDueDate) - { - [[todo parent] addTimeZone: tz]; - } +// if (hasStartDate || hasDueDate) +// { +// [[todo parent] addTimeZone: tz]; +// } - if (hasStartDate) - [(iCalDateTime *)[todo uniqueChildWithTag: @"dtstart"] setTimeZone: tz]; +// if (hasStartDate) +// [(iCalDateTime *)[todo uniqueChildWithTag: @"dtstart"] setTimeZone: tz]; - if (hasDueDate) - [(iCalDateTime *)[todo uniqueChildWithTag: @"due"] setTimeZone: tz]; - } +// if (hasDueDate) +// [(iCalDateTime *)[todo uniqueChildWithTag: @"due"] setTimeZone: tz]; +// } -} +// } - (id) changeStatusAction { NSString *newStatus; + iCalToDo *todo; - [self todo]; + todo = [self todo]; if (todo) { newStatus = [self queryParameterForKey: @"status"];