+2007-11-26 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * UI/PreferencesUI/UIxPreferences.m ([UIxPreferences -userHasCalendarAccess])
+ ([UIxPreferences -userHasMailAccess]): same as below, for
+ displaying preference tabs.
+
+ * UI/Common/UIxPageFrame.m ([UIxPageFrame
+ -userHasCalendarAccess]): new accessor for the link banner.
+ ([UIxPageFrame -userHasMailAccess]): same as above.
+
+ * SoObjects/SOGo/SOGoUserFolder.m ([SOGoUserFolder
+ -toManyRelationshipKeys]): do not report the path to modules to
+ which the user has no access. No longer cache this information
+ statically, the array will be generated at each call.
+ ([SOGoUserFolder -lookupName:_keyinContext:_ctxacquire:_flag]):
+ ignore the path to modules to which the user has no access.
+
+ * SoObjects/SOGo/SOGoUser.m ([SOGoUser -canAccessModule:module]):
+ new method that returns whether the user has access to the
+ specified module.
+
+ * SoObjects/SOGo/LDAPSource.m ([LDAPSource
+ -initFromUDSource:udSource]): take a new parameter named
+ 'ModulesContraints' that defines a set of constraints for
+ accessing specified named modules. This is an optout, meaning the
+ modules will be present unless a constraint is specified.
+
+ * SoObjects/Appointments/SOGoAppointmentFolder.m
+ ([SOGoAppointmentFolder
+ -roleForComponentsWithAccessClass:accessClassforUser:uid]): cache
+ acl to reduce method calls.
+ ([SOGoAppointmentFolder
+ -fetchFields:_fieldsfromFolder:_folderfrom:_startDateto:_endDatetitle:titlecomponent:_component]):
+ if the user is not the current user, we strip the useless info
+ from the meta data.
+
+ * UI/Common/WODirectAction+SOGo.m ([WODirectAction
+ -labelForKey:key]): new category method.
+
+ * UI/Scheduler/UIxAppointmentEditor.m ([UIxAppointmentEditor
+ -event]): same as below.
+
+ * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -todo]): request a
+ secured version of the component.
+
+ * UI/MailPartViewers/UIxMailPartViewer.m
+ ([-fallbackStringEncoding]): removed method.
+ ([UIxMailPartViewer -flatContentAsString]): we no longer use [self
+ fallbackStringEncoding]. Instead we directly specify
+ NSISOLatin1StringEncoding if UTF-8 has failed.
+
+ * UI/MailPartViewers/UIxMailPartICalViewer.m
+ ([-fallbackStringEncoding]): removed method.
+
+2007-11-25 Ludovic Marcotte <ludovic@inverse.ca>
+
+ * SoObjects/Mailer/SOGoMailForward.m
+ Use [sourceMail decodedSubject] instead of [sourceMail subject]
+ Signature fix in -signature - see the comment
+ for SOGoMailReply.
+
+ * SoObjects/Mailer/SOGoMailObject.m
+ Improved -stringFromData: to try UTF-8 then fallback to Latin1
+
+ * SoObjects/Mailer/SOGoMailReply.m
+ Modified -messageBody to strip the signature from the reply.
+ Also modified -signature to add "-- \n%@" instead of the
+ broken ""--\r\n%@" pattern.
+
+ * UI/MailPartViewers/UIxMailPartAlternativeViewer.m
+ We now favor text/calendar parts over text/html and
+ text/plain parts when viewing a multipart/alternative mail.
+ This allows us to show the email invitations coming from
+ Microsoft Outlook.
+
+ * UI/MailerUI/UIxMailAccountActions.m
+ Signature fix in -composeAction - see the comment
+ for SOGoMailReply.
+
+ * UI/MailPartViewers/UIxMailPartViewer.m
+ Modified -flatContentAsString to use UTF-8 as the
+ default fallback encoding for 8-bit content.
+
+ * SoObjects/Mailer/SOGoDraftObject.m
+ Modified _fillInReplyAddresses:replyToAll:envelope:
+ so that if there's no recipient, we add at least
+ ourself to the list.
+ Fixed a mem leak in the same method.
+
2007-11-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/MailPartViewers/UIxMailRenderingContext.m
+2007-11-26 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * iCalCalendar.m ([iCalCalendar -versitString]): same as below,
+ with "2.0" as value.
+
+ * NGVCard.m ([NGVCard -versitString]): overriden method by forcing
+ version to "3.0" since it's the format we comply with.
+
+
2007-11-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalEntityObject.m ([iCalEntityObject -compare:otherObject]):
@implementation NGVCard
-+ (id) parseSingleFromSource: (id) source
-{
- NGVCard *newCard;
-
- newCard = [super parseSingleFromSource: source];
- [newCard setVersion: @"3.0"];
-
- return newCard;
-}
-
+ (id) cardWithUid: (NSString *) _uid
{
NGVCard *newCard;
return [self _preferredElementWithTag: @"adr"];
}
+- (NSString *) versitString
+{
+ [self setVersion: @"3.0"];
+
+ return [super versitString];
+}
+
/* description */
- (void) appendAttributesToDescription: (NSMutableString *) _ms
return ma;
}
+- (NSString *) versitString
+{
+ [self setVersion: @"2.0"];
+
+ return [super versitString];
+}
+
/* ical typing */
- (NSString *) entityName
#import "SOGo/SOGoGCSFolder.h"
+#import <NGCards/iCalEntityObject.h>
+
@class NSArray;
@class NSCalendarDate;
@class NSException;
@class NSTimeZone;
@class GCSFolder;
-#import <NGCards/iCalEntityObject.h>
@interface SOGoAppointmentFolder : SOGoGCSFolder
{
NSTimeZone *timeZone;
NSMutableDictionary *uidToFilename;
+ NSMutableDictionary *aclMatrix;
+ NSMutableArray *stripFields;
}
- (BOOL) isActive;
#import <GDLContentStore/GCSFolder.h>
#import <DOM/DOMProtocols.h>
#import <EOControl/EOQualifier.h>
+#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceCalculator.h>
if ((self = [super initWithName: name inContainer: newContainer]))
{
timeZone = [[context activeUser] timeZone];
+ aclMatrix = [NSMutableDictionary new];
+ stripFields = nil;
+ uidToFilename = nil;
}
return self;
- (void) dealloc
{
+ [aclMatrix release];
+ [stripFields release];
[uidToFilename release];
[super dealloc];
}
[self _privacyClassificationStringsForUID: login],
email, email, email];
}
-
+
return privacySqlString;
}
{
NSString *accessRole, *prefix, *currentRole, *suffix;
NSEnumerator *acls;
+ NSMutableDictionary *userRoles;
accessRole = nil;
else
prefix = @"Confidential";
- acls = [[self aclsForUser: uid] objectEnumerator];
- currentRole = [acls nextObject];
- while (currentRole && !accessRole)
- if ([currentRole hasPrefix: prefix])
- {
- suffix = [currentRole substringFromIndex: [prefix length]];
- accessRole = [NSString stringWithFormat: @"Component%@", suffix];
- }
- else
+ userRoles = [aclMatrix objectForKey: uid];
+ if (!userRoles)
+ {
+ userRoles = [NSMutableDictionary dictionaryWithCapacity: 3];
+ [aclMatrix setObject: userRoles forKey: uid];
+ }
+
+ accessRole = [userRoles objectForKey: prefix];
+ if (!accessRole)
+ {
+ acls = [[self aclsForUser: uid] objectEnumerator];
currentRole = [acls nextObject];
+ while (currentRole && !accessRole)
+ if ([currentRole hasPrefix: prefix])
+ {
+ suffix = [currentRole substringFromIndex: [prefix length]];
+ accessRole = [NSString stringWithFormat: @"Component%@", suffix];
+ }
+ else
+ currentRole = [acls nextObject];
+ if (!accessRole)
+ accessRole = @"";
+ [userRoles setObject: accessRole forKey: prefix];
+ }
return accessRole;
}
+- (void) _buildStripFieldsFromFields: (NSArray *) fields
+{
+ stripFields = [[NSMutableArray alloc] initWithCapacity: [fields count]];
+ [stripFields setArray: fields];
+ [stripFields removeObjectsInArray: [NSArray arrayWithObjects: @"c_name",
+ @"c_uid", @"c_startdate",
+ @"c_enddate", @"c_isallday",
+ @"c_iscycle",
+ @"c_classification",
+ @"c_component", nil]];
+}
+
+- (void) _fixupProtectedInformation: (NSEnumerator *) ma
+ inFields: (NSArray *) fields
+ forUser: (NSString *) uid
+{
+ NSMutableDictionary *currentRecord;
+ NSString *roles[] = {nil, nil, nil};
+ iCalAccessClass accessClass;
+ NSString *role;
+
+ if (!stripFields)
+ [self _buildStripFieldsFromFields: fields];
+
+#warning we do not take the participation status into account
+ while ((currentRecord = [ma nextObject]))
+ {
+ accessClass
+ = [[currentRecord objectForKey: @"c_classification"] intValue];
+ role = roles[accessClass];
+ if (!role)
+ {
+ role = [[self roleForComponentsWithAccessClass: accessClass
+ forUser: uid] substringFromIndex: 9];
+ roles[accessClass] = role;
+ }
+ if ([role isEqualToString: @"DAndTViewer"])
+ [currentRecord removeObjectsForKeys: stripFields];
+ }
+}
+
- (NSArray *) fetchFields: (NSArray *) _fields
fromFolder: (GCSFolder *) _folder
from: (NSCalendarDate *) _startDate
NSMutableArray *fields, *ma = nil;
NSArray *records;
NSString *sql, *dateSqlString, *titleSqlString, *componentSqlString,
- *privacySqlString;
+ *privacySqlString, *currentLogin;
NGCalendarDateRange *r;
- if (_folder == nil) {
- [self errorWithFormat:@"(%s): missing folder for fetch!",
+ if (!_folder)
+ {
+ [self errorWithFormat:@"(%s): missing folder for fetch!",
__PRETTY_FUNCTION__];
- return nil;
- }
-
+ return nil;
+ }
+
if (_startDate && _endDate)
{
r = [NGCalendarDateRange calendarDateRangeWithStartDate: _startDate
if (logger)
[self debugWithFormat:@"returning %i records", [ma count]];
+ currentLogin = [[context activeUser] login];
+ if (![currentLogin isEqualToString: owner])
+ [self _fixupProtectedInformation: [ma objectEnumerator]
+ inFields: _fields
+ forUser: currentLogin];
// [ma makeObjectsPerform: @selector (setObject:forKey:)
// withObject: owner
// withObject: @"owner"];
qs = [NSString stringWithFormat:
@"(c_sn isCaseInsensitiveLike: '%@%%') OR "
@"(c_givenname isCaseInsensitiveLike: '%@%%') OR "
- @"(c_mail isCaseInsensitiveLike: '%@%%') OR "
+ @"(c_mail isCaseInsensitiveLike: '%%%@%%') OR "
@"(c_telephonenumber isCaseInsensitiveLike: '%%%@%%')",
filter, filter, filter, filter];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[to release];
}
+ /* If "to" is empty, we add at least ourself as a recipient!
+ This is for emails in the "Sent" folder that we reply to... */
+ if (![to count])
+ {
+ if ([[_envelope replyTo] count])
+ [self _addEMailsOfAddresses: [_envelope replyTo] toArray: to];
+ else
+ [self _addEMailsOfAddresses: [_envelope from] toArray: to];
+ }
+
[allRecipients release];
+ [addrs release];
}
- (NSArray *) _attachmentBodiesFromPaths: (NSArray *) paths
- (NSString *) subject
{
- return [sourceMail subject];
+ return [sourceMail decodedSubject];
}
- (NSString *) date
signature = [[context activeUser] signature];
if ([signature length])
- mailSignature = [NSString stringWithFormat: @"--\r\n%@", signature];
+ mailSignature = [NSString stringWithFormat: @"-- \n%@", signature];
else
mailSignature = @"";
charset = [[_info valueForKey: @"parameterList"] valueForKey: @"charset"];
if (![charset length])
{
- s = [[NSString alloc] initWithData: mailData encoding: NSUTF8StringEncoding];
- [s autorelease];
+ s = nil;
}
else
{
s = [NSString stringWithData: mailData usingEncodingNamed: charset];
-
- // If it has failed, we try at least using UTF-8. Normally, this can NOT fail
- if (!s)
- s = [[[NSString alloc] initWithData: mailData encoding: NSISOLatin1StringEncoding] autorelease];
}
+
+ // If it has failed, we try at least using UTF-8. Normally, this can NOT fail.
+ // Unfortunately, it seems to fail under GNUstep so we try latin1 if that's
+ // the case
+ if (!s)
+ s = [[[NSString alloc] initWithData: mailData encoding: NSUTF8StringEncoding] autorelease];
+
+ if (!s)
+ s = [[[NSString alloc] initWithData: mailData encoding: NSISOLatin1StringEncoding] autorelease];
}
else
s = nil;
- (NSString *) messageBody
{
- return [[sourceMail contentForEditing] stringByApplyingMailQuoting];
+ NSString *s;
+
+ s = [sourceMail contentForEditing];
+
+ if (s)
+ {
+ NSRange r;
+
+ r = [s rangeOfString: @"\n-- \n" options: NSBackwardsSearch];
+
+ if (r.length)
+ s = [s substringToIndex: r.location];
+ }
+
+ return [s stringByApplyingMailQuoting];
}
- (NSString *) signature
signature = [[context activeUser] signature];
if ([signature length])
- mailSignature = [NSString stringWithFormat: @"--\r\n%@", signature];
+ mailSignature = [NSString stringWithFormat: @"-- \n%@", signature];
else
mailSignature = @"";
NSArray *mailFields;
NSString *bindFields;
+ NSDictionary *modulesConstraints;
+
NGLdapConnection *ldapConnection;
NSMutableArray *searchAttributes;
}
[bindFields release];
[ldapConnection release];
[sourceID release];
+ [modulesConstraints release];
[super dealloc];
}
UIDField: [udSource objectForKey: @"UIDFieldName"]
mailFields: [udSource objectForKey: @"MailFieldNames"]
andBindFields: [udSource objectForKey: @"bindFields"]];
+ ASSIGN (modulesConstraints, [udSource objectForKey: @"ModulesConstraints"]);
return self;
}
return [EOQualifier qualifierWithQualifierFormat: qs];
}
+- (NSArray *) _contraintsFields
+{
+ NSMutableArray *fields;
+ NSEnumerator *values;
+ NSDictionary *currentConstraint;
+
+ fields = [NSMutableArray array];
+ values = [[modulesConstraints allValues] objectEnumerator];
+ while ((currentConstraint = [values nextObject]))
+ [fields addObjectsFromArray: [currentConstraint allKeys]];
+
+ return fields;
+}
+
- (NSArray *) _searchAttributes
{
if (!searchAttributes)
if (UIDField)
[searchAttributes addObject: UIDField];
[searchAttributes addObjectsFromArray: mailFields];
+ [searchAttributes addObjectsFromArray: [self _contraintsFields]];
[searchAttributes addObjectsFromArray: commonSearchFields];
}
emailFields = [mailFields objectEnumerator];
while ((currentFieldName = [emailFields nextObject]))
{
- value = [[ldapEntry attributeWithName: currentFieldName] stringValueAtIndex: 0];
+ value = [[ldapEntry attributeWithName: currentFieldName]
+ stringValueAtIndex: 0];
if (value)
[emails addObject: value];
}
[contactEntry setObject: emails forKey: @"c_emails"];
}
+- (void) _fillConstraints: (NGLdapEntry *) ldapEntry
+ forModule: (NSString *) module
+ intoContactEntry: (NSMutableDictionary *) contactEntry
+{
+ NSDictionary *constraints;
+ NSEnumerator *matches;
+ NSString *currentMatch, *currentValue, *ldapValue;
+ BOOL result;
+
+ result = YES;
+
+ constraints = [modulesConstraints objectForKey: module];
+ if (constraints)
+ {
+ matches = [[constraints allKeys] objectEnumerator];
+ currentMatch = [matches nextObject];
+ while (result && currentMatch)
+ {
+ ldapValue = [[ldapEntry attributeWithName: currentMatch]
+ stringValueAtIndex: 0];
+ currentValue = [constraints objectForKey: currentMatch];
+ if ([ldapValue isEqualToString: currentValue])
+ currentMatch = [matches nextObject];
+ else
+ result = NO;
+ }
+ }
+
+ [contactEntry setObject: [NSNumber numberWithBool: result]
+ forKey: [NSString stringWithFormat: @"%@Access", module]];
+}
+
- (NSDictionary *) _convertLDAPEntryToContact: (NGLdapEntry *) ldapEntry
{
NSMutableDictionary *contactEntry;
value = @"";
[contactEntry setObject: value forKey: @"c_cn"];
[self _fillEmailsOfEntry: ldapEntry intoContactEntry: contactEntry];
+ [self _fillConstraints: ldapEntry forModule: @"Calendar"
+ intoContactEntry: (NSMutableDictionary *) contactEntry];
+ [self _fillConstraints: ldapEntry forModule: @"Mail"
+ intoContactEntry: (NSMutableDictionary *) contactEntry];
return contactEntry;
}
LDAPSource *currentSource;
NSString *cn, *c_uid;
NSArray *c_emails;
-
+ BOOL access;
+
emails = [NSMutableArray array];
cn = nil;
c_uid = nil;
+ [currentUser setObject: [NSNumber numberWithBool: YES]
+ forKey: @"CalendarAccess"];
+ [currentUser setObject: [NSNumber numberWithBool: YES]
+ forKey: @"MailAccess"];
+
ldapSources = [sources objectEnumerator];
currentSource = [ldapSources nextObject];
while (currentSource)
c_emails = [userEntry objectForKey: @"c_emails"];
if ([c_emails count])
[emails addObjectsFromArray: c_emails];
+ access = [[userEntry objectForKey: @"CalendarAccess"] boolValue];
+ if (!access)
+ [currentUser setObject: [NSNumber numberWithBool: NO]
+ forKey: @"CalendarAccess"];
+ access = [[userEntry objectForKey: @"MailAccess"] boolValue];
+ if (!access)
+ [currentUser setObject: [NSNumber numberWithBool: NO]
+ forKey: @"MailAccess"];
}
currentSource = [ldapSources nextObject];
}
- (BOOL) isSuperUser;
+/* module access */
+- (BOOL) canAccessModule: (NSString *) module;
+
/* folders */
- (SOGoUserFolder *) homeFolderInContext: (id) context;
return [superUsernames containsObject: login];
}
+/* module access */
+- (BOOL) canAccessModule: (NSString *) module
+{
+ NSString *accessValue;
+
+ accessValue = [self _fetchFieldForUser:
+ [NSString stringWithFormat: @"%@Access", module]];
+
+ return [accessValue boolValue];
+}
+
@end /* SOGoUser */
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoClassSecurityInfo.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+
#import <NGExtensions/NSObject+Logs.h>
#import <Appointments/SOGoAppointmentFolders.h>
- (NSArray *) toManyRelationshipKeys
{
- static NSArray *children = nil;
+ NSMutableArray *children;
+ SOGoUser *currentUser;
+
+ children = [NSMutableArray arrayWithCapacity: 4];
- if (!children)
- children = [[NSArray alloc] initWithObjects:
- @"Calendar", @"Contacts", @"Mail",
- @"Preferences", nil];
+ currentUser = [context activeUser];
+ if ([currentUser canAccessModule: @"Calendar"])
+ [children addObject: @"Calendar"];
+ [children addObject: @"Contacts"];
+ if ([currentUser canAccessModule: @"Mail"])
+ [children addObject: @"Mail"];
+ [children addObject: @"Preferences"];
return children;
}
acquire: (BOOL) _flag
{
id obj;
+ SOGoUser *currentUser;
/* first check attributes directly bound to the application */
obj = [super lookupName: _key inContext: _ctx acquire: NO];
if (!obj)
{
- if ([_key isEqualToString: @"Calendar"])
+ currentUser = [_ctx activeUser];
+ if ([_key isEqualToString: @"Calendar"]
+ && [currentUser canAccessModule: _key])
obj = [self privateCalendars: @"Calendar" inContext: _ctx];
// if (![_key isEqualToString: @"Calendar"])
// obj = [obj lookupName: [_key pathExtension]
obj = [self privateContacts: _key inContext: _ctx];
// else if ([_key isEqualToString: @"Groups"])
// obj = [self groupsFolder: _key inContext: _ctx];
- else if ([_key isEqualToString: @"Mail"])
+ else if ([_key isEqualToString: @"Mail"]
+ && [currentUser canAccessModule: _key])
obj = [self mailAccountsFolder: _key inContext: _ctx];
else if ([_key isEqualToString: @"Preferences"])
obj = [$(@"SOGoPreferencesFolder") objectWithName: _key
&& [user isSuperUser]);
}
+- (BOOL) userHasCalendarAccess
+{
+ SOGoUser *user;
+
+ user = [context activeUser];
+
+ return [user canAccessModule: @"Calendar"];
+}
+
+- (BOOL) userHasMailAccess
+{
+ SOGoUser *user;
+
+ user = [context activeUser];
+
+ return [user canAccessModule: @"Mail"];
+}
+
/* browser/os identification */
- (BOOL) isCompatibleBrowser
- (WOResponse *) responseWith204;
- (WOResponse *) redirectToLocation: (NSString *) newLocation;
+- (NSString *) labelForKey: (NSString *) _str;
+
@end
#endif /* WODIRECTACTION_SOGO_H */
* Boston, MA 02111-1307, USA.
*/
+#import <Foundation/NSBundle.h>
-#import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WOResponse.h>
+#import <SoObjects/SOGo/NSDictionary+Utilities.h>
+#import <SoObjects/SOGo/SOGoUser.h>
+
#import "WODirectAction+SOGo.h"
@implementation WODirectAction (SOGoExtension)
return response;
}
+- (NSString *) labelForKey: (NSString *) key
+{
+ NSString *userLanguage, *label;
+ NSArray *paths;
+ NSBundle *bundle;
+ NSDictionary *strings;
+
+ bundle = [NSBundle bundleForClass: [self class]];
+ if (!bundle)
+ bundle = [NSBundle mainBundle];
+ userLanguage = [[context activeUser] language];
+ paths = [bundle pathsForResourcesOfType: @"strings"
+ inDirectory: [NSString stringWithFormat: @"%@.lproj",
+ userLanguage]
+ forLocalization: userLanguage];
+ if ([paths count] > 0)
+ {
+ strings = [NSDictionary
+ dictionaryFromStringsFile: [paths objectAtIndex: 0]];
+ label = [strings objectForKey: key];
+ if (!label)
+ label = key;
+ }
+ else
+ label = key;
+
+ return label;
+}
@end
if ((count = [_types count]) == 0)
return NSNotFound;
+ if ((i = [_types indexOfObject:@"text/calendar"]) != NSNotFound)
+ return i;
if ((i = [_types indexOfObject:@"text/html"]) != NSNotFound)
return i;
if ((i = [_types indexOfObject:@"text/plain"]) != NSNotFound)
[item release]; item = nil;
}
-/* raw content handling */
-
-- (NSStringEncoding) fallbackStringEncoding
-{
- /*
- iCalendar invitations sent by Outlook 2002 have the annoying bug that the
- mail states an UTF-8 content encoding but the actual iCalendar content is
- encoding in Latin-1 (or Windows Western?).
-
- As a result the content decoding will fail (TODO: always?). In this case we
- try to decode with Latin-1.
-
- Note: we could check for the Outlook x-mailer, but it was considered better
- to try Latin-1 as a fallback in any case (be tolerant).
- */
- return NSISOLatin1StringEncoding;
-}
-
/* accessors */
- (iCalCalendar *) inCalendar
return content;
}
-- (NSStringEncoding) fallbackStringEncoding
-{
- return 0;
-}
-
- (NSString *) flatContentAsString
{
/* Note: we even have the line count in the body-info! */
s = [NSString stringWithData: content usingEncodingNamed: charset];
if (![s length])
{
- /* latin 1 is used as a 8bit fallback charset... but does this
- encoding accept any byte from 0 to 255? */
+ /* UTF-8 is used as a 8bit fallback charset... */
s = [[NSString alloc] initWithData: content
- encoding: NSISOLatin1StringEncoding];
+ encoding: NSUTF8StringEncoding];
[s autorelease];
}
if (!s)
{
- /*
- Note: this can happend with iCalendar invitations sent by Outlook 2002.
- It will mark the content as UTF-8 but actually deliver it as
- Latin-1 (or Windows encoding?).
+ /*
+ iCalendar invitations sent by Outlook 2002 have the annoying bug that the
+ mail states an UTF-8 content encoding but the actual iCalendar content is
+ encoding in Latin-1 (or Windows Western?).
+
+ As a result the content decoding will fail (TODO: always?). In this case we
+ try to decode with Latin-1.
+
+ Note: we could check for the Outlook x-mailer, but it was considered better
+ to try Latin-1 as a fallback in any case (be tolerant).
*/
- [self errorWithFormat:@"could not convert content to text, charset: '%@'",
- charset];
- if ([self fallbackStringEncoding] > 0)
- {
- s = [[NSString alloc] initWithData:content
- encoding: [self fallbackStringEncoding]];
- if (s)
- [s autorelease];
- else
- [self errorWithFormat:
- @"an attempt to use fallback encoding failed to."];
- }
+
+ s = [[NSString alloc] initWithData:content
+ encoding: NSISOLatin1StringEncoding];
+ if (!s)
+ [self errorWithFormat: @"an attempt to use"
+ @" NSISOLatin1StringEncoding as callback failed"];
+ [s autorelease];
}
}
else
"Compose Mail To" = "Compose Mail To";
"Create Filter From Message..." = "Create Filter From Message...";
+/* Image Popup menu */
+"View Image" = "View Image";
+
/* Mailbox popup menus */
"Open in New Mail Window" = "Open in New Mail Window";
"Copy Folder Location" = "Copy Folder Location";
"Compose Mail To" = "Écrire à ";
"Create Filter From Message..." = "Créer un filtre à partir du message...";
+/* Image Popup menu */
+"View Image" = "Voir l'image";
+
/* Mailbox popup menus */
"Open in New Mail Window" = "Ouvrir dans une nouvelle fenétre";
"Copy Folder Location" = "Copier l'adresse du dossier";
"Compose Mail To" = "Verfassen";
"Create Filter From Message..." = "Filter aus Nachricht erstellen...";
+/* Image Popup menu */
+"View Image" = "View Image";
+
/* Mailbox popup menus */
"Open in New Mail Window" = "In neuem Fenster öffnen";
"Copy Folder Location" = "Ordner-Adresse kopieren";
if ([signature length])
{
[newDraftMessage
- setText: [NSString stringWithFormat: @"\r\n--\r\n%@", signature]];
+ setText: [NSString stringWithFormat: @"\n-- \n%@", signature]];
save = YES;
}
if (save)
{
result = [[self clientObject] sendMail];
if (!result)
- result = [self jsCloseWithRefreshMethod: @"refreshFolderByType(\"sent\")"];
+ result = [self jsCloseWithRefreshMethod: @"refreshCurrentFolder()"];
}
else
result = [self failedToSaveFormResponse];
- (NSString *) itemIdentityText
{
- return [item keysWithFormat: @"%{fullName} <%{email}>"];
+ return [(NSDictionary *) item keysWithFormat: @"%{fullName} <%{email}>"];
}
- (NSMutableDictionary *) defaultIdentity
return [[request method] isEqualToString: @"POST"];
}
+- (BOOL) userHasCalendarAccess
+{
+ return [user canAccessModule: @"Calendar"];
+}
+
+- (BOOL) userHasMailAccess
+{
+ return [user canAccessModule: @"Mail"];
+}
+
- (BOOL) shouldDisplayPasswordChange
{
return shouldDisplayPasswordChange;
if ([lTable hasPrefix:@"$"])
lTable = [self valueForKeyPath:[lTable substringFromIndex:1]];
-#if 0
- if ([lVal hasPrefix:@"$"])
- lVal = [self valueForKeyPath:[lVal substringFromIndex:1]];
-
-#endif
-
/* lookup string */
return [rm stringForKey: lKey
inTableNamed: lTable
"Location" = "Location";
"(Private Event)" = "(Private Event)";
+vevent_class0 = "(Public event)";
+vevent_class1 = "(Private event)";
+vevent_class2 = "(Confidential event)";
+
+vtodo_class0 = "(Public task)";
+vtodo_class1 = "(Private task)";
+vtodo_class2 = "(Confidential task)";
+
"closeThisWindowMessage" = "Thank you! You may now close this window or view your ";
"Multicolumn Day View" = "Multicolumn Day View";
"Location" = "Lieu";
"(Private Event)" = "(Événement privé)";
+vevent_class0 = "(Événement public)";
+vevent_class1 = "(Événement privé)";
+vevent_class2 = "(Événement confidentiel)";
+
+vtodo_class0 = "(Tâche publique)";
+vtodo_class1 = "(Tâche privée)";
+vtodo_class2 = "(Tâche confidentielle)";
+
"closeThisWindowMessage" = "Merci! Vous pouvez maintenant fermer cette fenêtre ou consulter votre ";
"Multicolumn Day View" = "Multicolonne";
"Location" = "Ort";
"(Private Event)" = "(Privater Termin)";
+/* event list */
+vevent_class0 = "(Public event)";
+vevent_class1 = "(Private event)";
+vevent_class2 = "(Confidential event)";
+
+vtodo_class0 = "(Public task)";
+vtodo_class1 = "(Private task)";
+vtodo_class2 = "(Confidential task)";
+
"closeThisWindowMessage" = "Vielen Dank! Sie können dieses Fenster jetzt schließen.";
"Multicolumn Day View" = "Multicolonne";
{
if (!event)
{
- event = (iCalEvent *) [[self clientObject] component: NO secure: NO];
+ event = (iCalEvent *) [[self clientObject] component: NO secure: YES];
[event retain];
}
unsigned int minutes;
iCalRecurrenceRule *rule;
- event = (iCalEvent *) [[self clientObject] component: NO secure: YES];
+ [self event];
if (event)
{
startDate = [event startDate];
iCalRecurrenceRule *rule;
clientObject = [self clientObject];
- event = (iCalEvent *) [clientObject component: YES secure: NO];
+ [self event];
[super takeValuesFromRequest: _rq inContext: _ctx];
#ifndef UIXCALLISTINGACTIONVIEW_H
#define UIXCALLISTINGACTIONVIEW_H
-
-#import <NGObjWeb/WODirectAction.h>
+#import <Common/WODirectAction+SOGo.h>
@class NSCalendarDate;
@class NSMutableDictionary;
}
}
-- (void) _updatePrivacyInComponent: (NSMutableDictionary *) component
- fromFolder: (SOGoAppointmentFolder *) folder
-{
- int privacyFlag;
- NSString *roleString;
-
- privacyFlag = [[component objectForKey: @"classification"] intValue];
- roleString = [folder roleForComponentsWithAccessClass: privacyFlag
- forUser: userLogin];
- if ([roleString isEqualToString: @"ComponentDAndTViewer"])
- {
- [component setObject: @"" forKey: @"c_title"];
- [component setObject: @"" forKey: @"c_location"];
- }
-}
-
- (SOGoAppointmentFolder *) _aptFolder: (NSString *) folder
withClientObject: (SOGoAppointmentFolder *) clientObject
{
return aptFolder;
}
+- (void) _fixComponentTitle: (NSMutableDictionary *) component
+ withType: (NSString *) type
+{
+ NSString *labelKey;
+
+ labelKey = [NSString stringWithFormat: @"%@_class%@",
+ type, [component objectForKey: @"c_classification"]];
+ [component setObject: [self labelForKey: labelKey]
+ forKey: @"c_title"];
+}
+
- (NSArray *) _fetchFields: (NSArray *) fields
forComponentOfType: (NSString *) component
{
marker = [NSNull null];
- clientObject = [self clientObject];
+ clientObject = [self clientObject];
folders = [[clientObject subFolders] objectEnumerator];
currentFolder = [folders nextObject];
infos = [NSMutableArray array];
+
while (currentFolder)
{
if ([currentFolder isActive])
{
- currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
- to: endDate
- title: title
- component: component] objectEnumerator];
+ currentInfos
+ = [[currentFolder fetchCoreInfosFrom: startDate
+ to: endDate
+ title: title
+ component: component] objectEnumerator];
while ((newInfo = [currentInfos nextObject]))
{
- [self _updatePrivacyInComponent: newInfo
- fromFolder: currentFolder];
[newInfo setObject: [currentFolder nameInContainer]
forKey: @"c_folder"];
-
+ if (![[newInfo objectForKey: @"c_title"] length])
+ [self _fixComponentTitle: newInfo withType: component];
[infos addObject: [newInfo objectsForKeys: fields
notFoundMarker: marker]];
}
newEvents = [NSMutableArray array];
fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status",
@"c_title", @"c_startdate", @"c_enddate", @"c_location",
- @"c_isallday", nil];
+ @"c_isallday", @"c_classification", nil];
events = [[self _fetchFields: fields
forComponentOfType: @"vevent"] objectEnumerator];
oldEvent = [events nextObject];
[self _setupContext];
fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status",
- @"c_title", @"c_enddate", nil];
+ @"c_title", @"c_enddate", @"c_classification", nil];
tasks = [[self _fetchFields: fields
forComponentOfType: @"vtodo"] objectEnumerator];
{
if (!todo)
{
- todo = (iCalToDo *) [[self clientObject] component: NO secure: NO];
+ todo = (iCalToDo *) [[self clientObject] component: NO secure: YES];
[todo retain];
}
NSString *duration;
unsigned int minutes;
- todo = (iCalToDo *) [[self clientObject] component: NO secure: YES];
+ [self todo];
if (todo)
{
startDate = [todo startDate];
- (void) takeValuesFromRequest: (WORequest *) _rq
inContext: (WOContext *) _ctx
{
- SOGoTaskObject *clientObject;
-
- clientObject = [self clientObject];
- todo = (iCalToDo *) [clientObject component: YES secure: NO];
+ [self todo];
[super takeValuesFromRequest: _rq inContext: _ctx];
- (id) changeStatusAction
{
- SOGoTaskObject *clientObject;
NSString *newStatus;
- clientObject = [self clientObject];
- todo = (iCalToDo *) [clientObject component: NO secure: NO];
- [todo retain];
+ [self todo];
if (todo)
{
newStatus = [self queryParameterForKey: @"status"];
[todo setPercentComplete: @"0"];
[todo setStatus: @"IN-PROCESS"];
}
- [clientObject saveComponent: todo];
+ [[self clientObject] saveComponent: todo];
}
return [self responseWith204];
</ul>
</div>
+ <div class="menu" id="messagesListMenu">
+ <ul>
+ <li><var:string label:value="Forward"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Move To"/></li>
+ <li><var:string label:value="Copy To"/></li>
+ <li><var:string label:value="Label"/></li>
+ <li><var:string label:value="Mark"/></li>
+ <li><!-- separator --></li>
+ <li><var:string label:value="Print Preview"/></li>
+ <li><var:string label:value="Print..."/></li>
+ <li><var:string label:value="Delete Selected Messages"/></li>
+ </ul>
+ </div>
+
<div class="menu" id="messageContentMenu">
<ul>
<li><var:string label:value="Reply to Sender Only"/></li>
</ul>
</div>
+ <div class="menu" id="imageMenu">
+ <ul>
+ <li id="view_image"><var:string label:value="View Image"/></li>
+ </ul>
+ </div>
+
<div id="leftPanel">
<div class="titlediv"><var:string label:value="Folders" /></div>
<div id="folderTreeContent"><!-- space --></div>
<ul>
<li target="generalView"><var:string
label:value="General"/></li>
- <li target="calendarOptionsView"><var:string
- label:value="Calendar Options"/></li>
- <li target="mailOptionsView"><var:string
- label:value="Mail Options"/></li>
- <li target="identitiesView"><var:string
- label:value="Identities"/></li>
+ <var:if condition="userHasCalendarAccess">
+ <li target="calendarOptionsView"><var:string
+ label:value="Calendar Options"/></li>
+ </var:if>
+ <var:if condition="userHasMailAccess">
+ <li target="mailOptionsView"><var:string
+ label:value="Mail Options"/></li>
+ <li target="identitiesView"><var:string
+ label:value="Identities"/></li>
+ </var:if>
<var:if condition="shouldDisplayPasswordChange">
<li target="passwordView"><var:string label:value="Password"/></li>
</var:if>
/></label>
</div>
</div>
- <div id="calendarOptionsView" class="tab">
- <label><var:string label:value="Week begins on :"/>
- <var:popup list="daysList" item="item"
- string="itemWeekStartDay" selection="userWeekStartDay"
- /></label><br/>
- <label><var:string label:value="Day start time :"/>
- <var:popup list="hoursList" item="item"
- string="item" selection="userDayStartTime"
- /></label>
- <label><var:string label:value="Day end time :"/>
- <var:popup list="hoursList" item="item"
- string="item" selection="userDayEndTime"
- /></label><br/>
- <label><var:string label:value="First week of year :"/>
- <var:popup list="firstWeekList" item="item"
- string="itemFirstWeekText" selection="userFirstWeek"
- /></label><br/>
- <br/>
- <label><input class="checkBox"
- type="checkbox" var:selection="reminderEnabled"
- var:checked="reminderEnabled"/><var:string
- label:value="Enable reminders for Calendar items"/></label><br/>
- <label><input class="checkBox"
- type="checkbox" var:selection="remindWithASound"
- var:checked="remindWithASound"/><var:string
- label:value="Play a sound when a reminder comes due"/></label><br/>
- <label><var:string label:value="Default reminder :"/>
- <var:popup list="reminderTimesList" item="item"
- string="itemReminderTimeText" selection="userReminderTime"/></label>
- </div>
- <div id="mailOptionsView" class="tab">
- <label><var:string label:value="Check for new mail:"/>
- <var:popup list="messageCheckList" item="item"
- string="itemMessageCheckText" selection="userMessageCheck"/></label><br/>
- <label><var:string label:value="Forward messages:"/>
- <var:popup list="messageForwardingList" item="item"
- string="itemMessageForwardingText" selection="userMessageForwarding"/></label><br/>
+ <var:if condition="userHasCalendarAccess">
+ <div id="calendarOptionsView" class="tab">
+ <label><var:string label:value="Week begins on :"/>
+ <var:popup list="daysList" item="item"
+ string="itemWeekStartDay" selection="userWeekStartDay"
+ /></label><br/>
+ <label><var:string label:value="Day start time :"/>
+ <var:popup list="hoursList" item="item"
+ string="item" selection="userDayStartTime"
+ /></label>
+ <label><var:string label:value="Day end time :"/>
+ <var:popup list="hoursList" item="item"
+ string="item" selection="userDayEndTime"
+ /></label><br/>
+ <label><var:string label:value="First week of year :"/>
+ <var:popup list="firstWeekList" item="item"
+ string="itemFirstWeekText" selection="userFirstWeek"
+ /></label><br/>
+ <br/>
+ <label><input class="checkBox"
+ type="checkbox" var:selection="reminderEnabled"
+ var:checked="reminderEnabled"/><var:string
+ label:value="Enable reminders for Calendar items"/></label><br/>
+ <label><input class="checkBox"
+ type="checkbox" var:selection="remindWithASound"
+ var:checked="remindWithASound"/><var:string
+ label:value="Play a sound when a reminder comes due"/></label><br/>
+ <label><var:string label:value="Default reminder :"/>
+ <var:popup list="reminderTimesList" item="item"
+ string="itemReminderTimeText" selection="userReminderTime"/></label>
+ </div>
+ </var:if>
+ <var:if condition="userHasMailAccess">
+ <div id="mailOptionsView" class="tab">
+ <label><var:string label:value="Check for new mail:"/>
+ <var:popup list="messageCheckList" item="item"
+ string="itemMessageCheckText" selection="userMessageCheck"/></label><br/>
+ <label><var:string label:value="Forward messages:"/>
+ <var:popup list="messageForwardingList" item="item"
+ string="itemMessageForwardingText" selection="userMessageForwarding"/></label><br/>
<!-- <label><input
const:name="inTheOffice" type="radio" const:value="YES"
var:selection="inTheOffice"/>
<label><var:string label:value="AutoReply only once to each sender with the following text :"/><br/>
<textarea const:name="autoReplyText" var:value="autoReplyText"/>
</label> -->
- </div>
- <div id="identitiesView" class="tab">
- <!--<var:multiselection const:id="identitiesList" item="item"
+ </div>
+ <div id="identitiesView" class="tab">
+ <!--<var:multiselection const:id="identitiesList" item="item"
list="identitiesList" displayString="itemIdentityText">
</var:multiselection>
- <br/>-->
- <var:string label:value="Signature:"/><br/>
- <textarea const:id="signature" const:name="signature"
- var:value="signature"/>
- </div>
+ <br/>-->
+ <var:string label:value="Signature:"/><br/>
+ <textarea const:id="signature" const:name="signature"
+ var:value="signature"/>
+ </div>
+ </var:if>
<var:if condition="shouldDisplayPasswordChange">
<div id="passwordView" class="tab">
<label><var:string label:value="New password:"
>
<var:if condition="shortUserNameForDisplay" const:value="anonymous"
const:negate="YES"
- ><var:if condition="shortUserNameForDisplay" const:value="wrongusernamepassword"
+ ><var:if condition="shortUserNameForDisplay"
+ const:value="wrongusernamepassword"
const:negate="YES"
><var:if condition="isPopup" const:negate="YES"
><var:if condition="context.isUIxDebugEnabled"
<div id="linkBanner" class="linkbanner">
<a id="logoff" var:href="logoffPath"
><var:string label:value="Disconnect" /></a>
- <a var:href="relativeCalendarPath"
- ><var:string label:value="Calendar" /></a> |
- <a var:href="relativeContactsPath"
+ <var:if condition="userHasCalendarAccess">
+ <a id="calendarBannerLink"
+ var:href="relativeCalendarPath"
+ ><var:string label:value="Calendar" /></a> |
+ </var:if>
+ <a id="contactsBannerLink"
+ var:href="relativeContactsPath"
><var:string label:value="Address Book" /></a> |
- <a var:href="relativeMailPath"
- ><var:string label:value="Mail" /></a> |
- <a var:href="relativePreferencesPath"
+ <var:if condition="userHasMailAccess">
+ <a id="mailBannerLink" var:href="relativeMailPath"
+ ><var:string label:value="Mail" /></a> |
+ </var:if>
+ <a id="preferencesBannerLink"
+ var:href="relativePreferencesPath"
><var:string label:value="Preferences" /></a>
<var:if condition="context.isUIxDebugEnabled"
- >| <a href="#"><var:string
+ >| <a id="consoleBannerLink"
+ href="#"><var:string
label:value="Log Console (dev.)" /></a
></var:if>
</div>
<var:foreach list="additionalJSFiles" item="item"
><script type="text/javascript" var:src="item"><!-- space --></script>
</var:foreach>
- <script type="text/javascript">addEvent(window, 'load', onFinalLoadHandler);</script>
+ <script type="text/javascript">FastInit.addOnLoad(onFinalLoadHandler);</script>
</var:if>
<var:if condition="isCompatibleBrowser" const:negate="YES">
<div id="loginScreen">
}
var Mailer = {
+ currentMailbox: null,
+ currentMailboxType: "",
currentMessages: {},
maxCachedMessages: 20,
- cachedMessages: new Array(),
- currentMailbox: null,
- currentMailboxType: ""
+ cachedMessages: new Array()
};
var usersRightsWindowHeight = 320;
if (isHttpStatus204(http.status)) {
var data = http.callbackData;
deleteCachedMessage(data["messageId"]);
+ deleteMessageRequestCount--;
if (Mailer.currentMailbox == data["mailbox"]) {
var div = $('messageContent');
}
var row = $("row_" + data["id"]);
+ var nextRow = row.next("tr");
+ if (!nextRow)
+ nextRow = row.previous("tr");
row.parentNode.removeChild(row);
// row.addClassName("deleted"); // when we'll offer "mark as deleted"
-
- deleteMessageRequestCount--;
- if (deleteMessageRequestCount == 0)
+ if (deleteMessageRequestCount == 0) {
+ if (nextRow)
+ Mailer.currentMessages[Mailer.currentMailbox] = nextRow.getAttribute("id").substr(4);
openMailbox(data["mailbox"], true);
+ }
}
}
}
function _onMailboxMenuAction(menuEntry, error, actionName) {
var targetMailbox = menuEntry.mailbox.fullName();
+ var messages = new Array();
if (targetMailbox == Mailer.currentMailbox)
window.alert(labels[error]);
else {
- var message;
if (document.menuTarget.tagName == "DIV")
- message = Mailer.currentMessages[Mailer.currentMailbox];
+ // Menu called from message content view
+ messages.push(Mailer.currentMessages[Mailer.currentMailbox]);
+ else if (Object.isArray(document.menuTarget))
+ // Menu called from multiple selection in messages list view
+ messages = $(document.menuTarget).collect(function(row) {
+ return row.getAttribute("id").substr(4);
+ });
else
- message = document.menuTarget.getAttribute("id").substr(4);
-
- var urlstr = (URLForFolderID(Mailer.currentMailbox) + "/" + message
- + "/" + actionName + "?folder=" + targetMailbox);
- triggerAjaxRequest(urlstr, folderRefreshCallback, Mailer.currentMailbox);
+ // Menu called from one selection in messages list view
+ messages.push(document.menuTarget.getAttribute("id").substr(4));
+
+ var url_prefix = URLForFolderID(Mailer.currentMailbox) + "/";
+ messages.each(function(msgid, i) {
+ var url = url_prefix + msgid + "/" + actionName
+ + "?folder=" + targetMailbox;
+ triggerAjaxRequest(url, folderRefreshCallback,
+ ((i == messages.size() - 1)?Mailer.currentMailbox:""));
+ });
}
}
if (http.readyState == 4
&& http.status == 200) {
- document.messageListAjaxRequest = null;
+ document.messageListAjaxRequest = null;
if (table) {
// Update table
function onMessageContextMenu(event) {
var menu = $('messageListMenu');
- Event.observe(menu, "hideMenu", onMessageContextMenuHide);
- popupMenu(event, "messageListMenu", this);
-
var topNode = $('messageList');
var selectedNodes = topNode.getSelectedRows();
- for (var i = 0; i < selectedNodes.length; i++)
- selectedNodes[i].deselect();
- topNode.menuSelectedRows = selectedNodes;
- topNode.menuSelectedEntry = this;
- this.select();
+
+ Event.observe(menu, "hideMenu", onMessageContextMenuHide);
+
+ if (selectedNodes.length > 1)
+ popupMenu(event, "messagesListMenu", selectedNodes);
+ else
+ popupMenu(event, "messageListMenu", this);
}
function onMessageContextMenuHide(event) {
messageDiv)[0];
if (!document.body.hasClassName("popup"))
mailContentDiv.observe("contextmenu", onMessageContentMenu);
+
var anchors = messageDiv.getElementsByTagName('a');
for (var i = 0; i < anchors.length; i++)
if (anchors[i].href.substring(0,7) == "mailto:") {
else
$(anchors[i]).observe("click", onMessageAnchorClick);
+ var images = messageDiv.getElementsByTagName('img');
+ for (var i = 0; i < images.length; i++)
+ $(images[i]).observe("contextmenu", onImageClick);
+
var editDraftButton = $("editDraftButton");
if (editDraftButton)
Event.observe(editDraftButton, "click",
function onMessageContentMenu(event) {
var element = getTarget(event);
- if (element.tagName == 'A' && element.href.substring(0,7) == "mailto:")
+ if ((element.tagName == 'A' && element.href.substring(0,7) == "mailto:")
+ || element.tagName == 'IMG')
// Don't show the default contextual menu; let the click propagate to
// other observers
return true;
preventDefault(event);
}
+function onImageClick(event) {
+ popupMenu(event, 'imageMenu', this);
+ preventDefault(event);
+ return false;
+}
+
function messageCallback(http) {
var div = $('messageContent');
preventDefault(event);
}
+function viewImage(event) {
+ var img = document.menuTarget;
+ window.open(img.getAttribute("src"),'_blank','resizable=1');
+}
+
/* contacts */
function newContactFromEmail(event) {
var mailto = document.menuTarget.innerHTML;
onFolderMenuClick.bindAsEventListener(nodes[i]));
if (!inboxFound
&& nodes[i].parentNode.getAttribute("datatype") == "inbox") {
+ Mailer.currentMailboxType = "inbox";
openInbox(nodes[i]);
inboxFound = true;
}
menu.appendChild(menuEntry);
var mailbox = accounts[mailAccounts[i]];
var newSubmenuId = generateMenuForMailbox(mailbox,
- key, mailboxActions[key]);
+ key, mailboxActions[key]);
submenuIds.push(newSubmenuId);
}
initMenu(menuDIV, submenuIds);
}
function onMenuLabelNone() {
- var rowId = document.menuTarget.getAttribute("id").substr(4);
- var messageId = Mailer.currentMailbox + "/" + rowId;
- var urlstr = ApplicationBaseURL + messageId + "/removeAllLabels";
- triggerAjaxRequest(urlstr, messageFlagCallback,
- { mailbox: Mailer.currentMailbox, msg: rowId, label: null } );
+ var messages = new Array();
+
+ if (document.menuTarget.tagName == "DIV")
+ // Menu called from message content view
+ messages.push(Mailer.currentMessages[Mailer.currentMailbox]);
+ else if (Object.isArray(document.menuTarget))
+ // Menu called from multiple selection in messages list view
+ $(document.menuTarget).collect(function(row) {
+ messages.push(row.getAttribute("id").substr(4));
+ });
+ else
+ // Menu called from one selection in messages list view
+ messages.push(document.menuTarget.getAttribute("id").substr(4));
+
+ var url = ApplicationBaseURL + Mailer.currentMailbox + "/";
+ messages.each(function(id) {
+ triggerAjaxRequest(url + id + "/removeAllLabels",
+ messageFlagCallback,
+ { mailbox: Mailer.currentMailbox, msg: id, label: null } );
+ });
}
function _onMenuLabelFlagX(flag) {
- var flags = document.menuTarget.getAttribute("labels").split(" ");
-
- var rowId = document.menuTarget.getAttribute("id").substr(4);
- var messageId = Mailer.currentMailbox + "/" + rowId;
+ var messages = new Hash();
+
+ if (document.menuTarget.tagName == "DIV")
+ // Menu called from message content view
+ messages.set(Mailer.currentMessages[Mailer.currentMailbox],
+ $('tr#row_' + Mailer.currentMessages[Mailer.currentMailbox]).getAttribute("labels"));
+ else if (Object.isArray(document.menuTarget))
+ // Menu called from multiple selection in messages list view
+ $(document.menuTarget).collect(function(row) {
+ messages.set(row.getAttribute("id").substr(4),
+ row.getAttribute("labels"));
+ });
+ else
+ // Menu called from one selection in messages list view
+ messages.set(document.menuTarget.getAttribute("id").substr(4),
+ document.menuTarget.getAttribute("labels"));
+
+ var url = ApplicationBaseURL + Mailer.currentMailbox + "/";
+ messages.keys().each(function(id) {
+ var flags = messages.get(id).split(" ");
+ var operation = "add";
+
+ if (flags.indexOf("label" + flag) > -1)
+ operation = "remove";
- var operation = "add";
- if (flags.indexOf("label" + flag) > -1)
- operation = "remove";
- var urlstr = (ApplicationBaseURL + messageId
- + "/" + operation + "Label" + flag);
- triggerAjaxRequest(urlstr, messageFlagCallback,
- { mailbox: Mailer.currentMailbox, msg: rowId,
- label: operation + flag } );
+ triggerAjaxRequest(url + id + "/" + operation + "Label" + flag,
+ messageFlagCallback,
+ { mailbox: Mailer.currentMailbox, msg: id,
+ label: operation + flag } );
+ });
}
function onMenuLabelFlag1() {
"mark-menu", "-", null,
onMenuViewMessageSource, null,
null, onMenuDeleteMessage);
+ menus["messagesListMenu"] = new Array(onMenuForwardMessage,
+ "-", "moveMailboxMenu",
+ "copyMailboxMenu", "label-menu",
+ "mark-menu", "-",
+ null, null,
+ onMenuDeleteMessage);
+ menus["imageMenu"] = new Array(viewImage);
menus["messageContentMenu"] = new Array(onMenuReplyToSender,
onMenuReplyToAll,
onMenuForwardMessage,
td = document.createElement("td");
row.appendChild(td);
Event.observe(td, "mousedown", listRowMouseDownHandler, true);
- td.appendChild(document.createTextNode(data[i][8]));
+ td.appendChild(document.createTextNode(data[i][9]));
td = document.createElement("td");
row.appendChild(td);
Event.observe(td, "mousedown", listRowMouseDownHandler, true);
- td.appendChild(document.createTextNode(data[i][9]));
+ td.appendChild(document.createTextNode(data[i][10]));
td = document.createElement("td");
row.appendChild(td);
var selected = folders.getSelectedNodes()[0];
if (UserLogin == selected.getAttribute("owner")) {
- var node = selected.childNodes[4];
+ var node = selected.childNodes[selected.childNodes.length - 1];
var currentName = node.nodeValue.trim();
var newName = window.prompt(labels["Name of the Calendar"],
currentName);
padding-bottom: 0em; }
SELECT#calendarList
-{ width: 18em; }
+{ width: 17em; }
A#changeUrlButton
{ margin-left: 1em; }
window.shouldPreserve = true;
document.pageform.action = "send";
document.pageform.submit();
-
+
return false;
}
}
if (signatureLength > 0) {
var length = this.getValue().length - signatureLength - 1;
- this.setSelectionRange(length, length);
+ this.selectText(length, length);
}
Event.stopObserving(this, "focus", onTextFocus);
}
list-style-image: none;
margin: 0px;
padding: 0px;
+ padding-top: 1px;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #9e9a92;
.menu LI
{ padding-left: 1em;
padding-right: 1em;
- padding-top: .2em;
- padding-bottom: .2em;
+ padding-top: .15em;
+ padding-bottom: .15em;
margin: 0px;
width: auto;
white-space: nowrap; }
else {
var r = new RegExp("[\.\/-]", "g");
wId = wId.replace(r, "_");
- if (document.body.hasClassName("popup"))
- parentWindow = window.opener;
}
+
+ if (document.body.hasClassName("popup"))
+ parentWindow = window.opener;
var w = parentWindow.open(url, wId,
"width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
}
var initialSelection = $(node.parentNode).getSelectedNodes();
+
+ if (initialSelection.length > 0 && !Event.isLeftClick(event))
+ // Ignore non primary-click (ie right-click)
+ return true;
+
if ((event.shiftKey == 1 || event.ctrlKey == 1)
&& (lastClickedRow >= 0)
&& (acceptMultiSelect(node.parentNode)
var menuTop = (parentNode.offsetTop - 1
+ this.offsetTop);
+
if (window.height()
- < (menuTop + submenuNode.offsetHeight)
- && submenuNode.offsetHeight < window.height())
- menuTop -= submenuNode.offsetHeight - this.offsetHeight - 4;
+ < (menuTop + submenuNode.offsetHeight))
+ if (submenuNode.offsetHeight < window.height())
+ menuTop = window.height() - submenuNode.offsetHeight;
+ else
+ menuTop = 0;
+
var menuLeft = (parentNode.offsetLeft + parentNode.offsetWidth - 3);
if (window.width()
< (menuLeft + submenuNode.offsetWidth))
}
function initMenu(menuDIV, callbacks) {
- var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
+ var lis = $(menuDIV.down("ul")).childNodesWithTag("li");
for (var j = 0; j < lis.length; j++) {
var node = $(lis[j]);
node.observe("mousedown", listRowMouseDownHandler, false);
function configureLinkBanner() {
var linkBanner = $("linkBanner");
if (linkBanner) {
- var anchors = linkBanner.childNodesWithTag("a");
- for (var i = 1; i < 3; i++) {
- $(anchors[i]).observe("mousedown", listRowMouseDownHandler);
- $(anchors[i]).observe("click", onLinkBannerClick);
+ var moduleLinks = [ "calendar", "contacts", "mail" ];
+ for (var i = 0; i < moduleLinks.length; i++) {
+ var link = $(moduleLinks[i] + "BannerLink");
+ if (link) {
+ link.observe("mousedown", listRowMouseDownHandler);
+ link.observe("click", onLinkBannerClick);
+ }
+ }
+ link = $("preferencesBannerLink");
+ if (link) {
+ link.observe("mousedown", listRowMouseDownHandler);
+ link.observe("click", onPreferencesClick);
+ }
+ link = $("consoleBannerLink");
+ if (link) {
+ link.observe("mousedown", listRowMouseDownHandler);
+ link.observe("click", toggleLogConsole);
}
- $(anchors[4]).observe("mousedown", listRowMouseDownHandler);
- $(anchors[4]).observe("click", onPreferencesClick);
- if (anchors.length > 5)
- $(anchors[5]).observe("click", toggleLogConsole);
}
}