]> err.no Git - scalable-opengroupware.org/blob - OGoContentStore/OCSiCalFieldExtractor.m
09cbe20efc10ac0c3b60f932b20677bcd7e1de84
[scalable-opengroupware.org] / OGoContentStore / OCSiCalFieldExtractor.m
1 /*
2   Copyright (C) 2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "OCSiCalFieldExtractor.h"
23 #include "common.h"
24 #include <NGCards/NGCards.h>
25 #include "iCalEntityObject+OCS.h"
26 #include "iCalRepeatableEntityObject+OCS.h"
27
28 @implementation OCSiCalFieldExtractor
29
30 static OCSiCalFieldExtractor     *extractor           = nil;
31 static NSCalendarDate            *distantFuture       = nil;
32 static NSNumber                  *distantFutureNumber = nil;
33
34 + (void) initialize
35 {
36   static BOOL didInit = NO;
37   
38   if (didInit) return;
39   didInit = YES;
40
41   distantFuture       = [[NSCalendarDate distantFuture] retain];
42   /* INT_MAX due to Postgres constraint */
43   distantFutureNumber = [[NSNumber numberWithUnsignedInt:INT_MAX] retain];
44 }
45
46 + (id) sharedICalFieldExtractor
47 {
48   if (extractor == nil)
49     extractor = [self new];
50
51   return extractor;
52 }
53
54 /* operations */
55
56 - (NSNumber *) numberForDate: (NSCalendarDate *) _date
57 {
58   if (_date == distantFuture)
59     return distantFutureNumber;
60
61   return [NSNumber numberWithUnsignedInt:[_date timeIntervalSince1970]];
62 }
63
64 - (NSMutableDictionary *) extractQuickFieldsFromEvent: (iCalEvent *) _event
65 {
66   NSMutableDictionary *row;
67   NSCalendarDate      *startDate, *endDate;
68   NSArray             *attendees;
69   NSString            *uid, *title, *location, *status, *accessClass;
70   NSNumber            *sequence;
71   id                  organizer;
72   id                  participants, partmails;
73   NSMutableString     *partstates;
74   unsigned            i, count;
75
76   if (_event == nil)
77     return nil;
78
79   /* extract values */
80   
81   startDate    = [_event startDate];
82   endDate      = [_event endDate];
83   uid          = [_event uid];
84   title        = [_event summary];
85   location     = [_event location];
86   sequence     = [_event sequence];
87   accessClass  = [[_event accessClass] uppercaseString];
88   status       = [[_event status] uppercaseString];
89
90   attendees    = [_event attendees];
91   partmails    = [attendees valueForKey:@"rfc822Email"];
92   partmails    = [partmails componentsJoinedByString:@"\n"];
93   participants = [attendees valueForKey:@"cn"];
94   participants = [participants componentsJoinedByString:@"\n"];
95
96   /* build row */
97
98   row = [NSMutableDictionary dictionaryWithCapacity:8];
99
100   [row setObject: @"vevent" forKey: @"component"];
101   
102   if ([uid isNotNull]) 
103     [row setObject:uid forKey:@"uid"];
104   else
105     [self logWithFormat:@"WARNING: could not extract a uid from event!"];
106
107   [row setObject:[NSNumber numberWithBool: [_event isAllDay]]
108        forKey: @"isallday"];
109   [row setObject:[NSNumber numberWithBool: [_event isRecurrent]]
110        forKey: @"iscycle"];
111   [row setObject:[NSNumber numberWithBool: [_event isOpaque]]
112        forKey: @"isopaque"];
113   [row setObject:[NSNumber numberWithInt: [_event priorityNumber]]
114        forKey: @"priority"];
115
116   if ([title isNotNull]) [row setObject: title forKey:@"title"];
117   if ([location isNotNull]) [row setObject: location forKey:@"location"];
118   if ([sequence isNotNull]) [row setObject: sequence forKey:@"sequence"];
119
120   if ([startDate isNotNull]) 
121     [row setObject: [self numberForDate: startDate] forKey:@"startdate"];
122   if ([endDate isNotNull]) 
123     [row setObject: [self numberForDate: endDate] forKey:@"enddate"];
124   if ([_event isRecurrent]) {
125     NSCalendarDate *date;
126     
127     date = [_event lastPossibleRecurrenceStartDate];
128     if (!date) {
129       /* this could also be *nil*, but in the end it makes the fetchspecs
130          more complex - thus we set it to a "reasonable" distant future */
131       date = distantFuture;
132     }
133     [row setObject:[self numberForDate:date] forKey:@"cycleenddate"];
134     [row setObject:[_event cycleInfo] forKey:@"cycleinfo"];
135   }
136
137   if ([participants length] > 0)
138     [row setObject: participants forKey:@"participants"];
139   if ([partmails length] > 0)
140     [row setObject: partmails forKey:@"partmails"];
141
142   if ([status isNotNull]) {
143     int code = 1;
144     
145     if ([status isEqualToString:@"TENTATIVE"])
146       code = 2;
147     else if ([status isEqualToString:@"CANCELLED"])
148       code = 0;
149     [row setObject:[NSNumber numberWithInt:code] forKey:@"status"];
150   }
151   else {
152     /* confirmed by default */
153     [row setObject: [NSNumber numberWithInt:1] forKey: @"status"];
154   }
155
156   if([accessClass isNotNull] && ![accessClass isEqualToString:@"PUBLIC"]) {
157     [row setObject:[NSNumber numberWithBool:NO] forKey:@"ispublic"];
158   }
159   else {
160     [row setObject:[NSNumber numberWithBool:YES] forKey:@"ispublic"];
161   }
162
163   organizer = [_event organizer];
164   if (organizer) {
165     NSString *email;
166     
167     email = [organizer valueForKey:@"rfc822Email"];
168     if (email)
169       [row setObject:email forKey:@"orgmail"];
170   }
171   
172   /* construct partstates */
173   count        = [attendees count];
174   partstates   = [[NSMutableString alloc] initWithCapacity:count * 2];
175   for ( i = 0; i < count; i++) {
176     iCalPerson         *p;
177     iCalPersonPartStat stat;
178     
179     p    = [attendees objectAtIndex:i];
180     stat = [p participationStatus];
181     if(i != 0)
182       [partstates appendString:@"\n"];
183     [partstates appendFormat:@"%d", stat];
184   }
185   [row setObject:partstates forKey:@"partstates"];
186   [partstates release];
187   return row;
188 }
189
190 - (NSMutableDictionary *) extractQuickFieldsFromTodo: (iCalToDo *) _task
191 {
192   NSMutableDictionary *row;
193   NSCalendarDate      *startDate, *dueDate;
194   NSArray             *attendees;
195   NSString            *uid, *title, *location, *status, *accessClass;
196   NSNumber            *sequence;
197   id                  organizer, date;
198   id                  participants, partmails;
199   NSMutableString     *partstates;
200   unsigned            i, count, code;
201
202   if (_task == nil)
203     return nil;
204
205   /* extract values */
206   
207   startDate    = [_task startDate];
208   dueDate      = [_task due];
209   uid          = [_task uid];
210   title        = [_task summary];
211   location     = [_task location];
212   sequence     = [_task sequence];
213   accessClass  = [[_task accessClass] uppercaseString];
214   status       = [[_task status] uppercaseString];
215
216   attendees    = [_task attendees];
217   partmails    = [attendees valueForKey:@"rfc822Email"];
218   partmails    = [partmails componentsJoinedByString:@"\n"];
219   participants = [attendees valueForKey:@"cn"];
220   participants = [participants componentsJoinedByString:@"\n"];
221
222   /* build row */
223
224   row = [NSMutableDictionary dictionaryWithCapacity:8];
225
226   [row setObject: @"vtodo" forKey: @"component"];
227
228   if ([uid isNotNull]) 
229     [row setObject:uid forKey:@"uid"];
230   else
231     [self logWithFormat:@"WARNING: could not extract a uid from event!"];
232
233   [row setObject:[NSNumber numberWithBool:[_task isRecurrent]]
234        forKey:@"iscycle"];
235   [row setObject:[NSNumber numberWithInt:[_task priorityNumber]]
236        forKey:@"priority"];
237
238   if ([title isNotNull]) [row setObject: title forKey:@"title"];
239   if ([location isNotNull]) [row setObject: location forKey:@"location"];
240   if ([sequence isNotNull]) [row setObject: sequence forKey:@"sequence"];
241   
242   if ([startDate isNotNull])
243     date = [self numberForDate: startDate];
244   else
245     date = [NSNull null];
246   [row setObject: date forKey: @"startdate"];
247
248   if ([dueDate isNotNull]) 
249     date = [self numberForDate: dueDate];
250   else
251     date = [NSNull null];
252   [row setObject: date forKey: @"enddate"];
253
254   if ([participants length] > 0)
255     [row setObject:participants forKey:@"participants"];
256   if ([partmails length] > 0)
257     [row setObject:partmails forKey:@"partmails"];
258
259   if ([status isNotNull]) {
260     code = 0; /* NEEDS-ACTION */
261     if ([status isEqualToString:@"COMPLETED"])
262       code = 1;
263     else if ([status isEqualToString:@"IN-PROCESS"])
264       code = 2;
265     else if ([status isEqualToString:@"CANCELLED"])
266       code = 3;
267     [row setObject: [NSNumber numberWithInt: code] forKey:@"status"];
268   }
269   else {
270     /* confirmed by default */
271     [row setObject:[NSNumber numberWithInt:1] forKey:@"status"];
272   }
273
274   if([accessClass isNotNull] && ![accessClass isEqualToString:@"PUBLIC"]) {
275     [row setObject:[NSNumber numberWithBool:NO] forKey:@"ispublic"];
276   }
277   else {
278     [row setObject:[NSNumber numberWithBool:YES] forKey:@"ispublic"];
279   }
280
281   organizer = [_task organizer];
282   if (organizer) {
283     NSString *email;
284     
285     email = [organizer valueForKey:@"rfc822Email"];
286     if (email)
287       [row setObject:email forKey:@"orgmail"];
288   }
289   
290   /* construct partstates */
291   count        = [attendees count];
292   partstates   = [[NSMutableString alloc] initWithCapacity:count * 2];
293   for ( i = 0; i < count; i++) {
294     iCalPerson         *p;
295     iCalPersonPartStat stat;
296     
297     p    = [attendees objectAtIndex:i];
298     stat = [p participationStatus];
299     if(i != 0)
300       [partstates appendString:@"\n"];
301     [partstates appendFormat:@"%d", stat];
302   }
303   [row setObject:partstates forKey:@"partstates"];
304   [partstates release];
305   return row;
306 }
307
308 - (CardGroup *) firstElementFromCalendar: (iCalCalendar *) ical
309 {
310   NSArray *elements;
311   CardGroup *element;
312   unsigned int count;
313   
314   elements = [ical allObjects];
315   count = [elements count];
316   if (count)
317     {
318       if (count > 1)
319         [self logWithFormat:
320                 @"WARNING: given calendar contains more than one event: %@",
321               ical];
322       element = [elements objectAtIndex: 0];
323     }
324   else
325     {
326       [self logWithFormat:@"ERROR: given calendar contains no elements: %@", ical];
327       element = nil;
328     }
329
330   return element;
331 }
332
333 - (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content {
334   NSAutoreleasePool *pool;
335   NSDictionary *fields;
336   id cal;
337   
338   if ([_content length] == 0)
339     return nil;
340
341   pool = [[NSAutoreleasePool alloc] init];
342   cal = [iCalCalendar parseSingleFromSource: _content];
343   
344   fields = nil;
345   if (cal)
346     {
347       if ([cal isKindOfClass:[iCalCalendar class]])
348         cal = [self firstElementFromCalendar: cal];
349
350       if ([cal isKindOfClass:[iCalEvent class]])
351         fields = [[self extractQuickFieldsFromEvent:cal] retain];
352       else if ([cal isKindOfClass:[iCalToDo class]])
353         fields = [[self extractQuickFieldsFromTodo:cal] retain];
354       else if ([cal isNotNull]) {
355         [self logWithFormat:@"ERROR: unexpected iCalendar parse result: %@",
356               cal];
357       }
358     }
359   else
360     [self logWithFormat:@"ERROR: parsing source didn't return anything"];
361
362   [pool release];
363   
364   return [fields autorelease];
365 }
366
367 @end /* OCSiCalFieldExtractor */