merge of 'c410032070bc4cd3697a1e0b498960cb5afae02f'
and 'dde4cb6401010f8fb6e70639215b4bf693f18f1d' Monotone-Parent: c410032070bc4cd3697a1e0b498960cb5afae02f Monotone-Parent: dde4cb6401010f8fb6e70639215b4bf693f18f1d Monotone-Revision: eeede995522aac10418121af680e6cf94cb7bb4a Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2010-11-03T20:47:52 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
commit
761462e26c
42
ChangeLog
42
ChangeLog
|
@ -1,3 +1,21 @@
|
|||
2010-11-03 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* Tests/Integration/teststrings.sh: new utility script to test the
|
||||
parsability of the localization strings file.
|
||||
|
||||
* UI/WebServerResources/generic.js: (onCASRecoverIFrameLoaded):
|
||||
go back to the user's page instead of the logoff page.
|
||||
|
||||
2010-11-02 Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
|
||||
* Implemented http://sogo.nu/bugs/view.php?id=821
|
||||
|
||||
2010-11-02 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/NSDictionary+BSJSONAdditions.[hm]:
|
||||
SoObjects/SOGo/NSScanner+BSJSONAdditions.[hm]: removed classes,
|
||||
obsoleted by the use of the SBJson library.
|
||||
|
||||
2010-11-01 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/MailerUI.js (mailListToggleMessagesRead):
|
||||
|
@ -19,6 +37,30 @@
|
|||
|
||||
2010-11-01 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailUpdate.m (-setupValues): avoid
|
||||
setting values that are already set in SOGoAptMailNotification.
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailDeletion.m (-getBody): added
|
||||
the same details as for the meeting invitations.
|
||||
|
||||
* SoObjects/Appointments/SOGoAptMailNotification.m (-setupValues):
|
||||
merged setup code from SOGoAptMailInvitation.m.
|
||||
|
||||
* UI/WebServerResources/ContactsUI.js: (resetCategoriesMenu)
|
||||
(onCategoriesMenuPrepareVisibility, onCategoriesMenuItemClick)
|
||||
(setCategoryOnNode, unsetCategoryOnNode): new methods designed to
|
||||
handle the handling of categories directly from the contacts list.
|
||||
|
||||
* SoObjects/Contacts/SOGoContactGCSFolder.m (+initialize): we now
|
||||
return the "c_categories" field too.
|
||||
|
||||
* UI/Contacts/UIxContactActions.m: new class module implementing
|
||||
actions on contacts.
|
||||
(-setCategoryAction, -unsetCategoryAction): new actions.
|
||||
|
||||
* UI/WebServerResources/generic.js (triggerAjaxRequest): invoke
|
||||
the request callback only when set on the http object.
|
||||
|
||||
* SoObjects/Mailer/SOGoMailBaseObject.m (-_createIMAP4Connection):
|
||||
we need to initialize newConnection to nil when the return
|
||||
password is nil.
|
||||
|
|
Binary file not shown.
|
@ -6,8 +6,8 @@ include $(GNUSTEP_MAKEFILES)/common.make
|
|||
SUBPROJECTS = \
|
||||
SOPE/NGCards \
|
||||
SOPE/GDLContentStore \
|
||||
OGoContentStore \
|
||||
SoObjects \
|
||||
OGoContentStore \
|
||||
Main \
|
||||
UI \
|
||||
Tools
|
||||
|
|
|
@ -9,7 +9,7 @@ libOGoContentStore_LIBRARIES_DEPEND_UPON += \
|
|||
-lSaxObjC \
|
||||
-lSOGo
|
||||
|
||||
ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE
|
||||
ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE -I../SoObjects
|
||||
|
||||
ADDITIONAL_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR) -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -L../SoObjects/SOGo/SOGo.framework/
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Tarefa Confidencial)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Tasca confidencial)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Esdeveniment suspès: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}ha suspès aquest esdeveniment: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}ha suspès aquest esdeveniment: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Skrytý úkol)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Zrušení události: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Vertrouwelijke taak)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Confidential task)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Tâche confidentielle)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Réunion annulée : « %{Summary} »";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}a annulé cette réunion : « %{Summary} »";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}a annulé cette réunion : « %{Summary} ».\n\nDébut: %{StartDate} à %{StartTime}\nFin: %{EndDate} à %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Termin abgesagt: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}hat diesen Termin abgesagt: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}hat diesen Termin abgesagt: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Bizalmas feladat)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Attività confidenziale)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Evento cancellato: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}ha cancellato questo evento: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}ha cancellato questo evento: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Zadanie poufne)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Wydarzenie anulowane: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}anulował(a) to wydarzenie: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}anulował(a) to wydarzenie: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Confidential task)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -49,8 +49,11 @@
|
|||
if (!values)
|
||||
[self setupValues];
|
||||
|
||||
bodyFormat = [self labelForKey: @"%{Organizer} %{SentByText}has"
|
||||
@" cancelled this event: %{Summary}."
|
||||
bodyFormat = [self labelForKey: (@"%{Organizer} %{SentByText}has cancelled"
|
||||
@" this event: %{Summary}.\n\n"
|
||||
@"Start: %{StartDate} at %{StartTime}\n"
|
||||
@"End: %{EndDate} at %{EndTime}\n"
|
||||
@"Description: %{Description}")
|
||||
inContext: context];
|
||||
|
||||
return [values keysWithFormat: bodyFormat];
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import "iCalPerson+SOGo.h"
|
||||
|
||||
|
@ -35,34 +34,6 @@
|
|||
|
||||
@implementation SOGoAptMailInvitation
|
||||
|
||||
- (void) setupValues
|
||||
{
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
NSCalendarDate *date;
|
||||
NSString *description;
|
||||
|
||||
[super setupValues];
|
||||
|
||||
|
||||
dateFormatter = [[context activeUser] dateFormatterInContext: context];
|
||||
|
||||
date = [self newStartDate];
|
||||
[values setObject: [dateFormatter shortFormattedDate: date]
|
||||
forKey: @"StartDate"];
|
||||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"StartTime"];
|
||||
|
||||
date = [self newEndDate];
|
||||
[values setObject: [dateFormatter shortFormattedDate: date]
|
||||
forKey: @"EndDate"];
|
||||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"EndTime"];
|
||||
|
||||
description = [[self apt] comment];
|
||||
[values setObject: (description ? description : @"")
|
||||
forKey: @"Description"];
|
||||
}
|
||||
|
||||
- (NSString *) getSubject
|
||||
{
|
||||
NSString *subjectFormat;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSObject+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
|
@ -172,9 +173,11 @@
|
|||
|
||||
- (void) setupValues
|
||||
{
|
||||
NSString *sentBy, *sentByText, *description;
|
||||
NSCalendarDate *date;
|
||||
NSDictionary *sentByValues;
|
||||
NSString *sentBy, *sentByText;
|
||||
SOGoUser *user;
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
|
||||
user = [context activeUser];
|
||||
viewTZ = [[user userDefaults] timeZone];
|
||||
|
@ -200,6 +203,25 @@
|
|||
sentByText = @"";
|
||||
[values setObject: sentByText forKey: @"SentByText"];
|
||||
}
|
||||
|
||||
dateFormatter = [[context activeUser] dateFormatterInContext: context];
|
||||
|
||||
date = [self newStartDate];
|
||||
[values setObject: [dateFormatter shortFormattedDate: date]
|
||||
forKey: @"StartDate"];
|
||||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"StartTime"];
|
||||
|
||||
date = [self newEndDate];
|
||||
[values setObject: [dateFormatter shortFormattedDate: date]
|
||||
forKey: @"EndDate"];
|
||||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"EndTime"];
|
||||
|
||||
description = [[self apt] comment];
|
||||
[values setObject: (description ? description : @"")
|
||||
forKey: @"Description"];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -134,12 +134,6 @@
|
|||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"OldStartTime"];
|
||||
|
||||
date = [self newStartDate];
|
||||
[values setObject: [dateFormatter shortFormattedDate: date]
|
||||
forKey: @"StartDate"];
|
||||
[values setObject: [dateFormatter formattedTime: date]
|
||||
forKey: @"StartTime"];
|
||||
|
||||
[self _setupBodyValuesWithFormatter: dateFormatter];
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Tarea confidencial)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Konfidentiell uppgift)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Конфіденційне завдання)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Подію скасовано: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText} скасував подію: %{Summary}."
|
||||
= "%{Organizer} %{SentByText} скасував подію: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText} скасував подію: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -39,8 +39,8 @@ vtodo_class2 = "(Tasg gyfrinachol)";
|
|||
|
||||
/* Deletion */
|
||||
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}."
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.";
|
||||
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
|
||||
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
|
||||
|
||||
/* Update */
|
||||
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
|
||||
|
|
|
@ -62,6 +62,7 @@ static NSArray *folderListingFields = nil;
|
|||
@"c_cn", @"c_givenname", @"c_sn",
|
||||
@"c_screenname", @"c_o",
|
||||
@"c_mail", @"c_telephonenumber",
|
||||
@"c_categories",
|
||||
@"c_component", nil];
|
||||
}
|
||||
|
||||
|
|
|
@ -236,8 +236,9 @@ static NSString *sieveScriptName = @"sogo";
|
|||
SOGoUserDefaults *ud;
|
||||
SOGoDomainDefaults *dd;
|
||||
NGSieveClient *client;
|
||||
NSString *filterScript, *v, *password;
|
||||
NSString *filterScript, *v, *password, *sieveServer;
|
||||
SOGoSieveConverter *converter;
|
||||
int sievePort;
|
||||
BOOL b;
|
||||
|
||||
dd = [[context activeUser] domainDefaults];
|
||||
|
@ -338,9 +339,36 @@ static NSString *sieveScriptName = @"sogo";
|
|||
[script insertString: header atIndex: 0];
|
||||
}
|
||||
|
||||
// We connect to our Sieve server and upload the script
|
||||
address = [NGInternetSocketAddress addressWithPort: 2000
|
||||
onHost: [[self imap4URL] host]];
|
||||
// We connect to our Sieve server and upload the script.
|
||||
//
|
||||
// sieveServer might have the following format:
|
||||
//
|
||||
// sieve://localhost
|
||||
// sieve://localhost:2000
|
||||
//
|
||||
// Values such as "localhost" or "localhost:2000" are NOT supported.
|
||||
//
|
||||
sieveServer = [dd sieveServer];
|
||||
sievePort = 2000;
|
||||
|
||||
if (!sieveServer)
|
||||
{
|
||||
sieveServer = @"localhost";
|
||||
}
|
||||
else
|
||||
{
|
||||
NSURL *url;
|
||||
|
||||
url = [NSURL URLWithString: sieveServer];
|
||||
|
||||
if ([url host])
|
||||
sieveServer = [url host];
|
||||
|
||||
if ([[url port] intValue] != 0)
|
||||
sievePort = [[url port] intValue];
|
||||
}
|
||||
|
||||
address = [NGInternetSocketAddress addressWithPort: sievePort onHost: sieveServer];
|
||||
|
||||
client = [NGSieveClient clientWithAddress: address];
|
||||
|
||||
|
|
|
@ -56,9 +56,6 @@ SOGo_HEADER_FILES = \
|
|||
SOGoGroup.h \
|
||||
SOGoUser.h \
|
||||
\
|
||||
NSDictionary+BSJSONAdditions.h \
|
||||
NSScanner+BSJSONAdditions.h \
|
||||
\
|
||||
DOMNode+SOGo.h \
|
||||
\
|
||||
WORequest+SOGo.h \
|
||||
|
@ -121,9 +118,6 @@ SOGo_OBJC_FILES = \
|
|||
SOGoGroup.m \
|
||||
SOGoUser.m \
|
||||
\
|
||||
NSDictionary+BSJSONAdditions.m \
|
||||
NSScanner+BSJSONAdditions.m \
|
||||
\
|
||||
DOMNode+SOGo.m \
|
||||
\
|
||||
WORequest+SOGo.m \
|
||||
|
|
|
@ -13,14 +13,14 @@ SOGo_LIBRARIES_DEPEND_UPON += \
|
|||
-L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-lmemcached \
|
||||
-lOGoContentStore \
|
||||
-lGDLAccess \
|
||||
-lNGObjWeb \
|
||||
-lNGCards \
|
||||
-lNGMime \
|
||||
-lNGStreams -lNGExtensions -lEOControl \
|
||||
-lXmlRpc -lDOM -lSaxObjC -lcrypt \
|
||||
-lNGLdap
|
||||
-lNGLdap -lSBJson \
|
||||
-lGDLContentStore
|
||||
|
||||
ADDITIONAL_TOOL_LIBS += \
|
||||
-L$(GNUSTEP_OBJ_DIR)/ \
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// BSJSONAdditions
|
||||
//
|
||||
// Created by Blake Seely on 2/1/06.
|
||||
// Copyright 2006 Blake Seely - http://www.blakeseely.com All rights reserved.
|
||||
// Permission to use this code:
|
||||
//
|
||||
// Feel free to use this code in your software, either as-is or
|
||||
// in a modified form. Either way, please include a credit in
|
||||
// your software's "About" box or similar, mentioning at least
|
||||
// my name (Blake Seely).
|
||||
//
|
||||
// Permission to redistribute this code:
|
||||
//
|
||||
// You can redistribute this code, as long as you keep these
|
||||
// comments. You can also redistribute modified versions of the
|
||||
// code, as long as you add comments to say that you've made
|
||||
// modifications (keeping these original comments too).
|
||||
//
|
||||
// If you do use or redistribute this code, an email would be
|
||||
// appreciated, just to let me know that people are finding my
|
||||
// code useful. You can reach me at blakeseely@mac.com
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
extern NSString *jsonIndentString;
|
||||
extern const int jsonDoNotIndent;
|
||||
|
||||
@interface NSDictionary (BSJSONAdditions)
|
||||
|
||||
- (NSString *)jsonStringValue;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSDictionary (PrivateBSJSONAdditions)
|
||||
|
||||
- (NSString *)jsonStringValueWithIndentLevel:(int)level;
|
||||
- (NSString *)jsonStringForValue:(id)value withIndentLevel:(int)level;
|
||||
- (NSString *)jsonStringForArray:(NSArray *)array withIndentLevel:(int)level;
|
||||
- (NSString *)jsonStringForString:(NSString *)string;
|
||||
- (NSString *)jsonIndentStringForLevel:(int)level;
|
||||
|
||||
@end
|
|
@ -1,190 +0,0 @@
|
|||
//
|
||||
// BSJSONAdditions
|
||||
//
|
||||
// Created by Blake Seely on 2/1/06.
|
||||
// Copyright 2006 Blake Seely - http://www.blakeseely.com All rights reserved.
|
||||
// Permission to use this code:
|
||||
//
|
||||
// Feel free to use this code in your software, either as-is or
|
||||
// in a modified form. Either way, please include a credit in
|
||||
// your software's "About" box or similar, mentioning at least
|
||||
// my name (Blake Seely).
|
||||
//
|
||||
// Permission to redistribute this code:
|
||||
//
|
||||
// You can redistribute this code, as long as you keep these
|
||||
// comments. You can also redistribute modified versions of the
|
||||
// code, as long as you add comments to say that you've made
|
||||
// modifications (keeping these original comments too).
|
||||
//
|
||||
// If you do use or redistribute this code, an email would be
|
||||
// appreciated, just to let me know that people are finding my
|
||||
// code useful. You can reach me at blakeseely@mac.com
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDecimalNumber.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSScanner+BSJSONAdditions.h"
|
||||
|
||||
NSString *jsonIndentString = @"\t"; // Modify this string to change how the output formats.
|
||||
const int jsonDoNotIndent = -1;
|
||||
|
||||
@implementation NSDictionary (BSJSONAdditions)
|
||||
|
||||
- (NSString *)jsonStringValue
|
||||
{
|
||||
return [self jsonStringValueWithIndentLevel:0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSDictionary (PrivateBSJSONAdditions)
|
||||
|
||||
- (NSString *)jsonStringValueWithIndentLevel:(int)level
|
||||
{
|
||||
NSMutableString *jsonString = [[NSMutableString alloc] init];
|
||||
[jsonString appendString:jsonObjectStartString];
|
||||
|
||||
NSEnumerator *keyEnum = [self keyEnumerator];
|
||||
NSString *keyString = [keyEnum nextObject];
|
||||
NSString *valueString;
|
||||
if (keyString != nil) {
|
||||
valueString = [self jsonStringForValue:[self objectForKey:keyString] withIndentLevel:level];
|
||||
if (level != jsonDoNotIndent) { // indent before each key
|
||||
[jsonString appendString:[self jsonIndentStringForLevel:level]];
|
||||
}
|
||||
[jsonString appendFormat:@" %@ %@ %@", [self jsonStringForString:keyString], jsonKeyValueSeparatorString, valueString];
|
||||
}
|
||||
|
||||
while ((keyString = [keyEnum nextObject])) {
|
||||
valueString = [self jsonStringForValue:[self objectForKey:keyString] withIndentLevel:level]; // TODO bail if valueString is nil? How to bail successfully from here?
|
||||
[jsonString appendString:jsonValueSeparatorString];
|
||||
if (level != jsonDoNotIndent) { // indent before each key
|
||||
[jsonString appendFormat:@"%@", [self jsonIndentStringForLevel:level]];
|
||||
}
|
||||
[jsonString appendFormat:@" %@ %@ %@", [self jsonStringForString:keyString], jsonKeyValueSeparatorString, valueString];
|
||||
}
|
||||
|
||||
//[jsonString appendString:@"\n"];
|
||||
[jsonString appendString:jsonObjectEndString];
|
||||
|
||||
return [jsonString autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)jsonStringForValue:(id)value withIndentLevel:(int)level
|
||||
{
|
||||
NSString *jsonString;
|
||||
if ([value respondsToSelector:@selector(characterAtIndex:)]) // String
|
||||
jsonString = [self jsonStringForString:(NSString *)value];
|
||||
else if ([value respondsToSelector:@selector(keyEnumerator)]) // Dictionary
|
||||
jsonString = [(NSDictionary *)value jsonStringValueWithIndentLevel:(level + 1)];
|
||||
else if ([value respondsToSelector:@selector(objectAtIndex:)]) // Array
|
||||
jsonString = [self jsonStringForArray:(NSArray *)value withIndentLevel:level];
|
||||
else if (value == [NSNull null]) // null
|
||||
jsonString = jsonNullString;
|
||||
else if ([value respondsToSelector:@selector(objCType)]) { // NSNumber - representing true, false, and any form of numeric
|
||||
NSNumber *number = (NSNumber *)value;
|
||||
const char *t = [number objCType];
|
||||
if (((*t == 'c') || *t == 'C') && ([number boolValue] == YES)) // true
|
||||
jsonString = jsonTrueString;
|
||||
else if (((*t == 'c') || *t == 'C') && ([number boolValue] == NO)) // false
|
||||
jsonString = jsonFalseString;
|
||||
else // attempt to handle as a decimal number - int, fractional, exponential
|
||||
// TODO: values converted from exponential json to dict and back to json do not format as exponential again
|
||||
jsonString = [[NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]] stringValue];
|
||||
} else {
|
||||
// TODO: error condition - it's not any of the types that I know about.
|
||||
return nil;
|
||||
}
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
- (NSString *)jsonStringForArray:(NSArray *)array withIndentLevel:(int)level
|
||||
{
|
||||
NSMutableString *jsonString = [[NSMutableString alloc] init];
|
||||
[jsonString appendString:jsonArrayStartString];
|
||||
|
||||
if ([array count] > 0) {
|
||||
[jsonString appendString:[self jsonStringForValue:[array objectAtIndex:0] withIndentLevel:level]];
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 1; i < [array count]; i++) {
|
||||
[jsonString appendFormat:@"%@ %@", jsonValueSeparatorString, [self jsonStringForValue:[array objectAtIndex:i] withIndentLevel:level]];
|
||||
}
|
||||
|
||||
[jsonString appendString:jsonArrayEndString];
|
||||
return [jsonString autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)jsonStringForString:(NSString *)string
|
||||
{
|
||||
NSMutableString *jsonString = [[NSMutableString alloc] init];
|
||||
[jsonString appendString:jsonStringDelimiterString];
|
||||
|
||||
// Build the result one character at a time, inserting escaped characters as necessary
|
||||
int i;
|
||||
unichar nextChar;
|
||||
for (i = 0; i < [string length]; i++) {
|
||||
nextChar = [string characterAtIndex:i];
|
||||
switch (nextChar) {
|
||||
case '\"':
|
||||
[jsonString appendString:@"\\\""];
|
||||
break;
|
||||
case '\\':
|
||||
[jsonString appendString:@"\\\\"];
|
||||
break;
|
||||
/* TODO: email out to json group on this - spec says to handlt his, examples and example code don't handle this.
|
||||
case '\/':
|
||||
[jsonString appendString:@"\\/"];
|
||||
break;
|
||||
*/
|
||||
case '\b':
|
||||
[jsonString appendString:@"\\b"];
|
||||
break;
|
||||
case '\f':
|
||||
[jsonString appendString:@"\\f"];
|
||||
break;
|
||||
case '\n':
|
||||
[jsonString appendString:@"\\n"];
|
||||
break;
|
||||
case '\r':
|
||||
[jsonString appendString:@"\\r"];
|
||||
break;
|
||||
case '\t':
|
||||
[jsonString appendString:@"\\t"];
|
||||
break;
|
||||
/* TODO: Find and encode unicode characters here?
|
||||
case '\u':
|
||||
[jsonString appendString:@"\\n"];
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
[jsonString appendString:[NSString stringWithCharacters:&nextChar length:1]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
[jsonString appendString:jsonStringDelimiterString];
|
||||
return [jsonString autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)jsonIndentStringForLevel:(int)level
|
||||
{
|
||||
NSMutableString *indentString = [[NSMutableString alloc] init];
|
||||
if (level != jsonDoNotIndent) {
|
||||
[indentString appendString:@"\n"];
|
||||
int i;
|
||||
for (i = 0; i < level; i++) {
|
||||
[indentString appendString:jsonIndentString];
|
||||
}
|
||||
}
|
||||
|
||||
return [indentString autorelease];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// BSJSONAdditions
|
||||
//
|
||||
// Created by Blake Seely on 2/1/06.
|
||||
// Copyright 2006 Blake Seely - http://www.blakeseely.com All rights reserved.
|
||||
// Permission to use this code:
|
||||
//
|
||||
// Feel free to use this code in your software, either as-is or
|
||||
// in a modified form. Either way, please include a credit in
|
||||
// your software's "About" box or similar, mentioning at least
|
||||
// my name (Blake Seely).
|
||||
//
|
||||
// Permission to redistribute this code:
|
||||
//
|
||||
// You can redistribute this code, as long as you keep these
|
||||
// comments. You can also redistribute modified versions of the
|
||||
// code, as long as you add comments to say that you've made
|
||||
// modifications (keeping these original comments too).
|
||||
//
|
||||
// If you do use or redistribute this code, an email would be
|
||||
// appreciated, just to let me know that people are finding my
|
||||
// code useful. You can reach me at blakeseely@mac.com
|
||||
//
|
||||
|
||||
#import <Foundation/NSScanner.h>
|
||||
|
||||
@class NSArray;
|
||||
@class NSDictionary;
|
||||
@class NSNumber;
|
||||
@class NSString;
|
||||
|
||||
extern NSString *jsonObjectStartString;
|
||||
extern NSString *jsonObjectEndString;
|
||||
extern NSString *jsonArrayStartString;
|
||||
extern NSString *jsonArrayEndString;
|
||||
extern NSString *jsonKeyValueSeparatorString;
|
||||
extern NSString *jsonValueSeparatorString;
|
||||
extern NSString *jsonStringDelimiterString;
|
||||
extern NSString *jsonStringEscapedDoubleQuoteString;
|
||||
extern NSString *jsonStringEscapedSlashString;
|
||||
extern NSString *jsonTrueString;
|
||||
extern NSString *jsonFalseString;
|
||||
extern NSString *jsonNullString;
|
||||
|
||||
|
||||
@interface NSScanner (PrivateBSJSONAdditions)
|
||||
|
||||
- (BOOL)scanJSONObject:(NSMutableDictionary **)dictionary;
|
||||
- (BOOL)scanJSONArray:(NSArray **)array;
|
||||
- (BOOL)scanJSONString:(NSString **)string;
|
||||
- (BOOL)scanJSONValue:(id *)value;
|
||||
- (BOOL)scanJSONNumber:(NSNumber **)number;
|
||||
|
||||
- (BOOL)scanJSONWhiteSpace;
|
||||
- (BOOL)scanJSONKeyValueSeparator;
|
||||
- (BOOL)scanJSONValueSeparator;
|
||||
- (BOOL)scanJSONObjectStartString;
|
||||
- (BOOL)scanJSONObjectEndString;
|
||||
- (BOOL)scanJSONArrayStartString;
|
||||
- (BOOL)scanJSONArrayEndString;
|
||||
- (BOOL)scanJSONArrayEndString;
|
||||
- (BOOL)scanJSONStringDelimiterString;
|
||||
|
||||
@end
|
|
@ -1,347 +0,0 @@
|
|||
//
|
||||
// BSJSONAdditions
|
||||
//
|
||||
// Created by Blake Seely on 2/1/06.
|
||||
// Copyright 2006 Blake Seely - http://www.blakeseely.com All rights reserved.
|
||||
// Permission to use this code:
|
||||
//
|
||||
// Feel free to use this code in your software, either as-is or
|
||||
// in a modified form. Either way, please include a credit in
|
||||
// your software's "About" box or similar, mentioning at least
|
||||
// my name (Blake Seely).
|
||||
//
|
||||
// Permission to redistribute this code:
|
||||
//
|
||||
// You can redistribute this code, as long as you keep these
|
||||
// comments. You can also redistribute modified versions of the
|
||||
// code, as long as you add comments to say that you've made
|
||||
// modifications (keeping these original comments too).
|
||||
//
|
||||
// If you do use or redistribute this code, an email would be
|
||||
// appreciated, just to let me know that people are finding my
|
||||
// code useful. You can reach me at blakeseely@mac.com
|
||||
//
|
||||
//
|
||||
// Version 1.2: Includes modifications by Bill Garrison: http://www.standardorbit.com , which included
|
||||
// Unit Tests adapted from Jonathan Wight's CocoaJSON code: http://www.toxicsoftware.com
|
||||
// I have included those adapted unit tests in this package.
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDecimalNumber.h>
|
||||
#import <Foundation/NSNull.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import "NSScanner+BSJSONAdditions.h"
|
||||
|
||||
NSString *jsonObjectStartString = @"{";
|
||||
NSString *jsonObjectEndString = @"}";
|
||||
NSString *jsonArrayStartString = @"[";
|
||||
NSString *jsonArrayEndString = @"]";
|
||||
NSString *jsonKeyValueSeparatorString = @":";
|
||||
NSString *jsonValueSeparatorString = @",";
|
||||
NSString *jsonStringDelimiterString = @"\"";
|
||||
NSString *jsonStringEscapedDoubleQuoteString = @"\\\"";
|
||||
NSString *jsonStringEscapedSlashString = @"\\\\";
|
||||
NSString *jsonTrueString = @"true";
|
||||
NSString *jsonFalseString = @"false";
|
||||
NSString *jsonNullString = @"null";
|
||||
|
||||
@implementation NSScanner (PrivateBSJSONAdditions)
|
||||
|
||||
- (BOOL)scanJSONObject:(NSMutableDictionary **)dictionary
|
||||
{
|
||||
//[self setCharactersToBeSkipped:nil];
|
||||
|
||||
BOOL result = NO;
|
||||
|
||||
/* START - April 21, 2006 - Updated to bypass irrelevant characters at the beginning of a JSON string */
|
||||
NSString *ignoredString;
|
||||
[self scanUpToString:jsonObjectStartString intoString:&ignoredString];
|
||||
/* END - April 21, 2006 */
|
||||
|
||||
if (![self scanJSONObjectStartString]) {
|
||||
// TODO: Error condition. For now, return false result, do nothing with the dictionary handle
|
||||
} else {
|
||||
NSMutableDictionary *jsonKeyValues = [[[NSMutableDictionary alloc] init] autorelease];
|
||||
NSString *key = nil;
|
||||
id value;
|
||||
[self scanJSONWhiteSpace];
|
||||
while (([self scanJSONString:&key]) && ([self scanJSONKeyValueSeparator]) && ([self scanJSONValue:&value])) {
|
||||
[jsonKeyValues setObject:value forKey:key];
|
||||
[self scanJSONWhiteSpace];
|
||||
// check to see if the character at scan location is a value separator. If it is, do nothing.
|
||||
if ([[[self string] substringWithRange:NSMakeRange([self scanLocation], 1)] isEqualToString:jsonValueSeparatorString]) {
|
||||
[self scanJSONValueSeparator];
|
||||
}
|
||||
}
|
||||
if ([self scanJSONObjectEndString]) {
|
||||
// whether or not we found a key-val pair, we found open and close brackets - completing an object
|
||||
result = YES;
|
||||
*dictionary = jsonKeyValues;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONArray:(NSArray **)array
|
||||
{
|
||||
BOOL result = NO;
|
||||
NSMutableArray *values = [[[NSMutableArray alloc] init] autorelease];
|
||||
[self scanJSONArrayStartString];
|
||||
id value = nil;
|
||||
|
||||
while ([self scanJSONValue:&value]) {
|
||||
[values addObject:value];
|
||||
[self scanJSONWhiteSpace];
|
||||
if ([[[self string] substringWithRange:NSMakeRange([self scanLocation], 1)] isEqualToString:jsonValueSeparatorString]) {
|
||||
[self scanJSONValueSeparator];
|
||||
}
|
||||
}
|
||||
if ([self scanJSONArrayEndString]) {
|
||||
result = YES;
|
||||
*array = values;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONString:(NSString **)string
|
||||
{
|
||||
BOOL result = NO;
|
||||
if ([self scanJSONStringDelimiterString]) {
|
||||
NSMutableString *chars = [[[NSMutableString alloc] init] autorelease];
|
||||
NSString *characterFormat = @"%C";
|
||||
|
||||
// process character by character until we finish the string or reach another double-quote
|
||||
while ((![self isAtEnd]) && ([[self string] characterAtIndex:[self scanLocation]] != '\"')) {
|
||||
unichar currentChar = [[self string] characterAtIndex:[self scanLocation]];
|
||||
unichar nextChar;
|
||||
if (currentChar != '\\') {
|
||||
[chars appendFormat:characterFormat, currentChar];
|
||||
[self setScanLocation:([self scanLocation] + 1)];
|
||||
} else {
|
||||
nextChar = [[self string] characterAtIndex:([self scanLocation] + 1)];
|
||||
switch (nextChar) {
|
||||
case '\"':
|
||||
[chars appendString:@"\""];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case '\\':
|
||||
[chars appendString:@"\\"]; // debugger shows result as having two slashes, but final output is correct. Possible debugger error?
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
/* TODO: json.org docs mention this seq, so does yahoo, but not recognized here by xcode, note from crockford: not a required escape
|
||||
case '\/':
|
||||
[chars appendString:@"\/"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
*/
|
||||
case 'b':
|
||||
[chars appendString:@"\b"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case 'f':
|
||||
[chars appendString:@"\f"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case 'n':
|
||||
[chars appendString:@"\n"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case 'r':
|
||||
[chars appendString:@"\r"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case 't':
|
||||
[chars appendString:@"\t"];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
case 'u': // unicode sequence - get string of hex chars, convert to int, convert to unichar, append
|
||||
[self setScanLocation:([self scanLocation] + 2)]; // advance past '\u'
|
||||
NSString *digits = [[self string] substringWithRange:NSMakeRange([self scanLocation], 4)];
|
||||
/* START Updated code modified from code fix submitted by Bill Garrison - March 28, 2006 - http://www.standardorbit.net */
|
||||
NSScanner *hexScanner = [NSScanner scannerWithString:digits];
|
||||
NSString *verifiedHexDigits;
|
||||
NSCharacterSet *hexDigitSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
|
||||
if (NO == [hexScanner scanCharactersFromSet:hexDigitSet intoString:&verifiedHexDigits])
|
||||
return NO;
|
||||
if (4 != [verifiedHexDigits length])
|
||||
return NO;
|
||||
|
||||
// Read in the hex value
|
||||
[hexScanner setScanLocation:0];
|
||||
unsigned unicodeHexValue;
|
||||
if (NO == [hexScanner scanHexInt:&unicodeHexValue]) {
|
||||
return NO;
|
||||
}
|
||||
[chars appendFormat:characterFormat, unicodeHexValue];
|
||||
/* END update - March 28, 2006 */
|
||||
[self setScanLocation:([self scanLocation] + 4)];
|
||||
break;
|
||||
default:
|
||||
[chars appendFormat:@"\\%C", nextChar];
|
||||
[self setScanLocation:([self scanLocation] + 2)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (![self isAtEnd]) {
|
||||
result = [self scanJSONStringDelimiterString];
|
||||
*string = chars;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
/* this code is more appropriate if you have a separate method to unescape the found string
|
||||
for example, between inputting json and outputting it, it may make more sense to have a category on NSString to perform
|
||||
escaping and unescaping. Keeping this code and looking into this for a future update.
|
||||
unsigned int searchLength = [[self string] length] - [self scanLocation];
|
||||
unsigned int quoteLocation = [[self string] rangeOfString:jsonStringDelimiterString options:0 range:NSMakeRange([self scanLocation], searchLength)].location;
|
||||
searchLength = [[self string] length] - quoteLocation;
|
||||
while (([[[self string] substringWithRange:NSMakeRange((quoteLocation - 1), 2)] isEqualToString:jsonStringEscapedDoubleQuoteString]) &&
|
||||
(quoteLocation != NSNotFound) &&
|
||||
(![[[self string] substringWithRange:NSMakeRange((quoteLocation -2), 2)] isEqualToString:jsonStringEscapedSlashString])){
|
||||
searchLength = [[self string] length] - (quoteLocation + 1);
|
||||
quoteLocation = [[self string] rangeOfString:jsonStringDelimiterString options:0 range:NSMakeRange((quoteLocation + 1), searchLength)].location;
|
||||
}
|
||||
|
||||
*string = [[self string] substringWithRange:NSMakeRange([self scanLocation], (quoteLocation - [self scanLocation]))];
|
||||
// TODO: process escape sequences out of the string - replacing with their actual characters. a function that does just this belongs
|
||||
// in another class. So it may make more sense to change this whole implementation to just go character by character instead.
|
||||
[self setScanLocation:(quoteLocation + 1)];
|
||||
*/
|
||||
result = YES;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONValue:(id *)value
|
||||
{
|
||||
BOOL result = NO;
|
||||
|
||||
[self scanJSONWhiteSpace];
|
||||
NSString *substring = [[self string] substringWithRange:NSMakeRange([self scanLocation], 1)];
|
||||
NSUInteger trueLocation = [[self string] rangeOfString:jsonTrueString options:0 range:NSMakeRange([self scanLocation], ([[self string] length] - [self scanLocation]))].location;
|
||||
NSUInteger falseLocation = [[self string] rangeOfString:jsonFalseString options:0 range:NSMakeRange([self scanLocation], ([[self string] length] - [self scanLocation]))].location;
|
||||
NSUInteger nullLocation = [[self string] rangeOfString:jsonNullString options:0 range:NSMakeRange([self scanLocation], ([[self string] length] - [self scanLocation]))].location;
|
||||
|
||||
if ([substring isEqualToString:jsonStringDelimiterString]) {
|
||||
result = [self scanJSONString:value];
|
||||
} else if ([substring isEqualToString:jsonObjectStartString]) {
|
||||
result = [self scanJSONObject:value];
|
||||
} else if ([substring isEqualToString:jsonArrayStartString]) {
|
||||
result = [self scanJSONArray:value];
|
||||
} else if ([self scanLocation] == trueLocation) {
|
||||
result = YES;
|
||||
*value = [NSNumber numberWithBool:YES];
|
||||
[self setScanLocation:([self scanLocation] + [jsonTrueString length])];
|
||||
} else if ([self scanLocation] == falseLocation) {
|
||||
result = YES;
|
||||
*value = [NSNumber numberWithBool:NO];
|
||||
[self setScanLocation:([self scanLocation] + [jsonFalseString length])];
|
||||
} else if ([self scanLocation] == nullLocation) {
|
||||
result = YES;
|
||||
*value = [NSNull null];
|
||||
[self setScanLocation:([self scanLocation] + [jsonNullString length])];
|
||||
} else if (([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[[self string] characterAtIndex:[self scanLocation]]]) ||
|
||||
([[self string] characterAtIndex:[self scanLocation]] == '-')){ // check to make sure it's a digit or -
|
||||
result = [self scanJSONNumber:value];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONNumber:(NSNumber **)number
|
||||
{
|
||||
//NSDecimal decimal;
|
||||
//BOOL result = [self scanDecimal:&decimal];
|
||||
//*number = [NSDecimalNumber decimalNumberWithDecimal:decimal];
|
||||
long long intValue, commaValue;
|
||||
double doubleValue;
|
||||
NSUInteger curLocation, length;
|
||||
NSString *stringValue;
|
||||
|
||||
stringValue = [self string];
|
||||
|
||||
BOOL result = [self scanLongLong: &intValue];
|
||||
if (result)
|
||||
{
|
||||
curLocation = [self scanLocation];
|
||||
if (curLocation < [stringValue length]
|
||||
&& [stringValue characterAtIndex: curLocation] == '.')
|
||||
{
|
||||
[self scanString: @"." intoString: NULL];
|
||||
if ([self scanLongLong: &commaValue])
|
||||
{
|
||||
result = YES;
|
||||
length = [self scanLocation] - curLocation - 1;
|
||||
if (intValue < 0)
|
||||
commaValue = -commaValue;
|
||||
doubleValue = intValue + commaValue / pow(10.0, length);
|
||||
*number = [NSNumber numberWithDouble: doubleValue];
|
||||
}
|
||||
else
|
||||
*number = [NSNumber numberWithLongLong: intValue];
|
||||
}
|
||||
else
|
||||
*number = [NSNumber numberWithLongLong: intValue];
|
||||
}
|
||||
else
|
||||
*number = nil;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONWhiteSpace
|
||||
{
|
||||
//NSLog(@"Scanning white space - here are the next ten chars ---%@---", [[self string] substringWithRange:NSMakeRange([self scanLocation], 10)]);
|
||||
BOOL result = NO;
|
||||
NSCharacterSet *space = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
||||
while ([space characterIsMember:[[self string] characterAtIndex:[self scanLocation]]]) {
|
||||
[self setScanLocation:([self scanLocation] + 1)];
|
||||
result = YES;
|
||||
}
|
||||
//NSLog(@"Done Scanning white space - here are the next ten chars ---%@---", [[self string] substringWithRange:NSMakeRange([self scanLocation], 10)]);
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONKeyValueSeparator
|
||||
{
|
||||
return [self scanString:jsonKeyValueSeparatorString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONValueSeparator
|
||||
{
|
||||
return [self scanString:jsonValueSeparatorString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONObjectStartString
|
||||
{
|
||||
return [self scanString:jsonObjectStartString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONObjectEndString
|
||||
{
|
||||
return [self scanString:jsonObjectEndString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONArrayStartString
|
||||
{
|
||||
return [self scanString:jsonArrayStartString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONArrayEndString
|
||||
{
|
||||
return [self scanString:jsonArrayEndString intoString:NULL];
|
||||
}
|
||||
|
||||
- (BOOL)scanJSONStringDelimiterString;
|
||||
{
|
||||
return [self scanString:jsonStringDelimiterString intoString:NULL];
|
||||
}
|
||||
|
||||
@end
|
|
@ -29,12 +29,13 @@
|
|||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
#import <NGExtensions/NSDictionary+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGMime/NGMimeHeaderFieldGenerator.h>
|
||||
#import <SBJson/SBJsonParser.h>
|
||||
|
||||
#import "NSArray+Utilities.h"
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSDictionary+URL.h"
|
||||
#import "NSScanner+BSJSONAdditions.h"
|
||||
|
||||
#import "NSString+Utilities.h"
|
||||
|
||||
|
@ -513,21 +514,28 @@ static NSMutableCharacterSet *safeLDIFStartChars = nil;
|
|||
|
||||
- (id) objectFromJSONString
|
||||
{
|
||||
NSScanner *scanner;
|
||||
SBJsonParser *parser;
|
||||
NSObject *object;
|
||||
NSError *error;
|
||||
|
||||
object = nil;
|
||||
|
||||
if ([self length] > 0)
|
||||
{
|
||||
scanner = [[NSScanner alloc] initWithString: self];
|
||||
if (![scanner scanJSONValue: &object])
|
||||
object = nil;
|
||||
[scanner autorelease];
|
||||
parser = [SBJsonParser new];
|
||||
[parser autorelease];
|
||||
error = nil;
|
||||
object = [parser objectWithString: self
|
||||
error: &error];
|
||||
if (error)
|
||||
{
|
||||
[self errorWithFormat: @"json parser: %@", error];
|
||||
[self errorWithFormat: @"original string is: %@", self];
|
||||
object = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSDictionary+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoObject.h"
|
||||
|
@ -167,7 +167,7 @@
|
|||
[sessionDict setObject: identifier forKey: @"identifier"];
|
||||
if ([proxyTickets count])
|
||||
[sessionDict setObject: proxyTickets forKey: @"proxyTickets"];
|
||||
jsonSession = [sessionDict jsonStringValue];
|
||||
jsonSession = [sessionDict jsonRepresentation];
|
||||
[cache setCASSession: jsonSession
|
||||
withTicket: ticket
|
||||
forIdentifier: identifier];
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
#import <NGObjWeb/SoObject.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSDictionary+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoObject.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
@ -484,7 +484,7 @@ static memcached_st *handle = NULL;
|
|||
forPath: (NSString *) thePath
|
||||
{
|
||||
if (theACLs)
|
||||
[self _cacheValues: [theACLs jsonStringValue]
|
||||
[self _cacheValues: [theACLs jsonRepresentation]
|
||||
ofType: @"acl"
|
||||
forKey: thePath];
|
||||
else
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
- (NSString *) mailDomain;
|
||||
- (NSString *) imapServer;
|
||||
- (NSString *) sieveServer;
|
||||
- (NSString *) imapAclStyle;
|
||||
- (NSString *) imapFolderSeparator;
|
||||
- (BOOL) imapAclConformsToIMAPExt;
|
||||
|
|
|
@ -113,6 +113,11 @@
|
|||
return [self stringForKey: @"SOGoIMAPServer"];
|
||||
}
|
||||
|
||||
- (NSString *) sieveServer
|
||||
{
|
||||
return [self stringForKey: @"SOGoSieveServer"];
|
||||
}
|
||||
|
||||
#warning should be removed when we make use of imap namespace
|
||||
- (NSString *) imapAclStyle
|
||||
{
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import "NSArray+Utilities.h"
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "NSObject+Utilities.h"
|
||||
#import "SOGoDomainDefaults.h"
|
||||
#import "SOGoSource.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
@ -440,7 +440,7 @@
|
|||
// internal cache.
|
||||
[currentUser setObject: _pwd forKey: @"password"];
|
||||
[[SOGoCache sharedCache]
|
||||
setUserAttributes: [currentUser jsonStringValue]
|
||||
setUserAttributes: [currentUser jsonRepresentation]
|
||||
forLogin: _login];
|
||||
}
|
||||
else
|
||||
|
@ -481,7 +481,7 @@
|
|||
// internal cache.
|
||||
[currentUser setObject: newPassword forKey: @"password"];
|
||||
[[SOGoCache sharedCache]
|
||||
setUserAttributes: [currentUser jsonStringValue]
|
||||
setUserAttributes: [currentUser jsonRepresentation]
|
||||
forLogin: login];
|
||||
}
|
||||
else
|
||||
|
@ -595,13 +595,13 @@
|
|||
key = [newUser objectForKey: @"c_uid"];
|
||||
if (key)
|
||||
[[SOGoCache sharedCache]
|
||||
setUserAttributes: [newUser jsonStringValue]
|
||||
setUserAttributes: [newUser jsonRepresentation]
|
||||
forLogin: key];
|
||||
|
||||
emails = [[newUser objectForKey: @"emails"] objectEnumerator];
|
||||
while ((key = [emails nextObject]))
|
||||
[[SOGoCache sharedCache]
|
||||
setUserAttributes: [newUser jsonStringValue]
|
||||
setUserAttributes: [newUser jsonRepresentation]
|
||||
forLogin: key];
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSPropertyList.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
|
@ -30,7 +31,6 @@
|
|||
#import "NSObject+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
|
||||
#import "NSDictionary+BSJSONAdditions.h"
|
||||
#import "SOGoCache.h"
|
||||
|
||||
#import "SOGoUserProfile.h"
|
||||
|
@ -139,7 +139,7 @@
|
|||
{
|
||||
[self logWithFormat: @"database value for %@"
|
||||
@" (uid: '%@') is a plist", [self profileTypeName], uid];
|
||||
jsonValue = [plist jsonStringValue];
|
||||
jsonValue = [plist jsonRepresentation];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -223,7 +223,7 @@
|
|||
SOGoCache *cache;
|
||||
BOOL rc;
|
||||
|
||||
jsonRepresentation = [values jsonStringValue];
|
||||
jsonRepresentation = [values jsonRepresentation];
|
||||
if (jsonRepresentation)
|
||||
{
|
||||
rc = [self storeJSONProfileInDB: jsonRepresentation];
|
||||
|
|
|
@ -32,6 +32,6 @@ BUNDLE_LIBS += \
|
|||
-lNGObjWeb \
|
||||
-lNGCards -lNGMime -lNGLdap \
|
||||
-lNGStreams -lNGExtensions -lEOControl \
|
||||
-lXmlRpc -lDOM -lSaxObjC
|
||||
-lXmlRpc -lDOM -lSaxObjC -lSBJson
|
||||
|
||||
ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
|
||||
|
|
19
Tests/Integration/GNUmakefile
Normal file
19
Tests/Integration/GNUmakefile
Normal file
|
@ -0,0 +1,19 @@
|
|||
PROGRAM=teststrings
|
||||
|
||||
SRCS=teststrings.m
|
||||
OBJS=$(SRCS:.m=.o)
|
||||
|
||||
teststrings: $(OBJS)
|
||||
$(CC) -o $@ $< $(GNUSTEP_LIBRARIES_FLAGS) -lgnustep-base -L../../SoObjects/SOGo/SOGo.framework -lSOGo
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
all: $(PROGRAM)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAM) $(OBJS) *~
|
||||
|
||||
.m.o:
|
||||
$(CC) -o $@ -c $(GNUSTEP_HEADERS_FLAGS) $(AUXILIARY_OBJCFLAGS) $(OBJCFLAGS) $(CPPFLAGS) -I../../SoObjects $<
|
||||
|
||||
.SUFFIXES: .m .o
|
79
Tests/Integration/teststrings.m
Normal file
79
Tests/Integration/teststrings.m
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* teststrings.m - this file is part of SOGO
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
|
||||
static int
|
||||
performTest (char *filename)
|
||||
{
|
||||
NSDictionary *testDict;
|
||||
NSString *nsFilename;
|
||||
int rc;
|
||||
|
||||
nsFilename = [NSString stringWithFormat: @"%s", filename];
|
||||
NS_DURING
|
||||
{
|
||||
testDict = [NSDictionary dictionaryFromStringsFile: nsFilename];
|
||||
if ([testDict count] == 0)
|
||||
{
|
||||
NSLog (@"Bad or empty strings file");
|
||||
rc = 2;
|
||||
testDict = nil;
|
||||
}
|
||||
else
|
||||
rc = 0;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
NSLog (@"An exception was caught: %@", localException);
|
||||
rc = 1;
|
||||
testDict = nil;
|
||||
}
|
||||
NS_ENDHANDLER;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
int rc;
|
||||
|
||||
pool = [NSAutoreleasePool new];
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
rc = performTest (argv[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog (@"Usage: %s file.strings", argv[0]);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
|
||||
return rc;
|
||||
}
|
20
Tests/Integration/teststrings.sh
Executable file
20
Tests/Integration/teststrings.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
|
||||
TOPDIR=../..
|
||||
|
||||
if [ ! -f teststrings ]
|
||||
then
|
||||
make teststrings
|
||||
fi
|
||||
|
||||
for stringfile in ${TOPDIR}/*/*/*.lproj/Localizable.strings
|
||||
do
|
||||
./teststrings "$stringfile" > /dev/null
|
||||
code=$?
|
||||
if test $code -eq 0;
|
||||
then
|
||||
echo "$stringfile: passed";
|
||||
else
|
||||
echo "$stringfile: failed (code: $code)";
|
||||
fi
|
||||
done
|
|
@ -15,6 +15,7 @@ ContactsUI_OBJC_FILES = \
|
|||
\
|
||||
ContactsUIProduct.m \
|
||||
UIxContactsFilterPanel.m \
|
||||
UIxContactActions.m \
|
||||
UIxContactView.m \
|
||||
UIxContactEditor.m \
|
||||
UIxListView.m \
|
||||
|
|
131
UI/Contacts/UIxContactActions.m
Normal file
131
UI/Contacts/UIxContactActions.m
Normal file
|
@ -0,0 +1,131 @@
|
|||
/* UIxContactActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGObjWeb/WODirectAction.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
|
||||
#import <NGCards/NGVCard.h>
|
||||
|
||||
#import <Contacts/SOGoContactGCSEntry.h>
|
||||
|
||||
#import <Common/WODirectAction+SOGo.h>
|
||||
|
||||
@interface NGVCard (SOGoActionCategory)
|
||||
|
||||
- (BOOL) addOrRemove: (BOOL) set
|
||||
category: (NSString *) newCategory;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NGVCard (SOGoActionCategory)
|
||||
|
||||
- (BOOL) addOrRemove: (BOOL) set
|
||||
category: (NSString *) category
|
||||
{
|
||||
NSMutableArray *categories;
|
||||
BOOL modified;
|
||||
NSUInteger idx;
|
||||
|
||||
modified = NO;
|
||||
|
||||
categories = [[self categories] mutableCopy];
|
||||
[categories autorelease];
|
||||
if (set)
|
||||
{
|
||||
if (![categories containsObject: category])
|
||||
{
|
||||
[categories addObject: category];
|
||||
modified = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = [categories indexOfObject: category];
|
||||
if (idx != NSNotFound)
|
||||
{
|
||||
[categories removeObjectAtIndex: idx];
|
||||
modified = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified)
|
||||
[self setCategories: categories];
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface UIxContactActions : WODirectAction
|
||||
|
||||
- (WOResponse *) setCategoryAction;
|
||||
- (WOResponse *) unsetCategoryAction;
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIxContactActions
|
||||
|
||||
- (WOResponse *) _setOrUnsetCategoryAction: (BOOL) set
|
||||
{
|
||||
SOGoContactGCSEntry *contact;
|
||||
NSString *category;
|
||||
WORequest *rq;
|
||||
WOResponse *response;
|
||||
NGVCard *card;
|
||||
|
||||
rq = [context request];
|
||||
category = [rq formValueForKey: @"category"];
|
||||
if ([category length] > 0)
|
||||
{
|
||||
contact = [self clientObject];
|
||||
if (![contact isNew])
|
||||
{
|
||||
card = [contact vCard];
|
||||
if ([card addOrRemove: set category: category])
|
||||
[contact save];
|
||||
response = [self responseWith204];
|
||||
}
|
||||
else
|
||||
response = [self responseWithStatus: 404
|
||||
andString: @"Contact does not exist"];
|
||||
}
|
||||
else
|
||||
response = [self responseWithStatus: 403
|
||||
andString: @"Missing 'category' parameter"];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) setCategoryAction
|
||||
{
|
||||
return [self _setOrUnsetCategoryAction: YES];
|
||||
}
|
||||
|
||||
- (WOResponse *) unsetCategoryAction
|
||||
{
|
||||
return [self _setOrUnsetCategoryAction: NO];
|
||||
}
|
||||
|
||||
@end
|
|
@ -184,6 +184,16 @@
|
|||
pageName = "UIxContactEditor";
|
||||
actionName = "write";
|
||||
};
|
||||
setCategory = {
|
||||
protectedBy = "Change Images And Files";
|
||||
actionClass = "UIxContactActions";
|
||||
actionName = "setCategory";
|
||||
};
|
||||
unsetCategory = {
|
||||
protectedBy = "Change Images And Files";
|
||||
actionClass = "UIxContactActions";
|
||||
actionName = "unsetCategory";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxMailPartHTMLViewer.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2009 Inverse inc.
|
||||
* Copyright (C) 2007-2010 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
*
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSDictionary+BSJSONAdditions.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
@ -659,9 +658,7 @@
|
|||
[capabilities retain];
|
||||
}
|
||||
|
||||
return [[NSDictionary dictionary]
|
||||
jsonStringForArray: capabilities
|
||||
withIndentLevel: 0];
|
||||
return [capabilities jsonRepresentation];
|
||||
}
|
||||
|
||||
- (BOOL) isVacationEnabled
|
||||
|
@ -683,9 +680,7 @@
|
|||
|
||||
- (NSString *) sieveFiltersValue
|
||||
{
|
||||
return [[NSDictionary dictionary]
|
||||
jsonStringForArray: sieveFilters
|
||||
withIndentLevel: 0];
|
||||
return [sieveFilters jsonRepresentation];
|
||||
}
|
||||
|
||||
- (void) setEnableVacation: (BOOL) enableVacation
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
xmlns:uix="OGo:uix"
|
||||
xmlns:label="OGo:label"
|
||||
xmlns:rsrc="OGo:url"
|
||||
const:userDefaultsKeys="SOGoContactsCategories"
|
||||
className="UIxPageFrame"
|
||||
title="name"
|
||||
var:popup="isPopup">
|
||||
|
@ -72,6 +73,7 @@
|
|||
<div class="menu" id="contactMenu">
|
||||
<ul>
|
||||
<li><var:string label:value="Properties" /></li>
|
||||
<li><var:string label:value="Categories" /></li>
|
||||
<li><!-- separator --></li>
|
||||
<li><var:string label:value="Write" /></li>
|
||||
<li><var:string label:value="Instant Message" /></li>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<tbody id="contactsListTbody">
|
||||
<var:foreach list="contactInfos" item="currentContact">
|
||||
<tr class="tableview"
|
||||
var:categories="currentContact.c_categories"
|
||||
var:id="currentContact.c_name"
|
||||
var:contactname="currentContact.c_cn">
|
||||
<td class="displayName"><var:string value="currentContact.c_cn" const:escapeHTML="YES" /></td>
|
||||
|
|
|
@ -1078,10 +1078,14 @@ function onContactMenuPrepareVisibility() {
|
|||
aim: false };
|
||||
|
||||
var elements = $(this).down("ul").childElements();
|
||||
var writeOption = elements[2];
|
||||
var aimOption = elements[3];
|
||||
var deleteOption = elements[5];
|
||||
var moveOption = elements[7];
|
||||
|
||||
var categoriesOption = elements[1];
|
||||
if (selectedFolder.getAttribute("owner") == UserLogin) {
|
||||
categoriesOption.removeClassName("disabled");
|
||||
}
|
||||
else {
|
||||
categoriesOption.addClassName("disabled");
|
||||
}
|
||||
|
||||
$A(contactRows).each(function(contactRow) {
|
||||
var cells = contactRow.getElementsByTagName('td');
|
||||
|
@ -1091,15 +1095,20 @@ function onContactMenuPrepareVisibility() {
|
|||
options.aim |= (aimCell.firstChild != null);
|
||||
});
|
||||
|
||||
var writeOption = elements[3];
|
||||
if (options.write)
|
||||
writeOption.removeClassName("disabled");
|
||||
else
|
||||
writeOption.addClassName("disabled");
|
||||
|
||||
var aimOption = elements[4];
|
||||
if (options.aim)
|
||||
aimOption.removeClassName("disabled");
|
||||
else
|
||||
aimOption.addClassName("disabled");
|
||||
|
||||
var deleteOption = elements[6];
|
||||
var moveOption = elements[8];
|
||||
if ($(selectedFolder).hasClassName("remote")) {
|
||||
// Remote address books are always read-only
|
||||
deleteOption.addClassName("disabled");
|
||||
|
@ -1120,7 +1129,9 @@ function getMenus() {
|
|||
onAddressBookRemove, "-",
|
||||
onAddressBookExport, onAddressBookImport, "-",
|
||||
onMenuSharing);
|
||||
menus["contactMenu"] = new Array(onMenuEditContact, "-",
|
||||
menus["contactMenu"] = new Array(onMenuEditContact,
|
||||
"categoriesMenu",
|
||||
"-",
|
||||
onMenuWriteToContact, onMenuAIMContact,
|
||||
"-", onMenuDeleteContact, "-",
|
||||
"moveContactMenu", "copyContactMenu",
|
||||
|
@ -1255,6 +1266,107 @@ function initContacts(event) {
|
|||
// Default sort options
|
||||
sorting["attribute"] = "c_cn";
|
||||
sorting["ascending"] = true;
|
||||
|
||||
resetCategoriesMenu();
|
||||
}
|
||||
|
||||
function resetCategoriesMenu() {
|
||||
var menu = $("categoriesMenu");
|
||||
if (menu) {
|
||||
menu.parentNode.removeChild(menu);
|
||||
}
|
||||
|
||||
menu = createElement("div", "categoriesMenu", "menu");
|
||||
var menuUL = createElement("ul", null, "choiceMenu");
|
||||
menu.appendChild(menuUL);
|
||||
if (UserDefaults && UserDefaults["SOGoContactsCategories"]) {
|
||||
for (var i = 0;
|
||||
i < UserDefaults["SOGoContactsCategories"].length;
|
||||
i++) {
|
||||
var catName = UserDefaults["SOGoContactsCategories"][i];
|
||||
if (catName.length > 0) {
|
||||
var menuLI = createElement("li");
|
||||
var bound = onCategoriesMenuItemClick.bindAsEventListener(menuLI);
|
||||
menuLI.observe("click", bound);
|
||||
menuLI.category = catName;
|
||||
menuLI.appendChild(document.createTextNode(catName));
|
||||
menuUL.appendChild(menuLI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu.prepareVisibility = onCategoriesMenuPrepareVisibility;
|
||||
|
||||
var pageContent = $("pageContent");
|
||||
pageContent.appendChild(menu);
|
||||
}
|
||||
|
||||
function onCategoriesMenuPrepareVisibility() {
|
||||
var rows = contactsList.getSelectedRows();
|
||||
if (rows.length > 0) {
|
||||
var catList = rows[0].getAttribute("categories");
|
||||
var catsArray;
|
||||
if (catList && catList.length > 0) {
|
||||
catsArray = catList.split(",");
|
||||
}
|
||||
else {
|
||||
catsArray = [];
|
||||
}
|
||||
|
||||
var menu = $("categoriesMenu");
|
||||
var ul = menu.down("ul");
|
||||
var listElements = ul.select("li");
|
||||
for (var i = 0; i < listElements.length; i++) {
|
||||
var li = listElements[i];
|
||||
if (catsArray.indexOf(li.category) > -1) {
|
||||
li.addClassName("_chosen");
|
||||
}
|
||||
else {
|
||||
li.removeClassName("_chosen");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCategoriesMenuItemClick() {
|
||||
var set = !this.hasClassName("_chosen");
|
||||
var method = (set ? "setCategory" : "unsetCategory");
|
||||
var contactsList = $("contactsList");
|
||||
var rowIds = contactsList.getSelectedRowsId();
|
||||
if (rowIds.length > 0) {
|
||||
log("coucou...");
|
||||
for (var i = 0; i < rowIds.length; i++) {
|
||||
var url = (URLForFolderID(Contact.currentAddressBook)
|
||||
+ "/" + rowIds[i] + "/" + method);
|
||||
url += "?category=" + escape(this.category);
|
||||
triggerAjaxRequest(url);
|
||||
if (set) {
|
||||
setCategoryOnNode($(rowIds[i]), this.category);
|
||||
}
|
||||
else {
|
||||
unsetCategoryOnNode($(rowIds[i]), this.category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setCategoryOnNode(contactNode, category) {
|
||||
var catList = contactNode.getAttribute("categories");
|
||||
var catsArray = catList.split(",");
|
||||
if (catsArray.indexOf(category) == -1) {
|
||||
catsArray.push(category);
|
||||
contactNode.setAttribute("categories", catsArray.join(","));
|
||||
}
|
||||
}
|
||||
|
||||
function unsetCategoryOnNode(contactNode, category) {
|
||||
var catList = contactNode.getAttribute("categories");
|
||||
var catsArray = catList.split(",");
|
||||
var catIdx = catsArray.indexOf(category);
|
||||
if (catsArray.indexOf(category) > -1) {
|
||||
catsArray.splice(catIdx, 1);
|
||||
contactNode.setAttribute("categories", catsArray.join(","));
|
||||
}
|
||||
}
|
||||
|
||||
function configureDraggables() {
|
||||
|
@ -1362,7 +1474,6 @@ function dropSelectedContacts (action, toId) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function onContactsReload () {
|
||||
openContactsFolder(Contact.currentAddressBook, true);
|
||||
}
|
||||
|
|
|
@ -270,8 +270,7 @@ function onCASRecoverIFrameLoaded(event) {
|
|||
1);
|
||||
}
|
||||
else {
|
||||
var logoffUrl = UserFolderURL + "logoff";
|
||||
window.location.href = logoffUrl;
|
||||
window.location.href = UserFolderURL;
|
||||
}
|
||||
this.request = null;
|
||||
}
|
||||
|
@ -287,7 +286,7 @@ function onAjaxRequestStateChange(http) {
|
|||
createCASRecoveryIFrame(http);
|
||||
}
|
||||
else if (activeAjaxRequests > 0) {
|
||||
if (!http.aborted)
|
||||
if (!http.aborted && http.callback)
|
||||
http.callback(http);
|
||||
activeAjaxRequests--;
|
||||
checkAjaxRequestsState();
|
||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -1,7 +1,7 @@
|
|||
Source: sogo
|
||||
Priority: optional
|
||||
Maintainer: Inverse Support <support@inverse.ca>
|
||||
Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libssl-dev
|
||||
Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev
|
||||
Section: web
|
||||
Standards-Version: 3.9.1
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ Group: Productivity/Groupware
|
|||
Source: SOGo-%{sogo_version}.tar.gz
|
||||
Prefix: %{sogo_prefix}
|
||||
AutoReqProv: off
|
||||
Requires: gnustep-base, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, memcached, libmemcached
|
||||
Requires: gnustep-base, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-sbjson, memcached, libmemcached
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
|
||||
BuildPreReq: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel libmemcached-devel
|
||||
BuildPreReq: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel sope%{sope_major_version}%{sope_minor_version}-sbjson-devel libmemcached-devel
|
||||
|
||||
%description
|
||||
SOGo is a groupware server built around OpenGroupware.org (OGo) and
|
||||
|
|
Loading…
Reference in a new issue