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:
Jean Raby
2012-05-30 13:21:07 +00:00
25 changed files with 580 additions and 242 deletions

View File

@@ -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]

View File

@@ -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):

6
NEWS
View File

@@ -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)
-------------------

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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}.";

View File

@@ -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}\".";

View File

@@ -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

View File

@@ -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]];

View File

@@ -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"

View File

@@ -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__":

View File

@@ -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)

View File

@@ -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,

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -1,5 +1,3 @@
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
var listFilter = 'view_today';
var listOfSelection = null;

View File

@@ -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)

View File

@@ -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++) {

View File

@@ -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) {

View File

@@ -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
View File

@@ -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

View File

@@ -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