mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-19 11:59:28 +00:00
merge of '244af3dc56c63ece14336e6211a7dd546a398220'
and 'b293c0227fba2b5897bfbdad32b4ca68f9773305' Monotone-Parent: 244af3dc56c63ece14336e6211a7dd546a398220 Monotone-Parent: b293c0227fba2b5897bfbdad32b4ca68f9773305 Monotone-Revision: 7ce650138898448e808ecf1785eedab0ad0d3e6d Monotone-Author: jraby@inverse.ca Monotone-Date: 2012-05-30T13:21:07 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -9,6 +9,13 @@ AliasMatch /SOGo/so/ControlPanel/Products/(.*)/Resources/(.*) \
|
||||
AllowOverride None
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
|
||||
# Explicitly allow caching of static content to avoid browser specific behavior.
|
||||
# A resource's URL MUST change in order to have the client load the new version.
|
||||
<IfModule expires_module>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 year"
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
<LocationMatch "^/SOGo/so/ControlPanel/Products/.*UI/Resources/.*\.(jpg|png|gif|css|js)">
|
||||
@@ -64,4 +71,4 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
|
||||
# The remote address will appear in SOGo's log files and in the X-Forward
|
||||
# header of emails.
|
||||
RewriteEngine On
|
||||
RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
|
||||
RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
|
||||
|
||||
72
ChangeLog
72
ChangeLog
@@ -1,3 +1,75 @@
|
||||
2012-05-24 Jean Raby <jraby@inverse.ca>
|
||||
|
||||
* debian*/rules: Restart sogod after pkg upgrade (dh_installinit -R)
|
||||
* Scripts/sogo-init.d-*: add support for conditional restart.
|
||||
Patch from Romain Le Disez
|
||||
* sogo.spec: Restart sogod during post installation if it was already running
|
||||
|
||||
* Apache/SOGo.conf:
|
||||
Use mod_expires to allow long term caching of static content. (1 year)
|
||||
Note that from now on, a resource's URL _must_ change to let the client
|
||||
reload it. This is now done automatically for 'rsrc' in the wox templates,
|
||||
but must be done manually for files referenced from css and js.
|
||||
|
||||
2012-05-23 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/generic.js
|
||||
(clickEventWrapper.button_clickEventWrappe): don't invoke
|
||||
"preventDefault" on elements that do not have a tagName of "A".
|
||||
|
||||
2012-05-18 Jean Raby <jraby@inverse.ca>
|
||||
|
||||
* SoObjects/Mailer/SOGoDraftObject.m (bodyPartForAttachmentWithName):
|
||||
Merge back lost code to handle encoding of binary and rcf822 attachments.
|
||||
|
||||
2012-05-15 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* OpenChange/gen-property-selectors.py: the use of the "{}" to
|
||||
construct a set is only valid on py2.7 therefore we make use of
|
||||
set([]) instead.
|
||||
|
||||
2012-05-15 Jean Raby <jraby@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.m (_handleResourcesConflicts):
|
||||
Deny access to resources if the resource's ACL don't allow the organizer
|
||||
to read its freebusy info.
|
||||
Without this, sogo would always auto-accept invitations from 'unprivileged'
|
||||
users, potentially bypassing the multiplebooking parameter.
|
||||
|
||||
2012-05-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* OpenChange/gen-property-selectors.py: "bannedProps" is now a
|
||||
set, for faster lookups.
|
||||
|
||||
* UI/WebServerResources/UIxContactsUserFolders.js: folder ids are
|
||||
in the form "user:module/folder", therefore we must remove the
|
||||
first char, which is a slash.
|
||||
|
||||
* UI/WebServerResources/generic.js (subscribeToFolder): prepend a
|
||||
"/" to the folder name, as it comes originally in the form
|
||||
user:module/folder.
|
||||
|
||||
* UI/WebServerResources/UIxPreferences.js (initMailAccounts)
|
||||
(displayMailAccount): getElementsByTagName seems too standard for
|
||||
IE, we use $$() now so that we can invoke "each" on the result set
|
||||
without triggering an exception.
|
||||
|
||||
2012-05-09 Jean Raby <jraby@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAppointmentObject.m (PUTAction):
|
||||
Delete bitrotten code that could end up duplicating attendees.
|
||||
Behavior exposed by the new caldav tests
|
||||
|
||||
* Tests/Integration/config.py.in
|
||||
* Tests/Integration/test-caldav-scheduling.py
|
||||
* Tests/Integration/test-ical.py
|
||||
* Tests/Integration/test-davacl.py:
|
||||
Use an unprivileged webdavclient where possible.
|
||||
This would have uncovered the resources calendar autocreation bug.
|
||||
|
||||
* Tests/Integration/test-caldav-scheduling.py:
|
||||
New tests to excercise somewhat fragile code in dav autoscheduling.
|
||||
|
||||
2012-05-09 Jean Raby <jraby@inverse.ca>
|
||||
|
||||
* SoObject/SOGo/SOGoParentFolder.m (_createPersonalFolder):
|
||||
|
||||
Binary file not shown.
Binary file not shown.
6
NEWS
6
NEWS
@@ -1,4 +1,4 @@
|
||||
1.3.15 (2012-05-DD)
|
||||
1.3.15 (2012-05-15)
|
||||
-------------------
|
||||
New Features
|
||||
- sources address books are now exposed in Apple and iOS AddressBook app
|
||||
@@ -9,6 +9,7 @@ New Features
|
||||
calculator
|
||||
- new domain default (SOGoMailCustomFromEnabled) to allow users to change
|
||||
their "from" and "reply-to" headers
|
||||
- access to external calendar subscriptions (.ics) with authentication
|
||||
- new domain default (SOGoHideSystemEMail) to hide or not the system
|
||||
email. This is currently limited to CalDAV operations
|
||||
|
||||
@@ -27,6 +28,9 @@ Bug Fixes
|
||||
- when saving a draft, fixed content-transfer-encoding to properly handle
|
||||
8bit data
|
||||
- escaped single-quote in HTML view of contacts
|
||||
- fixed support of recurrent events with Apple iCal
|
||||
- fixed overbooking handling of resources with recurrent events
|
||||
- fixed auto-accept of resources when added later to an event
|
||||
|
||||
1.3.14 (2012-03-23)
|
||||
-------------------
|
||||
|
||||
@@ -106,32 +106,37 @@ extern const enum MAPITAGS MAPIStoreSupportedProperties[];
|
||||
|
||||
# hack: some properties have multiple and incompatible types. Sometimes those
|
||||
# props are not related at all...
|
||||
bannedProps = [ "PidTagBodyHtml", "PidTagFavAutosubfolders",
|
||||
"PidTagAttachDataObj", "PidTagAclTable", "PidTagAclData",
|
||||
"PidTagRulesTable", "PidTagRulesData", "PidTagDisableWinsock",
|
||||
"PidTagHierarchyServer", "PidTagOfflineAddrbookEntryid",
|
||||
"PidTagShorttermEntryidFromObject",
|
||||
"PidTagNormalMessageSizeExtended",
|
||||
"PidTagAssocMessageSizeExtended", "PidTagMessageSizeExtended",
|
||||
"PidTagOabContainerGuid",
|
||||
"PidTagOfflineAddressBookMessageClass", "PidTagScriptData",
|
||||
"PidTagOfflineAddressBookTruncatedProperties",
|
||||
"PidTagOfflineAddressBookContainerGuid",
|
||||
"PidTagOfflineAddressBookDistinguishedName",
|
||||
"PidTagOfflineAddressBookShaHash",
|
||||
"PidTagSenderTelephoneNumber", "PidTagGatewayNeedsToRefresh",
|
||||
"PidTagWlinkType", "PidTagWlinkFlags",
|
||||
"PidTagWlinkGroupClsid", "PidTagWlinkGroupName",
|
||||
"PidTagWlinkGroupHeaderID",
|
||||
"PidTagScheduleInfoDelegatorWantsCopy", "PidTagWlinkOrdinal",
|
||||
"PidTagWlinkSection", "PidTagWlinkCalendarColor",
|
||||
"PidTagWlinkAddressBookEID", "PidTagWlinkFolderType",
|
||||
"PidTagScheduleInfoDelegateNames",
|
||||
"PidTagScheduleInfoDelegateEntryIds",
|
||||
"PidTagBusiness2TelephoneNumbers",
|
||||
"PidTagHome2TelephoneNumbers",
|
||||
"PidTagAttachDataObject", "PidTagShorttermEntryIdFromObject",
|
||||
]
|
||||
bannedProps = set(["PidTagBodyHtml", "PidTagFavAutosubfolders",
|
||||
"PidTagAttachDataObj", "PidTagAclTable", "PidTagAclData",
|
||||
"PidTagRulesTable", "PidTagRulesData",
|
||||
"PidTagDisableWinsock",
|
||||
"PidTagHierarchyServer", "PidTagOfflineAddrbookEntryid",
|
||||
"PidTagShorttermEntryidFromObject",
|
||||
"PidTagNormalMessageSizeExtended",
|
||||
"PidTagAssocMessageSizeExtended",
|
||||
"PidTagMessageSizeExtended",
|
||||
"PidTagOabContainerGuid",
|
||||
"PidTagOfflineAddressBookMessageClass", "PidTagScriptData",
|
||||
"PidTagOfflineAddressBookTruncatedProperties",
|
||||
"PidTagOfflineAddressBookContainerGuid",
|
||||
"PidTagOfflineAddressBookDistinguishedName",
|
||||
"PidTagOfflineAddressBookShaHash",
|
||||
"PidTagSenderTelephoneNumber",
|
||||
"PidTagGatewayNeedsToRefresh",
|
||||
"PidTagWlinkType", "PidTagWlinkFlags",
|
||||
"PidTagWlinkGroupClsid", "PidTagWlinkGroupName",
|
||||
"PidTagWlinkGroupHeaderID",
|
||||
"PidTagScheduleInfoDelegatorWantsCopy",
|
||||
"PidTagWlinkOrdinal",
|
||||
"PidTagWlinkSection", "PidTagWlinkCalendarColor",
|
||||
"PidTagWlinkAddressBookEID", "PidTagWlinkFolderType",
|
||||
"PidTagScheduleInfoDelegateNames",
|
||||
"PidTagScheduleInfoDelegateEntryIds",
|
||||
"PidTagBusiness2TelephoneNumbers",
|
||||
"PidTagHome2TelephoneNumbers",
|
||||
"PidTagAttachDataObject",
|
||||
"PidTagShorttermEntryIdFromObject",
|
||||
])
|
||||
|
||||
def ParseExchangeH(names, lines):
|
||||
state = 0
|
||||
@@ -199,7 +204,7 @@ def FindHFile(filename):
|
||||
return found
|
||||
|
||||
def ProcessHeaders(names, hdict):
|
||||
for filename in hdict.keys():
|
||||
for filename in hdict:
|
||||
header_filename = FindHFile(filename)
|
||||
header_file = open(header_filename, "r")
|
||||
lines = header_file.readlines()
|
||||
@@ -228,8 +233,8 @@ if __name__ == "__main__":
|
||||
|
||||
names = {}
|
||||
ProcessHeaders(names,
|
||||
{ "gen_ndr/exchange.h": ParseExchangeH,
|
||||
"mapistore/mapistore_nameid.h": ParseMapistoreNameIDH })
|
||||
{"gen_ndr/exchange.h": ParseExchangeH,
|
||||
"mapistore/mapistore_nameid.h": ParseMapistoreNameIDH})
|
||||
|
||||
getters = []
|
||||
getters_idx = []
|
||||
@@ -243,12 +248,10 @@ if __name__ == "__main__":
|
||||
|
||||
prop_types = {}
|
||||
# sanitization: only take unicode version of text properties
|
||||
all_keys = names.keys()
|
||||
for name in all_keys:
|
||||
prop_tag = names[name]
|
||||
for name, prop_tag in names.iteritems():
|
||||
prop_id = prop_tag >> 16
|
||||
prop_type = prop_tag & 0xffff
|
||||
if not prop_types.has_key(prop_id):
|
||||
if not prop_id in prop_types:
|
||||
prop_types[prop_id] = []
|
||||
prop_types[prop_id].append(prop_type)
|
||||
if (prop_type & 0xfff) == 0x001e:
|
||||
@@ -256,19 +259,15 @@ if __name__ == "__main__":
|
||||
names[name] = prop_tag
|
||||
|
||||
#sanitization: report multiple types for the same keynames
|
||||
all_keys = prop_types.keys()
|
||||
for prop_id in all_keys:
|
||||
xtypes = prop_types[prop_id]
|
||||
for prop_id, xtypes in prop_types.iteritems():
|
||||
cnt = len(xtypes)
|
||||
if cnt > 1:
|
||||
print "%d types available for prop id 0x%.4x: %s" % (cnt, prop_id, ", ".join(["%.4x" % x for x in xtypes]))
|
||||
|
||||
supported_properties = []
|
||||
all_keys = names.keys()
|
||||
current_getter_idx = 0
|
||||
highest_prop_idx = 0
|
||||
for name in all_keys:
|
||||
prop_tag = names[name]
|
||||
for name, prop_tag in names.iteritems():
|
||||
supported_properties.append(" 0x%.8x" % prop_tag);
|
||||
prop_idx = (prop_tag & 0xffff0000) >> 16
|
||||
getters_idx[prop_idx] = " 0x%.4x" % current_getter_idx
|
||||
@@ -285,14 +284,14 @@ if __name__ == "__main__":
|
||||
filename = "%s.m" % output
|
||||
h_filename = "%s.h" % output
|
||||
outf = open(filename, "wb+")
|
||||
outf.write(m_template % { "getters_idx": ",\n".join(getters_idx),
|
||||
"getters": ",\n".join(getters),
|
||||
"nbr_getters": len(getters),
|
||||
"last_property": highest_prop_idx,
|
||||
"nbr_supported_properties": len(supported_properties),
|
||||
"supported_properties": ",\n".join(supported_properties),
|
||||
"filename": filename,
|
||||
"h_filename": h_filename })
|
||||
outf.write(m_template % {"getters_idx": ",\n".join(getters_idx),
|
||||
"getters": ",\n".join(getters),
|
||||
"nbr_getters": len(getters),
|
||||
"last_property": highest_prop_idx,
|
||||
"nbr_supported_properties": len(supported_properties),
|
||||
"supported_properties": ",\n".join(supported_properties),
|
||||
"filename": filename,
|
||||
"h_filename": h_filename})
|
||||
outf.close()
|
||||
|
||||
outf = open(h_filename, "wb+")
|
||||
@@ -301,7 +300,7 @@ if __name__ == "__main__":
|
||||
if ord(x) < 65 or ord(x) > 90:
|
||||
x = "_"
|
||||
exclusion = exclusion + x
|
||||
outf.write(h_template % { "prototypes": "\n".join(prototypes),
|
||||
"h_exclusion": exclusion,
|
||||
"filename": h_filename })
|
||||
outf.write(h_template % {"prototypes": "\n".join(prototypes),
|
||||
"h_exclusion": exclusion,
|
||||
"filename": h_filename })
|
||||
outf.close()
|
||||
|
||||
@@ -146,12 +146,17 @@ case "$1" in
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
if status -p "$PIDFILE" $DAEMON >&/dev/null; then
|
||||
restart
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
status -p "$PIDFILE" $DAEMON
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/$NAME
|
||||
echo "Usage: $N {start|stop|restart|status}" >&2
|
||||
echo "Usage: $N {start|stop|restart|condrestart|status}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -96,6 +96,11 @@ case "$1" in
|
||||
startproc -u $USER $DAEMON $DAEMON_OPTS || true
|
||||
echo "$NAME."
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
if checkproc -p "$PIDFILE" $DAEMON >&/dev/null; then
|
||||
restart
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
checkproc -p $PIDFILE $DAEMON
|
||||
result="$?"
|
||||
@@ -115,7 +120,7 @@ case "$1" in
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $NAME {start|stop|restart|status}" >&2
|
||||
echo "Usage: $NAME {start|stop|restart|condrestart|status}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -67,4 +67,5 @@ vtodo_class2 = "(Confidential task)";
|
||||
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}.";
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Cannot access resource: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}.";
|
||||
|
||||
@@ -67,4 +67,5 @@ vtodo_class2 = "(Tâche confidentielle)";
|
||||
= "%{Attendee} %{SentByText}choisit de reporter sa décision par rapport à votre invitation.";
|
||||
|
||||
/* Resources */
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Le nombre maximum (%{NumberOfSimultaneousBookings}) de réservation(s) simultanée(s) a été atteint pour la ressource \"%{Cn} %{SystemEmail}\".";
|
||||
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Impossible d'accéder à la resource: \"%{Cn} %{SystemEmail}\"";
|
||||
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Le nombre maximum (%{NumberOfSimultaneousBookings}) de réservation(s) simultanée(s) a été atteint pour la ressource \"%{Cn} %{SystemEmail}\".";
|
||||
|
||||
@@ -467,7 +467,19 @@
|
||||
folder = [[SOGoUser userWithLogin: currentUID]
|
||||
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]];
|
||||
|
||||
@@ -1891,16 +1903,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
// one from the request.
|
||||
[rq setContent: [[[newEvent parent] versitString] dataUsingEncoding: [rq contentEncoding]]];
|
||||
}
|
||||
|
||||
// A RECURRENCE-ID was removed so there has to be a change in the master event
|
||||
// We could also have an EXDATE added in the master component of the attendees
|
||||
// so we always compare the MASTER event.
|
||||
if (!master)
|
||||
{
|
||||
newEvent = [newEvents objectAtIndex: 0];
|
||||
oldEvent = [oldEvents objectAtIndex: 0];
|
||||
[self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent];
|
||||
}
|
||||
}
|
||||
//
|
||||
// else => attendee is responding
|
||||
|
||||
@@ -1119,7 +1119,7 @@ static NSString *userAgent = nil;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NSString *s;
|
||||
NSData *content;
|
||||
BOOL attachAsString;
|
||||
BOOL attachAsString, attachAsRFC822;
|
||||
NSString *p;
|
||||
id body;
|
||||
|
||||
@@ -1134,6 +1134,7 @@ static NSString *userAgent = nil;
|
||||
return nil;
|
||||
}
|
||||
attachAsString = NO;
|
||||
attachAsRFC822 = NO;
|
||||
|
||||
/* prepare header of body part */
|
||||
|
||||
@@ -1143,6 +1144,8 @@ static NSString *userAgent = nil;
|
||||
[map setObject:s forKey: @"content-type"];
|
||||
if ([s hasPrefix: @"text/plain"] || [s hasPrefix: @"text/html"])
|
||||
attachAsString = YES;
|
||||
else if ([s hasPrefix: @"message/rfc822"])
|
||||
attachAsRFC822 = YES;
|
||||
}
|
||||
if ((s = [self contentDispositionForAttachmentWithName:_name]))
|
||||
{
|
||||
@@ -1181,6 +1184,19 @@ static NSString *userAgent = nil;
|
||||
content = [[NSData alloc] initWithContentsOfMappedFile:p];
|
||||
[content autorelease];
|
||||
|
||||
if (attachAsRFC822)
|
||||
{
|
||||
[map setObject: @"8bit" forKey: @"content-transfer-encoding"];
|
||||
[map setObject: @"inline" forKey: @"content-disposition"];
|
||||
}
|
||||
else
|
||||
{
|
||||
content = [content dataByEncodingBase64];
|
||||
[map setObject: @"base64" forKey: @"content-transfer-encoding"];
|
||||
}
|
||||
[map setObject:[NSNumber numberWithInt:[content length]]
|
||||
forKey: @"content-length"];
|
||||
|
||||
/* Note: the -init method will create a temporary file! */
|
||||
body = [[NGMimeFileData alloc] initWithBytes:[content bytes]
|
||||
length:[content length]];
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
# setup: username must be super-user or have read-access to PUBLIC events in
|
||||
# both attendee and delegate's personal calendar
|
||||
# setup: 4 user are needed: username, superuser, attendee1, attendee1_delegate
|
||||
# superuser must be a sogo superuser...
|
||||
|
||||
hostname = "localhost"
|
||||
port = "80"
|
||||
username = "myuser"
|
||||
password = "mypass"
|
||||
|
||||
superuser = "super"
|
||||
superuser_password="pass"
|
||||
|
||||
subscriber_username = "otheruser"
|
||||
subscriber_password = "otherpass"
|
||||
|
||||
attendee1 = "user@domain.com"
|
||||
attendee1_delegate = "otheruser@domain.com"
|
||||
attendee1_username = "user"
|
||||
attendee1_password = "pass"
|
||||
|
||||
attendee1_delegate = "user2@domain.com"
|
||||
attendee1_delegate_username = "sogo2"
|
||||
attendee1_delegate_password = "sogo"
|
||||
|
||||
resource_no_overbook = "res"
|
||||
resource_can_overbook = "res-nolimit"
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# setup: username must be super-user or have read-access to PUBLIC events in
|
||||
# both attendee and delegate's personal calendar
|
||||
# setup: 4 users are needed: username, attendee1_username,
|
||||
# attendee1_delegate_username and superuser.
|
||||
# when writing new tests, avoid using superuser when not absolutely needed
|
||||
|
||||
from config import hostname, port, username, password, \
|
||||
attendee1, attendee1_delegate, \
|
||||
superuser, superuser_password, \
|
||||
attendee1, attendee1_username, \
|
||||
attendee1_password, \
|
||||
attendee1_delegate, attendee1_delegate_username, \
|
||||
attendee1_delegate_password, \
|
||||
resource_no_overbook, resource_can_overbook
|
||||
|
||||
import datetime
|
||||
@@ -98,10 +103,17 @@ class CalDAVPropertiesTest(unittest.TestCase):
|
||||
% (proppatch.response["status"],
|
||||
proppatch.response["body"]))
|
||||
|
||||
class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
class CalDAVSchedulingTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.superuser_client = webdavlib.WebDAVClient(hostname, port,
|
||||
superuser, superuser_password)
|
||||
self.client = webdavlib.WebDAVClient(hostname, port,
|
||||
username, password)
|
||||
self.attendee1_client = webdavlib.WebDAVClient(hostname, port,
|
||||
attendee1_username, attendee1_password)
|
||||
self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port,
|
||||
attendee1_delegate_username, attendee1_password)
|
||||
|
||||
utility = utilities.TestUtility(self, self.client)
|
||||
(self.user_name, self.user_email) = utility.fetchUserInfo(username)
|
||||
(self.attendee1_name, self.attendee1_email) = utility.fetchUserInfo(attendee1)
|
||||
@@ -113,18 +125,24 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1
|
||||
self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate
|
||||
|
||||
# fetch non existing event to let sogo create the calendars in the db
|
||||
self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404)
|
||||
self._getEvent(self.attendee1_client, "%snonexistent" % self.attendee1_calendar, exp_status=404)
|
||||
self._getEvent(self.attendee1_delegate_client, "%snonexistent" %
|
||||
self.attendee1_delegate_calendar, exp_status=404)
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-delegation.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
||||
None)
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-add-attendee.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%stest-add-attendee.ics" % self.attendee1_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-no-overbook.ics" % self.user_calendar, None)
|
||||
@@ -135,9 +153,13 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-can-overbook-overlap.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-remove-attendee.ics" % self.user_calendar, None)
|
||||
"%stest-rrule-exception-invitation-dance.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%stest-rrule-exception-invitation-dance.ics" % self.attendee1_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-remove-attendee-no-org.ics" % self.user_calendar, None)
|
||||
"%stest-rrule-invitation-deleted-exdate-dance.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%stest-rrule-invitation-deleted-exdate-dance.ics" % self.attendee1_calendar, None)
|
||||
|
||||
def _newEvent(self, summary="test event", uid="test", transp=0):
|
||||
transparency = ("OPAQUE", "TRANSPARENT")
|
||||
@@ -156,7 +178,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
vevent.add('dtstamp').value = now
|
||||
vevent.add('last-modified').value = now
|
||||
vevent.add('created').value = now
|
||||
|
||||
vevent.add('class').value = "PUBLIC"
|
||||
vevent.add('sequence').value = "0"
|
||||
|
||||
return newCal
|
||||
@@ -242,6 +264,41 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
% (email,
|
||||
compared_attendees[email], attendees[email]))
|
||||
|
||||
def testAddAttendee(self):
|
||||
""" add attendee after event creation """
|
||||
|
||||
# make sure the event doesn't exist
|
||||
ics_name = "test-add-attendee.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar,ics_name), None)
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%s%s" % (self.attendee1_calendar,ics_name), None)
|
||||
|
||||
# 1. create an event in the organiser's calendar
|
||||
event = self._newEvent(summary="Test add attendee", uid="Test add attendee")
|
||||
organizer = event.vevent.add('organizer')
|
||||
organizer.cn_param = self.user_name
|
||||
organizer.value = self.user_email
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||
|
||||
# 2. add an attendee
|
||||
event.add("method").value = "REQUEST"
|
||||
attendee = event.vevent.add('attendee')
|
||||
attendee.cn_param = self.attendee1_name
|
||||
attendee.rsvp_param = "TRUE"
|
||||
attendee.partstat_param = "NEEDS-ACTION"
|
||||
attendee.value = self.attendee1_email
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event,
|
||||
exp_status=204)
|
||||
|
||||
|
||||
# 3. verify that the attendee has the event
|
||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
# 4. make sure the received event match the original one
|
||||
# XXX is this enough?
|
||||
self.assertEquals(event.vevent.uid, attendee_event.vevent.uid)
|
||||
|
||||
def testUninviteAttendee(self):
|
||||
""" Remove attendee after event creation """
|
||||
|
||||
@@ -249,7 +306,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
ics_name = "test-remove-attendee.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar,ics_name), None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%s%s" % (self.attendee1_calendar,ics_name), None)
|
||||
|
||||
# 1. create an event in the organiser's calendar
|
||||
@@ -275,7 +332,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
exp_status=204)
|
||||
|
||||
# 3. verify that the attendee has the event
|
||||
attendee_event = self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
# 4. make sure the received event match the original one
|
||||
self.assertEquals(event.vevent.uid, attendee_event.vevent.uid)
|
||||
@@ -287,92 +344,42 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
exp_status=204)
|
||||
|
||||
# 6. verify that the attendee doesn't have the event anymore
|
||||
attendee_event = self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name), 404)
|
||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), 404)
|
||||
|
||||
def testUninviteAttendeeNoOrganizer(self):
|
||||
""" Remove attendee and organizer after event creation """
|
||||
def testAddAttendee(self):
|
||||
""" add attendee after event creation """
|
||||
|
||||
# make sure the event doesn't exist
|
||||
ics_name = "test-remove-attendee-no-org.ics"
|
||||
ics_name = "test-add-attendee.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar,ics_name), None)
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.attendee1_calendar,ics_name), None)
|
||||
|
||||
# 1. create an event in the organiser's calendar
|
||||
event = self._newEvent(summary="Test uninvite attendee no org", uid="Test uninvite attendee no org")
|
||||
# keep a copy around for updates without other attributes
|
||||
plainEvent = vobject.iCalendar()
|
||||
plainEvent.copy(event)
|
||||
|
||||
event = self._newEvent(summary="Test add attendee", uid="Test add attendee")
|
||||
organizer = event.vevent.add('organizer')
|
||||
organizer.cn_param = self.user_name
|
||||
organizer.value = self.user_email
|
||||
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||
|
||||
|
||||
# 2. add an attendee
|
||||
event.add("method").value = "REQUEST"
|
||||
attendee = event.vevent.add('attendee')
|
||||
attendee.cn_param = self.attendee1_name
|
||||
attendee.rsvp_param = "TRUE"
|
||||
attendee.partstat_param = "NEEDS-ACTION"
|
||||
attendee.value = self.attendee1_email
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event,
|
||||
exp_status=204)
|
||||
exp_status=204)
|
||||
|
||||
|
||||
# 3. verify that the attendee has the event
|
||||
attendee_event = self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
# 4. make sure the received event match the original one
|
||||
# XXX is this enough?
|
||||
self.assertEquals(event.vevent.uid, attendee_event.vevent.uid)
|
||||
|
||||
# 5. put the event back without attendee or organizer
|
||||
now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal"))
|
||||
plainEvent.vevent.last_modified.value = now
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), plainEvent,
|
||||
exp_status=204)
|
||||
|
||||
# 6. verify that the attendee doesn't have the event anymore
|
||||
attendee_event = self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name), 404)
|
||||
|
||||
|
||||
def testAddAttendee(self):
|
||||
""" add attendee after event creation """
|
||||
|
||||
# make sure the event doesn't exist
|
||||
ics_name = "test-add-attendee.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar,ics_name), None)
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.attendee1_calendar,ics_name), None)
|
||||
|
||||
# 1. create an event in the organiser's calendar
|
||||
event = self._newEvent(summary="Test add attendee", uid="Test add attendee")
|
||||
organizer = event.vevent.add('organizer')
|
||||
organizer.cn_param = self.user_name
|
||||
organizer.value = self.user_email
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||
|
||||
# 2. add an attendee
|
||||
event.add("method").value = "REQUEST"
|
||||
attendee = event.vevent.add('attendee')
|
||||
attendee.cn_param = self.attendee1_name
|
||||
attendee.rsvp_param = "TRUE"
|
||||
attendee.partstat_param = "NEEDS-ACTION"
|
||||
attendee.value = self.attendee1_email
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event,
|
||||
exp_status=204)
|
||||
|
||||
|
||||
# 3. verify that the attendee has the event
|
||||
attendee_event = self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
# 4. make sure the received event match the original one
|
||||
# XXX is this enough?
|
||||
self.assertEquals(event.vevent.uid, attendee_event.vevent.uid)
|
||||
|
||||
def testResourceNoOverbook(self):
|
||||
""" try to overbook a resource """
|
||||
|
||||
@@ -450,15 +457,221 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ob_ics_name), event)
|
||||
|
||||
|
||||
def testRruleExceptionInvitationDance(self):
|
||||
""" RRULE exception invitation dance """
|
||||
|
||||
# This workflow is based on what lightning 1.2.1 does
|
||||
# create a reccurring event
|
||||
# add an exception
|
||||
# invite bob to the exception:
|
||||
# bob is declined in the master event
|
||||
# bob needs-action in the exception
|
||||
# bob accepts
|
||||
# bob is declined in the master event
|
||||
# bob is accepted in the exception
|
||||
# the organizer 'uninvites' bob
|
||||
# the event disappears from bob's calendar
|
||||
# bob isn't in the master+exception event
|
||||
|
||||
ics_name = "test-rrule-exception-invitation-dance.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar, ics_name), None)
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%s%s" % (self.attendee1_calendar, ics_name), None)
|
||||
|
||||
# 1. create a recurring event in the organiser's calendar
|
||||
summary="Test reccuring exception invite cancel"
|
||||
uid="Test-recurring-exception-invite-cancel"
|
||||
event = self._newEvent(summary, uid)
|
||||
event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5"
|
||||
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||
|
||||
# read the event back from the server
|
||||
org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name))
|
||||
|
||||
# 2. Add an exception to the master event and invite attendee1 to it
|
||||
now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal"))
|
||||
org_ev.vevent.last_modified.value = now
|
||||
orig_dtstart = org_ev.vevent.dtstart.value
|
||||
orig_dtend = org_ev.vevent.dtend.value
|
||||
|
||||
ev_exception = org_ev.add("vevent")
|
||||
ev_exception.add('created').value = now
|
||||
ev_exception.add('last-modified').value = now
|
||||
ev_exception.add('dtstamp').value = now
|
||||
ev_exception.add('uid').value = uid
|
||||
ev_exception.add('summary').value = summary
|
||||
# out of laziness, add the exception for the first occurence of the event
|
||||
recurrence_id = orig_dtstart
|
||||
ev_exception.add('recurrence-id').value = recurrence_id
|
||||
|
||||
ev_exception.add('transp').value = "OPAQUE"
|
||||
ev_exception.add('description').value = "Exception"
|
||||
ev_exception.add('sequence').value = "1"
|
||||
ev_exception.add('dtstart').value = orig_dtstart
|
||||
ev_exception.add('dtend').value = orig_dtend
|
||||
|
||||
# 2.1 Add attendee1 and organizer to the exception
|
||||
organizer = ev_exception.add('organizer')
|
||||
organizer.cn_param = self.user_name
|
||||
organizer.partstat_param = "ACCEPTED"
|
||||
organizer.value = self.user_email
|
||||
attendee = ev_exception.add('attendee')
|
||||
attendee.cn_param = self.attendee1_name
|
||||
attendee.rsvp_param = "TRUE"
|
||||
attendee.role_param = "REQ-PARTICIPANT"
|
||||
attendee.partstat_param = "NEEDS-ACTION"
|
||||
attendee.value = self.attendee1_email
|
||||
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), org_ev,
|
||||
exp_status=204)
|
||||
|
||||
# 3. Make sure the attendee got the event
|
||||
attendee_ev = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
for ev in attendee_ev.vevent_list:
|
||||
try:
|
||||
if (ev.recurrence_id.value):
|
||||
attendee_ev_exception = ev
|
||||
except:
|
||||
attendee_ev_master = ev
|
||||
|
||||
# make sure sogo doesn't duplicate attendees - yes, we've seen that
|
||||
self.assertEquals(len(attendee_ev_master.attendee_list), 1)
|
||||
self.assertEquals(len(attendee_ev_exception.attendee_list), 1)
|
||||
|
||||
# 4. The master event must contain the invitation, declined
|
||||
self.assertEquals(attendee_ev_master.attendee.partstat_param, "DECLINED")
|
||||
|
||||
# 5. The exception event contain the invitation, NEEDS-ACTION
|
||||
self.assertEquals(attendee_ev_exception.attendee.partstat_param, "NEEDS-ACTION")
|
||||
|
||||
# 6. attendee accepts invitation
|
||||
attendee_ev_exception.attendee.partstat_param = "ACCEPTED"
|
||||
self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name),
|
||||
attendee_ev, exp_status=204)
|
||||
|
||||
# fetch the organizer's event
|
||||
org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name))
|
||||
for ev in org_ev.vevent_list:
|
||||
try:
|
||||
if (ev.recurrence_id.value):
|
||||
org_ev_exception = ev
|
||||
except:
|
||||
org_ev_master = ev
|
||||
|
||||
# make sure sogo doesn't duplicate attendees
|
||||
self.assertEquals(len(org_ev_master.attendee_list), 1)
|
||||
self.assertEquals(len(org_ev_exception.attendee_list), 1)
|
||||
|
||||
# 7. Make sure organizer got the accept for the exception and
|
||||
# that the attendee is still declined in the master
|
||||
self.assertEquals(org_ev_exception.attendee.partstat_param, "ACCEPTED")
|
||||
self.assertEquals(org_ev_master.attendee.partstat_param, "DECLINED")
|
||||
|
||||
# 8. delete the attendee from the master event (uninvite)
|
||||
# The event should be deleted from the attendee's calendar
|
||||
del org_ev_exception.attendee
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name),
|
||||
org_ev, exp_status=204)
|
||||
del org_ev_master.attendee
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name),
|
||||
org_ev, exp_status=204)
|
||||
|
||||
self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name),
|
||||
exp_status=404)
|
||||
|
||||
# now be happy
|
||||
|
||||
def testRruleInvitationDeleteExdate(self):
|
||||
"""RRULE invitation delete exdate dance"""
|
||||
|
||||
# Workflow:
|
||||
# Create an recurring event and invite Bob
|
||||
# Add an exdate to the master event
|
||||
# Verify that the exdate has propagated to Bob's calendar
|
||||
# Add an exdate to bob's version of the event
|
||||
# Verify that an exception has been created in the org's calendar
|
||||
# and that bob is 'declined'
|
||||
|
||||
ics_name = "test-rrule-invitation-deleted-exdate-dance.ics"
|
||||
self._deleteEvent(self.client,
|
||||
"%s%s" % (self.user_calendar, ics_name), None)
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%s%s" % (self.attendee1_calendar, ics_name), None)
|
||||
|
||||
# 1. create a recurring event in the organiser's calendar
|
||||
summary="Test-rrule-invitation-deleted-exdate-dance"
|
||||
uid=summary
|
||||
event = self._newEvent(summary, uid)
|
||||
event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5"
|
||||
organizer = event.vevent.add('organizer')
|
||||
organizer.cn_param = self.user_name
|
||||
organizer.partstat_param = "ACCEPTED"
|
||||
organizer.value = self.user_email
|
||||
attendee = event.vevent.add('attendee')
|
||||
attendee.cn_param = self.attendee1_name
|
||||
attendee.rsvp_param = "TRUE"
|
||||
attendee.role_param = "REQ-PARTICIPANT"
|
||||
attendee.partstat_param = "NEEDS-ACTION"
|
||||
attendee.value = self.attendee1_email
|
||||
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event)
|
||||
|
||||
# 2. Make sure the attendee got it
|
||||
self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name))
|
||||
|
||||
# 3. Add exdate to master event
|
||||
org_ev=self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name))
|
||||
orig_dtstart = org_ev.vevent.dtstart.value
|
||||
# exdate is a list in vobject.icalendar
|
||||
org_exdate = [orig_dtstart.astimezone(dateutil.tz.gettz("UTC"))]
|
||||
org_ev.vevent.add('exdate').value = org_exdate
|
||||
self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), org_ev, exp_status=204)
|
||||
|
||||
# 4. make sure the attendee has the exdate
|
||||
attendee_ev = self._getEvent(self.attendee1_client, "%s%s" %
|
||||
(self.attendee1_calendar, ics_name))
|
||||
self.assertEqual(org_exdate, attendee_ev.vevent.exdate.value)
|
||||
|
||||
# 5. Create an exdate in the attendee's calendar
|
||||
new_exdate = orig_dtstart + datetime.timedelta(days=2)
|
||||
attendee_exdate = [new_exdate.astimezone(dateutil.tz.gettz("UTC"))]
|
||||
attendee_ev.vevent.add('exdate').value = attendee_exdate
|
||||
now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal"))
|
||||
attendee_ev.vevent.last_modified.value = now
|
||||
self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name),
|
||||
attendee_ev, exp_status=204)
|
||||
|
||||
# 6. Make sure the attendee is:
|
||||
# needs-action in master event
|
||||
# declined in the new exception created by the exdate above
|
||||
org_ev=self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name))
|
||||
for ev in org_ev.vevent_list:
|
||||
try:
|
||||
if (ev.recurrence_id.value == attendee_exdate[0]):
|
||||
org_ev_exception = ev
|
||||
except:
|
||||
org_ev_master = ev
|
||||
|
||||
self.assertTrue(org_ev_exception)
|
||||
# make sure sogo doesn't duplicate attendees
|
||||
self.assertEquals(len(org_ev_master.attendee_list), 1)
|
||||
self.assertEquals(len(org_ev_exception.attendee_list), 1)
|
||||
|
||||
self.assertEqual(org_ev_master.attendee.partstat_param, "NEEDS-ACTION");
|
||||
self.assertEqual(org_ev_exception.attendee.partstat_param, "DECLINED");
|
||||
|
||||
def testInvitationDelegation(self):
|
||||
""" invitation delegation """
|
||||
|
||||
# the invitation must not exist
|
||||
self._deleteEvent(self.client,
|
||||
"%stest-delegation.ics" % self.user_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar, None)
|
||||
self._deleteEvent(self.client,
|
||||
self._deleteEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
||||
None)
|
||||
|
||||
@@ -482,7 +695,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
"%stest-delegation.ics" % self.user_calendar,
|
||||
invitation)
|
||||
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics"
|
||||
% self.attendee1_calendar)
|
||||
self._compareAttendees(att_inv, invitation)
|
||||
@@ -502,19 +715,19 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
delegate.partstat_param = "NEEDS-ACTION"
|
||||
delegate.value = self.attendee1_delegate_email
|
||||
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, invitation,
|
||||
self.attendee1_email, [self.attendee1_delegate_email])
|
||||
invitation.method.value = "REPLY"
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, invitation,
|
||||
self.attendee1_email, [self.user_email])
|
||||
del invitation.method
|
||||
self._putEvent(self.client,
|
||||
self._putEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar,
|
||||
invitation, 204)
|
||||
|
||||
del_inv = self._getEvent(self.client,
|
||||
del_inv = self._getEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics"
|
||||
% self.attendee1_delegate_calendar)
|
||||
self._compareAttendees(del_inv, invitation)
|
||||
@@ -528,18 +741,18 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
|
||||
invitation.add("method").value = "REPLY"
|
||||
delegate.partstat_param = "ACCEPTED"
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_delegate_client,
|
||||
self.attendee1_delegate_calendar, invitation,
|
||||
self.attendee1_delegate_email, [self.user_email, self.attendee1_email])
|
||||
del invitation.method
|
||||
self._putEvent(self.client,
|
||||
self._putEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
||||
invitation, 204)
|
||||
|
||||
org_inv = self._getEvent(self.client,
|
||||
"%stest-delegation.ics" % self.user_calendar)
|
||||
self._compareAttendees(org_inv, invitation)
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar)
|
||||
self._compareAttendees(att_inv, invitation)
|
||||
|
||||
@@ -551,7 +764,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
cancellation.copy(invitation)
|
||||
cancellation.add("method").value = "CANCEL"
|
||||
cancellation.vevent.sequence.value = "1"
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, cancellation,
|
||||
self.attendee1_email, [self.attendee1_delegate_email])
|
||||
|
||||
@@ -560,12 +773,12 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
del attendee1.delegated_to_param
|
||||
invitation.add("method").value = "REPLY"
|
||||
invitation.vevent.remove(delegate)
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, invitation,
|
||||
self.attendee1_email, [self.user_email])
|
||||
|
||||
del invitation.method
|
||||
self._putEvent(self.client,
|
||||
self._putEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar,
|
||||
invitation, 204)
|
||||
|
||||
@@ -573,7 +786,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
"%stest-delegation.ics" % self.user_calendar)
|
||||
self._compareAttendees(org_inv, invitation)
|
||||
|
||||
del_inv = self._getEvent(self.client,
|
||||
del_inv = self._getEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar, 404)
|
||||
|
||||
# 5. org updates inv.
|
||||
@@ -595,7 +808,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
"%stest-delegation.ics" % self.user_calendar,
|
||||
invitation, 204)
|
||||
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar)
|
||||
self._compareAttendees(att_inv, invitation)
|
||||
|
||||
@@ -613,22 +826,22 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
delegate.partstat_param = "NEEDS-ACTION"
|
||||
delegate.value = self.attendee1_delegate_email
|
||||
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, invitation,
|
||||
self.attendee1_email, [self.attendee1_delegate_email])
|
||||
invitation.method.value = "REPLY"
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_client,
|
||||
self.attendee1_calendar, invitation,
|
||||
self.attendee1_email, [self.user_email])
|
||||
del invitation.method
|
||||
self._putEvent(self.client,
|
||||
self._putEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar,
|
||||
invitation, 204)
|
||||
|
||||
org_inv = self._getEvent(self.client,
|
||||
"%stest-delegation.ics" % self.user_calendar)
|
||||
self._compareAttendees(org_inv, invitation)
|
||||
del_inv = self._getEvent(self.client,
|
||||
del_inv = self._getEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics"
|
||||
% self.attendee1_delegate_calendar)
|
||||
self._compareAttendees(del_inv, invitation)
|
||||
@@ -638,19 +851,19 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
|
||||
invitation.add("method").value = "REPLY"
|
||||
delegate.partstat_param = "ACCEPTED"
|
||||
self._postEvent(self.client,
|
||||
self._postEvent(self.attendee1_delegate_client,
|
||||
self.attendee1_delegate_calendar, invitation,
|
||||
self.attendee1_delegate_email, [self.user_email,
|
||||
self.attendee1_email])
|
||||
del invitation.method
|
||||
self._putEvent(self.client,
|
||||
self._putEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar,
|
||||
invitation, 204)
|
||||
|
||||
org_inv = self._getEvent(self.client,
|
||||
"%stest-delegation.ics" % self.user_calendar)
|
||||
self._compareAttendees(org_inv, invitation)
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar)
|
||||
self._compareAttendees(att_inv, invitation)
|
||||
|
||||
@@ -674,10 +887,10 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
"%stest-delegation.ics" % self.user_calendar,
|
||||
invitation, 204)
|
||||
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar)
|
||||
self._compareAttendees(att_inv, invitation)
|
||||
del_inv = self._getEvent(self.client,
|
||||
del_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar)
|
||||
self._compareAttendees(del_inv, invitation)
|
||||
|
||||
@@ -702,9 +915,9 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
|
||||
"%stest-delegation.ics" % self.user_calendar,
|
||||
invitation, 204)
|
||||
|
||||
att_inv = self._getEvent(self.client,
|
||||
att_inv = self._getEvent(self.attendee1_client,
|
||||
"%stest-delegation.ics" % self.attendee1_calendar, 404)
|
||||
del_inv = self._getEvent(self.client,
|
||||
del_inv = self._getEvent(self.attendee1_delegate_client,
|
||||
"%stest-delegation.ics" % self.attendee1_delegate_calendar, 404)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from config import hostname, port, username, password, subscriber_username, subscriber_password
|
||||
from config import hostname, port, username, password, subscriber_username, subscriber_password, \
|
||||
superuser, superuser_password
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
@@ -25,7 +26,7 @@ import utilities
|
||||
class DAVCalendarSuperUserAclTest(unittest.TestCase):
|
||||
def __init__(self, arg):
|
||||
self.client = webdavlib.WebDAVClient(hostname, port,
|
||||
username, password)
|
||||
superuser, superuser_password)
|
||||
self.resource = "/SOGo/dav/%s/Calendar/test-dav-superuser-acl/" % subscriber_username
|
||||
self.filename = "suevent.ics"
|
||||
self.url = "%s%s" % (self.resource, self.filename)
|
||||
@@ -949,6 +950,8 @@ class DAVPublicAccessTest(unittest.TestCase):
|
||||
class DAVCalendarPublicAclTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.createdRsrc = None
|
||||
self.superuser_client = webdavlib.WebDAVClient(hostname, port,
|
||||
superuser, superuser_password)
|
||||
self.client = webdavlib.WebDAVClient(hostname, port,
|
||||
username, password)
|
||||
self.subscriber_client = webdavlib.WebDAVClient(hostname, port,
|
||||
@@ -959,7 +962,7 @@ class DAVCalendarPublicAclTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
if self.createdRsrc is not None:
|
||||
delete = webdavlib.WebDAVDELETE(self.createdRsrc)
|
||||
self.client.execute(delete)
|
||||
self.superuser_client.execute(delete)
|
||||
|
||||
def testCollectionAccessNormalUser(self):
|
||||
"""normal user access to (non-)shared resource from su"""
|
||||
@@ -1091,7 +1094,7 @@ class DAVCalendarPublicAclTest(unittest.TestCase):
|
||||
for rsrc in [ 'personal', 'test-dav-acl' ]:
|
||||
resource = '%s%s/' % (parentColl, rsrc)
|
||||
mkcol = webdavlib.WebDAVMKCOL(resource)
|
||||
self.client.execute(mkcol)
|
||||
self.superuser_client.execute(mkcol)
|
||||
acl_utility = utilities.TestCalendarACLUtility(self,
|
||||
self.subscriber_client,
|
||||
resource)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from config import hostname, port, username, password, subscriber_username
|
||||
# FIXME: we should avoid using superuser if possible
|
||||
|
||||
from config import hostname, port, username, password, subscriber_username, \
|
||||
superuser, superuser_password
|
||||
|
||||
import unittest
|
||||
import sogotests
|
||||
@@ -48,7 +51,7 @@ class iCalTest(unittest.TestCase):
|
||||
for x in members ]
|
||||
props = { "{DAV:}group-member-set": membersHref }
|
||||
proppatch = webdavlib.WebDAVPROPPATCH(resource, props)
|
||||
client = webdavlib.WebDAVClient(hostname, port, username, password)
|
||||
client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password)
|
||||
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
|
||||
client.execute(proppatch)
|
||||
self.assertEquals(proppatch.response["status"], 207,
|
||||
@@ -60,7 +63,7 @@ class iCalTest(unittest.TestCase):
|
||||
resource = '/SOGo/dav/%s/' % user
|
||||
propfind = webdavlib.WebDAVPROPFIND(resource,
|
||||
["{DAV:}group-membership"], 0)
|
||||
client = webdavlib.WebDAVClient(hostname, port, username, password)
|
||||
client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password)
|
||||
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
|
||||
client.execute(propfind)
|
||||
|
||||
@@ -73,7 +76,7 @@ class iCalTest(unittest.TestCase):
|
||||
resource = '/SOGo/dav/%s/' % user
|
||||
prop = "{http://calendarserver.org/ns/}calendar-proxy-%s-for" % perm
|
||||
propfind = webdavlib.WebDAVPROPFIND(resource, [prop], 0)
|
||||
client = webdavlib.WebDAVClient(hostname, port, username, password)
|
||||
client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password)
|
||||
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
|
||||
client.execute(propfind)
|
||||
|
||||
@@ -136,7 +139,7 @@ class iCalTest(unittest.TestCase):
|
||||
|
||||
def testCalendarProxy2(self):
|
||||
"""calendar-proxy as used from SOGo"""
|
||||
client = webdavlib.WebDAVClient(hostname, port, username, password)
|
||||
client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password)
|
||||
client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)"
|
||||
personal_resource = "/SOGo/dav/%s/Calendar/personal/" % username
|
||||
dav_utility = utilities.TestCalendarACLUtility(self,
|
||||
|
||||
@@ -1509,45 +1509,45 @@ function currentFolderIsRemote() {
|
||||
return rc;
|
||||
}
|
||||
|
||||
function startDragging(e, itm) {
|
||||
var row = e.target;
|
||||
var handle = $("dragDropVisual");
|
||||
function startDragging(event, ui) {
|
||||
var row = event.target;
|
||||
var handle = ui.helper;
|
||||
var contacts = $('contactsList').getSelectedRowsId();
|
||||
var count = contacts.length;
|
||||
|
||||
if (count == 0 || contacts.indexOf(row.id) < 0) {
|
||||
onRowClick(e, $(row.id));
|
||||
onRowClick(event, $(row.id));
|
||||
contacts = $("contactsList").getSelectedRowsId();
|
||||
count = contacts.length;
|
||||
}
|
||||
handle.update(count);
|
||||
handle.html(count);
|
||||
|
||||
if (e.shiftKey || currentFolderIsRemote()) {
|
||||
handle.addClassName("copy");
|
||||
if (event.shiftKey || currentFolderIsRemote()) {
|
||||
handle.addClass("copy");
|
||||
}
|
||||
handle.show();
|
||||
}
|
||||
|
||||
function whileDragging(e, itm) {
|
||||
if (e) {
|
||||
var handle = $("dragDropVisual");
|
||||
if (e.shiftKey || currentFolderIsRemote())
|
||||
handle.addClassName ("copy");
|
||||
else if (handle.hasClassName ("copy"))
|
||||
handle.removeClassName ("copy");
|
||||
function whileDragging(event, ui) {
|
||||
if (event) {
|
||||
var handle = ui.helper;
|
||||
if (event.shiftKey || currentFolderIsRemote())
|
||||
handle.addClass("copy");
|
||||
else if (handle.hasClass("copy"))
|
||||
handle.removeClass("copy");
|
||||
}
|
||||
}
|
||||
|
||||
function stopDragging(e, itm) {
|
||||
var handle = $("dragDropVisual");
|
||||
function stopDragging(event, ui) {
|
||||
var handle = ui.helper;
|
||||
handle.hide();
|
||||
if (handle.hasClassName("copy"))
|
||||
handle.removeClassName("copy");
|
||||
if (handle.hasClass("copy"))
|
||||
handle.removeClass("copy");
|
||||
}
|
||||
|
||||
function dropAction(event, ui) {
|
||||
var action = "move";
|
||||
if ($("dragDropVisual").hasClassName("copy"))
|
||||
if (ui.helper.hasClass("copy"))
|
||||
action = "copy";
|
||||
else
|
||||
$('contactView').update();
|
||||
|
||||
@@ -2932,7 +2932,7 @@ function configureDroppables() {
|
||||
drop: dropAction });
|
||||
}
|
||||
|
||||
function startDragging(e, ui) {
|
||||
function startDragging(event, ui) {
|
||||
var handle = ui.helper;
|
||||
var count = $('messageListBody').getSelectedRowsId().length;
|
||||
|
||||
@@ -2942,29 +2942,29 @@ function startDragging(e, ui) {
|
||||
}
|
||||
handle.html(count);
|
||||
|
||||
if (e.shiftKey) {
|
||||
handle.addClassName("copy");
|
||||
if (event.shiftKey) {
|
||||
handle.addClass("copy");
|
||||
}
|
||||
handle.show();
|
||||
}
|
||||
|
||||
function whileDragging(e, ui) {
|
||||
if (e) {
|
||||
var handle = $("dragDropVisual");
|
||||
if (e.shiftKey)
|
||||
handle.addClassName("copy");
|
||||
else if (handle.hasClassName("copy"))
|
||||
handle.removeClassName("copy");
|
||||
function whileDragging(event, ui) {
|
||||
if (event) {
|
||||
var handle = ui.helper;
|
||||
if (event.shiftKey)
|
||||
handle.addClass("copy");
|
||||
else if (handle.hasClass("copy"))
|
||||
handle.removeClass("copy");
|
||||
}
|
||||
}
|
||||
|
||||
function stopDragging() {
|
||||
var handle = $("dragDropVisual");
|
||||
function stopDragging(event, ui) {
|
||||
var handle = ui.helper;
|
||||
handle.hide();
|
||||
if (handle.hasClassName("copy"))
|
||||
handle.removeClassName("copy");
|
||||
if (handle.hasClass("copy"))
|
||||
handle.removeClass("copy");
|
||||
for (var i = 0; i < accounts.length; i++) {
|
||||
handle.removeClassName("account" + i);
|
||||
handle.removeClass("account" + i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2975,7 +2975,7 @@ function dropAction(event, ui) {
|
||||
var destAct = destination.getAttribute("dataname").split("/")[1];
|
||||
if (sourceAct == destAct) {
|
||||
var f;
|
||||
if ($("dragDropVisual").hasClassName("copy")) {
|
||||
if (ui.helper.hasClass("copy")) {
|
||||
// Message(s) copied
|
||||
f = onMailboxMenuCopy.bind(destination);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
var listFilter = 'view_today';
|
||||
|
||||
var listOfSelection = null;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* -*- Mode: java; tab-width: 2; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
var d;
|
||||
|
||||
function onSearchFormSubmit() {
|
||||
@@ -182,7 +180,7 @@ function addFolderBranchToTree(tree, user, folder, nodeId, subId, isLast) {
|
||||
icon += 'tb-mail-addressbook-flat-16x16.png';
|
||||
else
|
||||
icon += 'calendar-folder-16x16.png';
|
||||
var folderId = user + ":" + folderInfos[1];
|
||||
var folderId = user + ":" + folderInfos[1].substr(1);
|
||||
var name = folderInfos[0]; // name has the format "Folername (Firstname Lastname <email>)"
|
||||
var pos = name.lastIndexOf(' (');
|
||||
if (pos > -1)
|
||||
|
||||
@@ -469,26 +469,23 @@ function initMailAccounts() {
|
||||
}
|
||||
}
|
||||
|
||||
var info = $("accountInfo");
|
||||
var inputs = info.getElementsByTagName("input");
|
||||
var inputs = $$("#accountInfo input");
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
$(inputs[i]).observe("change", onMailAccountInfoChange);
|
||||
}
|
||||
|
||||
info = $("identityInfo");
|
||||
inputs = info.getElementsByTagName("input");
|
||||
inputs = $$("#identityInfo input");
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
$(inputs[i]).observe("change", onMailIdentityInfoChange);
|
||||
}
|
||||
$("actSignature").observe("click", onMailIdentitySignatureClick);
|
||||
displayMailAccount(mailAccounts[0], true);
|
||||
|
||||
info = $("returnReceiptsInfo");
|
||||
inputs = info.getElementsByTagName("input");
|
||||
inputs = $$("#returnReceiptsInfo input");
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
$(inputs[i]).observe("change", onMailReceiptInfoChange);
|
||||
}
|
||||
inputs = info.getElementsByTagName("select");
|
||||
inputs = $$("#returnReceiptsInfo select");
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
$(inputs[i]).observe("change", onMailReceiptActionChange);
|
||||
}
|
||||
@@ -656,13 +653,11 @@ function onMailAccountEntryClick(event) {
|
||||
}
|
||||
|
||||
function displayMailAccount(mailAccount, readOnly) {
|
||||
var fieldSet = $("accountInfo");
|
||||
var inputs = $(fieldSet.getElementsByTagName("input"));
|
||||
var inputs = $$("#accountInfo input");
|
||||
inputs.each(function (i) { i.disabled = readOnly;
|
||||
i.mailAccount = mailAccount; });
|
||||
|
||||
fieldSet = $("identityInfo");
|
||||
inputs = $(fieldSet.getElementsByTagName("input"));
|
||||
inputs = $$("#identityInfo input");
|
||||
inputs.each(function (i) { i.mailAccount = mailAccount; });
|
||||
if (!mailCustomFromEnabled) {
|
||||
for (var i = 0; i < 2; i++) {
|
||||
|
||||
@@ -47,9 +47,17 @@ var removeFolderRequestCount = 0;
|
||||
// Email validation regexp
|
||||
var emailRE = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i;
|
||||
|
||||
|
||||
/* This function enables the execution of a wrapper function just before the
|
||||
user callback is executed. The wrapper in question executes "preventDefault"
|
||||
to the event parameter if and only when "this" is a link. The goal of this
|
||||
operation is to prevent links with attached even handlers to be followed,
|
||||
including those with an href set to "#". */
|
||||
function clickEventWrapper(functionRef) {
|
||||
function button_clickEventWrapper(event) {
|
||||
preventDefault(event);
|
||||
if (this.tagName == "A") {
|
||||
preventDefault(event);
|
||||
}
|
||||
return functionRef.apply(this, [event]);
|
||||
}
|
||||
|
||||
@@ -1211,7 +1219,7 @@ function subscribeToFolder(refreshCallback, refreshCallbackData) {
|
||||
var folderPath = folderData[1];
|
||||
if (username != UserLogin) {
|
||||
var url = (UserFolderURL + "../" + username
|
||||
+ folderPath + "/subscribe");
|
||||
+ "/" + folderPath + "/subscribe");
|
||||
if (document.subscriptionAjaxRequest) {
|
||||
document.subscriptionAjaxRequest.aborted = true;
|
||||
document.subscriptionAjaxRequest.abort();
|
||||
@@ -1266,10 +1274,10 @@ function accessToSubscribedFolder(serverFolder) {
|
||||
var username = parts[0];
|
||||
var paths = parts[1].split("/");
|
||||
if (username == UserLogin) {
|
||||
folder = paths[2];
|
||||
folder = paths[1];
|
||||
}
|
||||
else {
|
||||
folder = "/" + username.asCSSIdentifier() + "_" + paths[2];
|
||||
folder = "/" + username.asCSSIdentifier() + "_" + paths[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -2047,7 +2055,7 @@ function _showPromptDialog(title, label, callback, defaultValue) {
|
||||
document.body.appendChild(dialog);
|
||||
dialogs[title+label] = dialog;
|
||||
}
|
||||
jQuery(dialog).fadeIn('fast', function () { dialog.down("input").focus(); });
|
||||
jQuery(dialog).fadeIn('fast', function () { dialog.down("input").focus(); });
|
||||
}
|
||||
|
||||
function showSelectDialog(title, label, options, button, callbackFcn, callbackArg, defaultValue) {
|
||||
@@ -2098,14 +2106,10 @@ function _showSelectDialog(title, label, options, button, callbackFcn, callbackA
|
||||
|
||||
function showAuthenticationDialog(label, callback) {
|
||||
var div = $("bgDialogDiv");
|
||||
if (div && div.visible() && div.getOpacity() > 0) {
|
||||
log("push");
|
||||
if (div && div.visible() && div.getOpacity() > 0)
|
||||
dialogsStack.push(_showAuthenticationDialog.bind(this, label, callback));
|
||||
}
|
||||
else {
|
||||
log("show");
|
||||
else
|
||||
_showAuthenticationDialog(label, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function _showAuthenticationDialog(label, callback) {
|
||||
|
||||
@@ -85,7 +85,7 @@ install-arch: build-arch
|
||||
binary-arch: build-arch install-arch
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installinit -r
|
||||
dh_installinit -R
|
||||
dh_installlogrotate
|
||||
dh_installcron
|
||||
dh_installchangelogs ChangeLog
|
||||
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -84,7 +84,7 @@ install-arch: build-arch
|
||||
binary-arch: build-arch install-arch
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installinit -r
|
||||
dh_installinit -R
|
||||
dh_installlogrotate
|
||||
dh_installcron
|
||||
dh_installchangelogs ChangeLog
|
||||
|
||||
@@ -292,6 +292,7 @@ if ! id sogo >& /dev/null; then /usr/sbin/adduser sogo > /dev/null 2>&1; fi
|
||||
# update timestamp on imgs,css,js to let apache know the files changed
|
||||
find %{_libdir}/GNUstep/SOGo/WebServerResources -exec touch {} \;
|
||||
/sbin/chkconfig --add sogod
|
||||
/etc/init.d/sogod condrestart >&/dev/null
|
||||
|
||||
%preun
|
||||
if [ "$1" == "0" ]
|
||||
@@ -311,6 +312,9 @@ fi
|
||||
|
||||
# ********************************* changelog *************************
|
||||
%changelog
|
||||
* Fri May 24 2012 Jean Raby <jraby@inverse.ca>
|
||||
- %post: restart sogo if it was running before rpm install
|
||||
|
||||
* Fri Mar 16 2012 Jean Raby <jraby@inverse.ca>
|
||||
- %post: update timestamp on imgs,css,js to let apache know the files changed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user