See ChangeLog

Monotone-Parent: ef0aa48a6604ad9c6048a42d79be6779460dcd1a
Monotone-Revision: 485b2bbaa9cf22a075daa65673c3fac7256a86e4

Monotone-Author: ludovic@Sophos.ca
Monotone-Date: 2009-02-24T01:54:59
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Ludovic Marcotte 2009-02-24 01:54:59 +00:00
parent 5605ebc668
commit 282be5d22b
20 changed files with 401 additions and 320 deletions

View File

@ -1,3 +1,14 @@
2009-02-23 Ludovic Marcotte <lmarcotte@inverse.ca>
* Updated German translation. Patch from
Alexander Greiner-Baer <sogo-algb@freenet.de>
* Added Italian translation. Patch from
Marco Lertora <marco.lertora@infoporto.it>
* Combined the various caches and moved the logic
towards the SOGoCache class. Deprecated the
SOGoLDAPUserManagerCleanupInterval defaults.
Now only SOGoCacheCleanupInterval should be used.
2009-02-19 Francis Lachapelle <flachapelle@inverse.ca>
* UI/Contacts/UIxContactsListView.m ([UIxContactsListView

View File

@ -1,6 +1,6 @@
/* LDAPUserManager.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
* Copyright (C) 2007-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -34,11 +34,9 @@
@interface LDAPUserManager : NSObject
{
NSMutableDictionary *sources;
NSMutableDictionary *sourcesMetadata;
NSTimeInterval cleanupInterval;
NSTimer *cleanupTimer;
NSMutableDictionary *users;
@private
NSMutableDictionary *sources;
NSMutableDictionary *sourcesMetadata;
}
+ (id) sharedUserManager;

View File

@ -1,6 +1,6 @@
/* LDAPUserManager.m - this file is part of SOGo
*
* Copyright (C) 2007-2008 Inverse inc.
* Copyright (C) 2007-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
@ -22,6 +22,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSDistributedNotificationCenter.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSString.h>
@ -33,6 +34,7 @@
#import "NSArray+Utilities.h"
#import "LDAPSource.h"
#import "LDAPUserManager.h"
#import "SOGoCache.h"
static NSString *defaultMailDomain = nil;
static NSString *LDAPContactInfoAttribute = nil;
@ -147,7 +149,6 @@ static NSLock *lock = nil;
- (id) init
{
NSUserDefaults *ud;
NSString *cleanupSetting;
if ((self = [super init]))
{
@ -155,27 +156,6 @@ static NSLock *lock = nil;
sources = nil;
sourcesMetadata = nil;
users = [NSMutableDictionary new];
cleanupSetting
= [ud objectForKey: @"SOGoLDAPUserManagerCleanupInterval"];
if (cleanupSetting)
cleanupInterval = [cleanupSetting doubleValue];
else
cleanupInterval = 0.0;
if (cleanupInterval > 0.0)
{
cleanupTimer
= [NSTimer scheduledTimerWithTimeInterval: cleanupInterval
target: self
selector: @selector (_cleanupSources)
userInfo: nil
repeats: YES];
[self logWithFormat: @"cleanup interval set every %f seconds",
cleanupInterval];
}
else
[self
logWithFormat: @"no cleanup interval set: memory usage will grow"];
[self _prepareLDAPSourcesWithDefaults: ud];
}
@ -185,7 +165,6 @@ static NSLock *lock = nil;
- (void) dealloc
{
[sources release];
[users release];
[super dealloc];
}
@ -312,16 +291,15 @@ static NSLock *lock = nil;
- (BOOL) checkLogin: (NSString *) login
andPassword: (NSString *) password
{
BOOL checkOK;
NSDate *cleanupDate;
NSMutableDictionary *currentUser;
NSString *dictPassword;
BOOL checkOK;
#if defined(THREADSAFE)
[lock lock];
#endif
currentUser = [users objectForKey: login];
currentUser = [[SOGoCache sharedCache] userAttributesForLogin: login];
dictPassword = [currentUser objectForKey: @"password"];
if (currentUser && dictPassword)
checkOK = ([dictPassword isEqualToString: password]);
@ -331,19 +309,13 @@ static NSLock *lock = nil;
if (!currentUser)
{
currentUser = [NSMutableDictionary dictionary];
[users setObject: currentUser forKey: login];
[[SOGoCache sharedCache] cacheAttributes: currentUser forLogin: login];
}
[currentUser setObject: password forKey: @"password"];
}
else
checkOK = NO;
if (cleanupInterval)
{
cleanupDate = [[NSDate date] addTimeInterval: cleanupInterval];
[currentUser setObject: cleanupDate forKey: @"cleanupDate"];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
@ -422,35 +394,59 @@ static NSLock *lock = nil;
[self _fillContactMailRecords: currentUser];
}
//
// We cache here all identities, including those
// associated with email addresses.
//
- (void) _retainUser: (NSDictionary *) newUser
{
NSString *key;
NSEnumerator *emails;
NSString *key;
#if defined(THREADSAFE)
[lock lock];
#endif
key = [newUser objectForKey: @"c_uid"];
if (key)
[users setObject: newUser forKey: key];
[[SOGoCache sharedCache] cacheAttributes: newUser forLogin: key];
emails = [[newUser objectForKey: @"emails"] objectEnumerator];
while ((key = [emails nextObject]))
[users setObject: newUser forKey: key];
{
[[SOGoCache sharedCache] cacheAttributes: newUser forLogin: key];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
// We propagate the loaded LDAP attributes to other sogod instances
// which will cache them in SOGoCache (excluding for the instance
// that actually posts the notification)
if ([newUser objectForKey: @"c_uid"])
{
NSMutableDictionary *d;
d = [NSMutableDictionary dictionary];
[d setObject: newUser forKey: @"values"];
[d setObject: [newUser objectForKey: @"c_uid"]
forKey: @"uid"];
[[NSDistributedNotificationCenter defaultCenter]
postNotificationName: @"SOGoUserAttributesHaveLoaded"
object: nil
userInfo: d
deliverImmediately: YES];
}
}
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
{
NSMutableDictionary *currentUser, *contactInfos;
NSDate *cleanupDate;
BOOL newUser;
if ([uid length] > 0)
{
contactInfos = [NSMutableDictionary dictionary];
currentUser = [users objectForKey: uid];
currentUser = [[SOGoCache sharedCache] userAttributesForLogin: uid];
#if defined(THREADSAFE)
[lock lock];
#endif
@ -475,11 +471,6 @@ static NSLock *lock = nil;
}
}
if (cleanupInterval && currentUser)
{
cleanupDate = [[NSDate date] addTimeInterval: cleanupInterval];
[currentUser setObject: cleanupDate forKey: @"cleanupDate"];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
@ -583,40 +574,4 @@ static NSLock *lock = nil;
matching: filter];
}
- (void) _cleanupSources
{
NSEnumerator *userIDs;
NSString *currentID;
NSDictionary *currentUser;
NSDate *now;
unsigned int count;
#if defined(THREADSAFE)
[lock lock];
#endif
now = [NSDate date];
count = 0;
userIDs = [[users allKeys] objectEnumerator];
while ((currentID = [userIDs nextObject]))
{
currentUser = [users objectForKey: currentID];
if ([now earlierDate:
[currentUser objectForKey: @"cleanupDate"]] == now)
{
[users removeObjectForKey: currentID];
count++;
}
}
if (count)
[self logWithFormat: @"cleaned %d users records from cache", count];
#if defined(THREADSAFE)
[lock unlock];
#endif
}
@end

View File

@ -1,6 +1,6 @@
/* SOGoCache.h - this file is part of SOGo
*
* Copyright (C) 2008 Inverse inc.
* Copyright (C) 2008-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
@ -54,12 +54,11 @@
- (void) registerUser: (SOGoUser *) user;
- (id) userNamed: (NSString *) name;
+ (void) setCachedUserDefaults: (SOGoUserDefaults *) theDefaults
user: (NSString *) login;
+ (NSDictionary *) cachedUserDefaults;
+ (void) setCachedUserSettings: (SOGoUserDefaults *) theSettings
user: (NSString *) login;
+ (NSDictionary *) cachedUserSettings;
- (void) cacheAttributes: (NSDictionary *) theAttributes
forLogin: (NSString *) theLogin;
- (NSMutableDictionary *) userAttributesForLogin: (NSString *) theLogin;
- (SOGoUserDefaults *) userDefaultsForLogin: (NSString *) theLogin;
- (SOGoUserDefaults *) userSettingsForLogin: (NSString *) theLogin;
@end

View File

@ -1,6 +1,6 @@
/* SOGoCache.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse inc.
* Copyright (C) 2008-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
@ -21,6 +21,24 @@
* Boston, MA 02111-1307, USA.
*/
/*
* [ Structure ]
* users: key = user ID value = NSMutableDictionary instance
* value: key = @"user" value = SOGOUser instance
* key = @"cleanupDate" value = NSDate instance
* key = @"defaults" value = SOGoUserDefaults instance
* key = @"settings" value = SOGoUserDefaults instance
* key = @"attributes" value = NSDictionary instance (attributes from LDAP)
*
* [ Workflows - processes A and B ]
*
* A cache user defaults and posts the notification
* B ....
*
* B crashes
* B receives a notificaion update
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSDistributedNotificationCenter.h>
@ -42,14 +60,11 @@
// We define the default value for cleaning up cached
// users' preferences. This value should be relatively
// high to avoid useless database calls.
static NSTimeInterval cleanupInterval = 1800;
static NSTimeInterval cleanupInterval = 300;
static NSMutableDictionary *cache = nil;
static NSMutableDictionary *users = nil;
static NSMutableDictionary *s_userDefaults = nil;
static NSMutableDictionary *s_userSettings = nil;
static SOGoCache *sharedCache = nil;
#if defined(THREADSAFE)
@ -60,11 +75,9 @@ static NSLock *lock;
+ (void) initialize
{
#if defined(THREADSAFE)
#if defined(THREADSAFE)
lock = [NSLock new];
#endif
s_userDefaults = [[NSMutableDictionary alloc] init];
s_userSettings = [[NSMutableDictionary alloc] init];
}
+ (NSTimeInterval) cleanupInterval
@ -92,7 +105,6 @@ static NSLock *lock;
[lock lock];
#endif
[cache removeAllObjects];
[users removeAllObjects];
#if defined(THREADSAFE)
[lock unlock];
#endif
@ -108,12 +120,30 @@ static NSLock *lock;
users = [[NSMutableDictionary alloc] init];
// We register ourself for notifications
[[NSDistributedNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_userAttributesHaveLoaded:)
name: @"SOGoUserAttributesHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_userDefaultsHaveLoaded:)
name: @"SOGoUserDefaultsHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_userDefaultsHaveChanged:)
name: @"SOGoUserDefaultsHaveChanged"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_userSettingsHaveLoaded:)
name: @"SOGoUserSettingsHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_userSettingsHaveChanged:)
@ -132,7 +162,7 @@ static NSLock *lock;
selector: @selector(_cleanupSources)
userInfo: nil
repeats: YES];
[self logWithFormat: @"cleanup interval set every %f seconds",
[self logWithFormat: @"Cache cleanup interval set every %f seconds",
cleanupInterval];
}
@ -141,10 +171,25 @@ static NSLock *lock;
- (void) dealloc
{
[[NSDistributedNotificationCenter defaultCenter]
removeObserver: self
name: @"SOGoUserAttributesHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
removeObserver: self
name: @"SOGoUserDefaultsHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
removeObserver: self
name: @"SOGoUserDefaultsHaveChanged"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
removeObserver: self
name: @"SOGoUserSettingsHaveLoaded"
object: nil];
[[NSDistributedNotificationCenter defaultCenter]
removeObserver: self
@ -157,7 +202,7 @@ static NSLock *lock;
}
- (NSString *) _pathFromObject: (SOGoObject *) container
withName: (NSString *) name
withName: (NSString *) name
{
NSString *fullPath, *nameInContainer;
NSMutableArray *names;
@ -197,14 +242,11 @@ withName: (NSString *) name
#endif
if (![cache objectForKey: fullPath])
{
// NSLog (@"registering '%@'", fullPath);
[cache setObject: object forKey: fullPath];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
// else
// NSLog (@"'%@' already registered", fullPath);
}
}
@ -223,11 +265,21 @@ withName: (NSString *) name
- (void) registerUser: (SOGoUser *) user
{
NSData *cleanupDate;
#if defined(THREADSAFE)
[lock lock];
#endif
[users setObject: user
forKey: [user login]];
cleanupDate = [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]];
if (![users objectForKey: [user login]])
[users setObject: [NSMutableDictionary dictionary] forKey: [user login]];
[[users objectForKey: [user login]] setObject: user forKey: @"user"];
[[users objectForKey: [user login]] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
#if defined(THREADSAFE)
[lock unlock];
#endif
@ -235,99 +287,184 @@ withName: (NSString *) name
- (id) userNamed: (NSString *) name
{
return [users objectForKey: name];
return [[users objectForKey: name] objectForKey: @"user"];
}
+ (NSDictionary *) cachedUserDefaults
- (void) cacheAttributes: (NSDictionary *) theAttributes
forLogin: (NSString *) theLogin
{
return s_userDefaults;
if (![users objectForKey: theLogin])
[users setObject: [NSMutableDictionary dictionary] forKey: theLogin];
[[users objectForKey: theLogin] setObject: theAttributes forKey: @"attributes"];
[[users objectForKey: theLogin] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
}
/* defaults */
+ (void) setCachedUserDefaults: (SOGoUserDefaults *) theDefaults
user: (NSString *) login
- (NSMutableDictionary *) userAttributesForLogin: (NSString *) theLogin
{
NSDate *cleanupDate;
return [[users objectForKey: theLogin] objectForKey: @"attributes"];
}
- (SOGoUserDefaults *) userDefaultsForLogin: (NSString *) theLogin
{
return [[users objectForKey: theLogin] objectForKey: @"defaults"];
}
- (SOGoUserDefaults *) userSettingsForLogin: (NSString *) theLogin
{
return [[users objectForKey: theLogin] objectForKey: @"settings"];
}
//
// Notification callbacks.
//
- (void) _cacheValues: (NSDictionary *) theValues
login: (NSString *) theLogin
url: (NSString *) theURL
key: (NSString *) theKey
{
SOGoUserDefaults *defaults;
NSURL *url;
#if defined(THREADSAFE)
[lock lock];
#endif
cleanupDate = [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]];
[s_userDefaults setObject: [NSDictionary dictionaryWithObjectsAndKeys:
theDefaults, @"dictionary",
cleanupDate, @"cleanupDate", nil]
forKey: login];
url = [[[NSURL alloc] initWithString: theURL] autorelease];
defaults = [[[SOGoUserDefaults alloc] initWithTableURL: url
uid: theLogin
fieldName: [NSString stringWithFormat: @"c_%@", theKey]]
autorelease];
if (![users objectForKey: theLogin])
[users setObject: [NSMutableDictionary dictionary] forKey: theLogin];
[[users objectForKey: theLogin] setObject: defaults forKey: theKey];
[[users objectForKey: theLogin] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
//NSLog(@"\n\n\nCached user %@ for UID: %@\nvalues: %@\n\n", theKey, theLogin, theValues);
#if defined(THREADSAFE)
[lock unlock];
#endif
}
+ (NSDictionary *) cachedUserSettings
- (void) _userAttributesHaveLoaded: (NSNotification *) theNotification
{
return s_userSettings;
NSString *uid;
uid = [[theNotification userInfo] objectForKey: @"uid"];
//NSLog(@"Caching user attributes for UID: %@", uid);
if (![self userAttributesForLogin: uid])
{
NSEnumerator *emails;
NSDictionary *values;
NSString *key;
if (![users objectForKey: uid])
[users setObject: [NSMutableDictionary dictionary] forKey: uid];
values = [[theNotification userInfo] objectForKey: @"values"];
[self cacheAttributes: values forLogin: uid];
emails = [[values objectForKey: @"emails"] objectEnumerator];
while ((key = [emails nextObject]))
{
[self cacheAttributes: values forLogin: key];
}
[[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
}
}
+ (void) setCachedUserSettings: (SOGoUserDefaults *) theSettings
user: (NSString *) login
- (void) _userDefaultsHaveLoaded: (NSNotification *) theNotification
{
NSDate *cleanupDate;
#if defined(THREADSAFE)
[lock lock];
#endif
cleanupDate = [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]];
[s_userSettings setObject: [NSDictionary dictionaryWithObjectsAndKeys:
theSettings, @"dictionary",
cleanupDate, @"cleanupDate", nil]
forKey: login];
#if defined(THREADSAFE)
[lock unlock];
#endif
NSString *uid;
uid = [[theNotification userInfo] objectForKey: @"uid"];
//NSLog(@"Loading user defaults for UID: %@", uid);
if (![self userDefaultsForLogin: uid])
{
[self _cacheValues: [[theNotification userInfo] objectForKey: @"values"]
login: uid
url: [[theNotification userInfo] objectForKey: @"url"]
key: @"defaults"];
}
}
- (void) _userDefaultsHaveChanged: (NSNotification *) theNotification
{
SOGoUser *user;
NSString *uid;
SOGoUserDefaults *defaults;
NSString *uid;
uid = [[theNotification userInfo] objectForKey: @"uid"];
#if defined(THREADSAFE)
[lock lock];
#endif
if ((user = [users objectForKey: uid]))
//NSLog(@"Updating user defaults for UID: %@", uid);
defaults = (SOGoUserDefaults *)[self userDefaultsForLogin: uid];
if (defaults)
{
defaults = (SOGoUserDefaults *) [user userDefaults];
#if defined(THREADSAFE)
[lock lock];
#endif
[defaults setValues: [[theNotification userInfo] objectForKey: @"values"]];
[SOGoCache setCachedUserDefaults: defaults user: uid];
[[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
#if defined(THREADSAFE)
[lock unlock];
#endif
}
else
{
[s_userDefaults removeObjectForKey: uid];
[self _cacheValues: [[theNotification userInfo] objectForKey: @"values"]
login: uid
url: [[theNotification userInfo] objectForKey: @"url"]
key: @"defaults"];
}
}
- (void) _userSettingsHaveLoaded: (NSNotification *) theNotification
{
NSString *uid;
uid = [[theNotification userInfo] objectForKey: @"uid"];
//NSLog(@"Loading user settings for UID: %@", uid);
if (![self userSettingsForLogin: uid])
{
[self _cacheValues: [[theNotification userInfo] objectForKey: @"values"]
login: uid
url: [[theNotification userInfo] objectForKey: @"url"]
key: @"settings"];
}
#if defined(THREADSAFE)
[lock unlock];
#endif
}
- (void) _userSettingsHaveChanged: (NSNotification *) theNotification
{
SOGoUser *user;
SOGoUserDefaults *settings;
NSString *uid;
SOGoUserDefaults *settings;
uid = [[theNotification userInfo] objectForKey: @"uid"];
if ((user = [users objectForKey: uid]))
//NSLog(@"Updating user settings for UID: %@", uid);
settings = (SOGoUserDefaults *)[self userSettingsForLogin: uid];
if (settings)
{
settings = (SOGoUserDefaults *) [user userSettings];
#if defined(THREADSAFE)
[lock lock];
#endif
[settings setValues: [[theNotification userInfo] objectForKey: @"values"]];
[SOGoCache setCachedUserSettings: settings user: uid];
[[users objectForKey: uid] setObject: [[NSDate date] addTimeInterval: [SOGoCache cleanupInterval]]
forKey: @"cleanupDate"];
#if defined(THREADSAFE)
[lock unlock];
#endif
}
else
{
[s_userSettings removeObjectForKey: uid];
[self _cacheValues: [[theNotification userInfo] objectForKey: @"values"]
login: uid
url: [[theNotification userInfo] objectForKey: @"url"]
key: @"settings"];
}
}
@ -345,40 +482,23 @@ withName: (NSString *) name
#endif
now = [NSDate date];
// We cleanup the user defaults
userIDs = [[s_userDefaults allKeys] objectEnumerator];
count = 0;
while ((currentID = [userIDs nextObject]))
{
currentEntry = [s_userDefaults objectForKey: currentID];
if ([now earlierDate: [currentEntry objectForKey: @"cleanupDate"]] == now)
{
[s_userDefaults removeObjectForKey: currentID];
count++;
}
}
if (count)
[self logWithFormat: @"cleaned %d users records from user defaults cache", count];
// We cleanup the user settings
userIDs = [[s_userSettings allKeys] objectEnumerator];
// We cleanup the user cache
userIDs = [[users allKeys] objectEnumerator];
count = 0;
while ((currentID = [userIDs nextObject]))
{
currentEntry = [s_userSettings objectForKey: currentID];
currentEntry = [users objectForKey: currentID];
if ([now earlierDate: [currentEntry objectForKey: @"cleanupDate"]] == now)
{
[s_userSettings removeObjectForKey: currentID];
[users removeObjectForKey: currentID];
count++;
}
}
if (count)
[self logWithFormat: @"cleaned %d users records from user settings cache",
[self logWithFormat: @"cleaned %d users records from users cache",
count];
#if defined(THREADSAFE)

View File

@ -85,30 +85,13 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (NSString *) currentPassword;
/* properties */
// - (NSString *) fullEmail;
// - (NSString *) primaryEmail;
// - (NSString *) systemEmail;
- (NSArray *) allEmails;
- (BOOL) hasEmail: (NSString *) email;
- (NSString *) cn;
- (NSURL *) freeBusyURL;
- (SOGoDateFormatter *) dateFormatterInContext: (WOContext *) context;
/* shares and identities */
// - (NSString *) primaryIMAP4AccountString;
// - (NSString *) primaryIMAP4AccountString;
// - (NSString *) primaryMailServer;
// - (NSArray *) additionalIMAP4AccountStrings;
// - (NSArray *) additionalEMailAddresses;
// - (NSDictionary *) additionalIMAP4AccountsAndEMails;
/* defaults */
- (NSUserDefaults *) userDefaults;
- (NSUserDefaults *) userSettings;

View File

@ -23,11 +23,13 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSDistributedNotificationCenter.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WORequest.h>
@ -330,20 +332,6 @@ _timeValue (NSString *key)
}
/* properties */
// - (NSString *) fullEmail
// {
// return [[LDAPUserManager sharedUserManager] getFullEmailForUID: login];
// }
// - (NSString *) primaryEmail
// {
// if (!allEmails)
// [self _fetchAllEmails];
// return [allEmails objectAtIndex: 0];
// }
- (NSArray *) allEmails
{
if (!allEmails)
@ -415,26 +403,6 @@ _timeValue (NSString *key)
@" variable(s) mentionned above are configured"];
}
// - (NSString *) primaryMailServer
// {
// return [[self userManager] getServerForUID: [self login]];
// }
// - (NSArray *) additionalIMAP4AccountStrings
// {
// return [[self userManager]getSharedMailboxAccountStringsForUID: [self login]];
// }
// - (NSArray *) additionalEMailAddresses
// {
// return [[self userManager] getSharedMailboxEMailsForUID: [self login]];
// }
// - (NSDictionary *) additionalIMAP4AccountsAndEMails
// {
// return [[self userManager] getSharedMailboxesAndEMailsForUID: [self login]];
// }
- (NSURL *) freeBusyURL
{
return nil;
@ -491,15 +459,16 @@ _timeValue (NSString *key)
- (NSUserDefaults *) userDefaults
{
SOGoUserDefaults *defaults;
NSDictionary *cachedDefaults;
cachedDefaults = [[SOGoCache cachedUserDefaults] objectForKey: login];
if (cachedDefaults)
defaults = [cachedDefaults objectForKey: @"dictionary"];
else
defaults = [[SOGoCache sharedCache] userDefaultsForLogin: login];
if (!defaults)
{
NSMutableDictionary *d;
defaults = [self primaryUserDefaults];
/* Required parameters for the web interface */
// Required parameters for the Web interface
if (![[defaults stringForKey: @"ReplyPlacement"] length])
[defaults setObject: defaultReplyPlacement forKey: @"ReplyPlacement"];
if (![[defaults stringForKey: @"SignaturePlacement"] length])
@ -509,8 +478,22 @@ _timeValue (NSString *key)
if (![[defaults stringForKey: @"MessageCheck"] length])
[defaults setObject: defaultMessageCheck forKey: @"MessageCheck"];
[SOGoCache setCachedUserDefaults: defaults user: login];
// We propagate the loaded user defaults to other sogod instances
// which will cache them in SOGoCache (including for the instance
// that actually posts the notification)
d = [NSMutableDictionary dictionary];
[d setObject: [defaults values] forKey: @"values"];
[d setObject: login forKey: @"uid"];
[d setObject: [SOGoProfileURL absoluteString] forKey: @"url"];
[[NSDistributedNotificationCenter defaultCenter]
postNotificationName: @"SOGoUserDefaultsHaveLoaded"
object: nil
userInfo: d
deliverImmediately: YES];
}
//else
// NSLog(@"User defaults cache hit for %@", login);
return (NSUserDefaults *) defaults;
}
@ -518,16 +501,31 @@ _timeValue (NSString *key)
- (NSUserDefaults *) userSettings
{
SOGoUserDefaults *settings;
NSDictionary *cachedSettings;
cachedSettings = [[SOGoCache cachedUserSettings] objectForKey: login];
if (cachedSettings)
settings = [cachedSettings objectForKey: @"dictionary"];
else
settings = [[SOGoCache sharedCache] userSettingsForLogin: login];
if (!settings)
{
NSMutableDictionary *d;
settings = [self primaryUserSettings];
[SOGoCache setCachedUserSettings: settings user: login];
[settings fetchProfile];
// We propagate the loaded user settings to other sogod instances
// which will cache them in SOGoCache (including for the instance
// that actually posts the notification)
d = [NSMutableDictionary dictionary];
[d setObject: [settings values] forKey: @"values"];
[d setObject: login forKey: @"uid"];
[d setObject: [SOGoProfileURL absoluteString] forKey: @"url"];
[[NSDistributedNotificationCenter defaultCenter]
postNotificationName: @"SOGoUserSettingsHaveLoaded"
object: nil
userInfo: d
deliverImmediately: YES];
}
//else
// NSLog(@"User settings cache hit for %@", login);
return (NSUserDefaults *) settings;
}

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2008-2009 Inverse inc.
This file is part of OpenGroupware.org.
This file is part of SOGo.
OGo is free software; you can redistribute it and/or modify it under
SOGo 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 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo 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 Lesser General Public
License for more details.
@ -46,7 +47,6 @@
{
int modified: 1;
int isNew: 1;
int reserved: 30;
} defFlags;
}
@ -56,6 +56,7 @@
/* value access */
- (void) setValues: (NSDictionary *) theValues;
- (NSDictionary *) values;
- (void) setObject: (id) value
forKey: (NSString *) key;

View File

@ -1,15 +1,15 @@
/*
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2008 Inverse inc.
Copyright (C) 2008-2009 Inverse inc.
This file is part of OpenGroupware.org.
This file is part of SOGo.
OGo is free software; you can redistribute it and/or modify it under
SOGo 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 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
SOGo 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 Lesser General Public
License for more details.
@ -159,7 +159,7 @@ static NSString *uidColumnName = @"c_uid";
else
values = [NSMutableDictionary new];
ASSIGN (lastFetch, [NSCalendarDate date]);
ASSIGN(lastFetch, [NSCalendarDate date]);
defFlags.modified = NO;
rc = YES;
}
@ -292,6 +292,7 @@ static NSString *uidColumnName = @"c_uid";
d = [NSMutableDictionary dictionary];
[d setObject: values forKey: @"values"];
[d setObject: uid forKey: @"uid"];
[d setObject: [url absoluteString] forKey: @"url"];
[[NSDistributedNotificationCenter defaultCenter]
postNotificationName: ([fieldName isEqualToString: @"c_defaults"]
@ -322,6 +323,14 @@ static NSString *uidColumnName = @"c_uid";
{
[values removeAllObjects];
[values addEntriesFromDictionary: theValues];
ASSIGN(lastFetch, [NSCalendarDate date]);
defFlags.modified = NO;
defFlags.isNew = NO;
}
- (NSDictionary *) values
{
return values;
}
- (void) setObject: (id) value

View File

@ -37,6 +37,6 @@
"The user rights cannot be edited for this object!" = "I permessi di questo oggetto non possono essere modificati!";
"You are not allowed to access this module or this system. Please contact your system administrator."
= "You are not allowed to access this module or this system. Please contact your system administrator.";
= "Non sei abilitato ad accedere a questo modulo. Contatta il tuo amministratore di sistema.";
"You don't have the required privileges to perform the operation."
= "You don't have the required privileges to perform the operation.";
= "Non disponi dei privilegi richiesti per eseguire questa operazione.";

View File

@ -67,7 +67,7 @@
"Remove" = "Löschen";
"Please wait..." = "Bitte warten...";
"No possible subscription" = "Kein abonnieren möglich";
"No possible subscription" = "Kein Abonnieren möglich";
"Preferred" = "Bevorzugt";
"Card for %@" = "%@";
@ -114,22 +114,22 @@
"Blind Carbon Copy" = "Blindkopie";
"New Addressbook..." = "Neues Adressbuch...";
"Subscribe to an Addressbook..." = "Abonnieren...";
"Remove the selected Addressbook" = "Adressbuch löschen";
"Subscribe to an Addressbook..." = "Ein Adressbuch abonnieren...";
"Remove the selected Addressbook" = "Gewähltes Adressbuch löschen";
"Name of the Address Book" = "Adressbuch-Name";
"Are you sure you want to delete the selected address book?"
= "Wollen Sie wirklich das ausgewählte Adressbuch löschen?";
= "Wollen Sie wirklich das gewählte Adressbuch löschen?";
"You cannot remove nor unsubscribe from a public addressbook."
= "Sie können das gemeinsame Adressbuch nicht löschen.";
"You cannot remove nor unsubscribe from your personal addressbook."
= "Sie können ihr Persönliches Adressbuch nicht löschen.";
"Are you sure you want to delete the selected contacts?"
= "Wollen Sie wirklich die ausgewählte Karten löschen?";
= "Wollen Sie wirklich die gewählten Karten löschen?";
"You cannot delete the selected contact(s)"
= "Ausgewählte Karten können nicht gelöscht werden.";
= "Gewählte Karten können nicht gelöscht werden.";
"You cannot delete the card of \"%{0}\"."
= "Sie können die Karte von \"%{0}\" nicht löschen.";
@ -140,7 +140,7 @@
"Unable to subscribe to that folder!"
= "Abonnieren des Ordners nicht möglich!";
"Default Roles" = "Standard-Rollen";
"Default Roles" = "Standardrechte";
"User rights for:" = "Benutzerrechte für:";
"This person can add cards to this addressbook."
@ -152,7 +152,7 @@
"This person can read the cards of this addressbook."
= "Diese Person kann Karten dieses Adressbuches anzeigen.";
"This person can erase cards from this addressbook."
= "Diese Person kann Karten aus diesem Adressbuch löschen";
= "Diese Person kann Karten aus diesem Adressbuch löschen.";
"The selected contact has no email address."
= "Der gewählte Kontakt hat keine E-Mail-Adresse.";
@ -163,8 +163,8 @@
"SoAccessDeniedException" = "Sie können nicht auf dieses Adressbuch schreiben.";
"Forbidden" = "Sie können nicht auf dieses Adressbuch schreiben.";
"Invalid Contact" = "Der ausgewählte Kontakt existiert nicht mehr.";
"Unknown Destination Folder" = "Das ausgewählte Ziel-Adressbuch existiert nicht mehr.";
"Invalid Contact" = "Der gewählte Kontakt existiert nicht mehr.";
"Unknown Destination Folder" = "Das gewählte Ziel-Adressbuch existiert nicht mehr.";
"Move To" = "Verschieben in";
"Copy To" = "Kopieren in";

View File

@ -15,7 +15,7 @@
"Addressbook" = "Rubrica";
"Addresses" = "Indirizzi";
"Update" = "Aggiorna";
"Cancel" = "Anulla";
"Cancel" = "Annulla";
"Common" = "Comuni";
"Contact editor" = "Editor dei contatti";
"Contact viewer" = "Visulizzatore dei contatti";
@ -42,7 +42,7 @@
"delete" = "cancella";
"edit" = "modifica";
"invalidemailwarn" = "L'indirizzo email specificato non è valido";
"new" = "new";
"new" = "nuovo";
"Preferred Phone" = "Telefono lavoro";
/* Tooltips */
@ -71,8 +71,8 @@
"Add..." = "Aggiungi...";
"Remove" = "Rimuovi";
"Please wait..." = "Please wait...";
"No possible subscription" = "No possible subscription";
"Please wait..." = "Attendere prego...";
"No possible subscription" = "Nessuna sottoscrizione possibile";
"Preferred" = "Predefinito";
"Card for %@" = "Biglietto da visita di %@";
@ -125,6 +125,9 @@
"Carbon Copy" = "Copia Carbone";
"Blind Carbon Copy" = "Copia Carbone Nascosta";
"Move To" = "Sposta in ";
"Copy To" = "Copia in ";
"New Addressbook..." = "Nuova rubrica...";
"Subscribe to an Addressbook..." = "Sottoscrivi una rubrica...";
"Remove the selected Addressbook" = "Rimuovi la rubrica selezionata";
@ -133,9 +136,9 @@
"Are you sure you want to delete the selected address book?"
= "Sei sicuro di voler cancellare la rubrica selezionata?";
"You cannot remove nor unsubscribe from a public addressbook."
= "You cannot remove nor unsubscribe from a public addressbook.";
= "Non puoi rimuovere una rubrica pubblica.";
"You cannot remove nor unsubscribe from your personal addressbook."
= "You cannot remove nor unsubscribe from your personal addressbook.";
= "Non puoi rimuovere la tua rubrica personale.";
"Are you sure you want to delete the selected contacts?"
= "Sei sicuro di voler eliminare i contatti selezionati?";

View File

@ -1,12 +1,12 @@
ACCEPTED = "akzeptiert";
COMPLETED = "abgeschlossen";
DECLINED = "zurückgewiesen";
DELEGATED = "weitergeleited";
DELEGATED = "weitergeleitet";
IN-PROCESS = "in Bearbeitung";
NEEDS-ACTION = "Reaktion erwarted";
TENTATIVE = "vorläufig";
organized_by_you = "von Ihnen organisiert";
you_are_an_attendee = "sind sind ein Teilnehmer";
you_are_an_attendee = "Sie sind ein Teilnehmer";
add_info_text = "iMIP 'ADD' Anfragen werden von SOGo noch nicht unterstützt.";
publish_info_text = "Der Sender informiert Sie über den angefügten Termin.";
cancel_info_text = "Ihre Einladung zu dem Termin wurde abgesagt.";
@ -16,7 +16,7 @@ Appointment = "Termin";
Organizer = "Organisator";
Time = "Zeit";
Attendees = "Teilnehmer";
request_info = "lädt sie ein an einem Treffen teilzunehmen.";
request_info = "lädt sie ein, an einem Treffen teilzunehmen.";
"Add to calendar" = "Zum Kalender hinzufügen";
"Delete from calendar" = "Aus Kalender entfernen";
"Update status" = "Status aktualisieren";

View File

@ -7,7 +7,7 @@ NEEDS-ACTION = "richiede un'azione";
TENTATIVE = "tentativo";
organized_by_you = "organizzata da te";
you_are_an_attendee = "sei uno degli invitati";
add_info_text = "Le richieste iMIP 'ADD' non sono ancora supportate da SOGo.";
add_info_text = "Le richieste IMIP 'ADD' non sono ancora supportate da SOGo.";
publish_info_text = "Il mittente ti invia in allegato informazioni sull'evento.";
cancel_info_text = "Il tuo invito o l'intero evento sono stati cancellati.";
request_info_no_attendee = "sta proponento un incontro agli invitati. Ricevi questa email come notifica, non sei incluso come partecipante.";

View File

@ -4,7 +4,7 @@
"Create" = "Crea";
"Empty Trash" = "Svuota cestino";
"Delete" = "Cancella";
"Expunge" = "Expunge";
"Expunge" = "Pulisci";
"Forward" = "Inoltra";
"Get Mail" = "Scarica posta";
"Junk" = "Indesiderato";
@ -13,15 +13,15 @@
"Print" = "Stampa";
"Stop" = "Stop";
"Write" = "Scrivi";
"Attachment" = "Attachment";
"Unread" = "Unread";
"Attachment" = "Allegato";
"Unread" = "Non letta";
"Flagged" = "Flagged";
"Send" = "Invia";
"Contacts" = "Contatti";
"Attach" = "Allegato";
"Save" = "Salva";
"Priority" = "Priority";
"Priority" = "Priorità";
/* Tooltips */
@ -74,7 +74,7 @@
"Add subfolders to this folder" = "Aggiungi sottocartelle a questa cartella";
"Remove this folder" = "Rimuovi questa cartella";
"Erase mails from this folder" = "Elimina emails da questa cartella";
"Expunge this folder" = "Expugne per questa cartella";
"Expunge this folder" = "Pulisci questa cartella";
"Modify the acl of this folder" = "Modifica i permessi per questa cartella";
"Update" = "Aggiorna";
@ -104,15 +104,15 @@
"Anais" = "Anais";
"Edit Draft..." = "Modifica bozza...";
"Load Images" = "Load Images";
"Load Images" = "Carica Immagini";
"highest" = "Highest";
"high" = "High";
"normal" = "Normal";
"low" = "Low";
"lowest" = "Lowest";
"highest" = "Molto alta";
"high" = "Alta";
"normal" = "Normale";
"low" = "Bassa";
"lowest" = "Molto bassa";
"This mail is being sent from an unsecure network!" = "Questa email è stata spedita da un network contrassegnato come non sicuro!";
"This mail is being sent from an unsecure network!" = "Questa email è stata spedita da una rete contrassegnata come non sicuro!";
/* Popup "show" */
@ -205,9 +205,9 @@
"View Message Source" = "Visualizza sorgente";
"Print..." = "Stampa...";
"Delete Message" = "Cancella messaggio";
"Delete Selected Messages" = "Delete Selected Messages";
"Delete Selected Messages" = "Cancella i messaggi selezionati";
"This Folder" = "This Folder";
"This Folder" = "Questa cartella";
/* Label popup menu */
"None" = "Nessuno";
@ -230,7 +230,7 @@
/* Folder operations */
"Name :" = "Nome :";
"Enter the new name of your folder :"
= "Inserisci il nuovo nome della tua cartella :";
= "Inserisci il nuovo nome della cartella :";
"Do you really want to move this folder into the trash ?"
= "Sei sicuro di voler spostare la cartella nel cestino ?";
"Operation failed" = "Operazione non riuscita";
@ -243,15 +243,15 @@
"Please select only one message to print." = "Per favore seleziona un solo messaggio da stampare.";
"The folder with name \"%{0}\" could not be created."
= "The folder with name \"%{0}\" could not be created.";
= "La cartella con nome \"%{0}\" non può essere creata.";
"This folder could not be renamed to \"%{0}\"."
= "This folder could not be renamed to \"%{0}\".";
= "Questa cartella non puo essere rinominata in \"%{0}\".";
"The folder could not be deleted."
= "The folder could not be deleted.";
= "Questa cartella non può essere eliminata.";
"The trash could not be emptied."
= "The trash could not be emptied.";
= "Il cestino non puo essere svuotato.";
"The folder functionality could not be changed."
= "The folder functionality could not be changed.";
= "La funzionalita della cartella non puo essere cambiata.";
"You need to choose a non-virtual folder!" = "Devi selezionare una cartella fisica, non virtuale!";

View File

@ -23,12 +23,12 @@
NSShortDateFormatString = "%d/%m/%y";
NSShortMonthNameArray = (Gen, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic);
NSShortTimeDateFormatString = "%d/%m/%y %H:%M";
NSShortWeekDayNameArray = (Lun, Mar, Mer, Gio, Ven, Sab, Dom);
NSShortWeekDayNameArray = (Dom, Lun, Mar, Mer, Gio, Ven, Sab);
NSThisDayDesignations = (oggi, ora);
NSThousandsSeparator = ",";
NSTimeDateFormatString = "%A, %e %B, %Y %H:%M:%S %Z";
NSTimeFormatString = "%H:%M:%S";
NSWeekDayNameArray = (Lunedi, Martedi, Mercoledi, Giovedi, Venerdi, Sabato, Domenica);
NSWeekDayNameArray = (Domenica, Lunedi, Martedi, Mercoledi, Giovedi, Venerdi, Sabato);
NSYearMonthWeekDesignations = (anno, mese, settimana);
NSPositiveCurrencyFormatString = "⬠9,999.00";
NSNegativeCurrencyFormatString = "-⬠9,999.00";

View File

@ -14,8 +14,8 @@
"alternativeBrowserSafari" = "Alternativamente, puoi utilizzare Safari.";
"Download" = "Scarica";
"Language:" = "Language:";
"choose" = "Choose ...";
"Language:" = "Lingua:";
"choose" = "Scegli...";
"Dutch" = "Dutch";
"English" = "Inglese";
"French" = "Francese";

View File

@ -83,14 +83,14 @@
"Default identity:" = "Identità principale:";
"Manage identities..." = "Gestisci identità...";
"Signature" = "Firma";
"replyplacement_above" = "replyplacement_above";
"replyplacement_below" = "replyplacement_below";
"And place my signature" = "And place my signature";
"signatureplacement_above" = "signatureplacement_above";
"signatureplacement_below" = "signatureplacement_below";
"replyplacement_above" = "Inizia la risposta sopra il testo a cui si risponde";
"replyplacement_below" = "Inizia la risposta sotto il testo a cui si risponde";
"And place my signature" = "Metti la firma";
"signatureplacement_above" = "sopra il testo a cui si risponde";
"signatureplacement_below" = "sotto il testo a cui si risponde";
/* Additional Parameters */
"Additional Parameters" = "Additional Parameters";
"Additional Parameters" = "Parametri addizionali";
/* password */
"New password:" = "Nuova password:";

View File

@ -111,7 +111,7 @@
/* acls */
"Default Roles" = "Standard-Rollen";
"Default Roles" = "Standardrechte";
"User rights for:" = "Benutzerrechte für:";
"label_Public" = "Öffentlich";
@ -131,9 +131,9 @@
"None" = "Keine";
"This person can create objects in my calendar."
= "Diese Person kann Termine in meinem Kalender hinzufügen.";
= "Diese Person kann Objekte in meinem Kalender hinzufügen.";
"This person can erase objects from my calendar."
= "Diese Person kann Termine in meinem Kalender löschen.";
= "Diese Person kann Objekte in meinem Kalender löschen.";
/* Button Titles */
@ -397,7 +397,7 @@
/* transparency */
"Show Time as Free" = "Zeige Zeit als Frei";
"Show Time as Free" = "Zeige Zeit als Verfügbar";
/* validation errors */
@ -439,7 +439,7 @@ validate_endbeforestart = "Ihr Beginn ist nach dem Ende";
"taskDeleteConfirmation" = "Wollen Sie diese Aufgabe wirklich dauerhaft löschen?";
"Are you sure you want to delete the calendar \"%{0}\"?"
= "Wollen Sie diesen Kalender wirklich dauerhaft löschen \"%{0}\"?";
= "Wollen Sie diesen Kalender wirklich löschen \"%{0}\"?";
/* Legend */
"Required participant" = "notwendiger Teilnehmer";
@ -458,8 +458,8 @@ validate_endbeforestart = "Ihr Beginn ist nach dem Ende";
/* FreeBusy panel buttons */
"Suggest time slot:" = "Termin vorschlagen:";
"Previous slot" = "Vorherige";
"Next slot" = "Nächste";
"Previous slot" = "Vorheriger";
"Next slot" = "Nächster";
"Previous hour" = "Vorherige Stunde";
"Next hour" = "Nächste Stunde";

View File

@ -24,6 +24,7 @@
"Thursday" = "Giovedi";
"Friday" = "Venerdi";
"Saturday" = "Sabato";
"DayOfTheMonth" = "Giorno del mese";
"Sun" = "Dom";
"Mon" = "Lun";
@ -95,7 +96,7 @@
"Sharing..." = "Condivisione";
"Export Calendar..." = "Esporta calendario...";
"Publish Calendar..." = "Pubblica calendario...";
"Reload Remote Calendars" = "Aggiorna calendari Remoti";
"Reload Remote Calendars" = "Aggiorna calendari remoti";
"Properties" = "Proprietà";
"Compose E-Mail to All Attendees" = "Invia Email a tutti gli invitati";
@ -196,7 +197,7 @@
"Duration" = "Durata";
"Attendees:" = "Partecipanti:";
"Resources" = "Risorse";
"Organizer:" = "Organizzatore:";
"Organizer:" = "Proprietario:";
"Description:" = "Descrizione:";
"Document:" = "Documento:";
"Category:" = "Categoria:";
@ -256,7 +257,10 @@
"All day Event" = "Tutta la giornata";
"check for conflicts" = "Controlla conflitti";
"Browse URL" = "Cerca URL";
"Browse URL" = "Mostra URL";
"newAttendee" = "Nuovo partecipante";
/* calendar modes */
@ -396,7 +400,7 @@
/* transparency */
"Show Time as Free" = "Show Time as Free";
"Show Time as Free" = "Mostra comunque come libero";
/* validation errors */
@ -443,7 +447,7 @@ validate_endbeforestart = "La data finale specificata è precedente alla data
/* Legend */
"Required participant" = "Richiede partecipanti";
"Optional participant" = "Partecipanti opzionali";
"Chair" = "Chair";
"Chair" = "Sedia";
"Needs action" = "Richiede un'azione";
"Accepted" = "Accettato";
@ -491,4 +495,4 @@ vtodo_class2 = "(Attività confidenziale)";
/* Properties dialog */
"Name:" = "Nome:";
"Color:" = "Colore:";
"Tag:" = "Tag:";
"Tag:" = "Etichetta:";