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