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