/* Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. OGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with OGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "UIxContactEditorBase.h" #include #include #include "common.h" @implementation UIxContactEditorBase - (id)init { if ((self = [super init])) { self->snapshot = [[NSMutableDictionary alloc] initWithCapacity:16]; } return self; } - (void)dealloc { [self->snapshot release]; [self->errorText release]; [self->contentString release]; [super dealloc]; } /* accessors */ - (void)setContentString:(NSString *)_cstr { ASSIGNCOPY(self->contentString, _cstr); } - (NSString *)contentString { return self->contentString; } - (NSString *)contentStringTemplate { return @"{}"; /* empty property list */ } - (NSMutableDictionary *)snapshot { return self->snapshot; } - (void)setErrorText:(NSString *)_txt { ASSIGNCOPY(self->errorText, _txt); } - (NSString *)errorText { return self->errorText; } - (BOOL)hasErrorText { return [self->errorText length] > 0 ? YES : NO; } /* load/store content format */ - (void)loadValuesFromContentString:(NSString *)_s { NSDictionary *plist; if ((plist = [_s propertyList]) == nil) { [self errorWithFormat:@"could not parse content string!"]; return; } [self->snapshot removeAllObjects]; [self->snapshot addEntriesFromDictionary:plist]; } - (void)_fixupSnapshot { // TODO: perform sanity checking, eg build CN on demand NSString *cn, *gn, *sn; cn = [self->snapshot objectForKey:@"cn"]; gn = [self->snapshot objectForKey:@"givenName"]; sn = [self->snapshot objectForKey:@"sn"]; if (![sn isNotNull] || [sn length] == 0) sn = nil; if (![cn isNotNull] || [cn length] == 0) cn = nil; if (sn == nil) { if (cn == nil) sn = @"[noname]"; else { // TODO: need a better name parser here NSRange r; r = [cn rangeOfString:@" "]; sn = (r.length > 0) ? [cn substringFromIndex:(r.location + r.length)] : cn; } [self->snapshot setObject:sn forKey:@"sn"]; } if (sn == nil && gn == nil) cn = @"[noname]"; else if (sn == nil) cn = gn; else if (gn == nil) cn = sn; else cn = [[gn stringByAppendingString:@" "] stringByAppendingString:sn]; [self->snapshot setObject:cn forKey:@"cn"]; } - (void)saveValuesIntoRecord:(NSMutableDictionary *)_record { [self _fixupSnapshot]; [_record addEntriesFromDictionary:[self snapshot]]; } /* JavaScript */ - (NSString *)jsCopyContactCode { static NSString *jsCode = \ @"function unescapeCallbackParameter(s) {\n" @" if(!s || s.length == 0)\n" @" return s;\n" @" s = s.replace(/'/g, \"'\");\n" @" s = s.replace(/"/g, '\"');\n" @" return s;\n" @"}\n" @"\n" @"function copyContact()" @"{\n" @" var type = arguments[0]; \n" @" var email = arguments[1]; \n" @" var uid = arguments[2]; \n" @" var sn = arguments[3]; \n" @" var givenName = arguments[4]; \n" @" var telephoneNumber = arguments[5]; \n" @" var facsimileTelephoneNumber = arguments[6]; \n" @" var mobile = arguments[7]; \n" @" var postalAddress = arguments[8]; \n" @" var homePostalAddress = arguments[9]; \n" @" var departmentNumber = arguments[10]; \n" @" var l = arguments[11]; \n" @" var e;\n" @" e = document.getElementById('email');\n" @" e.setAttribute('value', email);\n" @" e = document.getElementById('sn');\n" @" e.setAttribute('value', unescapeCallbackParameter(sn));\n" @" e = document.getElementById('givenName');\n" @" e.setAttribute('value', unescapeCallbackParameter(givenName));\n" @" e = document.getElementById('telephoneNumber');\n" @" e.setAttribute('value', telephoneNumber);\n" @" e = document.getElementById('facsimileTelephoneNumber');\n" @" e.setAttribute('value', facsimileTelephoneNumber);\n" @" e = document.getElementById('mobile');\n" @" e.setAttribute('value', mobile);\n" @" e = document.getElementById('postalAddress');\n" @" e.setAttribute('value', unescapeCallbackParameter(postalAddress));\n" @" e = document.getElementById('homePostalAddress');\n" @" e.setAttribute('value', unescapeCallbackParameter(homePostalAddress));\n" @" e = document.getElementById('departmentNumber');\n" @" e.setAttribute('value', unescapeCallbackParameter(departmentNumber));\n" @" e = document.getElementById('l');\n" @" e.setAttribute('value', unescapeCallbackParameter(l));\n" @"}\n"; return jsCode; } /* helper */ - (NSString *)_completeURIForMethod:(NSString *)_method { // TODO: this is a DUP of UIxAppointmentEditor NSString *uri; NSRange r; uri = [[[self context] request] uri]; /* first: identify query parameters */ r = [uri rangeOfString:@"?" options:NSBackwardsSearch]; if (r.length > 0) uri = [uri substringToIndex:r.location]; /* next: append trailing slash */ if (![uri hasSuffix:@"/"]) uri = [uri stringByAppendingString:@"/"]; /* next: append method */ uri = [uri stringByAppendingString:_method]; /* next: append query parameters */ return [self completeHrefForMethod:uri]; } /* actions */ - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{ return YES; } - (id)defaultAction { // TODO: very similiar to apt-editor (apt editor would need to use std names NSString *c; /* load iCalendar file */ c = [[self clientObject] contentAsString]; if ([c length] == 0) /* a new contact */ c = [self contentStringTemplate]; [self setContentString:c]; [self loadValuesFromContentString:c]; return self; } - (BOOL)isWriteableClientObject { return [[self clientObject] respondsToSelector:@selector(saveContentString:)]; } - (NSString *)viewActionName { /* this is overridden in the mail based contacts UI to redirect to tb.edit */ return @""; } - (NSString *)editActionName { /* this is overridden in the mail based contacts UI to redirect to tb.edit */ return @"edit"; } - (id)saveAction { NSException *ex; NSString *recstr, *uri; id record; if (![self isWriteableClientObject]) { return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ reason:@"method cannot be invoked on " @"the specified object"]; } if ((record = [self contentString]) == nil) { [self setErrorText:@"Missing object content!"]; // localize return self; } record = [[[record propertyList] mutableCopy] autorelease]; if (record == nil) { [self setErrorText:@"Invalid property list data ..."]; // localize return self; } [self saveValuesIntoRecord:record]; // TODO: directly hacking the content, hm, not so nice or reasonable? recstr = [record description]; // make plist ex = [[self clientObject] saveContentString:recstr]; if (ex != nil) { [self setErrorText:[ex reason]]; return self; } uri = ([(uri = [self viewActionName]) length] > 0) ? [self viewActionName] : @".."; uri = [self _completeURIForMethod:uri]; return [self redirectToLocation:uri]; } - (id)testAction { [self logWithFormat:@"test ..."]; return self; } - (id)newAction { // TODO: this is almost a DUP of UIxAppointmentEditor /* This method creates a unique ID and redirects to the "edit" method on the new ID. It is actually a folder method and should be defined on the folder. Note: 'clientObject' is the SOGoAppointmentFolder! Update: remember that there are group folders as well. */ NSString *uri, *objectId, *nextMethod; objectId = [NSClassFromString(@"SOGoContactFolder") globallyUniqueObjectId]; if ([objectId length] == 0) { return [NSException exceptionWithHTTPStatus:500 /* Internal Error */ reason:@"could not create a unique ID"]; } nextMethod = [NSString stringWithFormat:@"../%@/%@", objectId, [self editActionName]]; uri = [self _completeURIForMethod:nextMethod]; return [self redirectToLocation:uri]; } @end /* UIxContactEditorBase */