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:
Francis Lachapelle 2010-11-03 20:47:52 +00:00
commit 761462e26c
53 changed files with 560 additions and 767 deletions

View file

@ -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.

View file

@ -6,8 +6,8 @@ include $(GNUSTEP_MAKEFILES)/common.make
SUBPROJECTS = \
SOPE/NGCards \
SOPE/GDLContentStore \
OGoContentStore \
SoObjects \
OGoContentStore \
Main \
UI \
Tools

View file

@ -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/

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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];

View file

@ -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;

View file

@ -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

View file

@ -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];
}

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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];
}

View file

@ -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];

View file

@ -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 \

View file

@ -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)/ \

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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];

View file

@ -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

View file

@ -41,6 +41,7 @@
- (NSString *) mailDomain;
- (NSString *) imapServer;
- (NSString *) sieveServer;
- (NSString *) imapAclStyle;
- (NSString *) imapFolderSeparator;
- (BOOL) imapAclConformsToIMAPExt;

View file

@ -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
{

View file

@ -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];
}

View file

@ -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];

View file

@ -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)

View 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

View 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;
}

View 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

View file

@ -15,6 +15,7 @@ ContactsUI_OBJC_FILES = \
\
ContactsUIProduct.m \
UIxContactsFilterPanel.m \
UIxContactActions.m \
UIxContactView.m \
UIxContactEditor.m \
UIxListView.m \

View 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

View file

@ -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";
};
};
};

View file

@ -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>
*

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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
View file

@ -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

View file

@ -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