]> err.no Git - scalable-opengroupware.org/blob - OGoContentStore/OCSiCalFieldExtractor.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1031 d1b88da0-ebda-0310...
[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;
70   NSNumber            *sequence;
71   id                  organizer;
72   id                  participants, partmails;
73   NSMutableString     *partstates;
74   unsigned            i, count;
75   iCalAccessClass accessClass;
76
77   if (_event == nil)
78     return nil;
79
80   /* extract values */
81   
82   startDate    = [_event startDate];
83   endDate      = [_event endDate];
84   uid          = [_event uid];
85   title        = [_event summary];
86   location     = [_event location];
87   sequence     = [_event sequence];
88   accessClass  = [_event symbolicAccessClass];
89   status       = [[_event status] uppercaseString];
90
91   attendees    = [_event attendees];
92   partmails    = [attendees valueForKey:@"rfc822Email"];
93   partmails    = [partmails componentsJoinedByString:@"\n"];
94   participants = [attendees valueForKey:@"cn"];
95   participants = [participants componentsJoinedByString:@"\n"];
96
97   /* build row */
98
99   row = [NSMutableDictionary dictionaryWithCapacity:8];
100
101   [row setObject: @"vevent" forKey: @"component"];
102   
103   if ([uid isNotNull]) 
104     [row setObject:uid forKey:@"uid"];
105   else
106     [self logWithFormat:@"WARNING: could not extract a uid from event!"];
107
108   [row setObject:[NSNumber numberWithBool: [_event isAllDay]]
109        forKey: @"isallday"];
110   [row setObject:[NSNumber numberWithBool: [_event isRecurrent]]
111        forKey: @"iscycle"];
112   [row setObject:[NSNumber numberWithBool: [_event isOpaque]]
113        forKey: @"isopaque"];
114   [row setObject:[NSNumber numberWithInt: [_event priorityNumber]]
115        forKey: @"priority"];
116
117   if ([title isNotNull]) [row setObject: title forKey:@"title"];
118   if ([location isNotNull]) [row setObject: location forKey:@"location"];
119   if ([sequence isNotNull]) [row setObject: sequence forKey:@"sequence"];
120
121   if ([startDate isNotNull])
122     [row setObject: [self numberForDate: startDate] forKey:@"startdate"];
123   if ([endDate isNotNull])
124     [row setObject: [self numberForDate: endDate] forKey:@"enddate"];
125   if ([_event isRecurrent]) {
126     NSCalendarDate *date;
127     
128     date = [_event lastPossibleRecurrenceStartDate];
129     if (!date) {
130       /* this could also be *nil*, but in the end it makes the fetchspecs
131          more complex - thus we set it to a "reasonable" distant future */
132       date = distantFuture;
133     }
134     [row setObject:[self numberForDate:date] forKey:@"cycleenddate"];
135     [row setObject:[_event cycleInfo] forKey:@"cycleinfo"];
136   }
137
138   if ([participants length] > 0)
139     [row setObject: participants forKey:@"participants"];
140   if ([partmails length] > 0)
141     [row setObject: partmails forKey:@"partmails"];
142
143   if ([status isNotNull]) {
144     int code = 1;
145     
146     if ([status isEqualToString:@"TENTATIVE"])
147       code = 2;
148     else if ([status isEqualToString:@"CANCELLED"])
149       code = 0;
150     [row setObject:[NSNumber numberWithInt:code] forKey:@"status"];
151   }
152   else {
153     /* confirmed by default */
154     [row setObject: [NSNumber numberWithInt:1] forKey: @"status"];
155   }
156
157   [row setObject: [NSNumber numberWithUnsignedInt: accessClass]
158        forKey: @"classification"];
159
160   organizer = [_event organizer];
161   if (organizer) {
162     NSString *email;
163     
164     email = [organizer valueForKey:@"rfc822Email"];
165     if (email)
166       [row setObject:email forKey:@"orgmail"];
167   }
168   
169   /* construct partstates */
170   count        = [attendees count];
171   partstates   = [[NSMutableString alloc] initWithCapacity:count * 2];
172   for ( i = 0; i < count; i++) {
173     iCalPerson         *p;
174     iCalPersonPartStat stat;
175     
176     p    = [attendees objectAtIndex:i];
177     stat = [p participationStatus];
178     if(i != 0)
179       [partstates appendString:@"\n"];
180     [partstates appendFormat:@"%d", stat];
181   }
182   [row setObject:partstates forKey:@"partstates"];
183   [partstates release];
184   return row;
185 }
186
187 - (NSMutableDictionary *) extractQuickFieldsFromTodo: (iCalToDo *) _task
188 {
189   NSMutableDictionary *row;
190   NSCalendarDate      *startDate, *dueDate;
191   NSArray             *attendees;
192   NSString            *uid, *title, *location, *status;
193   NSNumber            *sequence;
194   id                  organizer, date;
195   id                  participants, partmails;
196   NSMutableString     *partstates;
197   unsigned            i, count, code;
198   iCalAccessClass accessClass;
199
200   if (_task == nil)
201     return nil;
202
203   /* extract values */
204   
205   startDate    = [_task startDate];
206   dueDate      = [_task due];
207   uid          = [_task uid];
208   title        = [_task summary];
209   location     = [_task location];
210   sequence     = [_task sequence];
211   accessClass  = [_task symbolicAccessClass];
212   status       = [[_task status] uppercaseString];
213
214   attendees    = [_task attendees];
215   partmails    = [attendees valueForKey:@"rfc822Email"];
216   partmails    = [partmails componentsJoinedByString:@"\n"];
217   participants = [attendees valueForKey:@"cn"];
218   participants = [participants componentsJoinedByString:@"\n"];
219
220   /* build row */
221
222   row = [NSMutableDictionary dictionaryWithCapacity:8];
223
224   [row setObject: @"vtodo" forKey: @"component"];
225
226   if ([uid isNotNull]) 
227     [row setObject:uid forKey:@"uid"];
228   else
229     [self logWithFormat:@"WARNING: could not extract a uid from event!"];
230
231   [row setObject:[NSNumber numberWithBool:[_task isRecurrent]]
232        forKey:@"iscycle"];
233   [row setObject:[NSNumber numberWithInt:[_task priorityNumber]]
234        forKey:@"priority"];
235
236   if ([title isNotNull]) [row setObject: title forKey:@"title"];
237   if ([location isNotNull]) [row setObject: location forKey:@"location"];
238   if ([sequence isNotNull]) [row setObject: sequence forKey:@"sequence"];
239   
240   if ([startDate isNotNull])
241     date = [self numberForDate: startDate];
242   else
243     date = [NSNull null];
244   [row setObject: date forKey: @"startdate"];
245
246   if ([dueDate isNotNull]) 
247     date = [self numberForDate: dueDate];
248   else
249     date = [NSNull null];
250   [row setObject: date forKey: @"enddate"];
251
252   if ([participants length] > 0)
253     [row setObject:participants forKey:@"participants"];
254   if ([partmails length] > 0)
255     [row setObject:partmails forKey:@"partmails"];
256
257   if ([status isNotNull]) {
258     code = 0; /* NEEDS-ACTION */
259     if ([status isEqualToString:@"COMPLETED"])
260       code = 1;
261     else if ([status isEqualToString:@"IN-PROCESS"])
262       code = 2;
263     else if ([status isEqualToString:@"CANCELLED"])
264       code = 3;
265     [row setObject: [NSNumber numberWithInt: code] forKey:@"status"];
266   }
267   else {
268     /* confirmed by default */
269     [row setObject:[NSNumber numberWithInt:1] forKey:@"status"];
270   }
271
272   [row setObject: [NSNumber numberWithUnsignedInt: accessClass]
273        forKey: @"classification"];
274
275   organizer = [_task organizer];
276   if (organizer) {
277     NSString *email;
278     
279     email = [organizer valueForKey:@"rfc822Email"];
280     if (email)
281       [row setObject:email forKey:@"orgmail"];
282   }
283   
284   /* construct partstates */
285   count        = [attendees count];
286   partstates   = [[NSMutableString alloc] initWithCapacity:count * 2];
287   for ( i = 0; i < count; i++) {
288     iCalPerson         *p;
289     iCalPersonPartStat stat;
290     
291     p    = [attendees objectAtIndex:i];
292     stat = [p participationStatus];
293     if(i != 0)
294       [partstates appendString:@"\n"];
295     [partstates appendFormat:@"%d", stat];
296   }
297   [row setObject:partstates forKey:@"partstates"];
298   [partstates release];
299   return row;
300 }
301
302 - (CardGroup *) firstElementFromCalendar: (iCalCalendar *) ical
303 {
304   NSArray *elements;
305   CardGroup *element;
306   unsigned int count;
307   
308   elements = [ical allObjects];
309   count = [elements count];
310   if (count)
311     {
312       if (count > 1)
313         [self logWithFormat:
314                 @"WARNING: given calendar contains more than one event: %@",
315               ical];
316       element = [elements objectAtIndex: 0];
317     }
318   else
319     {
320       [self logWithFormat:@"ERROR: given calendar contains no elements: %@", ical];
321       element = nil;
322     }
323
324   return element;
325 }
326
327 - (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content {
328   NSAutoreleasePool *pool;
329   NSDictionary *fields;
330   id cal;
331   
332   if ([_content length] == 0)
333     return nil;
334
335   pool = [[NSAutoreleasePool alloc] init];
336   cal = [iCalCalendar parseSingleFromSource: _content];
337   
338   fields = nil;
339   if (cal)
340     {
341       if ([cal isKindOfClass:[iCalCalendar class]])
342         cal = [self firstElementFromCalendar: cal];
343
344       if ([cal isKindOfClass:[iCalEvent class]])
345         fields = [[self extractQuickFieldsFromEvent:cal] retain];
346       else if ([cal isKindOfClass:[iCalToDo class]])
347         fields = [[self extractQuickFieldsFromTodo:cal] retain];
348       else if ([cal isNotNull]) {
349         [self logWithFormat:@"ERROR: unexpected iCalendar parse result: %@",
350               cal];
351       }
352     }
353   else
354     [self logWithFormat:@"ERROR: parsing source didn't return anything"];
355
356   [pool release];
357   
358   return [fields autorelease];
359 }
360
361 @end /* OCSiCalFieldExtractor */