2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "UIxContactEditorBase.h"
23 #include <Contacts/SOGoContactObject.h>
24 #include <Contacts/SOGoContactFolder.h>
27 @implementation UIxContactEditorBase
30 if ((self = [super init])) {
31 self->snapshot = [[NSMutableDictionary alloc] initWithCapacity:16];
37 [self->snapshot release];
38 [self->errorText release];
39 [self->contentString release];
45 - (void)setContentString:(NSString *)_cstr {
46 ASSIGNCOPY(self->contentString, _cstr);
48 - (NSString *)contentString {
49 return self->contentString;
52 - (NSString *)contentStringTemplate {
53 return @"{}"; /* empty property list */
56 - (NSMutableDictionary *)snapshot {
57 return self->snapshot;
60 - (void)setErrorText:(NSString *)_txt {
61 ASSIGNCOPY(self->errorText, _txt);
63 - (NSString *)errorText {
64 return self->errorText;
66 - (BOOL)hasErrorText {
67 return [self->errorText length] > 0 ? YES : NO;
70 /* load/store content format */
72 - (void)loadValuesFromContentString:(NSString *)_s {
75 if ((plist = [_s propertyList]) == nil) {
76 [self errorWithFormat:@"could not parse content string!"];
80 [self->snapshot removeAllObjects];
81 [self->snapshot addEntriesFromDictionary:plist];
84 - (void)_fixupSnapshot {
85 // TODO: perform sanity checking, eg build CN on demand
86 NSString *cn, *gn, *sn;
88 cn = [self->snapshot objectForKey:@"cn"];
89 gn = [self->snapshot objectForKey:@"givenName"];
90 sn = [self->snapshot objectForKey:@"sn"];
92 if (![sn isNotNull] || [sn length] == 0)
94 if (![cn isNotNull] || [cn length] == 0)
101 // TODO: need a better name parser here
104 r = [cn rangeOfString:@" "];
106 ? [cn substringFromIndex:(r.location + r.length)]
109 [self->snapshot setObject:sn forKey:@"sn"];
112 if (sn == nil && gn == nil)
119 cn = [[gn stringByAppendingString:@" "] stringByAppendingString:sn];
120 [self->snapshot setObject:cn forKey:@"cn"];
123 - (void)saveValuesIntoRecord:(NSMutableDictionary *)_record {
124 [self _fixupSnapshot];
125 [_record addEntriesFromDictionary:[self snapshot]];
130 - (NSString *)jsCopyContactCode {
131 static NSString *jsCode = \
132 @"function unescapeCallbackParameter(s) {\n"
133 @" if(!s || s.length == 0)\n"
135 @" s = s.replace(/'/g, \"'\");\n"
136 @" s = s.replace(/"/g, '\"');\n"
140 @"function copyContact()"
142 @" var type = arguments[0]; \n"
143 @" var email = arguments[1]; \n"
144 @" var uid = arguments[2]; \n"
145 @" var sn = arguments[3]; \n"
146 @" var givenName = arguments[4]; \n"
147 @" var telephoneNumber = arguments[5]; \n"
148 @" var facsimileTelephoneNumber = arguments[6]; \n"
149 @" var mobile = arguments[7]; \n"
150 @" var postalAddress = arguments[8]; \n"
151 @" var homePostalAddress = arguments[9]; \n"
152 @" var departmentNumber = arguments[10]; \n"
153 @" var l = arguments[11]; \n"
155 @" e = document.getElementById('email');\n"
156 @" e.setAttribute('value', email);\n"
157 @" e = document.getElementById('sn');\n"
158 @" e.setAttribute('value', unescapeCallbackParameter(sn));\n"
159 @" e = document.getElementById('givenName');\n"
160 @" e.setAttribute('value', unescapeCallbackParameter(givenName));\n"
161 @" e = document.getElementById('telephoneNumber');\n"
162 @" e.setAttribute('value', telephoneNumber);\n"
163 @" e = document.getElementById('facsimileTelephoneNumber');\n"
164 @" e.setAttribute('value', facsimileTelephoneNumber);\n"
165 @" e = document.getElementById('mobile');\n"
166 @" e.setAttribute('value', mobile);\n"
167 @" e = document.getElementById('postalAddress');\n"
168 @" e.setAttribute('value', unescapeCallbackParameter(postalAddress));\n"
169 @" e = document.getElementById('homePostalAddress');\n"
170 @" e.setAttribute('value', unescapeCallbackParameter(homePostalAddress));\n"
171 @" e = document.getElementById('departmentNumber');\n"
172 @" e.setAttribute('value', unescapeCallbackParameter(departmentNumber));\n"
173 @" e = document.getElementById('l');\n"
174 @" e.setAttribute('value', unescapeCallbackParameter(l));\n"
181 - (NSString *)_completeURIForMethod:(NSString *)_method {
182 // TODO: this is a DUP of UIxAppointmentEditor
186 uri = [[[self context] request] uri];
188 /* first: identify query parameters */
189 r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
191 uri = [uri substringToIndex:r.location];
193 /* next: append trailing slash */
194 if (![uri hasSuffix:@"/"])
195 uri = [uri stringByAppendingString:@"/"];
197 /* next: append method */
198 uri = [uri stringByAppendingString:_method];
200 /* next: append query parameters */
201 return [self completeHrefForMethod:uri];
206 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
210 - (id)defaultAction {
211 // TODO: very similiar to apt-editor (apt editor would need to use std names
214 /* load iCalendar file */
216 c = [[self clientObject] contentAsString];
217 if ([c length] == 0) /* a new contact */
218 c = [self contentStringTemplate];
220 [self setContentString:c];
221 [self loadValuesFromContentString:c];
226 - (BOOL)isWriteableClientObject {
227 return [[self clientObject]
228 respondsToSelector:@selector(saveContentString:)];
231 - (NSString *)viewActionName {
232 /* this is overridden in the mail based contacts UI to redirect to tb.edit */
235 - (NSString *)editActionName {
236 /* this is overridden in the mail based contacts UI to redirect to tb.edit */
242 NSString *recstr, *uri;
245 if (![self isWriteableClientObject]) {
246 return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
247 reason:@"method cannot be invoked on "
248 @"the specified object"];
251 if ((record = [self contentString]) == nil) {
252 [self setErrorText:@"Missing object content!"]; // localize
255 record = [[[record propertyList] mutableCopy] autorelease];
257 [self setErrorText:@"Invalid property list data ..."]; // localize
261 [self saveValuesIntoRecord:record];
263 // TODO: directly hacking the content, hm, not so nice or reasonable?
264 recstr = [record description]; // make plist
265 ex = [[self clientObject] saveContentString:recstr];
267 [self setErrorText:[ex reason]];
271 uri = ([(uri = [self viewActionName]) length] > 0)
272 ? [self viewActionName] : @"..";
273 uri = [self _completeURIForMethod:uri];
274 return [self redirectToLocation:uri];
278 [self logWithFormat:@"test ..."];
283 // TODO: this is almost a DUP of UIxAppointmentEditor
285 This method creates a unique ID and redirects to the "edit" method on the
287 It is actually a folder method and should be defined on the folder.
289 Note: 'clientObject' is the SOGoAppointmentFolder!
290 Update: remember that there are group folders as well.
292 NSString *uri, *objectId, *nextMethod;
294 objectId = [NSClassFromString(@"SOGoContactFolder") globallyUniqueObjectId];
295 if ([objectId length] == 0) {
296 return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
297 reason:@"could not create a unique ID"];
300 nextMethod = [NSString stringWithFormat:@"../%@/%@",
301 objectId, [self editActionName]];
302 uri = [self _completeURIForMethod:nextMethod];
303 return [self redirectToLocation:uri];
306 @end /* UIxContactEditorBase */