+2007-12-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * SoObjects/Appointments/SOGoAppointmentFolder.m
+ ([SOGoAppointmentFolder -davCalendarQuery:queryContext]): fixed a leak.
+
+ * SoObjects/SOGo/SOGoGCSFolder.m ([SOGoGCSFolder -davSubscribe:localContext])
+ ([SOGoGCSFolder -davUnsubscribe:localContext]): subscribe an
+ unsubscribe from DAV-based accesses. We could be compatible with
+ Microsoft's extensions but we have no need for a "subcription id",
+ so we implement our own.
+
+ * SoObjects/SOGo/SOGoObject.m ([SOGoObject
+ -POSTAction:localContext]): new method to intercept DAV POSTs,
+ which we now use to implement certain custom commands such as
+ "subscribe" and "unsubscribe".
+
+ * SoObjects/SOGo/SOGoUserFolder.m ([SOGoUserFolder
+ -davNamespaces]): declare the
+ "urn:inverse:params:xml:ns:inverse-dav" xml ns.
+ ([SOGoUserFolder -foldersOfType:folderTypeforUID:uid]): new method
+ designed to replace the UIxContactFoldersView.m mechanism for
+ displaying folders to subcribe to, as a common code base for both
+ Web and DAV-based subscriptions.
+ ([SOGoUserFolder -foldersOfType:typematchingUID:uid]): same as
+ above.
+ ([SOGoUserFolder -davCollectionQuery:queryContext]): new method
+ that implement a custom DAV-based protocol query for querying
+ folder based on specified attributes.
+
+2007-12-03 Ludovic Marcotte <ludovic@inverse.ca>
+
+ * Main/NSException+Stacktrace.{h,m} - new files
+ to handle automatic stack trace generation
+ upon an uncaught exception.
+
+ * Updated the templates and Localizable.string files
+ to fix typos and add new strings.
+
+ * SoObjects/Mailer/SOGoMailBodyPart.m
+ Fixed attachment retreival when the first character
+ is a digit.
+
+ * SoObjects/SOGo/SOGoGCSFolder.m
+ Fixed the sending of emails when folders are created/removed.
+ Notifications are sent if the defaults SOGoFoldersSendEMailNotifications
+ is set to YES.
+
+ * UI/Common/UIxUserRightsEditor.m
+ * UI/Templates/SOGoACLEnglishModificationAdvisory.wox
+ * UI/Templates/SOGoACLFrenchModificationAdvisory.wox
+ * UI/Templates/SOGoACLGermanModificationAdvisory.wox
+ Added the capabilities to email notifications when ACLs have
+ changed on a DAV collection or an IMAP mailbox. Also added
+ new templates (3 .wox) to deal with this.
+
+ * UI/WebServerResources/ContactsUI.js
+ UI/WebServerResources/SchedulerUI.js
+ Added warnings on operations w/o selection.
+
2007-11-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoParentFolder.m ([SOGoParentFolder
include ./Version
ADDITIONAL_INCLUDE_DIRS += -I../SOPE/
-ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj/
+ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj/ -lbfd
SOGOD = sogod-$(MAJOR_VERSION).$(MINOR_VERSION)
TOOL_NAME = $(SOGOD)
$(SOGOD)_OBJC_FILES += \
sogod.m \
+ NSException+Stacktrace.m\
SOGo.m \
SOGoProductLoader.m \
build.m
#import "build.h"
#import "SOGoProductLoader.h"
+#import "NSException+Stacktrace.h"
@interface SOGo : SoApplication
{
NSMutableArray *filters;
NSDictionary *filter;
- filters = [NSMutableArray new];
-
+ filters = [NSMutableArray array];
children = [[parentNode getElementsByTagName: @"comp-filter"]
objectEnumerator];
node = [children nextObject];
max = [filters count];
for (count = 0; count < max; count++)
{
-#warning huh? why not objectAtIndex: count?
- currentFilter = [filters objectAtIndex: 0];
+ currentFilter = [filters objectAtIndex: count];
apts = [self fetchCoreInfosFrom: [currentFilter objectForKey: @"start"]
to: [currentFilter objectForKey: @"end"]
title: [currentFilter objectForKey: @"title"]
[_info setObject: to forKey: @"to"];
/* CC processing if we reply-to-all: add all 'to' and 'cc' */
-
if (_replyToAll)
{
to = [NSMutableArray new];
[self _addEMailsOfAddresses: [_envelope from] toArray: to];
}
+ /* If we have no To but we have Cc recipients, let's move the Cc
+ to the To bucket... */
+ if ([[_info objectForKey: @"to"] count] == 0 && [_info objectForKey: @"cc"])
+ {
+ id o;
+
+ o = [_info objectForKey: @"cc"];
+ [_info setObject: o forKey: @"to"];
+ [_info removeObjectForKey: @"cc"];
+ }
+
[allRecipients release];
[addrs release];
}
return [clazz objectWithName: _key inContainer: self];
}
+/* We overwrite the super's class method in order to make sure
+ we aren't dealing with our actual filename as the _key. That
+ could lead to problems if we weren't doing this as our filename
+ could start with a digit, leading to a wrong assumption in
+ the super class
+*/
+- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx
+{
+ NSString *s;
+
+ s = [[[self partInfo] objectForKey: @"parameterList"] objectForKey: @"name"];
+
+ if (!s)
+ s = [[[[self partInfo] objectForKey: @"disposition"] objectForKey: @"parameterList"] objectForKey: @"filename"];
+
+ if (s && [s isEqualToString: _key]) return NO;
+
+ return [super isBodyPartKey: _key inContext: _ctx];
+}
+
- (id) lookupName: (NSString *) _key
inContext: (id) _ctx
acquire: (BOOL) _flag
#import <Foundation/NSException.h>
#import <Foundation/NSKeyValueCoding.h>
#import <Foundation/NSURL.h>
+#import <Foundation/NSUserDefaults.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoObject.h>
#import <NGObjWeb/SoObject+SoDAV.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WOApplication.h>
+#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOQualifier.h>
#import "SOGoGCSFolder.h"
static NSString *defaultUserID = @"<default>";
+static BOOL sendFolderAdvisories = NO;
@implementation SOGoGCSFolder
++ (void) initialize
+{
+ NSUserDefaults *ud;
+
+ ud = [NSUserDefaults standardUserDefaults];
+ sendFolderAdvisories = [ud boolForKey: @"SOGoFoldersSendEMailNotifications"];
+}
+
+ (id) folderWithSubscriptionReference: (NSString *) reference
inContainer: (id) aContainer
{
[page send];
}
-// if (!result) [self sendFolderAdvisoryTemplate: @"Addition"];
-
- (BOOL) create
{
NSException *result;
withName: displayName
atPath: ocsPath];
+ if (!result && sendFolderAdvisories) [self sendFolderAdvisoryTemplate: @"Addition"];
+
return (result == nil);
}
else
error = [[self folderManager] deleteFolderAtPath: ocsPath];
+ if (!error && sendFolderAdvisories) [self sendFolderAdvisoryTemplate: @"Removal"];
+
return error;
}
-// if (!error) [self sendFolderAdvisoryTemplate: @"Removal"];
-
- (void) renameTo: (NSString *) newName
{
GCSChannelManager *cm;
return names;
}
+#warning this code should be cleaned up
+#warning this code is a dup of UIxFolderActions,\
+ we should remove the methods there instead
+- (WOResponse *) _subscribe: (BOOL) reallyDo
+ inContext: (WOContext *) localContext
+{
+ WOResponse *response;
+ NSMutableArray *folderSubscription;
+ NSString *subscriptionPointer, *baseFolder, *folder;
+ SOGoUser *activeUser;
+ NSUserDefaults *ud;
+ NSArray *realFolderPath;
+ NSMutableDictionary *moduleSettings;
+
+ activeUser = [localContext activeUser];
+ ud = [activeUser userSettings];
+ baseFolder = [container nameInContainer];
+ moduleSettings = [ud objectForKey: baseFolder];
+
+ response = [localContext response];
+ if ([owner isEqualToString: [activeUser login]])
+ {
+ [response setStatus: 403];
+ [response appendContentString:
+ @"You cannot (un)subscribe to a folder that you own!"];
+ }
+ else
+ {
+ folderSubscription
+ = [moduleSettings objectForKey: @"SubscribedFolders"];
+ if (!(folderSubscription
+ && [folderSubscription isKindOfClass: [NSMutableArray class]]))
+ {
+ folderSubscription = [NSMutableArray array];
+ [moduleSettings setObject: folderSubscription
+ forKey: @"SubscribedFolders"];
+ }
+
+ realFolderPath = [nameInContainer componentsSeparatedByString: @"_"];
+ if ([realFolderPath count] > 1)
+ folder = [realFolderPath objectAtIndex: 1];
+ else
+ folder = [realFolderPath objectAtIndex: 0];
+
+ subscriptionPointer = [NSString stringWithFormat: @"%@:%@/%@",
+ owner, baseFolder, folder];
+ if (reallyDo)
+ [folderSubscription addObjectUniquely: subscriptionPointer];
+ else
+ [folderSubscription removeObject: subscriptionPointer];
+
+ [ud synchronize];
+
+ [response setStatus: 204];
+ }
+
+ return response;
+}
+
+- (id <WOActionResults>) davSubscribe: (WOContext *) localContext
+{
+ return [self _subscribe: YES inContext: localContext];
+}
+
+- (id <WOActionResults>) davUnsubscribe: (WOContext *) localContext
+{
+ return [self _subscribe: NO inContext: localContext];
+}
+
/* acls as a container */
- (NSArray *) aclUsersForObjectAtPath: (NSArray *) objectPathArray;
#import <NGObjWeb/NSException+HTTP.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
+#import <DOM/DOMProtocols.h>
#import <NGCards/NSDictionary+NGCards.h>
#import <UI/SOGoUI/SOGoACLAdvisory.h>
return response;
}
+- (NSString *) _parseXMLCommand: (id <DOMDocument>) document
+{
+ NSString *command;
+
+ command = [[document firstChild] nodeName];
+
+ return [NSString stringWithFormat: @"%@:", command];
+}
+
+- (id) POSTAction: (id) localContext
+{
+ id obj;
+ NSString *cType, *command;
+ id <DOMDocument> document;
+ SEL commandSel;
+ WORequest *rq;
+
+ obj = nil;
+
+ rq = [localContext request];
+ if ([rq isSoWebDAVRequest])
+ {
+ cType = [rq headerForKey: @"content-type"];
+ if ([cType isEqualToString: @"application/xml"])
+ {
+ document = [rq contentAsDOMDocument];
+ command = [[self _parseXMLCommand: document] davMethodToObjC];
+ commandSel = NSSelectorFromString (command);
+ if ([self respondsToSelector: commandSel])
+ obj = [self performSelector: commandSel withObject: localContext];
+ }
+ }
+
+ return obj;
+}
+
- (id) GETAction: (id) localContext
{
// TODO: I guess this should really be done by SOPE (redirect to
/SOGo/so/znek/Calendar
*/
+@class NSArray;
+@class NSDictionary;
@class NSString;
@class WOContext;
- (NSString *) ownerInContext: (WOContext *) _ctx;
+- (NSArray *) foldersOfType: (NSString *) folderType
+ forUID: (NSString *) uid;
+- (NSDictionary *) foldersOfType: (NSString *) type
+ matchingUID: (NSString *) uid;
+
/* TODO: not implemented, bad bad */
// - (id)lookupFreeBusyObject;
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
+#import <Foundation/NSURL.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoClassSecurityInfo.h>
+#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSObject+Logs.h>
+#import <NGExtensions/NSString+misc.h>
+#import <DOM/DOMDocument.h>
+#import <DOM/DOMNode.h>
+#import <DOM/DOMProtocols.h>
+#import <SaxObjC/SaxObjC.h>
+#import <SaxObjC/XMLNamespaces.h>
#import <Appointments/SOGoAppointmentFolders.h>
#import <Appointments/SOGoFreeBusyObject.h>
#import <Contacts/SOGoContactFolders.h>
#import <Mailer/SOGoMailAccounts.h>
+#import "NSDictionary+Utilities.h"
+#import "LDAPUserManager.h"
#import "SOGoPermissions.h"
#import "SOGoUser.h"
return self;
}
+- (NSArray *) davNamespaces
+{
+ return [NSArray arrayWithObject: @"urn:inverse:params:xml:ns:inverse-dav"];
+}
+
+- (NSDictionary *) _parseCollectionFilters: (id <DOMDocument>) parentNode
+{
+ NSEnumerator *children;
+ NGDOMNode *node;
+ NSMutableDictionary *filter;
+ NSString *componentName;
+
+ filter = [NSMutableDictionary dictionaryWithCapacity: 2];
+ children = [[parentNode getElementsByTagName: @"prop-match"]
+ objectEnumerator];
+ while ((node = [children nextObject]))
+ {
+ componentName = [[node attribute: @"name"] lowercaseString];
+ [filter setObject: [node textValue] forKey: componentName];
+ }
+
+ return filter;
+}
+
+#warning UIxContactsFoldersView should use these methods\
+ instead from now on...
+
+- (NSArray *) _subFoldersFromFolder: (SOGoParentFolder *) parentFolder
+{
+ NSMutableArray *folders;
+ NSEnumerator *subfolders;
+ SOGoFolder *currentFolder;
+ NSString *folderName;
+ NSMutableDictionary *currentDictionary;
+ SoSecurityManager *securityManager;
+
+ securityManager = [SoSecurityManager sharedSecurityManager];
+
+ folders = [NSMutableArray array];
+
+ subfolders = [[parentFolder subFolders] objectEnumerator];
+ while ((currentFolder = [subfolders nextObject]))
+ {
+ if (![securityManager validatePermission: SOGoPerm_AccessObject
+ onObject: currentFolder inContext: context])
+ {
+ folderName = [NSString stringWithFormat: @"/%@/%@",
+ [parentFolder nameInContainer],
+ [currentFolder nameInContainer]];
+ currentDictionary
+ = [NSMutableDictionary dictionaryWithCapacity: 3];
+ [currentDictionary setObject: [currentFolder displayName]
+ forKey: @"displayName"];
+ [currentDictionary setObject: folderName forKey: @"name"];
+ [currentDictionary setObject: [currentFolder folderType]
+ forKey: @"type"];
+ [folders addObject: currentDictionary];
+ }
+ }
+
+ return folders;
+}
+
+- (NSArray *) foldersOfType: (NSString *) folderType
+ forUID: (NSString *) uid
+{
+ NSObject *userFolder;
+ SOGoParentFolder *parentFolder;
+ NSMutableArray *folders;
+
+ folders = [NSMutableArray array];
+
+ userFolder = [container lookupName: uid inContext: context acquire: NO];
+
+ /* FIXME: should be moved in the SOGo* classes. Maybe by having a SOGoFolderManager. */
+ if ([folderType length] == 0 || [folderType isEqualToString: @"calendar"])
+ {
+ parentFolder = [userFolder lookupName: @"Calendar"
+ inContext: context acquire: NO];
+ [folders
+ addObjectsFromArray: [self _subFoldersFromFolder: parentFolder]];
+ }
+ if ([folderType length] == 0 || [folderType isEqualToString: @"contact"])
+ {
+ parentFolder = [userFolder lookupName: @"Contacts"
+ inContext: context acquire: NO];
+ [folders
+ addObjectsFromArray: [self _subFoldersFromFolder: parentFolder]];
+ }
+
+ return folders;
+}
+
+- (NSDictionary *) foldersOfType: (NSString *) type
+ matchingUID: (NSString *) uid
+{
+ NSArray *contacts, *folders;
+ NSEnumerator *enumerator;
+ NSDictionary *contact;
+ NSMutableDictionary *results;
+
+ results = [NSMutableDictionary dictionary];
+
+ contacts
+ = [[LDAPUserManager sharedUserManager] fetchContactsMatching: uid];
+ enumerator = [contacts objectEnumerator];
+ while ((contact = [enumerator nextObject]))
+ {
+ uid = [contact objectForKey: @"c_uid"];
+ folders = [self foldersOfType: type
+ forUID: [contact objectForKey: @"c_uid"]];
+ [results setObject: folders forKey: contact];
+ }
+
+ return results;
+}
+
+- (NSString *) _baseDAVURLWithSuffix: (NSString *) suffix
+{
+ NSURL *prefixURL;
+
+ prefixURL = [NSURL URLWithString: [NSString stringWithFormat: @"../%@", suffix]
+ relativeToURL: [self davURL]];
+
+ return [[prefixURL standardizedURL] absoluteString];
+}
+
+- (void) _appendFolders: (NSDictionary *) users
+ toResponse: (WOResponse *) r
+{
+ NSDictionary *currentContact, *currentFolder;
+ NSEnumerator *keys, *folders;
+ NSString *baseHREF, *data;
+
+ baseHREF = [self _baseDAVURLWithSuffix: @"./"];
+
+ keys = [[users allKeys] objectEnumerator];
+ while ((currentContact = [keys nextObject]))
+ {
+ folders = [[users objectForKey: currentContact] objectEnumerator];
+ while ((currentFolder = [folders nextObject]))
+ {
+ [r appendContentString: @"<D:response><D:href>"];
+ data = [NSString stringWithFormat: @"%@%@%@", baseHREF,
+ [currentContact objectForKey: @"c_uid"],
+ [currentFolder objectForKey: @"name"]];
+ [r appendContentString: data];
+ [r appendContentString: @"</D:href><D:propstat>"];
+ [r appendContentString: @"<D:status>HTTP/1.1 200 OK</D:status>"];
+ [r appendContentString: @"</D:propstat><D:owner>"];
+ data = [NSString stringWithFormat: @"%@users/%@", baseHREF,
+ [currentContact objectForKey: @"c_uid"]];
+ [r appendContentString: data];
+ [r appendContentString: @"</D:owner><ownerdisplayname>"];
+ data = [currentContact keysWithFormat: @"%{cn} <%{c_email}>"];
+ [r appendContentString: [data stringByEscapingXMLString]];
+ [r appendContentString: @"</ownerdisplayname><D:displayname>"];
+ data = [currentFolder objectForKey: @"displayName"];
+ [r appendContentString: [data stringByEscapingXMLString]];
+ [r appendContentString: @"</D:displayname></D:response>\r\n"];
+ }
+ }
+}
+
+- (void) _appendCollectionsMatchingFilter: (NSDictionary *) filter
+ toResponse: (WOResponse *) r
+{
+ NSString *prefix, *queryOwner, *uid;
+ NSDictionary *folders;
+
+ prefix = [self _baseDAVURLWithSuffix: @"users/"];
+ queryOwner = [filter objectForKey: @"owner"];
+ if ([queryOwner hasPrefix: prefix])
+ {
+ uid = [queryOwner substringFromIndex: [prefix length]];
+ folders = [self foldersOfType: [filter objectForKey: @"resource-type"]
+ matchingUID: uid];
+ [self _appendFolders: folders toResponse: r];
+ }
+}
+
+- (id) davCollectionQuery: (id) queryContext
+{
+ WOResponse *r;
+ NSDictionary *filter;
+ id <DOMDocument> document;
+
+ r = [context response];
+ [r setStatus: 207];
+ [r setContentEncoding: NSUTF8StringEncoding];
+ [r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
+ [r setHeader: @"no-cache" forKey: @"pragma"];
+ [r setHeader: @"no-cache" forKey: @"cache-control"];
+ [r appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
+ [r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
+ @" xmlns=\"urn:ietf:params:xml:ns:inverse-dav\">\r\n"];
+
+ document = [[context request] contentAsDOMDocument];
+ filter = [self _parseCollectionFilters: document];
+ [self _appendCollectionsMatchingFilter: filter toResponse: r];
+
+ [r appendContentString:@"</D:multistatus>\r\n"];
+
+ return r;
+}
+
// - (SOGoGroupsFolder *) lookupGroupsFolder
// {
// return [self lookupName: @"Groups" inContext: nil acquire: NO];
*/
#import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOResponse.h>
#import <NGObjWeb/WORequest.h>
#import <SoObjects/SOGo/LDAPUserManager.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import <SoObjects/SOGo/SOGoObject.h>
+#import <SoObjects/SOGo/SOGoUser.h>
+#import <UI/SOGoUI/SOGoACLAdvisory.h>
+#import <Foundation/NSUserDefaults.h>
#import "UIxUserRightsEditor.h"
+static BOOL sendACLAdvisories = NO;
+
@implementation UIxUserRightsEditor
++ (void) initialize
+{
+ NSUserDefaults *ud;
+
+ ud = [NSUserDefaults standardUserDefaults];
+ sendACLAdvisories = [ud boolForKey: @"SOGoACLsSendEMailNotifications"];
+}
+
- (id) init
{
if ((self = [super init]))
return response;
}
+- (void) sendACLAdvisoryTemplateForObject: (id) theObject
+{
+ NSString *language, *pageName;
+ SOGoUser *user;
+ SOGoACLAdvisory *page;
+ WOApplication *app;
+
+ user = [SOGoUser userWithLogin: uid roles: nil];
+ language = [user language];
+ pageName = [NSString stringWithFormat: @"SOGoACL%@ModificationAdvisory",
+ language];
+
+ app = [WOApplication application];
+ page = [app pageWithName: pageName inContext: context];
+ [page setACLObject: theObject];
+ [page setRecipientUID: uid];
+ [page send];
+}
+
- (id <WOActionResults>) saveUserRightsAction
{
id <WOActionResults> response;
reason: @"No such user."];
else
{
+ NSArray *o;
+
+ o = [NSArray arrayWithArray: userRights];
+
[self updateRights];
[[self clientObject] setRoles: userRights forUser: uid];
+
+ if (![o isEqualToArray: userRights] && sendACLAdvisories)
+ {
+ [self sendACLAdvisoryTemplateForObject: [self clientObject]];
+ }
+
response = [self jsCloseWithRefreshMethod: nil];
}
"edit" = "edit";
"invalidemailwarn" = "invalidemailwarn";
"new" = "new";
+"Preferred Phone" = "Preferred Phone";
/* Folders */
"Personal Address Book" = "Personal Address Book";
"The selected contact has no email address."
= "The selected contact has no email address.";
+
+"Please select a contact." = "Please select a contact.";
"edit" = "Éditer";
"invalidemailwarn" = "Champ de l'email invalide, continuer quand même ?";
"new" = "Nouveau";
+"Preferred Phone" = "Numéro préféré";
/* Folders */
"Personal Address Book" = "Carnet d'adresses personnel";
"The selected contact has no email address."
= "Cette personne n'a pas d'adresse courriel.";
+"Please select a contact." = "Veuillez sélectionner un contact..";
\ No newline at end of file
"edit" = "Éditer";
"invalidemailwarn" = "Champ de l'email invalide, continuer quand même ?";
"new" = "Neu";
+"Preferred Phone" = "FIXME";
/* Folders */
"Personal Address Book" = "Personal Address Book";
"The selected contact has no email address."
= "The selected contact has no email address.";
+
+"Please select a contact." = "Bitte einen Kontakt auswählen.";
\ No newline at end of file
SoSecurityManager *securityManager;
securityManager = [SoSecurityManager sharedSecurityManager];
-
+
// return (([securityManager validatePermission: SoPerm_AccessContentsInformation
// onObject: contactFolder
// inContext: context] == nil)
responseString = [NSMutableString new];
contacts = [results objectEnumerator];
- contact = [contacts nextObject];
- while (contact)
+ while ((contact = [contacts nextObject]))
{
uid = [contact objectForKey: @"c_uid"];
folders = [self _foldersForUID: uid ofType: folderType];
[contact objectForKey: @"cn"],
[contact objectForKey: @"c_email"],
foldersString];
- contact = [contacts nextObject];
}
[response appendContentString: responseString];
[responseString release];
- (id) init
{
if ((self = [super init]))
- currentIndex = 0;
+ currentIndex = -1;
return self;
}
/* identifiers */
+- (NSString *) nextId
+{
+ currentIndex++;
+
+ return @"";
+}
+
- (NSString *) currentRowId
{
+ [self nextId];
+
return [NSString stringWithFormat: @"row_%d", currentIndex];
}
- (NSString *) currentPopUpId
{
+
return [NSString stringWithFormat: @"popup_%d", currentIndex];
}
return [NSString stringWithFormat: @"addr_%d", currentIndex];
}
-- (NSString *) nextId
-{
- currentIndex++;
-
- return @"";
-}
-
/* handling requests */
- (void) _fillAddresses: (NSMutableArray *) addresses
return [to count] + [cc count] + [bcc count];
}
-- (int) currentIndex
-{
- int count;
-
- count = [self addressCount];
-
- return count > 0 ? count - 1 : 0;
-}
-
@end /* UIxMailToSelection */
@interface SOGoACLGermanAdditionAdvisory : SOGoACLAdditionAdvisory
@end
+@interface SOGoACLEnglishModificationAdvisory : SOGoACLAdditionAdvisory
+@end
+
+@interface SOGoACLFrenchModificationAdvisory : SOGoACLAdditionAdvisory
+@end
+
+@interface SOGoACLGermanModificationAdvisory : SOGoACLAdditionAdvisory
+@end
+
@interface SOGoACLEnglishRemovalAdvisory : SOGoACLRemovalAdvisory
@end
return url;
}
+- (NSString *) httpFolderURL
+{
+ NSString *absoluteString;
+ NSMutableString *url;
+
+#warning the url returned by SOGoMail may be empty, we need to handle that
+ absoluteString = [[aclObject soURL] absoluteString];
+ url = [NSMutableString stringWithString: absoluteString];
+
+ if (![url hasSuffix: @"/"])
+ [url appendString: @"/"];
+
+ return url;
+}
+
- (NSString *) resourceName
{
return [aclObject nameInContainer];
@end
+@implementation SOGoACLModificationAdvisory
+
+- (NSString *) aclMethod { return @"modify"; }
+
+@end
+
@implementation SOGoACLEnglishAdditionAdvisory
@end
@implementation SOGoACLGermanAdditionAdvisory
@end
+@implementation SOGoACLEnglishModificationAdvisory
+@end
+
+@implementation SOGoACLFrenchModificationAdvisory
+@end
+
+@implementation SOGoACLGermanModificationAdvisory
+@end
+
@implementation SOGoACLEnglishRemovalAdvisory
@end
"closeThisWindowMessage" = "Thank you! You may now close this window or view your ";
"Multicolumn Day View" = "Multicolumn Day View";
+
+"Please select an event or a task." = "Please select an event or a task.";
\ No newline at end of file
"closeThisWindowMessage" = "Merci! Vous pouvez maintenant fermer cette fenêtre ou consulter votre ";
"Multicolumn Day View" = "Multicolonne";
+
+"Please select an event or a task." = "Veuillez sélectionner un événement ou une tâche.";
\ No newline at end of file
"closeThisWindowMessage" = "Vielen Dank! Sie können dieses Fenster jetzt schließen.";
"Multicolumn Day View" = "Multicolonne";
+
+"Please select an event or a task." = "Veuillez sélectionner un événement ou une tâche.";
\ No newline at end of file
><td class="tbtv_headercell sortableTableHeader" id="orgHeader"
><var:string label:value="Organization" /></td
><td class="tbtv_headercell sortableTableHeader" id="phoneHeader"
- ><var:string label:value="Work Phone" /></td
+ ><var:string label:value="Preferred Phone" /></td
></tr>
</thead>
<tbody>
<var:if condition="isBody">
<var:string value="currentUserName"/> has added you to the access list for his '<var:string value="resourceName"/>' folder.
+
You can subscribe directly to that folder by following this link:
<var:string value="httpAdvisoryURL"/>subscribe?mail-invitation=YES
Otherwise, you will be able to subscribe later from the SOGo web interface.
+
+You can also access this resource remotely using the following URL:
+
+<var:string value="httpFolderURL"/>
</var:if>
</container>
<var:if condition="isBody">
<var:string value="currentUserName"/> has removed you from the access list for his '<var:string value="resourceName"/>' folder.
+
You can unsubscribe directly to that folder by following this link:
<var:string value="httpAdvisoryURL"/>unsubscribe?mail-invitation=YES
Otherwise, you will be able to unsubscribe later from the SOGo web interface.
+
+You can also no longer access this resource using the following URL:
+
+<var:string value="httpFolderURL"/>
</var:if>
</container>
xmlns:label="OGo:label">
<var:if condition="isSubject">
- <var:string value="currentUserName"/> vous a ajouté
+ <var:string value="currentUserName"/> a modifié les droits
</var:if>
<var:if condition="isBody">
-<var:string value="currentUserName"/> vous a ajouté a sa liste de permission pour son dossier '<var:string value="resourceName"/>'.
-Vous pouvez vous inscrire directement a ce dossier en cliquant sur le lien suivant:
+<var:string value="currentUserName"/> vous a ajouté à sa liste de permission pour son dossier '<var:string value="resourceName"/>'.
+
+Vous pouvez vous inscrire directement à ce dossier en cliquant sur le lien suivant:
<var:string value="httpAdvisoryURL"/>unsubscribe?mail-invitation=YES
Autrement, il vous sera toujours possible de vous inscrire plus tard via l'interface web de SOGo.
+
+De plus, vous pouvez aussi accéder au dossier en utilisant le lien suivant:
+
+<var:string value="httpFolderURL"/>
</var:if>
</container>
<var:if condition="isBody">
<var:string value="currentUserName"/> vous a enlevé de sa liste de permission pour son dossier '<var:string value="resourceName"/>'.
+
Vous pouvez vous désinscrire directement en cliquant sur le lien suivant:
<var:string value="httpAdvisoryURL"/>unsubscribe?mail-invitation=YES
Autrement, il vous sera toujours possible de vous désinscrire plus tard via l'interface web de SOGo.
+
+De plus, vous ne pouvez plus accéder au dossier en utilisant le lien suivant:
+
+<var:string value="httpFolderURL"/>
</var:if>
</container>
<var:if condition="isBody">
<var:string value="currentUserName"/> hat Ihnen den Zugang zu seinem Ordner '<var:string value="resourceName"/>' erlaubt.
+
Folgende URL erlaubt Ihnen, sich sofort an diesem Ordner zu abonnieren:
<var:string value="httpAdvisoryURL"/>unsubscribe?mail-invitation=YES
<var:if condition="isBody">
<var:string value="currentUserName"/> erlaubt Ihnen nicht mehr den Zugang zu seinem Order '<var:string value="resourceName"/>'.
+
Sie können sich sofort an folgender URL von diesem Order des-abonnieren:
<var:string value="httpAdvisoryURL"/>unsubscribe?mail-invitation=YES
}
function onMenuWriteToContact(event) {
- var contactId = document.menuTarget.getAttribute('id');
- var contactRow = $(contactId);
- var emailCell = contactRow.down('td', 1);
+ var contactId = document.menuTarget.getAttribute('id');
+ var contactRow = $(contactId);
+ var emailCell = contactRow.down('td', 1);
- if (!emailCell.firstChild) { // .nodeValue is the contact email address
- window.alert(labels["The selected contact has no email address."]);
- return false;
- }
+ if (!emailCell.firstChild) { // .nodeValue is the contact email address
+ window.alert(labels["The selected contact has no email address."]);
+ return false;
+ }
- openMailComposeWindow(ApplicationBaseURL + currentContactFolder
- + "/" + contactId + "/write");
+ openMailComposeWindow(ApplicationBaseURL + currentContactFolder
+ + "/" + contactId + "/write");
- if (document.body.hasClassName("popup"))
- window.close();
+ if (document.body.hasClassName("popup"))
+ window.close();
}
function onMenuDeleteContact(event) {
var contactsList = $('contactsList');
var rows = contactsList.getSelectedRowsId();
+ if (rows.length == 0) {
+ window.alert(labels["Please select a contact."]);
+ return false;
+ }
+
for (var i = 0; i < rows.length; i++) {
openContactWindow(URLForFolderID(currentContactFolder)
+ "/" + rows[i] + "/edit", rows[i]);
var rows = contactsList.getSelectedRowsId();
var rowsWithEmail = 0;
- if (rows.length == 0)
+ if (rows.length == 0) {
+ window.alert(labels["Please select a contact."]);
return false;
+ }
for (var i = 0; i < rows.length; i++) {
var emailCell = $(rows[i]).down('td', 1);
var contactsList = $('contactsList');
var rows = contactsList.getSelectedRowsId();
+ if (rows.length == 0) {
+ window.alert(labels["Please select a contact."]);
+ return false;
+ }
+
var contactView = $('contactView');
contactView.innerHTML = '';
if (listOfSelection) {
var nodes = listOfSelection.getSelectedRows();
+ if (nodes.length == 0) {
+ window.alert(labels["Please select an event or a task."]);
+ return false;
+ }
+
for (var i = 0; i < nodes.length; i++)
_editEventId(nodes[i].getAttribute("id"),
nodes[i].calendar);
} else if (selectedCalendarCell) {
- _editEventId(selectedCalendarCell[0].cname,
- selectedCalendarCell[0].calendar);
+ _editEventId(selectedCalendarCell[0].cname,
+ selectedCalendarCell[0].calendar);
+ } else {
+ window.alert(labels["Please select an event or a task."]);
}
return false; /* stop following the link */
}
_batchDeleteEvents();
}
+ } else {
+ window.alert(labels["Please select an event or a task."]);
}
}
else if (selectedCalendarCell) {
}
}
else
- window.alert("no selection");
+ window.alert(labels["Please select an event or a task."]);
return false;
}
lastChild = $("lastRow");
currentIndex++;
-
- proto = $('row_' + lastIndex);
+ proto = lastChild.previous("tr");
row = proto.cloneNode(true);
- row.id = 'row_' + currentIndex;
+ row.setAttribute("id", 'row_' + currentIndex);
// select popup
var rowNodes = row.childNodesWithTag("td");
select = $(rowNodes[0]).childNodesWithTag("select")[0];
select.name = 'popup_' + currentIndex;
-// select.value = row.childNodesWithTag("span")[0].childNodesWithTag("select")[0].value;
+ select.value = proto.down("select").value;
input = $(rowNodes[1]).childNodesWithTag("input")[0];
input.name = 'addr_' + currentIndex;
input.id = 'addr_' + currentIndex;
function addressFieldLostFocus(sender) {
lastIndex = this.getIndexFromIdentifier(sender.id);
-
+
return false;
}
if (idx == 0) return;
addr = $('addr_' + idx);
if (!addr) return;
- if (addr.value != '') return;
+ if (addr.value.strip() != '') return;
addr = this.findAddressWithIndex(idx);
if(addr) {
var addresses = $('addr_addresses');
return (count > 0)
}
-/* addressbook helpers */
+function initMailToSelection() {
+ currentIndex = lastIndex = $$("table#addressList tr").length - 2;
+}
+FastInit.addOnLoad(initMailToSelection);