]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Scheduler/UIxAppointmentProposal.m
39996737f367ec8b71e83eb266bdae923990f8b1
[scalable-opengroupware.org] / SOGo / 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 }
49
50 @end
51
52 #include <SoObjects/Appointments/SOGoAppointmentFolder.h>
53 #include <NGExtensions/NGCalendarDateRange.h>
54 #include "common.h"
55
56 @implementation UIxAppointmentProposal
57
58 - (void)dealloc {
59   [self->blockedRanges   release];
60
61   [self->startDateHour   release];
62   [self->startDateMinute release];
63   [self->startDateDay    release];
64   [self->startDateMonth  release];
65   [self->startDateYear   release];
66
67   [self->endDateHour     release];
68   [self->endDateMinute   release];
69   [self->endDateDay      release];
70   [self->endDateMonth    release];
71   [self->endDateYear     release];
72   [self->duration        release];
73     
74   [self->participants    release];
75   [self->resources       release];
76   [super dealloc];
77 }
78
79 /* notifications */
80
81 - (void)sleep {
82   [self->currentDay release]; self->currentDay = nil;
83   [self->item       release]; self->item       = nil;
84   [super sleep];
85 }
86
87 /* accessors */
88
89 - (void)setItem:(id)_item {
90   ASSIGN(self->item, _item);
91 }
92 - (id)item {
93   return self->item;
94 }
95
96 - (void)setStartDateHour:(id)_startDateHour {
97   ASSIGN(self->startDateHour, _startDateHour);
98 }
99 - (id)startDateHour {
100   return self->startDateHour;
101 }
102 - (void)setStartDateMinute:(id)_startDateMinute {
103   ASSIGN(self->startDateMinute, _startDateMinute);
104 }
105 - (id)startDateMinute {
106   return self->startDateMinute;
107 }
108 - (void)setStartDateDay:(id)_startDateDay {
109   ASSIGN(self->startDateDay, _startDateDay);
110 }
111 - (id)startDateDay {
112   return self->startDateDay;
113 }
114 - (void)setStartDateMonth:(id)_startDateMonth {
115   ASSIGN(self->startDateMonth, _startDateMonth);
116 }
117 - (id)startDateMonth {
118   return self->startDateMonth;
119 }
120 - (void)setStartDateYear:(id)_startDateYear {
121   ASSIGN(self->startDateYear, _startDateYear);
122 }
123 - (id)startDateYear {
124   return self->startDateYear;
125 }
126 - (void)setEndDateHour:(id)_endDateHour {
127   ASSIGN(self->endDateHour, _endDateHour);
128 }
129 - (id)endDateHour {
130   return self->endDateHour;
131 }
132 - (void)setEndDateMinute:(id)_endDateMinute {
133   ASSIGN(self->endDateMinute, _endDateMinute);
134 }
135 - (id)endDateMinute {
136   return self->endDateMinute;
137 }
138 - (void)setEndDateDay:(id)_endDateDay {
139   ASSIGN(self->endDateDay, _endDateDay);
140 }
141 - (id)endDateDay {
142   return self->endDateDay;
143 }
144 - (void)setEndDateMonth:(id)_endDateMonth {
145   ASSIGN(self->endDateMonth, _endDateMonth);
146 }
147 - (id)endDateMonth {
148   return self->endDateMonth;
149 }
150 - (void)setEndDateYear:(id)_endDateYear {
151   ASSIGN(self->endDateYear, _endDateYear);
152 }
153 - (id)endDateYear {
154   return self->endDateYear;
155 }
156
157 - (void)setStartDate:(NSCalendarDate *)_date {
158   [self setStartDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
159   [self setStartDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
160   [self setStartDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
161   [self setStartDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
162   [self setStartDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
163 }
164 - (NSCalendarDate *)startDate {
165   return [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
166                          month:[[self startDateMonth] intValue]
167                          day:[[self startDateDay] intValue]
168                          hour:[[self startDateHour] intValue]
169                          minute:[[self startDateMinute] intValue]
170                          second:0
171                          timeZone:[self viewTimeZone]];
172 }
173 - (void)setEndDate:(NSCalendarDate *)_date {
174   [self setEndDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
175   [self setEndDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
176   [self setEndDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
177   [self setEndDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
178   [self setEndDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
179 }
180 - (NSCalendarDate *)endDate {
181   return [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
182                          month:[[self endDateMonth] intValue]
183                          day:[[self endDateDay] intValue]
184                          hour:[[self endDateHour] intValue]
185                          minute:[[self endDateMinute] intValue]
186                          second:59
187                          timeZone:[self viewTimeZone]];
188 }
189
190 - (void)setDuration:(id)_duration {
191   ASSIGN(self->duration, _duration);
192 }
193 - (id)duration {
194   return self->duration;
195 }
196 - (int)durationInMinutes {
197   return [[self duration] intValue];
198 }
199 - (NSTimeInterval)durationAsTimeInterval {
200   return [self durationInMinutes] * 60;
201 }
202
203 - (NSString *)itemDurationText {
204   // TODO: use a formatter
205   // TODO: localize
206   switch ([[self item] intValue]) {
207   case  30: return @"30 minutes";
208   case  60: return @"1 hour";
209   case 120: return @"2 hours";
210   case 240: return @"4 hours";
211   case 480: return @"8 hours";
212   default:
213     return [NSString stringWithFormat:@"%@ minutes", [self item]];
214   }
215 }
216
217 - (void)setParticipants:(NSArray *)_parts {
218   ASSIGN(self->participants, _parts);
219 }
220 - (NSArray *)participants {
221   return self->participants;
222 }
223 - (void)setResources:(NSArray *)_res {
224   ASSIGN(self->resources, _res);
225 }
226 - (NSArray *)resources {
227   return self->resources;
228 }
229
230 - (NSArray *)attendees {
231   NSArray *a, *b;
232   
233   a = [self participants];
234   b = [self resources];
235   if ([b count] == 0) return a;
236   if ([a count] == 0) return b;
237   return [a arrayByAddingObjectsFromArray:b];
238 }
239
240 - (void)setCurrentDay:(id)_day {
241   ASSIGN(self->currentDay, _day);
242 }
243 - (id)currentDay {
244   return self->currentDay;
245 }
246
247 - (NSArray *)hours {
248   // TODO: from 'earliest start' to 'latest endtime'
249   unsigned lStartHour = 9, lEndHour = 17, i;
250   NSMutableArray *ma;
251   
252   lStartHour = [[self startDateHour] intValue];
253   lEndHour   = [[self endDateHour]   intValue];
254   if (lStartHour < 1) lStartHour = 1;
255   if (lEndHour < lStartHour) lEndHour = lStartHour + 1;
256   
257   ma = [NSMutableArray arrayWithCapacity:lEndHour - lStartHour + 2];
258   for (i = lStartHour; i <= lEndHour; i++)
259     [ma addObject:[NSNumber numberWithInt:i]];
260   return ma;
261 }
262
263 - (NSArray *)days {
264   // TODO: from startdate to enddate
265   NSMutableArray *ma;
266   NSCalendarDate *base, *stop, *current;
267   
268   base = [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
269                          month:[[self startDateMonth] intValue]
270                          day:[[self startDateDay] intValue]
271                          hour:12 minute:0 second:0
272                          timeZone:[self viewTimeZone]];
273   stop = [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
274                          month:[[self endDateMonth] intValue]
275                          day:[[self endDateDay] intValue]
276                          hour:12 minute:0 second:0
277                          timeZone:[self viewTimeZone]];
278   
279   ma = [NSMutableArray arrayWithCapacity:16];
280   
281   current = base;
282   while ([current compare:stop] != NSOrderedDescending) {
283     [current setTimeZone:[self viewTimeZone]];
284     [ma addObject:current];
285     
286     /* Note: remember the timezone behaviour of the method below! */
287     current = [current dateByAddingYears:0 months:0 days:1];
288   }
289   return ma;
290 }
291
292 - (NSArray *)durationSteps {
293   // TODO: make configurable
294   return [NSArray arrayWithObjects:
295                     @"30", @"60", @"120", @"240", @"480", nil];
296 }
297
298 /* slots */
299
300 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
301   unsigned idx;
302   
303   idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
304   if (idx != NSNotFound) {
305     [self debugWithFormat:@"blocked range:\n  range: %@\n  block: %@", 
306             _range,
307             [self->blockedRanges objectAtIndex:idx]];
308   }
309   
310   return idx == NSNotFound ? YES : NO;
311 }
312
313 - (BOOL)isSlotRangeGreen:(NGCalendarDateRange *)_slotRange {
314   NGCalendarDateRange *aptRange;
315   NSCalendarDate *aptStartDate, *aptEndDate;
316
317   if (_slotRange == nil)
318     return NO;
319   
320   /* calculate the interval requested by the user (can be larger) */
321   
322   aptStartDate = [_slotRange startDate];
323   aptEndDate   = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
324                                            [aptStartDate timeIntervalSince1970]
325                                          + [self durationAsTimeInterval] - 1];
326   [aptStartDate setTimeZone:[self viewTimeZone]];
327   [aptEndDate   setTimeZone:[self viewTimeZone]];
328   aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate:aptStartDate
329                                   endDate:aptEndDate];
330   [aptEndDate release]; aptEndDate = nil;
331   
332   return [self isRangeGreen:aptRange];
333 }
334
335 - (BOOL)isFirstHalfGreen {
336   /* currentday is the date, self->item the hour */
337   NSCalendarDate *from, *to;
338   NGCalendarDateRange *range;
339
340   from  = [self->currentDay hour:[[self item] intValue] minute:0];
341   to    = [self->currentDay hour:[[self item] intValue] minute:30];
342   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
343   return [self isSlotRangeGreen:range];
344 }
345 - (BOOL)isSecondHalfGreen {
346   /* currentday is the date, self->item the hour */
347   NSCalendarDate *from, *to;
348   NGCalendarDateRange *range;
349
350   from  = [self->currentDay hour:[[self item] intValue] minute:30];
351   to    = [self->currentDay hour:[[self item] intValue] minute:59];
352   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
353   return [self isSlotRangeGreen:range];
354 }
355
356 - (BOOL)isFirstHalfBlocked {
357   return [self isFirstHalfGreen] ? NO : YES;
358 }
359 - (BOOL)isSecondHalfBlocked {
360   return [self isSecondHalfGreen] ? NO : YES;
361 }
362
363 /* actions */
364
365 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
366   return YES;
367 }
368
369 - (id)defaultAction {
370   NSCalendarDate *now;
371   
372   now = [NSCalendarDate date];
373
374   [self setDuration:@"120"]; /* 1 hour as default */
375   [self setStartDate:[now hour:9 minute:0]];
376   [self setEndDate:[now hour:18 minute:0]];
377   
378   return self;
379 }
380
381 - (id)proposalSearchAction {
382   NSArray               *attendees, *uids;
383   SOGoAppointmentFolder *groupCalendar;
384   NSArray *coreInfos;
385   NSArray *ranges;
386   
387   [self logWithFormat:@"search from %@ to %@", 
388           [self startDate], [self endDate]];
389   
390   attendees = [self attendees];
391   uids      = [[self clientObject] uidsFromICalPersons:attendees];
392   if ([uids count] == 0) {
393     [self logWithFormat:@"Note: no UIDs selected."];
394     return self;
395   }
396   
397   groupCalendar = [[self clientObject] lookupGroupCalendarFolderForUIDs:uids
398                                        inContext:[self context]];
399   [self debugWithFormat:@"group calendar: %@", groupCalendar];
400   
401   if (![groupCalendar respondsToSelector:@selector(fetchCoreInfosFrom:to:)]) {
402     return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
403                         reason:@"invalid folder to run proposal query on!"];
404   }
405   
406   // TODO: just query startdate and enddate ... (freebusy)
407   coreInfos = [groupCalendar fetchCoreInfosFrom:[self startDate]
408                              to:[self endDate]];
409   [self debugWithFormat:@"  process: %d events", [coreInfos count]];
410
411   ranges = [coreInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
412                         @"startDate"
413                       andEndDateKey:@"endDate"];
414   ranges = [ranges arrayByCompactingContainedDateRanges];
415   [self debugWithFormat:@"  ranges: %@", ranges];
416   
417   ASSIGNCOPY(self->blockedRanges, ranges);
418   
419   return self;
420 }
421
422 @end /* UIxAppointmentProposal */