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