diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index 99fb10ea4..494f0832e 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -39,6 +39,7 @@ SchedulerUI_OBJC_FILES = \ UIxCalYearOverview.m \ UIxCalInlineMonthOverview.m \ UIxComponentEditor.m \ + UIxFreeBusyUserSelector.m \ UIxAppointmentView.m \ UIxAppointmentEditor.m \ UIxAppointmentProposal.m \ diff --git a/UI/Scheduler/UIxFreeBusyUserSelector.h b/UI/Scheduler/UIxFreeBusyUserSelector.h new file mode 100644 index 000000000..73ff0ce1b --- /dev/null +++ b/UI/Scheduler/UIxFreeBusyUserSelector.h @@ -0,0 +1,62 @@ +/* UIxFreeBusyUserSelector.h - this file is part of SOGo + * + * Copyright (C) 2006 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef UIXFREEBUSYUSERSELECTOR_H +#define UIXFREEBUSYUSERSELECTOR_H + +#import + +@class NSArray; +@class NSMutableArray; +@class NSCalendarDate; +@class NSNumber; +@class SOGoDateFormatter; + +@interface UIxFreeBusyUserSelector : UIxComponent +{ + SOGoDateFormatter *dateFormatter; + NSCalendarDate *startDate; + NSCalendarDate *endDate; + NSNumber *dayStartHour; + NSNumber *dayEndHour; + NSMutableArray *daysToDisplay; + NSMutableArray *hoursToDisplay; + NSCalendarDate *currentDayToDisplay; + NSNumber *currentHourToDisplay; +} + +- (void) setStartDate: (NSCalendarDate *) newStartDate; +- (void) setEndDate: (NSCalendarDate *) newEndDate; + +- (void) setDayStartHour: (NSNumber *) newDayStartHour; +- (void) setDayEndHour: (NSNumber *) newDayEndHour; + +- (NSArray *) daysToDisplay; +- (NSArray *) hoursToDisplay; +- (void) setCurrentDayToDisplay: (NSCalendarDate *) newCurrentDayToDisplay; +- (void) setCurrentHourToDisplay: (NSNumber *) newCurrentHourToDisplay; +- (NSCalendarDate *) currentDayToDisplay; +- (NSNumber *) currentHourToDisplay; + +@end + +#endif /* UIXFREEBUSYUSERSELECTOR_H */ diff --git a/UI/Scheduler/UIxFreeBusyUserSelector.m b/UI/Scheduler/UIxFreeBusyUserSelector.m new file mode 100644 index 000000000..81f6d346f --- /dev/null +++ b/UI/Scheduler/UIxFreeBusyUserSelector.m @@ -0,0 +1,169 @@ +/* UIxFreeBusyUserSelector.m - this file is part of SOGo + * + * Copyright (C) 2006 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import + +#import + +#import + +#import "UIxFreeBusyUserSelector.h" + +@implementation UIxFreeBusyUserSelector + +- (id) init +{ + if ((self = [super init])) + { + startDate = [NSCalendarDate calendarDate]; + [startDate retain]; + endDate = [NSCalendarDate calendarDate]; + [endDate retain]; + dayStartHour = [NSNumber numberWithInt: 8]; + [dayStartHour retain]; + dayEndHour = [NSNumber numberWithInt: 18]; + [dayEndHour retain]; + daysToDisplay = nil; + hoursToDisplay = nil; + dateFormatter = [[SOGoDateFormatter alloc] + initWithLocale: [self locale]]; + } + + return self; +} + +- (void) dealloc +{ + [startDate release]; + [endDate release]; + [dayStartHour release]; + [dayEndHour release]; + if (daysToDisplay) + [daysToDisplay release]; + if (hoursToDisplay) + [hoursToDisplay release]; + [dateFormatter release]; + [super dealloc]; +} + +- (void) setStartDate: (NSCalendarDate *) newStartDate +{ + ASSIGN (startDate, newStartDate); +#warning The following code is hackish and should not be shown to children < 18. + if (daysToDisplay) + { + [daysToDisplay release]; + daysToDisplay = nil; + } +} + +- (void) setEndDate: (NSCalendarDate *) newEndDate +{ + ASSIGN (endDate, newEndDate); + if (daysToDisplay) + { + [daysToDisplay release]; + daysToDisplay = nil; + } +} + +- (void) setDayStartHour: (NSNumber *) newDayStartHour +{ + ASSIGN (dayStartHour, newDayStartHour); +} + +- (void) setDayEndHour: (NSNumber *) newDayEndHour +{ + ASSIGN (dayEndHour, newDayEndHour); +} + +/* in-template operations */ +- (NSArray *) daysToDisplay +{ + NSCalendarDate *currentDay, *finalDay; + + if (!daysToDisplay) + { + daysToDisplay = [NSMutableArray new]; + finalDay = [endDate dateByAddingYears: 0 months: 0 days: 2]; + currentDay = startDate; + [daysToDisplay addObject: currentDay]; + while (![currentDay isDateOnSameDay: finalDay]) + { + currentDay = [currentDay dateByAddingYears: 0 months: 0 days: 1]; + [daysToDisplay addObject: currentDay]; + } + } + + return daysToDisplay; +} + +- (NSArray *) hoursToDisplay +{ + NSNumber *currentHour; + + if (!hoursToDisplay) + { + hoursToDisplay = [NSMutableArray new]; + currentHour = dayStartHour; + [hoursToDisplay addObject: currentHour]; + while (![currentHour isEqual: dayEndHour]) + { + currentHour = [NSNumber numberWithInt: [currentHour intValue] + 1]; + [hoursToDisplay addObject: currentHour]; + } + } + + return hoursToDisplay; +} + +- (void) setCurrentDayToDisplay: (NSCalendarDate *) newCurrentDayToDisplay +{ + currentDayToDisplay = newCurrentDayToDisplay; +} + +- (void) setCurrentHourToDisplay: (NSNumber *) newCurrentHourToDisplay +{ + currentHourToDisplay = newCurrentHourToDisplay; +} + +- (NSCalendarDate *) currentDayToDisplay +{ + return currentDayToDisplay; +} + +- (NSNumber *) currentHourToDisplay +{ + return currentHourToDisplay; +} + +- (NSString *) currentFormattedDay +{ + return [NSString stringWithFormat: @"%@, %.4d-%.2d-%.2d", + [dateFormatter shortDayOfWeek: [currentDayToDisplay dayOfWeek]], + [currentDayToDisplay yearOfCommonEra], + [currentDayToDisplay monthOfYear], + [currentDayToDisplay dayOfMonth]]; +} + +@end diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index 2fb8ca078..8a83383f3 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -1,5 +1,5 @@ { - requires = ( MAIN, CommonUI, Appointments ); + requires = ( MAIN, CommonUI, Appointments, Contacts ); publicResources = ( previous_week.gif, @@ -188,6 +188,11 @@ pageName = "UIxAppointmentEditor"; actionName = "test"; }; + contactSearch = { + protectedBy = "View"; + pageName = "UIxFreeBusyUserSelector"; + actionName = "contactSearch"; + }; }; }; diff --git a/UI/Templates/SchedulerUI/UIxFreeBusyUserSelector.wox b/UI/Templates/SchedulerUI/UIxFreeBusyUserSelector.wox new file mode 100644 index 000000000..a3b55c902 --- /dev/null +++ b/UI/Templates/SchedulerUI/UIxFreeBusyUserSelector.wox @@ -0,0 +1,60 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
00
+
+
diff --git a/UI/WebServerResources/UIxFreeBusyUserSelector.js b/UI/WebServerResources/UIxFreeBusyUserSelector.js new file mode 100644 index 000000000..f13364753 --- /dev/null +++ b/UI/WebServerResources/UIxFreeBusyUserSelector.js @@ -0,0 +1,208 @@ +var resultsDiv; +var searchField; +var running = false; +var address; +var delay = 500; +var requestField; + +function onContactKeyUp(node, event) +{ + if (!running && (event.keyCode == 8 + || event.keyCode == 13 + || event.keyCode == 32 + || event.keyCode > 47)) { + running = true; + requestField = node; + setTimeout("triggerRequest()", delay); + } +} + +function triggerRequest() { + if (document.contactLookupAjaxRequest) { + document.contactLookupAjaxRequest.aborted = yes; + document.contactLookupAjaxRequest.abort(); + } + var urlstr = ( UserFolderURL + "Contacts/contactSearch?search=" + + requestField.value ); + document.contactLookupAjaxRequest = triggerAjaxRequest(urlstr, + updateResults, + requestField); +} + +function updateResults(http) +{ + if (http.readyState == 4) { + if (http.status == 200) { + var searchField = http.callbackData; + var start = searchField.value.length; + var text = http.responseText.split(":"); + if (text[0].length > 0) + searchField.uid = text[0]; + else + searchField.uid = null; + searchField.hasfreebusy = false; + searchField.value = text[1]; + var end = searchField.value.length; + searchField.setSelectionRange(start, end); + } + running = false; + document.contactLookupAjaxRequest = null; + } +} + +function resetFreeBusyZone() +{ + var table = $("attendeesView").childNodesWithTag("div")[0].childNodesWithTag("table")[0]; + var row = table.tHead.rows[2]; + for (var i = 1; i < row.cells.length; i++) + { + var nodes = row.cells[i].childNodesWithTag("span"); + for (var j = 0; j < nodes.length; j++) + nodes[j].removeClassName("busy"); + } +} + +function redisplayFreeBusyZone() +{ + var table = $("attendeesView").childNodesWithTag("div")[0].childNodesWithTag("table")[0]; + var row = table.tHead.rows[2]; + var stHour = parseInt(document.forms['editform']["startTime_time_hour"].value); + var stMinute + = parseInt(document.forms['editform']["startTime_time_minute"].value) / 15; + var etHour = parseInt(document.forms['editform']["endTime_time_hour"].value); + var etMinute + = parseInt(document.forms['editform']["endTime_time_minute"].value) / 15; + if (stHour < 8) { + stHour = 8; + stMinute = 0; + } + if (stHour > 18) { + stHour = 18; + stMinute = 0; + } + if (etHour < 8) { + etHour = 8; + etMinute = 0; + } + if (etHour > 18) { + etHour = 18; + etMinute = 0; + } + if (stHour > etHour) { + var swap = etHour; + etHour = stHour; + stHour = swap; + swap = etMinute; + etMinute = stMinute; + stMinute = etMinute; + } + + var deltaCells = (etHour - stHour); + var deltaSpans = (deltaCells * 4 ) + (etMinute - stMinute); + var currentCellNbr = stHour - 7; + var currentCell = row.cells[currentCellNbr]; + var currentSpanNbr = stMinute; + var spans = currentCell.childNodesWithTag("span"); + resetFreeBusyZone(); + while (deltaSpans > 0) { + var currentSpan = spans[currentSpanNbr]; + currentSpan.addClassName("busy"); + currentSpanNbr++; + if (currentSpanNbr > 3) { + currentSpanNbr = 0; + currentCellNbr++; + currentCell = row.cells[currentCellNbr]; + spans = currentCell.childNodesWithTag("span"); + } + deltaSpans--; + } +} + +function newAttendee(node) +{ + var table = $("attendeesView").childNodesWithTag("div")[0].childNodesWithTag("table")[0]; + var tbody = table.childNodesWithTag("tbody")[0]; + var model = tbody.rows[tbody.rows.length - 1]; + var newAttendeeRow = tbody.rows[tbody.rows.length - 2] + var newRow = model.cloneNode(true); + newRow.setAttribute("class", ""); + tbody.insertBefore(newRow, newAttendeeRow); + newRow.childNodesWithTag("td")[0].childNodesWithTag("input")[0].focus(); +} + +function checkAttendee(node) +{ + var th = node.parentNode.parentNode; + var tbody = th.parentNode; + if (node.value.trim().length == 0) + tbody.removeChild(th); + else if (!node.hasfreebusy) { + displayFreeBusyForNode(node); + node.hasfreebusy = true; + } +} + +function displayFreeBusyForNode(node) +{ + if (node.uid) { + var nodes = node.parentNode.parentNode.cells; + for (var i = 1; i < nodes.length; i++) { + nodes[i].removeClassName("noFreeBusy"); + nodes[i].innerHTML = ('' + + '' + + '' + + ''); + } + if (document.contactFreeBusyAjaxRequest) { + document.contactFreeBusyAjaxRequest.aborted = true; + document.contactFreeBusyAjaxRequest.abort(); + } + var sd = startDayAsShortString(); + var ed = endDayAsShortString(); + var urlstr = ( UserFolderURL + "../" + node.uid + "/freebusy.ifb/ajaxRead?" + + "sday=" + sd + "&eday=" + ed); + document.contactFreeBusyAjaxRequest = triggerAjaxRequest(urlstr, + updateFreeBusyData, + node); + } else { + var nodes = node.parentNode.parentNode.cells; + for (var i = 1; i < nodes.length; i++) { + nodes[i].addClassName("noFreeBusy"); + nodes[i].innerHTML = ''; + } + } +} + +function setSlot(tds, nbr, status) { + var tdnbr = Math.floor(nbr / 4); + var spannbr = nbr - (tdnbr * 4); + var days = 0; + if (tdnbr > 24) { + days = tdnbr / 24; + tdnbr -= (days * 24); + } + log("td: " + tdnbr); + if (tdnbr > 7 && tdnbr < 19) { + log("length: " + tds.length); + log("td: " + days * 12 + tdnbr - 8); + var td = tds[days * 12 + tdnbr - 7]; + var spans = td.childNodesWithTag("span"); + spans[spannbr].addClassName("busy"); + } +} + +function updateFreeBusyData(http) +{ + if (http.readyState == 4) { + if (http.status == 200) { + var node = http.callbackData; + var slots = http.responseText.split(","); + var tds = node.parentNode.parentNode.cells; + for (var i = 0; i < slots.length; i++) { + if (slots[i] != '0') + setSlot(tds, i, slots[i]); + } + } + document.contactFreeBusyAjaxRequest = null; + } +}