]> err.no Git - scalable-opengroupware.org/blob - UI/Contacts/UIxContactEditor.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1015 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / Contacts / UIxContactEditor.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 #import <NGCards/NGVCard.h>
23 #import <NGCards/NSArray+NGCards.h>
24
25 #import <NGObjWeb/SoObject.h>
26
27 #import <Contacts/SOGoContactObject.h>
28 #import <Contacts/SOGoContactFolder.h>
29 #import "common.h"
30
31 #import "UIxContactEditor.h"
32
33 @implementation UIxContactEditor
34
35 - (id) init
36 {
37   if ((self = [super init]))
38     {
39       snapshot = [[NSMutableDictionary alloc] initWithCapacity: 16];
40       preferredEmail = nil;
41     }
42
43   return self;
44 }
45
46 - (void) dealloc
47 {
48   [snapshot      release];
49   [errorText     release];
50   [super dealloc];
51 }
52
53 /* accessors */
54
55 - (void) setErrorText: (NSString *) _txt
56 {
57   ASSIGNCOPY(errorText, _txt);
58 }
59
60 - (NSString *) errorText
61 {
62   return errorText;
63 }
64
65 - (BOOL) hasErrorText
66 {
67   return [errorText length] > 0 ? YES : NO;
68 }
69
70 /* load/store content format */
71
72 - (void)_fixupSnapshot {
73   // TODO: perform sanity checking, eg build CN on demand
74   NSString *cn, *gn, *sn;
75   
76   cn = [snapshot objectForKey:@"cn"];
77   gn = [snapshot objectForKey:@"givenName"];
78   sn = [snapshot objectForKey:@"sn"];
79   
80   if (![sn isNotNull] || [sn length] == 0)
81     sn = nil;
82   if (![cn isNotNull] || [cn length] == 0)
83     cn = nil;
84   
85   if (sn == nil) {
86     if (cn == nil)
87       sn = @"[noname]";
88     else {
89       // TODO: need a better name parser here
90       NSRange r;
91       
92       r = [cn rangeOfString:@" "];
93       sn = (r.length > 0)
94         ? [cn substringFromIndex:(r.location + r.length)]
95         : cn;
96     }
97     [snapshot setObject:sn forKey:@"sn"];
98   }
99   if (sn == nil && gn == nil)
100     cn = @"[noname]";
101   else if (sn == nil)
102     cn = gn;
103   else if (gn == nil)
104     cn = sn;
105   else
106     cn = [[gn stringByAppendingString:@" "] stringByAppendingString:sn];
107   [snapshot setObject:cn forKey:@"cn"];
108 }
109
110 /* helper */
111
112 - (NSString *)_completeURIForMethod:(NSString *)_method {
113   // TODO: this is a DUP of UIxAppointmentEditor
114   NSString *uri;
115   NSRange r;
116     
117   uri = [[[self context] request] uri];
118     
119   /* first: identify query parameters */
120   r = [uri rangeOfString:@"?" options:NSBackwardsSearch];
121   if (r.length > 0)
122     uri = [uri substringToIndex:r.location];
123     
124   /* next: append trailing slash */
125   if (![uri hasSuffix:@"/"])
126     uri = [uri stringByAppendingString:@"/"];
127   
128   /* next: append method */
129   uri = [uri stringByAppendingString:_method];
130     
131   /* next: append query parameters */
132   return [self completeHrefForMethod:uri];
133 }
134
135 /* actions */
136
137 - (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq
138                            inContext: (WOContext*) _c
139 {
140   return YES;
141 }
142
143 - (void) _setSnapshotValue: (NSString *) key
144                         to: (NSString *) aValue
145 {
146   if (!aValue)
147     aValue = @"";
148
149   [snapshot setObject: aValue forKey: key];
150 }
151
152 - (NSMutableDictionary *) snapshot
153 {
154   return snapshot;
155 }
156
157 - (NSString *) _simpleValueForType: (NSString *) aType
158                            inArray: (NSArray *) anArray
159 {
160   NSArray *elements;
161   NSString *value;
162
163   elements = [anArray cardElementsWithAttribute: @"type"
164                       havingValue: aType];
165   if ([elements count] > 0)
166     value = [[elements objectAtIndex: 0] value: 0];
167   else
168     value = nil;
169
170   return value;
171 }
172
173 - (void) _setupEmailFields
174 {
175   NSArray *elements;
176   NSString *workMail, *homeMail, *prefMail, *potential;
177   unsigned int max;
178
179   elements = [card childrenWithTag: @"email"];
180   max = [elements count];
181   workMail = [self _simpleValueForType: @"work"
182                    inArray: elements];
183   homeMail = [self _simpleValueForType: @"home"
184                    inArray: elements];
185   prefMail = [self _simpleValueForType: @"pref"
186                    inArray: elements];
187
188   if (max > 0)
189     {
190       potential = [[elements objectAtIndex: 0] value: 0];
191       if (!workMail)
192         {
193           if (homeMail && homeMail == potential && max > 1)
194             workMail = [[elements objectAtIndex: 1] value: 0];
195           else
196             workMail = potential;
197         }
198       if (!homeMail && max > 1)
199         {
200           if (workMail && workMail == potential)
201             homeMail = [[elements objectAtIndex: 1] value: 0];
202           else
203             homeMail = potential;
204         }
205
206       if (prefMail)
207         {
208           if (prefMail == workMail)
209             preferredEmail = @"work";
210           else if (prefMail == homeMail)
211             preferredEmail = @"home";
212         }
213     }
214
215   [self _setSnapshotValue: @"workMail" to: workMail];
216   [self _setSnapshotValue: @"homeMail" to: homeMail];
217 }
218
219 - (void) _setupOrgFields
220 {
221   NSArray *org, *orgServices;
222   NSRange aRange;
223   unsigned int max;
224
225   org = [card org];
226   max = [org count];
227   if (max > 0)
228     {
229       [self _setSnapshotValue: @"workCompany" to: [org objectAtIndex: 0]];
230       if (max > 1)
231         {
232           aRange = NSMakeRange (1, max - 1);
233           orgServices = [org subarrayWithRange: aRange];
234           [self _setSnapshotValue: @"workService"
235                 to: [orgServices componentsJoinedByString: @", "]];
236         }
237     }
238 }
239
240 - (NSString *) preferredEmail
241 {
242   return preferredEmail;
243 }
244
245 - (void) setPreferredEmail: (NSString *) aString
246 {
247   preferredEmail = aString;
248 }
249
250 - (void) _retrieveQueryParameter: (NSString *) queryKey
251                intoSnapshotValue: (NSString *) snapshotKey
252 {
253   NSString *queryValue;
254
255   queryValue = [self queryParameterForKey: queryKey];
256   if (queryValue && [queryValue length] > 0)
257     [self _setSnapshotValue: snapshotKey to: queryValue];
258 }
259
260 - (void) initSnapshot
261 {
262   NSArray *n, *elements;
263   CardElement *element;
264   unsigned int max;
265
266   n = [card n];
267   if (n)
268     {
269       max = [n count];
270       if (max > 0)
271         {
272           [self _setSnapshotValue: @"sn" to: [n objectAtIndex: 0]];
273           if (max > 1)
274             [self _setSnapshotValue: @"givenName" to: [n objectAtIndex: 1]];
275         }
276     }
277   [self _setSnapshotValue: @"fn" to: [card fn]];
278   [self _setSnapshotValue: @"nickname" to: [card nickname]];
279
280   elements = [card childrenWithTag: @"tel"];
281   [self _setSnapshotValue: @"telephoneNumber"
282         to: [self _simpleValueForType: @"work" inArray: elements]];
283   [self _setSnapshotValue: @"homeTelephoneNumber"
284         to: [self _simpleValueForType: @"home" inArray: elements]];
285   [self _setSnapshotValue: @"mobile"
286         to: [self _simpleValueForType: @"cell" inArray: elements]];
287   [self _setSnapshotValue: @"facsimileTelephoneNumber"
288         to: [self _simpleValueForType: @"fax" inArray: elements]];
289   [self _setSnapshotValue: @"pager"
290         to: [self _simpleValueForType: @"pager" inArray: elements]];
291
292   [self _setupEmailFields];
293
294   elements = [card childrenWithTag: @"adr"
295                    andAttribute: @"type" havingValue: @"work"];
296   if (elements && [elements count] > 0)
297     {
298       element = [elements objectAtIndex: 0];
299       [self _setSnapshotValue: @"workStreetAddress"
300             to: [element value: 2]];
301       [self _setSnapshotValue: @"workCity"
302             to: [element value: 3]];
303       [self _setSnapshotValue: @"workState"
304             to: [element value: 4]];
305       [self _setSnapshotValue: @"workPostalCode"
306             to: [element value: 5]];
307       [self _setSnapshotValue: @"workCountry"
308             to: [element value: 6]];
309     }
310
311   elements = [card childrenWithTag: @"adr"
312                    andAttribute: @"type" havingValue: @"home"];
313   if (elements && [elements count] > 0)
314     {
315       element = [elements objectAtIndex: 0];
316       [self _setSnapshotValue: @"homeStreetAddress"
317             to: [element value: 2]];
318       [self _setSnapshotValue: @"homeCity"
319             to: [element value: 3]];
320       [self _setSnapshotValue: @"homeState"
321             to: [element value: 4]];
322       [self _setSnapshotValue: @"homePostalCode"
323             to: [element value: 5]];
324       [self _setSnapshotValue: @"homeCountry"
325             to: [element value: 6]];
326     }
327
328   elements = [card childrenWithTag: @"url"];
329   [self _setSnapshotValue: @"workURL"
330         to: [self _simpleValueForType: @"work" inArray: elements]];
331   [self _setSnapshotValue: @"homeURL"
332         to: [self _simpleValueForType: @"home" inArray: elements]];
333   [self _setSnapshotValue: @"calFBURL"
334         to: [[card uniqueChildWithTag: @"FBURL"] value: 0]];
335
336   [self _setSnapshotValue: @"title" to: [card title]];
337   [self _setupOrgFields];
338
339   [self _setSnapshotValue: @"bday" to: [card bday]];
340   [self _setSnapshotValue: @"tz" to: [card tz]];
341   [self _setSnapshotValue: @"note" to: [card note]];
342
343   [self _retrieveQueryParameter: @"contactEmail"
344         intoSnapshotValue: @"workMail"];
345   [self _retrieveQueryParameter: @"contactFN"
346         intoSnapshotValue: @"fn"];
347 }
348
349 - (id <WOActionResults>) defaultAction
350 {
351   card = [[self clientObject] vCard];
352   if (card)
353     [self initSnapshot];
354   else
355     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
356                         reason:@"could not open contact"];
357
358   return self;
359 }
360
361 - (NSString *) viewActionName
362 {
363   /* this is overridden in the mail based contacts UI to redirect to tb.edit */
364   return @"";
365 }
366
367 - (NSString *) editActionName
368 {
369   /* this is overridden in the mail based contacts UI to redirect to tb.edit */
370   return @"edit";
371 }
372
373 - (CardElement *) _elementWithTag: (NSString *) tag
374                            ofType: (NSString *) type
375 {
376   NSArray *elements;
377   CardElement *element;
378
379   elements = [card childrenWithTag: tag
380                    andAttribute: @"type" havingValue: type];
381   if ([elements count] > 0)
382     element = [elements objectAtIndex: 0];
383   else
384     {
385       element = [CardElement new];
386       [element autorelease];
387       [element setTag: tag];
388       [element addType: type];
389       [card addChild: element];
390     }
391
392   return element;
393 }
394
395 - (void) _savePhoneValues
396 {
397   CardElement *phone;
398
399   phone = [self _elementWithTag: @"tel" ofType: @"work"];
400   [phone setValue: 0 to: [snapshot objectForKey: @"telephoneNumber"]];
401   phone = [self _elementWithTag: @"tel" ofType: @"home"];
402   [phone setValue: 0 to: [snapshot objectForKey: @"homeTelephoneNumber"]];
403   phone = [self _elementWithTag: @"tel" ofType: @"cell"];
404   [phone setValue: 0 to: [snapshot objectForKey: @"mobile"]];
405   phone = [self _elementWithTag: @"tel" ofType: @"fax"];
406   [phone setValue: 0
407          to: [snapshot objectForKey: @"facsimileTelephoneNumber"]];
408   phone = [self _elementWithTag: @"tel" ofType: @"pager"];
409   [phone setValue: 0
410          to: [snapshot objectForKey: @"pager"]];
411 }
412
413 - (void) _saveEmails
414 {
415   CardElement *workMail, *homeMail;
416
417   workMail = [self _elementWithTag: @"email" ofType: @"work"];
418   [workMail setValue: 0 to: [snapshot objectForKey: @"workMail"]];
419   homeMail = [self _elementWithTag: @"email" ofType: @"home"];
420   [homeMail setValue: 0 to: [snapshot objectForKey: @"homeMail"]];
421   if (preferredEmail)
422     {
423       if ([preferredEmail isEqualToString: @"work"])
424         [card setPreferred: workMail];
425       else
426         [card setPreferred: homeMail];
427     }
428 }
429
430 - (void) _saveSnapshot
431 {
432   CardElement *element;
433
434   [card setNWithFamily: [snapshot objectForKey: @"sn"]
435         given: [snapshot objectForKey: @"givenName"]
436         additional: nil
437         prefixes: nil
438         suffixes: nil];
439   [card setNickname: [snapshot objectForKey: @"nickname"]];
440   [card setFn: [snapshot objectForKey: @"fn"]];
441   [card setTitle: [snapshot objectForKey: @"title"]];
442   [card setBday: [snapshot objectForKey: @"bday"]];
443   [card setNote: [snapshot objectForKey: @"note"]];
444   [card setTz: [snapshot objectForKey: @"tz"]];
445
446   element = [self _elementWithTag: @"adr" ofType: @"home"];
447   [element setValue: 2 to: [snapshot objectForKey: @"homeStreetAddress"]];
448   [element setValue: 3 to: [snapshot objectForKey: @"homeCity"]];
449   [element setValue: 4 to: [snapshot objectForKey: @"homeState"]];
450   [element setValue: 5 to: [snapshot objectForKey: @"homePostalCode"]];
451   [element setValue: 6 to: [snapshot objectForKey: @"homeCountry"]];
452
453   element = [self _elementWithTag: @"adr" ofType: @"work"];
454   [element setValue: 2 to: [snapshot objectForKey: @"workStreetAddress"]];
455   [element setValue: 3 to: [snapshot objectForKey: @"workCity"]];
456   [element setValue: 4 to: [snapshot objectForKey: @"workState"]];
457   [element setValue: 5 to: [snapshot objectForKey: @"workPostalCode"]];
458   [element setValue: 6 to: [snapshot objectForKey: @"workCountry"]];
459
460   element = [CardElement simpleElementWithTag: @"fburl"
461                          value: [snapshot objectForKey: @"calFBURL"]];
462   [card setUniqueChild: element];
463
464   [self _savePhoneValues];
465   [self _saveEmails];
466 }
467
468 - (id <WOActionResults>) saveAction
469 {
470   id <SOGoContactObject> contact;
471   id result;
472   NSString *jsRefreshMethod;
473
474   contact = [self clientObject];
475   card = [contact vCard];
476   if (card)
477     {
478       [self _saveSnapshot];
479       [contact save];
480
481       if ([[[[self context] request] formValueForKey: @"nojs"] intValue])
482         result = [self redirectToLocation: [self applicationPath]];
483       else
484         {
485           jsRefreshMethod
486             = [NSString stringWithFormat: @"refreshContacts(\"%@\")",
487                         [contact nameInContainer]];
488           result = [self jsCloseWithRefreshMethod: jsRefreshMethod];
489         }
490     }
491   else
492     result = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
493                           reason: @"method cannot be invoked on "
494                           @"the specified object"];
495
496   return result;
497 }
498
499 - (id) writeAction
500 {
501   NSString *email, *url;
502
503   card = [[self clientObject] vCard];
504   [self initSnapshot];
505   if ([preferredEmail isEqualToString: @"home"])
506     email = [snapshot objectForKey: @"homeMail"];
507   else
508     email = [snapshot objectForKey: @"workMail"];
509
510   if (email)
511     url = [NSString stringWithFormat: @"Mail/compose?mailto=%@", email];
512   else
513     url = @"Mail/compose";
514
515   return
516     [self redirectToLocation: [self relativePathToUserFolderSubPath: url]];
517 }
518
519 - (id)newAction {
520   // TODO: this is almost a DUP of UIxAppointmentEditor
521   /*
522     This method creates a unique ID and redirects to the "edit" method on the
523     new ID.
524     It is actually a folder method and should be defined on the folder.
525     
526     Note: 'clientObject' is the SOGoAppointmentFolder!
527           Update: remember that there are group folders as well.
528   */
529   NSString *uri, *objectId, *nextMethod;
530   id <SOGoContactFolder> co;
531
532   co = [self clientObject];
533   if ([[co class] respondsToSelector: @selector (globallyUniqueObjectId)])
534     objectId = [[[self clientObject] class] globallyUniqueObjectId];
535   else
536     objectId = nil;
537
538   if ([objectId length] == 0)
539     return [NSException exceptionWithHTTPStatus:500 /* Internal Error */
540                         reason:@"could not create a unique ID"];
541
542   nextMethod = [NSString stringWithFormat:@"../%@/%@", 
543                            objectId, [self editActionName]];
544   uri = [self _completeURIForMethod:nextMethod];
545   return [self redirectToLocation:uri];
546 }
547
548 @end /* UIxContactEditor */