]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxAppointmentProposal.m
initial sync
[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 - (NSCalendarDate *)startDate {
174   return [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
175                          month:[[self startDateMonth] intValue]
176                          day:[[self startDateDay] intValue]
177                          hour:[[self startDateHour] intValue]
178                          minute:[[self startDateMinute] intValue]
179                          second:0
180                          timeZone:[[self clientObject] userTimeZone]];
181 }
182 - (void)setEndDate:(NSCalendarDate *)_date {
183   [self setEndDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
184   [self setEndDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
185   [self setEndDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
186   [self setEndDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
187   [self setEndDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
188 }
189 - (NSCalendarDate *)endDate {
190   return [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
191                          month:[[self endDateMonth] intValue]
192                          day:[[self endDateDay] intValue]
193                          hour:[[self endDateHour] intValue]
194                          minute:[[self endDateMinute] intValue]
195                          second:59
196                          timeZone:[[self clientObject] userTimeZone]];
197 }
198
199 - (void)setDuration:(id)_duration {
200   ASSIGN(self->duration, _duration);
201 }
202 - (id)duration {
203   return self->duration;
204 }
205 - (int)durationInMinutes {
206   return [[self duration] intValue];
207 }
208 - (NSTimeInterval)durationAsTimeInterval {
209   return [self durationInMinutes] * 60;
210 }
211
212 - (NSString *)itemDurationText {
213   // TODO: use a formatter
214   // TODO: localize
215   switch ([[self item] intValue]) {
216   case  30: return @"30 minutes";
217   case  60: return @"1 hour";
218   case 120: return @"2 hours";
219   case 240: return @"4 hours";
220   case 480: return @"8 hours";
221   default:
222     return [NSString stringWithFormat:@"%@ minutes", [self item]];
223   }
224 }
225
226 - (void)setParticipants:(NSArray *)_parts {
227   ASSIGN(self->participants, _parts);
228 }
229 - (NSArray *)participants {
230   return self->participants;
231 }
232 - (void)setResources:(NSArray *)_res {
233   ASSIGN(self->resources, _res);
234 }
235 - (NSArray *)resources {
236   return self->resources;
237 }
238
239 - (NSArray *)attendees {
240   NSArray *a, *b;
241   
242   a = [self participants];
243   b = [self resources];
244   if ([b count] == 0) return a;
245   if ([a count] == 0) return b;
246   return [a arrayByAddingObjectsFromArray:b];
247 }
248
249 - (void)setCurrentDay:(id)_day {
250   ASSIGN(self->currentDay, _day);
251 }
252 - (id)currentDay {
253   return self->currentDay;
254 }
255
256 - (NSArray *)hours {
257   // TODO: from 'earliest start' to 'latest endtime'
258   unsigned lStartHour = 9, lEndHour = 17, i;
259   NSMutableArray *ma;
260   
261   lStartHour = [[self startDateHour] intValue];
262   lEndHour   = [[self endDateHour]   intValue];
263   if (lStartHour < 1) lStartHour = 1;
264   if (lEndHour < lStartHour) lEndHour = lStartHour + 1;
265   
266   ma = [NSMutableArray arrayWithCapacity:lEndHour - lStartHour + 2];
267   for (i = lStartHour; i <= lEndHour; i++)
268     [ma addObject:[NSNumber numberWithInt:i]];
269   return ma;
270 }
271
272 - (NSArray *)days {
273   // TODO: from startdate to enddate
274   NSMutableArray *ma;
275   NSCalendarDate *base, *stop, *current;
276   
277   base = [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
278                          month:[[self startDateMonth] intValue]
279                          day:[[self startDateDay] intValue]
280                          hour:12 minute:0 second:0
281                          timeZone:[[self clientObject] userTimeZone]];
282   stop = [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
283                          month:[[self endDateMonth] intValue]
284                          day:[[self endDateDay] intValue]
285                          hour:12 minute:0 second:0
286                          timeZone:[[self clientObject] userTimeZone]];
287   
288   ma = [NSMutableArray arrayWithCapacity:16];
289   
290   current = base;
291   while ([current compare:stop] != NSOrderedDescending) {
292     [current setTimeZone:[[self clientObject] userTimeZone]];
293     [ma addObject:current];
294     
295     /* Note: remember the timezone behaviour of the method below! */
296     current = [current dateByAddingYears:0 months:0 days:1];
297   }
298   return ma;
299 }
300
301 - (NSArray *)durationSteps {
302   // TODO: make configurable
303   return [NSArray arrayWithObjects:
304                     @"30", @"60", @"120", @"240", @"480", nil];
305 }
306
307 /* slots */
308
309 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
310   unsigned idx;
311   
312   idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
313   if (idx != NSNotFound) {
314     [self debugWithFormat:@"blocked range:\n  range: %@\n  block: %@\nintersection:%@", 
315             _range,
316             [self->blockedRanges objectAtIndex:idx],
317       [_range intersectionDateRange:[self->blockedRanges objectAtIndex:idx]]];
318   }
319   
320   return idx == NSNotFound ? YES : NO;
321 }
322
323 - (BOOL)isSlotRangeGreen:(NGCalendarDateRange *)_slotRange {
324   NGCalendarDateRange *aptRange;
325   NSCalendarDate *aptStartDate, *aptEndDate;
326
327   if (_slotRange == nil)
328     return NO;
329   
330   /* calculate the interval requested by the user (can be larger) */
331
332   aptStartDate = [_slotRange startDate];
333   // TODO: gives warning on MacOSX
334   aptEndDate   = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
335                                            [aptStartDate timeIntervalSince1970]
336                                          + [self durationAsTimeInterval]];
337   [aptStartDate setTimeZone:[[self clientObject] userTimeZone]];
338   [aptEndDate   setTimeZone:[[self clientObject] userTimeZone]];
339   aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate:aptStartDate
340                                   endDate:aptEndDate];
341   [aptEndDate release]; aptEndDate = nil;
342   
343   return [self isRangeGreen:aptRange];
344 }
345
346 - (BOOL)isFirstHalfGreen {
347   /* currentday is the date, self->item the hour */
348   NSCalendarDate *from, *to;
349   NGCalendarDateRange *range;
350
351   from  = [self->currentDay hour:[[self item] intValue] minute:0];
352   to    = [self->currentDay hour:[[self item] intValue] minute:30];
353   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
354   return [self isSlotRangeGreen:range];
355 }
356 - (BOOL)isSecondHalfGreen {
357   /* currentday is the date, self->item the hour */
358   NSCalendarDate *from, *to;
359   NGCalendarDateRange *range;
360
361   from  = [self->currentDay hour:[[self item] intValue]     minute:30];
362   to    = [self->currentDay hour:[[self item] intValue] + 1 minute:0];
363   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
364   return [self isSlotRangeGreen:range];
365 }
366
367 - (BOOL)isFirstHalfBlocked {
368   return [self isFirstHalfGreen] ? NO : YES;
369 }
370 - (BOOL)isSecondHalfBlocked {
371   return [self isSecondHalfGreen] ? NO : YES;
372 }
373
374 /* actions */
375
376 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
377   return YES;
378 }
379
380 - (id<WOActionResults>)defaultAction {
381   NSCalendarDate *now;
382   
383   now = [NSCalendarDate date];
384
385   [self setDuration:@"120"]; /* 1 hour as default */
386   [self setStartDate:[now hour:9 minute:0]];
387   [self setEndDate:[now hour:18 minute:0]];
388   
389   return self;
390 }
391
392 - (id)proposalSearchAction {
393   NSArray        *attendees, *uids, *fbos, *ranges;
394   unsigned       i, count;
395   NSMutableArray *allInfos;
396
397   [self logWithFormat:@"search from %@ to %@", 
398           [self startDate], [self endDate]];
399   
400   attendees = [self attendees];
401   uids      = [[self clientObject] uidsFromICalPersons:attendees];
402   if ([uids count] == 0) {
403     [self logWithFormat:@"Note: no UIDs selected."];
404     return self;
405   }
406
407   fbos     = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
408                                   inContext:[self context]];
409   count    = [fbos count];
410   // wild guess at capacity
411   allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
412
413   for (i = 0; i < count; i++) {
414     SOGoFreeBusyObject *fb;
415     NSArray            *infos;
416     
417     fb    = [fbos objectAtIndex:i];
418     if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
419       infos = [fb fetchFreeBusyInfosFrom:[self startDate] to:[self endDate]];
420       [allInfos addObjectsFromArray:infos];
421     }
422   }
423   [self debugWithFormat:@"  processing: %d infos", [allInfos count]];
424   ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
425                        @"startDate"
426                      andEndDateKey:@"endDate"];
427   ranges = [ranges arrayByCompactingContainedDateRanges];
428   [self debugWithFormat:@"  ranges: %@", ranges];
429   
430   ASSIGNCOPY(self->blockedRanges, ranges);
431   
432   return self;
433 }
434
435 /* URLs */
436
437 - (NSMutableDictionary *)currentQueryParameters {
438   if(!self->currentQueryParameters) {
439     self->currentQueryParameters = [[self queryParameters] mutableCopy];
440     [self->currentQueryParameters setObject:[self duration] forKey:@"dur"];
441     [self setICalPersons:[self resources] asQueryParameter:@"rs"];
442     [self setICalPersons:[self participants] asQueryParameter:@"ps"];
443   }
444   return self->currentQueryParameters;
445 }
446
447 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp {
448   NSMutableString *s;
449   unsigned i, count;
450   
451   s = [[NSMutableString alloc] init];
452   count = [_ps count];
453   for(i = 0; i < count; i++) {
454     iCalPerson *p = [_ps objectAtIndex:i];
455     [s appendString:[p rfc822Email]];
456     if(i != (count - 1))
457       [s appendString:@","];
458   }
459   [[self currentQueryParameters] setObject:s forKey:_qp];
460   [s release];
461 }
462
463 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first {
464   NSMutableDictionary *qp;
465   NSString *hmString;
466   NSCalendarDate *date;
467   unsigned minute;
468
469   minute = _first ? 0 : 30;
470   qp = [self currentQueryParameters];
471
472   date = [self currentDay];
473   hmString = [NSString stringWithFormat:@"%02d%02d",
474     [item intValue], minute];
475   [self setSelectedDateQueryParameter:date inDictionary:qp];
476   [qp setObject:hmString forKey:@"hm"];
477   return qp;
478 }
479
480 - (NSDictionary *)currentFirstHalfQueryParameters {
481   return [self currentHourQueryParametersForFirstHalf:YES];
482 }
483
484 - (NSDictionary *)currentSecondHalfQueryParameters {
485   return [self currentHourQueryParametersForFirstHalf:NO];
486 }
487
488 @end /* UIxAppointmentProposal */