]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxAppointmentProposal.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1236 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / Scheduler / UIxAppointmentProposal.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 #import <Foundation/NSCalendarDate.h>
23 #import <Foundation/NSNull.h>
24 #import <Foundation/NSValue.h>
25
26 #import <NGExtensions/NGCalendarDateRange.h>
27 #import <NGExtensions/NSCalendarDate+misc.h>
28 #import <NGCards/iCalPerson.h>
29
30 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
31 #import <SoObjects/Appointments/SOGoFreeBusyObject.h>
32 #import <SOGoUI/UIxComponent.h>
33
34 @interface UIxAppointmentProposal : UIxComponent
35 {
36   id item;
37   id currentDay;
38
39   /* individual values */
40   id startDateHour;
41   id startDateMinute;
42   id endDateHour;
43   id endDateMinute;
44
45   id startDateDay;
46   id startDateMonth;
47   id startDateYear;
48   id endDateDay;
49   id endDateMonth;
50   id endDateYear;
51   
52   NSArray        *participants; /* array of iCalPerson's */
53   NSArray        *resources;    /* array of iCalPerson's */
54   id             duration;
55   
56   NSArray *blockedRanges;
57   NSMutableDictionary *currentQueryParameters;
58 }
59
60 - (NSMutableDictionary *)currentQueryParameters;
61 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first;
62
63 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp;
64 @end
65
66 @implementation UIxAppointmentProposal
67
68 - (void)dealloc {
69   [self->blockedRanges   release];
70
71   [self->startDateHour   release];
72   [self->startDateMinute release];
73   [self->startDateDay    release];
74   [self->startDateMonth  release];
75   [self->startDateYear   release];
76
77   [self->endDateHour     release];
78   [self->endDateMinute   release];
79   [self->endDateDay      release];
80   [self->endDateMonth    release];
81   [self->endDateYear     release];
82   [self->duration        release];
83     
84   [self->participants    release];
85   [self->resources       release];
86   
87   [self->currentQueryParameters release];
88   [super dealloc];
89 }
90
91 /* notifications */
92
93 - (void)sleep {
94   [self->currentDay release]; self->currentDay = nil;
95   [self->item       release]; self->item       = nil;
96   [super sleep];
97 }
98
99 /* accessors */
100
101 - (void)setItem:(id)_item {
102   ASSIGN(self->item, _item);
103 }
104 - (id)item {
105   return self->item;
106 }
107
108 - (void)setStartDateHour:(id)_startDateHour {
109   ASSIGN(self->startDateHour, _startDateHour);
110 }
111 - (id)startDateHour {
112   return self->startDateHour;
113 }
114 - (void)setStartDateMinute:(id)_startDateMinute {
115   ASSIGN(self->startDateMinute, _startDateMinute);
116 }
117 - (id)startDateMinute {
118   return self->startDateMinute;
119 }
120 - (void)setStartDateDay:(id)_startDateDay {
121   ASSIGN(self->startDateDay, _startDateDay);
122 }
123 - (id)startDateDay {
124   return self->startDateDay;
125 }
126 - (void)setStartDateMonth:(id)_startDateMonth {
127   ASSIGN(self->startDateMonth, _startDateMonth);
128 }
129 - (id)startDateMonth {
130   return self->startDateMonth;
131 }
132 - (void)setStartDateYear:(id)_startDateYear {
133   ASSIGN(self->startDateYear, _startDateYear);
134 }
135 - (id)startDateYear {
136   return self->startDateYear;
137 }
138 - (void)setEndDateHour:(id)_endDateHour {
139   ASSIGN(self->endDateHour, _endDateHour);
140 }
141 - (id)endDateHour {
142   return self->endDateHour;
143 }
144 - (void)setEndDateMinute:(id)_endDateMinute {
145   ASSIGN(self->endDateMinute, _endDateMinute);
146 }
147 - (id)endDateMinute {
148   return self->endDateMinute;
149 }
150 - (void)setEndDateDay:(id)_endDateDay {
151   ASSIGN(self->endDateDay, _endDateDay);
152 }
153 - (id)endDateDay {
154   return self->endDateDay;
155 }
156 - (void)setEndDateMonth:(id)_endDateMonth {
157   ASSIGN(self->endDateMonth, _endDateMonth);
158 }
159 - (id)endDateMonth {
160   return self->endDateMonth;
161 }
162 - (void)setEndDateYear:(id)_endDateYear {
163   ASSIGN(self->endDateYear, _endDateYear);
164 }
165 - (id)endDateYear {
166   return self->endDateYear;
167 }
168
169 - (void)setStartDate:(NSCalendarDate *)_date {
170   [self setStartDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
171   [self setStartDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
172   [self setStartDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
173   [self setStartDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
174   [self setStartDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
175 }
176
177 - (NSCalendarDate *) startDate
178 {
179   NSTimeZone *timeZone;
180
181   timeZone = [[context activeUser] timeZone];
182
183   return [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
184                          month:[[self startDateMonth] intValue]
185                          day:[[self startDateDay] intValue]
186                          hour:[[self startDateHour] intValue]
187                          minute:[[self startDateMinute] intValue]
188                          second:0
189                          timeZone: timeZone];
190 }
191
192 - (void) setEndDate: (NSCalendarDate *) _date
193 {
194   [self setEndDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
195   [self setEndDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
196   [self setEndDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
197   [self setEndDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
198   [self setEndDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
199 }
200
201 - (NSCalendarDate *) endDate
202 {
203   NSTimeZone *timeZone;
204
205   timeZone = [[context activeUser] timeZone];
206
207   return [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
208                          month:[[self endDateMonth] intValue]
209                          day:[[self endDateDay] intValue]
210                          hour:[[self endDateHour] intValue]
211                          minute:[[self endDateMinute] intValue]
212                          second:59
213                          timeZone: timeZone];
214 }
215
216 - (void)setDuration:(id)_duration {
217   ASSIGN(self->duration, _duration);
218 }
219 - (id)duration {
220   return self->duration;
221 }
222 - (int)durationInMinutes {
223   return [[self duration] intValue];
224 }
225 - (NSTimeInterval)durationAsTimeInterval {
226   return [self durationInMinutes] * 60;
227 }
228
229 - (NSString *)itemDurationText {
230   // TODO: use a formatter
231   // TODO: localize
232   switch ([[self item] intValue]) {
233   case  30: return @"30 minutes";
234   case  60: return @"1 hour";
235   case 120: return @"2 hours";
236   case 240: return @"4 hours";
237   case 480: return @"8 hours";
238   default:
239     return [NSString stringWithFormat:@"%@ minutes", [self item]];
240   }
241 }
242
243 - (void)setParticipants:(NSArray *)_parts {
244   ASSIGN(self->participants, _parts);
245 }
246 - (NSArray *)participants {
247   return self->participants;
248 }
249 - (void)setResources:(NSArray *)_res {
250   ASSIGN(self->resources, _res);
251 }
252 - (NSArray *)resources {
253   return self->resources;
254 }
255
256 - (NSArray *)attendees {
257   NSArray *a, *b;
258   
259   a = [self participants];
260   b = [self resources];
261   if ([b count] == 0) return a;
262   if ([a count] == 0) return b;
263   return [a arrayByAddingObjectsFromArray:b];
264 }
265
266 - (void)setCurrentDay:(id)_day {
267   ASSIGN(self->currentDay, _day);
268 }
269 - (id)currentDay {
270   return self->currentDay;
271 }
272
273 - (NSArray *)hours {
274   // TODO: from 'earliest start' to 'latest endtime'
275   unsigned lStartHour = 9, lEndHour = 17, i;
276   NSMutableArray *ma;
277   
278   lStartHour = [[self startDateHour] intValue];
279   lEndHour   = [[self endDateHour]   intValue];
280   if (lStartHour < 1) lStartHour = 1;
281   if (lEndHour < lStartHour) lEndHour = lStartHour + 1;
282   
283   ma = [NSMutableArray arrayWithCapacity:lEndHour - lStartHour + 2];
284   for (i = lStartHour; i <= lEndHour; i++)
285     [ma addObject:[NSNumber numberWithInt:i]];
286   return ma;
287 }
288
289 - (NSArray *)days {
290   // TODO: from startdate to enddate
291   NSMutableArray *ma;
292   NSCalendarDate *base, *stop, *current;
293   NSTimeZone *timeZone;
294
295   timeZone = [[context activeUser] timeZone];
296   base = [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
297                          month:[[self startDateMonth] intValue]
298                          day:[[self startDateDay] intValue]
299                          hour:12 minute:0 second:0
300                          timeZone: timeZone];
301   stop = [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
302                          month:[[self endDateMonth] intValue]
303                          day:[[self endDateDay] intValue]
304                          hour:12 minute:0 second:0
305                          timeZone: timeZone];
306   ma = [NSMutableArray arrayWithCapacity: 16];
307   
308   current = base;
309   while ([current compare:stop] != NSOrderedDescending)
310     {
311       [current setTimeZone: timeZone];
312       [ma addObject:current];
313     
314     /* Note: remember the timezone behaviour of the method below! */
315       current = [current dateByAddingYears:0 months:0 days:1];
316     }
317
318   return ma;
319 }
320
321 - (NSArray *)durationSteps {
322   // TODO: make configurable
323   return [NSArray arrayWithObjects:
324                     @"30", @"60", @"120", @"240", @"480", nil];
325 }
326
327 /* slots */
328
329 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
330   unsigned idx;
331   
332   idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
333   if (idx != NSNotFound) {
334     [self debugWithFormat:@"blocked range:\n  range: %@\n  block: %@\nintersection:%@", 
335             _range,
336             [self->blockedRanges objectAtIndex:idx],
337       [_range intersectionDateRange:[self->blockedRanges objectAtIndex:idx]]];
338   }
339   
340   return idx == NSNotFound ? YES : NO;
341 }
342
343 - (BOOL)isSlotRangeGreen:(NGCalendarDateRange *)_slotRange {
344   NGCalendarDateRange *aptRange;
345   NSCalendarDate *aptStartDate, *aptEndDate;
346   NSTimeZone *timeZone;
347
348   if (_slotRange == nil)
349     return NO;
350   
351   /* calculate the interval requested by the user (can be larger) */
352
353   aptStartDate = [_slotRange startDate];
354   // TODO: gives warning on MacOSX
355   aptEndDate   = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
356                                            [aptStartDate timeIntervalSince1970]
357                                          + [self durationAsTimeInterval]];
358   timeZone = [[context activeUser] timeZone];
359   [aptStartDate setTimeZone: timeZone];
360   [aptEndDate   setTimeZone: timeZone];
361   aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate:aptStartDate
362                                   endDate:aptEndDate];
363   [aptEndDate release]; aptEndDate = nil;
364   
365   return [self isRangeGreen:aptRange];
366 }
367
368 - (BOOL)isFirstHalfGreen {
369   /* currentday is the date, self->item the hour */
370   NSCalendarDate *from, *to;
371   NGCalendarDateRange *range;
372
373   from  = [self->currentDay hour:[[self item] intValue] minute:0];
374   to    = [self->currentDay hour:[[self item] intValue] minute:30];
375   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
376   return [self isSlotRangeGreen:range];
377 }
378 - (BOOL)isSecondHalfGreen {
379   /* currentday is the date, self->item the hour */
380   NSCalendarDate *from, *to;
381   NGCalendarDateRange *range;
382
383   from  = [self->currentDay hour:[[self item] intValue]     minute:30];
384   to    = [self->currentDay hour:[[self item] intValue] + 1 minute:0];
385   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
386   return [self isSlotRangeGreen:range];
387 }
388
389 - (BOOL)isFirstHalfBlocked {
390   return [self isFirstHalfGreen] ? NO : YES;
391 }
392 - (BOOL)isSecondHalfBlocked {
393   return [self isSecondHalfGreen] ? NO : YES;
394 }
395
396 /* actions */
397
398 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
399   return YES;
400 }
401
402 - (id<WOActionResults>)defaultAction {
403   NSCalendarDate *now;
404   
405   now = [NSCalendarDate date];
406
407   [self setDuration:@"120"]; /* 1 hour as default */
408   [self setStartDate:[now hour:9 minute:0]];
409   [self setEndDate:[now hour:18 minute:0]];
410   
411   return self;
412 }
413
414 - (id)proposalSearchAction {
415   NSArray        *attendees, *uids, *fbos, *ranges;
416   unsigned       i, count;
417   NSMutableArray *allInfos;
418
419   [self logWithFormat:@"search from %@ to %@", 
420           [self startDate], [self endDate]];
421   
422   attendees = [self attendees];
423   uids      = [[self clientObject] uidsFromICalPersons:attendees];
424   if ([uids count] == 0) {
425     [self logWithFormat:@"Note: no UIDs selected."];
426     return self;
427   }
428
429   fbos     = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
430                                   inContext:[self context]];
431   count    = [fbos count];
432   // wild guess at capacity
433   allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
434
435   for (i = 0; i < count; i++) {
436     SOGoFreeBusyObject *fb;
437     NSArray            *infos;
438     
439     fb    = [fbos objectAtIndex:i];
440     if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
441       infos = [fb fetchFreeBusyInfosFrom:[self startDate] to:[self endDate]];
442       [allInfos addObjectsFromArray:infos];
443     }
444   }
445   [self debugWithFormat:@"  processing: %d infos", [allInfos count]];
446   ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
447                        @"startDate"
448                      andEndDateKey:@"endDate"];
449   ranges = [ranges arrayByCompactingContainedDateRanges];
450   [self debugWithFormat:@"  ranges: %@", ranges];
451   
452   ASSIGNCOPY(self->blockedRanges, ranges);
453   
454   return self;
455 }
456
457 /* URLs */
458
459 - (NSMutableDictionary *)currentQueryParameters {
460   if(!self->currentQueryParameters) {
461     self->currentQueryParameters = [[self queryParameters] mutableCopy];
462     [self->currentQueryParameters setObject:[self duration] forKey:@"dur"];
463     [self setICalPersons:[self resources] asQueryParameter:@"rs"];
464     [self setICalPersons:[self participants] asQueryParameter:@"ps"];
465   }
466   return self->currentQueryParameters;
467 }
468
469 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp {
470   NSMutableString *s;
471   unsigned i, count;
472   
473   s = [[NSMutableString alloc] init];
474   count = [_ps count];
475   for(i = 0; i < count; i++) {
476     iCalPerson *p = [_ps objectAtIndex:i];
477     [s appendString:[p rfc822Email]];
478     if(i != (count - 1))
479       [s appendString:@","];
480   }
481   [[self currentQueryParameters] setObject:s forKey:_qp];
482   [s release];
483 }
484
485 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first {
486   NSMutableDictionary *qp;
487   NSString *hmString;
488   NSCalendarDate *date;
489   unsigned minute;
490
491   minute = _first ? 0 : 30;
492   qp = [self currentQueryParameters];
493
494   date = [self currentDay];
495   hmString = [NSString stringWithFormat:@"%02d%02d",
496     [item intValue], minute];
497   [self setSelectedDateQueryParameter:date inDictionary:qp];
498   [qp setObject:hmString forKey:@"hm"];
499   return qp;
500 }
501
502 - (NSDictionary *)currentFirstHalfQueryParameters {
503   return [self currentHourQueryParametersForFirstHalf:YES];
504 }
505
506 - (NSDictionary *)currentSecondHalfQueryParameters {
507   return [self currentHourQueryParametersForFirstHalf:NO];
508 }
509
510 @end /* UIxAppointmentProposal */