Monotone-Parent: 29948ba19f24f47aa6c1b2c0764586b89ff9493f

Monotone-Revision: 1a5434f30282a4932bf29227da4cf2da44ef5783

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2008-07-11T22:28:12
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2008-07-11 22:28:12 +00:00
parent 45f0686d92
commit 6c5003d7bc
22 changed files with 773 additions and 480 deletions

View File

@ -1,3 +1,17 @@
2008-07-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/Appointments/SOGoAppointmentFolder.m
([SOGoAppointmentFolder
-fetchFields:_fieldsfrom:_startDateto:_endDatetitle:titlecomponent:_componentadditionalFilters:filters]):
take exceptional modifications into account.
* SoObjects/Appointments/iCalToDo+SOGo.m: new class module with
category methods for iCalToDo.
([iCalToDo -quickRecord]): same as for method below.
* SoObjects/Appointments/iCalEvent+SOGo.m ([iCalEvent
-quickRecord]): new method taken from OCSiCalFieldExtractor.
2008-07-10 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/NSString+DAV.m ([NSString -asWebDAVTuple]): new

View File

@ -17,9 +17,6 @@ ADDITIONAL_INCLUDE_DIRS += -I../SOPE
ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj
libOGoContentStore_OBJC_FILES += \
iCalEntityObject+OCS.m \
iCalRepeatableEntityObject+OCS.m \
\
OCSiCalFieldExtractor.m \
OCSContactFieldExtractor.m \

View File

@ -19,34 +19,21 @@
02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalCalendar.h>
#import "iCalEntityObject+OCS.h"
#import "iCalRepeatableEntityObject+OCS.h"
#import <SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h>
#import "OCSiCalFieldExtractor.h"
@implementation OCSiCalFieldExtractor
static NSCalendarDate *distantFuture = nil;
static NSNumber *distantFutureNumber = nil;
+ (void) initialize
{
if (!distantFuture)
{
distantFuture = [[NSCalendarDate distantFuture] retain];
/* INT_MAX due to Postgres constraint */
distantFutureNumber = [[NSNumber numberWithUnsignedInt: INT_MAX] retain];
}
}
+ (id) sharedICalFieldExtractor
{
static OCSiCalFieldExtractor *extractor = nil;
@ -59,287 +46,6 @@ static NSNumber *distantFutureNumber = nil;
/* operations */
- (NSNumber *) numberForDate: (NSCalendarDate *) _date
{
return ((_date == distantFuture)
? distantFutureNumber
: [NSNumber numberWithUnsignedInt: [_date timeIntervalSince1970]]);
}
- (NSMutableDictionary *) extractQuickFieldsFromEvent: (iCalEvent *) _event
{
NSMutableDictionary *row;
NSCalendarDate *startDate, *endDate;
NSArray *attendees;
NSString *uid, *title, *location, *status;
NSNumber *sequence, *dateNumber;
id organizer;
id participants, partmails;
NSMutableString *partstates;
unsigned int i, count;
BOOL isAllDay;
iCalAccessClass accessClass;
if (_event == nil)
return nil;
/* extract values */
startDate = [_event startDate];
endDate = [_event endDate];
uid = [_event uid];
title = [_event summary];
if (![title isNotNull])
title = @"";
location = [_event location];
sequence = [_event sequence];
accessClass = [_event symbolicAccessClass];
isAllDay = [_event isAllDay];
status = [[_event status] uppercaseString];
attendees = [_event attendees];
partmails = [attendees valueForKey: @"rfc822Email"];
partmails = [partmails componentsJoinedByString: @"\n"];
participants = [attendees valueForKey: @"cn"];
participants = [participants componentsJoinedByString: @"\n"];
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vevent" forKey: @"c_component"];
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
[row setObject: [NSNumber numberWithBool: isAllDay]
forKey: @"c_isallday"];
[row setObject: [NSNumber numberWithBool: [_event isRecurrent]]
forKey: @"c_iscycle"];
[row setObject: [NSNumber numberWithBool: [_event isOpaque]]
forKey: @"c_isopaque"];
[row setObject: [NSNumber numberWithInt: [_event priorityNumber]]
forKey: @"c_priority"];
[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])
[row setObject: [self numberForDate: startDate]
forKey: @"c_startdate"];
if ([endDate isNotNull])
{
if (endDate == distantFuture)
dateNumber = distantFutureNumber;
else
{
if (isAllDay)
i = 1;
else
i = 0;
dateNumber
= [NSNumber numberWithUnsignedInt:
[endDate timeIntervalSince1970] - i];
}
[row setObject: dateNumber forKey: @"c_enddate"];
}
if ([_event isRecurrent]) {
NSCalendarDate *date;
date = [_event lastPossibleRecurrenceStartDate];
if (!date) {
/* this could also be *nil*, but in the end it makes the fetchspecs
more complex - thus we set it to a "reasonable" distant future */
date = distantFuture;
}
[row setObject:[self numberForDate:date] forKey: @"c_cycleenddate"];
[row setObject:[_event cycleInfo] forKey: @"c_cycleinfo"];
}
if ([participants length] > 0)
[row setObject: participants forKey: @"c_participants"];
if ([partmails length] > 0)
[row setObject: partmails forKey: @"c_partmails"];
if ([status isNotNull]) {
int code = 1;
if ([status isEqualToString: @"TENTATIVE"])
code = 2;
else if ([status isEqualToString: @"CANCELLED"])
code = 0;
[row setObject:[NSNumber numberWithInt:code] forKey: @"c_status"];
}
else {
/* confirmed by default */
[row setObject: [NSNumber numberWithInt:1] forKey: @"c_status"];
}
[row setObject: [NSNumber numberWithUnsignedInt: accessClass]
forKey: @"c_classification"];
organizer = [_event organizer];
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];
for ( i = 0; i < count; i++) {
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if(i != 0)
[partstates appendString: @"\n"];
[partstates appendFormat: @"%d", stat];
}
[row setObject:partstates forKey: @"c_partstates"];
[partstates release];
return row;
}
- (NSMutableDictionary *) extractQuickFieldsFromTodo: (iCalToDo *) _task
{
NSMutableDictionary *row;
NSCalendarDate *startDate, *dueDate;
NSArray *attendees;
NSString *uid, *title, *location, *status;
NSNumber *sequence;
id organizer, date;
id participants, partmails;
NSMutableString *partstates;
unsigned i, count, code;
iCalAccessClass accessClass;
if (_task == nil)
return nil;
/* extract values */
startDate = [_task startDate];
dueDate = [_task due];
uid = [_task uid];
title = [_task summary];
if (![title isNotNull])
title = @"";
location = [_task location];
sequence = [_task sequence];
accessClass = [_task symbolicAccessClass];
status = [[_task status] uppercaseString];
attendees = [_task attendees];
partmails = [attendees valueForKey: @"rfc822Email"];
partmails = [partmails componentsJoinedByString: @"\n"];
participants = [attendees valueForKey: @"cn"];
participants = [participants componentsJoinedByString: @"\n"];
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vtodo" forKey: @"c_component"];
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
[row setObject:[NSNumber numberWithBool:[_task isRecurrent]]
forKey: @"c_iscycle"];
[row setObject:[NSNumber numberWithInt:[_task priorityNumber]]
forKey: @"c_priority"];
[row setObject: [NSNumber numberWithBool: NO]
forKey: @"c_isallday"];
[row setObject: [NSNumber numberWithBool: NO]
forKey: @"c_isopaque"];
[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 numberForDate: startDate];
else
date = [NSNull null];
[row setObject: date forKey: @"c_startdate"];
if ([dueDate isNotNull])
date = [self numberForDate: dueDate];
else
date = [NSNull null];
[row setObject: date forKey: @"c_enddate"];
if ([_task isRecurrent])
{
[row setObject: [self numberForDate: distantFuture]
forKey: @"c_cycleenddate"];
[row setObject: [_task cycleInfo]
forKey: @"c_cycleinfo"];
}
if ([participants length] > 0)
[row setObject:participants forKey: @"c_participants"];
if ([partmails length] > 0)
[row setObject:partmails forKey: @"c_partmails"];
if ([status isNotNull]) {
code = 0; /* NEEDS-ACTION */
if ([status isEqualToString: @"COMPLETED"])
code = 1;
else if ([status isEqualToString: @"IN-PROCESS"])
code = 2;
else if ([status isEqualToString: @"CANCELLED"])
code = 3;
[row setObject: [NSNumber numberWithInt: code] forKey: @"c_status"];
}
else {
/* confirmed by default */
[row setObject:[NSNumber numberWithInt:1] forKey: @"c_status"];
}
[row setObject: [NSNumber numberWithUnsignedInt: accessClass]
forKey: @"c_classification"];
organizer = [_task organizer];
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];
for ( i = 0; i < count; i++) {
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if(i != 0)
[partstates appendString: @"\n"];
[partstates appendFormat: @"%d", stat];
}
[row setObject:partstates forKey: @"c_partstates"];
[partstates release];
return row;
}
- (CardGroup *) firstElementFromCalendar: (iCalCalendar *) ical
{
NSArray *elements;
@ -349,13 +55,7 @@ static NSNumber *distantFutureNumber = nil;
elements = [ical allObjects];
count = [elements count];
if (count)
{
if (count > 1)
[self logWithFormat:
@"WARNING: given calendar contains more than one event: %@",
ical];
element = [elements objectAtIndex: 0];
}
element = [elements objectAtIndex: 0];
else
{
[self logWithFormat: @"ERROR: given calendar contains no elements: %@", ical];
@ -365,34 +65,32 @@ static NSNumber *distantFutureNumber = nil;
return element;
}
- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content {
NSDictionary *fields;
- (NSMutableDictionary *) extractQuickFieldsFromContent: (NSString *) _content
{
NSMutableDictionary *fields;
id cal;
if ([_content length] == 0)
return nil;
cal = [iCalCalendar parseSingleFromSource: _content];
fields = nil;
if (cal)
if ([_content length])
{
if ([cal isKindOfClass:[iCalCalendar class]])
cal = [self firstElementFromCalendar: cal];
cal = [iCalCalendar parseSingleFromSource: _content];
if (cal)
{
if ([cal isKindOfClass: [iCalCalendar class]])
cal = [self firstElementFromCalendar: cal];
if ([cal isKindOfClass:[iCalEvent class]])
fields = [[self extractQuickFieldsFromEvent:cal] retain];
else if ([cal isKindOfClass:[iCalToDo class]])
fields = [[self extractQuickFieldsFromTodo:cal] retain];
else if ([cal isNotNull]) {
[self logWithFormat: @"ERROR: unexpected iCalendar parse result: %@",
cal];
}
if ([cal isKindOfClass: [iCalRepeatableEntityObject class]])
fields = [cal quickRecord];
else if ([cal isNotNull])
[self logWithFormat: @"ERROR: unexpected iCalendar parse result: %@",
cal];
}
else
[self logWithFormat: @"ERROR: parsing source didn't return anything"];
}
else
[self logWithFormat: @"ERROR: parsing source didn't return anything"];
return [fields autorelease];
return fields;
}
@end /* OCSiCalFieldExtractor */

View File

@ -1,33 +0,0 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#ifndef __OGoContentStore_iCalEntityObject_OCS_H_
#define __OGoContentStore_iCalEntityObject_OCS_H_
#include <NGCards/NGCards.h>
@interface iCalEntityObject (OCS)
- (int)priorityNumber;
@end
#endif /* __OGoContentStore_iCalEntityObject_OCS_H_ */

View File

@ -1,42 +0,0 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import "iCalEntityObject+OCS.h"
@implementation iCalEntityObject (OCS)
- (int)priorityNumber {
NSString *prio;
prio = [self priority];
if(prio) {
NSRange r;
r = [prio rangeOfString:@";"];
if(r.length > 0) {
prio = [prio substringToIndex:r.location];
}
return [prio intValue];
}
return 0;
}
@end

View File

@ -26,59 +26,4 @@
@implementation iCalRepeatableEntityObject (OCS)
- (NSArray *) _indexedRules: (NSArray *) rules
{
NSMutableArray *ma;
unsigned int i, count;
NSString *valuesString;
ma = nil;
count = [rules count];
if (count > 0)
{
ma = [NSMutableArray arrayWithCapacity:count];
for (i = 0; i < count; i++)
{
iCalRecurrenceRule *rule;
rule = [rules objectAtIndex:i];
#warning we could return an NSArray instead and feed it as such to the iCalRecurrenceRule in SOGoAppointmentFolder...
valuesString = [[rule values] componentsJoinedByString: @";"];
[ma addObject: valuesString];
}
}
return ma;
}
- (NSString *) cycleInfo
{
NSArray *rules;
NSString *value;
NSMutableDictionary *cycleInfo;
if ([self isRecurrent])
{
cycleInfo = [NSMutableDictionary dictionaryWithCapacity: 3];
/* rules */
rules = [self _indexedRules: [self recurrenceRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"rules"];
rules = [self _indexedRules: [self exceptionRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"exRules"];
rules = [self _indexedRules: [self exceptionDates]];
if (rules)
[cycleInfo setObject: rules forKey: @"exDates"];
value = [cycleInfo description];
}
else
value = nil;
return value;
}
@end

View File

@ -1,3 +1,9 @@
2008-07-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalEntityObject.m ([iCalEntityObject -setRecurrenceId:newRecId])
([iCalEntityObject -recurrenceId]): new accessors to handle the
"RECURRENCE-ID" fields in recurrence exceptions.
2008-07-07 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* CardElement.m ([CardElement -addValue:aValue])

View File

@ -190,8 +190,8 @@
- (NSArray *) allObjects
{
NSMutableArray *ma;
ma = [NSMutableArray new];
ma = [NSMutableArray array];
[ma addObjectsFromArray: [self events]];
[ma addObjectsFromArray: [self todos]];
[ma addObjectsFromArray: [self journals]];

View File

@ -79,6 +79,9 @@ typedef enum
- (NSCalendarDate *) startDate;
- (BOOL) hasStartDate;
- (void) setRecurrenceId: (NSCalendarDate *) newRecId;
- (NSCalendarDate *) recurrenceId;
- (void) setLastModified: (NSCalendarDate *) _value;
- (NSCalendarDate *) lastModified;
@ -110,7 +113,7 @@ typedef enum
- (NSArray *) resources;
- (BOOL) isParticipant: (id) _email;
- (iCalPerson *) findParticipantWithEmail: (id) _email;
- (void) removeAllAlarms;
- (void) addToAlarms: (id) _alarm;
- (NSArray *) alarms;

View File

@ -60,6 +60,7 @@
tagClass = [CardElement class];
else if ([classTag isEqualToString: @"DTSTAMP"]
|| [classTag isEqualToString: @"DTSTART"]
|| [classTag isEqualToString: @"RECURRENCE-ID"]
|| [classTag isEqualToString: @"CREATED"]
|| [classTag isEqualToString: @"LAST-MODIFIED"])
tagClass = [iCalDateTime class];
@ -257,6 +258,18 @@
dateTime];
}
- (void) setRecurrenceId: (NSCalendarDate *) newRecId
{
[(iCalDateTime *) [self uniqueChildWithTag: @"recurrence-id"]
setDateTime: newRecId];
}
- (NSCalendarDate *) recurrenceId
{
return [(iCalDateTime *) [self uniqueChildWithTag: @"recurrence-id"]
dateTime];
}
- (BOOL) hasStartDate
{
return ([[self childrenWithTag: @"dtstart"] count] > 0);

View File

@ -11,9 +11,11 @@ Appointments_OBJC_FILES = \
Product.m \
NSArray+Appointments.m \
iCalEntityObject+SOGo.m \
iCalRepeatableEntityObject+SOGo.m \
iCalEvent+SOGo.m \
iCalEventChanges+SOGo.m \
iCalPerson+SOGo.m \
iCalToDo+SOGo.m \
\
SOGoCalendarComponent.m \
SOGoAppointmentObject.m \

View File

@ -59,12 +59,14 @@
#import <SOGo/SOGoCache.h>
// #import <SOGo/SOGoCustomGroupFolder.h>
#import <SOGo/LDAPUserManager.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoWebDAVAclManager.h>
#import <SoObjects/SOGo/iCalEntityObject+Utilities.h>
#import <SOGo/iCalEntityObject+Utilities.h>
#import "iCalEntityObject+SOGo.h"
#import "SOGoAppointmentObject.h"
#import "SOGoAppointmentFolders.h"
#import "SOGoFreeBusyObject.h"
@ -98,7 +100,10 @@ static Class sogoAppointmentFolderKlass = Nil;
didInit = YES;
if (!sogoAppointmentFolderKlass)
sogoAppointmentFolderKlass = self;
{
sogoAppointmentFolderKlass = self;
[iCalEntityObject initializeSOGoExtensions];
}
if (!reportQueryFields)
reportQueryFields = [[NSArray alloc] initWithObjects: @"c_name",
@ -554,7 +559,6 @@ static Class sogoAppointmentFolderKlass = Nil;
}
- (NSMutableDictionary *) fixupRecord: (NSDictionary *) _record
fetchRange: (NGCalendarDateRange *) _r
{
NSMutableDictionary *md;
static NSString *fields[] = { @"c_startdate", @"startDate",
@ -598,8 +602,7 @@ static Class sogoAppointmentFolderKlass = Nil;
ma = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
row = [self fixupRecord: [records objectAtIndex: count]
fetchRange: r];
row = [self fixupRecord: [records objectAtIndex: count]];
if (row)
[ma addObject: row];
}
@ -632,6 +635,117 @@ static Class sogoAppointmentFolderKlass = Nil;
return md;
}
- (int) _indexOfRecordMatchingDate: (NSCalendarDate *) matchDate
inArray: (NSArray *) recordArray
{
int count, max, recordIndex;
NSDictionary *currentRecord;
recordIndex = -1;
count = 0;
max = [recordArray count];
while (recordIndex == -1 && count < max)
{
currentRecord = [recordArray objectAtIndex: count];
if ([[currentRecord objectForKey: @"startDate"]
isEqual: matchDate])
recordIndex = count;
}
return recordIndex;
}
- (void) _fixExceptionRecord: (NSMutableDictionary *) recRecord
fromRow: (NSDictionary *) row
{
NSArray *objects;
static NSArray *fields = nil;
if (!fields)
{
fields = [NSArray arrayWithObjects: @"c_name", nil];
[fields retain];
}
objects = [row objectsForKeys: fields notFoundMarker: @""];
[recRecord setObjects: objects forKeys: fields];
}
- (void) _appendCycleException: (iCalRepeatableEntityObject *) component
fromRow: (NSDictionary *) row
forRange: (NGCalendarDateRange *) dateRange
toArray: (NSMutableArray *) ma
{
NSCalendarDate *startDate, *recurrenceId;
NSMutableDictionary *newRecord;
NGCalendarDateRange *newRecordRange;
int recordIndex;
newRecord = nil;
recurrenceId = [component recurrenceId];
if ([dateRange containsDate: recurrenceId])
{
recordIndex = [self _indexOfRecordMatchingDate: recurrenceId
inArray: ma];
if (recordIndex > -1)
{
startDate = [component startDate];
if ([dateRange containsDate: startDate])
{
newRecord = [self fixupRecord: [component quickRecord]];
[ma replaceObjectAtIndex: recordIndex withObject: newRecord];
}
else
[ma removeObjectAtIndex: recordIndex];
}
else
[self errorWithFormat:
@"missing exception record for recurrence-id: %@",
recurrenceId];
}
else
{
newRecord = [self fixupRecord: [component quickRecord]];
newRecordRange = [NGCalendarDateRange calendarDateRangeWithStartDate:
[newRecord objectForKey:
@"startDate"]
endDate: [newRecord objectForKey:
@"endDate"]];
if ([dateRange doesIntersectWithDateRange: newRecordRange])
[ma addObject: newRecord];
}
if (newRecord)
[self _fixExceptionRecord: newRecord fromRow: row];
}
- (void) _appendCycleExceptionsFromRow: (NSDictionary *) row
forRange: (NGCalendarDateRange *) dateRange
toArray: (NSMutableArray *) ma
{
NSArray *elements, *components;
unsigned int count, max;
NSString *content;
content = [row objectForKey: @"c_content"];
if ([content length])
{
elements = [iCalCalendar parseFromSource: content];
if ([elements count])
{
components = [[elements objectAtIndex: 0] allObjects];
max = [components count];
for (count = 1; count < max; count++)
[self _appendCycleException: [components objectAtIndex: count]
fromRow: row
forRange: dateRange
toArray: ma];
}
}
}
- (void) _flattenCycleRecord: (NSDictionary *) _row
forRange: (NGCalendarDateRange *) _r
intoArray: (NSMutableArray *) _ma
@ -660,7 +774,7 @@ static Class sogoAppointmentFolderKlass = Nil;
return;
}
row = [self fixupRecord:_row fetchRange: _r];
row = [self fixupRecord: _row];
[row removeObjectForKey: @"c_cycleinfo"];
[row setObject: sharedYes forKey: @"isRecurrentEvent"];
@ -685,6 +799,10 @@ static Class sogoAppointmentFolderKlass = Nil;
if (fixedRow)
[_ma addObject:fixedRow];
}
[self _appendCycleExceptionsFromRow: row
forRange: _r
toArray: _ma];
}
- (NSArray *) fixupCyclicRecords: (NSArray *) _records
@ -693,16 +811,16 @@ static Class sogoAppointmentFolderKlass = Nil;
// TODO: is the result supposed to be sorted by date?
NSMutableArray *ma;
NSDictionary *row;
unsigned int i, count;
unsigned int count, max;
count = [_records count];
ma = [NSMutableArray arrayWithCapacity: count];
if (count > 0)
for (i = 0; i < count; i++)
{
row = [_records objectAtIndex: i];
[self _flattenCycleRecord: row forRange: _r intoArray: ma];
}
max = [_records count];
ma = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
row = [_records objectAtIndex: count];
[self _flattenCycleRecord: row forRange: _r intoArray: ma];
}
return ma;
}
@ -844,9 +962,10 @@ static Class sogoAppointmentFolderKlass = Nil;
{
if (r)
records = [self fixupCyclicRecords: records fetchRange: r];
if (!ma)
ma = [NSMutableArray arrayWithCapacity: [records count]];
[ma addObjectsFromArray: records];
if (ma)
[ma addObjectsFromArray: records];
else
ma = [NSMutableArray arrayWithArray: records];
}
else if (!ma)
{
@ -891,6 +1010,8 @@ static Class sogoAppointmentFolderKlass = Nil;
#warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:)
#warning components in calendar-data query are ignored
#warning the two following methods should be replaced with the new dav rendering mechanism
- (NSString *) _nodeTagForProperty: (NSString *) property
{
NSString *namespace, *nodeName, *nsRep;

View File

@ -27,8 +27,13 @@
@class SOGoUser;
NSCalendarDate *iCalDistantFuture;
NSNumber *iCalDistantFutureNumber;
@interface iCalEntityObject (SOGoExtensions)
+ (void) initializeSOGoExtensions;
- (BOOL) userIsParticipant: (SOGoUser *) user;
- (BOOL) userIsOrganizer: (SOGoUser *) user;
@ -38,6 +43,10 @@
- (id) itipEntryWithMethod: (NSString *) method;
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user;
- (NSMutableDictionary *) quickRecord;
- (int) priorityNumber;
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date;
@end

View File

@ -23,6 +23,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalPerson.h>
@ -36,6 +37,16 @@
@implementation iCalEntityObject (SOGoExtensions)
+ (void) initializeSOGoExtensions;
{
if (!iCalDistantFuture)
{
iCalDistantFuture = [[NSCalendarDate distantFuture] retain];
/* INT_MAX due to Postgres constraint */
iCalDistantFutureNumber = [[NSNumber numberWithUnsignedInt: INT_MAX] retain];
}
}
- (BOOL) userIsParticipant: (SOGoUser *) user
{
NSEnumerator *participants;
@ -128,4 +139,38 @@
return newAttendees;
}
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date
{
return ((_date == iCalDistantFuture)
? iCalDistantFutureNumber
: [NSNumber numberWithUnsignedInt: [_date timeIntervalSince1970]]);
}
- (NSMutableDictionary *) quickRecord
{
[self subclassResponsibility: _cmd];
return nil;
}
- (int) priorityNumber
{
NSString *prio;
NSRange r;
int priorityNumber;
prio = [self priority];
if (prio)
{
r = [prio rangeOfString: @";"];
if (r.length)
prio = [prio substringToIndex:r.location];
priorityNumber = [prio intValue];
}
else
priorityNumber = 0;
return priorityNumber;
}
@end

View File

@ -25,9 +25,12 @@
#import <NGCards/iCalEvent.h>
@class NSMutableDictionary;
@interface iCalEvent (SOGoExtensions)
- (BOOL) isStillRelevant;
- (NSMutableDictionary *) quickRecord;
@end

View File

@ -20,7 +20,18 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/iCalPerson.h>
#import "iCalRepeatableEntityObject+SOGo.h"
#import "iCalEvent+SOGo.h"
@ -35,4 +46,150 @@
return ([[self endDate] earlierDate: now] == now);
}
- (NSMutableDictionary *) quickRecord
{
NSMutableDictionary *row;
NSCalendarDate *startDate, *endDate;
NSArray *attendees;
NSString *uid, *title, *location, *status;
NSNumber *sequence, *dateNumber;
id organizer;
id participants, partmails;
NSMutableString *partstates;
unsigned int i, count, boolTmp;
BOOL isAllDay;
iCalAccessClass accessClass;
/* extract values */
startDate = [self startDate];
endDate = [self endDate];
uid = [self uid];
title = [self summary];
if (![title isNotNull])
title = @"";
location = [self location];
sequence = [self sequence];
accessClass = [self symbolicAccessClass];
isAllDay = [self isAllDay];
status = [[self status] uppercaseString];
attendees = [self attendees];
partmails = [attendees valueForKey: @"rfc822Email"];
partmails = [partmails componentsJoinedByString: @"\n"];
participants = [attendees valueForKey: @"cn"];
participants = [participants componentsJoinedByString: @"\n"];
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vevent" forKey: @"c_component"];
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
boolTmp = ((isAllDay) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_isallday"];
boolTmp = (([self isRecurrent]) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_iscycle"];
boolTmp = (([self isOpaque]) ? 1 : 0);
[row setObject: [NSNumber numberWithInt: boolTmp]
forKey: @"c_isopaque"];
[row setObject: [NSNumber numberWithInt: [self priorityNumber]]
forKey: @"c_priority"];
[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])
[row setObject: [self quickRecordDateAsNumber: startDate]
forKey: @"c_startdate"];
if ([endDate isNotNull])
{
if (endDate == iCalDistantFuture)
dateNumber = iCalDistantFutureNumber;
else
{
if (isAllDay)
i = 1;
else
i = 0;
dateNumber
= [NSNumber numberWithUnsignedInt:
[endDate timeIntervalSince1970] - i];
}
[row setObject: dateNumber forKey: @"c_enddate"];
}
if ([self isRecurrent]) {
NSCalendarDate *date;
date = [self lastPossibleRecurrenceStartDate];
if (!date) {
/* this could also be *nil*, but in the end it makes the fetchspecs
more complex - thus we set it to a "reasonable" distant future */
date = iCalDistantFuture;
}
[row setObject:[self quickRecordDateAsNumber:date] forKey: @"c_cycleenddate"];
[row setObject:[self cycleInfo] forKey: @"c_cycleinfo"];
}
if ([participants length] > 0)
[row setObject: participants forKey: @"c_participants"];
if ([partmails length] > 0)
[row setObject: partmails forKey: @"c_partmails"];
if ([status isNotNull]) {
int code = 1;
if ([status isEqualToString: @"TENTATIVE"])
code = 2;
else if ([status isEqualToString: @"CANCELLED"])
code = 0;
[row setObject:[NSNumber numberWithInt:code] forKey: @"c_status"];
}
else {
/* confirmed by default */
[row setObject: [NSNumber numberWithInt:1] forKey: @"c_status"];
}
[row setObject: [NSNumber numberWithUnsignedInt: accessClass]
forKey: @"c_classification"];
organizer = [self organizer];
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];
for (i = 0; i < count; i++)
{
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if (i)
[partstates appendString: @"\n"];
[partstates appendFormat: @"%d", stat];
}
[row setObject:partstates forKey: @"c_partstates"];
[partstates release];
return row;
}
@end

View File

@ -0,0 +1,33 @@
/* iCalRepeatableEntityObject+SOGo.h - this file is part of SOGo
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2008 Inverse groupe conseil
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <NGCards/iCalRepeatableEntityObject.h>
#import "iCalEntityObject+SOGo.h"
@class NSString;
@interface iCalRepeatableEntityObject (SOGoExtensions)
- (NSString *) cycleInfo;
@end

View File

@ -0,0 +1,89 @@
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2008 Inverse groupe conseil
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <NGCards/iCalRecurrenceRule.h>
#import "iCalRepeatableEntityObject+SOGo.h"
@implementation iCalRepeatableEntityObject (SOGoExtensions)
- (NSArray *) _indexedRules: (NSArray *) rules
{
NSMutableArray *ma;
unsigned int i, count;
NSString *valuesString;
ma = nil;
count = [rules count];
if (count > 0)
{
ma = [NSMutableArray arrayWithCapacity:count];
for (i = 0; i < count; i++)
{
iCalRecurrenceRule *rule;
rule = [rules objectAtIndex:i];
#warning we could return an NSArray instead and feed it as such to the iCalRecurrenceRule in SOGoAppointmentFolder...
valuesString = [[rule values] componentsJoinedByString: @";"];
[ma addObject: valuesString];
}
}
return ma;
}
- (NSString *) cycleInfo
{
NSArray *rules;
NSString *value;
NSMutableDictionary *cycleInfo;
if ([self isRecurrent])
{
cycleInfo = [NSMutableDictionary dictionaryWithCapacity: 3];
/* rules */
rules = [self _indexedRules: [self recurrenceRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"rules"];
rules = [self _indexedRules: [self exceptionRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"exRules"];
rules = [self _indexedRules: [self exceptionDates]];
if (rules)
[cycleInfo setObject: rules forKey: @"exDates"];
value = [cycleInfo description];
}
else
value = nil;
return value;
}
@end

View File

@ -0,0 +1,36 @@
/* iCalToDo+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef ICALTODO_SOGO_H
#define ICALTODO_SOGO_H
#import <NGCards/iCalToDo.h>
@class NSMutableArray;
@interface iCalToDo (SOGoExtensions)
- (NSMutableDictionary *) quickRecord;
@end
#endif /* ICALTODO_SOGO_H */

View File

@ -0,0 +1,168 @@
/* iCalEvent+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
* Copyright (C) 2004-2005 SKYRIX Software AG
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalPerson.h>
#import "iCalRepeatableEntityObject+SOGo.h"
#import "iCalToDo+SOGo.h"
@implementation iCalToDo (SOGoExtensions)
- (NSMutableDictionary *) quickRecord
{
NSMutableDictionary *row;
NSCalendarDate *startDate, *dueDate;
NSArray *attendees;
NSString *uid, *title, *location, *status;
NSNumber *sequence;
id organizer, date;
id participants, partmails;
NSMutableString *partstates;
unsigned i, count, code;
iCalAccessClass accessClass;
/* extract values */
startDate = [self startDate];
dueDate = [self due];
uid = [self uid];
title = [self summary];
if (![title isNotNull])
title = @"";
location = [self location];
sequence = [self sequence];
accessClass = [self symbolicAccessClass];
status = [[self status] uppercaseString];
attendees = [self attendees];
partmails = [attendees valueForKey: @"rfc822Email"];
partmails = [partmails componentsJoinedByString: @"\n"];
participants = [attendees valueForKey: @"cn"];
participants = [participants componentsJoinedByString: @"\n"];
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vtodo" forKey: @"c_component"];
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
[row setObject:[NSNumber numberWithBool:[self isRecurrent]]
forKey: @"c_iscycle"];
[row setObject:[NSNumber numberWithInt:[self priorityNumber]]
forKey: @"c_priority"];
[row setObject: [NSNumber numberWithBool: NO]
forKey: @"c_isallday"];
[row setObject: [NSNumber numberWithBool: NO]
forKey: @"c_isopaque"];
[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];
else
date = [NSNull null];
[row setObject: date forKey: @"c_startdate"];
if ([dueDate isNotNull])
date = [self quickRecordDateAsNumber: dueDate];
else
date = [NSNull null];
[row setObject: date forKey: @"c_enddate"];
if ([self isRecurrent])
{
[row setObject: [self quickRecordDateAsNumber: iCalDistantFuture]
forKey: @"c_cycleenddate"];
[row setObject: [self cycleInfo]
forKey: @"c_cycleinfo"];
}
if ([participants length] > 0)
[row setObject:participants forKey: @"c_participants"];
if ([partmails length] > 0)
[row setObject:partmails forKey: @"c_partmails"];
if ([status isNotNull]) {
code = 0; /* NEEDS-ACTION */
if ([status isEqualToString: @"COMPLETED"])
code = 1;
else if ([status isEqualToString: @"IN-PROCESS"])
code = 2;
else if ([status isEqualToString: @"CANCELLED"])
code = 3;
[row setObject: [NSNumber numberWithInt: code] forKey: @"c_status"];
}
else {
/* confirmed by default */
[row setObject:[NSNumber numberWithInt:1] forKey: @"c_status"];
}
[row setObject: [NSNumber numberWithUnsignedInt: accessClass]
forKey: @"c_classification"];
organizer = [self organizer];
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];
for ( i = 0; i < count; i++) {
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if(i != 0)
[partstates appendString: @"\n"];
[partstates appendFormat: @"%d", stat];
}
[row setObject:partstates forKey: @"c_partstates"];
[partstates release];
return row;
}
@end

View File

@ -25,6 +25,7 @@
#import <Foundation/NSDictionary.h>
@class NSArray;
@class NSString;
@interface NSDictionary (SOGoDictionaryUtilities)
@ -36,4 +37,11 @@
@end
@interface NSMutableDictionary (SOGoDictionaryUtilities)
- (void) setObjects: (NSArray *) objects
forKeys: (NSArray *) keys;
@end
#endif /* NSDICTIONARY_UTILITIES_H */

View File

@ -23,6 +23,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSException.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
@ -103,3 +104,23 @@
}
@end
@implementation NSMutableDictionary (SOGoDictionaryUtilities)
- (void) setObjects: (NSArray *) objects
forKeys: (NSArray *) keys
{
unsigned int count, max;
max = [objects count];
if ([keys count] == max)
for (count = 0; count < max; count++)
[self setObject: [objects objectAtIndex: count]
forKey: [keys objectAtIndex: count]];
else
[NSException raise: NSInvalidArgumentException
format: @"Number of objects does not match"
@" the number of keys."];
}
@end