]> err.no Git - scalable-opengroupware.org/blob - UI/Scheduler/UIxTaskProposal.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1089 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 #include <SOGoUI/UIxComponent.h>
24
25 @interface UIxTaskProposal : 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 UIxTaskProposal
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 {
288   // TODO: from startdate to enddate
289   NSMutableArray *ma;
290   NSCalendarDate *base, *stop, *current;  
291   NSTimeZone *timeZone;
292
293   timeZone = [[context activeUser] timeZone];
294   base = [NSCalendarDate dateWithYear: [[self startDateYear] intValue]
295                          month: [[self startDateMonth] intValue]
296                          day: [[self startDateDay] intValue]
297                          hour: 12 minute: 0 second: 0
298                          timeZone: timeZone];
299   stop = [NSCalendarDate dateWithYear: [[self endDateYear] intValue]
300                          month: [[self endDateMonth] intValue]
301                          day: [[self endDateDay] intValue]
302                          hour: 12 minute: 0 second: 0
303                          timeZone: timeZone];
304   
305   ma = [NSMutableArray arrayWithCapacity:16];
306   
307   current = base;
308   while ([current compare:stop] != NSOrderedDescending) {
309     [current setTimeZone: timeZone];
310     [ma addObject:current];
311     
312     /* Note: remember the timezone behaviour of the method below! */
313     current = [current dateByAddingYears:0 months:0 days:1];
314   }
315
316   return ma;
317 }
318
319 - (NSArray *)durationSteps {
320   // TODO: make configurable
321   return [NSArray arrayWithObjects:
322                     @"30", @"60", @"120", @"240", @"480", nil];
323 }
324
325 /* slots */
326
327 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
328   unsigned idx;
329   
330   idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
331   if (idx != NSNotFound) {
332     [self debugWithFormat:@"blocked range:\n  range: %@\n  block: %@\nintersection:%@", 
333             _range,
334             [self->blockedRanges objectAtIndex:idx],
335       [_range intersectionDateRange:[self->blockedRanges objectAtIndex:idx]]];
336   }
337   
338   return idx == NSNotFound ? YES : NO;
339 }
340
341 - (BOOL)isSlotRangeGreen: (NGCalendarDateRange *) _slotRange
342 {
343   NGCalendarDateRange *aptRange;
344   NSCalendarDate *aptStartDate, *aptEndDate;
345   NSTimeZone *timeZone;
346
347   if (_slotRange == nil)
348     return NO;
349
350   timeZone = [[context activeUser] timeZone];  
351   /* calculate the interval requested by the user (can be larger) */
352
353   aptStartDate = [_slotRange startDate];
354   // TODO: gives warning on MacOSX
355   aptEndDate
356     = [[NSCalendarDate alloc]
357         initWithTimeIntervalSince1970: ([aptStartDate timeIntervalSince1970]
358                                         + [self durationAsTimeInterval])];
359   [aptStartDate setTimeZone: timeZone];
360   [aptEndDate setTimeZone: timeZone];
361   aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate: aptStartDate
362                                   endDate: aptEndDate];
363   [aptEndDate release];
364   aptEndDate = nil;
365   
366   return [self isRangeGreen: aptRange];
367 }
368
369 - (BOOL)isFirstHalfGreen {
370   /* currentday is the date, self->item the hour */
371   NSCalendarDate *from, *to;
372   NGCalendarDateRange *range;
373
374   from  = [self->currentDay hour:[[self item] intValue] minute:0];
375   to    = [self->currentDay hour:[[self item] intValue] minute:30];
376   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
377   return [self isSlotRangeGreen:range];
378 }
379 - (BOOL)isSecondHalfGreen {
380   /* currentday is the date, self->item the hour */
381   NSCalendarDate *from, *to;
382   NGCalendarDateRange *range;
383
384   from  = [self->currentDay hour:[[self item] intValue]     minute:30];
385   to    = [self->currentDay hour:[[self item] intValue] + 1 minute:0];
386   range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
387   return [self isSlotRangeGreen:range];
388 }
389
390 - (BOOL)isFirstHalfBlocked {
391   return [self isFirstHalfGreen] ? NO : YES;
392 }
393 - (BOOL)isSecondHalfBlocked {
394   return [self isSecondHalfGreen] ? NO : YES;
395 }
396
397 /* actions */
398
399 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
400   return YES;
401 }
402
403 - (id<WOActionResults>)defaultAction {
404   NSCalendarDate *now;
405   
406   now = [NSCalendarDate date];
407
408   [self setDuration:@"120"]; /* 1 hour as default */
409   [self setStartDate:[now hour:9 minute:0]];
410   [self setEndDate:[now hour:18 minute:0]];
411   
412   return self;
413 }
414
415 - (id)proposalSearchAction {
416   NSArray        *attendees, *uids, *fbos, *ranges;
417   unsigned       i, count;
418   NSMutableArray *allInfos;
419
420   [self logWithFormat:@"search from %@ to %@", 
421           [self startDate], [self endDate]];
422   
423   attendees = [self attendees];
424   uids      = [[self clientObject] uidsFromICalPersons:attendees];
425   if ([uids count] == 0) {
426     [self logWithFormat:@"Note: no UIDs selected."];
427     return self;
428   }
429
430   fbos     = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
431                                   inContext:[self context]];
432   count    = [fbos count];
433   // wild guess at capacity
434   allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
435
436   for (i = 0; i < count; i++) {
437     SOGoFreeBusyObject *fb;
438     NSArray            *infos;
439     
440     fb    = [fbos objectAtIndex:i];
441     if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
442       infos = [fb fetchFreeBusyInfosFrom:[self startDate] to:[self endDate]];
443       [allInfos addObjectsFromArray:infos];
444     }
445   }
446   [self debugWithFormat:@"  processing: %d infos", [allInfos count]];
447   ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
448                        @"startDate"
449                      andEndDateKey:@"endDate"];
450   ranges = [ranges arrayByCompactingContainedDateRanges];
451   [self debugWithFormat:@"  ranges: %@", ranges];
452   
453   ASSIGNCOPY(self->blockedRanges, ranges);
454   
455   return self;
456 }
457
458 /* URLs */
459
460 - (NSMutableDictionary *)currentQueryParameters {
461   if(!self->currentQueryParameters) {
462     self->currentQueryParameters = [[self queryParameters] mutableCopy];
463     [self->currentQueryParameters setObject:[self duration] forKey:@"dur"];
464     [self setICalPersons:[self resources] asQueryParameter:@"rs"];
465     [self setICalPersons:[self participants] asQueryParameter:@"ps"];
466   }
467   return self->currentQueryParameters;
468 }
469
470 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp {
471   NSMutableString *s;
472   unsigned i, count;
473   
474   s = [[NSMutableString alloc] init];
475   count = [_ps count];
476   for(i = 0; i < count; i++) {
477     iCalPerson *p = [_ps objectAtIndex:i];
478     [s appendString:[p rfc822Email]];
479     if(i != (count - 1))
480       [s appendString:@","];
481   }
482   [[self currentQueryParameters] setObject:s forKey:_qp];
483   [s release];
484 }
485
486 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first {
487   NSMutableDictionary *qp;
488   NSString *hmString;
489   NSCalendarDate *date;
490   unsigned minute;
491
492   minute = _first ? 0 : 30;
493   qp = [self currentQueryParameters];
494
495   date = [self currentDay];
496   hmString = [NSString stringWithFormat:@"%02d%02d",
497     [item intValue], minute];
498   [self setSelectedDateQueryParameter:date inDictionary:qp];
499   [qp setObject:hmString forKey:@"hm"];
500   return qp;
501 }
502
503 - (NSDictionary *)currentFirstHalfQueryParameters {
504   return [self currentHourQueryParametersForFirstHalf:YES];
505 }
506
507 - (NSDictionary *)currentSecondHalfQueryParameters {
508   return [self currentHourQueryParametersForFirstHalf:NO];
509 }
510
511 @end /* UIxTaskProposal */