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
21 // $Id: UIxAppointmentEditor.m 181 2004-08-11 15:13:25Z helge $
23 #include <SOGoUI/UIxComponent.h>
25 @interface UIxAppointmentProposal : UIxComponent
30 /* individual values */
43 NSArray *participants; /* array of iCalPerson's */
44 NSArray *resources; /* array of iCalPerson's */
47 NSArray *blockedRanges;
48 NSMutableDictionary *currentQueryParameters;
51 - (NSMutableDictionary *)currentQueryParameters;
52 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first;
54 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp;
57 #include <SoObjects/Appointments/SOGoAppointmentFolder.h>
58 #include <SoObjects/Appointments/SOGoFreeBusyObject.h>
59 #include <NGExtensions/NGCalendarDateRange.h>
60 #include <NGCards/NGCards.h>
63 @implementation UIxAppointmentProposal
66 [self->blockedRanges release];
68 [self->startDateHour release];
69 [self->startDateMinute release];
70 [self->startDateDay release];
71 [self->startDateMonth release];
72 [self->startDateYear release];
74 [self->endDateHour release];
75 [self->endDateMinute release];
76 [self->endDateDay release];
77 [self->endDateMonth release];
78 [self->endDateYear release];
79 [self->duration release];
81 [self->participants release];
82 [self->resources release];
84 [self->currentQueryParameters release];
91 [self->currentDay release]; self->currentDay = nil;
92 [self->item release]; self->item = nil;
98 - (void)setItem:(id)_item {
99 ASSIGN(self->item, _item);
105 - (void)setStartDateHour:(id)_startDateHour {
106 ASSIGN(self->startDateHour, _startDateHour);
108 - (id)startDateHour {
109 return self->startDateHour;
111 - (void)setStartDateMinute:(id)_startDateMinute {
112 ASSIGN(self->startDateMinute, _startDateMinute);
114 - (id)startDateMinute {
115 return self->startDateMinute;
117 - (void)setStartDateDay:(id)_startDateDay {
118 ASSIGN(self->startDateDay, _startDateDay);
121 return self->startDateDay;
123 - (void)setStartDateMonth:(id)_startDateMonth {
124 ASSIGN(self->startDateMonth, _startDateMonth);
126 - (id)startDateMonth {
127 return self->startDateMonth;
129 - (void)setStartDateYear:(id)_startDateYear {
130 ASSIGN(self->startDateYear, _startDateYear);
132 - (id)startDateYear {
133 return self->startDateYear;
135 - (void)setEndDateHour:(id)_endDateHour {
136 ASSIGN(self->endDateHour, _endDateHour);
139 return self->endDateHour;
141 - (void)setEndDateMinute:(id)_endDateMinute {
142 ASSIGN(self->endDateMinute, _endDateMinute);
144 - (id)endDateMinute {
145 return self->endDateMinute;
147 - (void)setEndDateDay:(id)_endDateDay {
148 ASSIGN(self->endDateDay, _endDateDay);
151 return self->endDateDay;
153 - (void)setEndDateMonth:(id)_endDateMonth {
154 ASSIGN(self->endDateMonth, _endDateMonth);
157 return self->endDateMonth;
159 - (void)setEndDateYear:(id)_endDateYear {
160 ASSIGN(self->endDateYear, _endDateYear);
163 return self->endDateYear;
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]]];
174 - (NSCalendarDate *) startDate
176 NSTimeZone *timeZone;
178 timeZone = [[context activeUser] timeZone];
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]
189 - (void) setEndDate: (NSCalendarDate *) _date
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]]];
198 - (NSCalendarDate *) endDate
200 NSTimeZone *timeZone;
202 timeZone = [[context activeUser] timeZone];
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]
213 - (void)setDuration:(id)_duration {
214 ASSIGN(self->duration, _duration);
217 return self->duration;
219 - (int)durationInMinutes {
220 return [[self duration] intValue];
222 - (NSTimeInterval)durationAsTimeInterval {
223 return [self durationInMinutes] * 60;
226 - (NSString *)itemDurationText {
227 // TODO: use a formatter
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";
236 return [NSString stringWithFormat:@"%@ minutes", [self item]];
240 - (void)setParticipants:(NSArray *)_parts {
241 ASSIGN(self->participants, _parts);
243 - (NSArray *)participants {
244 return self->participants;
246 - (void)setResources:(NSArray *)_res {
247 ASSIGN(self->resources, _res);
249 - (NSArray *)resources {
250 return self->resources;
253 - (NSArray *)attendees {
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];
263 - (void)setCurrentDay:(id)_day {
264 ASSIGN(self->currentDay, _day);
267 return self->currentDay;
271 // TODO: from 'earliest start' to 'latest endtime'
272 unsigned lStartHour = 9, lEndHour = 17, i;
275 lStartHour = [[self startDateHour] intValue];
276 lEndHour = [[self endDateHour] intValue];
277 if (lStartHour < 1) lStartHour = 1;
278 if (lEndHour < lStartHour) lEndHour = lStartHour + 1;
280 ma = [NSMutableArray arrayWithCapacity:lEndHour - lStartHour + 2];
281 for (i = lStartHour; i <= lEndHour; i++)
282 [ma addObject:[NSNumber numberWithInt:i]];
287 // TODO: from startdate to enddate
289 NSCalendarDate *base, *stop, *current;
290 NSTimeZone *timeZone;
292 timeZone = [[context activeUser] timeZone];
293 base = [NSCalendarDate dateWithYear:[[self startDateYear] intValue]
294 month:[[self startDateMonth] intValue]
295 day:[[self startDateDay] intValue]
296 hour:12 minute:0 second:0
298 stop = [NSCalendarDate dateWithYear:[[self endDateYear] intValue]
299 month:[[self endDateMonth] intValue]
300 day:[[self endDateDay] intValue]
301 hour:12 minute:0 second:0
303 ma = [NSMutableArray arrayWithCapacity: 16];
306 while ([current compare:stop] != NSOrderedDescending)
308 [current setTimeZone: timeZone];
309 [ma addObject:current];
311 /* Note: remember the timezone behaviour of the method below! */
312 current = [current dateByAddingYears:0 months:0 days:1];
318 - (NSArray *)durationSteps {
319 // TODO: make configurable
320 return [NSArray arrayWithObjects:
321 @"30", @"60", @"120", @"240", @"480", nil];
326 - (BOOL)isRangeGreen:(NGCalendarDateRange *)_range {
329 idx = [self->blockedRanges indexOfFirstIntersectingDateRange:_range];
330 if (idx != NSNotFound) {
331 [self debugWithFormat:@"blocked range:\n range: %@\n block: %@\nintersection:%@",
333 [self->blockedRanges objectAtIndex:idx],
334 [_range intersectionDateRange:[self->blockedRanges objectAtIndex:idx]]];
337 return idx == NSNotFound ? YES : NO;
340 - (BOOL)isSlotRangeGreen:(NGCalendarDateRange *)_slotRange {
341 NGCalendarDateRange *aptRange;
342 NSCalendarDate *aptStartDate, *aptEndDate;
343 NSTimeZone *timeZone;
345 if (_slotRange == nil)
348 /* calculate the interval requested by the user (can be larger) */
350 aptStartDate = [_slotRange startDate];
351 // TODO: gives warning on MacOSX
352 aptEndDate = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
353 [aptStartDate timeIntervalSince1970]
354 + [self durationAsTimeInterval]];
355 timeZone = [[context activeUser] timeZone];
356 [aptStartDate setTimeZone: timeZone];
357 [aptEndDate setTimeZone: timeZone];
358 aptRange = [NGCalendarDateRange calendarDateRangeWithStartDate:aptStartDate
360 [aptEndDate release]; aptEndDate = nil;
362 return [self isRangeGreen:aptRange];
365 - (BOOL)isFirstHalfGreen {
366 /* currentday is the date, self->item the hour */
367 NSCalendarDate *from, *to;
368 NGCalendarDateRange *range;
370 from = [self->currentDay hour:[[self item] intValue] minute:0];
371 to = [self->currentDay hour:[[self item] intValue] minute:30];
372 range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
373 return [self isSlotRangeGreen:range];
375 - (BOOL)isSecondHalfGreen {
376 /* currentday is the date, self->item the hour */
377 NSCalendarDate *from, *to;
378 NGCalendarDateRange *range;
380 from = [self->currentDay hour:[[self item] intValue] minute:30];
381 to = [self->currentDay hour:[[self item] intValue] + 1 minute:0];
382 range = [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
383 return [self isSlotRangeGreen:range];
386 - (BOOL)isFirstHalfBlocked {
387 return [self isFirstHalfGreen] ? NO : YES;
389 - (BOOL)isSecondHalfBlocked {
390 return [self isSecondHalfGreen] ? NO : YES;
395 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
399 - (id<WOActionResults>)defaultAction {
402 now = [NSCalendarDate date];
404 [self setDuration:@"120"]; /* 1 hour as default */
405 [self setStartDate:[now hour:9 minute:0]];
406 [self setEndDate:[now hour:18 minute:0]];
411 - (id)proposalSearchAction {
412 NSArray *attendees, *uids, *fbos, *ranges;
414 NSMutableArray *allInfos;
416 [self logWithFormat:@"search from %@ to %@",
417 [self startDate], [self endDate]];
419 attendees = [self attendees];
420 uids = [[self clientObject] uidsFromICalPersons:attendees];
421 if ([uids count] == 0) {
422 [self logWithFormat:@"Note: no UIDs selected."];
426 fbos = [[self clientObject] lookupFreeBusyObjectsForUIDs:uids
427 inContext:[self context]];
428 count = [fbos count];
429 // wild guess at capacity
430 allInfos = [[[NSMutableArray alloc] initWithCapacity:count * 10] autorelease];
432 for (i = 0; i < count; i++) {
433 SOGoFreeBusyObject *fb;
436 fb = [fbos objectAtIndex:i];
437 if (fb != (SOGoFreeBusyObject *)[NSNull null]) {
438 infos = [fb fetchFreeBusyInfosFrom:[self startDate] to:[self endDate]];
439 [allInfos addObjectsFromArray:infos];
442 [self debugWithFormat:@" processing: %d infos", [allInfos count]];
443 ranges = [allInfos arrayByCreatingDateRangesFromObjectsWithStartDateKey:
445 andEndDateKey:@"endDate"];
446 ranges = [ranges arrayByCompactingContainedDateRanges];
447 [self debugWithFormat:@" ranges: %@", ranges];
449 ASSIGNCOPY(self->blockedRanges, ranges);
456 - (NSMutableDictionary *)currentQueryParameters {
457 if(!self->currentQueryParameters) {
458 self->currentQueryParameters = [[self queryParameters] mutableCopy];
459 [self->currentQueryParameters setObject:[self duration] forKey:@"dur"];
460 [self setICalPersons:[self resources] asQueryParameter:@"rs"];
461 [self setICalPersons:[self participants] asQueryParameter:@"ps"];
463 return self->currentQueryParameters;
466 - (void)setICalPersons:(NSArray *)_ps asQueryParameter:(NSString *)_qp {
470 s = [[NSMutableString alloc] init];
472 for(i = 0; i < count; i++) {
473 iCalPerson *p = [_ps objectAtIndex:i];
474 [s appendString:[p rfc822Email]];
476 [s appendString:@","];
478 [[self currentQueryParameters] setObject:s forKey:_qp];
482 - (NSDictionary *)currentHourQueryParametersForFirstHalf:(BOOL)_first {
483 NSMutableDictionary *qp;
485 NSCalendarDate *date;
488 minute = _first ? 0 : 30;
489 qp = [self currentQueryParameters];
491 date = [self currentDay];
492 hmString = [NSString stringWithFormat:@"%02d%02d",
493 [item intValue], minute];
494 [self setSelectedDateQueryParameter:date inDictionary:qp];
495 [qp setObject:hmString forKey:@"hm"];
499 - (NSDictionary *)currentFirstHalfQueryParameters {
500 return [self currentHourQueryParametersForFirstHalf:YES];
503 - (NSDictionary *)currentSecondHalfQueryParameters {
504 return [self currentHourQueryParametersForFirstHalf:NO];
507 @end /* UIxAppointmentProposal */