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