2 Copyright (C) 2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #import <Foundation/NSCalendarDate.h>
23 #import <Foundation/NSNull.h>
24 #import <Foundation/NSValue.h>
26 #import <NGExtensions/NGCalendarDateRange.h>
27 #import <NGExtensions/NSCalendarDate+misc.h>
28 #import <NGCards/iCalPerson.h>
30 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
31 #import <SoObjects/Appointments/SOGoFreeBusyObject.h>
32 #import <SOGoUI/UIxComponent.h>
34 @interface UIxAppointmentProposal : UIxComponent
39 /* individual values */
52 NSArray *participants; /* array of iCalPerson's */
53 NSArray *resources; /* array of iCalPerson's */
56 NSArray *blockedRanges;
57 NSMutableDictionary *currentQueryParameters;
60 - (NSMutableDictionary *)currentQueryParameters;
61 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first;
63 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp;
66 @implementation UIxAppointmentProposal
69 [self->blockedRanges release];
71 [self->startDateHour release];
72 [self->startDateMinute release];
73 [self->startDateDay release];
74 [self->startDateMonth release];
75 [self->startDateYear release];
77 [self->endDateHour release];
78 [self->endDateMinute release];
79 [self->endDateDay release];
80 [self->endDateMonth release];
81 [self->endDateYear release];
82 [self->duration release];
84 [self->participants release];
85 [self->resources release];
87 [self->currentQueryParameters release];
94 [self->currentDay release]; self->currentDay = nil;
95 [self->item release]; self->item = nil;
101 - (void)setItem:(id)_item {
102 ASSIGN(self->item, _item);
108 - (void)setStartDateHour:(id)_startDateHour {
109 ASSIGN(self->startDateHour, _startDateHour);
111 - (id)startDateHour {
112 return self->startDateHour;
114 - (void)setStartDateMinute:(id)_startDateMinute {
115 ASSIGN(self->startDateMinute, _startDateMinute);
117 - (id)startDateMinute {
118 return self->startDateMinute;
120 - (void)setStartDateDay:(id)_startDateDay {
121 ASSIGN(self->startDateDay, _startDateDay);
124 return self->startDateDay;
126 - (void)setStartDateMonth:(id)_startDateMonth {
127 ASSIGN(self->startDateMonth, _startDateMonth);
129 - (id)startDateMonth {
130 return self->startDateMonth;
132 - (void)setStartDateYear:(id)_startDateYear {
133 ASSIGN(self->startDateYear, _startDateYear);
135 - (id)startDateYear {
136 return self->startDateYear;
138 - (void)setEndDateHour:(id)_endDateHour {
139 ASSIGN(self->endDateHour, _endDateHour);
142 return self->endDateHour;
144 - (void)setEndDateMinute:(id)_endDateMinute {
145 ASSIGN(self->endDateMinute, _endDateMinute);
147 - (id)endDateMinute {
148 return self->endDateMinute;
150 - (void)setEndDateDay:(id)_endDateDay {
151 ASSIGN(self->endDateDay, _endDateDay);
154 return self->endDateDay;
156 - (void)setEndDateMonth:(id)_endDateMonth {
157 ASSIGN(self->endDateMonth, _endDateMonth);
160 return self->endDateMonth;
162 - (void)setEndDateYear:(id)_endDateYear {
163 ASSIGN(self->endDateYear, _endDateYear);
166 return self->endDateYear;
169 - (void)setStartDate:(NSCalendarDate *)_date {
170 [self setStartDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
171 [self setStartDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
172 [self setStartDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
173 [self setStartDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
174 [self setStartDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
177 - (NSCalendarDate *) startDate
179 NSTimeZone *timeZone;
181 timeZone = [[context activeUser] timeZone];
183 return [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
184 month:[[self startDateMonth] intValue]
185 day:[[self startDateDay] intValue]
186 hour:[[self startDateHour] intValue]
187 minute:[[self startDateMinute] intValue]
192 - (void) setEndDate: (NSCalendarDate *) _date
194 [self setEndDateHour:[NSNumber numberWithInt:[_date hourOfDay]]];
195 [self setEndDateMinute:[NSNumber numberWithInt:[_date minuteOfHour]]];
196 [self setEndDateDay:[NSNumber numberWithInt:[_date dayOfMonth]]];
197 [self setEndDateMonth:[NSNumber numberWithInt:[_date monthOfYear]]];
198 [self setEndDateYear:[NSNumber numberWithInt:[_date yearOfCommonEra]]];
201 - (NSCalendarDate *) endDate
203 NSTimeZone *timeZone;
205 timeZone = [[context activeUser] timeZone];
207 return [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
208 month:[[self endDateMonth] intValue]
209 day:[[self endDateDay] intValue]
210 hour:[[self endDateHour] intValue]
211 minute:[[self endDateMinute] intValue]
216 - (void)setDuration:(id)_duration {
217 ASSIGN(self->duration, _duration);
220 return self->duration;
222 - (int)durationInMinutes {
223 return [[self duration] intValue];
225 - (NSTimeInterval)durationAsTimeInterval {
226 return [self durationInMinutes] * 60;
229 - (NSString *)itemDurationText {
230 // TODO: use a formatter
232 switch ([[self item] intValue]) {
233 case 30: return @"30 minutes";
234 case 60: return @"1 hour";
235 case 120: return @"2 hours";
236 case 240: return @"4 hours";
237 case 480: return @"8 hours";
239 return [NSString stringWithFormat:@"%@ minutes", [self item]];
243 - (void)setParticipants:(NSArray *)_parts {
244 ASSIGN(self->participants, _parts);
246 - (NSArray *)participants {
247 return self->participants;
249 - (void)setResources:(NSArray *)_res {
250 ASSIGN(self->resources, _res);
252 - (NSArray *)resources {
253 return self->resources;
256 - (NSArray *)attendees {
259 a = [self participants];
260 b = [self resources];
261 if ([b count] == 0) return a;
262 if ([a count] == 0) return b;
263 return [a arrayByAddingObjectsFromArray:b];
266 - (void)setCurrentDay:(id)_day {
267 ASSIGN(self->currentDay, _day);
270 return self->currentDay;
274 // TODO: from 'earliest start' to 'latest endtime'
275 unsigned lStartHour = 9, lEndHour = 17, i;
278 lStartHour = [[self startDateHour] intValue];
279 lEndHour = [[self endDateHour] intValue];
280 if (lStartHour < 1) lStartHour = 1;
281 if (lEndHour < lStartHour) lEndHour = lStartHour + 1;
283 ma = [NSMutableArray arrayWithCapacity:lEndHour - lStartHour + 2];
284 for (i = lStartHour; i <= lEndHour; i++)
285 [ma addObject:[NSNumber numberWithInt:i]];
290 // TODO: from startdate to enddate
292 NSCalendarDate *base, *stop, *current;
293 NSTimeZone *timeZone;
295 timeZone = [[context activeUser] timeZone];
296 base = [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
297 month:[[self startDateMonth] intValue]
298 day:[[self startDateDay] intValue]
299 hour:12 minute:0 second:0
301 stop = [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
302 month:[[self endDateMonth] intValue]
303 day:[[self endDateDay] intValue]
304 hour:12 minute:0 second:0
306 ma = [NSMutableArray arrayWithCapacity: 16];
309 while ([current compare:stop] != NSOrderedDescending)
311 [current setTimeZone: timeZone];
312 [ma addObject:current];
314 /* Note: remember the timezone behaviour of the method below! */
315 current = [current dateByAddingYears:0 months:0 days:1];
321 - (NSArray *)durationSteps {
322 // TODO: make configurable
323 return [NSArray arrayWithObjects:
324 @"30", @"60", @"120", @"240", @"480", nil];
329 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
332 idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
333 if (idx != NSNotFound) {
334 [self debugWithFormat:@"blocked range:\n range: %@\n block: %@\nintersection:%@",
336 [self->blockedRanges objectAtIndex:idx],
337 [_range intersectionDateRange:[self->blockedRanges objectAtIndex:idx]]];
340 return idx == NSNotFound ? YES : NO;
343 - (BOOL)isSlotRangeGreen:(NGCalendarDateRange *)_slotRange {
344 NGCalendarDateRange *aptRange;
345 NSCalendarDate *aptStartDate, *aptEndDate;
346 NSTimeZone *timeZone;
348 if (_slotRange == nil)
351 /* calculate the interval requested by the user (can be larger) */
353 aptStartDate = [_slotRange startDate];
354 // TODO: gives warning on MacOSX
355 aptEndDate = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
356 [aptStartDate timeIntervalSince1970]
357 + [self durationAsTimeInterval]];
358 timeZone = [[context activeUser] timeZone];
359 [aptStartDate setTimeZone: timeZone];
360 [aptEndDate setTimeZone: timeZone];
361 aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate:aptStartDate
363 [aptEndDate release]; aptEndDate = nil;
365 return [self isRangeGreen:aptRange];
368 - (BOOL)isFirstHalfGreen {
369 /* currentday is the date, self->item the hour */
370 NSCalendarDate *from, *to;
371 NGCalendarDateRange *range;
373 from = [self->currentDay hour:[[self item] intValue] minute:0];
374 to = [self->currentDay hour:[[self item] intValue] minute:30];
375 range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
376 return [self isSlotRangeGreen:range];
378 - (BOOL)isSecondHalfGreen {
379 /* currentday is the date, self->item the hour */
380 NSCalendarDate *from, *to;
381 NGCalendarDateRange *range;
383 from = [self->currentDay hour:[[self item] intValue] minute:30];
384 to = [self->currentDay hour:[[self item] intValue] + 1 minute:0];
385 range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
386 return [self isSlotRangeGreen:range];
389 - (BOOL)isFirstHalfBlocked {
390 return [self isFirstHalfGreen] ? NO : YES;
392 - (BOOL)isSecondHalfBlocked {
393 return [self isSecondHalfGreen] ? NO : YES;
398 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
402 - (id<WOActionResults>)defaultAction {
405 now = [NSCalendarDate date];
407 [self setDuration:@"120"]; /* 1 hour as default */
408 [self setStartDate:[now hour:9 minute:0]];
409 [self setEndDate:[now hour:18 minute:0]];
414 - (id)proposalSearchAction {
415 NSArray *attendees, *uids, *fbos, *ranges;
417 NSMutableArray *allInfos;
419 [self logWithFormat:@"search from %@ to %@",
420 [self startDate], [self endDate]];
422 attendees = [self attendees];
423 uids = [[self clientObject] uidsFromICalPersons:attendees];
424 if ([uids count] == 0) {
425 [self logWithFormat:@"Note: no UIDs selected."];
429 fbos = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
430 inContext:[self context]];
431 count = [fbos count];
432 // wild guess at capacity
433 allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
435 for (i = 0; i < count; i++) {
436 SOGoFreeBusyObject *fb;
439 fb = [fbos objectAtIndex:i];
440 if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
441 infos = [fb fetchFreeBusyInfosFrom:[self startDate] to:[self endDate]];
442 [allInfos addObjectsFromArray:infos];
445 [self debugWithFormat:@" processing: %d infos", [allInfos count]];
446 ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
448 andEndDateKey:@"endDate"];
449 ranges = [ranges arrayByCompactingContainedDateRanges];
450 [self debugWithFormat:@" ranges: %@", ranges];
452 ASSIGNCOPY(self->blockedRanges, ranges);
459 - (NSMutableDictionary *)currentQueryParameters {
460 if(!self->currentQueryParameters) {
461 self->currentQueryParameters = [[self queryParameters] mutableCopy];
462 [self->currentQueryParameters setObject:[self duration] forKey:@"dur"];
463 [self setICalPersons:[self resources] asQueryParameter:@"rs"];
464 [self setICalPersons:[self participants] asQueryParameter:@"ps"];
466 return self->currentQueryParameters;
469 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp {
473 s = [[NSMutableString alloc] init];
475 for(i = 0; i < count; i++) {
476 iCalPerson *p = [_ps objectAtIndex:i];
477 [s appendString:[p rfc822Email]];
479 [s appendString:@","];
481 [[self currentQueryParameters] setObject:s forKey:_qp];
485 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first {
486 NSMutableDictionary *qp;
488 NSCalendarDate *date;
491 minute = _first ? 0 : 30;
492 qp = [self currentQueryParameters];
494 date = [self currentDay];
495 hmString = [NSString stringWithFormat:@"%02d%02d",
496 [item intValue], minute];
497 [self setSelectedDateQueryParameter:date inDictionary:qp];
498 [qp setObject:hmString forKey:@"hm"];
502 - (NSDictionary *)currentFirstHalfQueryParameters {
503 return [self currentHourQueryParametersForFirstHalf:YES];
506 - (NSDictionary *)currentSecondHalfQueryParameters {
507 return [self currentHourQueryParametersForFirstHalf:NO];
510 @end /* UIxAppointmentProposal */