Added support for dynamic mail labels/tags management.

The CSS in the UIxPreferences remains to be done.
pull/15/merge
Ludovic Marcotte 2013-11-11 10:49:58 -05:00
parent 6a9bcfda68
commit 37d3234b60
24 changed files with 625 additions and 230 deletions

View File

@ -16,6 +16,7 @@ Mailer_OBJC_FILES += \
SOGoMailAccounts.m \ SOGoMailAccounts.m \
SOGoMailAccount.m \ SOGoMailAccount.m \
SOGoMailFolder.m \ SOGoMailFolder.m \
SOGoMailLabel.m \
SOGoMailNamespace.m \ SOGoMailNamespace.m \
SOGoMailObject.m \ SOGoMailObject.m \
SOGoMailObject+Draft.m \ SOGoMailObject+Draft.m \

View File

@ -0,0 +1,50 @@
/*
Copyright (C) 2013 Inverse inc.
This file is part of SOGo.
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.
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.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#ifndef SOGOMAILLABEL_H
#define SOGOMAILLABEL_H
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import "../../UI/SOGoUI/UIxComponent.h";
@interface SOGoMailLabel : NSObject
{
NSString *_name;
NSString *_label;
NSString *_color;
}
- (id) initWithName: (NSString *) theName
label: (NSString *) theLabel
color: (NSString *) theColor;
- (NSString *) name;
- (NSString *) label;
- (NSString *) color;
+ (NSArray *) labelsFromDefaults: (NSDictionary *) theDefaults
component: (UIxComponent *) theComponent;
@end
#endif // SOGOMAILLABEL_H

View File

@ -0,0 +1,96 @@
/*
Copyright (C) 2007-2013 Inverse inc.
This file is part of SOGo.
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.
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.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import "SOGoMailLabel.h"
#import <Foundation/NSDictionary.h>
@implementation SOGoMailLabel
- (id) initWithName: (NSString *) theName
label: (NSString *) theLabel
color: (NSString *) theColor
{
self = [super init];
if (self)
{
ASSIGN(_name, theName);
ASSIGN(_label, theLabel);
ASSIGN(_color, theColor);
}
return self;
}
- (void) dealloc
{
RELEASE(_name);
RELEASE(_label);
RELEASE(_color);
[super dealloc];
}
- (NSString *) name
{
return _name;
}
- (NSString *) label
{
return _label;
}
- (NSString *) color
{
return _color;
}
+ (NSArray *) labelsFromDefaults: (NSDictionary *) theDefaults
component: (UIxComponent *) theComponent
{
NSMutableArray *allLabels, *allKeys;
NSDictionary *mailLabelsColors;
NSString *key, *name;
SOGoMailLabel *label;
NSArray *values;
int i;
allLabels = [NSMutableArray array];
allKeys = [[theDefaults allKeys] sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)];
for (i = 0; i < [allKeys count]; i++)
{
key = [allKeys objectAtIndex: i];
values = [theDefaults objectForKey: key];
name = [theComponent labelForKey: [values objectAtIndex: 0]];
label = [[self alloc] initWithName: key
label: name
color: [values objectAtIndex: 1]];
[allLabels addObject: label];
RELEASE(label);
}
return allLabels;
}
@end

View File

@ -76,4 +76,12 @@
SOGoRemindWithASound = YES; SOGoRemindWithASound = YES;
SOGoSearchMinimumWordLength = 2; SOGoSearchMinimumWordLength = 2;
SOGoMailLabelsColors = {
label1 = ("Important", "#f00");
label2 = ("Work", "#ff9a00");
label3 = ("Personal", "#009a00");
label4 = ("To Do", "#3130ff");
label5 = ("Later", "#9c309c");
};
} }

View File

@ -1,8 +1,6 @@
/* SOGoUserDefaults.h - this file is part of SOGo /* SOGoUserDefaults.h - this file is part of SOGo
* *
* Copyright (C) 2011-2012 Inverse inc. * Copyright (C) 2011-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -166,6 +164,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setForwardOptions: (NSDictionary *) newValue; - (void) setForwardOptions: (NSDictionary *) newValue;
- (NSDictionary *) forwardOptions; - (NSDictionary *) forwardOptions;
- (void) setMailLabelsColors: (NSDictionary *) newValues;
- (NSDictionary *) mailLabelsColors;
/* calendar */ /* calendar */
- (void) setCalendarCategories: (NSArray *) newValues; - (void) setCalendarCategories: (NSArray *) newValues;
- (NSArray *) calendarCategories; - (NSArray *) calendarCategories;

View File

@ -1,8 +1,6 @@
/* SOGoUserDefaults.m - this file is part of SOGo /* SOGoUserDefaults.m - this file is part of SOGo
* *
* Copyright (C) 2009-2012 Inverse inc. * Copyright (C) 2009-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -733,6 +731,26 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self boolForKey: @"SOGoRemindWithASound"]; return [self boolForKey: @"SOGoRemindWithASound"];
} }
//
// Dictionary of arrays. Example:
//
// {
// label1 => ("Important", "#FF0000");
// label2 => ("Work" "#00FF00");
// foo_bar => ("Foo Bar", "#0000FF");
// }
//
- (void) setMailLabelsColors: (NSDictionary *) newValues
{
[self setObject: newValues forKey: @"SOGoMailLabelsColors"];
}
- (NSDictionary *) mailLabelsColors
{
return [self objectForKey: @"SOGoMailLabelsColors"];
}
- (void) setSieveFilters: (NSArray *) newValue - (void) setSieveFilters: (NSArray *) newValue
{ {
[self setObject: newValue forKey: @"SOGoSieveFilters"]; [self setObject: newValue forKey: @"SOGoSieveFilters"];

View File

@ -1,8 +1,6 @@
/* UIxMailActions.h - this file is part of SOGo /* UIxMailActions.h - this file is part of SOGo
* *
* Copyright (C) 2007 Inverse inc. * Copyright (C) 2007-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@ -1,8 +1,6 @@
/* UIxMailActions.m - this file is part of SOGo /* UIxMailActions.m - this file is part of SOGo
* *
* Copyright (C) 2007 Inverse inc. * Copyright (C) 2007-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -21,6 +19,7 @@
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <NGObjWeb/WOContext.h> #import <NGObjWeb/WOContext.h>
@ -32,6 +31,8 @@
#import <SoObjects/Mailer/SOGoDraftsFolder.h> #import <SoObjects/Mailer/SOGoDraftsFolder.h>
#import <SoObjects/Mailer/SOGoMailAccount.h> #import <SoObjects/Mailer/SOGoMailAccount.h>
#import <SoObjects/Mailer/SOGoMailObject.h> #import <SoObjects/Mailer/SOGoMailObject.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import "../Common/WODirectAction+SOGo.h" #import "../Common/WODirectAction+SOGo.h"
@ -194,16 +195,18 @@
return response; return response;
} }
- (WOResponse *) _addLabel: (unsigned int) number - (WOResponse *) addLabelAction
{ {
WOResponse *response; WOResponse *response;
SOGoMailObject *co; SOGoMailObject *co;
NSException *error; NSException *error;
NSArray *flags; NSArray *flags;
NSString *flag;
flag = [[[self->context request] formValueForKey: @"flag"] fromCSSIdentifier];
co = [self clientObject]; co = [self clientObject];
flags = [NSArray arrayWithObject: flags = [NSArray arrayWithObject: flag];
[NSString stringWithFormat: @"$Label%u", number]];
error = [co addFlags: flags]; error = [co addFlags: flags];
if (error) if (error)
response = (WOResponse *) error; response = (WOResponse *) error;
@ -213,16 +216,18 @@
return response; return response;
} }
- (WOResponse *) _removeLabel: (unsigned int) number - (WOResponse *) removeLabelAction
{ {
WOResponse *response; WOResponse *response;
SOGoMailObject *co; SOGoMailObject *co;
NSException *error; NSException *error;
NSArray *flags; NSArray *flags;
NSString *flag;
flag = [[[self->context request] formValueForKey: @"flag"] fromCSSIdentifier];
co = [self clientObject]; co = [self clientObject];
flags = [NSArray arrayWithObject: flags = [NSArray arrayWithObject: flag];
[NSString stringWithFormat: @"$Label%u", number]];
error = [co removeFlags: flags]; error = [co removeFlags: flags];
if (error) if (error)
response = (WOResponse *) error; response = (WOResponse *) error;
@ -232,66 +237,25 @@
return response; return response;
} }
- (WOResponse *) addLabel1Action
{
return [self _addLabel: 1];
}
- (WOResponse *) addLabel2Action
{
return [self _addLabel: 2];
}
- (WOResponse *) addLabel3Action
{
return [self _addLabel: 3];
}
- (WOResponse *) addLabel4Action
{
return [self _addLabel: 4];
}
- (WOResponse *) addLabel5Action
{
return [self _addLabel: 5];
}
- (WOResponse *) removeLabel1Action
{
return [self _removeLabel: 1];
}
- (WOResponse *) removeLabel2Action
{
return [self _removeLabel: 2];
}
- (WOResponse *) removeLabel3Action
{
return [self _removeLabel: 3];
}
- (WOResponse *) removeLabel4Action
{
return [self _removeLabel: 4];
}
- (WOResponse *) removeLabel5Action
{
return [self _removeLabel: 5];
}
- (WOResponse *) removeAllLabelsAction - (WOResponse *) removeAllLabelsAction
{ {
NSMutableArray *flags;
WOResponse *response; WOResponse *response;
SOGoMailObject *co; SOGoMailObject *co;
NSException *error; NSException *error;
NSArray *flags; NSDictionary *v;
co = [self clientObject]; co = [self clientObject];
flags = [NSArray arrayWithObjects: @"$Label1", @"$Label2", @"$Label3",
@"$Label4", @"$Label5", nil]; v = [[[context activeUser] userDefaults] mailLabelsColors];
// We always unconditionally remove the Mozilla tags
flags = [NSMutableArray arrayWithObjects: @"$Label1", @"$Label2", @"$Label3",
@"$Label4", @"$Label5", nil];
[flags addObjectsFromArray: [v allKeys]];
error = [co removeFlags: flags]; error = [co removeFlags: flags];
if (error) if (error)
response = (WOResponse *) error; response = (WOResponse *) error;

View File

@ -1,6 +1,6 @@
/* /*
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2006-2011 Inverse inc. Copyright (C) 2006-2013 Inverse inc.
This file is part of SOGo This file is part of SOGo
@ -861,8 +861,9 @@
flags = [[message objectForKey: @"flags"] objectEnumerator]; flags = [[message objectForKey: @"flags"] objectEnumerator];
while ((currentFlag = [flags nextObject])) while ((currentFlag = [flags nextObject]))
if ([currentFlag hasPrefix: @"$label"]) {
[labels addObject: [currentFlag substringFromIndex: 1]]; [labels addObject: currentFlag];
}
return [labels componentsJoinedByString: @" "]; return [labels componentsJoinedByString: @" "];
} }

View File

@ -1,8 +1,6 @@
/* UIxMailMainFrame.h - this file is part of SOGo /* UIxMailMainFrame.h - this file is part of SOGo
* *
* Copyright (C) 2006-2011 Inverse inc. * Copyright (C) 2006-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -25,6 +23,8 @@
#import "../SOGoUI/UIxComponent.h" #import "../SOGoUI/UIxComponent.h"
@class SOGoMailLabel;
@interface UIxMailMainFrame : UIxComponent @interface UIxMailMainFrame : UIxComponent
{ {
SOGoUserSettings *us; SOGoUserSettings *us;
@ -33,6 +33,7 @@
NSArray *columnsOrder; NSArray *columnsOrder;
int folderType; int folderType;
NSDictionary *currentColumn; NSDictionary *currentColumn;
SOGoMailLabel *_currentLabel;
} }
- (WOResponse *) getFoldersStateAction; - (WOResponse *) getFoldersStateAction;

View File

@ -1,9 +1,5 @@
/* /*
Copyright (C) 2007-2011 Inverse inc. Copyright (C) 2007-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
Ludovic Marcotte <lmarcotte@inverse.ca>
This file is part of SOGo. This file is part of SOGo.
@ -48,6 +44,7 @@
#import <Mailer/SOGoMailAccount.h> #import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h> #import <Mailer/SOGoMailAccounts.h>
#import <Mailer/SOGoMailFolder.h> #import <Mailer/SOGoMailFolder.h>
#import <Mailer/SOGoMailLabel.h>
#import <Mailer/SOGoMailObject.h> #import <Mailer/SOGoMailObject.h>
#import <Mailer/SOGoSentFolder.h> #import <Mailer/SOGoSentFolder.h>
#import <SOGo/NSDictionary+URL.h> #import <SOGo/NSDictionary+URL.h>
@ -84,6 +81,12 @@
return self; return self;
} }
- (void) dealloc
{
RELEASE(_currentLabel);
[super dealloc];
}
- (void) _setupContext - (void) _setupContext
{ {
SOGoUser *activeUser; SOGoUser *activeUser;
@ -687,4 +690,32 @@
return [folders jsonRepresentation]; return [folders jsonRepresentation];
} }
//
// Standard mapping done by Thunderbird:
//
// label1 => Important
// label2 => Work
// label3 => Personal
// label4 => To Do
// label5 => Later
//
- (NSArray *) availableLabels
{
NSDictionary *v;
v = [[[context activeUser] userDefaults] mailLabelsColors];
return [SOGoMailLabel labelsFromDefaults: v component: self];
}
- (void) setCurrentLabel: (SOGoMailLabel *) theCurrentLabel
{
ASSIGN(_currentLabel, theCurrentLabel);
}
- (SOGoMailLabel *) currentLabel
{
return _currentLabel;
}
@end /* UIxMailMainFrame */ @end /* UIxMailMainFrame */

View File

@ -272,55 +272,15 @@
actionClass = "UIxMailActions"; actionClass = "UIxMailActions";
actionName = "markMessageRead"; actionName = "markMessageRead";
}; };
addLabel1 = { addLabel = {
protectedBy = "View"; protectedBy = "View";
actionClass = "UIxMailActions"; actionClass = "UIxMailActions";
actionName = "addLabel1"; actionName = "addLabel";
}; };
addLabel2 = { removeLabel = {
protectedBy = "View"; protectedBy = "View";
actionClass = "UIxMailActions"; actionClass = "UIxMailActions";
actionName = "addLabel2"; actionName = "removeLabel";
};
addLabel3 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "addLabel3";
};
addLabel4 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "addLabel4";
};
addLabel5 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "addLabel5";
};
removeLabel1 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "removeLabel1";
};
removeLabel2 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "removeLabel2";
};
removeLabel3 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "removeLabel3";
};
removeLabel4 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "removeLabel4";
};
removeLabel5 = {
protectedBy = "View";
actionClass = "UIxMailActions";
actionName = "removeLabel5";
}; };
removeAllLabels = { removeAllLabels = {
protectedBy = "View"; protectedBy = "View";

View File

@ -288,12 +288,13 @@
"Flagged" = "Flagged"; "Flagged" = "Flagged";
"Junk" = "Junk"; "Junk" = "Junk";
"Not Junk" = "Not Junk"; "Not Junk" = "Not Junk";
"Label 1" = "Label 1"; "Important" = "Important";
"Label 2" = "Label 2"; "Work" = "Work";
"Label 3" = "Label 3"; "Personal" = "Personal";
"Label 4" = "Label 4"; "To Do" = "To Do";
"Label 5" = "Label 5"; "Later" = "Later";
/* Password policy */
"The password was changed successfully." = "The password was changed successfully."; "The password was changed successfully." = "The password was changed successfully.";
"Password must not be empty." = "Password must not be empty."; "Password must not be empty." = "Password must not be empty.";
"The passwords do not match. Please try again." = "The passwords do not match. Please try again."; "The passwords do not match. Please try again." = "The passwords do not match. Please try again.";
@ -306,4 +307,4 @@
"Unhandled policy error: %{0}" = "Unhandled policy error: %{0}"; "Unhandled policy error: %{0}" = "Unhandled policy error: %{0}";
"Unhandled error response" = "Unhandled error response"; "Unhandled error response" = "Unhandled error response";
"Password change is not supported." = "Password change is not supported."; "Password change is not supported." = "Password change is not supported.";
"Unhandled HTTP error code: %{0}" = "Unhandled HTTP error code: %{0}"; "Unhandled HTTP error code: %{0}" = "Unhandled HTTP error code: %{0}";

View File

@ -1,8 +1,6 @@
/* UIxFilterEditor.m - this file is part of SOGo /* UIxFilterEditor.m - this file is part of SOGo
* *
* Copyright (C) 2010 Inverse inc. * Copyright (C) 2010-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -26,20 +24,44 @@
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h> #import <NGObjWeb/WOResponse.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGoUI/UIxComponent.h> #import <SOGoUI/UIxComponent.h>
@interface UIxFilterEditor : UIxComponent @interface UIxFilterEditor : UIxComponent
{ {
NSString *filterId; NSString *filterId;
NSDictionary *labels;
} }
@end @end
@implementation UIxFilterEditor @implementation UIxFilterEditor
- (id) init
{
self = [super init];
if (self)
{
filterId = nil;
labels = nil;
}
return self;
}
- (void) dealloc
{
RELEASE(filterId);
RELEASE(labels);
[super dealloc];
}
/* convert and save */ /* convert and save */
- (BOOL) _validateFilterId - (BOOL) _validateFilterId
{ {
@ -80,4 +102,14 @@
return filterId; return filterId;
} }
- (NSString *) labels
{
if (!labels)
{
ASSIGN(labels, [[[context activeUser] userDefaults] mailLabelsColors]);
}
return [labels jsonRepresentation];
}
@end @end

View File

@ -1,8 +1,6 @@
/* UIxPreferences.h - this file is part of SOGo /* UIxPreferences.h - this file is part of SOGo
* *
* Copyright (C) 2007-2010 Inverse inc. * Copyright (C) 2007-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -27,23 +25,36 @@
@class NSString; @class NSString;
@class SOGoMailLabel;
@class SOGoUser; @class SOGoUser;
@interface UIxPreferences : UIxComponent @interface UIxPreferences : UIxComponent
{ {
id item; id item;
SOGoUser *user; SOGoUser *user;
// Calendar categories
NSString *category; NSString *category;
NSArray *calendarCategories; NSArray *calendarCategories;
NSDictionary *calendarCategoriesColors; NSDictionary *calendarCategoriesColors;
NSArray *contactsCategories; NSArray *contactsCategories;
NSString *defaultCategoryColor; NSString *defaultCategoryColor;
NSCalendarDate *today; NSCalendarDate *today;
// Mail labels/tags
SOGoMailLabel *label;
NSArray *mailLabels;
// Sieve filtering
NSArray *daysOfWeek, *daysBetweenResponsesList; NSArray *daysOfWeek, *daysBetweenResponsesList;
NSArray *sieveFilters; NSArray *sieveFilters;
NSMutableDictionary *vacationOptions, *forwardOptions; NSMutableDictionary *vacationOptions, *forwardOptions;
BOOL mailCustomFromEnabled; BOOL mailCustomFromEnabled;
BOOL hasChanged; BOOL hasChanged;
} }
- (NSString *) userLongDateFormat; - (NSString *) userLongDateFormat;

View File

@ -1,9 +1,6 @@
/* UIxPreferences.m - this file is part of SOGo /* UIxPreferences.m - this file is part of SOGo
* *
* Copyright (C) 2007-2011 Inverse inc. * Copyright (C) 2007-2013 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Francis Lachapelle <flachapelle@inverse.ca>
* *
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -31,6 +28,8 @@
#import <NGObjWeb/WOContext.h> #import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <NGImap4/NSString+Imap4.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalTimeZone.h> #import <NGCards/iCalTimeZone.h>
@ -47,6 +46,7 @@
#import <SOGo/WOResourceManager+SOGo.h> #import <SOGo/WOResourceManager+SOGo.h>
#import <Mailer/SOGoMailAccount.h> #import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h> #import <Mailer/SOGoMailAccounts.h>
#import <Mailer/SOGoMailLabel.h>
#import "UIxPreferences.h" #import "UIxPreferences.h"
@ -77,6 +77,9 @@
defaultCategoryColor = nil; defaultCategoryColor = nil;
category = nil; category = nil;
label = nil;
mailLabels = nil;
ASSIGN (daysOfWeek, [locale objectForKey: NSWeekDayNameArray]); ASSIGN (daysOfWeek, [locale objectForKey: NSWeekDayNameArray]);
dd = [user domainDefaults]; dd = [user domainDefaults];
@ -120,6 +123,8 @@
[calendarCategoriesColors release]; [calendarCategoriesColors release];
[defaultCategoryColor release]; [defaultCategoryColor release];
[category release]; [category release];
[label release];
[mailLabels release];
[contactsCategories release]; [contactsCategories release];
[forwardOptions release]; [forwardOptions release];
[daysOfWeek release]; [daysOfWeek release];
@ -1175,6 +1180,67 @@
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)]; sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
} }
- (SOGoMailLabel *) label
{
return label;
}
- (void) setLabel: (SOGoMailLabel *) newLabel
{
ASSIGN(label, newLabel);
}
- (NSArray *) mailLabelList
{
if (!mailLabels)
{
NSDictionary *v;
v = [[[context activeUser] userDefaults] mailLabelsColors];
ASSIGN(mailLabels, [SOGoMailLabel labelsFromDefaults: v component: self]);
}
return mailLabels;
}
- (NSString *) mailLabelsValue
{
return @"";
}
- (void) setMailLabelsValue: (NSString *) value
{
NSMutableDictionary *sanitizedLabels;
NSDictionary *newLabels;
NSArray *allKeys;
NSString *name;
int i;
newLabels = [value objectFromJSONString];
if (newLabels && [newLabels isKindOfClass: [NSDictionary class]])
{
// We encode correctly our keys
sanitizedLabels = [NSMutableDictionary dictionary];
allKeys = [newLabels allKeys];
for (i = 0; i < [allKeys count]; i++)
{
name = [allKeys objectAtIndex: i];
if (![name is7bitSafe])
name = [name stringByEncodingImap4FolderName];
name = [name lowercaseString];
[sanitizedLabels setObject: [newLabels objectForKey: [allKeys objectAtIndex: i]]
forKey: name];
}
[userDefaults setMailLabelsColors: sanitizedLabels];
}
}
- (void) setCategory: (NSString *) newCategory - (void) setCategory: (NSString *) newCategory
{ {
ASSIGN (category, newCategory); ASSIGN (category, newCategory);

View File

@ -16,6 +16,10 @@
var unseenCountFolders = <var:string value="unseenCountFolders" const:escapeHTML="NO"/>; var unseenCountFolders = <var:string value="unseenCountFolders" const:escapeHTML="NO"/>;
</script> </script>
<style type="text/css"> <style type="text/css">
<var:foreach list="availableLabels" item="currentLabel">
#label-menu LI.<var:string value="currentLabel.name"/>
{ color: <var:string value="currentLabel.color"/>; }
</var:foreach>
<var:if condition="horizontalDragHandleStyle"> <var:if condition="horizontalDragHandleStyle">
DIV#verticalDragHandle, DIV#rightPanel DIV#verticalDragHandle, DIV#rightPanel
{ left: <var:string value="horizontalDragHandleStyle" />; } { left: <var:string value="horizontalDragHandleStyle" />; }
@ -170,11 +174,9 @@
<ul id="" class="choiceMenu"> <ul id="" class="choiceMenu">
<li><var:string label:value="None" /></li> <li><var:string label:value="None" /></li>
<li><!-- separator --></li> <li><!-- separator --></li>
<li class="label1"><var:string label:value="Important" /></li> <var:foreach list="availableLabels" item="currentLabel">
<li class="label2"><var:string label:value="Work" /></li> <li var:class="currentLabel.name" var:data-name="currentLabel.name"> <var:string value="currentLabel.label"/></li>
<li class="label3"><var:string label:value="Personal" /></li> </var:foreach>
<li class="label4"><var:string label:value="To Do" /></li>
<li class="label5"><var:string label:value="Later" /></li>
</ul> </ul>
</div> </div>

View File

@ -13,6 +13,7 @@
> >
<script type="text/javascript"> <script type="text/javascript">
var filterId = '<var:string value="filterId"/>'; var filterId = '<var:string value="filterId"/>';
var labels = <var:string value="labels"/>;
</script> </script>
<form id="mainForm" var:href="ownPath"> <form id="mainForm" var:href="ownPath">
<div id="filterNameContainer" class="container"> <div id="filterNameContainer" class="container">

View File

@ -283,6 +283,41 @@
</span></a> </span></a>
</div> </div>
</var:if> </var:if>
<label><var:string label:value="Labels"/></label>
<div id="mailLabelsListWrapper" class="listWrapper"
><table class="labelsList" cellspacing="0">
<thead>
<tr class="tableview"
><th const:class="tbtv_headercell" const:id="labelTableHeader"
><var:string label:value="Label"/></th
><th const:class="tbtv_headercell" const:id="colorTableHeader"
><var:string label:value="Color"/></th
></tr
></thead>
<tbody>
<var:foreach list="mailLabelList" item="label">
<tr var:data-name="label.name" const:class="labelListRow"
><td const:class="labelListCell"
><var:string var:value="label.label"/></td
><td const:class="labelListCell"
><div const:class="colorBox"><var:string var:value="label.color"
/></div></td
></tr>
</var:foreach>
</tbody>
</table>
</div>
<div class="bottomToolbar">
<a const:id="mailLabelAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
<a const:id="mailLabelDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</div>
<input type="hidden" const:id="mailLabelsValue"
const:name="mailLabelsValue" var:value="mailLabelsValue"/>
</div> </div>
<div id="mailAccountsView" class="tab"> <div id="mailAccountsView" class="tab">

View File

@ -1,14 +1,15 @@
/* /*
Copyright (C) 2005-2013 Inverse inc.
Copyright (C) 2005 SKYRIX Software AG Copyright (C) 2005 SKYRIX Software AG
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 the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any Free Software Foundation; either version 2, or (at your option) any
later version. 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details. License for more details.
@ -93,21 +94,6 @@ DIV#folderTreeContent
right: 0px; right: 0px;
overflow: auto; } overflow: auto; }
#label-menu LI.label1
{ color: #f00; }
#label-menu LI.label2
{ color: #ff9a00; }
#label-menu LI.label3
{ color: #009a00; }
#label-menu LI.label4
{ color: #3130ff; }
#label-menu LI.label5
{ color: #9c309c; }
.aptview_title .aptview_title
{ {
color: #000000; color: #000000;

View File

@ -2488,9 +2488,11 @@ function onMenuLabelNone() {
}); });
} }
function _onMenuLabelFlagX(flag) { function onMenuLabelFlag() {
var messages = new Hash(); var messages = new Hash();
var flag = this.readAttribute("data-name");
if (document.menuTarget.tagName == "DIV") if (document.menuTarget.tagName == "DIV")
// Menu called from message content view // Menu called from message content view
messages.set(Mailer.currentMessages[Mailer.currentMailbox], messages.set(Mailer.currentMessages[Mailer.currentMailbox],
@ -2502,45 +2504,25 @@ function _onMenuLabelFlagX(flag) {
if (row) if (row)
messages.set(rowID.substr(4), messages.set(rowID.substr(4),
row.getAttribute("labels")); row.getAttribute("labels"));
}); });
else else
// Menu called from one selection in messages list view // Menu called from one selection in messages list view
messages.set(document.menuTarget.getAttribute("id").substr(4), messages.set(document.menuTarget.getAttribute("id").substr(4),
document.menuTarget.getAttribute("labels")); document.menuTarget.getAttribute("labels"));
var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/"; var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/";
messages.keys().each(function(id) { messages.keys().each(function(id) {
var flags = messages.get(id).split(" "); var flags = messages.get(id).split(" ");
var operation = "add"; var operation = "add";
if (flags.indexOf("label" + flag) > -1) if (flags.indexOf(flag) > -1)
operation = "remove"; operation = "remove";
triggerAjaxRequest(url + id + "/" + operation + "Label" + flag, triggerAjaxRequest(url + id + "/" + operation + "Label?flag=" + flag.asCSSIdentifier(),
messageFlagCallback, messageFlagCallback,
{ mailbox: Mailer.currentMailbox, msg: id, { mailbox: Mailer.currentMailbox, msg: id,
label: operation + flag } ); label: operation + flag } );
}); });
}
function onMenuLabelFlag1() {
_onMenuLabelFlagX(1);
}
function onMenuLabelFlag2() {
_onMenuLabelFlagX(2);
}
function onMenuLabelFlag3() {
_onMenuLabelFlagX(3);
}
function onMenuLabelFlag4() {
_onMenuLabelFlagX(4);
}
function onMenuLabelFlag5() {
_onMenuLabelFlagX(5);
} }
function onMenuToggleMessageFlag(event) { function onMenuToggleMessageFlag(event) {
@ -2707,14 +2689,33 @@ function onLabelMenuPrepareVisibility() {
var lis = this.childNodesWithTag("ul")[0].childNodesWithTag("li"); var lis = this.childNodesWithTag("ul")[0].childNodesWithTag("li");
var isFlagged = false; var isFlagged = false;
for (var i = 1; i < 6; i++) {
if (flags["label" + i]) { // lis is our array of labels, ex:
isFlagged = true; // li
lis[1 + i].addClassName("_chosen"); // li.seperator
// li.label1
// li.broccoli
// ...
for (var i = 2; i < lis.length; i++) {
// We bind the event handlers if we need to
if (lis[i].menuCallback == null) {
lis[i].menuCallback = onMenuLabelFlag;
lis[i].on("mousedown", onMenuClickHandler);
lis[i].removeClassName("disabled");
}
var flag = lis[i].readAttribute("data-name");
if (flags[flag]) {
isFlagged = true;
lis[i].addClassName("_chosen");
}
else {
lis[i].removeClassName("_chosen");
} }
else
lis[1 + i].removeClassName("_chosen");
} }
if (isFlagged) if (isFlagged)
lis[0].removeClassName("_chosen"); lis[0].removeClassName("_chosen");
else else
@ -2834,12 +2835,9 @@ function getMenus() {
onMenuChangeToDraftsFolder, onMenuChangeToDraftsFolder,
onMenuChangeToTrashFolder ], onMenuChangeToTrashFolder ],
"label-menu": [ onMenuLabelNone, "-", onMenuLabelFlag1, "label-menu": [ onMenuLabelNone, "-" ],
onMenuLabelFlag2, onMenuLabelFlag3,
onMenuLabelFlag4, onMenuLabelFlag5 ],
"mark-menu": [ onMenuToggleMessageRead, null, null, null, "-", onMenuToggleMessageFlag ], "mark-menu": [ onMenuToggleMessageRead, null, null, null, "-", onMenuToggleMessageFlag ],
// , "-",
// null, null, null ],
searchMenu: [ setSearchCriteria, setSearchCriteria, searchMenu: [ setSearchCriteria, setSearchCriteria,
setSearchCriteria, setSearchCriteria, setSearchCriteria, setSearchCriteria,

View File

@ -1,11 +1,13 @@
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
var type;
function onLoadColorPicker(event) { function onLoadColorPicker(event) {
showColorPicker(); showColorPicker();
} }
function onChooseColor(newColor) { function onChooseColor(newColor) {
window.opener.onColorPickerChoice(newColor); window.opener.onColorPickerChoice(newColor, type);
window.close(); window.close();
} }

View File

@ -90,9 +90,10 @@ function setupConstants() {
"flagged": _("Flagged"), "flagged": _("Flagged"),
"junk": _("Junk"), "junk": _("Junk"),
"not_junk": _("Not Junk") }; "not_junk": _("Not Junk") };
for (var i = 1; i < 6; i++) {
var key = "label" + i;
flagLabels[key] = _("Label " + i); for (var name in labels) {
flagLabels[name] = _( labels[name][0] );
} }
} }

View File

@ -17,6 +17,10 @@ function savePreferences(sender) {
serializeContactsCategories(); serializeContactsCategories();
} }
if ($("mailLabelsListWrapper")) {
serializeMailLabels();
}
if (typeof mailCustomFromEnabled !== "undefined" && !emailRE.test($("email").value)) { if (typeof mailCustomFromEnabled !== "undefined" && !emailRE.test($("email").value)) {
showAlertDialog(_("Please specify a valid sender address.")); showAlertDialog(_("Please specify a valid sender address."));
sendForm = false; sendForm = false;
@ -182,6 +186,7 @@ function initPreferences() {
if (typeof (initAdditionalPreferences) != "undefined") if (typeof (initAdditionalPreferences) != "undefined")
initAdditionalPreferences(); initAdditionalPreferences();
// Calender categories
var wrapper = $("calendarCategoriesListWrapper"); var wrapper = $("calendarCategoriesListWrapper");
if (wrapper) { if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0]; var table = wrapper.childNodesWithTag("table")[0];
@ -195,6 +200,22 @@ function initPreferences() {
$("calendarCategoryDelete").observe("click", onCalendarCategoryDelete); $("calendarCategoryDelete").observe("click", onCalendarCategoryDelete);
} }
// Mail labels/tags
var wrapper = $("mailLabelsListWrapper");
if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0];
resetMailLabelsColors(null);
var r = $$("#mailLabelsListWrapper tbody tr");
for (var i= 0; i < r.length; i++)
r[i].identify();
table.multiselect = true;
resetMailTableActions();
$("mailLabelAdd").observe("click", onMailLabelAdd);
$("mailLabelDelete").observe("click", onMailLabelDelete);
}
// Contact categories
wrapper = $("contactsCategoriesListWrapper"); wrapper = $("contactsCategoriesListWrapper");
if (wrapper) { if (wrapper) {
var table = wrapper.childNodesWithTag("table")[0]; var table = wrapper.childNodesWithTag("table")[0];
@ -855,21 +876,15 @@ function compactMailAccounts() {
} }
} }
/* calendar categories */ /* common function between calendar categories and mail labels */
function resetCalendarTableActions() { function onColorEdit (e, type) {
var r = $$("#calendarCategoriesListWrapper tbody tr"); var r;
for (var i = 0; i < r.length; i++) {
var row = $(r[i]);
row.observe("mousedown", onRowClick);
var tds = row.childElements();
var editionCtlr = new RowEditionController();
editionCtlr.attachToRowElement(tds[0]);
tds[1].childElements()[0].observe("dblclick", onColorEdit);
}
}
function onColorEdit (e) { if (type == "calendar")
var r = $$("#calendarCategoriesListWrapper div.colorEditing"); r = $$("#calendarCategoriesListWrapper div.colorEditing");
else
r = $$("#mailLabelsListWrapper div.colorEditing");
for (var i=0; i<r.length; i++) for (var i=0; i<r.length; i++)
r[i].removeClassName("colorEditing"); r[i].removeClassName("colorEditing");
@ -881,12 +896,19 @@ function onColorEdit (e) {
+ "menubar=0,copyhistory=0", "test" + "menubar=0,copyhistory=0", "test"
); );
cPicker.focus(); cPicker.focus();
cPicker.type = type;
preventDefault(e); preventDefault(e);
} }
function onColorPickerChoice (newColor) { function onColorPickerChoice (newColor, type) {
var div = $$("#calendarCategoriesListWrapper div.colorEditing").first (); var div;
if (type == "calendar")
div = $$("#calendarCategoriesListWrapper div.colorEditing").first ();
else
div = $$("#mailLabelsListWrapper div.colorEditing").first ();
// div.removeClassName ("colorEditing"); // div.removeClassName ("colorEditing");
div.showColor = newColor; div.showColor = newColor;
div.style.background = newColor; div.style.background = newColor;
@ -895,6 +917,26 @@ function onColorPickerChoice (newColor) {
hasChanged.value = "1"; hasChanged.value = "1";
} }
} }
/* /common function between calendar categories and mail labels */
/* calendar categories */
function resetCalendarTableActions() {
var r = $$("#calendarCategoriesListWrapper tbody tr");
for (var i = 0; i < r.length; i++) {
var row = $(r[i]);
row.observe("mousedown", onRowClick);
var tds = row.childElements();
var editionCtlr = new RowEditionController();
editionCtlr.attachToRowElement(tds[0]);
tds[1].childElements()[0].observe("dblclick", onCalendarColorEdit);
}
}
function onCalendarColorEdit (e) {
var onCCE = onColorEdit.bind(this);
onCCE(e, "calendar");
}
function onCalendarCategoryAdd (e) { function onCalendarCategoryAdd (e) {
var row = new Element ("tr"); var row = new Element ("tr");
@ -959,6 +1001,95 @@ function resetCalendarCategoriesColors (e) {
/* /calendar categories */ /* /calendar categories */
/* mail label/tags */
function resetMailTableActions() {
var r = $$("#mailLabelsListWrapper tbody tr");
for (var i = 0; i < r.length; i++) {
var row = $(r[i]);
row.observe("mousedown", onRowClick);
var tds = row.childElements();
var editionCtlr = new RowEditionController();
editionCtlr.attachToRowElement(tds[0]);
tds[1].childElements()[0].observe("dblclick", onMailColorEdit);
}
}
function onMailColorEdit (e) {
var onMCE = onColorEdit.bind(this);
onMCE(e, "mail");
}
function onMailLabelAdd (e) {
var row = new Element ("tr");
var nametd = new Element ("td").update ("");
var colortd = new Element ("td");
var colordiv = new Element ("div", {"class": "colorBox"});
row.identify ();
row.addClassName ("labelListRow");
nametd.addClassName ("labelListCell");
colortd.addClassName ("labelListCell");
colordiv.innerHTML = "&nbsp;";
colordiv.showColor = "#F0F0F0";
colordiv.style.background = colordiv.showColor;
colortd.appendChild (colordiv);
row.appendChild (nametd);
row.appendChild (colortd);
$("mailLabelsListWrapper").childNodesWithTag("table")[0].tBodies[0].appendChild (row);
resetMailTableActions ();
nametd.editionController.startEditing();
}
function onMailLabelDelete (e) {
var list = $('mailLabelsListWrapper').down("TABLE").down("TBODY");
var rows = list.getSelectedNodes();
var count = rows.length;
for (var i=0; i < count; i++) {
rows[i].editionController = null;
rows[i].remove ();
}
}
function resetMailLabelsColors (e) {
var divs = $$("#mailLabelsListWrapper DIV.colorBox");
for (var i = 0; i < divs.length; i++) {
var d = divs[i];
var color = d.innerHTML;
d.showColor = color;
if (color != "undefined")
d.setStyle({ backgroundColor: color });
d.update("&nbsp;");
}
}
function serializeMailLabels() {
var r = $$("#mailLabelsListWrapper TBODY TR");
var values = [];
for (var i = 0; i < r.length; i++) {
var tds = r[i].childElements ();
var name = r[i].readAttribute("data-name");
var label = $(tds.first ()).innerHTML;
var color = $(tds.last ().childElements ().first ()).showColor;
/* if name is null, that's because we've just added a new tag */
if (!name) {
name = label.replace(/ /g,"_");
}
values.push("\"" + name + "\": [\"" + label + "\", \"" + color + "\"]");
}
$("mailLabelsValue").value = "{ " + values.join(",\n") + "}";
}
/* /mail label/tags */
/* contacts categories */ /* contacts categories */
function resetContactsTableActions() { function resetContactsTableActions() {
var r = $$("#contactsCategoriesListWrapper tbody tr"); var r = $$("#contactsCategoriesListWrapper tbody tr");