Improve JSON getter/setter for todos

pull/91/head
Francis Lachapelle 2015-02-18 16:44:09 -05:00
parent f47a22068f
commit 94105e937a
5 changed files with 530 additions and 348 deletions

View File

@ -39,6 +39,7 @@
- (NSString *) percentComplete; - (NSString *) percentComplete;
- (void) setDue: (NSCalendarDate *) _date; - (void) setDue: (NSCalendarDate *) _date;
- (void) setAllDayDue: (NSCalendarDate *) _date;
- (NSCalendarDate *) due; - (NSCalendarDate *) due;
- (void) setCompleted: (NSCalendarDate *) _date; - (void) setCompleted: (NSCalendarDate *) _date;

View File

@ -65,6 +65,12 @@
setDateTime: newDueDate]; setDateTime: newDueDate];
} }
- (void) setAllDayDue: (NSCalendarDate *) newDueDate
{
[(iCalDateTime *) [self uniqueChildWithTag: @"due"]
setDate: newDueDate];
}
- (NSCalendarDate *) due - (NSCalendarDate *) due
{ {
return [(iCalDateTime *) [self uniqueChildWithTag: @"due"] return [(iCalDateTime *) [self uniqueChildWithTag: @"due"]

View File

@ -27,10 +27,19 @@
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
#import <NGCards/NSString+NGCards.h> #import <NGCards/NSString+NGCards.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTrigger.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "iCalRepeatableEntityObject+SOGo.h" #import "iCalRepeatableEntityObject+SOGo.h"
@ -38,6 +47,179 @@
@implementation iCalToDo (SOGoExtensions) @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 - (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer container: (id) theContainer
{ {
@ -53,7 +235,7 @@
iCalAccessClass accessClass; iCalAccessClass accessClass;
/* extract values */ /* extract values */
startDate = [self startDate]; startDate = [self startDate];
dueDate = [self due]; dueDate = [self due];
uid = [self uid]; uid = [self uid];
@ -78,7 +260,7 @@
[row setObject: @"vtodo" forKey: @"c_component"]; [row setObject: @"vtodo" forKey: @"c_component"];
if ([uid isNotNull]) if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"]; [row setObject:uid forKey: @"c_uid"];
else else
[self logWithFormat: @"WARNING: could not extract a uid from event!"]; [self logWithFormat: @"WARNING: could not extract a uid from event!"];
@ -96,7 +278,7 @@
[row setObject: title forKey: @"c_title"]; [row setObject: title forKey: @"c_title"];
if ([location isNotNull]) [row setObject: location forKey: @"c_location"]; if ([location isNotNull]) [row setObject: location forKey: @"c_location"];
if ([sequence isNotNull]) [row setObject: sequence forKey: @"c_sequence"]; if ([sequence isNotNull]) [row setObject: sequence forKey: @"c_sequence"];
if ([startDate isNotNull]) if ([startDate isNotNull])
date = [self quickRecordDateAsNumber: startDate date = [self quickRecordDateAsNumber: startDate
withOffset: 0 forAllDay: NO]; withOffset: 0 forAllDay: NO];
@ -146,12 +328,12 @@
if (organizer) if (organizer)
{ {
NSString *email; NSString *email;
email = [organizer valueForKey: @"rfc822Email"]; email = [organizer valueForKey: @"rfc822Email"];
if (email) if (email)
[row setObject:email forKey: @"c_orgmail"]; [row setObject:email forKey: @"c_orgmail"];
} }
/* construct partstates */ /* construct partstates */
count = [attendees count]; count = [attendees count];
partstates = [[NSMutableString alloc] initWithCapacity:count * 2]; partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
@ -159,7 +341,7 @@
{ {
iCalPerson *p; iCalPerson *p;
iCalPersonPartStat stat; iCalPersonPartStat stat;
p = [attendees objectAtIndex:i]; p = [attendees objectAtIndex:i];
stat = [p participationStatus]; stat = [p participationStatus];
if(i != 0) if(i != 0)
@ -171,7 +353,7 @@
/* handle alarms */ /* handle alarms */
[self updateNextAlarmDateInRow: row forContainer: theContainer]; [self updateNextAlarmDateInRow: row forContainer: theContainer];
categories = [self categories]; categories = [self categories];
if ([categories count] > 0) if ([categories count] > 0)
[row setObject: [categories componentsJoinedByString: @","] [row setObject: [categories componentsJoinedByString: @","]

View File

@ -1,8 +1,6 @@
/* UIxTaskEditor.h - this file is part of SOGo /* UIxTaskEditor.h - this file is part of SOGo
* *
* Copyright (C) 2007-2009 Inverse inc. * Copyright (C) 2007-2015 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -28,35 +26,13 @@
@class iCalToDo; @class iCalToDo;
@class NSString; @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; SOGoDateFormatter *dateFormatter;
} }
/* template values */
- (NSString *) saveURL;
- (iCalToDo *) todo; - (iCalToDo *) todo;
/* icalendar values */
- (void) setTaskStartDate: (NSCalendarDate *) _date;
- (NSCalendarDate *) taskStartDate;
- (void) setTaskDueDate: (NSCalendarDate *) _date;
- (NSCalendarDate *) taskDueDate;
- (NSString *) taskStartDateTimeText;
- (NSString *) taskDueDateTimeText;
- (NSString *) statusDateText;
@end @end
#endif /* UIXAPPOINTMENTEDITOR_H */ #endif /* UIXAPPOINTMENTEDITOR_H */

View File

@ -37,7 +37,9 @@
#import <NGCards/iCalTimeZone.h> #import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalDateTime.h> #import <NGCards/iCalDateTime.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/NSDictionary+Utilities.h> #import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoContentObject.h> #import <SOGo/SOGoContentObject.h>
#import <SOGo/SOGoDateFormatter.h> #import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
@ -58,15 +60,7 @@
if ((self = [super init])) if ((self = [super init]))
{ {
taskStartDate = nil;
taskDueDate = nil;
statusDate = nil;
hasStartDate = NO;
hasDueDate = NO;
status = nil;
statusPercent = nil;
item = nil; item = nil;
todo = nil;
user = [[self context] activeUser]; user = [[self context] activeUser];
ASSIGN (dateFormatter, [user dateFormatterInContext: context]); ASSIGN (dateFormatter, [user dateFormatterInContext: context]);
@ -77,26 +71,14 @@
- (void) dealloc - (void) dealloc
{ {
[taskStartDate release];
[taskDueDate release];
[statusDate release];
[status release];
[statusPercent release];
[dateFormatter release]; [dateFormatter release];
[[todo parent] release];
[super dealloc]; [super dealloc];
} }
/* template values */ /* template values */
- (iCalToDo *) todo - (iCalToDo *) todo
{ {
if (!todo) return (iCalToDo *) component;
{
todo = (iCalToDo *) [[self clientObject] occurence];
[[todo parent] retain];
}
return todo;
} }
- (NSString *) saveURL - (NSString *) saveURL
@ -106,150 +88,61 @@
} }
/* icalendar values */ /* icalendar values */
- (void) setTaskStartDate: (NSCalendarDate *) newTaskStartDate
{
ASSIGN (taskStartDate, newTaskStartDate);
}
- (NSCalendarDate *) taskStartDate // - (NSArray *) statusList
{ // {
return taskStartDate; // static NSArray *statusItems = nil;
}
- (void) setHasStartDate: (BOOL) newHasStartDate // if (!statusItems)
{ // {
hasStartDate = newHasStartDate; // statusItems = [NSArray arrayWithObjects: @"NEEDS-ACTION",
} // @"IN-PROCESS",
// @"COMPLETED",
// @"CANCELLED",
// nil];
// [statusItems retain];
// }
- (BOOL) hasStartDate // return statusItems;
{ // }
return hasStartDate;
}
- (BOOL) startDateDisabled // - (NSString *) itemStatusText
{ // {
return !hasStartDate; // if (!item)
} // {
// item = status;
// if (!item)
// item = @"NOT-SPECIFIED";
// }
// return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]];
// }
- (void) setTaskDueDate: (NSCalendarDate *) newTaskDueDate // - (BOOL) statusDateDisabled
{ // {
ASSIGN (taskDueDate, newTaskDueDate); // return !([status isEqualToString: @"COMPLETED"] || statusDate);
} // }
- (NSCalendarDate *) taskDueDate // - (BOOL) statusPercentDisabled
{ // {
return taskDueDate; // return ([status length] == 0 || [status isEqualToString: @"CANCELLED"]);
} // }
- (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;
}
/* viewing read-only tasks */ /* viewing read-only tasks */
- (NSString *) taskStartDateTimeText // - (NSString *) taskStartDateTimeText
{ // {
return [dateFormatter formattedDateAndTime: taskStartDate]; // return [dateFormatter formattedDateAndTime: taskStartDate];
} // }
- (NSString *) taskDueDateTimeText // - (NSString *) taskDueDateTimeText
{ // {
return [dateFormatter formattedDateAndTime: taskDueDate]; // return [dateFormatter formattedDateAndTime: taskDueDate];
} // }
- (NSString *) statusDateText // - (NSString *) statusDateText
{ // {
return [dateFormatter formattedDate: statusDate]; // return [dateFormatter formattedDate: statusDate];
} // }
/* actions */ /* actions */
- (NSCalendarDate *) newStartDate - (NSCalendarDate *) newStartDate
@ -290,114 +183,208 @@
return newStartDate; return newStartDate;
} }
- (id <WOActionResults>) defaultAction // - (id <WOActionResults>) defaultAction
{ // {
NSCalendarDate *startDate, *dueDate; // NSCalendarDate *startDate, *dueDate;
NSString *duration; // NSString *duration;
NSTimeZone *timeZone; // NSTimeZone *timeZone;
SOGoUserDefaults *ud; // SOGoUserDefaults *ud;
unsigned int minutes; // iCalToDo *todo;
// unsigned int minutes;
ud = [[context activeUser] userDefaults]; // ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone]; // timeZone = [ud timeZone];
[self todo]; // todo = [self todo];
if (todo) // if (todo)
{ // {
startDate = [todo startDate]; // startDate = [todo startDate];
dueDate = [todo due]; // dueDate = [todo due];
if (startDate) // if (startDate)
hasStartDate = YES; // hasStartDate = YES;
else // else
startDate = [self newStartDate]; // startDate = [self newStartDate];
if (dueDate) // if (dueDate)
hasDueDate = YES; // hasDueDate = YES;
else // else
dueDate = [self newStartDate]; // dueDate = [self newStartDate];
ASSIGN (statusDate, [todo completed]); // ASSIGN (statusDate, [todo completed]);
[statusDate setTimeZone: timeZone]; // [statusDate setTimeZone: timeZone];
ASSIGN (status, [todo status]); // ASSIGN (status, [todo status]);
if ([status length] == 0 && statusDate) // if ([status length] == 0 && statusDate)
{ // {
ASSIGN(status, @"COMPLETED"); // ASSIGN(status, @"COMPLETED");
} // }
else // else
{ // {
ASSIGN (statusDate, [self newStartDate]); // ASSIGN (statusDate, [self newStartDate]);
} // }
ASSIGN (statusPercent, [todo percentComplete]); // ASSIGN (statusPercent, [todo percentComplete]);
} // }
else // else
{ // {
startDate = [self newStartDate]; // startDate = [self newStartDate];
duration = [self queryParameterForKey:@"dur"]; // duration = [self queryParameterForKey:@"dur"];
if ([duration length] > 0) // if ([duration length] > 0)
minutes = [duration intValue]; // minutes = [duration intValue];
else // else
minutes = 60; // minutes = 60;
dueDate = [startDate dateByAddingYears: 0 months: 0 days: 0 // dueDate = [startDate dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: minutes seconds: 0]; // hours: 0 minutes: minutes seconds: 0];
hasStartDate = NO; // hasStartDate = NO;
hasDueDate = NO; // hasDueDate = NO;
ASSIGN (statusDate, [self newStartDate]); // ASSIGN (statusDate, [self newStartDate]);
ASSIGN (status, @""); // ASSIGN (status, @"");
ASSIGN (statusPercent, @""); // ASSIGN (statusPercent, @"");
} // }
[startDate setTimeZone: timeZone]; // /* here comes the code for initializing repeat, reminder and isAllDay... */
ASSIGN (taskStartDate, startDate);
[dueDate setTimeZone: timeZone];
ASSIGN (taskDueDate, dueDate);
/* here comes the code for initializing repeat, reminder and isAllDay... */ // return self;
// }
return self; /**
} * @api {post} /so/:username/Calendar/:calendarId/:todoId/save Save todo
* @apiVersion 1.0.0
#warning this method could be replaced with a method common with UIxAppointmentEditor... * @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 <WOActionResults>) saveAction - (id <WOActionResults>) saveAction
{ {
NSString *newCalendar; NSDictionary *params;
NSException *ex;
NSString *newCalendar, *jsonResponse;
SOGoAppointmentFolder *thisFolder, *newFolder; SOGoAppointmentFolder *thisFolder, *newFolder;
SOGoTaskObject *co; SOGoTaskObject *co;
SoSecurityManager *sm; SoSecurityManager *sm;
WORequest *request;
iCalToDo *todo;
todo = [self todo];
co = [self clientObject]; co = [self clientObject];
[co saveComponent: todo];
newCalendar = [self queryParameterForKey: @"moveToCalendar"]; ex = nil;
if ([newCalendar length]) 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]; newCalendar = [self queryParameterForKey: @"moveToCalendar"];
if (![sm validatePermission: SoPerm_DeleteObjects if ([newCalendar length])
onObject: thisFolder {
inContext: context]) sm = [SoSecurityManager sharedSecurityManager];
{
newFolder = [[thisFolder container] lookupName: newCalendar thisFolder = [co container];
inContext: context if (![sm validatePermission: SoPerm_DeleteObjects
acquire: NO]; onObject: thisFolder
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles inContext: context])
onObject: newFolder {
inContext: context]) newFolder = [[thisFolder container] lookupName: newCalendar
[co moveToFolder: newFolder]; 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 * @apiVersion 1.0.0
* @apiName GetTaskView * @apiName GetToDoView
* @apiGroup Calendar * @apiGroup Calendar
* @apiExample {curl} Example usage: * @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} [resetAlarm] Mark alarm as triggered if set to 1
* @apiParam {Number} [snoozeAlarm] Snooze the alarm for this number of minutes * @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} id Todo ID
* @apiSuccess (Success 200) {String} pid Calendar ID (todo's folder) * @apiSuccess (Success 200) {String} pid Calendar ID (todo's folder)
* @apiSuccess (Success 200) {String} calendar Human readable name of calendar * @apiSuccess (Success 200) {String} calendar Human readable name of calendar
* @apiSuccess (Success 200) {String} startDate Formatted start date * @apiSuccess (Success 200) {String} startDate Start date (ISO8601)
* @apiSuccess (Success 200) {String} startTime Formatted start time * @apiSuccess (Success 200) {String} localizedStartDate Formatted start date
* @apiSuccess (Success 200) {String} dueDate Formatted due date * @apiSuccess (Success 200) {String} localizedStartTime Formatted start time
* @apiSuccess (Success 200) {String} dueTime Formatted due time * @apiSuccess (Success 200) {String} dueDate Due date (ISO8601)
* @apiSuccess (Success 200) {NSNumber} percentComplete Percent completion * @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] * From [iCalEntityObject+SOGo attributes]
* *
@ -458,26 +450,36 @@
- (id <WOActionResults>) viewAction - (id <WOActionResults>) viewAction
{ {
NSMutableDictionary *data; NSMutableDictionary *data;
NSCalendarDate *startDate, *dueDate; NSCalendarDate *startDate, *dueDate, *completedDate;
NSTimeZone *timeZone; NSTimeZone *timeZone;
SOGoCalendarComponent *co; SOGoCalendarComponent *co;
SOGoAppointmentFolder *thisFolder; SOGoAppointmentFolder *thisFolder;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
iCalAlarm *anAlarm; iCalAlarm *anAlarm;
BOOL resetAlarm; iCalToDo *todo;
BOOL snoozeAlarm; BOOL resetAlarm, snoozeAlarm;
BOOL isAllDayStartDate, isAllDayDueDate;
[self todo]; todo = [self todo];
co = [self clientObject]; co = [self clientObject];
thisFolder = [co container]; thisFolder = [co container];
ud = [[context activeUser] userDefaults]; ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone]; timeZone = [ud timeZone];
startDate = [todo startDate]; startDate = [todo startDate];
[startDate setTimeZone: timeZone]; isAllDayStartDate = [(iCalDateTime *) [todo uniqueChildWithTag: @"dtstart"] isAllDay];
if (!isAllDayStartDate)
[startDate setTimeZone: timeZone];
dueDate = [todo due]; 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 // 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 // interface of SOGo. See generic.js for details. snoozeAlarm=X is called when the
@ -512,99 +514,114 @@
[co snoozeAlarm: snoozeAlarm]; [co snoozeAlarm: snoozeAlarm];
} }
} }
resetAlarm = [[[context request] formValueForKey: @"resetAlarm"] boolValue];
data = [NSMutableDictionary dictionaryWithObjectsAndKeys: data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[co nameInContainer], @"id", [co nameInContainer], @"id",
[thisFolder nameInContainer], @"pid", [thisFolder nameInContainer], @"pid",
[thisFolder displayName], @"calendar", [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]; nil];
// Add attributes from iCalEntityObject+SOGo and iCalRepeatableEntityObject+SOGo if (startDate)
[data addEntriesFromDictionary: [todo attributes]]; {
[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 JSON representation
return [self responseWithStatus: 200 andJSONRepresentation: data]; return [self responseWithStatus: 200 andJSONRepresentation: data];
} }
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request // - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext*) context // inContext: (WOContext*) context
{ // {
NSString *actionName; // NSString *actionName;
actionName = [[request requestHandlerPath] lastPathComponent]; // actionName = [[request requestHandlerPath] lastPathComponent];
return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)] // return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)]
&& [actionName hasPrefix: @"save"]); // && [actionName hasPrefix: @"save"]);
} // }
- (void) takeValuesFromRequest: (WORequest *) _rq // - (void) takeValuesFromRequest: (WORequest *) _rq
inContext: (WOContext *) _ctx // inContext: (WOContext *) _ctx
{ // {
SOGoUserDefaults *ud; // SOGoUserDefaults *ud;
iCalTimeZone *tz; // iCalTimeZone *tz;
// iCalToDo *todo;
[self todo]; // todo = [self todo];
[super takeValuesFromRequest: _rq inContext: _ctx]; // [super takeValuesFromRequest: _rq inContext: _ctx];
if (hasStartDate) // if (hasStartDate)
[todo setStartDate: taskStartDate]; // [todo setStartDate: taskStartDate];
else // else
{ // {
[todo setStartDate: nil]; // [todo setStartDate: nil];
[todo removeAllAlarms]; // [todo removeAllAlarms];
} // }
if (hasDueDate) // if (hasDueDate)
[todo setDue: taskDueDate]; // [todo setDue: taskDueDate];
else // else
[todo setDue: nil]; // [todo setDue: nil];
if ([status isEqualToString: @"COMPLETED"]) // if ([status isEqualToString: @"COMPLETED"])
[todo setCompleted: statusDate]; // [todo setCompleted: statusDate];
else // else
[todo setCompleted: nil]; // [todo setCompleted: nil];
if ([status length] > 0) // if ([status length] > 0)
{ // {
[todo setStatus: status]; // [todo setStatus: status];
[todo setPercentComplete: statusPercent]; // [todo setPercentComplete: statusPercent];
} // }
else // else
{ // {
[todo setStatus: @""]; // [todo setStatus: @""];
[todo setPercentComplete: @""]; // [todo setPercentComplete: @""];
} // }
if ([[self clientObject] isNew]) // if ([[self clientObject] isNew])
{ // {
ud = [[context activeUser] userDefaults]; // ud = [[context activeUser] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]]; // tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
if (hasStartDate || hasDueDate) // if (hasStartDate || hasDueDate)
{ // {
[[todo parent] addTimeZone: tz]; // [[todo parent] addTimeZone: tz];
} // }
if (hasStartDate) // if (hasStartDate)
[(iCalDateTime *)[todo uniqueChildWithTag: @"dtstart"] setTimeZone: tz]; // [(iCalDateTime *)[todo uniqueChildWithTag: @"dtstart"] setTimeZone: tz];
if (hasDueDate) // if (hasDueDate)
[(iCalDateTime *)[todo uniqueChildWithTag: @"due"] setTimeZone: tz]; // [(iCalDateTime *)[todo uniqueChildWithTag: @"due"] setTimeZone: tz];
} // }
} // }
- (id) changeStatusAction - (id) changeStatusAction
{ {
NSString *newStatus; NSString *newStatus;
iCalToDo *todo;
[self todo]; todo = [self todo];
if (todo) if (todo)
{ {
newStatus = [self queryParameterForKey: @"status"]; newStatus = [self queryParameterForKey: @"status"];