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