propagate from branch 'ca.inverse.sogo.1_3_12' (head e13dda2a639bb68b9e67d7b4dd75235d9d98ef1b)

to branch 'ca.inverse.sogo' (head f9d2bf55bd46e8957cdcb8f5fb95a1102244215f)

Monotone-Parent: e13dda2a639bb68b9e67d7b4dd75235d9d98ef1b
Monotone-Parent: f9d2bf55bd46e8957cdcb8f5fb95a1102244215f
Monotone-Revision: f06b5c51d33b49e9e8e5c1bd337b720f3d1f28e0

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2012-01-20T16:44:09
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2012-01-20 16:44:09 +00:00
commit 11a22d590b
81 changed files with 3481 additions and 1708 deletions

208
ChangeLog
View File

@ -15,6 +15,12 @@
menu is disabled. When switching to text-based message, menu is disabled. When switching to text-based message,
the popup menu is now correctly re-enabled. the popup menu is now correctly re-enabled.
2012-01-16 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SQLSource.m (_lookupContactEntry:considerEmail:)
(fetchContactsMatching:): assigned self to the "source" key of the
returned dictionaries.
2012-01-13 Francis Lachapelle <flachapelle@inverse.ca> 2012-01-13 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/Appointments/SOGoAppointmentFolder.m * SoObjects/Appointments/SOGoAppointmentFolder.m
@ -76,16 +82,115 @@
when a contact uid is specified. Otherwise, perform the query on when a contact uid is specified. Otherwise, perform the query on
the user instance as usual (/SOGo/so/<contactuser>/freebusy.ifb/ajaxRead). the user instance as usual (/SOGo/so/<contactuser>/freebusy.ifb/ajaxRead).
2012-01-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreCalendarMessage.m (-save): same as below +
ensure that nil fields are removed from non-new instances.
* OpenChange/MAPIStoreAppointmentWrapper.m
(-getPrStartDate:inMemCtx:, -getPrEndDate:inMemCtx:): remove the
tz offset from dates in all-day events.
* OpenChange/MAPIStoreTasksMessage.m (-save): the dates provided
by Outlook for start, due and completed are all-day dates, we thus
need to remove the timezone offset from those dates.
2012-01-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreGCSMessageTable.m
(_fixedDatePropertyRestriction:inMemCtx:): attached the result to
the memCtx passed as parameter to avoid a leak.
2012-01-10 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2012-01-10 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/Appointments/SOGoAppointmentObject.m * SoObjects/Appointments/SOGoAppointmentObject.m
(_requireResponseFromAttendees:): initialize listHasChanged to NO. (_requireResponseFromAttendees:): initialize listHasChanged to NO.
* OpenChange/MAPIStoreMailFolder.m (_parseCOPYUID): the uniString
buffer was allocated one byte too short.
2012-01-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/NSObject+MAPIStore.m (-getValue:forTag:inMemCtx:):
handle PT_SVREID just as PT_BINARY.
* OpenChange/MAPIStoreCalendarFolder.m (-exchangeRightsForRoles):
add the freebusy read rights when the user has read permission on
calendar objects.
2012-01-05 Francis Lachapelle <flachapelle@inverse.ca> 2012-01-05 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/SOGo/SOGoUserManager.m (-_registerSource:inDomain::): * SoObjects/SOGo/SOGoUserManager.m (-_registerSource:inDomain::):
log error when duplicated IDs are found. log error when duplicated IDs are found.
2012-01-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/ContactsUI.js
(onAddressBooksMenuPrepareVisibility): the "new list", "sharing" and "import"
options are now greyed out properly, depending on the object type
and the new attributes below.
* UI/Contacts/UIxContactFoldersView.m
(-currentContactFolderAclEditing)
(-currentContactFolderListEditing): new attribute accessors.
* SoObjects/SOGo/SOGoFolder.m (-sendFolderAdvisoryTemplate:):
moved method from SOGoGCSFolder in order to make it available to
other folder classes.
* SoObjects/SOGo/LDAPSource.m (-initFromUDSource:inDomain:): use
the new setters for certain ivars + take the new "abOU" key into
account.
(-setListRequiresDot:, -listRequiresDot:, -setSourceID:)
(-setDisplayName, -displayName, -setModifiers:): new accessors.
(-_convertRecordToLDAPAttributes): we now strip object classes that
are not supported by the server prior to remove the related fields.
(-hasUserAddressBooks): new method that returns whether user
addressbooks are supported, i.e. when "abOU" is set.
(-addressBookSourcesForUser:): when "abOU" is set, returns an
array of LDAPSource instances representing the personal
addressbooks of the specified user.
(-addAddressBookSource:withDisplayName:forUser:)
(-renameAddressBookSource:withDisplayName:forUser:)
(-removeAddressBookSource:forUser:): new methods with a
self-explicit name.
* SoObjects/Contacts/SOGoContactSourceFolder.m
(-setIsPersonalSource, -isPersonalSource): new accessors for the
"isPersonalSource ivar".
(-lookupName:inContext:acquire:): setup the object classes of the
new entries to "inetorgperson" and "mozillaabpersonalpha".
(-lookupContactsWithFilter:onCriteria:sortBy:ordering:): check
whether "listRequiresDot" is set on the current source and return
the full listing if not required.
(-compare:): enhanced to treat personal sources as if they were
regular GCS folders, in order to sort them properly.
(-delete, -renameTo:): implemented method, required for the
corresponding web methods.
(-ownerInContext:) adapted method to personal sources.
* SoObjects/Contacts/SOGoContactFolders.m (-appendPersonalSource):
overriden method for returning LDAP-based user addresbook sources.
(-newFolderWithName:andNameInContainer:): idem
(-renameLDAPAddressBook:withDisplayName:): new method that enables
the renaming of LDAP-based user addresbook sources.
(-removeLDAPAddressBook:): new method that enables
the removal of LDAP-based user addresbook sources.
2012-01-03 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoParentFolder.m (-appendPersonalSources): made
method public so that it can be easily overriden in subclasses.
* SoObjects/SOGo/SOGoUser.m (-authenticationSource): new method
that returned the SOGoSource instance that successfully recognized
the user represented by the current instance.
* SoObjects/SOGo/SOGoUserManager.m
(_fillContactInfosForUser:withUIDorEmail:inDomain:): we now set
the identifier of the source that authenticated the specified user
as the "SOGoSource" entry of the returned dictionary.
2012-01-03 Francis Lachapelle <flachapelle@inverse.ca> 2012-01-03 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/Appointments/SOGoAptMailNotification.m (-setupValues): * SoObjects/Appointments/SOGoAptMailNotification.m (-setupValues):
@ -109,6 +214,109 @@
* UI/WebServerResources/UIxFilterEditor.js (ensureFieldValidity): * UI/WebServerResources/UIxFilterEditor.js (ensureFieldValidity):
a field value is always considered invalid when empty. a field value is always considered invalid when empty.
2011-12-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/LDAPSourceSchema.[hm]: new class module enabling
schema auto-discovery.
* UI/WebServerResources/UIxContactEditor.js
(validateContactEditor): the birth date is validated slightly
differently, by enabling empty and 2-digit years as well as single
digits months and days.
* UI/Contacts/UIxContactView.m (-defaultAction, -dealloc): retain
and release the "card" ivar.
(_formattedURL:): the url should be displayed only if it is
non-nil AND non-empty.
(-vcardAction): removed useless method.
* UI/Contacts/UIxContactEditor.m (init): removed the "snapshot",
"preferredEmail", "card", "photosURL" and "contactCategories" ivars.
(-ldifRecord): new getter that proxy the invocation to the client
object, but by taking the contactEmail and contactFN url
parameters. Replaces the "-snapshot" getter, since the LDIF record
is now directly edited.
(-addressBooksList): the client object container class is no
longer taken into account when fetching the current user's permissions.
(-supportCategories, -supportPhotos): new getters that enables the
categories and photo tabs.
(-setJsonContactCategories:, -jsonContactCategories): now make
use of the special "vcardcategories" parameter found in the
contact LDIF record.
* SoObjects/SOGo/SQLSource.m (_lookupContactEntry:considerEmail:):
enhanced to copy the "c_XX" fields to unprefixed equivalents.
* SoObjects/Contacts/NSString+LDIF.m (mustEncodeLDIFValue): new
method, replacing "_isLDIFSafe" in a clearly public manner.
* SoObjects/SOGo/LDAPSource.m (-[NGLdapEntry _asDictionary]): new
utility method to convert an entry into a "SOGo LDIF record".
(-[NGLdapAttribute _asArrayOrString]): new utility method to
convert an LDAP attribute into a string or an array of strings
when the attribute has one or more values.
(-initFromUDSource:inDomain:): handle the new "modifiers",
"mapping" and "objectClasses" configuration keys, used when the
source instance is used as an addressbook. All LDAP fields are now
converted to lowercase.
(_searchAttributes): removed method as the special "*" attribute
is not costly enough to justify its existence, thereby reducing
code complexity.
(-lookupContactEntry:, -lookupContactEntryWithUIDorEmail:)
(-lookupLoginByDN:): merged common code in the new
-_lookupLDAPEntry: method, that accepts an EOQualifier as argument.
(--addContactEntry:withID:, -updateContactEntry: and
removeContactEntryWithID:): new methods for editing addressbook
sources.
* SoObjects/Contacts/SOGoContactSourceFolder.m (-source): new
getter for the "source" ivar.
(-lookupName:inContext:acquire:): accept the creation of new LDIF
entries via web methods ending with "AsContact".
(-saveLDIFEntry:, -deleteLDIFEntry:): new proxy methods for the
new SOGoSource -addContactEntry:withID:, -updateContactEntry: and
removeContactEntryWithID: methods, enabling the creation,
modification and deletion of LDAP contacts.
(-aclsForUser:): implemented method based on the array returned by
-[<SOGoSource> modifiers].
* SoObjects/Contacts/SOGoContactLDIFEntry.m (-vCard): the vcard is
now generated automatically from the LDIF record of the entry,
using the new method provided by NGVCard+SOGo.
(-aclsForUser:): new overriden method similar to the
implementation from SOGoContentObject.
* SoObjects/Contacts/SOGoContactGCSEntry.m (-setLDIFRecord)
(-ldifRecord, -hasPhoto): new accessors required by the
SOGoContactObject protocol.
(-lookupName:inContext:acquire:): return the only photo element
when the "photo" key is requested, if present in the card.
(-save): now returns an NSException, instead of void.
* SoObjects/Contacts/SOGoContactEntryPhoto.m
(+entryPhotoWithID:inContainer:, -setPhotoID:): removed methods,
since there can only be one PHOTO element per contact.
(-photo): simplified thanks to the constraint mentionned above.
* SoObjects/Contacts/NSDictionary+LDIF.m (-ldifRecordAsString):
new method implementing the code previously found in
SOGo/NSDictionary+Utilities.m, in order to produce a textual
representation of an LDIF record.
* SoObjects/Contacts/NSDictionary+LDIF.[hm]: new category module.
* SoObjects/Contacts/NGVCard+SOGo.m (-asLDIFRecord): new method
implementing the conversion code previously found in
UIxContactEditor, in order to produce an "LDIF record" in the form
of an NSDictionary matching the inetOrgPerson and
mozillaAbPersonAlpha object classes.
(-updateFromLDIFRecord:) reciprocal method to "-asLDIFRecord",
with conversion code moved from UIxContactEditor.
* SoObjects/Contacts/SOGoFolder+CardDAV.m (_isValidFilter:): make
use of the lowercase instance of the string, which was erroneously
ignored previously.
2011-12-29 Ludovic Marcotte <lmarcotte@inverse.ca.> 2011-12-29 Ludovic Marcotte <lmarcotte@inverse.ca.>
* SoObjects/SOGo/SOGoSQLUserProfile.m (_sqlJsonRepresentation:): * SoObjects/SOGo/SOGoSQLUserProfile.m (_sqlJsonRepresentation:):

View File

@ -172,12 +172,12 @@ LIBMAPISTORE_CFLAGS = $(shell pkg-config libmapistore --cflags) -DSAMBA_PREFIX="
LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy LIBMAPISTORE_LIBS = $(shell pkg-config libmapistore --libs) -lmapiproxy
$(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends $(MAPISTORESOGO)_INSTALL_DIR = $(DESTDIR)/$(SAMBA_LIB_DIR)/mapistore_backends
$(MAPISTORESOGO)_LDFLAGS += \ $(MAPISTORESOGO)_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \
$(LIBMAPI_LIBS) \ $(LIBMAPI_LIBS) \
$(LIBMAPISTORE_LIBS) $(LIBMAPISTORE_LIBS)
$(SOGOBACKEND)_LDFLAGS += \ $(SOGOBACKEND)_LIB_DIRS += \
-L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \
-L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \
$(LIBMAPI_LIBS) \ $(LIBMAPI_LIBS) \

View File

@ -638,11 +638,19 @@ static NSCharacterSet *hexCharacterSet = nil;
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
NSCalendarDate *dateValue; NSCalendarDate *dateValue;
NSInteger offset;
if ([event isRecurrent]) if ([event isRecurrent])
dateValue = [event firstRecurrenceStartDate]; dateValue = [event firstRecurrenceStartDate];
else else
dateValue = [event startDate]; dateValue = [event startDate];
if ([event isAllDay])
{
offset = -[timeZone secondsFromGMTForDate: dateValue];
dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: offset];
}
[dateValue setTimeZone: utcTZ]; [dateValue setTimeZone: utcTZ];
*data = [dateValue asFileTimeInMemCtx: memCtx]; *data = [dateValue asFileTimeInMemCtx: memCtx];
@ -882,16 +890,18 @@ static NSCharacterSet *hexCharacterSet = nil;
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
NSCalendarDate *dateValue; NSCalendarDate *dateValue;
NSInteger offset;
if ([event isRecurrent]) if ([event isRecurrent])
dateValue = [event firstRecurrenceStartDate]; dateValue = [event firstRecurrenceStartDate];
else else
dateValue = [event startDate]; dateValue = [event startDate];
dateValue offset = [event durationAsTimeInterval];
= [dateValue dateByAddingYears: 0 months: 0 days: 0 if ([event isAllDay])
hours: 0 minutes: 0 offset -= [timeZone secondsFromGMTForDate: dateValue];
seconds: (NSInteger) [event dateValue = [dateValue dateByAddingYears: 0 months: 0 days: 0
durationAsTimeInterval]]; hours: 0 minutes: 0
seconds: offset];
[dateValue setTimeZone: utcTZ]; [dateValue setTimeZone: utcTZ];
*data = [dateValue asFileTimeInMemCtx: memCtx]; *data = [dateValue asFileTimeInMemCtx: memCtx];

View File

@ -24,6 +24,7 @@
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSURL.h> #import <Foundation/NSURL.h>
#import <NGObjWeb/WOContext+SoObjects.h> #import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOQualifier.h> #import <EOControl/EOQualifier.h>
#import <SOGo/SOGoPermissions.h> #import <SOGo/SOGoPermissions.h>
#import <Appointments/SOGoAppointmentFolder.h> #import <Appointments/SOGoAppointmentFolder.h>
@ -132,6 +133,8 @@
[roles addObject: SOGoCalendarRole_ConfidentialViewer]; [roles addObject: SOGoCalendarRole_ConfidentialViewer];
} }
// [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles];
return roles; return roles;
} }
@ -150,9 +153,11 @@
else if ([roles containsObject: SOGoCalendarRole_PublicViewer] else if ([roles containsObject: SOGoCalendarRole_PublicViewer]
&& [roles containsObject: SOGoCalendarRole_PrivateViewer] && [roles containsObject: SOGoCalendarRole_PrivateViewer]
&& [roles containsObject: SOGoCalendarRole_ConfidentialViewer]) && [roles containsObject: SOGoCalendarRole_ConfidentialViewer])
rights |= RightsReadItems; rights |= RightsReadItems | 0x1800;
if (rights != 0) if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */ rights |= RoleNone; /* actually "folder visible" */
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
return rights; return rights;
} }

View File

@ -207,7 +207,8 @@
- (int) getPidLidAppointmentSubType: (void **) data - (int) getPidLidAppointmentSubType: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
return [[self appointmentWrapper] getPidLidAppointmentSubType: data inMemCtx: memCtx]; return [[self appointmentWrapper] getPidLidAppointmentSubType: data
inMemCtx: memCtx];
} }
- (int) getPidLidBusyStatus: (void **) data // TODO - (int) getPidLidBusyStatus: (void **) data // TODO
@ -653,6 +654,7 @@
iCalEvent *newEvent; iCalEvent *newEvent;
iCalPerson *userPerson; iCalPerson *userPerson;
NSUInteger responseStatus = 0; NSUInteger responseStatus = 0;
NSInteger tzOffset;
SOGoUser *activeUser, *ownerUser; SOGoUser *activeUser, *ownerUser;
id value; id value;
@ -747,10 +749,11 @@
if (value) if (value)
[newEvent setLocation: value]; [newEvent setLocation: value];
isAllDay = [[properties isAllDay = [newEvent isAllDay];
objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)] value = [properties
boolValue]; objectForKey: MAPIPropertyKey (PidLidAppointmentSubType)];
if (value)
isAllDay = [value boolValue];
if (!isAllDay) if (!isAllDay)
{ {
tzName = [[self ownerTimeZone] name]; tzName = [[self ownerTimeZone] name];
@ -767,7 +770,14 @@
{ {
start = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtstart"]; start = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtstart"];
if (isAllDay) if (isAllDay)
[start setDate: value]; {
tzOffset = [[value timeZone] secondsFromGMTForDate: value];
value = [value dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -tzOffset];
[start setTimeZone: nil];
[start setDate: value];
}
else else
{ {
[start setTimeZone: tz]; [start setTimeZone: tz];
@ -776,14 +786,21 @@
} }
/* end */ /* end */
value = [properties objectForKey: MAPIPropertyKey(PR_END_DATE)]; value = [properties objectForKey: MAPIPropertyKey (PR_END_DATE)];
if (!value) if (!value)
value = [properties objectForKey: MAPIPropertyKey(PidLidAppointmentEndWhole)]; value = [properties objectForKey: MAPIPropertyKey (PidLidAppointmentEndWhole)];
if (value) if (value)
{ {
end = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtend"]; end = (iCalDateTime *) [newEvent uniqueChildWithTag: @"dtend"];
if (isAllDay) if (isAllDay)
[end setDate: value]; {
tzOffset = [[value timeZone] secondsFromGMTForDate: value];
value = [value dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -tzOffset];
[end setTimeZone: nil];
[end setDate: value];
}
else else
{ {
[end setTimeZone: tz]; [end setTimeZone: tz];
@ -840,13 +857,14 @@
if (value) if (value)
{ {
value = [[NSString alloc] initWithData: value value = [[NSString alloc] initWithData: value
encoding: NSUTF8StringEncoding]; encoding: NSUTF8StringEncoding];
[value autorelease]; [value autorelease];
value = [value htmlToText]; value = [value htmlToText];
} }
} }
if (value) if (value && [value length] == 0)
[newEvent setComment: value]; value = nil;
[newEvent setComment: value];
/* recurrence */ /* recurrence */
value = [properties value = [properties

View File

@ -25,8 +25,6 @@
#import <Foundation/NSObject.h> #import <Foundation/NSObject.h>
#import "MAPIStoreTable.h"
@class NSArray; @class NSArray;
@class NSMutableArray; @class NSMutableArray;
@class NSNumber; @class NSNumber;
@ -105,7 +103,7 @@
andFID: (uint64_t) fid; andFID: (uint64_t) fid;
- (int) deleteFolderWithFID: (uint64_t) fid; - (int) deleteFolderWithFID: (uint64_t) fid;
- (int) getChildCount: (uint32_t *) rowCount - (int) getChildCount: (uint32_t *) rowCount
ofTableType: (uint8_t) tableType; ofTableType: (enum mapistore_table_type) tableType;
- (int) createMessage: (MAPIStoreMessage **) messagePtr - (int) createMessage: (MAPIStoreMessage **) messagePtr
withMID: (uint64_t) mid withMID: (uint64_t) mid
@ -128,12 +126,12 @@
- (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr
andCN: (uint64_t *) cnPtr andCN: (uint64_t *) cnPtr
fromChangeNumber: (uint64_t) changeNum fromChangeNumber: (uint64_t) changeNum
inTableType: (uint8_t) tableType inTableType: (enum mapistore_table_type) tableType
inMemCtx: (TALLOC_CTX *) mem_ctx; inMemCtx: (TALLOC_CTX *) mem_ctx;
- (int) getTable: (MAPIStoreTable **) tablePtr - (int) getTable: (MAPIStoreTable **) tablePtr
andRowCount: (uint32_t *) count andRowCount: (uint32_t *) count
tableType: (uint8_t) tableType tableType: (enum mapistore_table_type) tableType
andHandleId: (uint32_t) handleId; andHandleId: (uint32_t) handleId;
- (int) modifyPermissions: (struct PermissionData *) permissions - (int) modifyPermissions: (struct PermissionData *) permissions
@ -150,7 +148,7 @@
andSortOrderings: (NSArray *) sortOrderings; andSortOrderings: (NSArray *) sortOrderings;
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum - (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
andCN: (NSNumber **) cnNbr andCN: (NSNumber **) cnNbr
inTableType: (uint8_t) tableType; inTableType: (enum mapistore_table_type) tableType;
- (NSString *) createFolder: (struct SRow *) aRow - (NSString *) createFolder: (struct SRow *) aRow
withFID: (uint64_t) newFID; withFID: (uint64_t) newFID;

View File

@ -383,7 +383,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
} }
- (int) getChildCount: (uint32_t *) rowCount - (int) getChildCount: (uint32_t *) rowCount
ofTableType: (uint8_t) tableType ofTableType: (enum mapistore_table_type) tableType
{ {
NSArray *keys; NSArray *keys;
int rc = MAPISTORE_SUCCESS; int rc = MAPISTORE_SUCCESS;
@ -863,7 +863,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr - (int) getDeletedFMIDs: (struct I8Array_r **) fmidsPtr
andCN: (uint64_t *) cnPtr andCN: (uint64_t *) cnPtr
fromChangeNumber: (uint64_t) changeNum fromChangeNumber: (uint64_t) changeNum
inTableType: (uint8_t) tableType inTableType: (enum mapistore_table_type) tableType
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
int rc; int rc;
@ -920,7 +920,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (int) getTable: (MAPIStoreTable **) tablePtr - (int) getTable: (MAPIStoreTable **) tablePtr
andRowCount: (uint32_t *) countPtr andRowCount: (uint32_t *) countPtr
tableType: (uint8_t) tableType tableType: (enum mapistore_table_type) tableType
andHandleId: (uint32_t) handleId andHandleId: (uint32_t) handleId
{ {
int rc = MAPISTORE_SUCCESS; int rc = MAPISTORE_SUCCESS;
@ -1076,7 +1076,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
andType: MAPISTORE_FAI_TABLE]; andType: MAPISTORE_FAI_TABLE];
} }
- (void) _cleanupTableCaches: (uint8_t) tableType - (void) _cleanupTableCaches: (enum mapistore_table_type) tableType
{ {
NSArray *tables; NSArray *tables;
NSUInteger count, max; NSUInteger count, max;
@ -1552,7 +1552,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum - (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
andCN: (NSNumber **) cnNbrs andCN: (NSNumber **) cnNbrs
inTableType: (uint8_t) tableType inTableType: (enum mapistore_table_type) tableType
{ {
return nil; return nil;
} }

View File

@ -58,7 +58,7 @@
NSCalendarDate *dateValue; NSCalendarDate *dateValue;
int32_t longDate; int32_t longDate;
translatedRes = talloc (NULL, struct mapi_SPropertyRestriction); translatedRes = talloc (memCtx, struct mapi_SPropertyRestriction);
translatedRes->ulPropTag = (res->ulPropTag & 0xffff0000) | PT_LONG; translatedRes->ulPropTag = (res->ulPropTag & 0xffff0000) | PT_LONG;
translatedRes->relop = res->relop; translatedRes->relop = res->relop;
dateValue = NSObjectFromMAPISPropValue (&res->lpProp); dateValue = NSObjectFromMAPISPropValue (&res->lpProp);

View File

@ -848,7 +848,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
/* sample: 1 OK [COPYUID 1311899334 1:3 11:13] Completed */ /* sample: 1 OK [COPYUID 1311899334 1:3 11:13] Completed */
max = [line length]; max = [line length];
uniString = NSZoneMalloc (NULL, max * sizeof (unichar) + 1); uniString = NSZoneMalloc (NULL, sizeof (unichar) * (max + 1));
[line getCharacters: uniString]; [line getCharacters: uniString];
uniString[max] = 0; uniString[max] = 0;
@ -1025,6 +1025,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
if (rights & RightsCreateSubfolders) if (rights & RightsCreateSubfolders)
[roles addObject: SOGoRole_FolderCreator]; [roles addObject: SOGoRole_FolderCreator];
// [self logWithFormat: @"roles for rights %.8x = (%@)", rights, roles];
return roles; return roles;
} }
@ -1051,6 +1053,8 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
if (rights != 0) if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */ rights |= RoleNone; /* actually "folder visible" */
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
return rights; return rights;
} }

View File

@ -326,7 +326,7 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
- (int) getRow: (struct mapistore_property_data **) dataP - (int) getRow: (struct mapistore_property_data **) dataP
withRowID: (uint32_t) rowId withRowID: (uint32_t) rowId
andQueryType: (enum table_query_type) queryType andQueryType: (enum mapistore_query_type) queryType
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
if (!fetchedCoreInfos) if (!fetchedCoreInfos)

View File

@ -43,10 +43,6 @@
#import "MAPIStoreTable.h" #import "MAPIStoreTable.h"
#import "NSObject+MAPIStore.h" #import "NSObject+MAPIStore.h"
#undef DEBUG
#include <stdbool.h>
#include <talloc.h>
#include <gen_ndr/exchange.h>
#include <mapistore/mapistore.h> #include <mapistore/mapistore.h>
#include <mapistore/mapistore_errors.h> #include <mapistore/mapistore_errors.h>
@ -331,7 +327,7 @@ sogo_folder_delete_folder(void *folder_object, uint64_t fid)
} }
static enum mapistore_error static enum mapistore_error
sogo_folder_get_child_count(void *folder_object, uint8_t table_type, uint32_t *child_count) sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table_type, uint32_t *child_count)
{ {
struct MAPIStoreTallocWrapper *wrapper; struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool; NSAutoreleasePool *pool;
@ -494,7 +490,7 @@ sogo_folder_move_copy_messages(void *folder_object,
static enum mapistore_error static enum mapistore_error
sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
uint8_t table_type, uint64_t change_num, enum mapistore_table_type table_type, uint64_t change_num,
struct I8Array_r **fmidsp, uint64_t *cnp) struct I8Array_r **fmidsp, uint64_t *cnp)
{ {
struct MAPIStoreTallocWrapper *wrapper; struct MAPIStoreTallocWrapper *wrapper;
@ -526,7 +522,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
static enum mapistore_error static enum mapistore_error
sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx,
uint8_t table_type, uint32_t handle_id, enum mapistore_table_type table_type, uint32_t handle_id,
void **table_object, uint32_t *row_count) void **table_object, uint32_t *row_count)
{ {
struct MAPIStoreTallocWrapper *wrapper; struct MAPIStoreTallocWrapper *wrapper;
@ -974,7 +970,7 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order,
static enum mapistore_error static enum mapistore_error
sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
enum table_query_type query_type, uint32_t row_id, enum mapistore_query_type query_type, uint32_t row_id,
struct mapistore_property_data **data) struct mapistore_property_data **data)
{ {
struct MAPIStoreTallocWrapper *wrapper; struct MAPIStoreTallocWrapper *wrapper;
@ -1003,7 +999,7 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
static enum mapistore_error static enum mapistore_error
sogo_table_get_row_count (void *table_object, sogo_table_get_row_count (void *table_object,
enum table_query_type query_type, enum mapistore_query_type query_type,
uint32_t *row_countp) uint32_t *row_countp)
{ {
struct MAPIStoreTallocWrapper *wrapper; struct MAPIStoreTallocWrapper *wrapper;

View File

@ -27,6 +27,9 @@
#import <Foundation/NSObject.h> #import <Foundation/NSObject.h>
#undef DEBUG
#include <mapistore/mapistore.h>
#define SENSITIVITY_NONE 0 #define SENSITIVITY_NONE 0
#define SENSITIVITY_PERSONAL 1 #define SENSITIVITY_PERSONAL 1
#define SENSITIVITY_PRIVATE 2 #define SENSITIVITY_PRIVATE 2
@ -62,7 +65,7 @@ typedef enum {
uint32_t currentRow; uint32_t currentRow;
MAPIStoreObject *currentChild; MAPIStoreObject *currentChild;
uint8_t tableType; /* mapistore */ enum mapistore_table_type tableType; /* mapistore */
/* proof of concept */ /* proof of concept */
uint16_t columnsCount; uint16_t columnsCount;
@ -75,13 +78,13 @@ typedef enum {
- (id) initForContainer: (MAPIStoreObject *) newContainer; - (id) initForContainer: (MAPIStoreObject *) newContainer;
- (id) container; - (id) container;
- (uint8_t) tableType; - (enum mapistore_table_type) tableType;
- (void) setHandleId: (uint32_t) newHandleId; - (void) setHandleId: (uint32_t) newHandleId;
- (void) destroyHandle: (uint32_t) handleId; - (void) destroyHandle: (uint32_t) handleId;
- (id) childAtRowID: (uint32_t) rowId - (id) childAtRowID: (uint32_t) rowId
forQueryType: (enum table_query_type) queryType; forQueryType: (enum mapistore_query_type) queryType;
- (void) cleanupCaches; - (void) cleanupCaches;
@ -92,10 +95,10 @@ typedef enum {
withCount: (uint16_t) newColumCount; withCount: (uint16_t) newColumCount;
- (int) getRow: (struct mapistore_property_data **) dataP - (int) getRow: (struct mapistore_property_data **) dataP
withRowID: (uint32_t) rowId withRowID: (uint32_t) rowId
andQueryType: (enum table_query_type) queryType andQueryType: (enum mapistore_query_type) queryType
inMemCtx: (TALLOC_CTX *) memCtx; inMemCtx: (TALLOC_CTX *) memCtx;
- (int) getRowCount: (uint32_t *) countP - (int) getRowCount: (uint32_t *) countP
withQueryType: (enum table_query_type) queryType; withQueryType: (enum mapistore_query_type) queryType;
- (void) notifyChangesForChild: (MAPIStoreObject *) child; - (void) notifyChangesForChild: (MAPIStoreObject *) child;

View File

@ -337,7 +337,7 @@ static Class NSDataK, NSStringK;
return container; return container;
} }
- (uint8_t) tableType - (enum mapistore_table_type) tableType
{ {
return tableType; return tableType;
} }
@ -765,7 +765,7 @@ static Class NSDataK, NSStringK;
} }
- (id) childAtRowID: (uint32_t) rowId - (id) childAtRowID: (uint32_t) rowId
forQueryType: (enum table_query_type) queryType forQueryType: (enum mapistore_query_type) queryType
{ {
id child; id child;
NSArray *children, *restrictedChildren; NSArray *children, *restrictedChildren;
@ -833,7 +833,7 @@ static Class NSDataK, NSStringK;
- (int) getRow: (struct mapistore_property_data **) dataP - (int) getRow: (struct mapistore_property_data **) dataP
withRowID: (uint32_t) rowId withRowID: (uint32_t) rowId
andQueryType: (enum table_query_type) queryType andQueryType: (enum mapistore_query_type) queryType
inMemCtx: (TALLOC_CTX *) memCtx inMemCtx: (TALLOC_CTX *) memCtx
{ {
NSUInteger count; NSUInteger count;
@ -860,7 +860,7 @@ static Class NSDataK, NSStringK;
} }
- (int) getRowCount: (uint32_t *) countP - (int) getRowCount: (uint32_t *) countP
withQueryType: (enum table_query_type) queryType withQueryType: (enum mapistore_query_type) queryType
{ {
NSArray *children; NSArray *children;

View File

@ -22,8 +22,10 @@
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSTimeZone.h>
#import <NGObjWeb/WOContext+SoObjects.h> #import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalCalendar.h> #import <NGCards/iCalCalendar.h>
@ -325,13 +327,11 @@
iCalCalendar *vCalendar; iCalCalendar *vCalendar;
iCalToDo *vToDo; iCalToDo *vToDo;
id value; id value;
SOGoUserDefaults *ud;
iCalTimeZone *tz;
iCalDateTime *date; iCalDateTime *date;
NSString *owner, *status, *priority; NSString *status, *priority;
NSCalendarDate *now; NSCalendarDate *now;
NSInteger tzOffset;
owner = [sogoObject ownerInContext: nil];
vToDo = [sogoObject component: YES secure: NO]; vToDo = [sogoObject component: YES secure: NO];
vCalendar = [vToDo parent]; vCalendar = [vToDo parent];
[vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"]; [vCalendar setProdID: @"-//Inverse inc.//OpenChange+SOGo//EN"];
@ -366,17 +366,16 @@
[vToDo setTimeStampAsDate: value]; [vToDo setTimeStampAsDate: value];
} }
ud = [[SOGoUser userWithLogin: owner] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
[vCalendar addTimeZone: tz];
// start // start
value = [properties objectForKey: MAPIPropertyKey (PidLidTaskStartDate)]; value = [properties objectForKey: MAPIPropertyKey (PidLidTaskStartDate)];
if (value) if (value)
{ {
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"]; date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"dtstart"];
[date setTimeZone: tz]; tzOffset = [[value timeZone] secondsFromGMTForDate: value];
[date setDateTime: value]; value = [value dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -tzOffset];
[date setDate: value];
} }
else else
{ {
@ -388,8 +387,11 @@
if (value) if (value)
{ {
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"]; date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"due"];
[date setTimeZone: tz]; tzOffset = [[value timeZone] secondsFromGMTForDate: value];
[date setDateTime: value]; value = [value dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -tzOffset];
[date setDate: value];
} }
else else
{ {
@ -401,8 +403,11 @@
if (value) if (value)
{ {
date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"]; date = (iCalDateTime *) [vToDo uniqueChildWithTag: @"completed"];
[date setTimeZone: tz]; tzOffset = [[value timeZone] secondsFromGMTForDate: value];
[date setDateTime: value]; value = [value dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -tzOffset];
[date setDate: value];
} }
else else
{ {

View File

@ -148,7 +148,6 @@ NSObjectFromMAPISPropValue (const struct mapi_SPropValue *value)
// #define PT_ERROR 0xa // #define PT_ERROR 0xa
// #define PT_OBJECT 0xd // #define PT_OBJECT 0xd
// #define PT_I8 0x14 // #define PT_I8 0x14
// #define PT_SVREID 0xFB
// #define PT_SRESTRICT 0xFD // #define PT_SRESTRICT 0xFD
// #define PT_ACTIONS 0xFE // #define PT_ACTIONS 0xFE
result = [NSNull null]; result = [NSNull null];
@ -244,7 +243,6 @@ NSObjectFromSPropValue (const struct SPropValue *value)
// #define PT_ERROR 0xa // #define PT_ERROR 0xa
// #define PT_OBJECT 0xd // #define PT_OBJECT 0xd
// #define PT_I8 0x14 // #define PT_I8 0x14
// #define PT_SVREID 0xFB
// #define PT_SRESTRICT 0xFD // #define PT_SRESTRICT 0xFD
// #define PT_ACTIONS 0xFE // #define PT_ACTIONS 0xFE
result = [NSNull null]; result = [NSNull null];

View File

@ -106,6 +106,7 @@ static int MAPIStoreTallocWrapperDestroy (void *data)
*data = [(NSCalendarDate * ) self asFileTimeInMemCtx: memCtx]; *data = [(NSCalendarDate * ) self asFileTimeInMemCtx: memCtx];
break; break;
case PT_BINARY: case PT_BINARY:
case PT_SVREID:
*data = [(NSData *) self asBinaryInMemCtx: memCtx]; *data = [(NSData *) self asBinaryInMemCtx: memCtx];
break; break;
case PT_CLSID: case PT_CLSID:

View File

@ -460,8 +460,6 @@ _orderedValuesAreVoid (NSArray *orderedValues)
NSMutableArray *orderedValues; NSMutableArray *orderedValues;
NSUInteger count, max; NSUInteger count, max;
result = YES;
keys = [values allKeys]; keys = [values allKeys];
max = [keys count]; max = [keys count];
for (count = 0; result && count < max; count++) for (count = 0; result && count < max; count++)

View File

@ -46,12 +46,15 @@
- (CardElement *) uniqueChildWithTag: (NSString *) aTag; - (CardElement *) uniqueChildWithTag: (NSString *) aTag;
- (void) setUniqueChild: (CardElement *) aChild; - (void) setUniqueChild: (CardElement *) aChild;
- (NSMutableArray *) children;
- (void) addChild: (CardElement *) aChild; - (void) addChild: (CardElement *) aChild;
- (void) addChildren: (NSArray *) someChildren; - (void) addChildren: (NSArray *) someChildren;
- (void) removeChild: (CardElement *) aChild; - (void) removeChild: (CardElement *) aChild;
- (void) removeChildren: (NSArray *) someChildren; - (void) removeChildren: (NSArray *) someChildren;
- (NSMutableArray *) children; - (void) cleanupEmptyChildren;
- (CardElement *) firstChildWithTag: (NSString *) aTag; - (CardElement *) firstChildWithTag: (NSString *) aTag;
- (NSArray *) childrenWithTag: (NSString *) aTag; - (NSArray *) childrenWithTag: (NSString *) aTag;
- (NSArray *) childrenWithAttribute: (NSString *) anAttribute - (NSArray *) childrenWithAttribute: (NSString *) anAttribute

View File

@ -142,6 +142,19 @@ static NGCardsSaxHandler *sax = nil;
return nil; return nil;
} }
- (BOOL) isVoid
{
BOOL isVoid = YES;
NSUInteger count, max;
max = [children count];
for (count = 0; isVoid && count < max; count++)
if (![[children objectAtIndex: count] isVoid])
isVoid = NO;
return isVoid;
}
- (void) addChild: (CardElement *) aChild - (void) addChild: (CardElement *) aChild
{ {
Class mappedClass; Class mappedClass;
@ -366,6 +379,23 @@ static NGCardsSaxHandler *sax = nil;
[self addChild: newChild]; [self addChild: newChild];
} }
- (void) cleanupEmptyChildren
{
NSUInteger max;
NSInteger count;
CardElement *child;
max = [children count];
for (count = max - 1; count > -1; count--)
{
child = [children objectAtIndex: count];
if ([child isKindOfClass: [CardGroup class]])
[(CardGroup *) child cleanupEmptyChildren];
if ([child isVoid])
[children removeObjectAtIndex: count];
}
}
- (NSString *) description - (NSString *) description
{ {
NSMutableString *str; NSMutableString *str;

View File

@ -8,6 +8,14 @@
* NSString+NGCards.m (-vCardSubvalues): fixed allocation of * NSString+NGCards.m (-vCardSubvalues): fixed allocation of
parsing buffer to avoid a buffer overflow. parsing buffer to avoid a buffer overflow.
2011-12-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* NGVCard.m (-initWithUid:): initialize "CLASS" and "PROFILE".
* CardGroup.m (-isVoid): overriden method.
(-cleanupEmptyChildren): make use of "isVoid" to detect and remove
empty children.
2011-11-21 Francis Lachapelle <flachapelle@inverse.ca> 2011-11-21 Francis Lachapelle <flachapelle@inverse.ca>
* iCalTimeZone.m (+knownTimeZoneNames): ignore files that don't * iCalTimeZone.m (+knownTimeZoneNames): ignore files that don't

View File

@ -50,6 +50,8 @@
[self setTag: @"vcard"]; [self setTag: @"vcard"];
[self setUid: _uid]; [self setUid: _uid];
[self setVersion: @"3.0"]; [self setVersion: @"3.0"];
[self setVClass: @"PUBLIC"];
[self setProfile: @"VCARD"];
} }
return self; return self;

View File

@ -9,17 +9,20 @@ Contacts_PRINCIPAL_CLASS = SOGoContactsProduct
Contacts_OBJC_FILES = \ Contacts_OBJC_FILES = \
Product.m \ Product.m \
NGVCard+SOGo.m \ NGVCard+SOGo.m \
NGVList+SOGo.m \ NGVList+SOGo.m \
SOGoFolder+CardDAV.m \ SOGoFolder+CardDAV.m \
SOGoContactFolders.m \ SOGoContactFolders.m \
SOGoContactGCSEntry.m \ SOGoContactGCSEntry.m \
SOGoContactGCSList.m \ SOGoContactGCSList.m \
SOGoContactGCSFolder.m \ SOGoContactGCSFolder.m \
SOGoContactLDIFEntry.m \ SOGoContactLDIFEntry.m \
SOGoContactSourceFolder.m \ SOGoContactSourceFolder.m \
SOGoUserFolder+Contacts.m \ SOGoUserFolder+Contacts.m \
SOGoContactEntryPhoto.m \ SOGoContactEntryPhoto.m \
\
NSDictionary+LDIF.m \
NSString+LDIF.m
Contacts_RESOURCE_FILES += \ Contacts_RESOURCE_FILES += \
product.plist \ product.plist \

View File

@ -25,9 +25,13 @@
#import <NGCards/NGVCard.h> #import <NGCards/NGVCard.h>
@class NSDictionary;
@class NSMutableDictionary;
@interface NGVCard (SOGoExtensions) @interface NGVCard (SOGoExtensions)
- (NSString *) ldifString; - (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
@end @end

View File

@ -21,153 +21,610 @@
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h> #import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <SOGo/NSDictionary+Utilities.h> #import <NGCards/NSArray+NGCards.h>
#import <NGCards/NSString+NGCards.h>
#import "NSDictionary+LDIF.h"
#import "NGVCard+SOGo.h" #import "NGVCard+SOGo.h"
/*
objectclass ( 2.5.6.6 NAME 'person'
DESC 'RFC2256: a person'
SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
objectclass ( 2.5.6.7 NAME 'organizationalPerson'
DESC 'RFC2256: an organizational person'
SUP person STRUCTURAL
MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
telephoneNumber $ internationaliSDNNumber $
facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
objectclass ( 2.16.840.1.113730.3.2.2
NAME 'inetOrgPerson'
DESC 'RFC2798: Internet Organizational Person'
SUP organizationalPerson
STRUCTURAL
MAY (
audio $ businessCategory $ carLicense $ departmentNumber $
displayName $ employeeNumber $ employeeType $ givenName $
homePhone $ homePostalAddress $ initials $ jpegPhoto $
labeledURI $ mail $ manager $ mobile $ o $ pager $
photo $ roomNumber $ secretary $ uid $ userCertificate $
x500uniqueIdentifier $ preferredLanguage $
userSMIMECertificate $ userPKCS12 )
)
objectclass ( 1.3.6.1.4.1.13769.9.1 NAME 'mozillaAbPersonAlpha'
SUP top AUXILIARY
MUST ( cn )
MAY( c $
description $
displayName $
facsimileTelephoneNumber $
givenName $
homePhone $
l $
mail $
mobile $
mozillaCustom1 $
mozillaCustom2 $
mozillaCustom3 $
mozillaCustom4 $
mozillaHomeCountryName $
mozillaHomeLocalityName $
mozillaHomePostalCode $
mozillaHomeState $
mozillaHomeStreet $
mozillaHomeStreet2 $
mozillaHomeUrl $
mozillaNickname $
mozillaSecondEmail $
mozillaUseHtmlMail $
mozillaWorkStreet2 $
mozillaWorkUrl $
nsAIMid $
o $
ou $
pager $
postalCode $
postOfficeBox $
sn $
st $
street $
telephoneNumber $
title ) )
additional vcard fields:
"vcardCategories"
test contact (export from tb):
dn:: Y249UHLDqW5vbSBOb20sbWFpbD1hZHIxQGVsZS5jb20=
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
objectclass: mozillaAbPersonAlpha
givenName:: UHLDqW5vbQ==
sn: Nom
cn:: UHLDqW5vbSBOb20=
mozillaNickname: Surnom
mail: adr1@ele.com
mozillaSecondEmail: adralt@ele.com
nsAIMid: pseudo aim
modifytimestamp: 1324509379
telephoneNumber: travail
homePhone: dom
facsimiletelephonenumber: fax
pager: pager
mobile: port
mozillaHomeStreet:: YWRyMSBwcml2w6ll
mozillaHomeStreet2:: YWRyMiBwcml2w6ll
mozillaHomeLocalityName: ville/locallite
mozillaHomeState:: w6l0YXQvcHJvdg==
mozillaHomePostalCode: codepos
mozillaHomeCountryName: pays
street: adr1 pro
mozillaWorkStreet2: adr2 pro
l: ville pro
st: etat pro
postalCode: codepro
c: payspro
title: fonction pro
ou: service pro
o: soc pro
mozillaWorkUrl: webpro
mozillaHomeUrl: web
birthyear: 1946
birthmonth: 12
birthday: 04
mozillaCustom1: d1
mozillaCustom2: d2
mozillaCustom3: d3
mozillaCustom4: d4
description: notes
convention:
- our "LDIF records" are inetOrgPerson + mozillaAbPersonAlpha + a few custom
fields (categories)
- all keys are lowercase
*/
@implementation NGVCard (SOGoExtensions) @implementation NGVCard (SOGoExtensions)
- (NSString *) ldifString /* LDIF -> VCARD */
- (CardElement *) _elementWithTag: (NSString *) elementTag
ofType: (NSString *) type
{ {
NSMutableString *rc; NSArray *elements;
NSString *buffer;
NSArray *array;
NSMutableArray *marray;
NSMutableDictionary *entry;
CardElement *element; CardElement *element;
id tmp;
entry = [NSMutableDictionary dictionary]; elements = [self childrenWithTag: elementTag
andAttribute: @"type" havingValue: type];
if ([elements count] > 0)
element = [elements objectAtIndex: 0];
else
{
element = [CardElement elementWithTag: elementTag];
[element addType: type];
[self addChild: element];
}
[entry setObject: [NSString stringWithFormat: @"cn=%@,mail=%@", return element;
[self fn], [self preferredEMail]] }
forKey: @"dn"];
[entry setObject: [NSArray arrayWithObjects: @"top", @"person", - (void) _setPhoneValues: (NSDictionary *) ldifRecord
@"organizationalPerson", @"inetOrgPerson", {
@"mozillaAbPersonObsolete", nil] CardElement *phone;
forKey: @"objectclass"];
phone = [self _elementWithTag: @"tel" ofType: @"work"];
[phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"home"];
[phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"cell"];
[phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"fax"];
[phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]
forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"pager"];
[phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""];
}
- (void) _setEmails: (NSDictionary *) ldifRecord
{
CardElement *mail, *homeMail;
mail = [self _elementWithTag: @"email" ofType: @"work"];
[mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""];
homeMail = [self _elementWithTag: @"email" ofType: @"home"];
[homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""];
[[self uniqueChildWithTag: @"x-mozilla-html"]
setSingleValue: [ldifRecord objectForKey: @"mozillausehtmlmail"]
forKey: @""];
}
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord
{
CardElement *element;
NSArray *units;
NSInteger year, yearOfToday, month, day;
NSCalendarDate *now;
NSString *ou;
[self setNWithFamily: [ldifRecord objectForKey: @"sn"]
given: [ldifRecord objectForKey: @"givenname"]
additional: nil prefixes: nil suffixes: nil];
[self setNickname: [ldifRecord objectForKey: @"mozillanickname"]];
[self setFn: [ldifRecord objectForKey: @"displayname"]];
[self setTitle: [ldifRecord objectForKey: @"title"]];
element = [self _elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet2"]
atIndex: 1 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestreet"]
atIndex: 2 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomelocalityname"]
atIndex: 3 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomestate"]
atIndex: 4 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomepostalcode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"mozillahomecountryname"]
atIndex: 6 forKey: @""];
element = [self _elementWithTag: @"adr" ofType: @"work"];
[element setSingleValue: [ldifRecord objectForKey: @"mozillaworkstreet2"]
atIndex: 1 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"street"]
atIndex: 2 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"l"]
atIndex: 3 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"st"]
atIndex: 4 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"postalcode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [ldifRecord objectForKey: @"c"]
atIndex: 6 forKey: @""];
ou = [ldifRecord objectForKey: @"ou"];
if (ou)
units = [NSArray arrayWithObject: ou];
else
units = nil;
[self setOrg: [ldifRecord objectForKey: @"o"]
units: units];
[self _setPhoneValues: ldifRecord];
[self _setEmails: ldifRecord];
[[self _elementWithTag: @"url" ofType: @"home"]
setSingleValue: [ldifRecord objectForKey: @"mozillahomeurl"] forKey: @""];
[[self _elementWithTag: @"url" ofType: @"work"]
setSingleValue: [ldifRecord objectForKey: @"mozillaworkurl"] forKey: @""];
[[self uniqueChildWithTag: @"x-aim"]
setSingleValue: [ldifRecord objectForKey: @"nsaimid"]
forKey: @""];
now = [NSCalendarDate date];
year = [[ldifRecord objectForKey: @"birthyear"] intValue];
if (year < 100)
{
yearOfToday = [now yearOfCommonEra];
if (year == 0)
year = yearOfToday;
else if (yearOfToday < (year + 2000))
year += 1900;
else
year += 2000;
}
month = [[ldifRecord objectForKey: @"birthmonth"] intValue];
day = [[ldifRecord objectForKey: @"birthday"] intValue];
if (year && month && day)
[self setBday: [NSString stringWithFormat: @"%.4d-%.2d-%.2d",
year, month, day]];
else
[self setBday: @""];
[self setNote: [ldifRecord objectForKey: @"description"]];
[self setCategories: [ldifRecord objectForKey: @"vcardcategories"]];
[self cleanupEmptyChildren];
}
/* VCARD -> LDIF */
- (NSString *) _simpleValueForType: (NSString *) aType
inArray: (NSArray *) anArray
excluding: (NSString *) aTypeToExclude
{
NSArray *elements;
NSString *value;
elements = [anArray cardElementsWithAttribute: @"type"
havingValue: aType];
value = nil;
if ([elements count] > 0)
{
CardElement *ce;
int i;
for (i = 0; i < [elements count]; i++)
{
ce = [elements objectAtIndex: i];
value = [ce flattenedValuesForKey: @""];
if (!aTypeToExclude)
break;
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
break;
value = nil;
}
}
return value;
}
- (void) _setValue: (NSString *) key
to: (NSString *) aValue
inLDIFRecord: (NSMutableDictionary *) ldifRecord
{
if (!aValue)
aValue = @"";
[ldifRecord setObject: aValue forKey: key];
}
- (void) _setupEmailFieldsInLDIFRecord: (NSMutableDictionary *) ldifRecord
{
NSArray *elements;
NSString *workMail, *homeMail, *potential;
unsigned int max;
elements = [self childrenWithTag: @"email"];
max = [elements count];
workMail = [self _simpleValueForType: @"work"
inArray: elements excluding: nil];
homeMail = [self _simpleValueForType: @"home"
inArray: elements excluding: nil];
if (max > 0)
{
potential = [[elements objectAtIndex: 0] flattenedValuesForKey: @""];
if (!workMail)
{
if (homeMail && homeMail == potential)
{
if (max > 1)
workMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""];
}
else
workMail = potential;
}
if (!homeMail && max > 1)
{
if (workMail && workMail == potential)
homeMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""];
else
homeMail = potential;
}
}
[self _setValue: @"mail" to: workMail inLDIFRecord: ldifRecord];
[self _setValue: @"mozillasecondemail" to: homeMail inLDIFRecord: ldifRecord];
[self _setValue: @"mozillausehtmlmail"
to: [[self uniqueChildWithTag: @"x-mozilla-html"]
flattenedValuesForKey: @""]
inLDIFRecord: ldifRecord];
}
- (void) _setupOrgFieldsInLDIFRecord: (NSMutableDictionary *) ldifRecord
{
NSMutableArray *orgServices;
CardElement *org;
NSString *service;
NSUInteger count, max;
org = [self org];
[self _setValue: @"o"
to: [org flattenedValueAtIndex: 0 forKey: @""]
inLDIFRecord: ldifRecord];
max = [[org valuesForKey: @""] count];
if (max > 1)
{
orgServices = [NSMutableArray arrayWithCapacity: max];
for (count = 1; count < max; count++)
{
service = [org flattenedValueAtIndex: count forKey: @""];
if ([service length] > 0)
[orgServices addObject: service];
}
[self _setValue: @"ou"
to: [orgServices componentsJoinedByString: @", "]
inLDIFRecord: ldifRecord];
}
}
- (NSMutableDictionary *) asLDIFRecord
{
NSArray *elements, *categories;
CardElement *element;
NSMutableDictionary *ldifRecord;
NSCalendarDate *birthDay;
NSString *dn, *stringValue, *stringValue2;
ldifRecord = [NSMutableDictionary dictionaryWithCapacity: 32];
[ldifRecord setObject: [NSArray arrayWithObjects: @"top", @"inetOrgPerson",
@"mozillaAbPersonAlpha", nil]
forKey: @"objectClass"];
element = [self n]; element = [self n];
tmp = [element flattenedValueAtIndex: 1 forKey: @""]; [self _setValue: @"sn"
if ([tmp length] > 0) to: [element flattenedValueAtIndex: 0 forKey: @""]
[entry setObject: tmp forKey: @"givenName"]; inLDIFRecord: ldifRecord];
[self _setValue: @"givenname"
tmp = [element flattenedValueAtIndex: 0 forKey: @""]; to: [element flattenedValueAtIndex: 1 forKey: @""]
if ([tmp length] > 0) inLDIFRecord: ldifRecord];
[entry setObject: tmp forKey: @"sn"]; [self _setValue: @"displayname" to: [self fn]
inLDIFRecord: ldifRecord];
[self _setValue: @"mozillanickname" to: [self nickname]
inLDIFRecord: ldifRecord];
tmp = [self fn]; elements = [self childrenWithTag: @"tel"];
if (tmp) // We do this (exclude FAX) in order to avoid setting the WORK number as the FAX
[entry setObject: tmp forKey: @"cn"]; // one if we do see the FAX field BEFORE the WORK number.
[self _setValue: @"telephonenumber"
tmp = [self preferredEMail]; to: [self _simpleValueForType: @"work" inArray: elements
if (tmp) excluding: @"fax"]
[entry setObject: tmp forKey: @"mail"]; inLDIFRecord: ldifRecord];
[self _setValue: @"homephone"
[entry setObject: @"0Z" forKey: @"modifytimestamp"]; to: [self _simpleValueForType: @"home" inArray: elements
excluding: @"fax"]
inLDIFRecord: ldifRecord];
[self _setValue: @"mobile"
to: [self _simpleValueForType: @"cell" inArray: elements
excluding: nil]
inLDIFRecord: ldifRecord];
[self _setValue: @"facsimiletelephonenumber"
to: [self _simpleValueForType: @"fax" inArray: elements
excluding: nil]
inLDIFRecord: ldifRecord];
[self _setValue: @"pager"
to: [self _simpleValueForType: @"pager" inArray: elements
excluding: nil]
inLDIFRecord: ldifRecord];
buffer = [self nickname]; // If we don't have a "home" and "work" phone number but
if (buffer && [buffer length] > 0) // we have a "voice" one defined, we set it to the "work" value
[entry setObject: buffer forKey: @"mozillaNickname"]; // This can happen when we have :
// VERSION:2.1
// N:name;surname;;;;
// TEL;VOICE;HOME:
// TEL;VOICE;WORK:
// TEL;PAGER:
// TEL;FAX;WORK:
// TEL;CELL:514 123 1234
// TEL;VOICE:450 456 6789
// ADR;HOME:;;;;;;
// ADR;WORK:;;;;;;
// ADR:;;;;;;
if ([[ldifRecord objectForKey: @"telephonenumber"] length] == 0 &&
[[ldifRecord objectForKey: @"homephone"] length] == 0 &&
[elements count] > 0)
[self _setValue: @"telephonenumber"
to: [self _simpleValueForType: @"voice" inArray: elements
excluding: nil]
inLDIFRecord: ldifRecord];
marray = [NSMutableArray arrayWithArray: [self childrenWithTag: @"email"]]; [self _setupEmailFieldsInLDIFRecord: ldifRecord];
[marray removeObjectsInArray: [self childrenWithTag: @"email"
andAttribute: @"type" [self _setValue: @"nsaimid"
havingValue: @"pref"]]; to: [[self uniqueChildWithTag: @"x-aim"]
if ([marray count]) flattenedValuesForKey: @""]
inLDIFRecord: ldifRecord];
elements = [self childrenWithTag: @"adr"
andAttribute: @"type" havingValue: @"work"];
if (elements && [elements count] > 0)
{ {
buffer = [[marray objectAtIndex: [marray count]-1] element = [elements objectAtIndex: 0];
flattenedValuesForKey: @""]; [self _setValue: @"mozillaworkstreet2"
to: [element flattenedValueAtIndex: 1 forKey: @""]
if ([buffer caseInsensitiveCompare: [self preferredEMail]] != NSOrderedSame) inLDIFRecord: ldifRecord];
[entry setObject: buffer forKey: @"mozillaSecondEmail"]; [self _setValue: @"street"
to: [element flattenedValueAtIndex: 2 forKey: @""]
inLDIFRecord: ldifRecord];
[self _setValue: @"l"
to: [element flattenedValueAtIndex: 3 forKey: @""]
inLDIFRecord: ldifRecord];
[self _setValue: @"st"
to: [element flattenedValueAtIndex: 4 forKey: @""]
inLDIFRecord: ldifRecord];
[self _setValue: @"postalcode"
to: [element flattenedValueAtIndex: 5 forKey: @""]
inLDIFRecord: ldifRecord];
[self _setValue: @"c"
to: [element flattenedValueAtIndex: 6 forKey: @""]
inLDIFRecord: ldifRecord];
} }
array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"home"]; elements = [self childrenWithTag: @"adr"
if ([array count]) andAttribute: @"type" havingValue: @"home"];
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] if (elements && [elements count] > 0)
forKey: @"homePhone"];
array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"fax"];
if ([array count])
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""]
forKey: @"fax"];
array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"cell"];
if ([array count])
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""]
forKey: @"mobile"];
array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"pager"];
if ([array count])
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""]
forKey: @"pager"];
array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"home"];
if ([array count])
{ {
tmp = [array objectAtIndex: 0]; element = [elements objectAtIndex: 0];
[entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""] [self _setValue: @"mozillahomestreet2"
forKey: @"mozillaHomeStreet2"]; to: [element flattenedValueAtIndex: 1 forKey: @""]
[entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] inLDIFRecord: ldifRecord];
forKey: @"homeStreet"]; [self _setValue: @"mozillahomestreet"
[entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] to: [element flattenedValueAtIndex: 2 forKey: @""]
forKey: @"mozillaHomeLocalityName"]; inLDIFRecord: ldifRecord];
[entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] [self _setValue: @"mozillahomelocalityname"
forKey: @"mozillaHomeState"]; to: [element flattenedValueAtIndex: 3 forKey: @""]
[entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] inLDIFRecord: ldifRecord];
forKey: @"mozillaHomePostalCode"]; [self _setValue: @"mozillahomestate"
[entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] to: [element flattenedValueAtIndex: 4 forKey: @""]
forKey: @"mozillaHomeCountryName"]; inLDIFRecord: ldifRecord];
[self _setValue: @"mozillahomepostalcode"
to: [element flattenedValueAtIndex: 5 forKey: @""]
inLDIFRecord: ldifRecord];
[self _setValue: @"mozillahomecountryname"
to: [element flattenedValueAtIndex: 6 forKey: @""]
inLDIFRecord: ldifRecord];
} }
element = [self org]; elements = [self childrenWithTag: @"url"];
tmp = [element flattenedValueAtIndex: 0 forKey: @""]; [self _setValue: @"mozillaworkurl"
if ([tmp length] > 0) to: [self _simpleValueForType: @"work" inArray: elements
[entry setObject: tmp forKey: @"o"]; excluding: nil]
inLDIFRecord: ldifRecord];
array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]; [self _setValue: @"mozillahomeurl"
if ([array count]) to: [self _simpleValueForType: @"home" inArray: elements
excluding: nil]
inLDIFRecord: ldifRecord];
// If we don't have a "work" or "home" URL but we still have
// an URL field present, let's add it to the "home" value
if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] == 0 &&
[[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 &&
[elements count] > 0)
[self _setValue: @"mozillahomeurl"
to: [[elements objectAtIndex: 0]
flattenedValuesForKey: @""]
inLDIFRecord: ldifRecord];
// If we do have a "work" URL but no "home" URL but two
// values URLs present, let's add the second one as the home URL
else if ([[ldifRecord objectForKey: @"mozillaworkurl"] length] > 0 &&
[[ldifRecord objectForKey: @"mozillahomeurl"] length] == 0 &&
[elements count] > 1)
{ {
tmp = [array objectAtIndex: 0]; int i;
[entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""]
forKey: @"mozillaWorkStreet2"]; for (i = 0; i < [elements count]; i++)
[entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] {
forKey: @"street"]; if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""]
[entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] caseInsensitiveCompare: [ldifRecord objectForKey: @"mozillaworkurl"]] != NSOrderedSame)
forKey: @"l"]; {
[entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] [self _setValue: @"mozillahomeurl"
forKey: @"st"]; to: [[elements objectAtIndex: i]
[entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] flattenedValuesForKey: @""]
forKey: @"postalCode"]; inLDIFRecord: ldifRecord];
[entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] break;
forKey: @"c"]; }
}
} }
[self _setValue: @"title" to: [self title] inLDIFRecord: ldifRecord];
[self _setupOrgFieldsInLDIFRecord: ldifRecord];
array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"work"]; categories = [self categories];
if ([array count]) if ([categories count] > 0)
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] [ldifRecord setValue: categories forKey: @"vcardcategories"];
forKey: @"telephoneNumber"];
array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"work"]; birthDay = [[self bday] asCalendarDate];
if ([array count]) if (birthDay)
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] {
forKey: @"workurl"]; stringValue = [NSString stringWithFormat: @"%.4d", [birthDay yearOfCommonEra]];
[self _setValue: @"birthyear" to: stringValue inLDIFRecord: ldifRecord];
stringValue = [NSString stringWithFormat: @"%.2d", [birthDay monthOfYear]];
[self _setValue: @"birthmonth" to: stringValue inLDIFRecord: ldifRecord];
stringValue = [NSString stringWithFormat: @"%.2d", [birthDay dayOfMonth]];
[self _setValue: @"birthday" to: stringValue inLDIFRecord: ldifRecord];
}
[self _setValue: @"description" to: [self note] inLDIFRecord: ldifRecord];
array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"home"]; stringValue = [ldifRecord objectForKey: @"displayname"];
if ([array count]) stringValue2 = [ldifRecord objectForKey: @"mail"];
[entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] if ([stringValue length] > 0)
forKey: @"homeurl"]; {
if ([stringValue2 length] > 0)
dn = [NSString stringWithFormat: @"cn=%@,mail=%@",
stringValue, stringValue2];
else
dn = [NSString stringWithFormat: @"cn=%@", stringValue];
}
else if ([stringValue2 length] > 0)
dn = [NSString stringWithFormat: @"mail=%@", stringValue2];
else
dn = @"";
[ldifRecord setObject: dn forKey: @"dn"];
tmp = [self note]; return ldifRecord;
if (tmp && [tmp length])
[entry setObject: tmp forKey: @"description"];
rc = [NSMutableString stringWithString: [entry userRecordAsLDIFEntry]];
[rc appendFormat: @"\n"];
return rc;
} }
@end /* NGVCard */ @end /* NGVCard */

View File

@ -27,7 +27,7 @@
#import <NGCards/NGVCardReference.h> #import <NGCards/NGVCardReference.h>
#import <SOGo/NSDictionary+Utilities.h> #import "NSDictionary+LDIF.h"
#import "NGVList+SOGo.h" #import "NGVList+SOGo.h"
@ -65,7 +65,7 @@
} }
[entry setObject: members forKey: @"member"]; [entry setObject: members forKey: @"member"];
rc = [NSMutableString stringWithString: [entry userRecordAsLDIFEntry]]; rc = [NSMutableString stringWithString: [entry ldifRecordAsString]];
[rc appendFormat: @"\n"]; [rc appendFormat: @"\n"];
return rc; return rc;

View File

@ -0,0 +1,34 @@
/* NSDictionary+LDIF.h - this file is part of SOGo
*
* Copyright (C) 2011 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.
*/
#ifndef NSDICTIONARY_LDIF_H
#define NSDICTIONARY_LDIF_H
#import <Foundation/NSDictionary.h>
@interface NSDictionary (SOGoLDIF)
- (NSString *) ldifRecordAsString;
@end
#endif /* NSDICTIONARY_LDIF_H */

View File

@ -0,0 +1,111 @@
/* NSDictionary+LDIF.m - this file is part of SOGo
*
* Copyright (C) 2011 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 <NGExtensions/NGBase64Coding.h>
#import "NSString+LDIF.h"
#import "NSDictionary+LDIF.h"
@implementation NSDictionary (SOGoLDIF)
- (void) _appendLDIFKey: (NSString *) key
value: (NSString *) value
toString: (NSMutableString *) ldifString
{
if ([value isKindOfClass: [NSString class]] && [value length] > 0)
{
if ([value mustEncodeLDIFValue])
[ldifString appendFormat: @"%@:: %@\n",
key, [value stringByEncodingBase64]];
else
[ldifString appendFormat: @"%@: %@\n", key, value];
}
}
- (void) _appendLDIFKey: (NSString *) key
toString: (NSMutableString *) ldifString
{
id value;
int count, max;
value = [self objectForKey: key];
if ([value isKindOfClass: [NSArray class]])
{
max = [value count];
for (count = 0; count < max; count++)
[self _appendLDIFKey: key value: [value objectAtIndex: count]
toString: ldifString];
}
else
[self _appendLDIFKey: key value: [self objectForKey: key]
toString: ldifString];
}
- (void) _appendObjectClassesToString: (NSMutableString *) ldifString
{
NSEnumerator *classes;
NSString *currentClass;
NSArray *objectClass;
objectClass = [self objectForKey: @"objectClass"];
if ([objectClass isKindOfClass: [NSString class]])
[self _appendLDIFKey: @"objectClass" value: (NSString *) objectClass
toString: ldifString];
else
{
classes = [objectClass objectEnumerator];
while ((currentClass = [classes nextObject]))
[self _appendLDIFKey: @"objectClass" value: currentClass
toString: ldifString];
}
}
- (NSString *) ldifRecordAsString
{
NSArray *keys;
NSMutableString *ldifString;
NSUInteger count, max;
NSString *currentKey;
// {CalendarAccess = YES; MailAccess = YES; c_cn = "Wolfgang Sourdeau"; c_emails = ("wolfgang@test.com"); c_name = "wolfgang@test.com"; c_uid = "wolfgang@test.com"; cn = "wolfgang@test.com"; displayName = "Wolfgang Sourdeau"; dn = "cn=wolfgang@test.com,ou=evariste,o=inverse.ca"; givenName = Wolfgang; mail = "wolfgang@test.com"; objectClass = organizationalPerson; sn = Sourdeau; }
ldifString = [NSMutableString string];
[self _appendLDIFKey: @"dn" toString: ldifString];
[self _appendObjectClassesToString: ldifString];
keys = [self allKeys];
max = [keys count];
for (count = 0; count < max; count++)
{
currentKey = [keys objectAtIndex: count];
if (!([currentKey hasPrefix: @"objectClass"]
|| [currentKey isEqualToString: @"dn"]))
[self _appendLDIFKey: currentKey toString: ldifString];
}
return ldifString;
}
@end

View File

@ -0,0 +1,34 @@
/* NSString+LDIF.h - this file is part of SOGo
*
* Copyright (C) 2011 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.
*/
#ifndef NSSTRING_LDIF_H
#define NSSTRING_LDIF_H
#import <Foundation/NSString.h>
@interface NSString (SOGoLDIF)
- (BOOL) mustEncodeLDIFValue;
@end
#endif /* NSSTRING_LDIF_H */

View File

@ -0,0 +1,67 @@
/* NSString+LDIF.m - this file is part of SOGo
*
* Copyright (C) 2011 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/NSCharacterSet.h>
#import "NSString+LDIF.h"
@implementation NSString (SOGoLDIF)
static NSMutableCharacterSet *safeLDIFChars = nil;
static NSMutableCharacterSet *safeLDIFStartChars = nil;
- (void) _initSafeLDIFChars
{
safeLDIFChars = [NSMutableCharacterSet new];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x01, 9)];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x0b, 2)];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x0e, 114)];
safeLDIFStartChars = [safeLDIFChars mutableCopy];
[safeLDIFStartChars removeCharactersInString: @" :<"];
}
- (BOOL) mustEncodeLDIFValue
{
int count, max;
BOOL rc;
if (!safeLDIFChars)
[self _initSafeLDIFChars];
rc = NO;
max = [self length];
if (max > 0)
{
if ([safeLDIFStartChars characterIsMember: [self characterAtIndex: 0]])
for (count = 1; !rc && count < max; count++)
rc = ![safeLDIFChars
characterIsMember: [self characterAtIndex: count]];
else
rc = YES;
}
return rc;
}
@end

View File

@ -30,11 +30,6 @@
int photoID; int photoID;
} }
+ (id) entryPhotoWithID: (int) photoId
inContainer: (id) container;
- (void) setPhotoID: (int) newPhotoID;
- (NSString *) davContentType; - (NSString *) davContentType;
@end @end

View File

@ -35,36 +35,9 @@
@implementation SOGoContactEntryPhoto @implementation SOGoContactEntryPhoto
+ (id) entryPhotoWithID: (int) photoID
inContainer: (id) container
{
id photo;
photo
= [super objectWithName: [NSString stringWithFormat: @"photo%d", photoID]
inContainer: container];
[photo setPhotoID: photoID];
return photo;
}
- (void) setPhotoID: (int) newPhotoID
{
photoID = newPhotoID;
}
- (NGVCardPhoto *) photo - (NGVCardPhoto *) photo
{ {
NGVCardPhoto *photo; return (NGVCardPhoto *) [[container vCard] firstChildWithTag: @"photo"];
NSArray *photoElements;
photoElements = [[container vCard] childrenWithTag: @"photo"];
if ([photoElements count] > photoID)
photo = [photoElements objectAtIndex: photoID];
else
photo = nil;
return photo;
} }
- (id) GETAction: (WOContext *) localContext - (id) GETAction: (WOContext *) localContext

View File

@ -27,6 +27,10 @@
@interface SOGoContactFolders : SOGoParentFolder @interface SOGoContactFolders : SOGoParentFolder
- (NSException *) renameLDAPAddressBook: (NSString *) sourceID
withDisplayName: (NSString *) newDisplayName;
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID;
@end @end
#endif /* SOGOCONTACTFOLDERS_H */ #endif /* SOGOCONTACTFOLDERS_H */

View File

@ -33,6 +33,7 @@
#import <Foundation/NSEnumerator.h> #import <Foundation/NSEnumerator.h>
#import <NGObjWeb/WOContext+SoObjects.h> #import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <DOM/DOMElement.h> #import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h> #import <DOM/DOMProtocols.h>
@ -45,6 +46,7 @@
#import "SOGoContactGCSFolder.h" #import "SOGoContactGCSFolder.h"
#import "SOGoContactSourceFolder.h" #import "SOGoContactSourceFolder.h"
#import "SOGoContactFolders.h" #import "SOGoContactFolders.h"
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav" #define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@ -60,6 +62,62 @@
return [SOGoContactGCSFolder class]; return [SOGoContactGCSFolder class];
} }
- (void) _fetchLDAPAddressBooks: (id <SOGoSource>) source
{
id <SOGoSource> abSource;
SOGoContactSourceFolder *folder;
NSArray *abSources;
NSUInteger count, max;
NSString *name;
abSources = [source addressBookSourcesForUser: owner];
max = [abSources count];
for (count = 0; count < max; count++)
{
abSource = [abSources objectAtIndex: count];
name = [abSource sourceID];
folder = [SOGoContactSourceFolder folderWithName: name
andDisplayName: [abSource displayName]
inContainer: self];
[folder setSource: abSource];
[folder setIsPersonalSource: YES];
[subFolders setObject: folder forKey: name];
}
}
- (NSException *) appendPersonalSources
{
SOGoUser *currentUser;
NSException *result;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
if ([source hasUserAddressBooks])
{
result = nil;
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
[self _fetchLDAPAddressBooks: source];
if (![subFolders objectForKey: @"personal"])
{
result = [source addAddressBookSource: @"personal"
withDisplayName: [self defaultFolderName]
forUser: owner];
if (!result)
[self _fetchLDAPAddressBooks: source];
}
}
}
else
result = [super appendPersonalSources];
return result;
}
- (NSException *) appendSystemSources - (NSException *) appendSystemSources
{ {
SOGoUserManager *um; SOGoUserManager *um;
@ -101,6 +159,88 @@
return nil; return nil;
} }
- (NSException *) newFolderWithName: (NSString *) name
andNameInContainer: (NSString *) newNameInContainer
{
SOGoUser *currentUser;
NSException *result;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
if ([source hasUserAddressBooks])
{
result = nil;
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
{
result = [source addAddressBookSource: newNameInContainer
withDisplayName: name
forUser: owner];
if (!result)
[self _fetchLDAPAddressBooks: source];
}
}
else
result = [super newFolderWithName: name
andNameInContainer: newNameInContainer];
return result;
}
- (NSException *) renameLDAPAddressBook: (NSString *) sourceID
withDisplayName: (NSString *) newDisplayName
{
NSException *result;
SOGoUser *currentUser;
id <SOGoSource> source;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
result = [source renameAddressBookSource: sourceID
withDisplayName: newDisplayName
forUser: owner];
else
result = [NSException exceptionWithHTTPStatus: 403
reason: @"operation denied"];
return result;
}
- (NSException *) removeLDAPAddressBook: (NSString *) sourceID
{
NSException *result;
SOGoUser *currentUser;
id <SOGoSource> source;
if ([sourceID isEqualToString: @"personal"])
result = [NSException exceptionWithHTTPStatus: 403
reason: @"folder 'personal' cannot be deleted"];
else
{
result = nil;
currentUser = [context activeUser];
source = [currentUser authenticationSource];
/* We don't handle ACLs for user LDAP addressbooks yet, therefore only
the owner has access to his addressbooks. */
if (activeUserIsOwner
|| [[currentUser login] isEqualToString: owner])
result = [source removeAddressBookSource: sourceID
forUser: owner];
else
result = [NSException exceptionWithHTTPStatus: 403
reason: @"operation denied"];
}
return result;
}
- (NSString *) defaultFolderName - (NSString *) defaultFolderName
{ {

View File

@ -19,11 +19,15 @@
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <NGCards/NGVCard.h> #import <NGCards/NGVCard.h>
#import <NGCards/NGVCardPhoto.h>
#import "NGVCard+SOGo.h"
#import "SOGoContactEntryPhoto.h" #import "SOGoContactEntryPhoto.h"
#import "SOGoContactGCSEntry.h" #import "SOGoContactGCSEntry.h"
@ -62,6 +66,21 @@
return card; return card;
} }
- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord
{
[[self vCard] updateFromLDIFRecord: newLDIFRecord];
}
- (NSDictionary *) ldifRecord
{
return [[self vCard] asLDIFRecord];
}
- (BOOL) hasPhoto
{
return ([[self vCard] firstChildWithTag: @"photo"] != nil);
}
/* actions */ /* actions */
- (id) lookupName: (NSString *) lookupName - (id) lookupName: (NSString *) lookupName
@ -69,16 +88,12 @@
acquire: (BOOL) acquire acquire: (BOOL) acquire
{ {
id obj; id obj;
int photoIndex;
NSArray *photoElements;
if ([lookupName hasPrefix: @"photo"]) if ([lookupName isEqualToString: @"photo"])
{ {
photoElements = [[self vCard] childrenWithTag: @"photo"]; if ([self hasPhoto])
photoIndex = [[lookupName substringFromIndex: 5] intValue]; obj = [SOGoContactEntryPhoto objectWithName: lookupName
if (photoIndex > -1 && photoIndex < [photoElements count]) inContainer: self];
obj = [SOGoContactEntryPhoto entryPhotoWithID: photoIndex
inContainer: self];
else else
obj = nil; obj = nil;
} }
@ -127,13 +142,16 @@
/* specialized actions */ /* specialized actions */
- (void) save - (NSException *) save
{ {
NGVCard *vcard; NSException *result;
vcard = [self vCard]; if (card)
result = [self saveContentString: [card versitString]];
else
result = nil; /* TODO: we should probably return an exception instead */
[self saveContentString: [vcard versitString]]; return result;
} }
- (NSException *) saveContentString: (NSString *) newContent - (NSException *) saveContentString: (NSString *) newContent

View File

@ -45,6 +45,7 @@
#import <SOGo/NSDictionary+Utilities.h> #import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
#import <SOGo/NSObject+DAV.h> #import <SOGo/NSObject+DAV.h>
#import <SOGo/WORequest+SOGo.h>
#import "SOGoContactGCSEntry.h" #import "SOGoContactGCSEntry.h"
#import "SOGoContactGCSList.h" #import "SOGoContactGCSList.h"

View File

@ -31,8 +31,8 @@
@interface SOGoContactLDIFEntry : SOGoObject <SOGoContactObject> @interface SOGoContactLDIFEntry : SOGoObject <SOGoContactObject>
{ {
BOOL isNew;
NSDictionary *ldifEntry; NSDictionary *ldifEntry;
NGVCard *vcard;
} }
+ (SOGoContactLDIFEntry *) contactEntryWithName: (NSString *) newName + (SOGoContactLDIFEntry *) contactEntryWithName: (NSString *) newName
@ -42,6 +42,9 @@
withLDIFEntry: (NSDictionary *) newEntry withLDIFEntry: (NSDictionary *) newEntry
inContainer: (id) newContainer; inContainer: (id) newContainer;
- (BOOL) isNew;
- (void) setIsNew: (BOOL) newIsNew;
- (NSString *) davEntityTag; - (NSString *) davEntityTag;
@end @end

View File

@ -29,9 +29,13 @@
#import <NGCards/CardVersitRenderer.h> #import <NGCards/CardVersitRenderer.h>
#import <SOGo/SOGoBuild.h> #import <SOGo/SOGoBuild.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoPermissions.h>
#import "NGVCard+SOGo.h"
#import "SOGoContactGCSEntry.h" #import "SOGoContactGCSEntry.h"
#import "SOGoContactLDIFEntry.h" #import "SOGoContactLDIFEntry.h"
#import "SOGoContactSourceFolder.h"
@implementation SOGoContactLDIFEntry @implementation SOGoContactLDIFEntry
@ -42,8 +46,8 @@
SOGoContactLDIFEntry *entry; SOGoContactLDIFEntry *entry;
entry = [[self alloc] initWithName: newName entry = [[self alloc] initWithName: newName
withLDIFEntry: newEntry withLDIFEntry: newEntry
inContainer: newContainer]; inContainer: newContainer];
[entry autorelease]; [entry autorelease];
return entry; return entry;
@ -56,7 +60,7 @@
if ((self = [self initWithName: newName inContainer: newContainer])) if ((self = [self initWithName: newName inContainer: newContainer]))
{ {
ASSIGN (ldifEntry, newEntry); ASSIGN (ldifEntry, newEntry);
vcard = nil; isNew = NO;
} }
return self; return self;
@ -64,169 +68,34 @@
- (void) dealloc - (void) dealloc
{ {
[vcard release];
[ldifEntry release]; [ldifEntry release];
[super dealloc]; [super dealloc];
} }
- (BOOL) isNew
{
return isNew;
}
- (void) setIsNew: (BOOL) newIsNew
{
isNew = newIsNew;
}
- (NSString *) contentAsString - (NSString *) contentAsString
{ {
return [[self vCard] versitString]; return [[self vCard] versitString];
} }
- (void) _setPhonesOfVCard: (NGVCard *) vCard
{
NSString *info;
info = [ldifEntry objectForKey: @"telephonenumber"];
if (info)
[vCard addTel: info
types: [NSArray arrayWithObjects: @"work", @"voice", @"pref", nil]];
info = [ldifEntry objectForKey: @"homephone"];
if (info)
[vCard addTel: info
types: [NSArray arrayWithObjects: @"home", @"voice", nil]];
info = [ldifEntry objectForKey: @"fax"];
if (info)
[vCard addTel: info
types: [NSArray arrayWithObjects: @"work", @"fax", nil]];
info = [ldifEntry objectForKey: @"pager"];
if (info)
[vCard addTel: info
types: [NSArray arrayWithObjects: @"pager", nil]];
info = [ldifEntry objectForKey: @"mobile"];
if (info)
[vCard addTel: info
types: [NSArray arrayWithObjects: @"cell", @"voice", nil]];
// telephoneNumber: work phone
// homePhone: home phone
// fax: fax phone
// pager: page phone
// mobile: mobile phone
}
- (NGVCard *) vCard - (NGVCard *) vCard
{ {
NSString *info, *surname, *streetAddress, *location, *region, *postalCode, *country, *org, *orgunit; NGVCard *vcard;
CardElement *element;
unsigned int count;
if (!vcard) vcard = [NGVCard cardWithUid: [self nameInContainer]];
{ [vcard setProdID: [NSString
vcard = [[NGVCard alloc] initWithUid: [self nameInContainer]]; stringWithFormat: @"-//Inverse inc./SOGo %@//EN",
[vcard setVClass: @"PUBLIC"]; SOGoVersion]];
[vcard setProdID: [NSString [vcard updateFromLDIFRecord: [self ldifRecord]];
stringWithFormat: @"-//Inverse inc./SOGo %@//EN",
SOGoVersion]];
[vcard setProfile: @"VCARD"];
info = [ldifEntry objectForKey: @"c_cn"];
if (![info length])
{
info = [ldifEntry objectForKey: @"displayname"];
if (![info length])
info = [ldifEntry objectForKey: @"cn"];
}
[vcard setFn: info];
surname = [ldifEntry objectForKey: @"sn"];
if (!surname)
surname = [ldifEntry objectForKey: @"surname"];
[vcard setNWithFamily: surname
given: [ldifEntry objectForKey: @"givenname"]
additional: nil
prefixes: nil
suffixes: nil];
info = [ldifEntry objectForKey: @"title"];
if (info)
[vcard setTitle: info];
info = [ldifEntry objectForKey: @"mozillanickname"];
if (info)
[vcard setNickname: info];
/* If "c_info" is defined, we set as the NOTE value in order for
Thunderbird (or any other CardDAV client) to display it. */
info = [ldifEntry objectForKey: @"c_info"];
if (![info length])
info = [ldifEntry objectForKey: @"description"];
if ([info length])
[vcard setNote: info];
info = [ldifEntry objectForKey: @"mail"];
if (info)
[vcard addEmail: info
types: [NSArray arrayWithObjects: @"internet", @"pref", nil]];
[self _setPhonesOfVCard: vcard];
streetAddress = [ldifEntry objectForKey: @"street"];
if (!streetAddress)
streetAddress = [ldifEntry objectForKey: @"streetaddress"];
location = [ldifEntry objectForKey: @"l"];
if (!location)
location = [ldifEntry objectForKey: @"locality"];
region = [ldifEntry objectForKey: @"st"];
if (!region)
region = [ldifEntry objectForKey: @"region"];
postalCode = [ldifEntry objectForKey: @"postalcode"];
if (!postalCode)
postalCode = [ldifEntry objectForKey: @"zip"];
country = [ldifEntry objectForKey: @"c"];
if (!country)
country = [ldifEntry objectForKey: @"countryname"];
element = [CardElement elementWithTag: @"adr"];
[element setValue: 0 ofAttribute: @"type" to: @"work"];
if (streetAddress)
[element setSingleValue: streetAddress atIndex: 2 forKey: @""];
if (location)
[element setSingleValue: location atIndex: 3 forKey: @""];
if (region)
[element setSingleValue: region atIndex: 4 forKey: @""];
if (postalCode)
[element setSingleValue: postalCode atIndex: 5 forKey: @""];
if (country)
[element setSingleValue: country atIndex: 6 forKey: @""];
if (streetAddress || location || region || postalCode || country)
[vcard addChild: element];
// We handle the org/orgunit stuff
element = [CardElement elementWithTag: @"org"];
org = [ldifEntry objectForKey: @"o"];
orgunit = [ldifEntry objectForKey: @"ou"];
if (!orgunit)
orgunit = [ldifEntry objectForKey: @"orgunit"];
if (org)
[element setSingleValue: org atIndex: 0 forKey: @""];
if (orgunit)
[element setSingleValue: orgunit atIndex: 1 forKey: @""];
if (org || orgunit)
[vcard addChild: element];
info = [ldifEntry objectForKey: @"calFBURL"];
if (info)
[vcard addChildWithTag: @"FBURL"
types: nil
singleValue: info];
for (count = 1; count < 5; count++)
{
info = [ldifEntry objectForKey:
[NSString stringWithFormat: @"mozillacustom%d",
count]];
if (info)
[vcard addChildWithTag: [NSString stringWithFormat: @"CUSTOM%d",
count]
types: nil
singleValue: info];
}
}
return vcard; return vcard;
} }
@ -236,6 +105,21 @@
return NO; return NO;
} }
- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord
{
ASSIGN (ldifEntry, newLDIFRecord);
}
- (NSDictionary *) ldifRecord
{
return ldifEntry;
}
- (BOOL) hasPhoto
{
return NO;
}
- (NSString *) davEntityTag - (NSString *) davEntityTag
{ {
unsigned int hash; unsigned int hash;
@ -251,13 +135,47 @@
return @"text/x-vcard"; return @"text/x-vcard";
} }
- (NSArray *) aclsForUser: (NSString *) uid - (NSException *) save
{ {
return nil; return [(SOGoContactSourceFolder *) container saveLDIFEntry: self];
} }
- (void) save - (NSException *) delete
{ {
return [(SOGoContactSourceFolder *) container deleteLDIFEntry: self];
}
/* acl */
- (NSArray *) aclsForUser: (NSString *) uid
{
NSMutableArray *acls;
NSArray *containerAcls;
acls = [NSMutableArray array];
/* this is unused... */
// ownAcls = [container aclsForUser: uid
// forObjectAtPath: [self pathArrayToSOGoObject]];
// [acls addObjectsFromArray: ownAcls];
containerAcls = [container aclsForUser: uid];
if ([containerAcls count] > 0)
{
[acls addObjectsFromArray: containerAcls];
/* The creation of an object is actually a "modification" to an
unexisting object. When the object is new, we give the
"ObjectCreator" the "ObjectModifier" role temporarily while we
disallow the "ObjectModifier" users to modify them, unless they are
ObjectCreators too. */
if (isNew)
{
if ([containerAcls containsObject: SOGoRole_ObjectCreator])
[acls addObject: SOGoRole_ObjectEditor];
else
[acls removeObject: SOGoRole_ObjectEditor];
}
}
return acls;
} }
/* DAV */ /* DAV */

View File

@ -22,25 +22,20 @@
#ifndef __Contacts_SOGoContactObject_H__ #ifndef __Contacts_SOGoContactObject_H__
#define __Contacts_SOGoContactObject_H__ #define __Contacts_SOGoContactObject_H__
/*
SOGoContactObject
Represents a single contact. This SOPE controller object manages all the
attendee storages (that is, it might store into multiple folders for meeting
appointments!).
Note: SOGoContactObject do not need to exist yet. They can also be "new"
appointments with an externally generated unique key.
*/
@class NSDictionary; @class NSDictionary;
@class NSString;
@class NGVCard; @class NGVCard;
@protocol SOGoContactObject @protocol SOGoContactObject
- (NGVCard *) vCard; - (NGVCard *) vCard;
- (void) save; - (BOOL) hasPhoto;
/* web editing */
- (void) setLDIFRecord: (NSDictionary *) newLDIFRecord;
- (NSDictionary *) ldifRecord;
- (NSException *) save;
- (NSException *) delete;
@end @end

View File

@ -26,14 +26,16 @@
#import "SOGoContactFolder.h" #import "SOGoContactFolder.h"
#import "SOGoFolder+CardDAV.h" #import "SOGoFolder+CardDAV.h"
@class NSMutableDictionary; #import <SOGo/SOGoSource.h>
#import "../SOGo/SOGoSource.h" @class NSMutableDictionary;
@class SOGoContactLDIFEntry;
@interface SOGoContactSourceFolder : SOGoFolder <SOGoContactFolder> @interface SOGoContactSourceFolder : SOGoFolder <SOGoContactFolder>
{ {
id source; id <SOGoSource> source;
NSMutableDictionary *childRecords; NSMutableDictionary *childRecords;
BOOL isPersonalSource;
} }
+ (id) folderWithName: (NSString *) aName + (id) folderWithName: (NSString *) aName
@ -42,7 +44,13 @@
- (id) initWithName: (NSString *) newName - (id) initWithName: (NSString *) newName
andDisplayName: (NSString *) newDisplayName andDisplayName: (NSString *) newDisplayName
inContainer: (id) newContainer; inContainer: (id) newContainer;
- (void) setSource: (id) newSource; - (void) setSource: (id <SOGoSource>) newSource;
- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry;
- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry;
- (void) setIsPersonalSource: (BOOL) isPersonal;
- (BOOL) isPersonalSource;
@end @end

View File

@ -38,11 +38,16 @@
#import <EOControl/EOSortOrdering.h> #import <EOControl/EOSortOrdering.h>
#import <SaxObjC/XMLNamespaces.h> #import <SaxObjC/XMLNamespaces.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h> #import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WORequest+SOGo.h>
#import "SOGoContactFolders.h"
#import "SOGoContactGCSFolder.h"
#import "SOGoContactLDIFEntry.h" #import "SOGoContactLDIFEntry.h"
#import "SOGoContactSourceFolder.h" #import "SOGoContactSourceFolder.h"
@ -97,11 +102,26 @@
[super dealloc]; [super dealloc];
} }
- (void) setSource: (id) newSource - (void) setSource: (id <SOGoSource>) newSource
{ {
ASSIGN (source, newSource); ASSIGN (source, newSource);
} }
- (id <SOGoSource>) source
{
return source;
}
- (void) setIsPersonalSource: (BOOL) isPersonal
{
isPersonalSource = isPersonal;
}
- (BOOL) isPersonalSource
{
return isPersonalSource;
}
- (NSString *) groupDavResourceType - (NSString *) groupDavResourceType
{ {
return @"vcard-collection"; return @"vcard-collection";
@ -127,7 +147,10 @@
acquire: (BOOL) acquire acquire: (BOOL) acquire
{ {
NSDictionary *ldifEntry; NSDictionary *ldifEntry;
id obj; SOGoContactLDIFEntry *obj;
NSString *url;
BOOL isNew = NO;
NSArray *baseClasses;
/* first check attributes directly bound to the application */ /* first check attributes directly bound to the application */
obj = [super lookupName: objectName inContext: lookupContext acquire: NO]; obj = [super lookupName: objectName inContext: lookupContext acquire: NO];
@ -140,11 +163,28 @@
ldifEntry = [source lookupContactEntry: objectName]; ldifEntry = [source lookupContactEntry: objectName];
if (ldifEntry) if (ldifEntry)
[childRecords setObject: ldifEntry forKey: objectName]; [childRecords setObject: ldifEntry forKey: objectName];
else if ([self isValidContentName: objectName])
{
url = [[[lookupContext request] uri] urlWithoutParameters];
if ([url hasSuffix: @"AsContact"])
{
baseClasses = [NSArray arrayWithObjects: @"inetorgperson",
@"mozillaabpersonalpha", nil];
ldifEntry = [NSMutableDictionary
dictionaryWithObject: baseClasses
forKey: @"objectclass"];
isNew = YES;
}
}
} }
if (ldifEntry) if (ldifEntry)
obj = [SOGoContactLDIFEntry contactEntryWithName: objectName {
withLDIFEntry: ldifEntry obj = [SOGoContactLDIFEntry contactEntryWithName: objectName
inContainer: self]; withLDIFEntry: ldifEntry
inContainer: self];
if (isNew)
[obj setIsNew: YES];
}
else else
obj = [NSException exceptionWithHTTPStatus: 404]; obj = [NSException exceptionWithHTTPStatus: 404];
} }
@ -157,6 +197,19 @@
return [source allEntryIDs]; return [source allEntryIDs];
} }
- (NSException *) saveLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry
{
return (([ldifEntry isNew])
? [source addContactEntry: [ldifEntry ldifRecord]
withID: [ldifEntry nameInContainer]]
: [source updateContactEntry: [ldifEntry ldifRecord]]);
}
- (NSException *) deleteLDIFEntry: (SOGoContactLDIFEntry *) ldifEntry
{
return [source removeContactEntryWithID: [ldifEntry nameInContainer]];
}
- (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord - (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord
{ {
NSMutableDictionary *newRecord; NSMutableDictionary *newRecord;
@ -267,7 +320,8 @@
result = nil; result = nil;
if ([filter length] > 0 && [criteria isEqualToString: @"name_or_address"]) if (([filter length] > 0 && [criteria isEqualToString: @"name_or_address"])
|| ![source listRequiresDot])
{ {
records = [source fetchContactsMatching: filter]; records = [source fetchContactsMatching: filter];
[childRecords setObjects: records [childRecords setObjects: records
@ -308,22 +362,88 @@
- (NSComparisonResult) compare: (id) otherFolder - (NSComparisonResult) compare: (id) otherFolder
{ {
NSComparisonResult comparison; NSComparisonResult comparison;
BOOL otherIsPersonal;
if ([NSStringFromClass([otherFolder class]) otherIsPersonal = ([otherFolder isKindOfClass: [SOGoContactGCSFolder class]]
isEqualToString: @"SOGoContactGCSFolder"]) || ([otherFolder isKindOfClass: isa] && [otherFolder isPersonalSource]));
comparison = NSOrderedDescending;
if (isPersonalSource)
{
if (otherIsPersonal && ![nameInContainer isEqualToString: @"personal"])
{
if ([[otherFolder nameInContainer] isEqualToString: @"personal"])
comparison = NSOrderedDescending;
else
comparison
= [[self displayName]
localizedCaseInsensitiveCompare: [otherFolder displayName]];
}
else
comparison = NSOrderedAscending;
}
else else
comparison {
= [[self displayName] if (otherIsPersonal)
localizedCaseInsensitiveCompare: [otherFolder displayName]]; comparison = NSOrderedDescending;
else
comparison
= [[self displayName]
localizedCaseInsensitiveCompare: [otherFolder displayName]];
}
return comparison; return comparison;
} }
/* common methods */
- (NSException *) delete
{
NSException *error;
if (isPersonalSource)
{
error = [(SOGoContactFolders *) container
removeLDAPAddressBook: nameInContainer];
if (!error && [[context request] handledByDefaultHandler])
[self sendFolderAdvisoryTemplate: @"Removal"];
}
else
error = [NSException exceptionWithHTTPStatus: 501 /* not implemented */
reason: @"delete not available on system sources"];
return error;
}
- (void) renameTo: (NSString *) newName
{
NSException *error;
if (isPersonalSource)
{
if (![[source displayName] isEqualToString: newName])
{
error = [(SOGoContactFolders *) container
renameLDAPAddressBook: nameInContainer
withDisplayName: newName];
if (!error)
[self setDisplayName: newName];
}
}
/* If public source then method is ignored, maybe we should return an
NSException instead... */
}
/* acls */ /* acls */
- (NSString *) ownerInContext: (WOContext *) noContext - (NSString *) ownerInContext: (WOContext *) noContext
{ {
return @"nobody"; NSString *sourceOwner;
if (isPersonalSource)
sourceOwner = [[source modifiers] objectAtIndex: 0];
else
sourceOwner = @"nobody";
return sourceOwner;
} }
- (NSArray *) subscriptionRoles - (NSArray *) subscriptionRoles
@ -331,10 +451,26 @@
return [NSArray arrayWithObject: SoRole_Authenticated]; return [NSArray arrayWithObject: SoRole_Authenticated];
} }
/* TODO: this might change one day when we support LDAP acls */
- (NSArray *) aclsForUser: (NSString *) uid - (NSArray *) aclsForUser: (NSString *) uid
{ {
return nil; NSArray *acls, *modifiers;
static NSArray *modifierRoles = nil;
if (!modifierRoles)
modifierRoles = [[NSArray alloc] initWithObjects: @"Owner",
@"ObjectViewer",
@"ObjectEditor", @"ObjectCreator",
@"ObjectEraser", nil];
modifiers = [source modifiers];
if ([modifiers containsObject: uid])
acls = [modifierRoles copy];
else
acls = [NSArray new];
[acls autorelease];
return acls;
} }
@end @end

View File

@ -118,10 +118,10 @@
newString = [theString lowercaseString]; newString = [theString lowercaseString];
return ([theString isEqualToString: @"sn"] return ([newString isEqualToString: @"sn"]
|| [theString isEqualToString: @"givenname"] || [newString isEqualToString: @"givenname"]
|| [theString isEqualToString: @"mail"] || [newString isEqualToString: @"mail"]
|| [theString isEqualToString: @"telephonenumber"]); || [newString isEqualToString: @"telephonenumber"]);
} }
- (NSDictionary *) _parseContactFilter: (id <DOMElement>) filterElement - (NSDictionary *) _parseContactFilter: (id <DOMElement>) filterElement

View File

@ -28,6 +28,7 @@ SOGo_HEADER_FILES = \
\ \
SOGoUserManager.h \ SOGoUserManager.h \
LDAPSource.h \ LDAPSource.h \
LDAPSourceSchema.h \
SQLSource.h \ SQLSource.h \
SOGoUserProfile.h \ SOGoUserProfile.h \
SOGoDateFormatter.h \ SOGoDateFormatter.h \
@ -97,6 +98,7 @@ SOGo_OBJC_FILES = \
SOGoStartupLogger.m \ SOGoStartupLogger.m \
SOGoUserManager.m \ SOGoUserManager.m \
LDAPSource.m \ LDAPSource.m \
LDAPSourceSchema.m \
SQLSource.m \ SQLSource.m \
SOGoUserProfile.m \ SOGoUserProfile.m \
SOGoSQLUserProfile.m \ SOGoSQLUserProfile.m \

View File

@ -30,10 +30,12 @@
#include "SOGoSource.h" #include "SOGoSource.h"
#include "SOGoConstants.h" #include "SOGoConstants.h"
@class NSDictionary; @class LDAPSourceSchema;
@class NSString;
@class NGLdapConnection;
@class NGLdapEntry; @class NGLdapEntry;
@class NSException;
@class NSMutableArray;
@class NSMutableDictionary;
@class NSString;
@interface LDAPSource : NSObject <SOGoDNSource> @interface LDAPSource : NSObject <SOGoDNSource>
{ {
@ -41,6 +43,8 @@
int queryTimeout; int queryTimeout;
NSString *sourceID; NSString *sourceID;
NSString *displayName;
NSString *bindDN; // The bindDN/password could be either the source's one NSString *bindDN; // The bindDN/password could be either the source's one
NSString *password; // or the current user if _bindAsCurrentUser is set to YES NSString *password; // or the current user if _bindAsCurrentUser is set to YES
NSString *sourceBindDN; // while sourceBindDN/sourceBindPassword always belong to the source NSString *sourceBindDN; // while sourceBindDN/sourceBindPassword always belong to the source
@ -49,20 +53,26 @@
unsigned int port; unsigned int port;
NSString *encryption; NSString *encryption;
NSString *_filter; NSString *_filter;
BOOL _bindAsCurrentUser;
NSString *_scope; NSString *_scope;
NSString *baseDN; NSString *baseDN;
LDAPSourceSchema *schema;
NSString *IDField; // the first part of a user DN NSString *IDField; // the first part of a user DN
NSString *CNField; NSString *CNField;
NSString *UIDField; NSString *UIDField;
NSArray *mailFields, *searchFields; NSArray *mailFields, *searchFields;
NSString *IMAPHostField, *IMAPLoginField; NSString *IMAPHostField, *IMAPLoginField;
NSArray *bindFields; NSArray *bindFields;
BOOL _bindAsCurrentUser;
BOOL listRequiresDot;
NSString *domain; NSString *domain;
NSString *contactInfoAttribute; NSString *contactInfoAttribute;
NSDictionary *contactMapping;
NSArray *contactObjectClasses;
NSDictionary *modulesConstraints; NSDictionary *modulesConstraints;
NSMutableArray *searchAttributes; NSMutableArray *searchAttributes;
@ -76,6 +86,12 @@
NSString *multipleBookingsField; NSString *multipleBookingsField;
NSString *MSExchangeHostname; NSString *MSExchangeHostname;
/* user addressbooks */
NSString *abOU;
/* ACL */
NSArray *modifiers;
} }
- (void) setBindDN: (NSString *) newBindDN - (void) setBindDN: (NSString *) newBindDN
@ -97,6 +113,11 @@
kindField: (NSString *) newKindField kindField: (NSString *) newKindField
andMultipleBookingsField: (NSString *) newMultipleBookingsField; andMultipleBookingsField: (NSString *) newMultipleBookingsField;
/* This enable the convertion of a contact entry with inetOrgPerson and mozillaAbPerson
to and from an LDAP record */
- (void) setContactMapping: (NSDictionary *) newMapping
andObjectClasses: (NSArray *) newObjectClasses;
- (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID; - (NGLdapEntry *) lookupGroupEntryByUID: (NSString *) theUID;
- (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail; - (NGLdapEntry *) lookupGroupEntryByEmail: (NSString *) theEmail;
- (NGLdapEntry *) lookupGroupEntryByAttribute: (NSString *) theAttribute - (NGLdapEntry *) lookupGroupEntryByAttribute: (NSString *) theAttribute

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
/* LDAPSourceSchema.h - this file is part of SOGo
*
* Copyright (C) 2011 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.
*/
#ifndef LDAPSOURCESCHEMA_H
#define LDAPSOURCESCHEMA_H
#import <Foundation/NSObject.h>
@class NSMutableDictionary;
@class NGLdapConnection;
@interface LDAPSourceSchema : NSObject
{
NSMutableDictionary *schema;
}
- (void) readSchemaFromConnection: (NGLdapConnection *) conn;
- (NSArray *) fieldsForClass: (NSString *) className;
/* merged list of attributes with unique names */
- (NSArray *) fieldsForClasses: (NSArray *) className;
@end
#endif /* LDAPSOURCESCHEMA_H */

View File

@ -0,0 +1,295 @@
/* LDAPSourceSchema.m - this file is part of SOGo
*
* Copyright (C) 2011 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/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import <EOControl/EOQualifier.h>
#import <NGLdap/NGLdapConnection.h>
#import <NGLdap/NGLdapAttribute.h>
#import <NGLdap/NGLdapEntry.h>
#import "LDAPSourceSchema.h"
#import "NSDictionary+Utilities.h"
static EOQualifier *allOCQualifier = nil;
@implementation LDAPSourceSchema
+ (void) initialize
{
allOCQualifier = [[EOKeyValueQualifier alloc]
initWithKey: @"objectClass"
operatorSelector: EOQualifierOperatorEqual
value: @"*"];
}
- (id) init
{
if ((self = [super init]))
{
schema = nil;
}
return self;
}
- (void) dealloc
{
[schema release];
[super dealloc];
}
static NSArray *
schemaTokens (NSString *schema)
{
unichar *characters;
NSUInteger count, max, parenLevel = 0, firstChar = (NSUInteger) -1;
NSMutableArray *arrayString, *parentArray, *currentArray = nil;
NSArray *topArray = nil;
NSString *token;
arrayString = [NSMutableArray array];
max = [schema length];
characters = malloc ((max + 1) * sizeof (unichar));
characters[max] = 0;
[schema getCharacters: characters];
for (count = 0; count < max; count++)
{
switch (characters[count])
{
case '(':
// NSLog (@"increase");
parenLevel++;
parentArray = currentArray;
currentArray = [NSMutableArray array];
if (parentArray == nil)
topArray = currentArray;
[parentArray addObject: currentArray];
[arrayString addObject: currentArray];
break;
case ')':
// NSLog (@"decrease");
parenLevel--;
[arrayString removeLastObject];
currentArray = [arrayString lastObject];
break;
case ' ':
if (firstChar != (NSUInteger) -1)
{
token = [NSString stringWithCharacters: characters + firstChar
length: (count - firstChar)];
if (![token isEqualToString: @"$"])
[currentArray addObject: token];
// NSLog (@"added token: %@", token);
firstChar = (NSUInteger) -1;
}
break;
default:
if (currentArray && (firstChar == (NSUInteger) -1))
firstChar = count;
}
}
free (characters);
return topArray;
}
static inline id
schemaValue (NSArray *tokens, NSString *key)
{
NSUInteger idx;
id value;
idx = [tokens indexOfObject: key];
if (idx != NSNotFound)
value = [tokens objectAtIndex: (idx + 1)];
else
value = nil;
return value;
}
static NSMutableDictionary *
parseSchema (NSString *schema)
{
NSArray *tokens;
NSMutableDictionary *schemaDict;
NSMutableArray *fields;
id value;
schemaDict = [NSMutableDictionary dictionaryWithCapacity: 6];
tokens = schemaTokens (schema);
// [schemaDict setObject: [tokens objectAtIndex: 0]
// forKey: @"oid"];
value = schemaValue (tokens, @"NAME");
if (value)
{
/* sometimes, objectClasses can have two names */
if ([value isKindOfClass: [NSString class]])
value = [NSArray arrayWithObject: value];
[schemaDict setObject: value forKey: @"names"];
}
value = schemaValue (tokens, @"SUP");
if (value)
[schemaDict setObject: value forKey: @"sup"];
fields = [NSMutableArray new];
[schemaDict setObject: fields forKey: @"fields"];
[fields release];
value = schemaValue (tokens, @"MUST");
if (value)
{
if ([value isKindOfClass: [NSArray class]])
[fields addObjectsFromArray: value];
else
[fields addObject: value];
}
value = schemaValue (tokens, @"MAY");
if (value)
{
if ([value isKindOfClass: [NSArray class]])
[fields addObjectsFromArray: value];
else
[fields addObject: value];
}
return schemaDict;
}
static void
fillSchemaFromEntry (NSMutableDictionary *schema, NGLdapEntry *entry)
{
NSEnumerator *strings;
NGLdapAttribute *attr;
NSMutableDictionary *schemaDict;
NSArray *names;
NSString *string, *name;
NSUInteger count, max;
attr = [entry attributeWithName: @"objectclasses"];
strings = [attr stringValueEnumerator];
while ((string = [strings nextObject]))
{
schemaDict = parseSchema (string);
names = [schemaDict objectForKey: @"names"];
max = [names count];
for (count = 0; count < max; count++)
{
name = [[names objectAtIndex: count] lowercaseString];
if ([name hasPrefix: @"'"] && [name hasSuffix: @"'"])
name
= [name substringWithRange: NSMakeRange (1, [name length] - 2)];
[schema setObject: schemaDict forKey: name];
}
/* the list of names is no longer required from the schema itself */
[schemaDict removeObjectForKey: @"names"];
}
}
- (void) readSchemaFromConnection: (NGLdapConnection *) conn
{
NSEnumerator *entries;
NGLdapEntry *entry;
NSString *dn;
ASSIGN (schema, [NSMutableDictionary new]);
[schema release];
entries = [conn baseSearchAtBaseDN: @""
qualifier: allOCQualifier
attributes: [NSArray arrayWithObject: @"subschemaSubentry"]];
entry = [entries nextObject];
if (entry)
{
dn = [[entry attributeWithName: @"subschemaSubentry"]
stringValueAtIndex: 0];
if (dn)
{
entries = [conn baseSearchAtBaseDN: dn
qualifier: allOCQualifier
attributes: [NSArray arrayWithObject: @"objectclasses"]];
entry = [entries nextObject];
if (entry)
fillSchemaFromEntry (schema, entry);
}
}
}
static void
fillFieldsForClass (NSMutableDictionary *schema, NSString *schemaName,
NSMutableArray *fields)
{
NSDictionary *schemaDict;
NSString *sup;
NSArray *schemaFields;
schemaDict = [schema objectForKey: [schemaName lowercaseString]];
if (schemaDict)
{
schemaFields = [schemaDict objectForKey: @"fields"];
if ([schemaFields count] > 0)
[fields addObjectsFromArray: schemaFields];
sup = [schemaDict objectForKey: @"sup"];
if ([sup length] > 0)
fillFieldsForClass (schema, sup, fields);
}
}
- (NSArray *) fieldsForClass: (NSString *) className
{
NSMutableArray *fields;
fields = [NSMutableArray arrayWithCapacity: 128];
fillFieldsForClass (schema, className, fields);
return fields;
}
- (NSArray *) fieldsForClasses: (NSArray *) classNames
{
NSMutableDictionary *fieldHash;
NSNumber *yesValue;
NSString *name;
NSUInteger count, max;
yesValue = [NSNumber numberWithBool: YES];
fieldHash = [NSMutableDictionary dictionary];
max = [classNames count];
for (count = 0; count < max; count++)
{
name = [classNames objectAtIndex: count];
[fieldHash setObject: yesValue forKeys: [self fieldsForClass: name]];
}
return [fieldHash allKeys];
}
@end

View File

@ -35,8 +35,7 @@
- (NSString *) jsonRepresentation; - (NSString *) jsonRepresentation;
- (NSString *) keysWithFormat: (NSString *) keyFormat; - (NSString *) keysWithFormat: (NSString *) keyFormat;
// LDIF methods - (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary;
- (NSString *) userRecordAsLDIFEntry;
@end @end

View File

@ -26,12 +26,8 @@
#import <Foundation/NSException.h> #import <Foundation/NSException.h>
#import <Foundation/NSNull.h> #import <Foundation/NSNull.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSCharacterSet.h>
#import <NGExtensions/NGBase64Coding.h>
#import "NSArray+Utilities.h" #import "NSArray+Utilities.h"
#import "NSObject+Utilities.h"
#import "NSString+Utilities.h" #import "NSString+Utilities.h"
#import "NSDictionary+Utilities.h" #import "NSDictionary+Utilities.h"
@ -109,84 +105,6 @@
return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]]; return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]];
} }
// LDIF Methods
#warning We might want to support more than just strings here
- (void) _appendLDIFKey: (NSString *) key
value: (NSString *) value
toString: (NSMutableString *) ldifString
{
if ([value isKindOfClass: [NSString class]])
{
if ([value _isLDIFSafe])
[ldifString appendFormat: @"%@: %@\n", key, value];
else
[ldifString appendFormat: @"%@:: %@\n",
key, [value stringByEncodingBase64]];
}
}
- (void) _appendLDIFKey: (NSString *) key
toString: (NSMutableString *) ldifString
{
id value;
int count, max;
value = [self objectForKey: key];
if ([value isKindOfClass: [NSArray class]])
{
max = [value count];
for (count = 0; count < max; count++)
[self _appendLDIFKey: key value: [value objectAtIndex: count]
toString: ldifString];
}
else
[self _appendLDIFKey: key value: [self objectForKey: key]
toString: ldifString];
}
- (void) _appendObjectClassesToString: (NSMutableString *) ldifString
{
NSEnumerator *classes;
NSString *currentClass;
classes = [[self objectForKey: @"objectClasses"] objectEnumerator];
while ((currentClass = [classes nextObject]))
[self _appendLDIFKey: @"objectClass" value: currentClass
toString: ldifString];
}
- (NSString *) userRecordAsLDIFEntry
{
NSMutableString *ldifString;
NSEnumerator *keys;
NSString *currentKey;
// {CalendarAccess = YES; MailAccess = YES; c_cn = "Wolfgang Sourdeau"; c_emails = ("wolfgang@test.com"); c_name = "wolfgang@test.com"; c_uid = "wolfgang@test.com"; cn = "wolfgang@test.com"; displayName = "Wolfgang Sourdeau"; dn = "cn=wolfgang@test.com,ou=evariste,o=inverse.ca"; givenName = Wolfgang; mail = "wolfgang@test.com"; objectClass = organizationalPerson; sn = Sourdeau; }
ldifString = [NSMutableString string];
[self _appendLDIFKey: @"dn" toString: ldifString];
[self _appendObjectClassesToString: ldifString];
keys = [[self allKeys] objectEnumerator];
while ((currentKey = [keys nextObject]))
{
if (!([currentKey isEqualToString: @"CalendarAccess"]
|| [currentKey isEqualToString: @"MailAccess"]
|| [currentKey isEqualToString: @"ContactAccess"]
|| [currentKey hasPrefix: @"objectClass"]
|| [currentKey hasPrefix: @"c_"]
|| [currentKey isEqualToString: @"dn"]
|| [currentKey isEqualToString: @"isGroup"]
|| [currentKey isEqualToString: @"isResource"]
|| [currentKey isEqualToString: @"numberOfSimultaneousBookings"]
|| [currentKey isEqualToString: @"canAuthenticate"]))
[self _appendLDIFKey: currentKey toString: ldifString];
}
return ldifString;
}
@end @end
@implementation NSMutableDictionary (SOGoDictionaryUtilities) @implementation NSMutableDictionary (SOGoDictionaryUtilities)

View File

@ -62,8 +62,6 @@
- (int) timeValue; - (int) timeValue;
- (BOOL) _isLDIFSafe;
- (BOOL) isJSONString; - (BOOL) isJSONString;
- (id) objectFromJSONString; - (id) objectFromJSONString;

View File

@ -486,44 +486,6 @@ static int cssEscapingCount;
return time; return time;
} }
static NSMutableCharacterSet *safeLDIFChars = nil;
static NSMutableCharacterSet *safeLDIFStartChars = nil;
- (void) _initSafeLDIFChars
{
safeLDIFChars = [NSMutableCharacterSet new];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x01, 9)];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x0b, 2)];
[safeLDIFChars addCharactersInRange: NSMakeRange (0x0e, 114)];
safeLDIFStartChars = [safeLDIFChars mutableCopy];
[safeLDIFStartChars removeCharactersInString: @" :<"];
}
- (BOOL) _isLDIFSafe
{
int count, max;
BOOL rc;
if (!safeLDIFChars)
[self _initSafeLDIFChars];
rc = YES;
max = [self length];
if (max > 0)
{
if ([safeLDIFStartChars characterIsMember: [self characterAtIndex: 0]])
for (count = 1; rc && count < max; count++)
rc = [safeLDIFChars
characterIsMember: [self characterAtIndex: count]];
else
rc = NO;
}
return rc;
}
- (BOOL) isJSONString - (BOOL) isJSONString
{ {
NSDictionary *jsonData; NSDictionary *jsonData;

View File

@ -25,6 +25,8 @@
#import "SOGoObject.h" #import "SOGoObject.h"
@class NSString;
@interface SOGoFolder : SOGoObject @interface SOGoFolder : SOGoObject
{ {
NSMutableString *displayName; NSMutableString *displayName;
@ -55,6 +57,9 @@
/* outlook */ /* outlook */
- (NSString *) outlookFolderClass; - (NSString *) outlookFolderClass;
/* email advisories */
- (void) sendFolderAdvisoryTemplate: (NSString *) template;
@end @end
@interface SOGoFolder (GroupDAVExtensions) @interface SOGoFolder (GroupDAVExtensions)

View File

@ -27,6 +27,7 @@
#import <Foundation/NSURL.h> #import <Foundation/NSURL.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOContext.h> #import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <NGObjWeb/SoWebDAVValue.h> #import <NGObjWeb/SoWebDAVValue.h>
@ -37,12 +38,16 @@
#import <SaxObjC/XMLNamespaces.h> #import <SaxObjC/XMLNamespaces.h>
#import <SOGoUI/SOGoFolderAdvisory.h>
#import "DOMNode+SOGo.h" #import "DOMNode+SOGo.h"
#import "NSArray+Utilities.h" #import "NSArray+Utilities.h"
#import "NSObject+DAV.h" #import "NSObject+DAV.h"
#import "NSString+DAV.h" #import "NSString+DAV.h"
#import "NSString+Utilities.h" #import "NSString+Utilities.h"
#import "SOGoPermissions.h" #import "SOGoPermissions.h"
#import "SOGoUser.h"
#import "SOGoDomainDefaults.h"
#import "SOGoWebDAVAclManager.h" #import "SOGoWebDAVAclManager.h"
#import "WORequest+SOGo.h" #import "WORequest+SOGo.h"
#import "WOResponse+SOGo.h" #import "WOResponse+SOGo.h"
@ -248,6 +253,30 @@
return comparison; return comparison;
} }
/* email advisories */
- (void) sendFolderAdvisoryTemplate: (NSString *) template
{
NSString *pageName;
SOGoUser *user;
SOGoFolderAdvisory *page;
NSString *language;
user = [SOGoUser userWithLogin: [self ownerInContext: context]];
if ([[user domainDefaults] foldersSendEMailNotifications])
{
language = [[user userDefaults] language];
pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory",
language, template];
page = [[WOApplication application] pageWithName: pageName
inContext: context];
[page setFolderObject: self];
[page setRecipientUID: [user login]];
[page send];
}
}
/* WebDAV */ /* WebDAV */
- (NSString *) davEntityTag - (NSString *) davEntityTag

View File

@ -118,9 +118,6 @@
- (void) removeAclsForUsers: (NSArray *) users - (void) removeAclsForUsers: (NSArray *) users
forObjectAtPath: (NSArray *) objectPathArray; forObjectAtPath: (NSArray *) objectPathArray;
/* advisories */
- (void) sendFolderAdvisoryTemplate: (NSString *) template;
/* DAV */ /* DAV */
- (NSURL *) publicDavURL; - (NSURL *) publicDavURL;
- (NSURL *) realDavURL; - (NSURL *) realDavURL;

View File

@ -52,7 +52,6 @@
#import <GDLContentStore/GCSFolder.h> #import <GDLContentStore/GCSFolder.h>
#import <GDLContentStore/NSURL+GCS.h> #import <GDLContentStore/NSURL+GCS.h>
#import <SaxObjC/XMLNamespaces.h> #import <SaxObjC/XMLNamespaces.h>
#import <SOGoUI/SOGoFolderAdvisory.h>
#import "NSDictionary+Utilities.h" #import "NSDictionary+Utilities.h"
#import "NSArray+Utilities.h" #import "NSArray+Utilities.h"
@ -500,28 +499,6 @@ static NSArray *childRecordFields = nil;
return folder; return folder;
} }
- (void) sendFolderAdvisoryTemplate: (NSString *) template
{
NSString *pageName;
SOGoUser *user;
SOGoFolderAdvisory *page;
NSString *language;
user = [SOGoUser userWithLogin: [self ownerInContext: context]];
if ([[user domainDefaults] foldersSendEMailNotifications])
{
language = [[user userDefaults] language];
pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory",
language, template];
page = [[WOApplication application] pageWithName: pageName
inContext: context];
[page setFolderObject: self];
[page setRecipientUID: [user login]];
[page send];
}
}
- (BOOL) create - (BOOL) create
{ {
NSException *result; NSException *result;

View File

@ -42,6 +42,8 @@
- (NSString *) defaultFolderName; - (NSString *) defaultFolderName;
- (NSException *) appendPersonalSources;
- (void) setBaseOCSPath: (NSString *) newOCSPath; - (void) setBaseOCSPath: (NSString *) newOCSPath;
- (NSArray *) toManyRelationshipKeys; - (NSArray *) toManyRelationshipKeys;

View File

@ -183,7 +183,6 @@ static SoSecurityManager *sm = nil;
SOGoGCSFolder *folder; SOGoGCSFolder *folder;
NSString *key; NSString *key;
NSException *error; NSException *error;
SOGoUser *currentUser;
if (!subFolderClass) if (!subFolderClass)
subFolderClass = [[self class] subFolderClass]; subFolderClass = [[self class] subFolderClass];
@ -191,8 +190,6 @@ static SoSecurityManager *sm = nil;
error = [fc evaluateExpressionX: sql]; error = [fc evaluateExpressionX: sql];
if (!error) if (!error)
{ {
currentUser = [context activeUser];
attrs = [fc describeResults: NO]; attrs = [fc describeResults: NO];
while ((row = [fc fetchAttributes: attrs withZone: NULL])) while ((row = [fc fetchAttributes: attrs withZone: NULL]))
{ {

View File

@ -28,9 +28,10 @@
#import "SOGoConstants.h" #import "SOGoConstants.h"
@class NSDictionary; @class NSDictionary;
@class NSException;
@class NSString; @class NSString;
@protocol SOGoSource @protocol SOGoSource <NSObject>
+ (id) sourceFromUDSource: (NSDictionary *) udSource + (id) sourceFromUDSource: (NSDictionary *) udSource
inDomain: (NSString *) domain; inDomain: (NSString *) domain;
@ -40,6 +41,10 @@
- (NSString *) domain; - (NSString *) domain;
/* requires a "." to obtain the full list of contacts */
- (void) setListRequiresDot: (BOOL) aBool;
- (BOOL) listRequiresDot;
- (BOOL) checkLogin: (NSString *) _login - (BOOL) checkLogin: (NSString *) _login
password: (NSString *) _pwd password: (NSString *) _pwd
perr: (SOGoPasswordPolicyError *) _perr perr: (SOGoPasswordPolicyError *) _perr
@ -56,8 +61,36 @@
- (NSArray *) allEntryIDs; - (NSArray *) allEntryIDs;
- (NSArray *) fetchContactsMatching: (NSString *) filter; - (NSArray *) fetchContactsMatching: (NSString *) filter;
- (void) setSourceID: (NSString *) newSourceID;
- (NSString *) sourceID; - (NSString *) sourceID;
- (void) setDisplayName: (NSString *) newDisplayName;
- (NSString *) displayName;
- (void) setModifiers: (NSArray *) newModifiers;
- (NSArray *) modifiers;
- (BOOL) hasUserAddressBooks;
- (NSArray *) addressBookSourcesForUser: (NSString *) user;
- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord
withID: (NSString *) aId;
- (NSException *) updateContactEntry: (NSDictionary *) ldifRecord;
- (NSException *) removeContactEntryWithID: (NSString *) aId;
/* user address books */
- (NSArray *) addressBookSourcesForUser: (NSString *) user;
- (NSException *) addAddressBookSource: (NSString *) newId
withDisplayName: (NSString *) newDisplayName
forUser: (NSString *) user;
- (NSException *) renameAddressBookSource: (NSString *) newId
withDisplayName: (NSString *) newDisplayName
forUser: (NSString *) user;
- (NSException *) removeAddressBookSource: (NSString *) newId
forUser: (NSString *) user;
@end @end
@protocol SOGoDNSource <SOGoSource> @protocol SOGoDNSource <SOGoSource>

View File

@ -52,6 +52,8 @@
@class SOGoUserProfile; @class SOGoUserProfile;
@class SOGoUserSettings; @class SOGoUserSettings;
@protocol SOGoSource;
@interface SOGoUser : SoUser @interface SOGoUser : SoUser
{ {
SOGoUserDefaults *_defaults; SOGoUserDefaults *_defaults;
@ -88,6 +90,7 @@
/* properties */ /* properties */
- (NSString *) domain; - (NSString *) domain;
- (id <SOGoSource>) authenticationSource;
- (NSArray *) allEmails; - (NSArray *) allEmails;
- (BOOL) hasEmail: (NSString *) email; - (BOOL) hasEmail: (NSString *) email;

View File

@ -287,6 +287,17 @@
return [self _fetchFieldForUser: @"c_domain"]; return [self _fetchFieldForUser: @"c_domain"];
} }
- (id <SOGoSource>) authenticationSource
{
NSString *sourceID;
SOGoUserManager *um;
sourceID = [self _fetchFieldForUser: @"SOGoSource"];
um = [SOGoUserManager sharedUserManager];
return [um sourceWithID: sourceID];
}
- (NSArray *) allEmails - (NSArray *) allEmails
{ {
if (!allEmails) if (!allEmails)

View File

@ -586,7 +586,7 @@
NSMutableArray *emails; NSMutableArray *emails;
NSDictionary *userEntry; NSDictionary *userEntry;
NSEnumerator *sogoSources; NSEnumerator *sogoSources;
NSObject <SOGoDNSource> *currentSource; NSObject <SOGoSource> *currentSource;
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin; NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin;
NSArray *c_emails; NSArray *c_emails;
BOOL access; BOOL access;
@ -605,12 +605,14 @@
sogoSources = [[self authenticationSourceIDsInDomain: domain] sogoSources = [[self authenticationSourceIDsInDomain: domain]
objectEnumerator]; objectEnumerator];
while ((sourceID = [sogoSources nextObject])) userEntry = nil;
while (!userEntry && (sourceID = [sogoSources nextObject]))
{ {
currentSource = [_sources objectForKey: sourceID]; currentSource = [_sources objectForKey: sourceID];
userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid]; userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid];
if (userEntry) if (userEntry)
{ {
[currentUser setObject: sourceID forKey: @"SOGoSource"];
if (!cn) if (!cn)
cn = [userEntry objectForKey: @"c_cn"]; cn = [userEntry objectForKey: @"c_cn"];
if (!c_uid) if (!c_uid)

View File

@ -22,8 +22,9 @@
*/ */
#import <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <Foundation/NSURL.h> #import <Foundation/NSURL.h>
@ -379,6 +380,7 @@
{ {
NSMutableDictionary *response; NSMutableDictionary *response;
NSMutableArray *qualifiers; NSMutableArray *qualifiers;
NSArray *fieldNames;
EOAdaptorChannel *channel; EOAdaptorChannel *channel;
EOQualifier *loginQualifier, *qualifier; EOQualifier *loginQualifier, *qualifier;
GCSChannelManager *cm; GCSChannelManager *cm;
@ -463,6 +465,16 @@
[response autorelease]; [response autorelease];
[channel cancelFetch]; [channel cancelFetch];
/* Convert all c_ fields to obtain their ldif equivalent */
fieldNames = [response allKeys];
for (i = 0; i < [fieldNames count]; i++)
{
field = [fieldNames objectAtIndex: i];
if ([field hasPrefix: @"c_"])
[response setObject: [response objectForKey: field]
forKey: [field substringFromIndex: 2]];
}
// We have to do this here since we do not manage modules // We have to do this here since we do not manage modules
// constraints right now over a SQL backend. // constraints right now over a SQL backend.
[response setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"]; [response setObject: [NSNumber numberWithBool: YES] forKey: @"CalendarAccess"];
@ -560,6 +572,8 @@
forKey: @"numberOfSimultaneousBookings"]; forKey: @"numberOfSimultaneousBookings"];
} }
} }
[response setObject: self forKey: @"source"];
} }
else else
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
@ -666,7 +680,12 @@
attrs = [channel describeResults: NO]; attrs = [channel describeResults: NO];
while ((row = [channel fetchAttributes: attrs withZone: NULL])) while ((row = [channel fetchAttributes: attrs withZone: NULL]))
[results addObject: row]; {
row = [row mutableCopy];
[(NSMutableDictionary *) row setObject: self forKey: @"source"];
[results addObject: row];
[row release];
}
} }
else else
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex]; [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
@ -679,9 +698,142 @@
return results; return results;
} }
- (void) setSourceID: (NSString *) newSourceID
{
}
- (NSString *) sourceID - (NSString *) sourceID
{ {
return _sourceID; return _sourceID;
} }
- (void) setDisplayName: (NSString *) newDisplayName
{
}
- (NSString *) displayName
{
/* This method is only used when supporting user "source" addressbooks,
which is only supported by the LDAP backend for now. */
return _sourceID;
}
- (void) setListRequiresDot: (BOOL) newListRequiresDot
{
}
- (BOOL) listRequiresDot
{
/* This method is not implemented for SQLSource. It must enable a mechanism
where using "." is not required to list the content of addressbooks. */
return YES;
}
/* card editing */
- (void) setModifiers: (NSArray *) newModifiers
{
}
- (NSArray *) modifiers
{
/* This method is only used when supporting card editing,
which is only supported by the LDAP backend for now. */
return nil;
}
- (NSException *) addContactEntry: (NSDictionary *) roLdifRecord
withID: (NSString *) aId
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
- (NSException *) updateContactEntry: (NSDictionary *) roLdifRecord
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
- (NSException *) removeContactEntryWithID: (NSString *) aId
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
/* user addressbooks */
- (BOOL) hasUserAddressBooks
{
return NO;
}
- (NSArray *) addressBookSourcesForUser: (NSString *) user
{
return nil;
}
- (NSException *) addAddressBookSource: (NSString *) newId
withDisplayName: (NSString *) newDisplayName
forUser: (NSString *) user
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
- (NSException *) renameAddressBookSource: (NSString *) newId
withDisplayName: (NSString *) newDisplayName
forUser: (NSString *) user
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
- (NSException *) removeAddressBookSource: (NSString *) newId
forUser: (NSString *) user
{
NSString *reason;
reason = [NSString stringWithFormat: @"method '%@' is not available"
@" for class '%@'", NSStringFromSelector (_cmd),
NSStringFromClass (isa)];
return [NSException exceptionWithName: @"SQLSourceIOException"
reason: reason
userInfo: nil];
}
@end @end

View File

@ -15,3 +15,5 @@ ADDITIONAL_LIB_DIRS += \
-L../../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \ -L../../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \
-L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \ -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \
-L/usr/local/lib -L/usr/lib -lEOControl -lNGStreams -lNGMime -lNGExtensions -L/usr/local/lib -L/usr/lib -lEOControl -lNGStreams -lNGMime -lNGExtensions
ADDITIONAL_LDFLAGS += -Wl,--no-as-needed

View File

@ -1,7 +1,7 @@
setup setup
----- -----
(you need "python-simplejson", "python-xml", "python-vobject" and "python-m2crypto" (you need "python-simplejson", "python-xml", "python-vobject", "python-dateutil" and "python-m2crypto"
in order to run the scripts on Debian) in order to run the scripts on Debian)
1) copy config.py.in to config.py (make sure to never EVER add it to monotone) 1) copy config.py.in to config.py (make sure to never EVER add it to monotone)

View File

@ -8,10 +8,10 @@ from config import hostname, port, username, password, \
resource_no_overbook, resource_can_overbook resource_no_overbook, resource_can_overbook
import datetime import datetime
import dateutil.tz
import sogotests import sogotests
import sys import sys
import time import time
import pytz
import unittest import unittest
import utilities import utilities
import vobject import vobject
@ -143,7 +143,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
vevent.add('summary').value = summary vevent.add('summary').value = summary
vevent.add('transp').value = transparency[transp] vevent.add('transp').value = transparency[transp]
now = datetime.datetime.now(pytz.timezone("America/Montreal")) now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal"))
startdate = vevent.add('dtstart') startdate = vevent.add('dtstart')
startdate.value = now startdate.value = now
enddate = vevent.add('dtend') enddate = vevent.add('dtend')

View File

@ -30,6 +30,7 @@ import sys
xmlns_dav = "DAV:" xmlns_dav = "DAV:"
xmlns_caldav = "urn:ietf:params:xml:ns:caldav" xmlns_caldav = "urn:ietf:params:xml:ns:caldav"
xmlns_carddav = "urn:ietf:params:xml:ns:carddav"
xmlns_inversedav = "urn:inverse:params:xml:ns:inverse-dav" xmlns_inversedav = "urn:inverse:params:xml:ns:inverse-dav"
url_re = None url_re = None
@ -401,6 +402,25 @@ class CalDAVCalendarQuery(WebDAVREPORT):
filter_node.append(cal_filter_node) filter_node.append(cal_filter_node)
self.top_node.append(filter_node) self.top_node.append(filter_node)
class CardDAVAddressBookQuery(WebDAVREPORT):
def __init__(self, url, properties, searchProperty = None, searchValue = None):
WebDAVQuery.__init__(self, url)
query_tag = self.ns_mgr.register("addressbook-query", xmlns_carddav)
ns_key = self.ns_mgr.xmlns[xmlns_carddav]
self.top_node = _WD_XMLTreeElement(query_tag)
if properties is not None and len(properties) > 0:
self._initProperties(properties)
if searchProperty is not None:
filter_node = _WD_XMLTreeElement("%s:filter" % ns_key)
self.top_node.append(filter_node)
propfilter_node = _WD_XMLTreeElement("%s:prop-filter" % ns_key, { "name": searchProperty })
filter_node.append(propfilter_node)
match_node = _WD_XMLTreeElement("%s:text-match" % ns_key,
{ "collation": "i;unicasemap", "match-type": "starts-with" })
propfilter_node.append(match_node)
match_node.appendSubtree(None, searchValue)
class MailDAVMailQuery(WebDAVREPORT): class MailDAVMailQuery(WebDAVREPORT):
def __init__(self, url, properties, filters = None, def __init__(self, url, properties, filters = None,
sort = None, ascending = True): sort = None, ascending = True):

View File

@ -4,6 +4,7 @@ include ../config.make
include $(GNUSTEP_MAKEFILES)/common.make include $(GNUSTEP_MAKEFILES)/common.make
include ../Version include ../Version
###
SOGO_TOOL = sogo-tool SOGO_TOOL = sogo-tool
$(SOGO_TOOL)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_TOOL)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
$(SOGO_TOOL)_OBJC_FILES += \ $(SOGO_TOOL)_OBJC_FILES += \
@ -18,7 +19,9 @@ $(SOGO_TOOL)_OBJC_FILES += \
SOGoToolRenameUser.m \ SOGoToolRenameUser.m \
SOGoToolUserPreferences.m \ SOGoToolUserPreferences.m \
SOGoToolExpireAutoReply.m SOGoToolExpireAutoReply.m
TOOL_NAME += $(SOGO_TOOL)
###
SOGO_SLAPD_SOCKD = sogo-slapd-sockd SOGO_SLAPD_SOCKD = sogo-slapd-sockd
$(SOGO_SLAPD_SOCKD)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_SLAPD_SOCKD)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
$(SOGO_SLAPD_SOCKD)_OBJC_FILES += \ $(SOGO_SLAPD_SOCKD)_OBJC_FILES += \
@ -26,16 +29,18 @@ $(SOGO_SLAPD_SOCKD)_OBJC_FILES += \
\ \
SOGoSockD.m \ SOGoSockD.m \
SOGoSockDScanner.m \ SOGoSockDScanner.m \
SOGoSockDOperation.m \ SOGoSockDOperation.m
TOOL_NAME += $(SOGO_SLAPD_SOCKD)
###
SOGO_EALARMS_NOTIFY = sogo-ealarms-notify SOGO_EALARMS_NOTIFY = sogo-ealarms-notify
$(SOGO_EALARMS_NOTIFY)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) $(SOGO_EALARMS_NOTIFY)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS)
$(SOGO_EALARMS_NOTIFY)_OBJC_FILES += \ $(SOGO_EALARMS_NOTIFY)_OBJC_FILES += \
sogo-ealarms-notify.m \ sogo-ealarms-notify.m \
\ \
SOGoEAlarmsNotifier.m SOGoEAlarmsNotifier.m
TOOL_NAME += $(SOGO_EALARMS_NOTIFY)
TOOL_NAME = $(SOGO_TOOL) $(SOGO_SLAPD_SOCKD) $(SOGO_EALARMS_NOTIFY)
-include GNUmakefile.preamble -include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/tool.make include $(GNUSTEP_MAKEFILES)/tool.make

View File

@ -29,6 +29,7 @@
#import <NGStreams/NGActiveSocket.h> #import <NGStreams/NGActiveSocket.h>
#import <Contacts/SOGoContactFolder.h> #import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h> #import <Contacts/SOGoContactFolders.h>
#import <Contacts/NSString+LDIF.h>
#import <SOGo/SOGoProductLoader.h> #import <SOGo/SOGoProductLoader.h>
#import <SOGo/SOGoUserFolder.h> #import <SOGo/SOGoUserFolder.h>
#import <SOGo/NSDictionary+Utilities.h> #import <SOGo/NSDictionary+Utilities.h>
@ -120,13 +121,13 @@ Class SOGoContactSourceFolderKlass = Nil;
value = [entry objectForKey: key]; value = [entry objectForKey: key];
if ([value isKindOfClass: [NSString class]] && [value length] > 0) if ([value isKindOfClass: [NSString class]] && [value length] > 0)
{ {
if ([value _isLDIFSafe]) if ([value mustEncodeLDIFValue])
[result appendFormat: @"%@: %@\n",
[key substringFromIndex: 2], value];
else
[result appendFormat: @"%@:: %@\n", [result appendFormat: @"%@:: %@\n",
[key substringFromIndex: 2], [key substringFromIndex: 2],
[value stringByEncodingBase64]]; [value stringByEncodingBase64]];
else
[result appendFormat: @"%@: %@\n",
[key substringFromIndex: 2], value];
} }
} }
[result appendString: @"\n"]; [result appendString: @"\n"];

View File

@ -37,11 +37,12 @@
#import <SOGo/SOGoUserManager.h> #import <SOGo/SOGoUserManager.h>
#import <SOGo/LDAPSource.h> #import <SOGo/LDAPSource.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h> #import <SOGo/SOGoProductLoader.h>
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h> #import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserProfile.h> #import <SOGo/SOGoUserProfile.h>
#import <SOGo/SOGoUserSettings.h> #import <SOGo/SOGoUserSettings.h>
#import <Contacts/NSDictionary+LDIF.h>
#import "SOGoTool.h" #import "SOGoTool.h"
@ -61,6 +62,12 @@
@implementation SOGoToolBackup @implementation SOGoToolBackup
+ (void) initialize
{
[[SOGoProductLoader productLoader]
loadProducts: [NSArray arrayWithObject: @"Contacts.SOGo"]];
}
+ (NSString *) command + (NSString *) command
{ {
return @"backup"; return @"backup";
@ -376,7 +383,7 @@
userEntry = [currentSource lookupContactEntry: uid]; userEntry = [currentSource lookupContactEntry: uid];
if (userEntry) if (userEntry)
{ {
[userRecord setObject: [userEntry userRecordAsLDIFEntry] [userRecord setObject: [userEntry ldifRecordAsString]
forKey: @"ldif_record"]; forKey: @"ldif_record"];
done = YES; done = YES;
} }

View File

@ -52,6 +52,8 @@
categories = [[self categories] mutableCopy]; categories = [[self categories] mutableCopy];
[categories autorelease]; [categories autorelease];
if (!categories)
categories = [NSMutableArray array];
if (set) if (set)
{ {
if (![categories containsObject: category]) if (![categories containsObject: category])

View File

@ -27,22 +27,18 @@
@class NSString; @class NSString;
@class NSMutableDictionary; @class NSMutableDictionary;
@class NGVCard;
@class SOGoContactFolder; @class SOGoContactFolder;
@interface UIxContactEditor : UIxComponent @interface UIxContactEditor : UIxComponent
{ {
id addressBookItem; id addressBookItem;
NSString *preferredEmail;
NSString *item; NSString *item;
NGVCard *card; NSMutableDictionary *ldifRecord; /* contains the values for editing */
NSMutableArray *photosURL;
NSMutableDictionary *snapshot; /* contains the values for editing */
SOGoContactFolder *componentAddressBook; SOGoContactFolder *componentAddressBook;
NSArray *contactCategories;
} }
- (NSMutableDictionary *) ldifRecord;
- (void) setAddressBookItem: (id) _item; - (void) setAddressBookItem: (id) _item;
- (id) addressBookItem; - (id) addressBookItem;

View File

@ -34,8 +34,6 @@
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <NGCards/NGVCard.h> #import <NGCards/NGVCard.h>
#import <NGCards/NGVCardPhoto.h>
#import <NGCards/NSArray+NGCards.h>
#import <SOGo/NSArray+Utilities.h> #import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
@ -50,20 +48,23 @@
#import "UIxContactEditor.h" #import "UIxContactEditor.h"
static Class SOGoContactGCSEntryK = Nil;
@implementation UIxContactEditor @implementation UIxContactEditor
+ (void) initialize
{
SOGoContactGCSEntryK = [SOGoContactGCSEntry class];
}
- (id) init - (id) init
{ {
if ((self = [super init])) if ((self = [super init]))
{ {
snapshot = [[NSMutableDictionary alloc] initWithCapacity: 16]; ldifRecord = nil;
preferredEmail = nil;
photosURL = nil;
addressBookItem = nil; addressBookItem = nil;
item = nil; item = nil;
card = nil;
componentAddressBook = nil; componentAddressBook = nil;
contactCategories = nil;
} }
return self; return self;
@ -71,18 +72,35 @@
- (void) dealloc - (void) dealloc
{ {
[snapshot release]; [ldifRecord release];
[preferredEmail release];
[photosURL release];
[addressBookItem release]; [addressBookItem release];
[item release]; [item release];
[componentAddressBook release]; [componentAddressBook release];
[contactCategories release];
[super dealloc]; [super dealloc];
} }
/* accessors */ /* accessors */
- (NSMutableDictionary *) ldifRecord
{
NSDictionary *clientLDIFRecord;
NSString *queryValue;
if (!ldifRecord)
{
clientLDIFRecord = [[self clientObject] ldifRecord];
ldifRecord = [clientLDIFRecord mutableCopy];
queryValue = [self queryParameterForKey: @"contactEmail"];
if ([queryValue length] > 0)
[ldifRecord setObject: queryValue forKey: @"mail"];
queryValue = [self queryParameterForKey: @"contactFN"];
if ([queryValue length] > 0)
[ldifRecord setObject: queryValue forKey: @"displayname"];
}
return ldifRecord;
}
- (void) setAddressBookItem: (id) _item - (void) setAddressBookItem: (id) _item
{ {
ASSIGN (addressBookItem, _item); ASSIGN (addressBookItem, _item);
@ -130,27 +148,6 @@
/* load/store content format */ /* load/store content format */
// - (void) _fixupSnapshot
// {
// NSString *currentKey, *currentString;
// NSMutableString *newString;
// NSArray *keys;
// unsigned int count, max;
// keys = [snapshot allKeys];
// max = [keys count];
// for (count = 0; count < max; count++)
// {
// currentKey = [keys objectAtIndex: count];
// currentString = [snapshot objectForKey: currentKey];
// newString = [currentString mutableCopy];
// [newString autorelease];
// [newString replaceString: @";" withString: @"\\;"];
// if (![newString isEqualToString: currentString])
// [snapshot setObject: newString forKey: currentKey];
// }
// }
/* helper */ /* helper */
- (NSString *) _completeURIForMethod: (NSString *) _method - (NSString *) _completeURIForMethod: (NSString *) _method
@ -179,12 +176,7 @@
- (BOOL) isNew - (BOOL) isNew
{ {
id co; return ([[self clientObject] isNew]);
co = [self clientObject];
return ([co isKindOfClass: [SOGoContentObject class]]
&& [co isNew]);
} }
- (NSArray *) addressBooksList - (NSArray *) addressBooksList
@ -205,14 +197,13 @@
while (currentFolder) while (currentFolder)
{ {
if ([currentFolder isEqual: folder] || if ([currentFolder isEqual: folder] ||
([currentFolder isKindOfClass: [SOGoContactGCSFolder class]] && ![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles onObject: currentFolder
onObject: currentFolder inContext: context])
inContext: context]))
[addressBooksList addObject: currentFolder]; [addressBooksList addObject: currentFolder];
currentFolder = [folders nextObject]; currentFolder = [folders nextObject];
} }
return addressBooksList; return addressBooksList;
} }
@ -245,26 +236,30 @@
return fDisplayName; return fDisplayName;
} }
- (void) setContactCategories: (NSString *) jsonCategories - (BOOL) supportCategories
{
return [[self clientObject] isKindOfClass: SOGoContactGCSEntryK];
}
- (void) setJsonContactCategories: (NSString *) jsonCategories
{ {
NSArray *newCategories; NSArray *newCategories;
newCategories = [jsonCategories objectFromJSONString]; newCategories = [jsonCategories objectFromJSONString];
if ([newCategories isKindOfClass: [NSArray class]]) if ([newCategories isKindOfClass: [NSArray class]])
ASSIGN (contactCategories, newCategories); [[self ldifRecord] setObject: newCategories
forKey: @"vcardcategories"];
else
[[self ldifRecord] removeObjectForKey: @"vcardcategories"];
} }
- (NSString *) contactCategories - (NSString *) jsonContactCategories
{ {
NSString *jsonCats; NSArray *categories;
if (!contactCategories) categories = [[self ldifRecord] objectForKey: @"vcardcategories"];
ASSIGN (contactCategories, [card categories]);
jsonCats = [contactCategories jsonRepresentation];
if (!jsonCats)
jsonCats = @"[]";
return jsonCats; return [categories jsonRepresentation];
} }
- (NSArray *) _languageContactsCategories - (NSArray *) _languageContactsCategories
@ -279,11 +274,11 @@
return [categoryLabels trimmedComponents]; return [categoryLabels trimmedComponents];
} }
- (NSArray *) _fetchAndCombineCategoriesList: (NSArray *) contactCats - (NSArray *) _fetchAndCombineCategoriesList
{ {
NSString *ownerLogin; NSString *ownerLogin;
SOGoUserDefaults *ud; SOGoUserDefaults *ud;
NSArray *cats, *newCats; NSArray *cats, *newCats, *contactCategories;
ownerLogin = [[self clientObject] ownerInContext: context]; ownerLogin = [[self clientObject] ownerInContext: context];
ud = [[SOGoUser userWithLogin: ownerLogin] userDefaults]; ud = [[SOGoUser userWithLogin: ownerLogin] userDefaults];
@ -291,9 +286,10 @@
if (!cats) if (!cats)
cats = [self _languageContactsCategories]; cats = [self _languageContactsCategories];
if (contactCats) contactCategories = [[self ldifRecord] objectForKey: @"vcardcategories"];
if (contactCategories)
{ {
newCats = [cats mergedArrayWithArray: contactCats]; newCats = [cats mergedArrayWithArray: contactCategories];
if ([newCats count] != [cats count]) if ([newCats count] != [cats count])
{ {
cats = [newCats sortedArrayUsingSelector: cats = [newCats sortedArrayUsingSelector:
@ -311,7 +307,7 @@
NSArray *cats; NSArray *cats;
NSString *list; NSString *list;
cats = [self _fetchAndCombineCategoriesList: [card categories]]; cats = [self _fetchAndCombineCategoriesList];
list = [cats jsonRepresentation]; list = [cats jsonRepresentation];
if (!list) if (!list)
list = @"[]"; list = @"[]";
@ -328,311 +324,7 @@
actionName = [[request requestHandlerPath] lastPathComponent]; actionName = [[request requestHandlerPath] lastPathComponent];
return ([[self clientObject] isKindOfClass: [SOGoContactGCSEntry class]] return ([actionName hasPrefix: @"save"]);
&& [actionName hasPrefix: @"save"]);
}
- (void) _setSnapshotValue: (NSString *) key
to: (NSString *) aValue
{
if (!aValue)
aValue = @"";
[snapshot setObject: aValue forKey: key];
}
- (NSMutableDictionary *) snapshot
{
return snapshot;
}
- (NSString *) _simpleValueForType: (NSString *) aType
inArray: (NSArray *) anArray
excluding: (NSString *) aTypeToExclude
{
NSArray *elements;
NSString *value;
elements = [anArray cardElementsWithAttribute: @"type"
havingValue: aType];
value = nil;
if ([elements count] > 0)
{
CardElement *ce;
int i;
for (i = 0; i < [elements count]; i++)
{
ce = [elements objectAtIndex: i];
value = [ce flattenedValuesForKey: @""];
if (!aTypeToExclude)
break;
if (![ce hasAttribute: @"type" havingValue: aTypeToExclude])
break;
value = nil;
}
}
return value;
}
- (void) _setupEmailFields
{
NSArray *elements;
NSString *workMail, *homeMail, *prefMail, *potential;
unsigned int max;
elements = [card childrenWithTag: @"email"];
max = [elements count];
workMail = [self _simpleValueForType: @"work"
inArray: elements excluding: nil];
homeMail = [self _simpleValueForType: @"home"
inArray: elements excluding: nil];
prefMail = [self _simpleValueForType: @"pref"
inArray: elements excluding: nil];
if (max > 0)
{
potential = [[elements objectAtIndex: 0] flattenedValuesForKey: @""];
if (!workMail)
{
if (homeMail && homeMail == potential)
{
if (max > 1)
workMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""];
}
else
workMail = potential;
}
if (!homeMail && max > 1)
{
if (workMail && workMail == potential)
homeMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""];
else
homeMail = potential;
}
if (prefMail)
{
if (prefMail == workMail)
preferredEmail = @"work";
else if (prefMail == homeMail)
preferredEmail = @"home";
}
}
[self _setSnapshotValue: @"workMail" to: workMail];
[self _setSnapshotValue: @"homeMail" to: homeMail];
[self _setSnapshotValue: @"mozillaUseHtmlMail"
to: [[card uniqueChildWithTag: @"x-mozilla-html"] flattenedValuesForKey: @""]];
}
- (void) _setupOrgFields
{
NSMutableArray *orgServices;
CardElement *org;
NSString *service;
NSUInteger count, max;
org = [card org];
[self _setSnapshotValue: @"workCompany"
to: [org flattenedValueAtIndex: 0 forKey: @""]];
max = [[org valuesForKey: @""] count];
if (max > 1)
{
orgServices = [NSMutableArray arrayWithCapacity: max];
for (count = 1; count < max; count++)
{
service = [org flattenedValueAtIndex: count forKey: @""];
if ([service length] > 0)
[orgServices addObject: service];
}
[self _setSnapshotValue: @"workService"
to: [orgServices componentsJoinedByString: @", "]];
}
}
- (NSString *) preferredEmail
{
return preferredEmail;
}
- (void) setPreferredEmail: (NSString *) aString
{
preferredEmail = aString;
}
- (void) _retrieveQueryParameter: (NSString *) queryKey
intoSnapshotValue: (NSString *) snapshotKey
{
NSString *queryValue;
queryValue = [self queryParameterForKey: queryKey];
if (queryValue && [queryValue length] > 0)
[self _setSnapshotValue: snapshotKey to: queryValue];
}
- (void) initSnapshot
{
NSArray *elements;
CardElement *element;
element = [card n];
[self _setSnapshotValue: @"sn"
to: [element flattenedValueAtIndex: 0 forKey: @""]];
[self _setSnapshotValue: @"givenName"
to: [element flattenedValueAtIndex: 1 forKey: @""]];
[self _setSnapshotValue: @"fn" to: [card fn]];
[self _setSnapshotValue: @"nickname" to: [card nickname]];
elements = [card childrenWithTag: @"tel"];
// We do this (exclude FAX) in order to avoid setting the WORK number as the FAX
// one if we do see the FAX field BEFORE the WORK number.
[self _setSnapshotValue: @"telephoneNumber"
to: [self _simpleValueForType: @"work" inArray: elements excluding: @"fax"]];
[self _setSnapshotValue: @"homeTelephoneNumber"
to: [self _simpleValueForType: @"home" inArray: elements excluding: @"fax"]];
[self _setSnapshotValue: @"mobile"
to: [self _simpleValueForType: @"cell" inArray: elements excluding: nil]];
[self _setSnapshotValue: @"facsimileTelephoneNumber"
to: [self _simpleValueForType: @"fax" inArray: elements excluding: nil]];
[self _setSnapshotValue: @"pager"
to: [self _simpleValueForType: @"pager" inArray: elements excluding: nil]];
// If we don't have a "home" and "work" phone number but
// we have a "voice" one defined, we set it to the "work" value
// This can happen when we have :
// VERSION:2.1
// N:name;surname;;;;
// TEL;VOICE;HOME:
// TEL;VOICE;WORK:
// TEL;PAGER:
// TEL;FAX;WORK:
// TEL;CELL:514 123 1234
// TEL;VOICE:450 456 6789
// ADR;HOME:;;;;;;
// ADR;WORK:;;;;;;
// ADR:;;;;;;
if ([[snapshot objectForKey: @"telephoneNumber"] length] == 0 &&
[[snapshot objectForKey: @"homeTelephoneNumber"] length] == 0 &&
[elements count] > 0)
{
[self _setSnapshotValue: @"telephoneNumber"
to: [self _simpleValueForType: @"voice" inArray: elements excluding: nil]];
}
[self _setupEmailFields];
[self _setSnapshotValue: @"screenName"
to: [[card uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]];
elements = [card childrenWithTag: @"adr"
andAttribute: @"type" havingValue: @"work"];
if (elements && [elements count] > 0)
{
element = [elements objectAtIndex: 0];
[self _setSnapshotValue: @"workExtendedAddress"
to: [element flattenedValueAtIndex: 1 forKey: @""]];
[self _setSnapshotValue: @"workStreetAddress"
to: [element flattenedValueAtIndex: 2 forKey: @""]];
[self _setSnapshotValue: @"workCity"
to: [element flattenedValueAtIndex: 3 forKey: @""]];
[self _setSnapshotValue: @"workState"
to: [element flattenedValueAtIndex: 4 forKey: @""]];
[self _setSnapshotValue: @"workPostalCode"
to: [element flattenedValueAtIndex: 5 forKey: @""]];
[self _setSnapshotValue: @"workCountry"
to: [element flattenedValueAtIndex: 6 forKey: @""]];
}
elements = [card childrenWithTag: @"adr"
andAttribute: @"type" havingValue: @"home"];
if (elements && [elements count] > 0)
{
element = [elements objectAtIndex: 0];
[self _setSnapshotValue: @"homeExtendedAddress"
to: [element flattenedValueAtIndex: 1 forKey: @""]];
[self _setSnapshotValue: @"homeStreetAddress"
to: [element flattenedValueAtIndex: 2 forKey: @""]];
[self _setSnapshotValue: @"homeCity"
to: [element flattenedValueAtIndex: 3 forKey: @""]];
[self _setSnapshotValue: @"homeState"
to: [element flattenedValueAtIndex: 4 forKey: @""]];
[self _setSnapshotValue: @"homePostalCode"
to: [element flattenedValueAtIndex: 5 forKey: @""]];
[self _setSnapshotValue: @"homeCountry"
to: [element flattenedValueAtIndex: 6 forKey: @""]];
}
elements = [card childrenWithTag: @"url"];
[self _setSnapshotValue: @"workURL"
to: [self _simpleValueForType: @"work" inArray: elements excluding: nil]];
[self _setSnapshotValue: @"homeURL"
to: [self _simpleValueForType: @"home" inArray: elements excluding: nil]];
// If we don't have a "work" or "home" URL but we still have
// an URL field present, let's add it to the "home" value
if ([[snapshot objectForKey: @"workURL"] length] == 0 &&
[[snapshot objectForKey: @"homeURL"] length] == 0 &&
[elements count] > 0)
{
[self _setSnapshotValue: @"homeURL"
to: [[elements objectAtIndex: 0] flattenedValuesForKey: @""]];
}
// If we do have a "work" URL but no "home" URL but two
// values URLs present, let's add the second one as the home URL
else if ([[snapshot objectForKey: @"workURL"] length] > 0 &&
[[snapshot objectForKey: @"homeURL"] length] == 0 &&
[elements count] > 1)
{
int i;
for (i = 0; i < [elements count]; i++)
{
if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""]
caseInsensitiveCompare: [snapshot objectForKey: @"workURL"]] != NSOrderedSame)
{
[self _setSnapshotValue: @"homeURL"
to: [[elements objectAtIndex: i] flattenedValuesForKey: @""]];
break;
}
}
}
[self _setSnapshotValue: @"calFBURL"
to: [[card uniqueChildWithTag: @"FBURL"] flattenedValuesForKey: @""]];
[self _setSnapshotValue: @"title" to: [card title]];
[self _setupOrgFields];
[self _setSnapshotValue: @"bday" to: [card bday]];
[self _setSnapshotValue: @"tz" to: [card tz]];
[self _setSnapshotValue: @"note" to: [card note]];
[self _retrieveQueryParameter: @"contactEmail"
intoSnapshotValue: @"workMail"];
[self _retrieveQueryParameter: @"contactFN"
intoSnapshotValue: @"fn"];
}
- (id <WOActionResults>) defaultAction
{
card = [[self clientObject] vCard];
if (card)
[self initSnapshot];
else
return [NSException exceptionWithHTTPStatus:404 /* Not Found */
reason: @"could not open contact"];
return self;
} }
- (NSString *) viewActionName - (NSString *) viewActionName
@ -647,216 +339,60 @@
return @"editAsContact"; return @"editAsContact";
} }
- (BOOL) canCreateOrModify - (BOOL) supportPhotos
{ {
SOGoObject *co; return [[self clientObject] isKindOfClass: SOGoContactGCSEntryK];
co = [self clientObject];
return ([co isKindOfClass: [SOGoContentObject class]]
&& [super canCreateOrModify]);
} }
- (NSArray *) photosURL - (BOOL) hasPhoto
{
return [[self clientObject] hasPhoto];
}
- (NSString *) photoURL
{ {
NSArray *photoElements;
NSURL *soURL; NSURL *soURL;
NSString *baseInlineURL, *photoURL;
NGVCardPhoto *photo;
int count, max;
if (!photosURL) soURL = [[self clientObject] soURL];
{
soURL = [[self clientObject] soURL];
baseInlineURL = [soURL absoluteString];
photoElements = [card childrenWithTag: @"photo"];
max = [photoElements count];
photosURL = [[NSMutableArray alloc] initWithCapacity: max];
for (count = 0; count < max; count++)
{
photo = [photoElements objectAtIndex: count];
if ([photo isInline])
photoURL = [NSString stringWithFormat: @"%@/photo%d",
baseInlineURL, count];
else
photoURL = [photo flattenedValuesForKey: @""];
[photosURL addObject: photoURL];
}
}
return photosURL; return [NSString stringWithFormat: @"%@/photo", [soURL absoluteString]];
}
- (CardElement *) _elementWithTag: (NSString *) tag
ofType: (NSString *) type
{
NSArray *elements;
CardElement *element;
elements = [card childrenWithTag: tag
andAttribute: @"type" havingValue: type];
if ([elements count] > 0)
element = [elements objectAtIndex: 0];
else
{
element = [CardElement new];
[element autorelease];
[element setTag: tag];
[element addType: type];
[card addChild: element];
}
return element;
}
- (void) _savePhoneValues
{
CardElement *phone;
phone = [self _elementWithTag: @"tel" ofType: @"work"];
[phone setSingleValue: [snapshot objectForKey: @"telephoneNumber"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"home"];
[phone setSingleValue: [snapshot objectForKey: @"homeTelephoneNumber"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"cell"];
[phone setSingleValue: [snapshot objectForKey: @"mobile"] forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"fax"];
[phone setSingleValue: [snapshot objectForKey: @"facsimileTelephoneNumber"]
forKey: @""];
phone = [self _elementWithTag: @"tel" ofType: @"pager"];
[phone setSingleValue: [snapshot objectForKey: @"pager"] forKey: @""];
}
- (void) _saveEmails
{
CardElement *workMail, *homeMail;
workMail = [self _elementWithTag: @"email" ofType: @"work"];
[workMail setSingleValue: [snapshot objectForKey: @"workMail"] forKey: @""];
homeMail = [self _elementWithTag: @"email" ofType: @"home"];
[homeMail setSingleValue: [snapshot objectForKey: @"homeMail"] forKey: @""];
if (preferredEmail)
{
if ([preferredEmail isEqualToString: @"work"])
[card setPreferred: workMail];
else
[card setPreferred: homeMail];
}
[[card uniqueChildWithTag: @"x-mozilla-html"]
setSingleValue: [snapshot objectForKey: @"mozillaUseHtmlMail"]
forKey: @""];
}
- (void) _saveSnapshot
{
CardElement *element;
NSArray *units;
[card setNWithFamily: [snapshot objectForKey: @"sn"]
given: [snapshot objectForKey: @"givenName"]
additional: nil
prefixes: nil
suffixes: nil];
[card setNickname: [snapshot objectForKey: @"nickname"]];
[card setFn: [snapshot objectForKey: @"fn"]];
[card setTitle: [snapshot objectForKey: @"title"]];
[card setBday: [snapshot objectForKey: @"bday"]];
[card setNote: [snapshot objectForKey: @"note"]];
[card setTz: [snapshot objectForKey: @"tz"]];
element = [self _elementWithTag: @"adr" ofType: @"home"];
[element setSingleValue: [snapshot objectForKey: @"homeExtendedAddress"]
atIndex: 1 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"homeStreetAddress"]
atIndex: 2 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"homeCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"homeState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"homePostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"homeCountry"]
atIndex: 6 forKey: @""];
element = [self _elementWithTag: @"adr" ofType: @"work"];
[element setSingleValue: [snapshot objectForKey: @"workExtendedAddress"]
atIndex: 1 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"workStreetAddress"]
atIndex: 2 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"workCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"workState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"workPostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [snapshot objectForKey: @"workCountry"]
atIndex: 6 forKey: @""];
element = [CardElement simpleElementWithTag: @"fburl"
value: [snapshot objectForKey: @"calFBURL"]];
[card setUniqueChild: element];
units = [NSArray arrayWithObject: [snapshot objectForKey: @"workService"]];
[card setOrg: [snapshot objectForKey: @"workCompany"]
units: units];
[self _savePhoneValues];
[self _saveEmails];
[[self _elementWithTag: @"url" ofType: @"home"]
setSingleValue: [snapshot objectForKey: @"homeURL"] forKey: @""];
[[self _elementWithTag: @"url" ofType: @"work"]
setSingleValue: [snapshot objectForKey: @"workURL"] forKey: @""];
[[card uniqueChildWithTag: @"x-aim"]
setSingleValue: [snapshot objectForKey: @"screenName"]
forKey: @""];
} }
- (id <WOActionResults>) saveAction - (id <WOActionResults>) saveAction
{ {
SOGoContactGCSEntry *contact; SOGoObject <SOGoContactObject> *contact;
id result; id result;
NSString *jsRefreshMethod; NSString *jsRefreshMethod;
SoSecurityManager *sm; SoSecurityManager *sm;
contact = [self clientObject]; contact = [self clientObject];
card = [contact vCard]; [contact setLDIFRecord: ldifRecord];
if (card) [self _fetchAndCombineCategoriesList];
{ [contact save];
// [self _fixupSnapshot];
[self _saveSnapshot];
[card setCategories: contactCategories];
[self _fetchAndCombineCategoriesList: contactCategories];
[contact save];
if (componentAddressBook && componentAddressBook != [self componentAddressBook]) if (componentAddressBook && componentAddressBook != [self componentAddressBook])
{ {
sm = [SoSecurityManager sharedSecurityManager]; sm = [SoSecurityManager sharedSecurityManager];
if (![sm validatePermission: SoPerm_DeleteObjects if (![sm validatePermission: SoPerm_DeleteObjects
onObject: componentAddressBook onObject: componentAddressBook
inContext: context]) inContext: context])
{
if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: componentAddressBook
inContext: context])
[contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception
}
}
if ([[[[self context] request] formValueForKey: @"nojs"] intValue])
result = [self redirectToLocation: [self modulePath]];
else
{ {
jsRefreshMethod if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
= [NSString stringWithFormat: @"refreshContacts(\"%@\")", onObject: componentAddressBook
[contact nameInContainer]]; inContext: context])
result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; [contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception
} }
} }
if ([[[[self context] request] formValueForKey: @"nojs"] intValue])
result = [self redirectToLocation: [self modulePath]];
else else
result = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ {
reason: @"method cannot be invoked on " jsRefreshMethod
@"the specified object"]; = [NSString stringWithFormat: @"refreshContacts(\"%@\")",
[contact nameInContainer]];
result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
}
return result; return result;
} }
@ -866,17 +402,15 @@
NSString *email, *cn, *url; NSString *email, *cn, *url;
NSMutableString *address; NSMutableString *address;
card = [[self clientObject] vCard]; [self ldifRecord];
[self initSnapshot]; email = [ldifRecord objectForKey: @"mail"];
if ([preferredEmail isEqualToString: @"home"]) if ([email length] == 0)
email = [snapshot objectForKey: @"homeMail"]; email = [ldifRecord objectForKey: @"mozillasecondemail"];
else
email = [snapshot objectForKey: @"workMail"];
if (email) if (email)
{ {
address = [NSMutableString string]; address = [NSMutableString string];
cn = [card fn]; cn = [ldifRecord objectForKey: @"cn"];
if ([cn length] > 0) if ([cn length] > 0)
[address appendFormat: @"%@ <%@>", cn, email]; [address appendFormat: @"%@ <%@>", cn, email];
else else

View File

@ -35,6 +35,7 @@
#import <Contacts/SOGoContactObject.h> #import <Contacts/SOGoContactObject.h>
#import <Contacts/SOGoContactFolder.h> #import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h> #import <Contacts/SOGoContactFolders.h>
#import <Contacts/NSDictionary+LDIF.h>
#import <SoObjects/Contacts/NGVCard+SOGo.h> #import <SoObjects/Contacts/NGVCard+SOGo.h>
#import <SoObjects/Contacts/NGVList+SOGo.h> #import <SoObjects/Contacts/NGVList+SOGo.h>
@ -76,9 +77,10 @@
inContext: [self context] inContext: [self context]
acquire: NO]; acquire: NO];
if ([currentChild respondsToSelector: @selector (vCard)]) if ([currentChild respondsToSelector: @selector (vCard)])
[content appendFormat: [[currentChild vCard] ldifString]]; [content appendFormat: [[currentChild ldifRecord] ldifRecordAsString]];
else if ([currentChild respondsToSelector: @selector (vList)]) else if ([currentChild respondsToSelector: @selector (vList)])
[content appendFormat: [[currentChild vList] ldifString]]; [content appendFormat: [[currentChild vList] ldifString]];
[content appendString: @"\n"];
} }
response = [context response]; response = [context response];

View File

@ -52,8 +52,16 @@
#import "UIxContactFoldersView.h" #import "UIxContactFoldersView.h"
Class SOGoContactSourceFolderK, SOGoGCSFolderK;
@implementation UIxContactFoldersView @implementation UIxContactFoldersView
+ (void) initialize
{
SOGoContactSourceFolderK = [SOGoContactSourceFolder class];
SOGoGCSFolderK = [SOGoGCSFolder class];
}
- (id) init - (id) init
{ {
if ((self = [super init])) if ((self = [super init]))
@ -110,11 +118,13 @@
folders = [self clientObject]; folders = [self clientObject];
folder = [folders lookupPersonalFolder: @"personal" ignoringRights: YES]; folder = [folders lookupPersonalFolder: @"personal" ignoringRights: YES];
if (folder && [folder conformsToProtocol: @protocol (SOGoContactFolder)])
contactInfos = [folder lookupContactsWithFilter: nil contactInfos = [folder lookupContactsWithFilter: nil
onCriteria: nil onCriteria: nil
sortBy: @"c_cn" sortBy: @"c_cn"
ordering: NSOrderedAscending]; ordering: NSOrderedAscending];
else
contactInfos = nil;
return contactInfos; return contactInfos;
} }
@ -183,7 +193,7 @@
{ {
folder = [folders objectAtIndex: i]; folder = [folders objectAtIndex: i];
/* We first search in LDAP folders (in case of duplicated entries in GCS folders) */ /* We first search in LDAP folders (in case of duplicated entries in GCS folders) */
if ([folder isKindOfClass: [SOGoContactSourceFolder class]]) if ([folder isKindOfClass: SOGoContactSourceFolderK])
[sortedFolders insertObject: folder atIndex: 0]; [sortedFolders insertObject: folder atIndex: 0];
else else
[sortedFolders addObject: folder]; [sortedFolders addObject: folder];
@ -287,10 +297,23 @@
- (NSString *) currentContactFolderClass - (NSString *) currentContactFolderClass
{ {
return ([currentFolder isKindOfClass: [SOGoContactSourceFolder class]] return (([currentFolder isKindOfClass: SOGoContactSourceFolderK]
&& ![currentFolder isPersonalSource])
? @"remote" : @"local"); ? @"remote" : @"local");
} }
- (NSString *) currentContactFolderAclEditing
{
return ([currentFolder isKindOfClass: SOGoGCSFolderK]
? @"available": @"unavailable");
}
- (NSString *) currentContactFolderListEditing
{
return ([currentFolder isKindOfClass: SOGoGCSFolderK]
? @"available": @"unavailable");
}
- (NSString *) verticalDragHandleStyle - (NSString *) verticalDragHandleStyle
{ {
NSString *vertical; NSString *vertical;

View File

@ -52,6 +52,7 @@
- (void) dealloc - (void) dealloc
{ {
[card release];
[photosURL release]; [photosURL release];
[super dealloc]; [super dealloc];
} }
@ -377,9 +378,7 @@
{ {
NSString *data; NSString *data;
data = nil; if ([url length] > 0)
if (url)
{ {
if (![[url lowercaseString] rangeOfString: @"://"].length) if (![[url lowercaseString] rangeOfString: @"://"].length)
url = [NSString stringWithFormat: @"http://%@", url]; url = [NSString stringWithFormat: @"http://%@", url];
@ -388,6 +387,8 @@
@"<a href=\"%@\" target=\"_blank\">%@</a>", @"<a href=\"%@\" target=\"_blank\">%@</a>",
url, url]; url, url];
} }
else
data = nil;
return [self _cardStringWithLabel: nil value: data]; return [self _cardStringWithLabel: nil value: data];
} }
@ -646,30 +647,12 @@
/* action */ /* action */
- (id <WOActionResults>) vcardAction
{
#warning this method is unused
WOResponse *response;
card = [[self clientObject] vCard];
if (card)
{
response = [context response];
[response setHeader: @"text/vcard" forKey: @"Content-type"];
[response appendContentString: [card versitString]];
}
else
return [NSException exceptionWithHTTPStatus: 404 /* Not Found */
reason:@"could not locate contact"];
return response;
}
- (id <WOActionResults>) defaultAction - (id <WOActionResults>) defaultAction
{ {
card = [[self clientObject] vCard]; card = [[self clientObject] vCard];
if (card) if (card)
{ {
[card retain];
phones = nil; phones = nil;
homeAdr = nil; homeAdr = nil;
workAdr = nil; workAdr = nil;

View File

@ -139,6 +139,11 @@
pageName = "UIxContactEditor"; pageName = "UIxContactEditor";
actionName = "new"; actionName = "new";
}; };
renameFolder = {
protectedBy = "Change Permissions";
actionClass = "UIxFolderActions";
actionName = "renameFolder";
};
mailer-contacts = { mailer-contacts = {
protectedBy = "<public>"; protectedBy = "<public>";
pageName = "UIxContactFoldersView"; pageName = "UIxContactFoldersView";
@ -234,13 +239,27 @@
SOGoContactLDIFEntry = { SOGoContactLDIFEntry = {
methods = { methods = {
view = { view = {
protectedBy = "<public>"; protectedBy = "Access Contents Information";
pageName = "UIxContactView"; pageName = "UIxContactView";
}; };
edit = { edit = {
protectedBy = "<public>"; protectedBy = "Access Contents Information";
pageName = "UIxContactEditor"; pageName = "UIxContactEditor";
}; };
editAsContact = {
protectedBy = "Access Contents Information";
pageName = "UIxContactEditor";
};
save = {
protectedBy = "Change Images And Files";
pageName = "UIxContactEditor";
actionName = "save";
};
saveAsContact = {
protectedBy = "Change Images And Files";
pageName = "UIxContactEditor";
actionName = "save";
};
write = { write = {
protectedBy = "<public>"; protectedBy = "<public>";
pageName = "UIxContactEditor"; pageName = "UIxContactEditor";

View File

@ -1,4 +1,4 @@
<?xml version='1.0' standalone='yes'?> <?xml version='1.0'?>
<!DOCTYPE var:component> <!DOCTYPE var:component>
<var:component <var:component
xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml"
@ -30,12 +30,16 @@
<li target="baseInfos"> <li target="baseInfos">
<span><var:string label:value="Contact" /></span> <span><var:string label:value="Contact" /></span>
</li> </li>
<li target="categoryInfos"> <var:if condition="supportCategories"
<span><var:string label:value="Categories" /></span></li> ><li target="categoryInfos">
<span><var:string label:value="Categories" /></span></li
></var:if>
<li target="addressesInfos"> <li target="addressesInfos">
<span><var:string label:value="Address" /></span></li> <span><var:string label:value="Address" /></span></li>
<li target="photos"> <var:if condition="supportPhotos"
<span><var:string label:value="Photos" /></span></li> ><li target="photos">
<span><var:string label:value="Photos" /></span></li
></var:if>
<li target="otherInfos"> <li target="otherInfos">
<span><var:string label:value="Other" /></span></li> <span><var:string label:value="Other" /></span></li>
</ul> </ul>
@ -46,34 +50,34 @@
<tr> <tr>
<td> <td>
<label><var:string label:value="First:" /> <label><var:string label:value="First:" />
<input type="text" class="textField" name="givenName" <input type="text" class="textField" name="givenname"
id="givenName" id="givenname"
var:value="snapshot.givenName" /> var:value="ldifRecord.givenname"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Last:" /> <label><var:string label:value="Last:" />
<input type="text" class="textField" name="sn" id="sn" <input type="text" class="textField" name="sn" id="sn"
var:value="snapshot.sn" /> var:value="ldifRecord.sn"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Display:" /> <label><var:string label:value="Display:" />
<input type="text" class="textField" name="fn" id="fn" <input type="text" class="textField" name="displayname" id="displayname"
var:value="snapshot.fn" /> var:value="ldifRecord.displayname"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Nickname:" /> <label><var:string label:value="Nickname:" />
<input type="text" class="textField" name="nickname" id="nickname" <input type="text" class="textField" name="mozillanickname" id="mozillanickname"
var:value="snapshot.nickname" /> var:value="ldifRecord.mozillanickname"
</label> /></label>
</td> </td>
</tr> </tr>
</table> </table>
@ -82,26 +86,26 @@
<tr> <tr>
<td> <td>
<label><var:string label:value="Email:" /> <label><var:string label:value="Email:" />
<input type="text" class="textField" name="workMail" id="workMail" <input type="text" class="textField" name="mail" id="mail"
var:value="snapshot.workMail" /> var:value="ldifRecord.mail"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Additional Email:" /> <label><var:string label:value="Additional Email:" />
<input type="text" class="textField" name="homeMail" <input type="text" class="textField" name="mozillasecondemail"
id="homeMail" var:value="snapshot.homeMail" /> id="mozillasecondemail" var:value="ldifRecord.mozillasecondemail"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Screen Name:" <label><var:string label:value="Screen Name:"
/> />
<input type="text" class="textField" name="screenName" <input type="text" class="textField" name="nsaimid"
id="screenName" var:value="snapshot.screenName" /> id="nsaimid" var:value="ldifRecord.nsaimid"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -111,9 +115,8 @@
<var:popup list="htmlMailFormatList" item="item" <var:popup list="htmlMailFormatList" item="item"
label:noSelectionString="htmlMailFormat_UNKNOWN" label:noSelectionString="htmlMailFormat_UNKNOWN"
string="itemHtmlMailFormatText" string="itemHtmlMailFormatText"
selection="snapshot.mozillaUseHtmlMail" selection="ldifRecord.mozillausehtmlmail"
/> /></label>
</label>
</td> </td>
</tr> </tr>
</table> </table>
@ -123,9 +126,9 @@
<td> <td>
<label><var:string label:value="Work:" /> <label><var:string label:value="Work:" />
<input type="text" class="textField" <input type="text" class="textField"
name="telephoneNumber" name="telephonenumber"
id="telephoneNumber" id="telephonenumber"
var:value="snapshot.telephoneNumber" var:value="ldifRecord.telephonenumber"
/></label> /></label>
</td> </td>
</tr> </tr>
@ -133,24 +136,19 @@
<td> <td>
<label><var:string label:value="Home:" /> <label><var:string label:value="Home:" />
<input type="text" class="textField" <input type="text" class="textField"
name="homeTelephoneNumber" name="homephone" id="homephone"
id="homeTelephoneNumber" var:value="ldifRecord.homephone"
var:value="snapshot.homeTelephoneNumber" /></label>
/>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Fax:" /> <label><var:string label:value="Fax:" />
<input type="text" class="textField" <input type="text" class="textField"
name="facsimileTelephoneNumber" name="facsimiletelephonenumber"
id="facsimileTelephoneNumber" id="facsimiletelephonenumber"
var:value="snapshot.facsimileTelephoneNumber" var:value="ldifRecord.facsimiletelephonenumber"
/></label>
/>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -159,38 +157,37 @@
<input type="text" class="textField" <input type="text" class="textField"
name="pager" name="pager"
id="pager" id="pager"
var:value="snapshot.pager" var:value="ldifRecord.pager"
/></label>
/>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<label><var:string label:value="Mobile:" /> <label><var:string label:value="Mobile:" />
<input type="text" class="textField" name="mobile" id="mobile" <input type="text" class="textField" name="mobile" id="mobile"
var:value="snapshot.mobile" /> var:value="ldifRecord.mobile"
</label> /></label>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
<div id="categoryInfos" class="tab"> <var:if condition="supportCategories"
<div id="categoryContainer"> ><div id="categoryInfos" class="tab">
</div> <div id="categoryContainer">
<var:if condition="canCreateOrModify" </div>
><input type="text" class="textField" id="emptyCategory" <var:if condition="canCreateOrModify"
const:readonly="readonly" ><input type="text" class="textField" id="emptyCategory"
const:name="emptyCategory" const:readonly="readonly"
const:name="emptyCategory"
const:value="" /></var:if> const:value="" /></var:if>
<script type="text/javascript"> <script type="text/javascript">
var gCategories = <var:string value="contactCategoriesList" const:escapeHTML="NO"/>; var gCategories = <var:string value="contactCategoriesList" const:escapeHTML="NO"/>;
</script> </script>
<input type="hidden" id="contactCategories" <input type="hidden" id="jsonContactCategories"
const:name="contactCategories" const:name="jsonContactCategories"
var:value="contactCategories" /> var:value="jsonContactCategories" />
</div> </div></var:if>
<div id="addressesInfos" class="tab"> <div id="addressesInfos" class="tab">
<span class="caption"><var:string label:value="Home" /></span> <span class="caption"><var:string label:value="Home" /></span>
@ -198,64 +195,64 @@
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Address:" /> <label><var:string label:value="Address:" />
<input type="text" class="textField" name="homeStreetAddress" <input type="text" class="textField" name="mozillahomestreet"
id="homeStreetAddress" id="mozillahomestreet"
var:value="snapshot.homeStreetAddress" /> var:value="ldifRecord.mozillahomestreet"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label> <label>
<input type="text" class="textField" name="homeExtendedAddress" <input type="text" class="textField" namCe="mozillahomestreet2"
id="homeExtendedAddress" id="mozillahomestreet2"
var:value="snapshot.homeExtendedAddress" /> var:value="ldifRecord.mozillahomestreet2"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="City:" /> <label><var:string label:value="City:" />
<input type="text" class="textField" name="homeCity" <input type="text" class="textField" name="mozillahomelocalityname"
id="homeCity" id="mozillahomelocalityname"
var:value="snapshot.homeCity" /> var:value="ldifRecord.mozillahomelocalityname"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="firstColumn"> <td class="firstColumn">
<label><var:string label:value="State_Province:" /> <label><var:string label:value="State_Province:" />
<input type="text" class="textField" name="homeState" <input type="text" class="textField" name="mozillahomestate"
id="homeState" id="mozillahomestate"
var:value="snapshot.homeState" /> var:value="ldifRecord.mozillahomestate"
</label> /></label>
</td> </td>
<td class="secondColumn"> <td class="secondColumn">
<label><var:string <label><var:string
label:value="ZIP_Postal Code:" label:value="ZIP_Postal Code:"
/> />
<input type="text" class="textField" name="homePostalCode" <input type="text" class="textField" name="mozillahomepostalcode"
id="homePostalCode" id="mozillahomepostalcode"
var:value="snapshot.homePostalCode" /> var:value="ldifRecord.mozillahomepostalcode"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Country:" /> <label><var:string label:value="Country:" />
<input type="text" class="textField" name="homeCountry" <input type="text" class="textField" name="mozillahomecountryname"
id="homeCountry" id="mozillahomecountryname"
var:value="snapshot.homeCountry" /> var:value="ldifRecord.mozillahomecountryname"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label> <label>
<var:string label:value="Web Page:" /> <var:string label:value="Web Page:" />
<input type="text" class="textField" name="homeURL" <input type="text" class="textField" name="mozillahomeurl"
var:value="snapshot.homeURL" /> var:value="ldifRecord.mozillahomeurl"
</label> /></label>
</td> </td>
</tr> </tr>
</table> </table>
@ -267,128 +264,116 @@
<var:string label:value="Title:" /> <var:string label:value="Title:" />
<input type="text" class="textField" name="title" <input type="text" class="textField" name="title"
id="title" id="title"
var:value="snapshot.title" /> var:value="ldifRecord.title"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label> <label>
<var:string label:value="Department:" /> <var:string label:value="Department:" />
<input type="text" class="textField" name="workService" <input type="text" class="textField" name="ou"
id="workService" id="ou"
var:value="snapshot.workService" /> var:value="ldifRecord.ou"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Organization:" /> <label><var:string label:value="Organization:" />
<input type="text" class="textField" name="workCompany" <input type="text" class="textField" name="o"
id="workCompany" id="o" var:value="ldifRecord.o"
var:value="snapshot.workCompany" /> /></label>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Address:" /> <label><var:string label:value="Address:" />
<input type="text" class="textField" name="workStreetAddress" <input type="text" class="textField" name="street"
id="workStreetAddress" id="street"
var:value="snapshot.workStreetAddress" /> var:value="ldifRecord.street"
</label> /></label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label> <label>
<input type="text" class="textField" name="workExtendedAddress" <input type="text" class="textField" name="mozillaworkstreet2"
id="workExtendedAddress" id="mozillaworkstreet2" var:value="ldifRecord.mozillaworkstreet2"
var:value="snapshot.workExtendedAddress" /> /></label>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="City:" /> <label><var:string label:value="City:" />
<input type="text" class="textField" name="workCity" <input type="text" class="textField" name="l"
id="workCity" id="l" var:value="ldifRecord.l"
var:value="snapshot.workCity" /> /></label>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="firstColumn"> <td class="firstColumn">
<label><var:string label:value="State_Province:" /> <label><var:string label:value="State_Province:" />
<input type="text" class="textField" name="workState" <input type="text" class="textField" name="st"
id="workState" id="st" var:value="ldifRecord.st"
var:value="snapshot.workState" /> /></label>
</label>
</td> </td>
<td class="secondColumn"> <td class="secondColumn">
<label><var:string <label><var:string
label:value="ZIP_Postal Code:" label:value="ZIP_Postal Code:"
/> />
<input type="text" class="textField" name="workPostalCode" <input type="text" class="textField" name="postalCode"
id="workPostalCode" id="postalCode" var:value="ldifRecord.postalCode"
var:value="snapshot.workPostalCode" /> /></label>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Country:" /> <label><var:string label:value="Country:" />
<input type="text" class="textField" name="workCountry" <input type="text" class="textField" name="c"
id="workCountry" id="c" var:value="ldifRecord.c"
var:value="snapshot.workCountry" /> /></label>
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<label><var:string label:value="Web Page:" /> <label><var:string label:value="Web Page:" />
<input type="text" class="textField" name="workURL" <input type="text" class="textField" name="mozillaworkurl"
var:value="snapshot.workURL" /> var:value="ldifRecord.mozillaworkurl"
</label> /></label>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
<div id="photos" class="tab"> <var:if condition="supportPhotos"
<var:foreach list="photosURL" item="currentPhotoURL"> ><div id="photos" class="tab">
<img var:src="currentPhotoURL" class="contactPhoto"/><br <var:if condition="hasPhoto"
/></var:foreach> ><img var:src="photoURL" class="contactPhoto"
</div> /></var:if>
</div
></var:if>
<div id="otherInfos" class="tab"> <div id="otherInfos" class="tab">
<table class="framenocaption"> <table class="framenocaption">
<tr> <tr>
<td class="firstColumn"> <td>
<label><var:string label:value="Birthday (yyyy-mm-dd):" /> <label><var:string label:value="Birthday (yyyy-mm-dd):"/>
<input type="text" class="textField" name="birthyear" id="birthyear"
var:value="ldifRecord.birthyear"
/></label>
-
<input type="text" class="textField" name="birthmonth" id="birthmonth"
var:value="ldifRecord.birthmonth" />
-
<input type="text" class="textField" name="birthday" id="birthday" <input type="text" class="textField" name="birthday" id="birthday"
var:value="snapshot.bday" /> var:value="ldifRecord.birthday"/>
</label>
</td>
<td class="secondColumn">
<label><var:string label:value="Timezone:" />
<input type="text" class="textField" name="tz" id="tz"
var:value="snapshot.tz" />
</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td>
<label><var:string label:value="Freebusy URL:" />
<input type="text" class="textField" name="calFBURL" id="calFBURL"
var:value="snapshot.calFBURL" />
</label>
</td>
</tr>
<tr>
<td colspan="2">
<label><var:string label:value="Note:" /> <label><var:string label:value="Note:" />
<textarea var:value="snapshot.note" name="note" id="note"></textarea> <textarea var:value="ldifRecord.description" name="note" id="note"></textarea>
</label> </label>
</td> </td>
</tr> </tr>

View File

@ -121,6 +121,8 @@
><li var:id="currentContactFolderId" ><li var:id="currentContactFolderId"
var:owner="currentContactFolderOwner" var:owner="currentContactFolderOwner"
var:class="currentContactFolderClass" var:class="currentContactFolderClass"
var:acl-editing="currentContactFolderAclEditing"
var:list-editing="currentContactFolderListEditing"
><var:string value="currentContactFolderName"/></li ><var:string value="currentContactFolderName"/></li
></var:foreach ></var:foreach
> >

View File

@ -258,17 +258,6 @@ function onContactContextMenuHide(event) {
this.stopObserving("contextmenu:hide", onContactContextMenuHide); this.stopObserving("contextmenu:hide", onContactContextMenuHide);
} }
function onFolderMenuHide(event) {
var topNode = $('d');
if (topNode.menuSelectedEntry) {
topNode.menuSelectedEntry.deselect();
topNode.menuSelectedEntry = null;
}
if (topNode.selectedEntry)
topNode.selectedEntry.selectElement();
}
function _onContactMenuAction(folderItem, action, refresh) { function _onContactMenuAction(folderItem, action, refresh) {
var selectedFolders = $("contactFolders").getSelectedNodes(); var selectedFolders = $("contactFolders").getSelectedNodes();
var folderId = $(folderItem).readAttribute("folderId"); var folderId = $(folderItem).readAttribute("folderId");
@ -308,8 +297,9 @@ function onContactMenuMove(event) {
function onMenuExportContact (event) { function onMenuExportContact (event) {
var selectedFolders = $("contactFolders").getSelectedNodes(); var selectedFolders = $("contactFolders").getSelectedNodes();
var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var canExport = (selectedFolders[0].getAttribute("owner") != "nobody");
if (selectedFolderId != "/shared") { if (canExport) {
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
var contactIds = $(document.menuTarget).collect(function(row) { var contactIds = $(document.menuTarget).collect(function(row) {
return row.getAttribute("id"); return row.getAttribute("id");
}); });
@ -512,9 +502,6 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) {
var contactsList = $('contactsList'); var contactsList = $('contactsList');
var rows = contactsList.getSelectedRowsId(); var rows = contactsList.getSelectedRowsId();
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {
var row = $(rows[i]);
row.deselect();
row.hide();
delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]]; delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]];
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/" var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/"
+ rows[i] + "/delete"); + rows[i] + "/delete");
@ -532,6 +519,7 @@ function onContactDeleteEventCallback(http) {
$("contactView").update(); $("contactView").update();
Contact.currentContact = null; Contact.currentContact = null;
} }
Contact.deleteContactsRequestCount--; Contact.deleteContactsRequestCount--;
if (Contact.deleteContactsRequestCount == 0) { if (Contact.deleteContactsRequestCount == 0) {
var nextRow = row.next("tr"); var nextRow = row.next("tr");
@ -543,7 +531,10 @@ function onContactDeleteEventCallback(http) {
loadContact(Contact.currentContact); loadContact(Contact.currentContact);
} }
} }
row.parentNode.removeChild(row); if (row) {
row.deselect();
row.parentNode.removeChild(row);
}
} }
else if (parseInt(http.status) == 403) { else if (parseInt(http.status) == 403) {
var row = $(http.callbackData); var row = $(http.callbackData);
@ -598,10 +589,11 @@ function newContact(sender) {
function newList(sender) { function newList(sender) {
var li = $(Contact.currentAddressBook); var li = $(Contact.currentAddressBook);
if (li.hasClassName("remote")) var listEditing = li.getAttribute("list-editing");
showAlertDialog(_("You cannot create a list in a shared address book.")); if (listEditing && listEditing == "available")
else
openContactWindow(URLForFolderID(Contact.currentAddressBook) + "/newlist"); openContactWindow(URLForFolderID(Contact.currentAddressBook) + "/newlist");
else
showAlertDialog(_("You cannot create a list in a shared address book."));
return false; return false;
} }
@ -1068,15 +1060,15 @@ function onMenuSharing(event) {
var folders = $("contactFolders"); var folders = $("contactFolders");
var selected = folders.getSelectedNodes()[0]; var selected = folders.getSelectedNodes()[0];
var owner = selected.getAttribute("owner"); var aclEditing = selected.getAttribute("acl-editing");
if (owner == "nobody") if (aclEditing && aclEditing == "available") {
showAlertDialog(_("The user rights cannot be edited for this object!"));
else {
var title = this.innerHTML; var title = this.innerHTML;
var url = URLForFolderID(selected.getAttribute("id")); var url = URLForFolderID(selected.getAttribute("id"));
openAclWindow(url + "/acls", title); openAclWindow(url + "/acls", title);
} }
else
showAlertDialog(_("The user rights cannot be edited for this object!"));
} }
function onAddressBooksMenuPrepareVisibility() { function onAddressBooksMenuPrepareVisibility() {
@ -1089,30 +1081,55 @@ function onAddressBooksMenuPrepareVisibility() {
var menu = $("contactFoldersMenu").down("ul");; var menu = $("contactFoldersMenu").down("ul");;
var listElements = menu.childNodesWithTag("li"); var listElements = menu.childNodesWithTag("li");
var modifyOption = listElements[0]; var modifyOption = listElements[0];
var newListOption = listElements[3];
var removeOption = listElements[5]; var removeOption = listElements[5];
var exportOption = listElements[7]; var exportOption = listElements[7];
var importOption = listElements[8];
var sharingOption = listElements[listElements.length - 1]; var sharingOption = listElements[listElements.length - 1];
// Disable the "Sharing" and "Modify" options when address book // Disable the "Sharing" and "Modify" options when address book
// is not owned by user // is not owned by user
if (folderOwner == UserLogin || IsSuperUser) { if (folderOwner == UserLogin || IsSuperUser) {
modifyOption.removeClassName("disabled"); // WARNING: will fail for super users anyway modifyOption.removeClassName("disabled"); // WARNING: will fail
sharingOption.removeClassName("disabled"); // for super users
// anyway
var aclEditing = selected[0].getAttribute("acl-editing");
if (aclEditing && aclEditing == "available") {
sharingOption.removeClassName("disabled");
}
else {
sharingOption.addClassName("disabled");
}
} }
else { else {
modifyOption.addClassName("disabled"); modifyOption.addClassName("disabled");
sharingOption.addClassName("disabled"); sharingOption.addClassName("disabled");
} }
var listEditing = selected[0].getAttribute("list-editing");
if (listEditing && listEditing == "available") {
newListOption.removeClassName("disabled");
}
else {
newListOption.addClassName("disabled");
}
/* Disable the "remove" and "export ab" options when address book is /* Disable the "remove" and "export ab" options when address book is
public */ public */
if (folderOwner == "nobody") { if (folderOwner == "nobody") {
exportOption.addClassName("disabled"); exportOption.addClassName("disabled");
importOption.addClassName("disabled");
removeOption.addClassName("disabled"); removeOption.addClassName("disabled");
} }
else { else {
exportOption.removeClassName("disabled"); exportOption.removeClassName("disabled");
removeOption.removeClassName("disabled"); importOption.removeClassName("disabled");
if (selected[0].getAttribute("id") == "/personal") {
removeOption.addClassName("disabled");
}
else {
removeOption.removeClassName("disabled");
}
} }
return true; return true;

View File

@ -69,3 +69,9 @@ INPUT.comboBoxField, #emptyCategory
#otherInfos TEXTAREA #otherInfos TEXTAREA
{ width: 70%; } { width: 70%; }
#birthday, #birthmonth
{ width: 18px; }
#birthyear
{ width: 36px; }

View File

@ -22,9 +22,9 @@
02111-1307, USA. 02111-1307, USA.
*/ */
var dateRegex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/; var dateRegex = /^(([0-9]{2})?[0-9])?[0-9]-[0-9]?[0-9]-[0-9]?[0-9]$/;
var displayNameChanged = false; var displaynameChanged = false;
var tabIndex = 0; var tabIndex = 0;
@ -37,43 +37,43 @@ function unescapeCallbackParameter(s) {
} }
function copyContact(type, email, uid, sn, function copyContact(type, email, uid, sn,
cn, givenName, telephoneNumber, facsimileTelephoneNumber, displayname, givenname, telephonenumber, facsimiletelephonenumber,
mobile, postalAddress, homePostalAddress, mobile, postalAddress, homePostalAddress,
departmentNumber, l) departmentnumber, l)
{ {
// var type = arguments[0]; // var type = arguments[0];
// var email = arguments[1]; // var email = arguments[1];
// var uid = arguments[2]; // var uid = arguments[2];
// var sn = arguments[3]; // var sn = arguments[3];
// var givenName = arguments[4]; // var givenname = arguments[4];
// var telephoneNumber = arguments[5]; // var telephonenumber = arguments[5];
// var facsimileTelephoneNumber = arguments[6]; // var facsimiletelephonenumber = arguments[6];
// var mobile = arguments[7]; // var mobile = arguments[7];
// var postalAddress = arguments[8]; // var postaladdress = arguments[8];
// var homePostalAddress = arguments[9]; // var homepostaladdress = arguments[9];
// var departmentNumber = arguments[10]; // var departmentnumber = arguments[10];
// var l = arguments[11]; // var l = arguments[11];
var e; var e;
e = $('cn'); e = $('displayname');
e.setAttribute('value', unescapeCallbackParameter(cn)); e.setAttribute('value', unescapeCallbackParameter(displayname));
e = $('email'); e = $('email');
e.setAttribute('value', email); e.setAttribute('value', email);
e = $('sn'); e = $('sn');
e.setAttribute('value', unescapeCallbackParameter(sn)); e.setAttribute('value', unescapeCallbackParameter(sn));
e = $('givenName'); e = $('givenname');
e.setAttribute('value', unescapeCallbackParameter(givenName)); e.setAttribute('value', unescapeCallbackParameter(givenname));
e = $('telephoneNumber'); e = $('telephonenumber');
e.setAttribute('value', telephoneNumber); e.setAttribute('value', telephonenumber);
e = $('facsimileTelephoneNumber'); e = $('facsimiletelephonenumber');
e.setAttribute('value', facsimileTelephoneNumber); e.setAttribute('value', facsimileTelephonenumber);
e = $('mobile'); e = $('mobile');
e.setAttribute('value', mobile); e.setAttribute('value', mobile);
e = $('postalAddress'); e = $('postaladdress');
e.setAttribute('value', unescapeCallbackParameter(postalAddress)); e.setAttribute('value', unescapeCallbackParameter(postalAddress));
e = $('homePostalAddress'); e = $('homepostaladdress');
e.setAttribute('value', unescapeCallbackParameter(homePostalAddress)); e.setAttribute('value', unescapeCallbackParameter(homePostalAddress));
e = $('departmentNumber'); e = $('departmentnumber');
e.setAttribute('value', unescapeCallbackParameter(departmentNumber)); e.setAttribute('value', unescapeCallbackParameter(departmentnumber));
e = $('l'); e = $('l');
e.setAttribute('value', unescapeCallbackParameter(l)); e.setAttribute('value', unescapeCallbackParameter(l));
}; };
@ -81,23 +81,25 @@ function copyContact(type, email, uid, sn,
function validateContactEditor() { function validateContactEditor() {
var rc = true; var rc = true;
var e = $('workMail'); var e = $('mail');
if (e.value.length > 0 if (e.value.length > 0
&& !emailRE.test(e.value)) { && !emailRE.test(e.value)) {
alert(_("invalidemailwarn")); alert(_("invalidemailwarn"));
rc = false; rc = false;
} }
e = $('homeMail'); e = $('mozillasecondemail');
if (e.value.length > 0 if (e.value.length > 0
&& !emailRE.test(e.value)) { && !emailRE.test(e.value)) {
alert(_("invalidemailwarn")); alert(_("invalidemailwarn"));
rc = false; rc = false;
} }
e = $('birthday'); var byear = $('birthyear');
if (e.value.length > 0 var bmonth = $('birthmonth');
&& !dateRegex.test(e.value)) { var bday = $('birthday');
var bdayValue = byear.value + "-" + bmonth.value + "-" + bday.value;
if (bdayValue != "--" && !dateRegex.test(bdayValue)) {
alert(_("invaliddatewarn")); alert(_("invaliddatewarn"));
rc = false; rc = false;
} }
@ -105,25 +107,25 @@ function validateContactEditor() {
return rc; return rc;
} }
function onFnKeyDown() { function onDisplaynameKeyDown() {
var fn = $("fn"); var fn = $("displayname");
fn.onkeydown = null; fn.onkeydown = null;
displayNameChanged = true; displaynameChanged = true;
return true; return true;
} }
function onFnNewValue(event) { function onDisplaynameNewValue(event) {
if (!displayNameChanged) { if (!displaynameChanged) {
var sn = $("sn").value.trim(); var sn = $("sn").value.trim();
var givenName = $("givenName").value.trim(); var givenname = $("givenname").value.trim();
var fullName = givenName; var fullname = givenname;
if (fullName && sn) if (fullname && sn)
fullName += ' '; fullname += ' ';
fullName += sn; fullname += sn;
$("fn").value = fullName; $("displayname").value = fullname;
} }
return true; return true;
@ -144,7 +146,7 @@ function onEditorSubmitClick(event) {
function saveCategories() { function saveCategories() {
var container = $("categoryContainer"); var container = $("categoryContainer");
var catsInput = $("contactCategories"); var catsInput = $("jsonContactCategories");
if (container && catsInput) { if (container && catsInput) {
var newCategories = $([]); var newCategories = $([]);
var inputs = container.select("INPUT"); var inputs = container.select("INPUT");
@ -164,8 +166,8 @@ function onDocumentKeydown(event) {
var target = Event.element(event); var target = Event.element(event);
if (target.tagName == "INPUT" || target.tagName == "SELECT") { if (target.tagName == "INPUT" || target.tagName == "SELECT") {
if (event.keyCode == Event.KEY_RETURN) { if (event.keyCode == Event.KEY_RETURN) {
var fcn = onEditorSubmitClick.bind($("submitButton")); var fdisplayname = onEditorSubmitClick.bind($("submitButton"));
fcn(); fdisplayname();
Event.stop(event); Event.stop(event);
} }
} }
@ -267,10 +269,10 @@ function initEditorForm() {
var controller = new SOGoTabsController(); var controller = new SOGoTabsController();
controller.attachToTabsContainer(tabsContainer); controller.attachToTabsContainer(tabsContainer);
displayNameChanged = ($("fn").value.length > 0); displaynameChanged = ($("displayname").value.length > 0);
$("fn").onkeydown = onFnKeyDown; $("displayname").onkeydown = onDisplaynameKeyDown;
$("sn").onkeyup = onFnNewValue; $("sn").onkeyup = onDisplaynameNewValue;
$("givenName").onkeyup = onFnNewValue; $("givenname").onkeyup = onDisplaynameNewValue;
$("cancelButton").observe("click", onEditorCancelClick); $("cancelButton").observe("click", onEditorCancelClick);
var submitButton = $("submitButton"); var submitButton = $("submitButton");
@ -280,8 +282,10 @@ function initEditorForm() {
Event.observe(document, "keydown", onDocumentKeydown); Event.observe(document, "keydown", onDocumentKeydown);
regenerateCategoriesMenu(); if (typeof(gCategories) != "undefined") {
var catsInput = $("contactCategories"); regenerateCategoriesMenu();
}
var catsInput = $("jsonContactCategories");
if (catsInput && catsInput.value.length > 0) { if (catsInput && catsInput.value.length > 0) {
var contactCats = $(catsInput.value.evalJSON(false)); var contactCats = $(catsInput.value.evalJSON(false));
for (var i = 0; i < contactCats.length; i++) { for (var i = 0; i < contactCats.length; i++) {
@ -294,6 +298,6 @@ function initEditorForm() {
emptyCategory.tabIndex = 10000; emptyCategory.tabIndex = 10000;
emptyCategory.observe("click", onEmptyCategoryClick); emptyCategory.observe("click", onEmptyCategoryClick);
} }
} }
document.observe("dom:loaded", initEditorForm); document.observe("dom:loaded", initEditorForm);

View File

@ -2,6 +2,6 @@
# This file is included by library makefiles to set the version information # This file is included by library makefiles to set the version information
# of the executable. # of the executable.
MAJOR_VERSION=1 MAJOR_VERSION=2
MINOR_VERSION=3 MINOR_VERSION=0
SUBMINOR_VERSION=12 SUBMINOR_VERSION=0