Prevent Invitations and whitelist GUI

pull/45/head
Alexandre Cloutier 2014-07-04 09:51:41 -04:00
parent 1cc93c700a
commit 8ded5a8aaf
8 changed files with 1264 additions and 855 deletions

View File

@ -412,6 +412,73 @@
withType: @"calendar:invitation-update"];
}
// This methods scans the list of attendees.
- (NSException *) _handleAttendeeAvalability: (NSArray *) theAttendees
forEvent: (iCalEvent *) theEvent
{
iCalPerson *currentAttendee;
NSMutableArray *attendees, *unavailableAttendees;
NSEnumerator *enumerator;
NSString *currentUID, *buffer;
NSMutableString *reason;
NSDictionary *values;
NSMutableDictionary *value;
SOGoUser *user, *currentUser, *ownerUser;
NSException *e;
int count = 0, i = 0;
// Build a list of the attendees uids without the ressources
attendees = [NSMutableArray arrayWithCapacity: [theAttendees count]];
unavailableAttendees = [[NSMutableArray alloc] init];
enumerator = [theAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
if (currentUID)
{
user = [SOGoUser userWithLogin: currentUID];
if (![user isResource])
{
// Check if the user can be invited to an event.
if ([[user userSettings] objectForKey:@"PreventInvitations"])
{
values = [NSDictionary dictionaryWithObject:[user cn] forKey:@"Cn"];
[unavailableAttendees addObject:values];
}
}
}
}
count = [unavailableAttendees count];
if (count > 0)
{
if (count > 1)
{
reason = [NSMutableString stringWithString:[self labelForKey: @"These persons cannot be invited :"]];
for (i = 0; i < count; i++)
{
value = [unavailableAttendees objectAtIndex:i];
[reason appendString:[value keysWithFormat: @"\n %{Cn}"]];
if (!(i == (count - 1)))
{
[reason appendString:@" &"];
}
}
}
else
{
value = [unavailableAttendees objectAtIndex:0];
reason = [self labelForKey: @"This person cannot be invited:"];
[reason appendString:[value keysWithFormat: @"\n %{Cn}"]];
}
[unavailableAttendees release];
return [NSException exceptionWithHTTPStatus:403 reason: reason];
}
else {
[unavailableAttendees release];
return nil;
}
}
//
// This methods scans the list of attendees. If they are
// considered as resource, it checks for conflicting
@ -435,172 +502,172 @@
NSEnumerator *enumerator;
NSString *currentUID;
SOGoUser *user, *currentUser, *ownerUser;
// Build a list of the attendees uids
attendees = [NSMutableArray arrayWithCapacity: [theAttendees count]];
enumerator = [theAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
if (currentUID)
{
currentUID = [currentAttendee uid];
if (currentUID)
{
[attendees addObject: currentUID];
}
[attendees addObject: currentUID];
}
}
// If the active user is not the owner of the calendar, check possible conflict when
// the owner is a resource
currentUser = [context activeUser];
if (!activeUserIsOwner && ![currentUser isSuperUser])
{
[attendees addObject: owner];
}
{
[attendees addObject: owner];
}
enumerator = [attendees objectEnumerator];
while ((currentUID = [enumerator nextObject]))
{
{
user = [SOGoUser userWithLogin: currentUID];
if ([user isResource])
{
SOGoAppointmentFolder *folder;
NSCalendarDate *start, *end;
NGCalendarDateRange *range;
NSMutableArray *fbInfo;
NSArray *allOccurences;
BOOL must_delete;
int i, j;
// We get the start/end date for our conflict range. If the event to be added is recurring, we
// check for at least a year to start with.
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
folder = [user personalCalendarFolderInContext: context];
// Deny access to the resource if the ACLs don't allow the user
if (![folder aclSQLListingFilter])
{
NSDictionary *values;
NSString *reason;
values = [NSDictionary dictionaryWithObjectsAndKeys:
[user cn], @"Cn",
[user systemEmail], @"SystemEmail"];
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
return [NSException exceptionWithHTTPStatus:403 reason: reason];
}
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
to: end]];
// We first remove any occurences in the freebusy that corresponds to the
// current event. We do this to avoid raising a conflict if we move a 1 hour
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
//
// We must also check here for repetitive events that don't overlap our event.
// We remove all events that don't overlap. The events here are already
// decomposed.
//
if ([theEvent isRecurrent])
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end]
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
endDate: [theEvent endDate]]];
else
allOccurences = nil;
for (i = [fbInfo count]-1; i >= 0; i--)
{
range = [NGCalendarDateRange calendarDateRangeWithStartDate: [[fbInfo objectAtIndex: i] objectForKey: @"startDate"]
endDate: [[fbInfo objectAtIndex: i] objectForKey: @"endDate"]];
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
{
SOGoAppointmentFolder *folder;
NSCalendarDate *start, *end;
NGCalendarDateRange *range;
NSMutableArray *fbInfo;
NSArray *allOccurences;
BOOL must_delete;
int i, j;
// We get the start/end date for our conflict range. If the event to be added is recurring, we
// check for at least a year to start with.
start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1];
end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1];
folder = [user personalCalendarFolderInContext: context];
// Deny access to the resource if the ACLs don't allow the user
if (![folder aclSQLListingFilter])
{
NSDictionary *values;
NSString *reason;
values = [NSDictionary dictionaryWithObjectsAndKeys:
[user cn], @"Cn",
[user systemEmail], @"SystemEmail"];
reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]];
return [NSException exceptionWithHTTPStatus:403 reason: reason];
}
fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start
to: end]];
// We first remove any occurences in the freebusy that corresponds to the
// current event. We do this to avoid raising a conflict if we move a 1 hour
// meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise.
//
// We must also check here for repetitive events that don't overlap our event.
// We remove all events that don't overlap. The events here are already
// decomposed.
//
if ([theEvent isRecurrent])
allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end]
firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate]
endDate: [theEvent endDate]]];
else
allOccurences = nil;
for (i = [fbInfo count]-1; i >= 0; i--)
{
range = [NGCalendarDateRange calendarDateRangeWithStartDate: [[fbInfo objectAtIndex: i] objectForKey: @"startDate"]
endDate: [[fbInfo objectAtIndex: i] objectForKey: @"endDate"]];
if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame)
{
[fbInfo removeObjectAtIndex: i];
continue;
}
// No need to check if the event isn't recurrent here as it's handled correctly
// when we compute the "end" date.
if ([allOccurences count])
// No need to check if the event isn't recurrent here as it's handled correctly
// when we compute the "end" date.
if ([allOccurences count])
{
must_delete = YES;
for (j = 0; j < [allOccurences count]; j++)
{
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
{
must_delete = NO;
break;
}
}
{
if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]])
{
must_delete = NO;
break;
}
}
if (must_delete)
[fbInfo removeObjectAtIndex: i];
[fbInfo removeObjectAtIndex: i];
}
}
// Find the attendee associated to the current UID
for (i = 0; i < [theAttendees count]; i++)
{
currentAttendee = [theAttendees objectAtIndex: i];
if ([[currentAttendee uid] isEqualToString: currentUID])
break;
else
currentAttendee = nil;
}
if ([fbInfo count])
{
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
// overlapping events
if ([user numberOfSimultaneousBookings] == 0 ||
[user numberOfSimultaneousBookings] > [fbInfo count])
{
if (currentAttendee)
{
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
}
else
{
iCalCalendar *calendar;
NSDictionary *values;
NSString *reason;
iCalEvent *event;
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
event = [[calendar events] lastObject];
values = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
[user cn], @"Cn",
[user systemEmail], @"SystemEmail",
([event summary] ? [event summary] : @""), @"EventTitle",
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
nil];
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
return [NSException exceptionWithHTTPStatus: 403
reason: reason];
}
}
else if (currentAttendee)
{
// No conflict, we auto-accept. We do this for resources automatically if no
// double-booking is observed. If it's not the desired behavior, just don't
// set the resource as one!
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
}
}
// Find the attendee associated to the current UID
for (i = 0; i < [theAttendees count]; i++)
{
currentAttendee = [theAttendees objectAtIndex: i];
if ([[currentAttendee uid] isEqualToString: currentUID])
break;
else
currentAttendee = nil;
}
if ([fbInfo count])
{
// If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit
// is imposed) or if numberOfSimultaneousBookings is greater than the number of
// overlapping events
if ([user numberOfSimultaneousBookings] == 0 ||
[user numberOfSimultaneousBookings] > [fbInfo count])
{
if (currentAttendee)
{
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
}
else
{
iCalCalendar *calendar;
NSDictionary *values;
NSString *reason;
iCalEvent *event;
calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]];
event = [[calendar events] lastObject];
values = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings",
[user cn], @"Cn",
[user systemEmail], @"SystemEmail",
([event summary] ? [event summary] : @""), @"EventTitle",
[[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate",
nil];
reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]];
return [NSException exceptionWithHTTPStatus: 403
reason: reason];
}
}
else if (currentAttendee)
{
// No conflict, we auto-accept. We do this for resources automatically if no
// double-booking is observed. If it's not the desired behavior, just don't
// set the resource as one!
[[currentAttendee attributes] removeObjectForKey: @"RSVP"];
[currentAttendee setParticipationStatus: iCalPersonPartStatAccepted];
}
}
}
return nil;
}
@ -608,27 +675,29 @@
//
//
- (NSException *) _handleAddedUsers: (NSArray *) attendees
fromEvent: (iCalEvent *) newEvent
fromEvent: (iCalEvent *) newEvent
{
iCalPerson *currentAttendee;
NSEnumerator *enumerator;
NSString *currentUID;
NSException *e;
// We check for conflicts
if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent]))
return e;
if ((e = [self _handleAttendeeAvalability: attendees forEvent: newEvent]))
return e;
enumerator = [attendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
owner: owner];
}
{
currentUID = [currentAttendee uid];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
owner: owner];
}
return nil;
}
@ -709,8 +778,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
withType: @"calendar:cancellation"];
}
if ((ex = [self _handleResourcesConflicts: [newEvent attendees]
forEvent: newEvent]))
if ((ex = [self _handleResourcesConflicts: [newEvent attendees] forEvent: newEvent]))
return ex;
if ((ex = [self _handleAttendeeAvalability: [newEvent attendees] forEvent: newEvent]))
return ex;
addedAttendees = [changes insertedAttendees];
@ -783,7 +853,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// +------------> _handleUpdatedEvent:fromOldEvent: ---> _addOrUpdateEvent:forUID:owner: <-----------+
// | | ^ |
// v v | |
// _handleRemovedUsers:withRecurrenceId: _handleSequenceUpdateInEvent:ignoringAttendees:fromOldEvent: |
// _handleRemovedUsers:withRecurrenceId: _handleSequenceUpdateInEvent:ignoringAttendees:fromOldEvent: |
// | |
// | [DELETEAction:] |
// | | {_handleAdded/Updated...}<--+ |

View File

@ -33,6 +33,7 @@
{
id item;
SOGoUser *user;
SOGoUserSettings *us;
NGSieveClient *client;
// Addressbook

View File

@ -41,6 +41,7 @@
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoSieveManager.h>
#import <SOGo/SOGoSystemDefaults.h>
@ -638,6 +639,23 @@ static NSArray *reminderValues = nil;
return [userDefaults busyOffHours];
}
- (void) setPreventInvitations: (BOOL) preventInvitations
{
SOGoUserSettings *us;
us = [user userSettings];
[us setBool: preventInvitations forKey: @"PreventInvitations"];
[us synchronize];
}
- (BOOL) preventInvitations
{
SOGoUserSettings *us;
us = [user userSettings];
return [[us objectForKey: @"PreventInvitations"] boolValue];
}
- (NSArray *) firstWeekList
{
return [NSArray arrayWithObjects:

View File

@ -1,37 +1,42 @@
<?xml version='1.0' standalone='yes'?>
<var:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
xmlns:uix="OGo:uix"
className="UIxPageFrame"
title="name"
const:popup="YES"
const:jsFiles="UIxMailEditor.js,SOGoAutoCompletion.js"
>
<script type="text/javascript">
var activeAddressBook = '<var:string value="clientObject.container.nameInContainer"/>';
</script>
<div class="popupMenu" id="contactsMenu">
<ul></ul>
</div>
<form var:href="saveURL" name="editform" id="mainForm">
<div id="listEditor">
<span class="caption"><var:string label:value="List details" /></span>
<table class="frame"><tr><td width="35%"><var:string label:value="List name:"/></td>
<td><input type="text" const:id="listName" const:name="listName"
var:value="listName" class="textField" /></td></tr>
<tr><td><var:string label:value="List nickname:"/></td>
<td><input type="text" const:id="nickname" const:name="nickname"
var:value="nickname" class="textField" /></td></tr>
<tr><td><var:string label:value="List description:"/></td>
<td><input type="text" const:id="description" const:name="description"
var:value="description" class="textField" /></td></tr>
</table>
<span class="caption"><var:string label:value="Members" /></span>
<table class="frame"><tr><td>
<var:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
xmlns:uix="OGo:uix"
className="UIxPageFrame"
title="name"
const:popup="YES"
const:jsFiles="UIxMailEditor.js,SOGoAutoCompletion.js"
>
<script type="text/javascript">
var activeAddressBook = '<var:string value="clientObject.container.nameInContainer"/>';
</script>
<div class="popupMenu" id="contactsMenu">
<ul></ul>
</div>
<form var:href="saveURL" name="editform" id="mainForm">
<div id="listEditor">
<span class="caption"><var:string label:value="List details" /></span>
<table class="frame">
<tr>
<td width="35%"><var:string label:value="List name:"/></td>
<td>
<input type="text" const:id="listName" const:name="listName" var:value="listName" class="textField" /></td></tr>
<tr>
<td><var:string label:value="List nickname:"/></td>
<td>
<input type="text" const:id="nickname" const:name="nickname" var:value="nickname" class="textField" /></td></tr>
<tr>
<td><var:string label:value="List description:"/></td>
<td>
<input type="text" const:id="description" const:name="description" var:value="description" class="textField" /></td></tr>
</table>
<span class="caption"><var:string label:value="Members" /></span>
<table class="frame"><tr><td>
<div id="referenceListWrapper">
<table id="referenceList" cellspacing="0">
<thead>
@ -44,40 +49,40 @@
<tbody>
<var:foreach list="references" item="reference">
<tr const:class="referenceListRow">
<td const:class="referenceListCell">
<input var:uid="reference.id"
var:value="reference.name"
const:style="display: none"/>
<td const:class="referenceListCell">
<input var:uid="reference.id"
var:value="reference.name"
const:style="display: none"/>
<span><var:string var:value="reference.name"/></span>
</td></tr>
</td></tr>
</var:foreach>
</tbody>
</table>
</div>
</td></tr>
<tr><td>
<div class="bottomToolbar">
<a const:id="referenceAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</td></tr>
<tr><td>
<div class="bottomToolbar">
<a const:id="referenceAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
<a const:id="referenceDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
<a const:id="referenceDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</div>
</td></tr>
</table>
</div>
<input type="hidden" name="referencesValue" id="referencesValue"
var:value="referencesValue" />
<div id="buttons">
<var:if condition="canCreateOrModify"
><a class="button actionButton" name="submit"
id="submitButton" href="#" >
<span><var:string label:value="Save"/></span>
</div>
</td></tr>
</table>
</div>
<input type="hidden" name="referencesValue" id="referencesValue"
var:value="referencesValue" />
<div id="buttons">
<var:if condition="canCreateOrModify"
><a class="button actionButton" name="submit"
id="submitButton" href="#" >
<span><var:string label:value="Save"/></span>
</a></var:if>
<a const:id="cancelButton" class="button" name="cancel" href="#">
<span><var:string label:value="Cancel"/></span>
</a>
</div>
</form>
</var:component>
<a const:id="cancelButton" class="button" name="cancel" href="#">
<span><var:string label:value="Cancel"/></span>
</a>
</div>
</form>
</var:component>

View File

@ -11,11 +11,14 @@
title="title"
const:popup="YES"
const:cssFiles="datepicker.css"
const:jsFiles="RowEditionController.js,PasswordPolicy.js,ckeditor/ckeditor.js,datepicker.js"
const:jsFiles="RowEditionController.js,PasswordPolicy.js,ckeditor/ckeditor.js,datepicker.js, SOGoAutoCompletion.js"
>
<script type="text/javascript">
var localeCode = '<var:string value="localeCode"/>';
</script>
<div class="popupMenu" id="contactsMenu">
<ul></ul>
</div>
<div id="colorPickerDialog" style="display: none;" class="dialog right bottom">
<div>
@ -91,33 +94,33 @@
<span class="blc-99FF99"><!-- --></span>
</div>
</div>
<form id="mainForm" var:href="ownPath">
<div class="tabsContainer" id="preferencesTabs">
<ul>
<li target="generalView"><span>
<var:string label:value="General" /></span></li>
<var:if condition="userHasCalendarAccess">
<var:string label:value="General" /></span></li>
<var:if condition="userHasCalendarAccess">
<li target="calendarOptionsView"><span><var:string
label:value="Calendar Options"/></span></li>
</var:if
><li target="contactsOptionsView"><span><var:string
label:value="Contacts Options"/></span></li
><var:if condition="userHasMailAccess">
label:value="Calendar Options"/></span></li>
</var:if
><li target="contactsOptionsView"><span><var:string
label:value="Contacts Options"/></span></li
><var:if condition="userHasMailAccess">
<li target="mailOptionsView"><span><var:string
label:value="Mail Options"/></span></li>
label:value="Mail Options"/></span></li>
<li target="mailAccountsView"><span><var:string
label:value="IMAP Accounts"/></span></li>
label:value="IMAP Accounts"/></span></li>
<var:if condition="isVacationEnabled"><li target="vacationView"><span><var:string
label:value="Vacation"/></span></li></var:if
><var:if condition="isForwardEnabled"><li target="forwardView"><span><var:string
label:value="Forward"/></span></li></var:if>
</var:if
><var:if condition="shouldDisplayAdditionalPreferences">
label:value="Vacation"/></span></li></var:if
><var:if condition="isForwardEnabled"><li target="forwardView"><span><var:string
label:value="Forward"/></span></li></var:if>
</var:if
><var:if condition="shouldDisplayAdditionalPreferences">
<li target="additionalView"><span>
<var:string label:value="Additional Parameters"/></span></li>
</var:if
><var:if condition="shouldDisplayPasswordChange">
<var:string label:value="Additional Parameters"/></span></li>
</var:if
><var:if condition="shouldDisplayPasswordChange">
<li target="passwordView"><span><var:string label:value="Password"/></span></li>
</var:if>
</ul>
@ -128,38 +131,38 @@
<dd><var:string value="sogoVersion"/></dd>
<dt><var:string label:value="Language :"/></dt>
<dd><var:popup list="languages" item="item"
const:id="language"
const:name="language"
string="languageText"
selection="language"
label:noSelectionString="choose" /></dd>
const:id="language"
const:name="language"
string="languageText"
selection="language"
label:noSelectionString="choose" /></dd>
<dt><var:string label:value="Current Time Zone :"/></dt>
<dd><var:popup list="timeZonesList" item="item"
const:id="timezone"
const:name="timezone"
string="item" selection="userTimeZone" /></dd>
const:id="timezone"
const:name="timezone"
string="item" selection="userTimeZone" /></dd>
<dt><var:string label:value="Short Date Format :"/></dt>
<dd><var:popup list="shortDateFormatsList" item="item"
const:id="shortDateFormat"
const:name="shortDateFormat"
string="itemShortDateFormatText" selection="userShortDateFormat"/></dd>
const:id="shortDateFormat"
const:name="shortDateFormat"
string="itemShortDateFormatText" selection="userShortDateFormat"/></dd>
<dt><var:string label:value="Long Date Format :"/></dt>
<dd><var:popup list="longDateFormatsList" item="item"
const:id="longDateFormat"
const:name="longDateFormat"
string="itemLongDateFormatText" selection="userLongDateFormat"
/></dd>
const:id="longDateFormat"
const:name="longDateFormat"
string="itemLongDateFormatText" selection="userLongDateFormat"
/></dd>
<dt><var:string label:value="Time Format :"/></dt>
<dd><var:popup list="timeFormatsList" item="item"
const:id="timeFormat"
const:name="timeFormat"
string="itemTimeFormatText" selection="userTimeFormat"
/></dd>
const:id="timeFormat"
const:name="timeFormat"
string="itemTimeFormatText" selection="userTimeFormat"
/></dd>
<dt><var:string label:value="Default module :"/></dt>
<dd><var:popup list="availableModules" item="item"
const:id="defaultModule"
const:name="defaultModule"
string="itemModuleText" selection="userDefaultModule"/></dd>
const:id="defaultModule"
const:name="defaultModule"
string="itemModuleText" selection="userDefaultModule"/></dd>
</dl>
</div>
<var:if condition="userHasCalendarAccess">
@ -167,73 +170,103 @@
<dl class="dl-horizontal">
<dt><var:string label:value="Week begins on :"/></dt>
<dd><var:popup list="daysList" item="item"
const:id="weekStartDay"
const:name="weekStartDay"
string="itemWeekStartDay" selection="userWeekStartDay"/></dd>
const:id="weekStartDay"
const:name="weekStartDay"
string="itemWeekStartDay" selection="userWeekStartDay"/></dd>
<dt><var:string label:value="Day start time :"/></dt>
<dd><var:popup list="hoursList" item="item"
const:id="dayStartTime"
const:name="dayStartTime"
string="item" selection="userDayStartTime"/></dd>
const:id="dayStartTime"
const:name="dayStartTime"
string="item" selection="userDayStartTime"/></dd>
<dt><var:string label:value="Day end time :"/></dt>
<dd><var:popup list="hoursList" item="item"
const:id="dayEndTime"
const:name="dayEndTime"
string="item" selection="userDayEndTime"/></dd>
const:id="dayEndTime"
const:name="dayEndTime"
string="item" selection="userDayEndTime"/></dd>
<dt></dt>
<dd><input type="checkbox"
const:name="busyOffHours"
const:id="busyOffHours"
var:checked="busyOffHours" />
<dd><input type="checkbox"
const:name="busyOffHours"
const:id="busyOffHours"
var:checked="busyOffHours" />
<var:string label:value="Show time as busy outside working hours"/></dd>
<dt><var:string label:value="First week of year :"/></dt>
<dd><var:popup list="firstWeekList" item="item"
const:id="firstWeek"
const:name="firstWeek"
string="itemFirstWeekText" selection="userFirstWeek"/></dd>
<dt><var:string label:value="Default calendar :"/></dt>
<dd><var:popup list="defaultCalendarList" item="item"
const:id="defaultCalendar"
const:name="defaultCalendar"
string="itemCalendarText" selection="userDefaultCalendar"/></dd>
<dt><var:string label:value="Default events classification :"/></dt>
<dd><var:popup list="calendarClassificationsList" item="item"
const:id="eventsClassification"
const:name="eventsClassification"
string="itemClassificationText" selection="eventsDefaultClassification"/></dd>
<dt><var:string label:value="Default tasks classification :"/></dt>
<dd><var:popup list="calendarClassificationsList" item="item"
const:id="tasksClassification"
const:name="tasksClassification"
string="itemClassificationText" selection="tasksDefaultClassification"/></dd>
<dt><var:string label:value="Default reminder :"/></dt>
<dd><var:popup list="reminderList" item="item"
const:disabledValue="-"
label:noSelectionString="reminder_NONE"
const:name="reminderList"
const:id="reminderList"
string="itemReminderText" var:selection="reminder"/></dd>
<dt><var:string label:value="First week of year :"/></dt>
<dd><var:popup list="firstWeekList" item="item"
const:id="firstWeek"
const:name="firstWeek"
string="itemFirstWeekText" selection="userFirstWeek"/></dd>
<dt><var:string label:value="Default calendar :"/></dt>
<dd><var:popup list="defaultCalendarList" item="item"
const:id="defaultCalendar"
const:name="defaultCalendar"
string="itemCalendarText" selection="userDefaultCalendar"/></dd>
<dt><var:string label:value="Default events classification :"/></dt>
<dd><var:popup list="calendarClassificationsList" item="item"
const:id="eventsClassification"
const:name="eventsClassification"
string="itemClassificationText" selection="eventsDefaultClassification"/></dd>
<dt><var:string label:value="Default tasks classification :"/></dt>
<dd><var:popup list="calendarClassificationsList" item="item"
const:id="tasksClassification"
const:name="tasksClassification"
string="itemClassificationText" selection="tasksDefaultClassification"/></dd>
<dt><var:string label:value="Default reminder :"/></dt>
<dd><var:popup list="reminderList" item="item"
const:disabledValue="-"
label:noSelectionString="reminder_NONE"
const:name="reminderList"
const:id="reminderList"
string="itemReminderText" var:selection="reminder"/></dd>
<dt><var:string label:value="Appointments invitations:"/></dt>
<dd><input type="checkbox"
const:name="preventInvitations"
const:id="preventInvitations"
var:checked="preventInvitations" />
<var:string label:value="Prevent from being invited to appointments"/></dd>
</dl>
<label><var:string label:value="Categories"/></label>
<div id="calendarCategoriesListWrapper" class="listWrapper"
><table class="categoriesList" cellspacing="0">
<label id="whiteListLabel"><var:string label:value="White list for appointments invitations:"/></label>
<div id="appointmentsWhiteListWrapper" class="listWrapper">
<table class="tableview" id="tableViewWhiteList">
<thead>
<tr class="tableview"
><th const:class="tbtv_headercell" const:id="nameTableHeader"
><var:string label:value="Name"/></th
><th const:class="tbtv_headercell" const:id="colorTableHeader"
><var:string label:value="Color"/></th
></tr
></thead>
<tr class="tableview">
<th const:class="tbtv_headercell" const:id="whiteListTableHeader">
<var:string label:value="Contacts names"/></th>
</tr></thead>
<tbody>
<var:foreach list="appointmentsWhiteList" item="contact">
<tr const:class="whiteListRow">
<td const:class="whiteListCell">
<var:string var:value="contact"/></td>
</tr>
</var:foreach>
</tbody>
</table>
</div>
<div id="whiteListToolbar">
<a const:id="appointmentsWhiteListAdd" href="#">
<img rsrc:src="add-icon.png" label:title="Add" id="WhiteListAdd"/></a>
<a const:id="appointmentsWhiteListDelete" href="#">
<img rsrc:src="remove-icon.png" label:title="Delete" id="WhiteListDelete" /></a>
</div>
<label id="calendarCategoriesLabel"><var:string label:value="Categories"/></label>
<div id="calendarCategoriesListWrapper" class="listWrapper">
<table class="categoriesList" cellspacing="0">
<thead>
<tr class="tableview">
<th const:class="tbtv_headercell" const:id="nameTableHeader">
<var:string label:value="Name"/></th>
<th const:class="tbtv_headercell" const:id="colorTableHeader">
<var:string label:value="Color"/></th>
</tr></thead>
<tbody>
<var:foreach list="calendarCategoryList" item="category">
<tr const:class="categoryListRow"
><td const:class="categoryListCell"
><var:string var:value="category"/></td
><td const:class="categoryListCell"
><div const:class="colorBox" var:data-color="categoryColor"><entity name="nbsp"/></div></td
></tr>
<tr const:class="categoryListRow">
<td const:class="categoryListCell">
<var:string var:value="category"/></td>
<td const:class="categoryListCell">
<div const:class="colorBox" var:data-color="categoryColor"><entity name="nbsp"/></div></td>
</tr>
</var:foreach>
</tbody>
</table>
@ -246,8 +279,8 @@
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</div>
<input type="hidden" const:id="calendarCategoriesValue"
const:name="calendarCategoriesValue" var:value="calendarCategoriesValue"/>
<input type="hidden" const:id="calendarCategoriesValue"
const:name="calendarCategoriesValue" var:value="calendarCategoriesValue"/>
</div>
</var:if>
<div id="contactsOptionsView" class="tab">
@ -258,14 +291,14 @@
<tr class="tableview"
><th const:class="tbtv_headercell" const:id="nameTableHeader"
><var:string label:value="Name"/></th
></tr
></thead>
></tr
></thead>
<tbody>
<var:foreach list="contactsCategoryList" item="category">
<tr const:class="categoryListRow"
><td const:class="categoryListCell"
><var:string var:value="category"/></td
></tr>
></tr>
</var:foreach>
</tbody>
</table>
@ -278,135 +311,135 @@
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</div>
<input type="hidden" const:id="contactsCategoriesValue"
const:name="contactsCategoriesValue" var:value="contactsCategoriesValue"/>
<input type="hidden" const:id="contactsCategoriesValue"
const:name="contactsCategoriesValue" var:value="contactsCategoriesValue"/>
</div>
<var:if condition="userHasMailAccess">
<div id="mailOptionsView" class="tab">
<dl class="dl-horizontal">
<dt></dt>
<dd><input type="checkbox"
const:name="subscribedFoldersOnly"
const:id="subscribedFoldersOnly"
var:checked="showSubscribedFoldersOnly" />
<var:string label:value="Show subscribed mailboxes only"/></dd>
<dt></dt>
<dd><input type="checkbox"
const:name="sortByThreads"
const:id="sortByThreads"
var:checked="sortByThreads" />
<var:string label:value="Sort messages by threads"/></dd>
<dd><input type="checkbox"
const:name="addOutgoingAddresses"
const:id="addOutgoingAddresses"
var:checked="addOutgoingAddresses"
onChange = "onAddOutgoingAddressesCheck(this);"/>
<var:string label:value="When sending mail, add unknown recipients to my"/><br/>
<var:popup list="addressBookList" item="item"
const:id="addressBookList"
const:name="addressBookList"
string="itemAddressBookText" selection="userAddressBook"/></dd>
const:name="subscribedFoldersOnly"
const:id="subscribedFoldersOnly"
var:checked="showSubscribedFoldersOnly" />
<var:string label:value="Show subscribed mailboxes only"/></dd>
<dt></dt>
<dd><input type="checkbox"
const:name="sortByThreads"
const:id="sortByThreads"
var:checked="sortByThreads" />
<var:string label:value="Sort messages by threads"/></dd>
<dd><input type="checkbox"
const:name="addOutgoingAddresses"
const:id="addOutgoingAddresses"
var:checked="addOutgoingAddresses"
onChange = "onAddOutgoingAddressesCheck(this);"/>
<var:string label:value="When sending mail, add unknown recipients to my"/><br/>
<var:popup list="addressBookList" item="item"
const:id="addressBookList"
const:name="addressBookList"
string="itemAddressBookText" selection="userAddressBook"/></dd>
<dt></dt>
<dt><var:string label:value="Check for new mail:"/></dt>
<dd><var:popup list="messageCheckList" item="item"
const:id="messageCheck"
const:name="messageCheck"
string="itemMessageCheckText" selection="userMessageCheck"/></dd>
const:id="messageCheck"
const:name="messageCheck"
string="itemMessageCheckText" selection="userMessageCheck"/></dd>
<dt><var:string label:value="Forward messages:"/></dt>
<dd><var:popup list="messageForwardingList" item="item"
const:id="messageForwarding"
const:name="messageForwarding"
string="itemMessageForwardingText"
selection="userMessageForwarding"/></dd>
const:id="messageForwarding"
const:name="messageForwarding"
string="itemMessageForwardingText"
selection="userMessageForwarding"/></dd>
<dt><var:string label:value="When replying to a message:"/></dt>
<dd><var:popup list="replyPlacementList" item="item"
const:id="replyPlacementList"
const:name="replyPlacementList"
string="itemReplyPlacementText"
selection="userReplyPlacement"/></dd>
const:id="replyPlacementList"
const:name="replyPlacementList"
string="itemReplyPlacementText"
selection="userReplyPlacement"/></dd>
<dt><var:string label:value="And place my signature"/></dt>
<dd><var:popup list="signaturePlacementList" item="item"
const:id="signaturePlacementList"
const:name="signaturePlacementList"
string="itemSignaturePlacementText"
selection="userSignaturePlacement"/></dd>
const:id="signaturePlacementList"
const:name="signaturePlacementList"
string="itemSignaturePlacementText"
selection="userSignaturePlacement"/></dd>
<dt><var:string label:value="Compose messages in"/></dt>
<dd><var:popup list="composeMessagesType" item="item"
const:id="composeMessagesType"
const:name="composeMessagesType"
string="itemComposeMessagesText"
selection="userComposeMessagesType"/></dd>
const:id="composeMessagesType"
const:name="composeMessagesType"
string="itemComposeMessagesText"
selection="userComposeMessagesType"/></dd>
<dt><var:string label:value="Display remote inline images"/></dt>
<dd><var:popup list="displayRemoteInlineImages" item="item"
const:id="displayRemoteInlineImages"
const:name="displayRemoteInlineImages"
string="itemDisplayRemoteInlineImagesText"
selection="userDisplayRemoteInlineImages"/></dd>
const:id="displayRemoteInlineImages"
const:name="displayRemoteInlineImages"
string="itemDisplayRemoteInlineImagesText"
selection="userDisplayRemoteInlineImages"/></dd>
</dl>
<div class="tabsContainer" id="mailOptionsTabs">
<ul>
<var:if condition="isSieveScriptsEnabled"
><li target="mailFiltersView"><span><var:string
label:value="Filters"/></span></li
></var:if>
><li target="mailFiltersView"><span><var:string
label:value="Filters"/></span></li
></var:if>
<li target="mailLabelsView"><span><var:string
label:value="Labels"/></span></li>
label:value="Labels"/></span></li>
</ul>
<div class="tabs">
<var:if condition="isSieveScriptsEnabled"
><div id="mailFiltersView" class="tab">
<script type="text/javascript">
var sieveCapabilities = <var:string value="sieveCapabilities" const:escapeHTML="NO"/>;
</script>
<div id="filtersListWrapper" class="listWrapper">
<table id="filtersList" cellspacing="0">
<thead>
<tr class="tableview">
<th const:class="tbtv_headercell" const:id="nameTableHeader"
><div id="mailFiltersView" class="tab">
<script type="text/javascript">
var sieveCapabilities = <var:string value="sieveCapabilities" const:escapeHTML="NO"/>;
</script>
<div id="filtersListWrapper" class="listWrapper">
<table id="filtersList" cellspacing="0">
<thead>
<tr class="tableview">
<th const:class="tbtv_headercell" const:id="nameTableHeader"
><var:string label:value="Name" /></th>
<th const:class="tbtv_headercell" const:id="activeTableHeader"
<th const:class="tbtv_headercell" const:id="activeTableHeader"
><var:string label:value="Active" /></th>
</tr>
</thead>
<tbody><!--space --></tbody>
</table>
<input type="hidden" const:name="sieveFilters" const:id="sieveFilters"
var:value="sieveFiltersValue"/>
</div><!-- #filtersListWrapper -->
<div class="bottomToolbar">
<a const:id="filterAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" /></span></a>
<a const:id="filterDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" /></span></a>
<a const:id="filterMoveUp" class="bottomButton" href="#">
<span><img rsrc:src="up-icon.png" label:title="Move Up" /></span></a>
<a const:id="filterMoveDown" class="bottomButton" href="#">
<span><img rsrc:src="down-icon.png" label:title="Move Down" /></span></a>
</div><!-- .bottomToolbar -->
</div
></var:if><!-- #mailFiltersView -->
</tr>
</thead>
<tbody><!--space --></tbody>
</table>
<input type="hidden" const:name="sieveFilters" const:id="sieveFilters"
var:value="sieveFiltersValue"/>
</div><!-- #filtersListWrapper -->
<div class="bottomToolbar">
<a const:id="filterAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" /></span></a>
<a const:id="filterDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" /></span></a>
<a const:id="filterMoveUp" class="bottomButton" href="#">
<span><img rsrc:src="up-icon.png" label:title="Move Up" /></span></a>
<a const:id="filterMoveDown" class="bottomButton" href="#">
<span><img rsrc:src="down-icon.png" label:title="Move Down" /></span></a>
</div><!-- .bottomToolbar -->
</div
></var:if><!-- #mailFiltersView -->
<div id="mailLabelsView" class="tab">
<div id="mailLabelsListWrapper" class="listWrapper">
<table id="labelsList" cellspacing="0">
<thead>
<tr class="tableview">
<th const:class="tbtv_headercell" const:id="labelTableHeader"
><var:string label:value="Label"/></th>
><var:string label:value="Label"/></th>
<th const:class="tbtv_headercell" const:id="colorTableHeader"
><var:string label:value="Color"/></th>
><var:string label:value="Color"/></th>
</tr>
</thead>
<tbody>
<var:foreach list="mailLabelList" item="label"
><tr var:data-name="label.name" const:class="labelListRow">
<td const:class="labelListCell"
><tr var:data-name="label.name" const:class="labelListRow">
<td const:class="labelListCell"
><var:string var:value="label.label"/></td>
<td const:class="labelListCell">
<div const:class="colorBox" var:data-color="label.color"><entity name="nbsp"/></div>
</td>
</tr>
<td const:class="labelListCell">
<div const:class="colorBox" var:data-color="label.color"><entity name="nbsp"/></div>
</td>
</tr>
</var:foreach>
</tbody>
</table>
@ -414,33 +447,33 @@
<div class="bottomToolbar">
<a const:id="mailLabelAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
</span></a>
<a const:id="mailLabelDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</span></a>
</div><!-- .bottomToolbar -->
<input type="hidden" const:id="mailLabelsValue"
const:name="mailLabelsValue" var:value="mailLabelsValue"/>
const:name="mailLabelsValue" var:value="mailLabelsValue"/>
</div><!-- #mailLabelsView -->
</div><!-- .tabs -->
</div><!-- #mailOptionsTabs -->
</div><!-- #mailOptionsView -->
<div id="mailAccountsView" class="tab">
<input type="hidden" const:name="mailAccountsJSON" const:id="mailAccountsJSON"
var:value="mailAccounts"/>
var:value="mailAccounts"/>
<div id="mailAccountsListWrapper" class="listWrapper"
><ul id="mailAccountsList"
><!-- space --></ul
></div>
><ul id="mailAccountsList"
><!-- space --></ul
></div>
<var:if condition="mailAuxiliaryUserAccountsEnabled">
<div const:id="mailAccountsToolbar" class="bottomToolbar">
<a const:id="mailAccountAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
</span></a>
<a const:id="mailAccountDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</span></a>
</div>
</var:if>
<div id="mailAccountEditor">
@ -448,15 +481,15 @@
<dl class="dl-horizontal">
<dt><var:string label:value="Server Name:"/></dt>
<dd><input const:name="serverName" const:id="serverName" type="text" const:value=""/>
<var:string label:value="Port:"/>
<input const:name="port" const:id="port" type="text" const:value=""/></dd>
<var:string label:value="Port:"/>
<input const:name="port" const:id="port" type="text" const:value=""/></dd>
<dt><var:string label:value="Encryption:"/></dt>
<dd><input const:name="encryption" type="radio" const:value="none"/>
<var:string label:value="None"/>
<input const:name="encryption" type="radio" const:value="ssl"/>
<var:string label:value="SSL"/>
<input const:name="encryption" type="radio" const:value="tls"/>
<var:string label:value="TLS"/></dd>
<var:string label:value="None"/>
<input const:name="encryption" type="radio" const:value="ssl"/>
<var:string label:value="SSL"/>
<input const:name="encryption" type="radio" const:value="tls"/>
<var:string label:value="TLS"/></dd>
<dt><var:string label:value="User Name:"/></dt>
<dd><input const:name="userName" const:id="userName" type="text" const:value=""/></dd>
<dt><var:string label:value="Password:"/></dt>
@ -464,11 +497,11 @@
</dl>
<input const:name="encryption" type="hidden" const:value="none"/>
</fieldset>
<script type="text/javascript">
var mailCustomFromEnabled = <var:string value="mailCustomFromEnabled" const:escapeHTML="NO"/>;
</script>
<fieldset const:id="identityInfo">
<dl class="dl-horizontal">
<dt><var:string label:value="Full Name:"/></dt>
@ -481,40 +514,40 @@
<dd><span id="actSignature"><!--space --></span></dd>
</dl>
</fieldset>
<fieldset const:id="returnReceiptsInfo">
<var:string
label:value="When I receive a request for a return receipt:"
/><br/>
label:value="When I receive a request for a return receipt:"
/><br/>
<label><input const:name="receipt-action" const:id="receipt-action-ignore"
type="radio" const:value="ignore"/>
type="radio" const:value="ignore"/>
<var:string
label:value="Never send a return receipt"/></label
><br/>
label:value="Never send a return receipt"/></label
><br/>
<label><input const:name="receipt-action" const:id="receipt-action-allow"
type="radio" const:value="allow"/>
type="radio" const:value="allow"/>
<var:string
label:value="Allow return receipts for some messages"/></label
><br/>
label:value="Allow return receipts for some messages"/></label
><br/>
<div id="receiptOptions">
<var:string
label:value="If I'm not in the To or Cc of the message:"/>
label:value="If I'm not in the To or Cc of the message:"/>
<select name="receipt-non-recipient-action" id="receipt-non-recipient-action">
<option const:value="ignore"><var:string label:value="Never send"/></option>
<option const:value="send"><var:string label:value="Always send"/></option>
<option const:value="ask"><var:string label:value="Ask me"/></option>
</select><br/>
<var:string
label:value="If the sender is outside my domain:"/>
label:value="If the sender is outside my domain:"/>
<select name="receipt-outside-domain-action" id="receipt-outside-domain-action">
<option const:value="ignore"><var:string label:value="Never send"/></option>
<option const:value="send"><var:string label:value="Always send"/></option>
<option const:value="ask"><var:string label:value="Ask me"/></option>
</select><br/>
<var:string
label:value="In all other cases:"/>
label:value="In all other cases:"/>
<select name="receipt-any-action" id="receipt-any-action">
<option const:value="ignore"><var:string label:value="Never send"/></option>
<option const:value="send"><var:string label:value="Always send"/></option>
@ -524,79 +557,79 @@
</fieldset>
</div>
</div>
<var:if condition="isVacationEnabled">
<div id="vacationView" class="tab">
<label><input type="checkbox"
const:name="enableVacation"
const:id="enableVacation"
var:checked="enableVacation" />
<label><input type="checkbox"
const:name="enableVacation"
const:id="enableVacation"
var:checked="enableVacation" />
<var:string label:value="Enable vacation auto reply"/></label>
<div id="vacation">
<label><var:string label:value="Auto reply message :"/><br/>
<textarea const:name="autoReplyText"
const:id="autoReplyText"
var:value="autoReplyText"/>
const:id="autoReplyText"
var:value="autoReplyText"/>
</label><br/>
<label><var:string label:value="Email addresses (separated by commas) :"/><br/>
<input type="hidden"
const:id="defaultEmailAddresses"
var:value="defaultEmailAddresses" />
const:id="defaultEmailAddresses"
var:value="defaultEmailAddresses" />
<textarea const:name="autoReplyEmailAddresses"
const:id="autoReplyEmailAddresses"
var:value="autoReplyEmailAddresses" /><br/>
const:id="autoReplyEmailAddresses"
var:value="autoReplyEmailAddresses" /><br/>
<span><a href="#" class="button" id="addDefaultEmailAddresses"><span>
<var:string label:value="Add default email addresses" /></span></a>
<var:string label:value="Add default email addresses" /></span></a>
</span>
</label><br/>
<label><var:string label:value="Days between responses :"/>
<label><var:string label:value="Days between responses :"/>
<var:popup list="daysBetweenResponsesList" item="item"
const:id="daysBetweenResponsesList"
const:name="daysBetweenResponsesList"
string="item"
selection="daysBetweenResponses" /></label><br/>
<label><input type="checkbox"
const:name="ignoreLists"
const:id="ignoreLists"
var:checked="ignoreLists" />
const:id="daysBetweenResponsesList"
const:name="daysBetweenResponsesList"
string="item"
selection="daysBetweenResponses" /></label><br/>
<label><input type="checkbox"
const:name="ignoreLists"
const:id="ignoreLists"
var:checked="ignoreLists" />
<var:string label:value="Do not send responses to mailing lists" /></label><br/>
<label class="timeDate"><input var:checked="enableVacationEndDate"
const:name="enableVacationEndDate" const:id="enableVacationEndDate" type="checkbox" class="checkBox"
/><var:string label:value="Disable auto reply on" /></label><var:component className="UIxTimeDateControl"
const:displayTimeControl="0"
var:disabled="disableVacationEndDate"
const:controlID="vacationEndDate"
date="vacationEndDate"
const:dayStartHour="0"
const:dayEndHour="23"
const:name="enableVacationEndDate" const:id="enableVacationEndDate" type="checkbox" class="checkBox"
/><var:string label:value="Disable auto reply on" /></label><var:component className="UIxTimeDateControl"
const:displayTimeControl="0"
var:disabled="disableVacationEndDate"
const:controlID="vacationEndDate"
date="vacationEndDate"
const:dayStartHour="0"
const:dayEndHour="23"
/>
</div>
</div>
</var:if
><var:if condition="isForwardEnabled">
</var:if
><var:if condition="isForwardEnabled">
<div id="forwardView" class="tab">
<label><input type="checkbox"
const:name="enableForward"
const:id="enableForward"
var:checked="enableForward" />
<label><input type="checkbox"
const:name="enableForward"
const:id="enableForward"
var:checked="enableForward" />
<var:string label:value="Forward incoming messages"/></label><br/>
<div id="forward">
<label><var:string label:value="Email addresses (separated by commas) :"/><br/>
<textarea const:name="forwardAddress"
const:id="forwardAddress"
var:value="forwardAddress" />
const:id="forwardAddress"
var:value="forwardAddress" />
</label><br/>
<label><input type="checkbox"
const:name="forwardKeepCopy"
const:id="forwardKeepCopy"
var:checked="forwardKeepCopy" />
<label><input type="checkbox"
const:name="forwardKeepCopy"
const:id="forwardKeepCopy"
var:checked="forwardKeepCopy" />
<var:string label:value="Keep a copy" /></label><br/>
</div>
</div>
@ -605,23 +638,23 @@
<var:if condition="shouldDisplayPasswordChange">
<div id="passwordView" class="tab">
<p id="passwordFields"><label><var:string label:value="New password:"
/><input const:id="newPasswordField" class="textField"
type="password" const:value=""/></label><br/>
<label><var:string label:value="Confirmation:"
/><input const:id="newPasswordConfirmationField" class="textField"
type="password" const:value=""/></label><br/>
<a href="#" class="button" id="changePasswordBtn"
><span><var:string label:value="Change"/></span></a><br/>
/><input const:id="newPasswordField" class="textField"
type="password" const:value=""/></label><br/>
<label><var:string label:value="Confirmation:"
/><input const:id="newPasswordConfirmationField" class="textField"
type="password" const:value=""/></label><br/>
<a href="#" class="button" id="changePasswordBtn"
><span><var:string label:value="Change"/></span></a><br/>
</p>
<p id="passwordError"><!-- space --></p>
</div>
</var:if
><var:if condition="shouldDisplayAdditionalPreferences"
</var:if
><var:if condition="shouldDisplayAdditionalPreferences"
><div id="additionalView" class="tab">
<var:component className="UIxAdditionalPreferences"/>
</div></var:if>
<input type="hidden" id="hasChanged" name="hasChanged"
var:value="hasChanged"/>
var:value="hasChanged"/>
</div>
</div>
</form>

View File

@ -2,277 +2,384 @@
// NOTE: The popup menu with id "contactsMenu" must exist before
// using this interface.
//
//
// This interface fires two events:
// - autocompletion:changed : fired when a new contact is selected
// - autocompletion:changedlist : fired when a new list is selected
//
var SOGoAutoCompletionInterface = {
// Attributes that could be changed from the object
// inheriting the inteface
uidField: "c_name",
addressBook: null,
excludeGroups: false,
excludeLists: false,
// Internal attributes
animationParent: null,
selectedIndex: -1,
delay: 0.750,
delayedSearch: false,
menu: null,
bind: function () {
this.menu = $('contactsMenu');
this.writeAttribute("autocomplete", "off");
this.writeAttribute("container", null);
this.confirmedValue = null;
this.observe("keydown", this.onKeydown.bindAsEventListener(this));
this.observe("blur", this.onBlur.bindAsEventListener(this));
},
onKeydown: function (event) {
if (event.ctrlKey || event.metaKey) {
this.focussed = true;
return;
}
if (event.keyCode == Event.KEY_TAB) {
if (this.confirmedValue)
this.value = this.confirmedValue;
else
this.writeAttribute("uid", null);
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
}
else if (event.keyCode == 0
|| event.keyCode == Event.KEY_BACKSPACE
|| event.keyCode == Event.KEY_DELETE
|| event.keyCode == 32 // Space
|| event.keyCode > 47) {
this.confirmedValue = null;
this.selectedIndex = -1;
if (this.delayedSearch)
window.clearTimeout(this.delayedSearch);
this.delayedSearch = this.performSearch.delay(this.delay, this);
}
else if (event.keyCode == Event.KEY_RETURN) {
preventDefault(event);
if (this.confirmedValue)
this.value = this.confirmedValue;
else
this.writeAttribute("uid", null);
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
this.selectedIndex = -1;
if (this.readAttribute("container")) {
this.confirmedValue = null;
this.fire("autocompletion:changedlist", this.readAttribute("container"));
}
else
this.fire("autocompletion:changed", event.keyCode);
}
else if (this.menu.getStyle('visibility') == 'visible') {
if (event.keyCode == Event.KEY_UP) { // Up arrow
if (this.selectedIndex > 0) {
var contacts = this.menu.select("li");
contacts[this.selectedIndex--].removeClassName("selected");
this.value = contacts[this.selectedIndex].readAttribute("address");
this.confirmedValue = this.value;
this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
contacts[this.selectedIndex].addClassName("selected");
var container = contacts[this.selectedIndex].readAttribute("container");
if (container)
this.writeAttribute("container", container);
}
}
else if (event.keyCode == Event.KEY_DOWN) { // Down arrow
var contacts = this.menu.select("li");
if (contacts.size() - 1 > this.selectedIndex) {
if (this.selectedIndex >= 0)
contacts[this.selectedIndex].removeClassName("selected");
this.selectedIndex++;
this.value = contacts[this.selectedIndex].readAttribute("address");
this.confirmedValue = this.value;
this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
contacts[this.selectedIndex].addClassName("selected");
var container = contacts[this.selectedIndex].readAttribute("container");
if (container)
this.writeAttribute("container", container);
}
}
}
},
onBlur: function (event) {
if (this.delayedSearch)
window.clearTimeout(this.delayedSearch);
if (this.confirmedValue) {
this.value = this.confirmedValue;
if (this.readAttribute("container"))
this.fire("autocompletion:changedlist", this.readAttribute("container"));
else
this.fire("autocompletion:changed", event.keyCode);
}
else
this.writeAttribute("uid", null);
},
performSearch: function (input) {
// Perform address completion
if (document.contactLookupAjaxRequest) {
// Abort any pending request
document.contactLookupAjaxRequest.aborted = true;
document.contactLookupAjaxRequest.abort();
}
if (input.value.trim().length > minimumSearchLength) {
var urlstr = UserFolderURL + "Contacts/";
if (input.addressBook)
urlstr += input.addressBook + "/contact";
else
urlstr += "allContact";
urlstr += "Search?search=" + encodeURIComponent(input.value);
if (input.excludeGroups)
urlstr += "&excludeGroups=1";
if (input.excludeLists)
urlstr += "&excludeLists=1";
if (input.animationParent)
startAnimation(input.animationParent);
document.contactLookupAjaxRequest =
triggerAjaxRequest(urlstr, input.performSearchCallback.bind(input), input);
}
else {
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
}
},
performSearchCallback: function (http) {
if (http.readyState == 4) {
var list = this.menu.down("ul");
var input = http.callbackData;
if (http.status == 200) {
var start = input.value.length;
var data = http.responseText.evalJSON(true);
if (data.contacts.length > 1) {
list.select("li").each(function(item) {
item.stopObserving("mousedown");
item.remove();
});
// Populate popup menu
for (var i = 0; i < data.contacts.length; i++) {
var contact = data.contacts[i];
var completeEmail = contact["c_cn"];
var uid = "" + contact[this.uidField];
var c_name = "" + contact['c_name'];
if (contact["c_mail"])
completeEmail += " <" + contact["c_mail"] + ">";
var node = new Element('li', { 'address': completeEmail,
'uid': uid });
var matchPosition = completeEmail.toLowerCase().indexOf(data.searchText.toLowerCase());
if (matchPosition > -1) {
var matchBefore = completeEmail.substring(0, matchPosition);
var matchText = completeEmail.substring(matchPosition, matchPosition + data.searchText.length);
var matchAfter = completeEmail.substring(matchPosition + data.searchText.length);
node.appendChild(document.createTextNode(matchBefore));
node.appendChild(new Element('strong').update(matchText));
node.appendChild(document.createTextNode(matchAfter));
}
else {
node.appendChild(document.createTextNode(completeEmail));
}
list.appendChild(node);
if (c_name.endsWith(".vlf")) {
// Keep track of list containers
node.writeAttribute("container", contact['container']);
}
if (contact["contactInfo"])
node.appendChild(document.createTextNode(" (" + contact["contactInfo"] + ")"));
$(node).observe("mousedown", this.onAddressResultClick.bindAsEventListener(this));
}
// Show popup menu
var offsetScroll = Element.cumulativeScrollOffset(input);
var offset = Element.positionedOffset(input);
if ($(document.body).hasClassName("popup") && typeof initPopupMailer == 'undefined')
// Hack for some situations where the offset must be computed differently
offset = Element.cumulativeOffset(input);
var top = offset.top - offsetScroll.top + node.offsetHeight + 3;
var height = 'auto';
var heightDiff = window.height() - offset[1];
var nodeHeight = node.getHeight();
if ((data.contacts.length * nodeHeight) > heightDiff)
// Limit the size of the popup to the window height, minus 12 pixels
height = parseInt(heightDiff/nodeHeight) * nodeHeight - 12 + 'px';
this.menu.setStyle({ top: top + "px",
left: offset[0] + "px",
height: height,
maxWidth: (window.width() - offset[0] - 12) + "px",
visibility: "visible" });
this.menu.scrollTop = 0;
document.currentPopupMenu = this.menu;
$(document.body).stopObserving("click");
$(document.body).observe("click", onBodyClickMenuHandler);
}
else {
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
if (data.contacts.length == 1) {
// Single result
var contact = data.contacts[0];
var uid = "" + contact[this.uidField];
var c_name = "" + contact['c_name'];
input.writeAttribute("uid", uid);
if (c_name.endsWith(".vlf")) {
this.writeAttribute("container", contact['container']);
}
var completeEmail = contact["c_cn"];
if (contact["c_mail"])
completeEmail += " <" + contact["c_mail"] + ">";
if (contact["c_cn"].substring(0, input.value.length).toUpperCase()
== input.value.toUpperCase())
input.value = completeEmail;
else
// The result matches email address, not user name
input.value += ' >> ' + completeEmail;
input.confirmedValue = completeEmail;
var end = input.value.length;
$(input).selectText(start, end);
this.selectedIndex = -1;
}
}
}
else
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
document.contactLookupAjaxRequest = null;
}
},
onAddressResultClick: function(event) {
var e = Event.element(event);
if (e.tagName != 'LI')
e = e.up('LI');
if (e) {
preventDefault(event);
this.value = e.readAttribute("address");
this.writeAttribute("uid", e.readAttribute("uid"));
if (e.readAttribute("container"))
this.fire("autocompletion:changedlist", e.readAttribute("container"));
else {
this.confirmedValue = this.value;
this.fire("autocompletion:changed", Event.KEY_RETURN);
}
}
// Attributes that could be changed from the object
// inheriting the inteface
uidField: "c_name",
addressBook: null,
SOGoUsersSearch: false,
excludeGroups: false,
excludeLists: false,
// Internal attributes
animationParent: null,
selectedIndex: -1,
delay: 0.750,
delayedSearch: false,
menu: null,
bind: function () {
this.menu = $('contactsMenu');
this.writeAttribute("autocomplete", "off");
this.writeAttribute("container", null);
this.confirmedValue = null;
this.observe("keydown", this.onKeydown.bindAsEventListener(this));
this.observe("blur", this.onBlur.bindAsEventListener(this));
},
onKeydown: function (event) {
if (event.ctrlKey || event.metaKey) {
this.focussed = true;
return;
}
if (event.keyCode == Event.KEY_TAB) {
if (this.confirmedValue)
this.value = this.confirmedValue;
else
this.writeAttribute("uid", null);
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
}
else if (event.keyCode == 0
|| event.keyCode == Event.KEY_BACKSPACE
|| event.keyCode == Event.KEY_DELETE
|| event.keyCode == 32 // Space
|| event.keyCode > 47) {
this.confirmedValue = null;
this.selectedIndex = -1;
if (this.delayedSearch)
window.clearTimeout(this.delayedSearch);
this.delayedSearch = this.performSearch.delay(this.delay, this);
}
else if (event.keyCode == Event.KEY_RETURN) {
preventDefault(event);
if (this.confirmedValue)
this.value = this.confirmedValue;
else
this.writeAttribute("uid", null);
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
this.selectedIndex = -1;
if (this.readAttribute("container")) {
this.confirmedValue = null;
this.fire("autocompletion:changedlist", this.readAttribute("container"));
}
else
this.fire("autocompletion:changed", event.keyCode);
}
else if (this.menu.getStyle('visibility') == 'visible') {
if (event.keyCode == Event.KEY_UP) { // Up arrow
if (this.selectedIndex > 0) {
var contacts = this.menu.select("li");
contacts[this.selectedIndex--].removeClassName("selected");
this.value = contacts[this.selectedIndex].readAttribute("address");
this.confirmedValue = this.value;
this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
contacts[this.selectedIndex].addClassName("selected");
var container = contacts[this.selectedIndex].readAttribute("container");
if (container)
this.writeAttribute("container", container);
}
}
else if (event.keyCode == Event.KEY_DOWN) { // Down arrow
var contacts = this.menu.select("li");
if (contacts.size() - 1 > this.selectedIndex) {
if (this.selectedIndex >= 0)
contacts[this.selectedIndex].removeClassName("selected");
this.selectedIndex++;
this.value = contacts[this.selectedIndex].readAttribute("address");
this.confirmedValue = this.value;
this.writeAttribute("uid", contacts[this.selectedIndex].readAttribute("uid"));
contacts[this.selectedIndex].addClassName("selected");
var container = contacts[this.selectedIndex].readAttribute("container");
if (container)
this.writeAttribute("container", container);
}
}
}
},
onBlur: function (event) {
if (this.delayedSearch)
window.clearTimeout(this.delayedSearch);
if (this.confirmedValue) {
this.value = this.confirmedValue;
if (this.readAttribute("container"))
this.fire("autocompletion:changedlist", this.readAttribute("container"));
else
this.fire("autocompletion:changed", event.keyCode);
}
else
this.writeAttribute("uid", null);
},
performSearch: function (input) {
// Perform address completion
if (document.contactLookupAjaxRequest) {
// Abort any pending request
document.contactLookupAjaxRequest.aborted = true;
document.contactLookupAjaxRequest.abort();
}
if (input.value.trim().length > minimumSearchLength) {
if (input.SOGoUsersSearch) {
var urlstr = UserFolderURL + "usersSearch?search=" + encodeURIComponent(input.value);
document.contactLookupAjaxRequest =
triggerAjaxRequest(urlstr, input.performUsersSearchCallback.bind(input), input);
}
else {
var urlstr = UserFolderURL + "Contacts/";
if (input.addressBook)
urlstr += input.addressBook + "/contact";
else
urlstr += "allContact";
urlstr += "Search?search=" + encodeURIComponent(input.value);
if (input.excludeGroups)
urlstr += "&excludeGroups=1";
if (input.excludeLists)
urlstr += "&excludeLists=1";
if (input.animationParent)
startAnimation(input.animationParent);
document.contactLookupAjaxRequest =
triggerAjaxRequest(urlstr, input.performSearchCallback.bind(input), input);
}
}
else {
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
}
},
performSearchCallback: function (http) {
if (http.readyState == 4) {
var list = this.menu.down("ul");
var input = http.callbackData;
if (http.status == 200) {
var start = input.value.length;
var data = http.responseText.evalJSON(true);
if (data.contacts.length > 1) {
list.select("li").each(function(item) {
item.stopObserving("mousedown");
item.remove();
});
// Populate popup menu
for (var i = 0; i < data.contacts.length; i++) {
var contact = data.contacts[i];
var completeEmail = contact["c_cn"];
var uid = "" + contact[this.uidField];
var c_name = "" + contact['c_name'];
if (contact["c_mail"])
completeEmail += " <" + contact["c_mail"] + ">";
var node = new Element('li', { 'address': completeEmail,
'uid': uid });
var matchPosition = completeEmail.toLowerCase().indexOf(data.searchText.toLowerCase());
if (matchPosition > -1) {
var matchBefore = completeEmail.substring(0, matchPosition);
var matchText = completeEmail.substring(matchPosition, matchPosition + data.searchText.length);
var matchAfter = completeEmail.substring(matchPosition + data.searchText.length);
node.appendChild(document.createTextNode(matchBefore));
node.appendChild(new Element('strong').update(matchText));
node.appendChild(document.createTextNode(matchAfter));
}
else {
node.appendChild(document.createTextNode(completeEmail));
}
list.appendChild(node);
if (c_name.endsWith(".vlf")) {
// Keep track of list containers
node.writeAttribute("container", contact['container']);
}
if (contact["contactInfo"])
node.appendChild(document.createTextNode(" (" + contact["contactInfo"] + ")"));
$(node).observe("mousedown", this.onAddressResultClick.bindAsEventListener(this));
}
// Show popup menu
var offsetScroll = Element.cumulativeScrollOffset(input);
var offset = Element.positionedOffset(input);
if ($(document.body).hasClassName("popup") && typeof initPopupMailer == 'undefined')
// Hack for some situations where the offset must be computed differently
offset = Element.cumulativeOffset(input);
var top = offset.top - offsetScroll.top + node.offsetHeight + 3;
var height = 'auto';
var heightDiff = window.height() - offset[1];
var nodeHeight = node.getHeight();
if ((data.contacts.length * nodeHeight) > heightDiff)
// Limit the size of the popup to the window height, minus 12 pixels
height = parseInt(heightDiff/nodeHeight) * nodeHeight - 12 + 'px';
this.menu.setStyle({ top: top + "px",
left: offset[0] + "px",
height: height,
maxWidth: (window.width() - offset[0] - 12) + "px",
visibility: "visible" });
this.menu.scrollTop = 0;
document.currentPopupMenu = this.menu;
$(document.body).stopObserving("click");
$(document.body).observe("click", onBodyClickMenuHandler);
}
else {
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
if (data.contacts.length == 1) {
// Single result
var contact = data.contacts[0];
var uid = "" + contact[this.uidField];
var c_name = "" + contact['c_name'];
input.writeAttribute("uid", uid);
if (c_name.endsWith(".vlf")) {
this.writeAttribute("container", contact['container']);
}
var completeEmail = contact["c_cn"];
if (contact["c_mail"])
completeEmail += " <" + contact["c_mail"] + ">";
if (contact["c_cn"].substring(0, input.value.length).toUpperCase()
== input.value.toUpperCase())
input.value = completeEmail;
else
// The result matches email address, not user name
input.value += ' >> ' + completeEmail;
input.confirmedValue = completeEmail;
var end = input.value.length;
$(input).selectText(start, end);
this.selectedIndex = -1;
}
}
}
else
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
document.contactLookupAjaxRequest = null;
}
},
performUsersSearchCallback: function (http) {
if (http.readyState == 4) {
var list = this.menu.down("ul");
var input = http.callbackData;
if (http.status == 200) {
var response = http.responseText.evalJSON();
if (response.length > 1) {
list.select("li").each(function(item) {
item.stopObserving("mousedown");
item.remove();
});
// Populate popup menu
for (var i = 0; i < response.length; i++) {
var c_name = response[i][1];
var completeEmail = c_name;
var c_mail = response[i][2];
var uid = response[i][3];
if (c_mail)
completeEmail += " <" + c_mail + ">";
var node = new Element('li', { 'address': completeEmail,
'uid': uid });
var matchPosition = completeEmail.toLowerCase().indexOf(input.getValue().toLowerCase());
if (matchPosition > -1) {
var matchBefore = completeEmail.substring(0, matchPosition);
var matchText = completeEmail.substring(matchPosition, matchPosition + input.getValue().length);
var matchAfter = completeEmail.substring(matchPosition + input.getValue().length);
node.appendChild(document.createTextNode(matchBefore));
node.appendChild(new Element('strong').update(matchText));
node.appendChild(document.createTextNode(matchAfter));
}
else {
node.appendChild(document.createTextNode(completeEmail));
}
list.appendChild(node);
$(node).observe("mousedown", this.onAddressResultClick.bindAsEventListener(this));
}
// Show popup menu
var offsetScroll = Element.cumulativeScrollOffset(input);
var offset = Element.positionedOffset(input);
if ($(document.body).hasClassName("popup") && typeof initPopupMailer == 'undefined')
// Hack for some situations where the offset must be computed differently
offset = Element.cumulativeOffset(input);
var top = offset.top - offsetScroll.top + node.offsetHeight + 3;
var height = 'auto';
var heightDiff = window.height() - offset[1];
var nodeHeight = node.getHeight();
if ((response.length * nodeHeight) > heightDiff)
// Limit the size of the popup to the window height, minus 12 pixels
height = parseInt(heightDiff/nodeHeight) * nodeHeight - 12 + 'px';
this.menu.setStyle({ top: top + "px",
left: offset[0] + "px",
height: height,
maxWidth: (window.width() - offset[0] - 12) + "px",
visibility: "visible" });
this.menu.scrollTop = 0;
document.currentPopupMenu = this.menu;
$(document.body).stopObserving("click");
$(document.body).observe("click", onBodyClickMenuHandler);
}
else {
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
if (response.length == 1) {
// Single result
var c_name = response[0][1];
var completeEmail = c_name;
var c_mail = response[0][2];
var c_uid = response[0][0];
input.writeAttribute("uid", c_uid);
if (c_mail)
completeEmail += " <" + c_mail + ">";
if (c_uid.substring(0, input.getValue().length).toUpperCase() == input.getValue().toUpperCase())
input.value = completeEmail;
else
// The result matches email address, not user name
input.value += ' >> ' + completeEmail;
input.confirmedValue = completeEmail;
var end = input.getValue().length;
$(input).selectText(start, end);
this.selectedIndex = -1;
}
}
}
else
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
document.contactLookupAjaxRequest = null;
}
},
onAddressResultClick: function(event) {
var e = Event.element(event);
if (e.tagName != 'LI')
e = e.up('LI');
if (e) {
preventDefault(event);
this.value = e.readAttribute("address");
this.writeAttribute("uid", e.readAttribute("uid"));
if (e.readAttribute("container"))
this.fire("autocompletion:changedlist", e.readAttribute("container"));
else {
this.confirmedValue = this.value;
this.fire("autocompletion:changed", Event.KEY_RETURN);
}
}
}
};

View File

@ -13,6 +13,23 @@ DIV.bottomToolbar
left: 2em;
right: 2em;
bottom: 8px; }
#whiteListToolbar
{ position: absolute;
height: 20px;
top: 286px;
width: 275px;
left: 235px;
background-image: url("thead_bg.png");
border: 1px solid #9B9B9B;
background-color: #E6E7E6;
}
#WhiteListAdd, #WhiteListDelete
{
border-bottom: 1px solid #9B9B9B;
border-right: 1px solid #9B9B9B;
}
#mailAccountsToolbar
{ left: 5px;
@ -47,17 +64,48 @@ DIV.listWrapper
padding: 0px;
margin-top: 2px;
border-left: 1px solid #9b9b9b;
border-right: 1px solid #9b9b9b;
background: #ccddec;}
.listWrapper TABLE TD
{ height: 22px; }
#whiteListLabel
{
position: absolute;
left: 12px;
}
#calendarCategoriesLabel
{
position: absolute;
top: 305px;
left:0;
}
#calendarCategoriesListWrapper
{ top: 232px;
{ top: 320px;
bottom: 30px;
right: 2em;
left: 2em; }
#appointmentsWhiteListWrapper
{ top: 230px;
height: 54px;
left:235px;
width:275px;
}
#tableViewWhiteList
{
width: 100%;
}
#whiteListTableHeader
{
}
#contactsCategoriesListWrapper
{ overflow: auto;
position: absolute;

View File

@ -9,6 +9,9 @@ function savePreferences(sender) {
if (sigList)
sigList.disabled = false;
// if ($("appointmentsWhiteListWrapper"))
// serializeAppointmentsWhiteList();
if ($("calendarCategoriesListWrapper"))
serializeCalendarCategories();
@ -201,97 +204,110 @@ function addDefaultEmailAddresses(event) {
}
function initPreferences() {
var tabsContainer = $("preferencesTabs");
var controller = new SOGoTabsController();
controller.attachToTabsContainer(tabsContainer);
// Inner tabs on the mail module tab
tabsContainer = $('mailOptionsTabs');
if (tabsContainer) {
var mailController = new SOGoTabsController();
mailController.attachToTabsContainer(tabsContainer);
}
_setupEvents();
// Optional function called when initializing the preferences
// Typically defined inline in the UIxAdditionalPreferences.wox template
if (typeof (initAdditionalPreferences) != "undefined")
initAdditionalPreferences();
// Color picker
$('colorPickerDialog').on('click', 'span', onColorPickerChoice);
$(document.body).on("click", onBodyClickHandler);
// Calender categories
var wrapper = $("calendarCategoriesListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
resetCalendarCategoriesColors(null);
var r = $$("#calendarCategoriesListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetCalendarTableActions();
$("calendarCategoryAdd").observe("click", onCalendarCategoryAdd);
$("calendarCategoryDelete").observe("click", onCalendarCategoryDelete);
wrapper.observe("scroll", onBodyClickHandler);
}
// Mail labels/tags
var wrapper = $("mailLabelsListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
resetMailLabelsColors(null);
var r = $$("#mailLabelsListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetMailTableActions();
$("mailLabelAdd").observe("click", onMailLabelAdd);
$("mailLabelDelete").observe("click", onMailLabelDelete);
}
// Contact categories
wrapper = $("contactsCategoriesListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
var r = $$("#contactsCategoriesListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetContactsTableActions();
$("contactsCategoryAdd").observe("click", onContactsCategoryAdd);
$("contactsCategoryDelete").observe("click", onContactsCategoryDelete);
}
if ($("replyPlacementList"))
onReplyPlacementListChange();
var button = $("addDefaultEmailAddresses");
if (button)
button.observe("click", addDefaultEmailAddresses);
button = $("changePasswordBtn");
if (button)
button.observe("click", onChangePasswordClick);
initSieveFilters();
initMailAccounts();
button = $("enableVacationEndDate");
if (button) {
jQuery("#vacationEndDate_date").closest(".date").datepicker(
{ autoclose: true, position: 'above', weekStart: $('weekStartDay').getValue() });
button.on("click", function(event) {
if (this.checked)
$("vacationEndDate_date").enable();
else
$("vacationEndDate_date").disable();
});
}
onAddOutgoingAddressesCheck();
var tabsContainer = $("preferencesTabs");
var controller = new SOGoTabsController();
controller.attachToTabsContainer(tabsContainer);
// Inner tabs on the mail module tab
tabsContainer = $('mailOptionsTabs');
if (tabsContainer) {
var mailController = new SOGoTabsController();
mailController.attachToTabsContainer(tabsContainer);
}
_setupEvents();
// Optional function called when initializing the preferences
// Typically defined inline in the UIxAdditionalPreferences.wox template
if (typeof (initAdditionalPreferences) != "undefined")
initAdditionalPreferences();
// Color picker
$('colorPickerDialog').on('click', 'span', onColorPickerChoice);
$(document.body).on("click", onBodyClickHandler);
// Calendar whiteList
var whiteList = $("appointmentsWhiteListWrapper");
if(whiteList) {
var table = whiteList.childNodesWithTag("table")[0];
var r = $$("#appointmentsWhiteListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
$("appointmentsWhiteListAdd").observe("click", onAppointmentsWhiteListAdd);
$("appointmentsWhiteListDelete").observe("click", onAppointmentsWhiteListDelete);
whiteList.observe("scroll", onBodyClickHandler);
}
// Calendar categories
var wrapper = $("calendarCategoriesListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
resetCalendarCategoriesColors(null);
var r = $$("#calendarCategoriesListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetCalendarTableActions();
$("calendarCategoryAdd").observe("click", onCalendarCategoryAdd);
$("calendarCategoryDelete").observe("click", onCalendarCategoryDelete);
wrapper.observe("scroll", onBodyClickHandler);
}
// Mail labels/tags
var wrapper = $("mailLabelsListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
resetMailLabelsColors(null);
var r = $$("#mailLabelsListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetMailTableActions();
$("mailLabelAdd").observe("click", onMailLabelAdd);
$("mailLabelDelete").observe("click", onMailLabelDelete);
}
// Contact categories
wrapper = $("contactsCategoriesListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
var r = $$("#contactsCategoriesListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetContactsTableActions();
$("contactsCategoryAdd").observe("click", onContactsCategoryAdd);
$("contactsCategoryDelete").observe("click", onContactsCategoryDelete);
}
if ($("replyPlacementList"))
onReplyPlacementListChange();
var button = $("addDefaultEmailAddresses");
if (button)
button.observe("click", addDefaultEmailAddresses);
button = $("changePasswordBtn");
if (button)
button.observe("click", onChangePasswordClick);
initSieveFilters();
initMailAccounts();
button = $("enableVacationEndDate");
if (button) {
jQuery("#vacationEndDate_date").closest(".date").datepicker(
{ autoclose: true, position: 'above', weekStart: $('weekStartDay').getValue() });
button.on("click", function(event) {
if (this.checked)
$("vacationEndDate_date").enable();
else
$("vacationEndDate_date").disable();
});
}
onAddOutgoingAddressesCheck();
}
function initSieveFilters() {
@ -977,6 +993,117 @@ function onCalendarColorEdit(e) {
onCCE(e, "calendarCategoriesListWrapper");
}
function makeEditable (element) {
element.addClassName("editing");
element.removeClassName("whiteListCell");
var span = element.down("SPAN");
span.update();
var textField = element.down("INPUT");
textField.show();
textField.focus();
textField.select();
return true;
}
function endAllEditables (e) {
var r = $$("TABLE#tableViewWhiteList TBODY TR TD");
for (var i = 0; i < r.length; i++) {
var element = $(r[i]);
if (r[i] != this && element.hasClassName("editing"))
endEditable(null, element.down("INPUT"));
}
}
function onNameEdit (e) {
endAllEditables();
if (!this.hasClassName("editing")) {
makeEditable (this);
}
}
function endEditable(event, textField) {
if (!textField)
textField = this;
var uid = textField.readAttribute("uid");
var cell = textField.up("TD");
var textSpan = cell.down("SPAN");
cell.removeClassName("editing");
cell.addClassName("whiteListCell");
textField.hide();
var tmp = textField.value;
tmp = tmp.replace (/</, "&lt;");
tmp = tmp.replace (/>/, "&gt;");
if (!uid)
cell.up("TR").addClassName("notfound");
if (tmp)
textSpan.update(tmp);
else
cell.up("TR").remove();
if (event)
Event.stop(event);
return false;
}
function onAppointmentsWhiteListAdd(e) {
var tablebody = $("appointmentsWhiteListWrapper").childNodesWithTag("table")[0].tBodies[0];
var row = new Element("tr");
var td = new Element("td").update("");
var textField = new Element("input");
var span = new Element("span");
row.addClassName("whiteListRow");
row.observe("mousedown", onRowClick);
td.addClassName ("whiteListCell");
td.observe("mousedown", endAllEditables);
td.observe("dblclick", onNameEdit);
textField.addInterface(SOGoAutoCompletionInterface);
textField.SOGoUsersSearch = true;
textField.observe("autocompletion:changed", endEditable);
textField.addClassName("textField");
td.appendChild(textField);
td.appendChild(span);
row.appendChild (td);
tablebody.appendChild(row);
$(tablebody).deselectAll();
row.selectElement();
makeEditable(td);
}
function onAppointmentsWhiteListDelete(e) {
var list = $('appointmentsWhiteListWrapper').down("TABLE").down("TBODY");
var rows = list.getSelectedNodes();
var count = rows.length;
for (var i=0; i < count; i++) {
rows[i].editionController = null;
rows[i].remove();
}
}
function serializeAppointmentsWhiteList() {
var r = $$("#appointmentsWhiteListWrapper TBODY TR");
var values = [];
for (var i = 0; i < r.length; i++) {
var tds = r[i].childElements();
var name = $(tds.first()).innerHTML;
var email = "";
values.push("\"" + name + "\": \"" + color + "\"");
}
//$("calendarCategoriesValue").value = "{ " + values.join(",\n") + "}";
}
function onCalendarCategoryAdd(e) {
var row = new Element("tr");
var nametd = new Element("td").update("");