diff --git a/ChangeLog b/ChangeLog index 9f43ee5ef..1f6cc956e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2007-12-03 Ludovic Marcotte + + * 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 * SoObjects/SOGo/SOGoParentFolder.m ([SOGoParentFolder diff --git a/Main/GNUmakefile b/Main/GNUmakefile index 53a8adcfe..5688a61dd 100644 --- a/Main/GNUmakefile +++ b/Main/GNUmakefile @@ -17,6 +17,7 @@ all:: $(SOGOD)_OBJC_FILES += \ sogod.m \ + NSException+Stacktrace.m\ SOGo.m \ SOGoProductLoader.m \ build.m diff --git a/Main/NSException+Stacktrace.h b/Main/NSException+Stacktrace.h new file mode 100644 index 000000000..c71901c78 --- /dev/null +++ b/Main/NSException+Stacktrace.h @@ -0,0 +1,37 @@ +/** Interface for NSException for GNUStep + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + + Written by: Adam Fedor + Date: 1995 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. + + NSException and NSAssertionHandler class reference + + AutogsdocSource: NSAssertionHandler.m + AutogsdocSource: NSException.m + +*/ + +#import + +@interface NSException (SOGoExtensions) + +- (void) raise; + +@end diff --git a/Main/NSException+Stacktrace.m b/Main/NSException+Stacktrace.m new file mode 100644 index 000000000..f56046f40 --- /dev/null +++ b/Main/NSException+Stacktrace.m @@ -0,0 +1,540 @@ +/** NSException - Object encapsulation of a general exception handler + Copyright (C) 1993, 1994, 1996, 1997, 1999 Free Software Foundation, Inc. + + Written by: Adam Fedor + Date: Mar 1995 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. + + $Date: 2007-09-14 07:36:11 -0400 (Fri, 14 Sep 2007) $ $Revision: 25482 $ + + NOTE: This code was taken from GNUstep Base Library and sligthly modified + by Ludovic Marcotte +*/ + +#include "NSException+Stacktrace.h" + +#include +#include +#include +#include +#include +#include + +// +// +// +static void _terminate() +{ + abort(); +} + +static void +_NSFoundationUncaughtExceptionHandler (NSException *exception) +{ + NSString *stack; + + fprintf(stderr, "Uncaught exception %s, reason: %s\n", + [[exception name] lossyCString], [[exception reason] lossyCString]); + fflush(stderr); /* NEEDED UNDER MINGW */ + stack = [[[exception userInfo] objectForKey: @"GSStackTraceKey"] description]; + if (stack != nil) + { + fprintf(stderr, "Stack\n%s\n", [stack lossyCString]); + } + fflush(stderr); /* NEEDED UNDER MINGW */ + + _terminate(); +} + +// +// +// +#define STACKSYMBOLS 1 + +@interface GSStackTrace : NSObject +{ + NSMutableArray *frames; +} ++ (GSStackTrace*) currentStack; + +- (NSString*) description; +- (NSEnumerator*) enumerator; +- (id) frameAt: (unsigned)index; +- (unsigned) frameCount; +- (NSEnumerator*) reverseEnumerator; + +@end + +#if defined(STACKSYMBOLS) + +// GSStackTrace inspired by FYStackTrace.m +// created by Wim Oudshoorn on Mon 11-Apr-2006 +// reworked by Lloyd Dupont @ NovaMind.com on 4-May-2006 + +#include + +@class GSBinaryFileInfo; + +@interface GSFunctionInfo : NSObject +{ + void *_address; + NSString *_fileName; + NSString *_functionName; + int _lineNo; + GSBinaryFileInfo *_module; +} +- (void*) address; +- (NSString *) fileName; +- (NSString *) function; +- (id) initWithModule: (GSBinaryFileInfo*)module + address: (void*)address + file: (NSString*)file + function: (NSString*)function + line: (int)lineNo; +- (int) lineNumber; +- (GSBinaryFileInfo*) module; + +@end + + +@interface GSBinaryFileInfo : NSObject +{ + NSString *_fileName; + bfd *_abfd; + asymbol **_symbols; + long _symbolCount; +} +- (NSString *) fileName; +- (GSFunctionInfo *) functionForAddress: (void*) address; +- (id) initWithBinaryFile: (NSString *)fileName; +- (id) init; // return info for the current executing process + +@end + +@implementation GSFunctionInfo + +- (void*) address +{ + return _address; +} + +- (oneway void) dealloc +{ + [_module release]; + _module = nil; + [_fileName release]; + _fileName = nil; + [_functionName release]; + _functionName = nil; + [super dealloc]; +} + +- (NSString *) description +{ + return [NSString stringWithFormat: @"(%@: %p) %@ %@: %d", + [_module fileName], _address, _functionName, _fileName, _lineNo]; +} + +- (NSString *) fileName +{ + return _fileName; +} + +- (NSString *) function +{ + return _functionName; +} + +- (id) init +{ + [self release]; + return nil; +} + +- (id) initWithModule: (GSBinaryFileInfo*)module + address: (void*)address + file: (NSString*)file + function: (NSString*)function + line: (int)lineNo +{ + _module = [module retain]; + _address = address; + _fileName = [file retain]; + _functionName = [function retain]; + _lineNo = lineNo; + + return self; +} + +- (int) lineNumber +{ + return _lineNo; +} + +- (GSBinaryFileInfo *) module +{ + return _module; +} + +@end + +@implementation GSBinaryFileInfo + ++ (GSBinaryFileInfo*) infoWithBinaryFile: (NSString *)fileName +{ + return [[[self alloc] initWithBinaryFile: fileName] autorelease]; +} + ++ (void) initialize +{ + static BOOL first = YES; + + if (first == NO) + { + return; + } + first = NO; + bfd_init (); +} + +- (oneway void) dealloc +{ + [_fileName release]; + _fileName = nil; + if (_abfd) + { + bfd_close (_abfd); + _abfd = NULL; + } + if (_symbols) + { + objc_free (_symbols); + _symbols = NULL; + } + [super dealloc]; +} + +- (NSString *) fileName +{ + return _fileName; +} + +- (id) init +{ + NSString *processName; + + processName = [[[NSProcessInfo processInfo] arguments] objectAtIndex: 0]; + return [self initWithBinaryFile: processName]; +} + +- (id) initWithBinaryFile: (NSString *)fileName +{ + int neededSpace; + + // 1st initialize the bfd + if ([fileName length] == 0) + { + //NSLog (@"GSBinaryFileInfo: No File"); + [self release]; + return nil; + } + _fileName = [fileName copy]; + _abfd = bfd_openr ([fileName cString], NULL); + if (!_abfd) + { + //NSLog (@"GSBinaryFileInfo: No Binary Info"); + [self release]; + return nil; + } + if (!bfd_check_format_matches (_abfd, bfd_object, NULL)) + { + //NSLog (@"GSBinaryFileInfo: BFD format object error"); + [self release]; + return nil; + } + + // second read the symbols from it + if (!(bfd_get_file_flags (_abfd) & HAS_SYMS)) + { + //NSLog (@"GSBinaryFileInfo: BFD does not contain any symbols"); + [self release]; + return nil; + } + + neededSpace = bfd_get_symtab_upper_bound (_abfd); + if (neededSpace < 0) + { + //NSLog (@"GSBinaryFileInfo: BFD error while deducing needed space"); + [self release]; + return nil; + } + if (neededSpace == 0) + { + //NSLog (@"GSBinaryFileInfo: BFD no space for symbols needed"); + [self release]; + return nil; + } + _symbols = objc_malloc (neededSpace); + if (!_symbols) + { + //NSLog (@"GSBinaryFileInfo: Can't allocate buffer"); + [self release]; + return nil; + } + _symbolCount = bfd_canonicalize_symtab (_abfd, _symbols); + if (_symbolCount < 0) + { + //NSLog (@"GSBinaryFileInfo: BFD error while reading symbols"); + [self release]; + return nil; + } + + return self; +} + +struct SearchAddressStruct +{ + void *theAddress; + GSBinaryFileInfo *module; + asymbol **symbols; + GSFunctionInfo *theInfo; +}; + +static void find_address (bfd *abfd, asection *section, + struct SearchAddressStruct *info) +{ + bfd_vma address; + bfd_vma vma; + unsigned size; + const char *fileName; + const char *functionName; + unsigned line = 0; + + if (info->theInfo) + { + return; + } + if (!(bfd_get_section_flags (abfd, section) & SEC_ALLOC)) + { + return; + } + + address = (bfd_vma) (intptr_t)info->theAddress; + + vma = bfd_get_section_vma (abfd, section); + +#if defined(bfd_get_section_size) + size = bfd_get_section_size (section); // recent +#else + size = bfd_section_size (abfd, section); // older version +#endif + + if (address < vma || address >= vma + size) + { + return; + } + + if (bfd_find_nearest_line (abfd, section, info->symbols, + address - vma, &fileName, &functionName, &line)) + { + GSFunctionInfo *fi; + NSString *file = nil; + NSString *func = nil; + + if (fileName != 0) + { + file = [NSString stringWithCString: fileName + encoding: [NSString defaultCStringEncoding]]; + } + if (functionName != 0) + { + func = [NSString stringWithCString: functionName + encoding: [NSString defaultCStringEncoding]]; + } + fi = [GSFunctionInfo alloc]; + fi = [fi initWithModule: info->module + address: info->theAddress + file: file + function: func + line: line]; + [fi autorelease]; + info->theInfo = fi; + } +} + +- (GSFunctionInfo *) functionForAddress: (void*) address +{ + struct SearchAddressStruct searchInfo = { address, self, _symbols, nil }; + + bfd_map_over_sections (_abfd, + (void (*) (bfd *, asection *, void *)) find_address, &searchInfo); + return searchInfo.theInfo; +} + +@end + +static NSRecursiveLock *modLock = nil; +static NSMutableDictionary *stackModules = nil; + +// initialize stack trace info +static id +GSLoadModule(NSString *fileName) +{ + GSBinaryFileInfo *module = nil; + + [modLock lock]; + + if (stackModules == nil) + { + NSEnumerator *enumerator; + NSBundle *bundle; + + stackModules = [NSMutableDictionary new]; + + /* + * Try to ensure we have the main, base and gui library bundles. + */ + [NSBundle mainBundle]; + [NSBundle bundleForClass: [NSObject class]]; + [NSBundle bundleForClass: NSClassFromString(@"NSView")]; + + /* + * Add file info for all bundles with code. + */ + enumerator = [[NSBundle allBundles] objectEnumerator]; + while ((bundle = [enumerator nextObject]) != nil) + { + if ([bundle load] == YES) + { + GSLoadModule([bundle executablePath]); + } + } + } + + if ([fileName length] > 0) + { + module = [stackModules objectForKey: fileName]; + if (module == nil); + { + module = [GSBinaryFileInfo infoWithBinaryFile: fileName]; + if (module == nil) + { + module = (id)[NSNull null]; + } + if ([stackModules objectForKey: fileName] == nil) + { + [stackModules setObject: module forKey: fileName]; + } + else + { + module = [stackModules objectForKey: fileName]; + } + } + } + [modLock unlock]; + + if (module == (id)[NSNull null]) + { + module = nil; + } + return module; +} + +#endif /* STACKSYMBOLS */ + + +// +// +// +@implementation NSException (SOGoExtensions) + +- (void) raise +{ +#ifndef _NATIVE_OBJC_EXCEPTIONS + NSThread *thread; + NSHandler *handler; +#endif + + if ([_e_info objectForKey: @"GSStackTraceKey"] == nil) + { + NSMutableDictionary *m; + + if (_e_info == nil) + { + _e_info = m = [NSMutableDictionary new]; + } + else if ([_e_info isKindOfClass: [NSMutableDictionary class]] == YES) + { + m = (NSMutableDictionary*)_e_info; + } + else + { + m = [_e_info mutableCopy]; + RELEASE(_e_info); + _e_info = m; + } + [m setObject: [GSStackTrace currentStack] forKey: @"GSStackTraceKey"]; + } + +#ifdef _NATIVE_OBJC_EXCEPTIONS + @throw self; +#else + thread = GSCurrentThread(); + handler = thread->_exception_handler; + if (handler == NULL) + { + static int recursion = 0; + + /* + * Set/check a counter to prevent recursive uncaught exceptions. + * Allow a little recursion in case we have different handlers + * being tried. + */ + if (recursion++ > 3) + { + fprintf(stderr, + "recursion encountered handling uncaught exception\n"); + fflush(stderr); /* NEEDED UNDER MINGW */ + _terminate(); + } + + /* + * Call the uncaught exception handler (if there is one). + */ + if (_NSUncaughtExceptionHandler != NULL) + { + (*_NSUncaughtExceptionHandler)(self); + } + + /* + * The uncaught exception handler which is set has not + * exited, so we call the builtin handler, (undocumented + * behavior of MacOS-X). + * The standard handler is guaranteed to exit/abort. + */ + _NSFoundationUncaughtExceptionHandler(self); + } + + thread->_exception_handler = handler->next; + handler->exception = self; + longjmp(handler->jumpState, 1); +#endif +} + +@end diff --git a/Main/SOGo.m b/Main/SOGo.m index 32be4cedd..6bdb86e32 100644 --- a/Main/SOGo.m +++ b/Main/SOGo.m @@ -51,6 +51,7 @@ #import "build.h" #import "SOGoProductLoader.h" +#import "NSException+Stacktrace.h" @interface SOGo : SoApplication { diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 0ebb34320..4aac23ded 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -467,7 +467,6 @@ static BOOL showTextAttachmentsInline = NO; [_info setObject: to forKey: @"to"]; /* CC processing if we reply-to-all: add all 'to' and 'cc' */ - if (_replyToAll) { to = [NSMutableArray new]; @@ -498,6 +497,17 @@ static BOOL showTextAttachmentsInline = NO; [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]; } diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index c3bcf78db..c60f34b9b 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -144,6 +144,26 @@ static BOOL debugOn = NO; 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 diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 69e2a3561..1c5646d59 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -55,9 +55,18 @@ #import "SOGoGCSFolder.h" static NSString *defaultUserID = @""; +static BOOL sendFolderAdvisories = NO; @implementation SOGoGCSFolder ++ (void) initialize +{ + NSUserDefaults *ud; + + ud = [NSUserDefaults standardUserDefaults]; + sendFolderAdvisories = [ud boolForKey: @"SOGoFoldersSendEMailNotifications"]; +} + + (id) folderWithSubscriptionReference: (NSString *) reference inContainer: (id) aContainer { @@ -254,8 +263,6 @@ static NSString *defaultUserID = @""; [page send]; } -// if (!result) [self sendFolderAdvisoryTemplate: @"Addition"]; - - (BOOL) create { NSException *result; @@ -264,6 +271,8 @@ static NSString *defaultUserID = @""; withName: displayName atPath: ocsPath]; + if (!result && sendFolderAdvisories) [self sendFolderAdvisoryTemplate: @"Addition"]; + return (result == nil); } @@ -280,11 +289,11 @@ static NSString *defaultUserID = @""; 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; diff --git a/UI/Common/UIxUserRightsEditor.m b/UI/Common/UIxUserRightsEditor.m index ad674f63e..d447ae844 100644 --- a/UI/Common/UIxUserRightsEditor.m +++ b/UI/Common/UIxUserRightsEditor.m @@ -21,16 +21,30 @@ */ #import +#import #import #import #import #import #import +#import +#import +#import #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])) @@ -121,6 +135,25 @@ 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 ) saveUserRightsAction { id response; @@ -130,8 +163,18 @@ 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]; } diff --git a/UI/Contacts/English.lproj/Localizable.strings b/UI/Contacts/English.lproj/Localizable.strings index f1dbba2ea..9d17ab3ea 100644 --- a/UI/Contacts/English.lproj/Localizable.strings +++ b/UI/Contacts/English.lproj/Localizable.strings @@ -31,6 +31,7 @@ "edit" = "edit"; "invalidemailwarn" = "invalidemailwarn"; "new" = "new"; +"Preferred Phone" = "Preferred Phone"; /* Folders */ "Personal Address Book" = "Personal Address Book"; @@ -131,3 +132,5 @@ "The selected contact has no email address." = "The selected contact has no email address."; + +"Please select a contact." = "Please select a contact."; diff --git a/UI/Contacts/French.lproj/Localizable.strings b/UI/Contacts/French.lproj/Localizable.strings index cb2df47d5..4854e4855 100644 --- a/UI/Contacts/French.lproj/Localizable.strings +++ b/UI/Contacts/French.lproj/Localizable.strings @@ -38,6 +38,7 @@ "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"; @@ -145,3 +146,4 @@ "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 diff --git a/UI/Contacts/German.lproj/Localizable.strings b/UI/Contacts/German.lproj/Localizable.strings index b220e74f2..c5ffe0bcf 100644 --- a/UI/Contacts/German.lproj/Localizable.strings +++ b/UI/Contacts/German.lproj/Localizable.strings @@ -38,6 +38,7 @@ "edit" = "Éditer"; "invalidemailwarn" = "Champ de l'email invalide, continuer quand même ?"; "new" = "Neu"; +"Preferred Phone" = "FIXME"; /* Folders */ "Personal Address Book" = "Personal Address Book"; @@ -135,3 +136,5 @@ "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 diff --git a/UI/SOGoUI/SOGoACLAdvisory.h b/UI/SOGoUI/SOGoACLAdvisory.h index 762b89a1d..d991dbad0 100644 --- a/UI/SOGoUI/SOGoACLAdvisory.h +++ b/UI/SOGoUI/SOGoACLAdvisory.h @@ -68,6 +68,15 @@ @interface SOGoACLGermanAdditionAdvisory : SOGoACLAdditionAdvisory @end +@interface SOGoACLEnglishModificationAdvisory : SOGoACLAdditionAdvisory +@end + +@interface SOGoACLFrenchModificationAdvisory : SOGoACLAdditionAdvisory +@end + +@interface SOGoACLGermanModificationAdvisory : SOGoACLAdditionAdvisory +@end + @interface SOGoACLEnglishRemovalAdvisory : SOGoACLRemovalAdvisory @end diff --git a/UI/SOGoUI/SOGoACLAdvisory.m b/UI/SOGoUI/SOGoACLAdvisory.m index b52ebcc19..97994ae3c 100644 --- a/UI/SOGoUI/SOGoACLAdvisory.m +++ b/UI/SOGoUI/SOGoACLAdvisory.m @@ -97,6 +97,21 @@ 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]; @@ -223,6 +238,12 @@ @end +@implementation SOGoACLModificationAdvisory + +- (NSString *) aclMethod { return @"modify"; } + +@end + @implementation SOGoACLEnglishAdditionAdvisory @end @@ -232,6 +253,15 @@ @implementation SOGoACLGermanAdditionAdvisory @end +@implementation SOGoACLEnglishModificationAdvisory +@end + +@implementation SOGoACLFrenchModificationAdvisory +@end + +@implementation SOGoACLGermanModificationAdvisory +@end + @implementation SOGoACLEnglishRemovalAdvisory @end diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 494b6d747..c6e1c5b0b 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -423,3 +423,5 @@ vtodo_class2 = "(Confidential task)"; "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 diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 1688824d9..725f03584 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -422,3 +422,5 @@ vtodo_class2 = "(Tâche confidentielle)"; "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 diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index 1f65637c7..15ab31454 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -411,3 +411,5 @@ vtodo_class2 = "(Confidential task)"; "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 diff --git a/UI/Templates/ContactsUI/UIxContactsListView.wox b/UI/Templates/ContactsUI/UIxContactsListView.wox index efc8c859f..0593bb3fb 100644 --- a/UI/Templates/ContactsUI/UIxContactsListView.wox +++ b/UI/Templates/ContactsUI/UIxContactsListView.wox @@ -23,7 +23,7 @@ > diff --git a/UI/Templates/SOGoACLEnglishAdditionAdvisory.wox b/UI/Templates/SOGoACLEnglishAdditionAdvisory.wox index b15cd0da6..250cb5189 100644 --- a/UI/Templates/SOGoACLEnglishAdditionAdvisory.wox +++ b/UI/Templates/SOGoACLEnglishAdditionAdvisory.wox @@ -13,10 +13,15 @@ has added you to the access list for his '' folder. + You can subscribe directly to that folder by following this link: 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: + + diff --git a/UI/Templates/SOGoACLEnglishModificationAdvisory.wox b/UI/Templates/SOGoACLEnglishModificationAdvisory.wox new file mode 100644 index 000000000..54fa443b8 --- /dev/null +++ b/UI/Templates/SOGoACLEnglishModificationAdvisory.wox @@ -0,0 +1,27 @@ + + + + + + has modified the access rights + + + + has modified your access rights for his '' folder. + +You can subscribe directly to that folder by following this link: + 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: + + + + + diff --git a/UI/Templates/SOGoACLEnglishRemovalAdvisory.wox b/UI/Templates/SOGoACLEnglishRemovalAdvisory.wox index eff944b00..6a4009357 100644 --- a/UI/Templates/SOGoACLEnglishRemovalAdvisory.wox +++ b/UI/Templates/SOGoACLEnglishRemovalAdvisory.wox @@ -13,10 +13,15 @@ has removed you from the access list for his '' folder. + You can unsubscribe directly to that folder by following this link: 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: + + diff --git a/UI/Templates/SOGoACLFrenchAdditionAdvisory.wox b/UI/Templates/SOGoACLFrenchAdditionAdvisory.wox index 4686ccf75..3166ecc23 100644 --- a/UI/Templates/SOGoACLFrenchAdditionAdvisory.wox +++ b/UI/Templates/SOGoACLFrenchAdditionAdvisory.wox @@ -8,15 +8,20 @@ xmlns:label="OGo:label"> - vous a ajouté + a modifié les droits - vous a ajouté a sa liste de permission pour son dossier ''. -Vous pouvez vous inscrire directement a ce dossier en cliquant sur le lien suivant: + vous a ajouté à sa liste de permission pour son dossier ''. + +Vous pouvez vous inscrire directement à ce dossier en cliquant sur le lien suivant: 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: + + diff --git a/UI/Templates/SOGoACLFrenchModificationAdvisory.wox b/UI/Templates/SOGoACLFrenchModificationAdvisory.wox new file mode 100644 index 000000000..3a5c5e042 --- /dev/null +++ b/UI/Templates/SOGoACLFrenchModificationAdvisory.wox @@ -0,0 +1,27 @@ + + + + + + vous a ajouté + + + + a modifité vos droits d'accès pour son dossier ''. + +Vous pouvez vous inscrire directement à ce dossier en cliquant sur le lien suivant: + 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: + + + + + diff --git a/UI/Templates/SOGoACLFrenchRemovalAdvisory.wox b/UI/Templates/SOGoACLFrenchRemovalAdvisory.wox index 7679866ab..497a54eaf 100644 --- a/UI/Templates/SOGoACLFrenchRemovalAdvisory.wox +++ b/UI/Templates/SOGoACLFrenchRemovalAdvisory.wox @@ -13,10 +13,15 @@ vous a enlevé de sa liste de permission pour son dossier ''. + Vous pouvez vous désinscrire directement en cliquant sur le lien suivant: 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: + + diff --git a/UI/Templates/SOGoACLGermanAdditionAdvisory.wox b/UI/Templates/SOGoACLGermanAdditionAdvisory.wox index 313f9a448..d53f17b87 100644 --- a/UI/Templates/SOGoACLGermanAdditionAdvisory.wox +++ b/UI/Templates/SOGoACLGermanAdditionAdvisory.wox @@ -13,6 +13,7 @@ hat Ihnen den Zugang zu seinem Ordner '' erlaubt. + Folgende URL erlaubt Ihnen, sich sofort an diesem Ordner zu abonnieren: unsubscribe?mail-invitation=YES diff --git a/UI/Templates/SOGoACLGermanModificationAdvisory.wox b/UI/Templates/SOGoACLGermanModificationAdvisory.wox new file mode 100644 index 000000000..5fd02794a --- /dev/null +++ b/UI/Templates/SOGoACLGermanModificationAdvisory.wox @@ -0,0 +1,23 @@ + + + + + + Zugangserlaubniss von erstellt + + + + PLEASE TRANSLATE '' XXXXXXXXXX. + +Folgende URL erlaubt Ihnen, sich sofort an diesem Ordner zu abonnieren: + unsubscribe?mail-invitation=YES + +Sie können sich auch später immer noch durch die SOGo Webseiten abonnieren. + + + diff --git a/UI/Templates/SOGoACLGermanRemovalAdvisory.wox b/UI/Templates/SOGoACLGermanRemovalAdvisory.wox index 9638f657c..9e02af19b 100644 --- a/UI/Templates/SOGoACLGermanRemovalAdvisory.wox +++ b/UI/Templates/SOGoACLGermanRemovalAdvisory.wox @@ -13,6 +13,7 @@ erlaubt Ihnen nicht mehr den Zugang zu seinem Order ''. + Sie können sich sofort an folgender URL von diesem Order des-abonnieren: unsubscribe?mail-invitation=YES diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 8b449799c..087d4f848 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -310,20 +310,20 @@ function onMenuEditContact(event) { } 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) { @@ -334,6 +334,11 @@ function onToolbarEditSelectedContacts(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]); @@ -347,8 +352,10 @@ function onToolbarWriteToSelectedContacts(event) { 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); @@ -373,6 +380,11 @@ function uixDeleteSelectedContacts(sender) { 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 = ''; diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 79be003e6..da4c678d8 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -81,12 +81,19 @@ function editEvent() { 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 */ @@ -135,6 +142,8 @@ function deleteEvent() { } _batchDeleteEvents(); } + } else { + window.alert(labels["Please select an event or a task."]); } } else if (selectedCalendarCell) { @@ -150,7 +159,7 @@ function deleteEvent() { } } else - window.alert("no selection"); + window.alert(labels["Please select an event or a task."]); return false; }