]> err.no Git - scalable-opengroupware.org/blob - SoObjects/Appointments/SOGoGroupAppointmentFolder.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1169 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / SoObjects / Appointments / SOGoGroupAppointmentFolder.m
1 /*
2   Copyright (C) 2004-2005 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 #import <Foundation/NSArray.h>
23 #import <Foundation/NSEnumerator.h>
24 #import <Foundation/NSException.h>
25
26 #import <NGObjWeb/SoSecurityManager.h>
27 #import <NGExtensions/NSNull+misc.h>
28 #import <NGExtensions/NSObject+Logs.h>
29 #import <NGCards/iCalEntityObject.h>
30
31 #import <SOGo/SOGoGroupFolder.h>
32 #import <SOGo/SOGoPermissions.h>
33
34 #import "SOGoGroupAppointmentFolder.h"
35
36 @implementation SOGoGroupAppointmentFolder
37
38 + (int)version {
39   return [super version] + 0 /* v1 */;
40 }
41 + (void)initialize {
42   NSAssert2([super version] == 1,
43             @"invalid superclass (%@) version %i !",
44             NSStringFromClass([self superclass]), [super version]);
45 }
46
47 - (void)dealloc {
48   [self->uidToFolder release];
49   [super dealloc];
50 }
51
52 /* looking up shared objects */
53
54 - (SOGoGroupsFolder *)lookupGroupsFolder {
55   return [[self container] lookupGroupsFolder];
56 }
57
58 /* selection */
59
60 - (NSArray *)calendarUIDs {
61   return [[self container] valueForKey:@"uids"];
62 }
63
64 /* folders */
65
66 - (void)resetFolderCaches {
67   [self->uidToFolder release]; self->uidToFolder = nil;
68 }
69
70 - (SOGoAppointmentFolder *)folderForUID:(NSString *)_uid {
71   if (self->uidToFolder == nil) {
72     // TODO: can we trigger a fetch?
73     [self errorWithFormat:
74             @"called -folderForUID method before fetchCoreInfos .."];
75     return nil;
76   }
77   
78   return [self->uidToFolder objectForKey:_uid];
79 }
80
81 /* merging */
82
83 - (BOOL)doesRecord:(NSDictionary *)_rec conflictWith:(NSDictionary *)_other {
84   if (_rec == _other) 
85     return NO;
86   if ([_rec isEqual:_other])
87     return NO;
88   
89   return YES;
90 }
91
92 - (NSDictionary *)_registerConflictingRecord:(NSDictionary *)_other
93   inRecord:(NSDictionary *)_record
94 {
95   NSMutableArray *conflicts;
96   
97   if (_record == nil) return _other;
98   if (_other  == nil) return _record;
99   
100   if ((conflicts = [_record objectForKey:@"conflicts"]) == nil) {
101     NSMutableDictionary *md;
102     
103     md = [[_record mutableCopy] autorelease];
104     conflicts = [NSMutableArray arrayWithCapacity:4];
105     [md setObject:conflicts forKey:@"conflicts"];
106     _record = md;
107   }
108   [conflicts addObject:_other];
109   return _record;
110 }
111
112 /* functionality */
113
114 - (SOGoAppointmentFolder *)calendarFolderForMemberFolder:(id)_folder {
115   SOGoAppointmentFolder *aptFolder;
116   
117   if (![_folder isNotNull])
118     return nil;
119   
120   aptFolder = [_folder lookupName:@"Calendar" inContext:nil acquire:NO];
121   if (![aptFolder isNotNull])
122     return nil;
123   
124   if (![aptFolder respondsToSelector:@selector(fetchCoreInfosFrom:to:component:)]) {
125     [self errorWithFormat:@"folder does not implemented required API: %@",
126             _folder];
127     return nil;
128   }
129   return aptFolder;
130 }
131
132 /* overridden */
133 - (NSArray *) fetchFields: (NSArray *) _fields
134                      from: (NSCalendarDate *) _startDate
135                        to: (NSCalendarDate *) _endDate
136                 component: (id) _component
137 {
138   NSArray             *folders;
139   NSMutableArray      *result;
140   NSMutableDictionary *uidToRecord;
141   unsigned            i, count;
142   SoSecurityManager *securityManager;
143
144   securityManager = [SoSecurityManager sharedSecurityManager];
145
146   folders = [[self container] memberFolders];
147   [self resetFolderCaches];
148   
149   if ((count = [folders count]) == 0)
150     return [NSArray array];
151   
152   if (self->uidToFolder == nil)
153     self->uidToFolder = [[NSMutableDictionary alloc] initWithCapacity:7*count];
154   else
155     [self->uidToFolder removeAllObjects];
156   
157   uidToRecord = [NSMutableDictionary dictionaryWithCapacity:(7 * count)];
158   result      = [NSMutableArray arrayWithCapacity:(7 * count)];
159   for (i = 0; i < count; i++) {
160     SOGoAppointmentFolder *aptFolder;
161     id                    results;
162     NSDictionary          *record;
163
164     aptFolder = [self calendarFolderForMemberFolder:
165                         [folders objectAtIndex:i]];
166     if (![aptFolder isNotNull]) {
167       [self debugWithFormat:@"did not find a Calendar folder in folder: %@",
168               [folders objectAtIndex:i]];
169       continue;
170     }
171
172     if ([securityManager validatePermission: SoPerm_AccessContentsInformation
173                          onObject: aptFolder
174                          inContext: context]) {
175       [self debugWithFormat:@"no permission to read the content of calendar: %@",
176             [folders objectAtIndex:i]];
177       continue;
178     }
179
180     results = [aptFolder fetchFields: _fields
181                          from: _startDate
182                          to: _endDate
183                          component: _component];
184     if (![results isNotNull]) continue;
185     
186     results = [results objectEnumerator];
187     while ((record = [results nextObject])) {
188       NSString     *uid;
189       NSDictionary *existingRecord;
190       
191       uid = [record objectForKey:@"uid"];
192       if (![uid isNotNull]) {
193         [self warnWithFormat:@"record without uid: %@", result];
194         [result addObject:record];
195         continue;
196       }
197       
198       if ((existingRecord = [uidToRecord objectForKey:uid]) == nil) {
199         /* record not yet in result set */
200         [uidToRecord setObject:record forKey:uid];
201         [result addObject:record];
202         
203         [self->uidToFolder setObject:aptFolder forKey:uid];
204       }
205       else if ([self doesRecord:existingRecord conflictWith:record]) {
206         /* record already registered and it conflicts (diff values) */
207         NSDictionary *newRecord;
208         int idx;
209         
210         newRecord = [self _registerConflictingRecord:record 
211                           inRecord:existingRecord];
212         [uidToRecord setObject:newRecord forKey:uid];
213         
214         if ((idx = [result indexOfObject:existingRecord]) != NSNotFound)
215           [result replaceObjectAtIndex:idx withObject:newRecord];
216       }
217       else {
218         /* record already registered, but values in sync, nothing to do */
219       }
220     }
221   }
222   return result;
223 }
224
225
226 /* URL generation */
227
228 - (NSString *)baseURLForAptWithUID:(NSString *)_uid inContext:(id)_ctx {
229   /* Note: fetchCore must have been called before this works */
230   SOGoAppointmentFolder *folder;
231   
232   if ([_uid length] == 0) {
233     [self errorWithFormat:@"got invalid UID."];
234     return nil;
235   }
236   
237   if ((folder = [self folderForUID:_uid]) == nil) {
238     [self errorWithFormat:@"did not find a folder containing UID: '%@'",
239             _uid];
240     return nil;
241   }
242   if (![folder respondsToSelector:_cmd]) {
243     [self errorWithFormat:@"found folder cannot construct UID URLs: %@",
244             folder];
245     return nil;
246   }
247   
248   [self debugWithFormat:@"found ID %@ in folder: %@", _uid, folder];
249   
250   return [folder baseURLForAptWithUID:_uid inContext:_ctx];
251 }
252
253 @end /* SOGoGroupAppointmentFolder */