From 320e9256d1f14125ac610bb068ff18e84592bfb9 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 6 May 2010 19:30:14 +0000 Subject: [PATCH] Monotone-Parent: 044582e9ad4ddb9d86e36c14f35c91195c979b7c Monotone-Revision: 8f6b1948a4de37d255b2c1ab1d3f1baa780f455d Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-05-06T19:30:14 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 26 ++++++ SOPE/sope-patchset-r1664.diff | 5 +- .../Appointments/SOGoAppointmentFolder.h | 3 + .../Appointments/SOGoAppointmentFolder.m | 48 +++++++++++ SoObjects/Appointments/SOGoFreeBusyObject.m | 4 +- Tests/Integration/test-caldav-scheduling.py | 82 +++++++++++++++++++ .../Localizable.strings | 2 + UI/Scheduler/Czech.lproj/Localizable.strings | 2 + UI/Scheduler/Dutch.lproj/Localizable.strings | 2 + .../English.lproj/Localizable.strings | 2 + UI/Scheduler/French.lproj/Localizable.strings | 14 ++-- UI/Scheduler/German.lproj/Localizable.strings | 2 + .../Hungarian.lproj/Localizable.strings | 2 + .../Italian.lproj/Localizable.strings | 2 + .../Russian.lproj/Localizable.strings | 2 + .../Spanish.lproj/Localizable.strings | 2 + .../Swedish.lproj/Localizable.strings | 2 + UI/Scheduler/UIxCalendarProperties.m | 19 +++++ UI/Scheduler/Welsh.lproj/Localizable.strings | 2 + .../SchedulerUI/UIxCalendarProperties.wox | 10 +++ UI/WebServerResources/SchedulerUI.js | 4 + 21 files changed, 227 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index e8ca12734..fe05d2acc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2010-05-06 Wolfgang Sourdeau + + * Tests/Integration/test-caldav-scheduling.py + (CalDAVPropertiesTest): new class for testing caldav-specific + properties + (CalDAVPropertiesTest.testDavScheduleCalendarTransparency): new + test method for testing "schedule-calendar-transp". + + * UI/Scheduler/UIxCalendarProperties.m (-includeFreeBusy) + (-setIncludeFreeBusy): new template accessors for the equivalent + methods below. + (-userIsOwner): new accessor that returns whether the current user + is the calendar's owner. + + * SoObjects/Appointments/SOGoFreeBusyObject.m + (-fetchFreeBusyInfosFrom:to:): test whether the listed calendars + must be included in the freebusy. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-setIncludeInFreeBusy, -includeInFreeBusy): new accessors that + determines whether the current calendar is included in the + computing of its owner's freebusy. + (-davScheduleCalendarTransparency) + (-setDavScheduleCalendarTransparency:): equivalent DAV accessors + for the above. + 2010-05-05 Francis Lachapelle * UI/WebServerResources/SchedulerUI.js (tasksListCallback): diff --git a/SOPE/sope-patchset-r1664.diff b/SOPE/sope-patchset-r1664.diff index 51a84fa99..1954e86b9 100644 --- a/SOPE/sope-patchset-r1664.diff +++ b/SOPE/sope-patchset-r1664.diff @@ -7812,16 +7812,17 @@ Index: sope-appserver/NGObjWeb/DAVPropMap.plist =================================================================== --- sope-appserver/NGObjWeb/DAVPropMap.plist (revision 1664) +++ sope-appserver/NGObjWeb/DAVPropMap.plist (working copy) -@@ -157,6 +157,8 @@ +@@ -157,6 +157,9 @@ "{urn:ietf:params:xml:ns:caldav}supported-calendar-data" = davSupportedCalendarDataTypes; "{urn:ietf:params:xml:ns:caldav}calendar-description" = davDescription; + "{urn:ietf:params:xml:ns:caldav}calendar-timezone" = davCalendarTimeZone; + "{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL" = davScheduleDefaultCalendarURL; ++ "{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp" = davScheduleCalendarTransparency; /* CardDAV */ "{urn:ietf:params:xml:ns:carddav}addressbook-home-set" = davAddressbookHomeSet; -@@ -168,6 +170,8 @@ +@@ -168,6 +171,8 @@ "{http://calendarserver.org/ns/}dropbox-home-URL" = davDropboxHomeURL; "{http://calendarserver.org/ns/}notifications-URL" = davNotificationsURL; "{http://calendarserver.org/ns/}getctag" = davCollectionTag; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index 92de59fde..7e88ebfb5 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -156,6 +156,9 @@ typedef enum { - (BOOL) synchronizeCalendar; - (void) setSynchronizeCalendar: (BOOL) new; +- (BOOL) includeInFreeBusy; +- (void) setIncludeInFreeBusy: (BOOL) newInclude; + - (BOOL) importComponent: (iCalEntityObject *) event; - (int) importCalendar: (iCalCalendar *) calendar; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index f78d49d70..f07200cb9 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -396,6 +396,26 @@ static NSNumber *sharedYes = nil; inCategory: @"FolderSynchronize"]; } +- (BOOL) includeInFreeBusy +{ + NSNumber *excludeFromFreeBusy; + + excludeFromFreeBusy + = [self folderPropertyValueInCategory: @"FreeBusyExclusions"]; + + return ![excludeFromFreeBusy boolValue]; +} + +- (void) setIncludeInFreeBusy: (BOOL) newInclude +{ + NSNumber *excludeFromFreeBusy; + + excludeFromFreeBusy = [NSNumber numberWithBool: !newInclude]; + + [self setFolderPropertyValue: excludeFromFreeBusy + inCategory: @"FreeBusyExclusions"]; +} + /* selection */ - (NSArray *) calendarUIDs @@ -2285,6 +2305,34 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return @""; } +- (NSArray *) davScheduleCalendarTransparency +{ + const NSString *opacity; + + opacity = ([self includeInFreeBusy] ? @"opaque" : @"transparent"); + + return [NSArray arrayWithObject: [NSArray arrayWithObjects: opacity, + XMLNS_CALDAV, + nil]]; +} + +- (NSException *) setDavScheduleCalendarTransparency: (id) newName +{ + NSException *error; + + error = nil; + + if ([newName rangeOfString: @"opaque"].location != NSNotFound) + [self setIncludeInFreeBusy: YES]; + else if ([newName rangeOfString: @"transparent"].location != NSNotFound) + [self setIncludeInFreeBusy: NO]; + else + error = [NSException exceptionWithHTTPStatus: 400 + reason: @"Bad transparency value."]; + + return error; +} + /* vevent UID handling */ - (NSString *) resourceNameForEventUID: (NSString *) uid diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.m b/SoObjects/Appointments/SOGoFreeBusyObject.m index 724076f0f..d4cd26af4 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.m +++ b/SoObjects/Appointments/SOGoFreeBusyObject.m @@ -254,9 +254,9 @@ for (count = 0; count < max; count++) { calFolder = [folders objectAtIndex: count]; - if (![calFolder isSubscription]) + if (![calFolder isSubscription] && [calFolder includeInFreeBusy]) [infos addObjectsFromArray: [calFolder fetchFreeBusyInfosFrom: startDate - to: endDate]]; + to: endDate]]; } return infos; diff --git a/Tests/Integration/test-caldav-scheduling.py b/Tests/Integration/test-caldav-scheduling.py index 99b3573a8..7538c32fd 100755 --- a/Tests/Integration/test-caldav-scheduling.py +++ b/Tests/Integration/test-caldav-scheduling.py @@ -37,6 +37,88 @@ def fetchUserInfo(login): return (displayName, email_nodes[0].childNodes[0].nodeValue) +class CalDAVPropertiesTest(unittest.TestCase): + def setUp(self): + self.client = webdavlib.WebDAVClient(hostname, port, + username, password) + self.test_calendar \ + = "/SOGo/dav/%s/Calendar/test-dav-properties/" % username + mkcol = webdavlib.WebDAVMKCOL(self.test_calendar) + self.client.execute(mkcol) + + def tearDown(self): + delete = webdavlib.WebDAVDELETE(self.test_calendar) + self.client.execute(delete) + + def testDavScheduleCalendarTransparency(self): + """{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp""" + + ## PROPFIND + propfind = webdavlib.WebDAVPROPFIND(self.test_calendar, + ["{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp"], + 0) + self.client.execute(propfind) + propfind.xpath_namespace = { "D": "DAV:", + "C": "urn:ietf:params:xml:ns:caldav" } + propstats = propfind.xpath_evaluate('/D:multistatus/D:response/D:propstat/D:prop/C:schedule-calendar-transp') + self.assertTrue(len(propstats) > 0, + "schedule-calendar-transp not present in response") + node = propstats[0] + status = propfind.xpath_evaluate('D:status', + node.parentNode.parentNode)[0] \ + .childNodes[0].nodeValue[9:12] + self.assertEquals(status, "200", + "schedule-calendar-transp marked as 'Not Found' in response") + values = node.childNodes + nvalues = len(values) + self.assertEquals(nvalues, 1, + "expected 1 value (%d received)" % nvalues) + value = values[0] + self.assertEquals(value.__class__.__name__, "Element", + "schedule-calendar-transp must be an instance of" \ + " %s, not %s" + % ("Element", value.__class__.__name__)) + self.assertEquals(value.namespaceURI, "urn:ietf:params:xml:ns:caldav", + "schedule-calendar-transp must have a value in"\ + " namespace '%s', not '%s'" + % ("urn:ietf:params:xml:ns:caldav", + value.namespaceURI)) + self.assertTrue(value.tagName == "opaque", + "schedule-calendar-transp must be 'opaque' on new" \ + " collections, not '%s'" % value.tagName) + + ## PROPPATCH + newValueNode = "{urn:ietf:params:xml:ns:caldav}thisvaluedoesnotexist" + proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, + {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ + { newValueNode: True }}) + self.client.execute(proppatch) + self.assertEquals(proppatch.response["status"], 400, + "expecting failure when setting transparency to" \ + " an invalid value") + + newValueNode = "{urn:ietf:params:xml:ns:caldav}transparent" + proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, + {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ + { newValueNode: True }}) + self.client.execute(proppatch) + self.assertEquals(proppatch.response["status"], 207, + "failure (%s) setting transparency to" \ + " 'transparent': '%s'" + % (proppatch.response["status"], + proppatch.response["body"])) + + newValueNode = "{urn:ietf:params:xml:ns:caldav}opaque" + proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, + {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ + { newValueNode: True }}) + self.client.execute(proppatch) + self.assertEquals(proppatch.response["status"], 207, + "failure (%s) setting transparency to" \ + " 'transparent': '%s'" + % (proppatch.response["status"], + proppatch.response["body"])) + class CalDAVITIPDelegationTest(unittest.TestCase): def setUp(self): self.client = webdavlib.WebDAVClient(hostname, port, diff --git a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings index 0a1611a13..fa9f4b133 100644 --- a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Tarefa Confidencial)"; "Name:" = "Nome:"; "Color:" = "Cor:"; +"Include in free-busy" = "Incluir na disponibilidade"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Marca:"; diff --git a/UI/Scheduler/Czech.lproj/Localizable.strings b/UI/Scheduler/Czech.lproj/Localizable.strings index 0db47391d..924ea93e9 100644 --- a/UI/Scheduler/Czech.lproj/Localizable.strings +++ b/UI/Scheduler/Czech.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Důvěrný úkol)"; "Name:" = "Název:"; "Color:" = "Barva:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronizace"; "Synchronize" = "Synchronizovat"; "Tag:" = "Štítek:"; diff --git a/UI/Scheduler/Dutch.lproj/Localizable.strings b/UI/Scheduler/Dutch.lproj/Localizable.strings index c12a962d0..0aa0f2bc1 100644 --- a/UI/Scheduler/Dutch.lproj/Localizable.strings +++ b/UI/Scheduler/Dutch.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Vertrouwelijke taak)"; "Name:" = "Naam:"; "Color:" = "Kleur:"; +"Include in free-busy" = "In de beschikbaarheid insluiten"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Markering:"; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 75a35cc04..ab16cb57e 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Confidential task)"; "Name:" = "Name:"; "Color:" = "Color:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 1d44ae291..c78b1df14 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Tâche confidentielle)"; "Name:" = "Nom :"; "Color:" = "Couleur :"; +"Include in free-busy" = "Inclure dans la disponibilité"; + "Synchronization" = "Synchronisation"; "Synchronize" = "Synchroniser"; "Tag:" = "Label :"; @@ -521,12 +523,12 @@ vtodo_class2 = "(Tâche confidentielle)"; "Show tasks" = "Afficher les tâches"; /* Error messages */ -"dayFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans le champ Jours."; -"weekFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans le champ Semaine(s)."; -"monthFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans le champ Mois."; -"monthDayFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans la journée du mois."; -"yearFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans le champ Année(s)."; -"appointmentFieldInvalid" = "Veuillez spécifier un chiffre superieur ou égal à 1 dans le champ rendez-vous."; +"dayFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans le champ Jours."; +"weekFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans le champ Semaine(s)."; +"monthFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans le champ Mois."; +"monthDayFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans la journée du mois."; +"yearFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans le champ Année(s)."; +"appointmentFieldInvalid" = "Veuillez spécifier un chiffre supérieur ou égal à 1 dans le champ rendez-vous."; "recurrenceUnsupported" = "Ce type de récurrence n\\'est pas supporté."; "tagNotDefined" = "Vous devez spécifier un label si vous désirez synchroniser ce calendrier."; "tagAlreadyExists" = "Le label spécifié est déjà associé à un autre calendrier. Choisissez-en un autre."; diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index f96487d1a..d51fac335 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Vertrauliche Aufgabe)"; "Name:" = "Name:"; "Color:" = "Farbe:"; +"Include in free-busy" = "In der Verfügbarkeit einschließen"; + "Synchronization" = "Synchronisation"; "Synchronize" = "Synchronisieren"; "Tag:" = "Tag:"; diff --git a/UI/Scheduler/Hungarian.lproj/Localizable.strings b/UI/Scheduler/Hungarian.lproj/Localizable.strings index 09823cbef..aec36bba0 100644 --- a/UI/Scheduler/Hungarian.lproj/Localizable.strings +++ b/UI/Scheduler/Hungarian.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Bizalmas feladat)"; "Name:" = "Név:"; "Color:" = "Szín:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Cimke:"; diff --git a/UI/Scheduler/Italian.lproj/Localizable.strings b/UI/Scheduler/Italian.lproj/Localizable.strings index 6b5830275..4546f938f 100644 --- a/UI/Scheduler/Italian.lproj/Localizable.strings +++ b/UI/Scheduler/Italian.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Attività confidenziale)"; "Name:" = "Nome:"; "Color:" = "Colore:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Etichetta:"; diff --git a/UI/Scheduler/Russian.lproj/Localizable.strings b/UI/Scheduler/Russian.lproj/Localizable.strings index 370af8a7c..44dc30a1c 100644 --- a/UI/Scheduler/Russian.lproj/Localizable.strings +++ b/UI/Scheduler/Russian.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Confidential task)"; "Name:" = "Название:"; "Color:" = "Цвет:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; diff --git a/UI/Scheduler/Spanish.lproj/Localizable.strings b/UI/Scheduler/Spanish.lproj/Localizable.strings index a83166f5d..cb0062784 100644 --- a/UI/Scheduler/Spanish.lproj/Localizable.strings +++ b/UI/Scheduler/Spanish.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Tarea confidencial)"; "Name:" = "Nombre:"; "Color:" = "Color:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Redacción:"; diff --git a/UI/Scheduler/Swedish.lproj/Localizable.strings b/UI/Scheduler/Swedish.lproj/Localizable.strings index 4258a297d..7c2b389dd 100644 --- a/UI/Scheduler/Swedish.lproj/Localizable.strings +++ b/UI/Scheduler/Swedish.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Konfidentiell uppgift)"; "Name:" = "Namn:"; "Color:" = "Färg:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synkronisering"; "Synchronize" = "Synkronisera"; "Tag:" = "Etikett:"; diff --git a/UI/Scheduler/UIxCalendarProperties.m b/UI/Scheduler/UIxCalendarProperties.m index 73ab8ef73..a25b6c566 100644 --- a/UI/Scheduler/UIxCalendarProperties.m +++ b/UI/Scheduler/UIxCalendarProperties.m @@ -70,6 +70,16 @@ [calendar setCalendarColor: newColor]; } +- (BOOL) includeInFreeBusy +{ + return [calendar includeInFreeBusy]; +} + +- (void) setIncludeInFreeBusy: (BOOL) newInclude +{ + [calendar setIncludeInFreeBusy: newInclude]; +} + - (BOOL) synchronizeCalendar { return [self mustSynchronize] || [calendar synchronizeCalendar]; @@ -177,6 +187,15 @@ return rc; } +- (BOOL) userIsOwner +{ + NSString *userLogin; + + userLogin = [[context activeUser] login]; + + return ([userLogin isEqualToString: [calendar ownerInContext: context]]); +} + - (BOOL) isWebCalendar { return ([calendar isKindOfClass: [SOGoWebAppointmentFolder class]]); diff --git a/UI/Scheduler/Welsh.lproj/Localizable.strings b/UI/Scheduler/Welsh.lproj/Localizable.strings index d91595117..b6dd18b33 100644 --- a/UI/Scheduler/Welsh.lproj/Localizable.strings +++ b/UI/Scheduler/Welsh.lproj/Localizable.strings @@ -512,6 +512,8 @@ vtodo_class2 = "(Tasg gyhoeddus)"; "Name:" = "Enw:"; "Color:" = "Lliw:"; +"Include in free-busy" = "Include in free-busy"; + "Synchronization" = "Synchronization"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; diff --git a/UI/Templates/SchedulerUI/UIxCalendarProperties.wox b/UI/Templates/SchedulerUI/UIxCalendarProperties.wox index 3b3ceb50a..5f2f6df4e 100644 --- a/UI/Templates/SchedulerUI/UIxCalendarProperties.wox +++ b/UI/Templates/SchedulerUI/UIxCalendarProperties.wox @@ -37,6 +37,16 @@ id="calendarColor" var:value="calendarColor" /> +
diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 4d5b2cad0..51380bcff 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -2167,6 +2167,10 @@ function onCalendarModify(event) { isWebCalendar = true; } } + var owner = selected.getAttribute("owner"); + if (owner == UserLogin) { + height += 24; + } if (isWebCalendar) height += 41; else if (calendarID == "/personal")