mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-08-19 21:53:19 +00:00
Monotone-Parent: 58a952b10d4574c10176dc598afd887a5e7d2ad2
Monotone-Revision: 01f82c6cdad306ebc5d1e92bf6efe32e51a0d96d Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2006-10-25T23:58:29 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -39,6 +39,7 @@ SchedulerUI_OBJC_FILES = \
|
||||
UIxCalYearOverview.m \
|
||||
UIxCalInlineMonthOverview.m \
|
||||
UIxComponentEditor.m \
|
||||
UIxFreeBusyUserSelector.m \
|
||||
UIxAppointmentView.m \
|
||||
UIxAppointmentEditor.m \
|
||||
UIxAppointmentProposal.m \
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* UIxFreeBusyUserSelector.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse groupe conseil
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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 <SOGoUI/UIxComponent.h>
|
||||
|
||||
@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 */
|
||||
@@ -0,0 +1,169 @@
|
||||
/* UIxFreeBusyUserSelector.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006 Inverse groupe conseil
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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 <Foundation/NSArray.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGExtensions/NSCalendarDate+misc.h>
|
||||
|
||||
#import <SOGoUI/SOGoDateFormatter.h>
|
||||
|
||||
#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
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<container
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:var="http://www.skyrix.com/od/binding"
|
||||
xmlns:const="http://www.skyrix.com/od/constant"
|
||||
xmlns:uix="OGo:uix"
|
||||
xmlns:rsrc="OGo:url"
|
||||
xmlns:label="OGo:label">
|
||||
<script type="text/javascript" rsrc:src="UIxFreeBusyUserSelector.js"><!-- space --></script>
|
||||
<div class="freeBusyView">
|
||||
<table class="freeBusy">
|
||||
<thead>
|
||||
<tr class="freeBusyHeader1">
|
||||
<th class="attendees"></th>
|
||||
<var:foreach list="daysToDisplay" item="currentDayToDisplay"
|
||||
><th colspan="11"><var:string value="currentFormattedDay" /></th
|
||||
></var:foreach>
|
||||
</tr>
|
||||
<tr class="freeBusyHeader2">
|
||||
<th class="attendees"></th>
|
||||
<var:foreach list="daysToDisplay" item="currentDayToDisplay"
|
||||
><var:foreach list="hoursToDisplay" item="currentHourToDisplay"
|
||||
><th><var:string value="currentHourToDisplay" const:numberformat="00:"/>00</th
|
||||
></var:foreach
|
||||
></var:foreach>
|
||||
</tr>
|
||||
<tr class="freeBusyHeader3">
|
||||
<th class="attendees"></th>
|
||||
<var:foreach list="daysToDisplay" item="currentDayToDisplay"
|
||||
><var:foreach list="hoursToDisplay" item="currentHourToDisplay"
|
||||
><th><span class="freeBusyZoneElement"><!-- space --></span><span class="freeBusyZoneElement"><!-- space --></span><span class="freeBusyZoneElement"><!-- space --></span><span class="freeBusyZoneElement"><!-- space --></span></th
|
||||
></var:foreach
|
||||
></var:foreach>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="futureAttendee">
|
||||
<td class="attendees"><input type="text" class="textField"
|
||||
readonly="readonly"
|
||||
onclick="newAttendee(this);" /></td>
|
||||
<var:foreach list="daysToDisplay" item="currentDayToDisplay"
|
||||
><var:foreach list="hoursToDisplay" item="currentHourToDisplay"
|
||||
><td></td
|
||||
></var:foreach
|
||||
></var:foreach>
|
||||
</tr>
|
||||
<tr class="attendeeModel">
|
||||
<td class="attendees"><input type="text" class="textField"
|
||||
onkeyup="onContactKeyUp(this, event);"
|
||||
onblur="checkAttendee(this);" /></td>
|
||||
<var:foreach list="daysToDisplay" item="currentDayToDisplay"
|
||||
><var:foreach list="hoursToDisplay" item="currentHourToDisplay"
|
||||
><td></td
|
||||
></var:foreach
|
||||
></var:foreach>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</container>
|
||||
@@ -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 = ('<span class="freeBusyZoneElement"></span>'
|
||||
+ '<span class="freeBusyZoneElement"></span>'
|
||||
+ '<span class="freeBusyZoneElement"></span>'
|
||||
+ '<span class="freeBusyZoneElement"></span>');
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user