]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Contacts/UIxContactEditorBase.m
6e67a38a6a243f53ea8b3a1fc67a991fd8dcd38b
[scalable-opengroupware.org] / SOGo / UI / Contacts / UIxContactEditorBase.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "UIxContactEditorBase.h"
23 #include <Contacts/SOGoContactObject.h>
24 #include <Contacts/SOGoContactFolder.h>
25 #include "common.h"
26
27 @implementation UIxContactEditorBase
28
29 - (id)init {
30   if ((self = [super init])) {
31     self->snapshot = [[NSMutableDictionary alloc] initWithCapacity:16];
32   }
33   return self;
34 }
35
36 - (void)dealloc {
37   [self->snapshot      release];
38   [self->errorText     release];
39   [self->contentString release];
40   [super dealloc];
41 }
42
43 /* accessors */
44
45 - (void)setContentString:(NSString *)_cstr {
46   ASSIGNCOPY(self->contentString, _cstr);
47 }
48 - (NSString *)contentString {
49   return self->contentString;
50 }
51
52 - (NSString *)contentStringTemplate {
53   return @"{}"; /* empty property list */
54 }
55
56 - (NSMutableDictionary *)snapshot {
57   return self->snapshot;
58 }
59
60 - (void)setErrorText:(NSString *)_txt {
61   ASSIGNCOPY(self->errorText, _txt);
62 }
63 - (NSString *)errorText {
64   return self->errorText;
65 }
66 - (BOOL)hasErrorText {
67   return [self->errorText length] > 0 ? YES : NO;
68 }
69
70 /* load/store content format */
71
72 - (void)loadValuesFromContentString:(NSString *)_s {
73   NSDictionary *plist;
74   
75   if ((plist = [_s propertyList]) == nil) {
76     [self errorWithFormat:@"could not parse content string!"];
77     return;
78   }
79   
80   [self->snapshot removeAllObjects];
81   [self->snapshot addEntriesFromDictionary:plist];
82 }
83
84 - (void)_fixupSnapshot {
85   // TODO: perform sanity checking, eg build CN on demand
86   NSString *cn, *gn, *sn;
87   
88   cn = [self->snapshot objectForKey:@"cn"];
89   gn = [self->snapshot objectForKey:@"givenName"];
90   sn = [self->snapshot objectForKey:@"sn"];
91   
92   if (![sn isNotNull] || [sn length] == 0)
93     sn = nil;
94   if (![cn isNotNull] || [cn length] == 0)
95     cn = nil;
96   
97   if (sn == nil) {
98     if (cn == nil)
99       sn = @"[noname]";
100     else {
101       // TODO: need a better name parser here
102       NSRange r;
103       
104       r = [cn rangeOfString:@" "];
105       sn = (r.length > 0)
106         ? [cn substringFromIndex:(r.location + r.length)]
107         : cn;
108     }
109     [self->snapshot setObject:sn forKey:@"sn"];
110   }
111   
112   if (sn == nil && gn == nil)
113     cn = @"[noname]";
114   else if (sn == nil)
115     cn = gn;
116   else if (gn == nil)
117     cn = sn;
118   else
119     cn = [[gn stringByAppendingString:@" "] stringByAppendingString:sn];
120   [self->snapshot setObject:cn forKey:@"cn"];
121 }
122
123 - (void)saveValuesIntoRecord:(NSMutableDictionary *)_record {
124   [self _fixupSnapshot];
125   [_record addEntriesFromDictionary:[self snapshot]];
126 }
127
128 /* JavaScript */
129
130 - (NSString *)jsCopyContactCode {
131   static NSString *jsCode = \
132     @"function unescapeCallbackParameter(s) {\n"
133     @"  if(!s || s.length == 0)\n"
134     @"    return s;\n"
135     @"  s = s.replace(/&apos;/g, \"'\");\n"
136     @"  s = s.replace(/&quot;/g, '\"');\n"
137     @"  return s;\n"
138     @"}\n"
139     @"\n"
140     @"function copyContact()"
141     @"{\n"
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"
154     @"  var e;\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"
175     @"}\n";
176   return jsCode;
177 }
178
179 /* helper */
180
181 - (NSString *)_completeURIForMethod:(NSString *)_method {
182   // TODO: this is a DUP of UIxAppointmentEditor
183   NSString *uri;
184   NSRange r;
185     
186   uri = [[[self context] request] uri];
187     
188   /* first: identify query parameters */
189   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
190   if (r.length > 0)
191     uri = [uri substringToIndex:r.location];
192     
193   /* next: append trailing slash */
194   if (![uri hasSuffix:@"/"])
195     uri = [uri stringByAppendingString:@"/"];
196   
197   /* next: append method */
198   uri = [uri stringByAppendingString:_method];
199     
200   /* next: append query parameters */
201   return [self completeHrefForMethod:uri];
202 }
203
204 /* actions */
205
206 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
207   return YES;
208 }
209
210 - (id)defaultAction {
211   // TODO: very similiar to apt-editor (apt editor would need to use std names
212   NSString *c;
213   
214   /* load iCalendar file */
215   
216   c = [[self clientObject] contentAsString];
217   if ([c length] == 0) /* a new contact */
218     c = [self contentStringTemplate];
219   
220   [self setContentString:c];
221   [self loadValuesFromContentString:c];
222   
223   return self;
224 }
225
226 - (BOOL)isWriteableClientObject {
227   return [[self clientObject] 
228                 respondsToSelector:@selector(saveContentString:)];
229 }
230
231 - (NSString *)viewActionName {
232   /* this is overridden in the mail based contacts UI to redirect to tb.edit */
233   return @"";
234 }
235 - (NSString *)editActionName {
236   /* this is overridden in the mail based contacts UI to redirect to tb.edit */
237   return @"edit";
238 }
239
240 - (id)saveAction {
241   NSException *ex;
242   NSString *recstr, *uri;
243   id record;
244   
245   if (![self isWriteableClientObject]) {
246     return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
247                         reason:@"method cannot be invoked on "
248                                @"the specified object"];
249   }
250   
251   if ((record = [self contentString]) == nil) {
252     [self setErrorText:@"Missing object content!"]; // localize
253     return self;
254   }
255   record = [[[record propertyList] mutableCopy] autorelease];
256   if (record == nil) {
257     [self setErrorText:@"Invalid property list data ..."]; // localize
258     return self;
259   }
260   
261   [self saveValuesIntoRecord:record];
262   
263   // TODO: directly hacking the content, hm, not so nice or reasonable?
264   recstr = [record description]; // make plist
265   ex = [[self clientObject] saveContentString:recstr];
266   if (ex != nil) {
267     [self setErrorText:[ex reason]];
268     return self;
269   }
270   
271   uri = ([(uri = [self viewActionName]) length] > 0)
272     ? [self viewActionName] : @"..";
273   uri = [self _completeURIForMethod:uri];
274   return [self redirectToLocation:uri];
275 }
276
277 - (id)testAction {
278   [self logWithFormat:@"test ..."];
279   return self;
280 }
281
282 - (id)newAction {
283   // TODO: this is almost a DUP of UIxAppointmentEditor
284   /*
285     This method creates a unique ID and redirects to the "edit" method on the
286     new ID.
287     It is actually a folder method and should be defined on the folder.
288     
289     Note: 'clientObject' is the SOGoAppointmentFolder!
290           Update: remember that there are group folders as well.
291   */
292   NSString *uri, *objectId, *nextMethod;
293   
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"];
298   }
299   
300   nextMethod = [NSString stringWithFormat:@"../%@/%@", 
301                            objectId, [self editActionName]];
302   uri = [self _completeURIForMethod:nextMethod];
303   return [self redirectToLocation:uri];
304 }
305
306 @end /* UIxContactEditorBase */