From: wolfgang Date: Fri, 1 Jun 2007 21:05:56 +0000 (+0000) Subject: git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1073 d1b88da0-ebda-0310... X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9a3339124557ebbf43f8ca97b4a0282340330b87;p=scalable-opengroupware.org git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1073 d1b88da0-ebda-0310-925b-ed51d893ca5b --- diff --git a/ChangeLog b/ChangeLog index 681ee4de..0a9ea4df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,142 @@ +2007-06-01 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoObject.m ([SOGoObject + -_urlPreferringParticle:expectedoverThisOne:possible]): unescape + the result of [self baseURLInContext:] to avoid a double escaping + in the resulting string. + + * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -aclsForUser:uid]): if + the user has the "ObjectReader" role on the parent container, then + he is granted the "ObjectViewer" role on this object. Same for + "ObjectEraser", although this might change later. + + * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor + -toolbar]): determine the toolbar filename based on the + permissions declared by the securitymanager. The role mechanism + has been adjusted on a lowerlevel so we can simplify the code. + + * SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject + -initWithName:newNameinContainer:newContainer]): read the content + directly from here. That way we can determine whether the object + is being created. + ([SOGoContentObject -aclsForUser:uid]): if the object is new and + the ObjectCreator role is enabled, we also grant the ObjectEditor + role to the current user. + + * UI/Common/UIxFolderActions.m ([-canAccessContentAction]): + changed the method paradigm to only return HTTP code 204. Not test + is done whatsoever since the security manager does it for us. + + * UI/Contacts/UIxContactsListView.m ([-canAccessContentAction]): + removed method because the same exists in UIxFolderActions.m + + * UI/Contacts/UIxContactsUserRightsEditor.m + ([UIxContactsUserRightsEditor + -setUserCanReadObjects:userCanReadObjects]): new method. + + * SoObjects/SOGo/SOGoPermissions.m: added roles "FolderViewer" and + "FolderEraser" and special permission "Access Object". Removed + role "SOGoMailRole_MessageEraser" since "SOGoRole_ObjectEraser" + can be used instead. Removed "FolderReader" since it is useless. + + * SoObjects/Contacts/SOGoContactGCSEntry.m ([SOGoContactGCSEntry + -vCard]): directly use the "content" ivar since it is initialized + during object creation. + + * SoObjects/Contacts/SOGoContactLDAPFolder.m + ([SOGoContactLDAPFolder -init]): no longer handle the + nameInContainer no the container ivars. They were removed because + this class is a descendant of SOGoObject which already has them. + ([SOGoContactLDAPFolder -davDisplayName]): new overriden method + returning "displayName". + ([SOGoContactLDAPFolder -isFolderish]): new overriden method + returning "YES". + + * SoObjects/Appointments/SOGoCalendarComponent.m + ([SOGoCalendarComponent -isOrganizerOrOwner:user]): commented out. + ([SOGoCalendarComponent -findParticipant:user]): new name for + method "participant:". + ([SOGoCalendarComponent -findParticipantWithUID:uid]): same as + findparticipant but taking a uid string as parameter. + ([SOGoCalendarComponent -contentAsString]): don't regenerate the + iCalendar automatically. Instead, this is done only when the user + is a "date and time viewer". + ([SOGoCalendarComponent -aclsForUser:uid]): take delegation roles + (modifier and responder) into account by compiling them with the + owner's roles. + + * SoObjects/Contacts/SOGoContactFolder.h: removed + "nameInContainer" from the list of required methods. + + * SoObjects/Appointments/SOGoFreeBusyObject.m ([SOGoFreeBusyObject + -davContentType]): returns "text/calendar". + + * SoObjects/Contacts/SOGoContactLDAPFolder.m + ([SOGoContactLDAPFolder -davResourceType]): declare the correct + groupdav resource-type. + + * SoObjects/Contacts/SOGoContactFolders.m ([SOGoContactFolders + -davContentType]): same as below. + + * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder + -initWithName:newNameinContainer:newContainer]): new overriden + method setting the custom owner directly. + + * SoObjects/Mailer/SOGoMailAccounts.m ([SOGoMailAccounts + -davContentType]): same as below. + + * SoObjects/Mailer/SOGoMailAccount.m ([SOGoMailAccount + -davContentType]): same as below. + + * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -davContentType]): + declare "httpd/unix-directory". + + * SoObjects/SOGo/SOGoUser.m ([SOGoUser +initialize]): declare + "UTC" as fallback timezone instead of "Canada/Eastern". + ([-hasEmail:email]): make use of the new NSArray's + containsCaseInsensitiveString: method. + + * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject + -davContentType]): declare "message/rfc822" as content type. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + ([SOGoAppointmentFolder -_privacySqlString]): "owner" is now an + ivar in SOGoObject. + ([SOGoAppointmentFolder + -fetchFields:_fieldsfromFolder:_folderfrom:_startDateto:_endDatecomponent:_component]): same as above. + ([-fetchContentObjectNames]): commented out method since it was + usefull only for testing Funambol. + + * SoObjects/SOGo/NSArray+Utilities.m ([NSArray + -containsCaseInsensitiveString:match]): an enhanced version of + containsObject:. + + * UI/MailerUI/UIxMailView.m ([-isTrashingAllowed]): removed + useless method. + ([-showMarkDeletedButton]): removed useless method. + ([-showTrashButton]): removed useless method. + + * SoObjects/Appointments/iCalEntityObject+SOGo.m + ([iCalEntityObject -userIsParticipant:user]): new proxy method + that invoked isParticipant on self for each possible email + addresses of the user passed as parameter. + ([iCalEntityObject -userIsOrganizer:user]): same as above. + + * SoObjects/Appointments/iCalEntityObject+SOGo.[hm]: new class + extension module. + + * SoObjects/Appointments/SOGoAppointmentObject.m + ([SOGoAppointmentObject + -saveContentString:contentStringbaseVersion:baseVersion]): remove + method since an event may not have an organizer. + + * SoObjects/SOGo/SOGoObject.m ([SOGoObject + -GETAction:localContext]): clarified method. Added support for + content-type (thanks to Helge Hess). + ([SOGoObject -initWithName:_nameinContainer:_container]): + initialize and retain the owner. + ([SOGoObject -davContentType]): returns "text/plain". + 2007-05-30 Wolfgang Sourdeau * UI/Common/UIxObjectActions.m ([UIxObjectActions diff --git a/SOPE/NGCards/iCalEntityObject.m b/SOPE/NGCards/iCalEntityObject.m index 1239e233..fc75f8d4 100644 --- a/SOPE/NGCards/iCalEntityObject.m +++ b/SOPE/NGCards/iCalEntityObject.m @@ -374,12 +374,14 @@ return list; } -- (BOOL) isOrganizer: (id)_email +- (BOOL) isOrganizer: (id) _email { - _email = [_email lowercaseString]; + NSString *organizerMail; + + organizerMail = [[self organizer] rfc822Email]; - return [[[[self organizer] rfc822Email] lowercaseString] - isEqualToString:_email]; + return [[organizerMail lowercaseString] + isEqualToString: [_email lowercaseString]]; } - (BOOL) isParticipant: (id) _email diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index c6effab6..4e3d3255 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -11,6 +11,7 @@ Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct Appointments_OBJC_FILES = \ Product.m \ NSArray+Appointments.m \ + iCalEntityObject+SOGo.m \ \ SOGoCalendarComponent.m \ SOGoAppointmentObject.m \ diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index a2cea22a..afeee305 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -728,12 +728,11 @@ static NSNumber *sharedYes = nil; - (NSString *) _privacySqlString { - NSString *privacySqlString, *owner, *login, *email; + NSString *privacySqlString, *login, *email; SOGoUser *activeUser; activeUser = [context activeUser]; login = [activeUser login]; - owner = [self ownerInContext: context]; if ([login isEqualToString: owner]) privacySqlString = @""; @@ -793,7 +792,7 @@ static NSNumber *sharedYes = nil; EOQualifier *qualifier; NSMutableArray *fields, *ma = nil; NSArray *records; - NSString *sql, *dateSqlString, *componentSqlString, *privacySqlString; /* , *owner; */ + NSString *sql, *dateSqlString, *componentSqlString, *privacySqlString; NGCalendarDateRange *r; if (_folder == nil) { @@ -862,7 +861,6 @@ static NSNumber *sharedYes = nil; if (!ma) ma = [NSMutableArray arrayWithCapacity: [records count]]; -// owner = [self ownerInContext: nil]; [ma addObjectsFromArray: records]; } else if (!ma) @@ -971,8 +969,8 @@ static NSNumber *sharedYes = nil; return nil; url = [self baseURLInContext:_ctx]; - if (![url hasSuffix:@"/"]) - url = [url stringByAppendingString:@"/"]; + if (![url hasSuffix: @"/"]) + url = [url stringByAppendingString: @"/"]; // TODO: this should run a query to determine the uid! return [url stringByAppendingString:_uid]; @@ -990,7 +988,7 @@ static NSNumber *sharedYes = nil; if (![_uid isNotNull]) return nil; - + /* create subcontext, so that we don't destroy our environment */ if ((ctx = [context createSubContext]) == nil) { @@ -1007,7 +1005,7 @@ static NSNumber *sharedYes = nil; result = [[ctx application] traversePathArray:path inContext:ctx error:&error acquire:NO]; if (error != nil) { - [self errorWithFormat:@"folder lookup failed (uid=%@): %@", + [self errorWithFormat: @"folder lookup failed (uid=%@): %@", _uid, error]; return nil; } @@ -1257,32 +1255,32 @@ static NSNumber *sharedYes = nil; return calendarFolders; } -- (NSArray *) fetchContentObjectNames -{ - NSMutableArray *objectNames; - NSArray *records; - NSCalendarDate *today, *startDate, *endDate; - -#warning this should be user-configurable - objectNames = [NSMutableArray array]; - today = [[NSCalendarDate calendarDate] beginOfDay]; - [today setTimeZone: timeZone]; - - startDate = [today dateByAddingYears: 0 months: 0 days: -1 - hours: 0 minutes: 0 seconds: 0]; - endDate = [startDate dateByAddingYears: 0 months: 0 days: 2 - hours: 0 minutes: 0 seconds: 0]; - records = [self fetchFields: [NSArray arrayWithObject: @"c_name"] - from: startDate to: endDate - component: @"vevent"]; - [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]]; - records = [self fetchFields: [NSArray arrayWithObject: @"c_name"] - from: startDate to: endDate - component: @"vtodo"]; - [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]]; - - return objectNames; -} +// - (NSArray *) fetchContentObjectNames +// { +// NSMutableArray *objectNames; +// NSArray *records; +// NSCalendarDate *today, *startDate, *endDate; + +// #warning this should be user-configurable +// objectNames = [NSMutableArray array]; +// today = [[NSCalendarDate calendarDate] beginOfDay]; +// [today setTimeZone: timeZone]; + +// startDate = [today dateByAddingYears: 0 months: 0 days: -1 +// hours: 0 minutes: 0 seconds: 0]; +// endDate = [startDate dateByAddingYears: 0 months: 0 days: 2 +// hours: 0 minutes: 0 seconds: 0]; +// records = [self fetchFields: [NSArray arrayWithObject: @"c_name"] +// from: startDate to: endDate +// component: @"vevent"]; +// [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]]; +// records = [self fetchFields: [NSArray arrayWithObject: @"c_name"] +// from: startDate to: endDate +// component: @"vtodo"]; +// [objectNames addObjectsFromArray: [records valueForKey: @"c_name"]]; + +// return objectNames; +// } /* folder type */ diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 691ed36e..37a7301a 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -410,7 +410,7 @@ /* perform */ - return [self deleteInUIDs:removedUIDs]; + return [self deleteInUIDs: removedUIDs]; } - (NSException *) saveContentString: (NSString *) _iCalString @@ -425,35 +425,4 @@ return @"IPM.Appointment"; } -- (NSException *) saveContentString: (NSString *) contentString - baseVersion: (unsigned int) baseVersion -{ - NSString *newContentString, *oldContentString; - iCalCalendar *eventCalendar; - iCalEvent *event; - iCalPerson *organizer; - NSArray *organizers; - - oldContentString = [self contentAsString]; - if (oldContentString) - newContentString = contentString; - else - { - eventCalendar = [iCalCalendar parseSingleFromSource: contentString]; - event = (iCalEvent *) [eventCalendar firstChildWithTag: [self componentTag]]; - organizers = [event childrenWithTag: @"organizer"]; - if ([organizers count]) - newContentString = contentString; - else - { - organizer = [self iCalPersonWithUID: [self ownerInContext: context]]; - [event setOrganizer: organizer]; - newContentString = [eventCalendar versitString]; - } - } - - return [super saveContentString: newContentString - baseVersion: baseVersion]; -} - @end /* SOGoAppointmentObject */ diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index 50a1cfa2..5675724b 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -60,8 +60,10 @@ andNewObject: (iCalRepeatableEntityObject *) _newObject toAttendees: (NSArray *) _attendees; -- (BOOL) isOrganizerOrOwner: (SOGoUser *) user; -- (iCalPerson *) participant: (SOGoUser *) user; +// - (BOOL) isOrganizerOrOwner: (SOGoUser *) user; + +- (iCalPerson *) findParticipant: (SOGoUser *) user; +- (iCalPerson *) findParticipantWithUID: (NSString *) uid; - (iCalPerson *) iCalPersonWithUID: (NSString *) uid; - (NSString *) getUIDForICalPerson: (iCalPerson *) person; diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index e6a6a65c..15242ccf 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -37,6 +37,7 @@ #import "common.h" #import "SOGoAptMailNotification.h" +#import "iCalEntityObject+SOGo.h" #import "SOGoCalendarComponent.h" static NSString *mailTemplateDefaultLanguage = nil; @@ -111,40 +112,33 @@ static BOOL sendEMailNotifications = NO; - (NSString *) contentAsString { - NSString *tmpContent, *email, *uid, *role; + NSString *uid; iCalCalendar *tmpCalendar; iCalRepeatableEntityObject *tmpComponent; + NSArray *roles; if (!calContent) { - tmpContent = [super contentAsString]; - calContent = tmpContent; uid = [[context activeUser] login]; - if (![[self ownerInContext: context] isEqualToString: uid] - && [tmpContent length] > 0) - { - tmpCalendar = [iCalCalendar parseSingleFromSource: tmpContent]; - tmpComponent = (iCalRepeatableEntityObject *) + roles = [self aclsForUser: uid]; + if ([roles containsObject: SOGoCalendarRole_Organizer] + || [roles containsObject: SOGoCalendarRole_Participant] + || [roles containsObject: SOGoCalendarRole_ComponentViewer]) + { + calContent = content; + [calContent retain]; + } + else if ([roles containsObject: SOGoCalendarRole_ComponentDAndTViewer]) + { + tmpCalendar = [[self calendar: NO] copy]; + tmpComponent = (iCalRepeatableEntityObject *) [tmpCalendar firstChildWithTag: [self componentTag]]; - email = [[context activeUser] primaryEmail]; - if (!([tmpComponent isOrganizer: email] - || [tmpComponent isParticipant: email])) - { - role = [container roleForComponentsWithAccessClass: [tmpComponent symbolicAccessClass] - forUser: uid]; - if ([role length] > 0) - { - if ([role isEqualToString: SOGoCalendarPerm_ViewDAndT]) - { - // content = tmpContent; - [self _filterComponent: tmpComponent]; - calContent = [tmpCalendar versitString]; - } - } - else - calContent = nil; - } - } + [self _filterComponent: tmpComponent]; + calContent = [tmpCalendar versitString]; + [tmpCalendar release]; + } + else + calContent = nil; [calContent retain]; } @@ -175,7 +169,7 @@ static BOOL sendEMailNotifications = NO; if (!calendar) { - iCalString = [self contentAsString]; + iCalString = [super contentAsString]; if ([iCalString length] > 0) calendar = [iCalCalendar parseSingleFromSource: iCalString]; else @@ -262,7 +256,7 @@ static BOOL sendEMailNotifications = NO; - (NSException *) changeParticipationStatus: (NSString *) _status { iCalRepeatableEntityObject *component; - iCalPerson *p; + iCalPerson *person; NSString *newContent; NSException *ex; NSString *myEMail; @@ -273,15 +267,15 @@ static BOOL sendEMailNotifications = NO; if (component) { myEMail = [[context activeUser] primaryEmail]; - p = [component findParticipantWithEmail: myEMail]; - if (p) + person = [self findParticipantWithUID: owner]; + if (person) { // TODO: send iMIP reply mails? - [p setPartStat: _status]; + [person setPartStat: _status]; newContent = [[component parent] versitString]; if (newContent) { - ex = [self saveContentString:newContent]; + ex = [self saveContentString: newContent]; if (ex) // TODO: why is the exception wrapped? /* Server Error */ @@ -445,24 +439,33 @@ static BOOL sendEMailNotifications = NO; } } -- (BOOL) isOrganizerOrOwner: (SOGoUser *) user +// - (BOOL) isOrganizerOrOwner: (SOGoUser *) user +// { +// BOOL isOrganizerOrOwner; +// iCalRepeatableEntityObject *component; +// NSString *organizerEmail; + +// component = [self component: NO]; +// organizerEmail = [[component organizer] rfc822Email]; +// if (component && [organizerEmail length] > 0) +// isOrganizerOrOwner = [user hasEmail: organizerEmail]; +// else +// isOrganizerOrOwner +// = [[container ownerInContext: context] isEqualToString: [user login]]; + +// return isOrganizerOrOwner; +// } + +- (iCalPerson *) findParticipantWithUID: (NSString *) uid { - BOOL isOrganizerOrOwner; - iCalRepeatableEntityObject *component; - NSString *organizerEmail; + SOGoUser *user; - component = [self component: NO]; - organizerEmail = [[component organizer] rfc822Email]; - if (component && [organizerEmail length] > 0) - isOrganizerOrOwner = [user hasEmail: organizerEmail]; - else - isOrganizerOrOwner - = [[container ownerInContext: context] isEqualToString: [user login]]; + user = [SOGoUser userWithLogin: uid roles: nil]; - return isOrganizerOrOwner; + return [self findParticipant: user]; } -- (iCalPerson *) participant: (SOGoUser *) user +- (iCalPerson *) findParticipant: (SOGoUser *) user { iCalPerson *participant, *currentParticipant; iCalEntityObject *component; @@ -534,34 +537,85 @@ static BOOL sendEMailNotifications = NO; return uids; } +- (NSString *) _roleOfOwner: (iCalRepeatableEntityObject *) component +{ + NSString *role; + iCalPerson *organizer; + SOGoUser *ownerUser; + + if (component) + { + organizer = [component organizer]; + if ([[organizer rfc822Email] length] > 0) + { + ownerUser = [SOGoUser userWithLogin: owner roles: nil]; + if ([component userIsOrganizer: ownerUser]) + role = SOGoCalendarRole_Organizer; + else if ([component userIsParticipant: ownerUser]) + role = SOGoCalendarRole_Participant; + else + role = SOGoRole_None; + } + else + role = SOGoCalendarRole_Organizer; + } + else + role = SOGoCalendarRole_Organizer; + + return role; +} + +- (NSString *) _compiledRoleForOwner: (NSString *) ownerRole + andUser: (NSString *) userRole +{ + NSString *role; + + if ([userRole isEqualToString: SOGoCalendarRole_ComponentModifier] + || ([userRole isEqualToString: SOGoCalendarRole_ComponentResponder] + && [ownerRole isEqualToString: SOGoCalendarRole_Participant])) + role = ownerRole; + else + role = SOGoRole_None; + + return role; +} + - (NSArray *) aclsForUser: (NSString *) uid { NSMutableArray *roles; NSArray *superAcls; iCalRepeatableEntityObject *component; - NSString *email, *accessRole; + NSString *accessRole, *ownerRole; roles = [NSMutableArray array]; + superAcls = [super aclsForUser: uid]; + if ([superAcls count] > 0) + [roles addObjectsFromArray: superAcls]; + component = [self component: NO]; - if (component) + ownerRole = [self _roleOfOwner: component]; + if ([owner isEqualToString: uid]) + [roles addObject: ownerRole]; + else { - email = [[LDAPUserManager sharedUserManager] getEmailForUID: uid]; - if ([component isOrganizer: email]) + if (component) + { + accessRole = [container roleForComponentsWithAccessClass: + [component symbolicAccessClass] + forUser: uid]; + if ([accessRole length] > 0) + { + [roles addObject: accessRole]; + [roles addObject: [self _compiledRoleForOwner: ownerRole + andUser: accessRole]]; + } + } + else if ([roles containsObject: SOGoRole_ObjectCreator]) [roles addObject: SOGoCalendarRole_Organizer]; - if ([component isParticipant: email]) - [roles addObject: SOGoCalendarRole_Participant]; - accessRole = [container roleForComponentsWithAccessClass: - [component symbolicAccessClass] - forUser: uid]; - if ([accessRole length] > 0) - [roles addObject: accessRole]; } - superAcls = [super aclsForUser: uid]; - if ([superAcls count] > 0) - [roles addObjectsFromArray: superAcls]; - if ([roles containsObject: SOGoRole_ObjectCreator]) - [roles addObject: SOGoCalendarRole_ComponentModifier]; + NSLog (@"all roles: %@" , roles); +// } return roles; } diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.m b/SoObjects/Appointments/SOGoFreeBusyObject.m index ae378a12..b892914c 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.m +++ b/SoObjects/Appointments/SOGoFreeBusyObject.m @@ -194,4 +194,9 @@ return r; } +- (NSString *) davContentType +{ + return @"text/calendar"; +} + @end diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h new file mode 100644 index 00000000..7f8702b9 --- /dev/null +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -0,0 +1,35 @@ +/* iCalEntityObject+SOGo.h - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ICALENTITYOBJECT_SOGO_H +#define ICALENTITYOBJECT_SOGO_H + +#import + +@interface iCalEntityObject (SOGoExtensions) + +- (BOOL) userIsParticipant: (SOGoUser *) user; +- (BOOL) userIsOrganizer: (SOGoUser *) user; + +@end + +#endif /* ICALENTITYOBJECT_SOGO_H */ diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m new file mode 100644 index 00000000..5b100f9a --- /dev/null +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -0,0 +1,70 @@ +/* iCalEntityObject+SOGo.m - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import + +#import + +#import "iCalEntityObject+SOGo.h" + +@implementation iCalEntityObject (SOGoExtensions) + +- (BOOL) userIsParticipant: (SOGoUser *) user +{ + NSEnumerator *emails; + NSString *currentEmail; + BOOL response; + + response = NO; + + emails = [[user allEmails] objectEnumerator]; + currentEmail = [emails nextObject]; + while (!response && currentEmail) + if ([self isParticipant: currentEmail]) + response = YES; + else + currentEmail = [emails nextObject]; + + return response; +} + +- (BOOL) userIsOrganizer: (SOGoUser *) user +{ + NSEnumerator *emails; + NSString *currentEmail; + BOOL response; + + response = NO; + + emails = [[user allEmails] objectEnumerator]; + currentEmail = [emails nextObject]; + while (!response && currentEmail) + if ([self isOrganizer: currentEmail]) + response = YES; + else + currentEmail = [emails nextObject]; + + return response; +} + +@end diff --git a/SoObjects/Appointments/product.plist b/SoObjects/Appointments/product.plist index ff9831d0..0d2bab48 100644 --- a/SoObjects/Appointments/product.plist +++ b/SoObjects/Appointments/product.plist @@ -24,18 +24,20 @@ "ViewDAndTOfConfidentialRecords" = ( "Owner", "ConfidentialDAndTViewer" ); "ModifyConfidentialRecords" = ( "Owner", "ConfidentialModifier" ); "RespondToConfidentialRecords" = ( "Owner", "ConfidentialModifier", "ConfidentialResponder" ); + "Access Contents Information" = ( "Owner", "AuthorizedSubscriber" ); }; }; SOGoGroupAppointmentFolder = { superclass = "SOGoAppointmentFolder"; }; SOGoCalendarComponent = { - superclass = "SOGoContentObject"; + superclass = "SOGoContentObject"; defaultRoles = { - "ViewAllComponent" = ( "Owner", "Organizer", "Participant", "ComponentViewer", "ComponentModifier" ); + "ViewAllComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer" ); "ViewDAndT" = ( "Organizer", "Participant", "ComponentDAndTViewer" ); - "ModifyComponent" = ( "Owner", "ComponentModifier" ); - "RespondToComponent" = ( "Participant", "ComponentResponder" ); + "ModifyComponent" = ( "Owner", "Organizer" ); + "RespondToComponent" = ( "Participant" ); + "Access Object" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); }; }; SOGoAppointmentObject = { diff --git a/SoObjects/Contacts/SOGoContactFolder.h b/SoObjects/Contacts/SOGoContactFolder.h index 904009d3..6ced261a 100644 --- a/SoObjects/Contacts/SOGoContactFolder.h +++ b/SoObjects/Contacts/SOGoContactFolder.h @@ -51,7 +51,6 @@ andDisplayName: (NSString *) aDisplayName inContainer: (SOGoObject *) aContainer; -- (NSString *) nameInContainer; - (NSString *) displayName; - (NSArray *) lookupContactsWithFilter: (NSString *) filter diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index bbe0c5f5..0aeeab8f 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -244,6 +244,11 @@ return YES; } +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + - (void) setBaseOCSPath: (NSString *) newOCSPath { if (OCSPath) diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index 39aca0a4..b56e5a92 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -40,8 +40,7 @@ - (void) dealloc { - if (card) - [card release]; + [card release]; [super dealloc]; } @@ -49,13 +48,10 @@ - (NGVCard *) vCard { - NSString *contentStr; - if (!card) { - contentStr = [self contentAsString]; - if ([[contentStr uppercaseString] hasPrefix:@"BEGIN:VCARD"]) - card = [NGVCard parseSingleFromSource: contentStr]; + if ([[content uppercaseString] hasPrefix: @"BEGIN:VCARD"]) + card = [NGVCard parseSingleFromSource: content]; else card = [NGVCard cardWithUid: [self nameInContainer]]; [card retain]; diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 42d966dc..6762c243 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -60,7 +60,7 @@ if ((self = [self initWithName: newName inContainer: newContainer])) ASSIGN (displayName, newDisplayName); - + return self; } @@ -146,8 +146,8 @@ EOQualifier *qualifier; EOSortOrdering *ordering; - NSLog (@"fetching records matching '%@', sorted by '%@' in order %d", - filter, sortKey, sortOrdering); +// NSLog (@"fetching records matching '%@', sorted by '%@' in order %d", +// filter, sortKey, sortOrdering); fields = folderListingFields; qualifier = [self _qualifierForFilter: filter]; @@ -210,7 +210,8 @@ return @"Contact"; } -- (NSString *)outlookFolderClass { +- (NSString *) outlookFolderClass +{ return @"IPF.Contact"; } diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.h b/SoObjects/Contacts/SOGoContactLDAPFolder.h index dd2f9bfc..da09d382 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.h +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.h @@ -31,7 +31,6 @@ @interface SOGoContactLDAPFolder : SOGoObject { - NSString *name; NSString *displayName; LDAPSource *ldapSource; NSMutableDictionary *entries; diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.m b/SoObjects/Contacts/SOGoContactLDAPFolder.m index 75c19894..39133639 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.m +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.m @@ -31,6 +31,7 @@ #import #import #import +#import #import #import "SOGoContactLDIFEntry.h" @@ -69,9 +70,7 @@ { if ((self = [super init])) { - name = nil; displayName = nil; - container = nil; entries = nil; ldapSource = nil; } @@ -83,20 +82,18 @@ andDisplayName: (NSString *) newDisplayName inContainer: (SOGoObject *) newContainer { - self = [self init]; - - ASSIGN (name, newName); - ASSIGN (displayName, newDisplayName); - ASSIGN (container, newContainer); + if ((self = [self initWithName: newName + inContainer: newContainer])) + { + ASSIGN (displayName, newDisplayName); + } return self; } - (void) dealloc { - [name release]; [displayName release]; - [container release]; [entries release]; [ldapSource release]; [super dealloc]; @@ -112,11 +109,6 @@ return displayName; } -- (NSString *) nameInContainer -{ - return name; -} - - (id) lookupName: (NSString *) objectName inContext: (WOContext *) lookupContext acquire: (BOOL) acquire @@ -132,7 +124,7 @@ { ldifEntry = [ldapSource lookupContactEntry: objectName]; obj = ((ldifEntry) - ? [SOGoContactLDIFEntry contactEntryWithName: name + ? [SOGoContactLDIFEntry contactEntryWithName: objectName withLDIFEntry: ldifEntry inContainer: self] : [NSException exceptionWithHTTPStatus: 404]); @@ -172,9 +164,35 @@ return result; } -- (NSString *) groupDavResourceType +- (NSArray *) davResourceType +{ + NSArray *rType, *groupDavCollection; + + groupDavCollection = [NSArray arrayWithObjects: @"vcard-collection", + XMLNS_GROUPDAV, nil]; + rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil]; + + return rType; +} + +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + +- (BOOL) davIsCollection +{ + return YES; +} + +- (NSString *) davDisplayName +{ + return displayName; +} + +- (BOOL) isFolderish { - return @"vcard-collection"; + return YES; } /* acls */ diff --git a/SoObjects/Contacts/product.plist b/SoObjects/Contacts/product.plist index 843eda8b..cb6b5124 100644 --- a/SoObjects/Contacts/product.plist +++ b/SoObjects/Contacts/product.plist @@ -10,34 +10,33 @@ classes = { SOGoContactFolders = { superclass = "SOGoFolder"; - protectedBy = ""; - defaultAccess = "allow"; + protectedBy = "Access Contents Information"; defaultRoles = { - "View" = ( "Owner" ); + "Access Contents Information" = ( "Authenticated" ); + "WebDAV Access" = ( "Authenticated" ); }; }; - SOGoContactGCSFolder = { superclass = "SOGoFolder"; }; - SOGoContactGCSEntry = { superclass = "SOGoContentObject"; - defaultRoles = { - "View" = ( "Owner", "Delegate", "Organizer", "Authenticated" ); - }; }; - SOGoContactLDAPFolder = { superclass = "SOGoFolder"; - defaultAccess = "allow"; - protectedBy = ""; + protectedBy = "Access Contents Information"; + defaultRoles = { + "Access Contents Information" = ( "Authenticated" ); + "WebDAV Access" = ( "Authenticated" ); + }; }; - SOGoContactLDIFEntry = { - superclass = "SOGoContentObject"; - defaultAccess = "allow"; - protectedBy = ""; + superclass = "SOGoContentObject"; + protectedBy = "Access Content Information"; + defaultRoles = { + "Access Content Information" = ( "Authenticated" ); + "WebDAV Access" = ( "Authenticated" ); + }; }; }; } diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 18427488..be0cea9f 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -387,15 +387,24 @@ static BOOL useAltNamespace = NO; /* WebDAV */ -- (BOOL)davIsCollection { +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + +- (BOOL) davIsCollection +{ return YES; } -- (NSException *)davCreateCollection:(NSString *)_name inContext:(id)_ctx { +- (NSException *) davCreateCollection: (NSString *) _name + inContext: (id) _ctx +{ return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]]; } -- (NSString *)shortTitle { +- (NSString *) shortTitle +{ NSString *s, *login, *host; NSRange r; @@ -425,7 +434,8 @@ static BOOL useAltNamespace = NO; return [NSString stringWithFormat:@"%@@%@", login, host]; } -- (NSString *)davDisplayName { +- (NSString *) davDisplayName +{ return [self shortTitle]; } diff --git a/SoObjects/Mailer/SOGoMailAccounts.m b/SoObjects/Mailer/SOGoMailAccounts.m index d215e8e1..8e7c8a58 100644 --- a/SoObjects/Mailer/SOGoMailAccounts.m +++ b/SoObjects/Mailer/SOGoMailAccounts.m @@ -185,6 +185,11 @@ static NSString *AgenorShareLoginMarker = @".-."; return YES; } +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + /* acls */ - (NSArray *) aclsForUser: (NSString *) uid diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index f3b86ab1..d79cb68d 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -62,6 +62,39 @@ static BOOL useAltNamespace = NO; useAltNamespace = [ud boolForKey:@"SOGoSpecialFoldersInRoot"]; } +- (void) _adjustOwner +{ + SOGoMailAccount *mailAccount; + NSString *path; + NSArray *names; + + mailAccount = [self mailAccountFolder]; + path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]]; + + if ([path hasPrefix: [mailAccount sharedFolderName]]) + owner = @"anyone"; + else if ([path hasPrefix: [mailAccount otherUsersFolderName]]) + { + names = [path componentsSeparatedByString: @"/"]; + if ([names count] > 1) + owner = [names objectAtIndex: 1]; + else + owner = @"anyone"; + } +} + +- (id) initWithName: (NSString *) newName + inContainer: (id) newContainer +{ + if ((self = [super initWithName: newName + inContainer: newContainer])) + { + [self _adjustOwner]; + } + + return self; +} + - (void) dealloc { [filenames release]; @@ -115,6 +148,11 @@ static BOOL useAltNamespace = NO; return subfoldersURL; } +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + - (NSArray *) toOneRelationshipKeys { NSArray *uids; @@ -406,10 +444,10 @@ static BOOL useAltNamespace = NO; [SOGoAcls addObjectUniquely: SOGoRole_FolderCreator]; break; case 'x': - [SOGoAcls addObjectUniquely: SOGoRole_ObjectEraser]; + [SOGoAcls addObjectUniquely: SOGoRole_FolderEraser]; break; case 't': - [SOGoAcls addObjectUniquely: SOGoMailRole_MessageEraser]; + [SOGoAcls addObjectUniquely: SOGoRole_ObjectEraser]; break; case 'e': [SOGoAcls addObjectUniquely: SOGoMailRole_Expunger]; @@ -449,9 +487,9 @@ static BOOL useAltNamespace = NO; character = 'p'; else if ([currentAcl isEqualToString: SOGoRole_FolderCreator]) character = 'k'; - else if ([currentAcl isEqualToString: SOGoRole_ObjectEraser]) + else if ([currentAcl isEqualToString: SOGoRole_FolderEraser]) character = 'x'; - else if ([currentAcl isEqualToString: SOGoMailRole_MessageEraser]) + else if ([currentAcl isEqualToString: SOGoRole_ObjectEraser]) character = 't'; else if ([currentAcl isEqualToString: SOGoMailRole_Expunger]) character = 'e'; @@ -562,31 +600,6 @@ static BOOL useAltNamespace = NO; return defaultUserID; } -- (NSString *) ownerInContext: (WOContext *) localContext -{ - SOGoMailAccount *mailAccount; - NSString *path, *owner; - NSArray *names; - - mailAccount = [self mailAccountFolder]; - path = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]]; - - if ([path hasPrefix: [mailAccount sharedFolderName]]) - owner = @"anyone"; - else if ([path hasPrefix: [mailAccount otherUsersFolderName]]) - { - names = [path componentsSeparatedByString: @"/"]; - if ([names count] > 1) - owner = [names objectAtIndex: 1]; - else - owner = @"anyone"; - } - else - owner = [super ownerInContext: localContext]; - - return owner; -} - - (NSString *) otherUsersPathToFolder { NSString *userPath, *selfPath, *otherUsers, *sharedFolders; @@ -604,8 +617,7 @@ static BOOL useAltNamespace = NO; else userPath = [NSString stringWithFormat: @"/%@/%@%@", [otherUsers stringByEscapingURL], - [self ownerInContext: context], - selfPath]; + owner, selfPath]; return userPath; } diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 68ec6326..8f91f247 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -407,6 +407,11 @@ static BOOL debugSoParts = NO; return [[content copy] autorelease]; } +- (NSString *) davContentType +{ + return @"message/rfc822"; +} + - (NSString *)contentAsString { NSString *s; NSData *content; @@ -650,10 +655,13 @@ static BOOL debugSoParts = NO; /* flags */ -- (NSException *)addFlags:(id)_flags { +- (NSException *) addFlags: (id) _flags +{ return [[self imap4Connection] addFlags:_flags toURL:[self imap4URL]]; } -- (NSException *)removeFlags:(id)_flags { + +- (NSException *) removeFlags: (id) _flags +{ return [[self imap4Connection] removeFlags:_flags toURL:[self imap4URL]]; } @@ -667,7 +675,7 @@ static BOOL debugSoParts = NO; login = [[context activeUser] login]; parentAcl = [[self container] aclsForUser: login]; - return [parentAcl containsObject: SOGoMailRole_MessageEraser]; + return [parentAcl containsObject: SOGoRole_ObjectEraser]; } /* name lookup */ diff --git a/SoObjects/SOGo/NSArray+Utilities.h b/SoObjects/SOGo/NSArray+Utilities.h index 025b2bce..b330cf85 100644 --- a/SoObjects/SOGo/NSArray+Utilities.h +++ b/SoObjects/SOGo/NSArray+Utilities.h @@ -33,6 +33,8 @@ - (NSArray *) stringsWithFormat: (NSString *) format; +- (BOOL) containsCaseInsensitiveString: (NSString *) match; + #ifdef GNUSTEP_BASE_LIBRARY - (void) makeObjectsPerform: (SEL) selector withObject: (id) object1 diff --git a/SoObjects/SOGo/NSArray+Utilities.m b/SoObjects/SOGo/NSArray+Utilities.m index 66054efb..77dabdcb 100644 --- a/SoObjects/SOGo/NSArray+Utilities.m +++ b/SoObjects/SOGo/NSArray+Utilities.m @@ -81,6 +81,26 @@ return representation; } +- (BOOL) containsCaseInsensitiveString: (NSString *) match +{ + BOOL response; + NSString *currentString, *cmpObject; + NSEnumerator *objects; + + response = NO; + + cmpObject = [match lowercaseString]; + objects = [self objectEnumerator]; + currentString = [objects nextObject]; + while (currentString && !response) + if ([[currentString lowercaseString] isEqualToString: cmpObject]) + response = YES; + else + currentString = [objects nextObject]; + + return response; +} + @end @implementation NSMutableArray (SOGoArrayUtilities) diff --git a/SoObjects/SOGo/SOGoContentObject.h b/SoObjects/SOGo/SOGoContentObject.h index 9d3545b4..f23f5cce 100644 --- a/SoObjects/SOGo/SOGoContentObject.h +++ b/SoObjects/SOGo/SOGoContentObject.h @@ -33,6 +33,7 @@ { NSString *ocsPath; NSString *content; + BOOL isNew; } /* accessors */ @@ -47,6 +48,7 @@ /* content */ +- (BOOL) isNew; - (NSString *) contentAsString; - (NSException *) saveContentString: (NSString *) _str baseVersion: (unsigned int) _baseVersion; diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index d5153f32..e771cefb 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -35,7 +35,22 @@ // TODO: check superclass version -- (void)dealloc { +- (id) initWithName: (NSString *) newName + inContainer: (id) newContainer +{ + if ((self = [super initWithName: newName inContainer: newContainer])) + { + ocsPath = nil; + content = [[self ocsFolder] fetchContentWithName: newName]; + [content retain]; + isNew = (!content); + } + + return self; +} + +- (void) dealloc +{ [content release]; [ocsPath release]; [super dealloc]; @@ -43,51 +58,60 @@ /* notifications */ -- (void)sleep { +- (void) sleep +{ [content release]; content = nil; [super sleep]; } /* accessors */ -- (BOOL)isFolderish { +- (BOOL) isFolderish +{ return NO; } -- (void)setOCSPath:(NSString *)_path { - if ([ocsPath isEqualToString:_path]) - return; - - if (ocsPath) - [self warnWithFormat:@"GCS path is already set! '%@'", _path]; +- (void) setOCSPath: (NSString *) newOCSPath +{ + if (![ocsPath isEqualToString: newOCSPath]) + { + if (ocsPath) + [self warnWithFormat:@"GCS path is already set! '%@'", newOCSPath]; - ASSIGNCOPY(ocsPath, _path); + ASSIGNCOPY (ocsPath, newOCSPath); + } } - (NSString *) ocsPath { - NSString *p; - + NSMutableString *newOCSPath; + if (!ocsPath) { - p = [self ocsPathOfContainer]; - if (p) + newOCSPath = [NSMutableString new]; + [newOCSPath appendString: [self ocsPathOfContainer]]; + if ([newOCSPath length] > 0) { - if (![p hasSuffix:@"/"]) - p = [p stringByAppendingString: @"/"]; - ocsPath = [p stringByAppendingString: [self nameInContainer]]; - [ocsPath retain]; + if (![newOCSPath hasSuffix:@"/"]) + [newOCSPath appendString: @"/"]; + [newOCSPath appendString: nameInContainer]; + ocsPath = newOCSPath; } } return ocsPath; } -- (NSString *)ocsPathOfContainer { - if (![[self container] respondsToSelector:@selector(ocsPath)]) - return nil; +- (NSString *) ocsPathOfContainer +{ + NSString *ocsPathOfContainer; - return [[self container] ocsPath]; + if ([container respondsToSelector: @selector (ocsPath)]) + ocsPathOfContainer = [container ocsPath]; + else + ocsPathOfContainer = nil; + + return ocsPath; } - (GCSFolder *) ocsFolder @@ -97,42 +121,48 @@ /* content */ -- (NSString *) contentAsString +- (BOOL) isNew { - if (!content) - { - content = [[self ocsFolder] fetchContentWithName: nameInContainer]; - [content retain]; - } + return isNew; +} +- (NSString *) contentAsString +{ return content; } -- (NSException *) saveContentString: (NSString *) _str - baseVersion: (unsigned int) _baseVersion +- (NSException *) saveContentString: (NSString *) newContent + baseVersion: (unsigned int) newBaseVersion { /* Note: "iCal multifolder saves" are implemented in the apt subclass! */ GCSFolder *folder; NSException *ex; - - if ((folder = [self ocsFolder]) == nil) { + + ex = nil; + + ASSIGN (content, newContent); + folder = [container ocsFolder]; + if (folder) + { + ex = [folder writeContent: newContent + toName: nameInContainer + baseVersion: newBaseVersion]; + if (ex) + [self errorWithFormat:@"write failed: %@", ex]; + } + else [self errorWithFormat:@"Did not find folder of content object."]; - return nil; - } - ex = [folder writeContent:_str toName:[self nameInContainer] - baseVersion:_baseVersion]; - if (ex != nil) { - [self errorWithFormat:@"write failed: %@", ex]; - return ex; - } - return nil; + return ex; } -- (NSException *)saveContentString:(NSString *)_str { - return [self saveContentString:_str baseVersion:0 /* don't check */]; + +- (NSException *) saveContentString: (NSString *) newContent +{ + return [self saveContentString: newContent baseVersion: 0]; } -- (NSException *)delete { +- (NSException *) delete +{ /* Note: "iCal multifolder saves" are implemented in the apt subclass! */ GCSFolder *folder; NSException *ex; @@ -153,7 +183,8 @@ /* actions */ -- (id)PUTAction:(WOContext *)_ctx { +- (id) PUTAction: (WOContext *) _ctx +{ WORequest *rq; NSException *error; unsigned int baseVersion; @@ -226,7 +257,7 @@ /* setup response */ // TODO: this should be automatic in the SoDispatcher if we return nil? - [[_ctx response] setStatus:201 /* Created */]; + [[_ctx response] setStatus: 201 /* Created */]; if ((etag = [self davEntityTag]) != nil) [[_ctx response] setHeader:etag forKey:@"etag"]; @@ -321,9 +352,13 @@ if ([containerAcls count] > 0) { if ([containerAcls containsObject: SOGoRole_ObjectCreator]) - [acls addObject: SOGoRole_ObjectCreator]; - if ([containerAcls containsObject: SOGoRole_ObjectEraser]) - [acls addObject: SOGoRole_ObjectEraser]; + { + [acls addObject: SOGoRole_ObjectCreator]; + if (isNew) + [acls addObject: SOGoRole_ObjectEditor]; + } + if ([containerAcls containsObject: SOGoRole_ObjectReader]) + [acls addObject: SOGoRole_ObjectViewer]; } return acls; diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index cf5b25c2..6dc6f4f0 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -237,6 +237,11 @@ static NSString *defaultUserID = @""; return rType; } +- (NSString *) davContentType +{ + return @"httpd/unix-directory"; +} + - (NSArray *) toOneRelationshipKeys { /* toOneRelationshipKeys are the 'files' contained in a folder */ NSMutableArray *ma; @@ -429,8 +434,27 @@ static NSString *defaultUserID = @""; - (NSArray *) aclsForUser: (NSString *) uid { - return [self aclsForUser: uid - forObjectAtPath: [self pathArrayToSoObject]]; + NSMutableArray *acls; + NSArray *ownAcls, *containerAcls; + + acls = [NSMutableArray array]; + ownAcls = [self aclsForUser: uid + forObjectAtPath: [self pathArrayToSoObject]]; + [acls addObjectsFromArray: ownAcls]; + if ([container respondsToSelector: @selector (aclsForUser:)]) + { + containerAcls = [container aclsForUser: uid]; + if ([containerAcls count] > 0) + { + if ([containerAcls containsObject: SOGoRole_ObjectReader]) + [acls addObject: SOGoRole_ObjectViewer]; +#warning this should be checked + if ([containerAcls containsObject: SOGoRole_ObjectEraser]) + [acls addObject: SOGoRole_ObjectEraser]; + } + } + + return acls; } - (void) setRoles: (NSArray *) roles diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 8d83c8d7..8370acf9 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -57,8 +57,8 @@ { WOContext *context; NSString *nameInContainer; - id container; - NSString *customOwner; + id container; + NSString *owner; } + (id) objectWithName: (NSString *)_name inContainer:(id)_container; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index cd60e585..1707caf3 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -40,6 +40,7 @@ #import #import #import +#import #import #import @@ -76,7 +77,7 @@ { return [NSDictionary dictionaryWithObjectsAndKeys: @"read", SoPerm_AccessContentsInformation, - @"read", SoPerm_View, + @"read", SoPerm_AccessContentsInformation, @"bind", SoPerm_AddDocumentsImagesAndFiles, @"unbind", SoPerm_DeleteObjects, @"write-acl", SoPerm_ChangePermissions, @@ -393,34 +394,40 @@ static BOOL kontactGroupDAV = YES; return YES; } -- (id)initWithName:(NSString *)_name inContainer:(id)_container { - if ((self = [self init])) +- (id) init +{ + if ((self = [super init])) { - context = [[WOApplication application] context]; - [context retain]; - nameInContainer = [_name copy]; - container = - [self doesRetainContainer] ? [_container retain] : _container; - customOwner = nil; + context = nil; + nameInContainer = nil; + container = nil; + owner = nil; } return self; } -- (id) init +- (id) initWithName: (NSString *) _name + inContainer: (id) _container { - if ((self = [super init])) + if ((self = [self init])) { - nameInContainer = nil; - container = nil; + context = [[WOApplication application] context]; + [context retain]; + nameInContainer = [_name copy]; + container = _container; + if ([self doesRetainContainer]) + [_container retain]; + ASSIGN (owner, [_container ownerInContext: context]); } return self; } -- (void)dealloc { +- (void) dealloc +{ [context release]; - [customOwner release]; + [owner release]; if ([self doesRetainContainer]) [container release]; [nameInContainer release]; @@ -429,10 +436,13 @@ static BOOL kontactGroupDAV = YES; /* accessors */ -- (NSString *)nameInContainer { +- (NSString *) nameInContainer +{ return nameInContainer; } -- (id)container { + +- (id) container +{ return container; } @@ -440,18 +450,18 @@ static BOOL kontactGroupDAV = YES; - (void) setOwner: (NSString *) newOwner { - ASSIGN (customOwner, newOwner); + ASSIGN (owner, newOwner); } -- (NSString *)ownerInContext:(id)_ctx { - return ((customOwner) - ? customOwner - : [[self container] ownerInContext:_ctx]); +- (NSString *) ownerInContext: (id) localContext +{ + return owner; } /* hierarchy */ -- (NSArray *)fetchSubfolders { +- (NSArray *) fetchSubfolders +{ NSMutableArray *ma; NSArray *names; unsigned i, count; @@ -485,11 +495,13 @@ static BOOL kontactGroupDAV = YES; return [container lookupUserFolder]; } + - (SOGoGroupsFolder *)lookupGroupsFolder { return [[self lookupUserFolder] lookupGroupsFolder]; } -- (void)sleep { +- (void) sleep +{ if ([self doesRetainContainer]) [container release]; container = nil; @@ -497,26 +509,30 @@ static BOOL kontactGroupDAV = YES; /* operations */ -- (NSException *)delete { - return [NSException exceptionWithHTTPStatus:501 /* not implemented */ - reason:@"delete not yet implemented, sorry ..."]; +- (NSException *) delete +{ + return [NSException exceptionWithHTTPStatus: 501 /* not implemented */ + reason: @"delete not yet implemented, sorry ..."]; } /* KVC hacks */ -- (id)valueForUndefinedKey:(NSString *)_key { +- (id) valueForUndefinedKey: (NSString *) _key +{ return nil; } /* WebDAV */ -- (NSString *)davDisplayName { +- (NSString *) davDisplayName +{ return [self nameInContainer]; } /* actions */ -- (id)DELETEAction:(id)_ctx { +- (id) DELETEAction: (id) _ctx +{ NSException *error; if ((error = [self delete]) != nil) @@ -526,44 +542,64 @@ static BOOL kontactGroupDAV = YES; return [NSNumber numberWithBool:YES]; /* delete worked out ... */ } -- (id)GETAction:(id)_ctx { +- (NSString *) davContentType +{ + return @"text/plain"; +} + +- (WOResponse *) _webDAVResponse: (WOContext *) localContext +{ + WOResponse *response; + NSString *contentType; + id etag; + + response = [localContext response]; + contentType = [NSString stringWithFormat: @"%@; charset=utf8", + [self davContentType]]; + [response setHeader: contentType forKey: @"content-type"]; + [response appendContentString: [self contentAsString]]; + etag = [self davEntityTag]; + if (etag) + [response setHeader: etag forKey: @"etag"]; + + return response; +} + +- (id) GETAction: (id) localContext +{ // TODO: I guess this should really be done by SOPE (redirect to // default method) - WORequest *rq; - WOResponse *r; + WORequest *request; NSString *uri; - - r = [(WOContext *)_ctx response]; - rq = [(WOContext *)_ctx request]; - - if ([rq isSoWebDAVRequest]) { - if ([self respondsToSelector:@selector(contentAsString)]) { - NSException *error; - id etag; - - if ((error = [self matchesRequestConditionInContext:_ctx]) != nil) - return error; - - [r appendContentString:[self contentAsString]]; - - if ((etag = [self davEntityTag]) != nil) - [r setHeader:etag forKey:@"etag"]; - - return r; + NSException *error; + id value; + + request = [localContext request]; + if ([request isSoWebDAVRequest]) + { + if ([self respondsToSelector: @selector (contentAsString)]) + { + error = [self matchesRequestConditionInContext: localContext]; + if (error) + value = error; + else + value = [self _webDAVResponse: localContext]; + } + else + value = [NSException exceptionWithHTTPStatus: 501 /* not implemented */ + reason: @"no WebDAV GET support?!"]; + } + else + { + value = [localContext response]; + uri = [[request uri] composeURLWithAction: @"view" + parameters: [request formValues] + andHash: NO]; + [value setStatus: 302 /* moved */]; + [value setHeader: uri forKey: @"location"]; } - - return [NSException exceptionWithHTTPStatus:501 /* not implemented */ - reason:@"no WebDAV GET support?!"]; - } - - uri = [rq uri]; - [r setStatus:302 /* moved */]; - [r setHeader: [uri composeURLWithAction: @"view" - parameters: [rq formValues] - andHash: NO] - forKey:@"location"]; - return r; + return value; } /* etag support */ @@ -780,12 +816,12 @@ static BOOL kontactGroupDAV = YES; - (NSURL *) _urlPreferringParticle: (NSString *) expected overThisOne: (NSString *) possible { - NSURL *serverURL, *davURL; + NSURL *serverURL, *url; NSMutableArray *path; NSString *baseURL, *urlMethod; serverURL = [context serverURL]; - baseURL = [self baseURLInContext: context]; + baseURL = [[self baseURLInContext: context] stringByUnescapingURL]; path = [NSMutableArray arrayWithArray: [baseURL componentsSeparatedByString: @"/"]]; urlMethod = [path objectAtIndex: 2]; @@ -797,12 +833,12 @@ static BOOL kontactGroupDAV = YES; [path insertObject: expected atIndex: 2]; } - davURL = [[NSURL alloc] initWithScheme: [serverURL scheme] - host: [serverURL host] - path: [path componentsJoinedByString: @"/"]]; - [davURL autorelease]; + url = [[NSURL alloc] initWithScheme: [serverURL scheme] + host: [serverURL host] + path: [path componentsJoinedByString: @"/"]]; + [url autorelease]; - return davURL; + return url; } - (NSURL *) davURL diff --git a/SoObjects/SOGo/SOGoPermissions.h b/SoObjects/SOGo/SOGoPermissions.h index 2b4e60cf..53ed0e96 100644 --- a/SoObjects/SOGo/SOGoPermissions.h +++ b/SoObjects/SOGo/SOGoPermissions.h @@ -28,13 +28,15 @@ #import extern NSString *SOGoRole_ObjectCreator; -extern NSString *SOGoRole_ObjectReader; extern NSString *SOGoRole_ObjectEraser; +extern NSString *SOGoRole_ObjectReader; extern NSString *SOGoRole_ObjectViewer; extern NSString *SOGoRole_ObjectEditor; extern NSString *SOGoRole_FolderCreator; -extern NSString *SOGoRole_FolderReader; +extern NSString *SOGoRole_FolderEraser; +extern NSString *SOGoRole_FolderViewer; + extern NSString *SOGoRole_AuthorizedSubscriber; extern NSString *SOGoRole_None; extern NSString *SOGoRole_FreeBusy; @@ -46,7 +48,6 @@ extern NSString *SOGoMailRole_Poster; extern NSString *SOGoMailRole_Expunger; extern NSString *SOGoMailRole_Creator; extern NSString *SOGoMailRole_Administrator; -extern NSString *SOGoMailRole_MessageEraser; extern NSString *SOGoCalendarRole_Organizer; extern NSString *SOGoCalendarRole_Participant; @@ -69,6 +70,7 @@ extern NSString *SOGoCalendarRole_ComponentDAndTViewer; extern NSString *SOGoCalendarRole_ComponentModifier; extern NSString *SOGoCalendarRole_ComponentResponder; +extern NSString *SOGoPerm_AccessObject; extern NSString *SOGoPerm_ReadAcls; extern NSString *SOGoPerm_FreeBusyLookup; diff --git a/SoObjects/SOGo/SOGoPermissions.m b/SoObjects/SOGo/SOGoPermissions.m index f5cb1f17..0e976915 100644 --- a/SoObjects/SOGo/SOGoPermissions.m +++ b/SoObjects/SOGo/SOGoPermissions.m @@ -28,10 +28,10 @@ NSString *SOGoRole_ObjectEraser = @"ObjectEraser"; NSString *SOGoRole_ObjectViewer = @"ObjectViewer"; NSString *SOGoRole_ObjectReader = @"ObjectReader"; NSString *SOGoRole_ObjectEditor = @"ObjectEditor"; + NSString *SOGoRole_FolderCreator = @"FolderCreator"; NSString *SOGoRole_FolderEraser = @"FolderEraser"; -NSString *SOGoRole_FolderViewer = @"FolderViewer"; -NSString *SOGoRole_FolderReader = @"FolderReader"; + NSString *SOGoRole_AuthorizedSubscriber = @"AuthorizedSubscriber"; NSString *SOGoRole_None = @"None"; @@ -73,6 +73,7 @@ NSString *SOGoMailRole_Administrator = @"MailAdministrator"; NSString *SOGoMailRole_MessageEraser = @"MailMessageEraser"; /* permissions */ +NSString *SOGoPerm_AccessObject= @"Access Object"; NSString *SOGoPerm_ReadAcls = @"ReadAcls"; /* the equivalent of "read-acl" in the WebDAV acls spec, which is currently missing from SOPE */ diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index e345602a..d60b5dc2 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -30,8 +30,10 @@ #import "AgenorUserDefaults.h" #import "LDAPUserManager.h" #import "SOGoContentObject.h" -#import "SOGoUser.h" #import "SOGoPermissions.h" +#import "NSArray+Utilities.h" + +#import "SOGoUser.h" static NSTimeZone *serverTimeZone = nil; static NSString *fallbackIMAP4Server = nil; @@ -57,7 +59,7 @@ static NSURL *AgenorProfileURL = nil; { tzName = [ud stringForKey: @"SOGoServerTimeZone"]; if (!tzName) - tzName = @"Canada/Eastern"; + tzName = @"UTC"; serverTimeZone = [NSTimeZone timeZoneWithName: tzName]; [serverTimeZone retain]; } @@ -186,23 +188,10 @@ static NSURL *AgenorProfileURL = nil; - (BOOL) hasEmail: (NSString *) email { - BOOL hasEmail; - NSString *currentEmail, *cmpEmail; - NSEnumerator *emails; - - hasEmail = NO; if (!allEmails) [self _fetchAllEmails]; - cmpEmail = [email lowercaseString]; - emails = [allEmails objectEnumerator]; - currentEmail = [emails nextObject]; - while (currentEmail && !hasEmail) - if ([[currentEmail lowercaseString] isEqualToString: cmpEmail]) - hasEmail = YES; - else - currentEmail = [emails nextObject]; - - return hasEmail; + + return [allEmails containsCaseInsensitiveString: email]; } - (NSString *) cn diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index 94edaea4..ca14b9d5 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -163,19 +163,9 @@ - (WOResponse *) canAccessContentAction { WOResponse *response; - SoSecurityManager *securityManager; - BOOL result; - - securityManager = [SoSecurityManager sharedSecurityManager]; - result = (![securityManager validatePermission: SoPerm_AccessContentsInformation - onObject: [self clientObject] - inContext: context]); response = [context response]; - [response setStatus: 200]; - [response setHeader: @"text/plain; charset=\"ascii\"" - forKey: @"content-type"]; - [response appendContentString: (result) ? @"1" : @"0"]; + [response setStatus: 204]; return response; } diff --git a/UI/Contacts/English.lproj/Localizable.strings b/UI/Contacts/English.lproj/Localizable.strings index 16704ace..9f8a0ac2 100644 --- a/UI/Contacts/English.lproj/Localizable.strings +++ b/UI/Contacts/English.lproj/Localizable.strings @@ -109,7 +109,9 @@ = "This person can add cards to this addressbook."; "This person can edit the cards of this addressbook." = "This person can edit the cards of this addressbook."; -"This person can view the cards of this addressbook." -= "This person can view the cards of this addressbook."; +"This person can list the content of this addressbook." += "This person can list the content of this addressbook."; +"This person can read the cards of this addressbook." += "This person can read the cards of this addressbook."; "This person can erase cards from this addressbook." = "This person can erase cards from this addressbook."; diff --git a/UI/Contacts/French.lproj/Localizable.strings b/UI/Contacts/French.lproj/Localizable.strings index 0d73332e..f0ee23b3 100644 --- a/UI/Contacts/French.lproj/Localizable.strings +++ b/UI/Contacts/French.lproj/Localizable.strings @@ -122,7 +122,10 @@ = "Cette personne peut ajouter des fiches à ce carnet d'adresses."; "This person can edit the cards of this addressbook." = "Cette personne peut éditer des fiches de ce carnet d'adresses."; -"This person can view the cards of this addressbook." -= "Cette personne peut visionner des fiches de ce carnet d'adresses."; +"This person can list the content of this addressbook." += "Cette personne peut lister le contenu de ce carnet d'adresses."; +"This person can read the cards of this addressbook." += "Cette personne peut visionner les fiches de ce carnet d'adresses."; "This person can erase cards from this addressbook." = "Cette personne peut effacer des fiches de ce carnet d'adresses."; + diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 4341f4ed..578c65ce 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -590,13 +590,13 @@ objectId = nil; if ([objectId length] == 0) - return [NSException exceptionWithHTTPStatus:500 /* Internal Error */ + return [NSException exceptionWithHTTPStatus: 500 /* Internal Error */ reason: @"could not create a unique ID"]; nextMethod = [NSString stringWithFormat: @"../%@/%@", objectId, [self editActionName]]; - uri = [self _completeURIForMethod:nextMethod]; - return [self redirectToLocation:uri]; + uri = [self _completeURIForMethod: nextMethod]; + return [self redirectToLocation: uri]; } @end /* UIxContactEditor */ diff --git a/UI/Contacts/UIxContactsListView.m b/UI/Contacts/UIxContactsListView.m index fc56f202..64c8fd90 100644 --- a/UI/Contacts/UIxContactsListView.m +++ b/UI/Contacts/UIxContactsListView.m @@ -175,23 +175,4 @@ return YES; } -- (WOResponse *) canAccessContentAction -{ - WOResponse *response; - NSString *clientClass; - - clientClass = NSStringFromClass([[self clientObject] class]); - - response = [context response]; - [response setStatus: 200]; - [response setHeader: @"text/plain; charset=\"ascii\"" - forKey: @"content-type"]; - [response - appendContentString: - ([clientClass isEqualToString: @"SOGoContactLDAPFolder"]) - ? @"1" : @"0"]; - - return response; -} - @end /* UIxContactsListView */ diff --git a/UI/Contacts/UIxContactsUserRightsEditor.m b/UI/Contacts/UIxContactsUserRightsEditor.m index a6d43ee0..65cb1d11 100644 --- a/UI/Contacts/UIxContactsUserRightsEditor.m +++ b/UI/Contacts/UIxContactsUserRightsEditor.m @@ -81,6 +81,19 @@ return [userRights containsObject: SOGoRole_ObjectViewer]; } +- (void) setUserCanReadObjects: (BOOL) userCanReadObjects +{ + if (userCanReadObjects) + [self appendRight: SOGoRole_ObjectReader]; + else + [self removeRight: SOGoRole_ObjectReader]; +} + +- (BOOL) userCanReadObjects +{ + return [userRights containsObject: SOGoRole_ObjectReader]; +} + - (void) updateRights { WORequest *request; @@ -102,6 +115,11 @@ else [self removeRight: SOGoRole_ObjectViewer]; + if ([[request formValueForKey: @"ObjectReader"] length] > 0) + [self appendRight: SOGoRole_ObjectReader]; + else + [self removeRight: SOGoRole_ObjectReader]; + if ([[request formValueForKey: @"ObjectEraser"] length] > 0) [self appendRight: SOGoRole_ObjectEraser]; else diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index 48d2d3c2..cff8e800 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -128,7 +128,7 @@ }; canAccessContent = { protectedBy = ""; - pageName = "UIxContactsListView"; + actionClass = "UIxFolderActions"; actionName = "canAccessContent"; }; }; diff --git a/UI/MailerUI/English.lproj/Localizable.strings b/UI/MailerUI/English.lproj/Localizable.strings index 9fbaf7c2..16c3021c 100644 --- a/UI/MailerUI/English.lproj/Localizable.strings +++ b/UI/MailerUI/English.lproj/Localizable.strings @@ -37,6 +37,7 @@ /* acls */ "Default Roles" = "Default Roles"; +"User rights for:" = "User rights for:"; "List and see this folder" = "List and see this folder"; "Read mails from this folder" = "Read mails from this folder"; diff --git a/UI/MailerUI/French.lproj/Localizable.strings b/UI/MailerUI/French.lproj/Localizable.strings index 7f015386..252b1eb2 100644 --- a/UI/MailerUI/French.lproj/Localizable.strings +++ b/UI/MailerUI/French.lproj/Localizable.strings @@ -36,6 +36,7 @@ /* acls */ "Default Roles" = "Rôles par défaut"; +"User rights for:" = "Autorisations pour:"; "List and see this folder" = "Lister et voir ce dossier"; "Read mails from this folder" = "Lire les messages de ce dossier"; diff --git a/UI/MailerUI/UIxMailUserRightsEditor.m b/UI/MailerUI/UIxMailUserRightsEditor.m index 2478b5c5..08942a3e 100644 --- a/UI/MailerUI/UIxMailUserRightsEditor.m +++ b/UI/MailerUI/UIxMailUserRightsEditor.m @@ -123,27 +123,27 @@ - (void) setUserCanRemoveFolder: (BOOL) userCanRemoveFolder { if (userCanRemoveFolder) - [self appendRight: SOGoRole_ObjectEraser]; + [self appendRight: SOGoRole_FolderEraser]; else - [self removeRight: SOGoRole_ObjectEraser]; + [self removeRight: SOGoRole_FolderEraser]; } - (BOOL) userCanRemoveFolder { - return [userRights containsObject: SOGoRole_ObjectEraser]; + return [userRights containsObject: SOGoRole_FolderEraser]; } - (void) setUserCanEraseMails: (BOOL) userCanEraseMails { if (userCanEraseMails) - [self appendRight: SOGoMailRole_MessageEraser]; + [self appendRight: SOGoRole_ObjectEraser]; else - [self removeRight: SOGoMailRole_MessageEraser]; + [self removeRight: SOGoRole_ObjectEraser]; } - (BOOL) userCanEraseMails { - return [userRights containsObject: SOGoMailRole_MessageEraser]; + return [userRights containsObject: SOGoRole_ObjectEraser]; } - (void) setUserCanExpungeFolder: (BOOL) userCanExpungeFolder @@ -213,16 +213,16 @@ else [self removeRight: SOGoRole_FolderCreator]; + if ([[request formValueForKey: SOGoRole_FolderEraser] length] > 0) + [self appendRight: SOGoRole_FolderEraser]; + else + [self removeRight: SOGoRole_FolderEraser]; + if ([[request formValueForKey: SOGoRole_ObjectEraser] length] > 0) [self appendRight: SOGoRole_ObjectEraser]; else [self removeRight: SOGoRole_ObjectEraser]; - if ([[request formValueForKey: SOGoMailRole_MessageEraser] length] > 0) - [self appendRight: SOGoMailRole_MessageEraser]; - else - [self removeRight: SOGoMailRole_MessageEraser]; - if ([[request formValueForKey: SOGoMailRole_Expunger] length] > 0) [self appendRight: SOGoMailRole_Expunger]; else diff --git a/UI/MailerUI/UIxMailView.m b/UI/MailerUI/UIxMailView.m index 7a9027ab..de312aa8 100644 --- a/UI/MailerUI/UIxMailView.m +++ b/UI/MailerUI/UIxMailView.m @@ -97,37 +97,6 @@ static NSString *mailETag = nil; [self objectTitle]]; } -/* expunge / delete setup and permissions */ - -- (BOOL) isTrashingAllowed -{ - id trash; - - trash = [[[self clientObject] mailAccountFolder] - trashFolderInContext:context]; - if ([trash isKindOfClass:[NSException class]]) - return NO; - - return [trash isWriteAllowed]; -} - -- (BOOL) showMarkDeletedButton -{ - // TODO: we might also want to add a default to always show delete - if (![[self clientObject] isDeletionAllowed]) - return NO; - - return [self isTrashingAllowed] ? NO : YES; -} - -- (BOOL) showTrashButton -{ - if (![[self clientObject] isDeletionAllowed]) - return NO; - - return [self isTrashingAllowed]; -} - /* links (DUP to UIxMailPartViewer!) */ - (NSString *)linkToEnvelopeAddress:(NGImap4EnvelopeAddress *)_address { @@ -162,7 +131,8 @@ static NSString *mailETag = nil; /* actions */ -- (id)defaultAction { +- (id) defaultAction +{ /* check etag to see whether we really must rerender */ if (mailETag != nil ) { /* diff --git a/UI/MainUI/product.plist b/UI/MainUI/product.plist index 2ce04921..dd5f0245 100644 --- a/UI/MainUI/product.plist +++ b/UI/MainUI/product.plist @@ -15,21 +15,45 @@ "View" = ( "Authenticated", "FreeBusy" ); }; }; - SOGoFolder = { - superclass = "SOGoObject"; + SOGoObject = { protectedBy = ""; defaultAccess = "allow"; defaultRoles = { - "Add Documents, Images, and Files" = ( "Owner", "ObjectCreator" ); - "View" = ( "Owner", "AuthorizedSubscriber" ); - "Access Contents Information" = ( "Owner", "ObjectViewer", "AuthorizedSubscriber" ); + "View" = ( "Owner", "ObjectViewer" ); "Change Images And Files" = ( "Owner", "ObjectEditor" ); - "WebDAV Access" = ( "Owner", "AuthorizedSubscriber" ); + "Access Contents Information" = ( "Owner", "ObjectReader" ); + "Add Documents, Images, and Files" = ( "Owner", "ObjectCreator" ); + "Add Folders" = ( "Owner", "FolderCreator" ); "ReadAcls" = ( "Owner", "AuthorizedSubscriber" ); "SaveAcls" = ( "Owner" ); "Delete Objects" = ( "Owner", "ObjectEraser" ); }; }; + SOGoContentObject = { + superclass = "SOGoObject"; + protectedBy = "Access Object"; + defaultRoles = { + "Access Object" = ( "Owner", "AuthorizedSubscriber" ); + }; + }; + SOGoFolder = { + superclass = "SOGoObject"; + protectedBy = "Access Object"; + defaultRoles = { + "Change Images And Files" = ( "Owner", "ObjectEditor" ); + "WebDAV Access" = ( "Owner", "AuthorizedSubscriber" ); + "Access Object" = ( "Owner", "AuthorizedSubscriber" ); + "Access Contents Information" = ( "Owner", "ObjectViewer" ); + }; + }; + SOGoUserFolder = { + superclass = "SOGoFolder"; + protectedBy = "Access Contents Information"; + defaultRoles = { + "Access Contents Information" = ( "Authenticated" ); + "WebDAV Access" = ( "Authenticated" ); + }; + }; }; categories = { @@ -48,12 +72,6 @@ SOGoRootPage = { }; SOGoUserFolder = { - methods = { - view = { - protectedBy = "View"; - pageName = "SOGoUserHomePage"; - }; - }; }; SOGoGroupsFolder = { methods = { diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 910fc8dc..7f7397c9 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -34,18 +34,20 @@ #import #import #import +#import +#import +#import #import #import #import -#import -#import -#import -#import +#import #import #import #import #import +#import +#import #import "UIxComponent+Scheduler.h" @@ -845,38 +847,41 @@ - (NSString *) toolbar { - SOGoUser *currentUser; SOGoCalendarComponent *clientObject; NSString *toolbarFilename; - iCalPerson *person; iCalPersonPartStat participationStatus; + SoSecurityManager *sm; + NSString *owner; + sm = [SoSecurityManager sharedSecurityManager]; clientObject = [self clientObject]; - currentUser = [[self context] activeUser]; - if ([clientObject isOrganizerOrOwner: currentUser]) + + if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent + onObject: clientObject + inContext: context]) { if ([[clientObject componentTag] isEqualToString: @"vevent"]) toolbarFilename = @"SOGoAppointmentObject.toolbar"; else toolbarFilename = @"SOGoTaskObject.toolbar"; } - else + else if (![sm validatePermission: SOGoCalendarPerm_RespondToComponent + onObject: clientObject + inContext: context]) { /* Lightning does not manage participation status within tasks */ - person = [clientObject participant: currentUser]; - if (person) - { - participationStatus = [person participationStatus]; - if (participationStatus == iCalPersonPartStatAccepted) - toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar"; - else if (participationStatus == iCalPersonPartStatDeclined) - toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar"; - else - toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; - } + owner = [clientObject ownerInContext: context]; + participationStatus + = [[clientObject findParticipantWithUID: owner] participationStatus]; + if (participationStatus == iCalPersonPartStatAccepted) + toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar"; + else if (participationStatus == iCalPersonPartStatDeclined) + toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar"; else - toolbarFilename = @"SOGoComponentClose.toolbar"; + toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; } + else + toolbarFilename = @"SOGoComponentClose.toolbar"; return toolbarFilename; } diff --git a/UI/Templates/ContactsUI/UIxContactsUserRightsEditor.wox b/UI/Templates/ContactsUI/UIxContactsUserRightsEditor.wox index 39b25787..620cfd20 100644 --- a/UI/Templates/ContactsUI/UIxContactsUserRightsEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactsUserRightsEditor.wox @@ -35,7 +35,12 @@ + label:value="This person can list the content of this addressbook."/> +
+

- +
diff --git a/UI/WebServerResources/ContactsUI.css b/UI/WebServerResources/ContactsUI.css index 89eefdd8..e11714e9 100644 --- a/UI/WebServerResources/ContactsUI.css +++ b/UI/WebServerResources/ContactsUI.css @@ -145,11 +145,6 @@ UL#contactFolders -moz-border-left-colors: #9c9a94 #000; overflow: auto; } -UL#contactFolders LI.denied -{ background: #fefefe; - font-style: italic; - color: #f33; } - DIV#contactFoldersList LI { padding: .2em 1em; @@ -158,12 +153,6 @@ DIV#contactFoldersList LI white-space: nowrap; } -DIV#contactFoldersList LI._selected -{ - background: #4b6983; - color: #fff; -} - .treecell { color: black; @@ -204,12 +193,6 @@ TABLE#contactsList TD IMG margin-right: .2em; } -TABLE#contactsList TR._selected TD -{ - background: #4b6983; - color: #fff; -} - TABLE#contactsList TR._deleted TD { text-decoration: line-through; diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 264ccac5..0bfa6a7d 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -3,7 +3,7 @@ var cachedContacts = new Array(); var currentContactFolder = '/personal'; -var usersRightsWindowHeight = 180; +var usersRightsWindowHeight = 200; var usersRightsWindowWidth = 450; function openContactWindow(sender, url) { @@ -561,10 +561,7 @@ function lookupDeniedFolders() { function deniedFoldersLookupCallback(http) { if (http.readyState == 4) { - var denied = true; - - if (http.status == 200) - denied = (http.responseText == "0"); + var denied = (http.status != 204) var entry = $(http.callbackData); if (denied) entry.addClassName("denied"); diff --git a/UI/WebServerResources/SchedulerUI.css b/UI/WebServerResources/SchedulerUI.css index 92f4c6e5..f127a277 100644 --- a/UI/WebServerResources/SchedulerUI.css +++ b/UI/WebServerResources/SchedulerUI.css @@ -81,11 +81,6 @@ UL#calendarList UL#calendarList LI { white-space: nowrap; } -UL#calendarList LI.denied -{ background-color: #fefefe; - font-style: italic; - color: #f33; } - UL#tasksList { position: absolute; width: 100%; @@ -303,14 +298,6 @@ TABLE#appointmentsList td.tbtv_subject_headercell, TABLE#appointmentsList td.headerLocation { width: 35%; } -#dateSelector TD._selected, -UL > LI._selected, -TABLE#appointmentsList TR._selected TD -{ - background: #4b6983 !important; - color: #fff !important; -} - ._unfocused#dateSelector TD._selected, UL._unfocused > LI._selected, TABLE._unfocused#appointmentsList TR._selected TD diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 3070c492..42738a71 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -911,20 +911,13 @@ function calendarStatusCallback(http) { } function calendarEntryCallback(http) { - var disabled = true; - - if (http.readyState == 4) { - if (http.status == 200) - disabled = (http.responseText == "0"); + if (http.readyState == 4) { + var denied = (http.status != 204) var entry = $(http.callbackData); - var input = entry.childNodesWithTag("input")[0]; - input.disabled = disabled; - if (disabled) { - input.checked = false; - $(entry).addClassName("denied"); - } + if (denied) + entry.addClassName("denied"); else - $(entry).removeClassName("denied"); + entry.removeClassName("denied"); } } diff --git a/UI/WebServerResources/UIxCalUserRightsEditor.css b/UI/WebServerResources/UIxCalUserRightsEditor.css index eaea5734..3e7b6991 100644 --- a/UI/WebServerResources/UIxCalUserRightsEditor.css +++ b/UI/WebServerResources/UIxCalUserRightsEditor.css @@ -1,7 +1,7 @@ DIV.title { color: #000; vertical-align: bottom; - padding-top: 15px; + padding-top: 8px; padding-left: 1em; height: 33px; background-color: #fff; @@ -9,7 +9,7 @@ DIV.title DIV.title SPAN.value { margin-left: 1em; - font-size: 18px; + font-size: 14px; font-weight: bold; } DIV.calendarUserRights diff --git a/UI/WebServerResources/UIxContactsUserRightsEditor.css b/UI/WebServerResources/UIxContactsUserRightsEditor.css index eaea5734..3e7b6991 100644 --- a/UI/WebServerResources/UIxContactsUserRightsEditor.css +++ b/UI/WebServerResources/UIxContactsUserRightsEditor.css @@ -1,7 +1,7 @@ DIV.title { color: #000; vertical-align: bottom; - padding-top: 15px; + padding-top: 8px; padding-left: 1em; height: 33px; background-color: #fff; @@ -9,7 +9,7 @@ DIV.title DIV.title SPAN.value { margin-left: 1em; - font-size: 18px; + font-size: 14px; font-weight: bold; } DIV.calendarUserRights diff --git a/UI/WebServerResources/UIxMailUserRightsEditor.css b/UI/WebServerResources/UIxMailUserRightsEditor.css index eaea5734..3e7b6991 100644 --- a/UI/WebServerResources/UIxMailUserRightsEditor.css +++ b/UI/WebServerResources/UIxMailUserRightsEditor.css @@ -1,7 +1,7 @@ DIV.title { color: #000; vertical-align: bottom; - padding-top: 15px; + padding-top: 8px; padding-left: 1em; height: 33px; background-color: #fff; @@ -9,7 +9,7 @@ DIV.title DIV.title SPAN.value { margin-left: 1em; - font-size: 18px; + font-size: 14px; font-weight: bold; } DIV.calendarUserRights diff --git a/UI/WebServerResources/events.js b/UI/WebServerResources/events.js new file mode 100644 index 00000000..fdf04908 --- /dev/null +++ b/UI/WebServerResources/events.js @@ -0,0 +1,165 @@ +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini +// http://dean.edwards.name/weblog/2005/10/add-event/ +function addEvent(element, type, handler) { + // Modification by Tanny O'Haley, http://tanny.ica.com to add the + // DOMContentLoaded for all browsers. + if (type == "DOMContentLoaded" || type == "domload") { + addDOMLoadEvent(handler); + return; + } + + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +}; + +// End Dean Edwards addEvent. + +// Tino Zijdel - crisp@xs4all.nl This little snippet fixes the problem that the onload attribute on +// the body-element will overwrite previous attached events on the window object for the onload event. +if (!window.addEventListener) { + document.onreadystatechange = function(){ + if (window.onload && window.onload != handleEvent) { + addEvent(window, 'load', window.onload); + window.onload = handleEvent; + } + } +} + +// Here are my functions for adding the DOMContentLoaded event to browsers other +// than Mozilla. + +// Array of DOMContentLoaded event handlers. +window.onDOMLoadEvents = new Array(); +window.DOMContentLoadedInitDone = false; + +// Function that adds DOMContentLoaded listeners to the array. +function addDOMLoadEvent(listener) { + window.onDOMLoadEvents[window.onDOMLoadEvents.length]=listener; +} + +// Function to process the DOMContentLoaded events array. +function DOMContentLoadedInit() { + // quit if this function has already been called + if (window.DOMContentLoadedInitDone) return; + + // flag this function so we don't do the same thing twice + window.DOMContentLoadedInitDone = true; + + // iterates through array of registered functions + for (var i=0; i<\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + DOMContentLoadedInit(); // call the onload handler + } + }; +/*@end @*/ diff --git a/UI/WebServerResources/generic.css b/UI/WebServerResources/generic.css index ca630042..6c1ff919 100644 --- a/UI/WebServerResources/generic.css +++ b/UI/WebServerResources/generic.css @@ -556,6 +556,24 @@ A[class~="_disabled"].button:active { color: #999; background: inherit; } +LI.denied +{ background-color: #fefefe; + font-style: italic; + color: #f33; } + +LI._selected, +TR._selected > TD, +TD._selected +{ + background-color: #4b6983; + color: #fff; +} + +LI[class~="_selected"].denied +{ + background-color: #f33; +} + /* folder tree (js) )*/ DIV.dTreeNode A SPAN.nodeName {