"Fossies" - the Fresh Open Source Software Archive

Member "opengroupware-5.5rc3/Logic/LSAddress/LSVCardCompanyFormatter.m" (5 Dec 2015, 20873 Bytes) of package /linux/privat/opengroupware-5.5rc3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "LSVCardCompanyFormatter.m" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.5rc2_vs_5.5rc3.

    1 /*
    2   Copyright (C) 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 "LSVCardCompanyFormatter.h"
   23 #include "LSVCardAddressFormatter.h"
   24 #include "LSVCardLabelFormatter.h"
   25 #include "LSVCardNameFormatter.h"
   26 #include "NSString+VCard.h"
   27 #include "common.h"
   28 
   29 NSString *LSVUidPrefix = @"vcfuid://";
   30 
   31 @implementation LSVCardCompanyFormatter
   32 
   33 static NSString     *skyrixId          = nil;
   34 static NSDictionary *telephoneMapping  = nil;
   35 static NSDictionary *addressMapping    = nil;
   36 static BOOL         renderOGoPhoneType = NO;
   37 
   38 + (void)initialize {
   39   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   40   
   41   skyrixId = [ud stringForKey:@"skyrix_id"];
   42   skyrixId = [[NSString alloc] initWithFormat:@"skyrix://%@/%@/",
   43                      [[NSHost currentHost] name], skyrixId];
   44   
   45   addressMapping   = [[ud dictionaryForKey:@"LSVCard_AddressMapping"]   copy];
   46   telephoneMapping = [[ud dictionaryForKey:@"LSVCard_TelephoneMapping"] copy];
   47 }
   48 
   49 + (id)formatter {
   50   return [[[self alloc] init] autorelease];
   51 }
   52 
   53 /* generic */
   54 
   55 - (void)appendPreambleToString:(NSMutableString *)_ms {
   56   [_ms appendString:@"BEGIN:VCARD\r\n"];
   57   [_ms appendString:@"VERSION:3.0\r\n"];
   58   [_ms appendFormat:@"PRODID:-//OpenGroupware.org//LSAddress v%i.%i.%i\r\n",
   59          OGO_MAJOR_VERSION, OGO_MINOR_VERSION, OGO_SUBMINOR_VERSION];
   60   [_ms appendString:@"PROFILE:vCard\r\n"];
   61 }
   62 - (void)appendPostambleToString:(NSMutableString *)_ms {
   63   [_ms appendString:@"END:VCARD\r\n"];
   64 }
   65 
   66 /* vCard formatting */
   67 
   68 - (void)_appendTextValue:(NSString *)_str toVCard:(NSMutableString *)_vCard {
   69   [_vCard appendString:[_str stringByEscapingUnsafeVCardCharacters]];
   70 }
   71 
   72 - (void)_appendName:(NSString *)_name andValue:(id)_value
   73   toVCard:(NSMutableString *)_vCard
   74 {
   75   [_vCard appendString:_name];
   76   [_vCard appendString:@":"];
   77   if ([_value isKindOfClass:[NSArray class]]) {
   78     NSUInteger cnt, i;
   79     
   80     for (i = 0, cnt = [_value count]; i < cnt; i++)
   81       [self _appendTextValue:[_value objectAtIndex:i] toVCard:_vCard];
   82   }
   83   else if ([_value isKindOfClass:[NSString class]])
   84     [self _appendTextValue:_value toVCard:_vCard];
   85   else 
   86     [self _appendTextValue:[_value description] toVCard:_vCard];
   87   [_vCard appendString:@"\r\n"];
   88 }
   89 
   90 /* common company stuff */
   91 
   92 - (void)_appendIdentifier:(id)_contact toVCard:(NSMutableString *)_vCard {
   93   // UID, SOURCE
   94   NSString *sourceUrl;
   95   id tmp;
   96   
   97   if ([(sourceUrl = [_contact valueForKey:@"sourceUrl"]) isNotNull]) {
   98     NSRange r;
   99     
  100     r = [sourceUrl rangeOfString:@"://"];
  101     if (r.length == 0) {
  102       /* not a URL, use it as ID, prefix with UID prefix */
  103       tmp = [LSVUidPrefix stringByAppendingString:sourceUrl];
  104       [self _appendName:@"UID"    andValue:sourceUrl toVCard:_vCard];
  105       [self _appendName:@"SOURCE" andValue:tmp       toVCard:_vCard];
  106     }
  107     else {
  108       /* a URL, check for UID prefix, otherwise reuse the URL */
  109       tmp = [sourceUrl hasPrefix:LSVUidPrefix]
  110     ? [sourceUrl substringFromIndex:[LSVUidPrefix length]]
  111     : sourceUrl;
  112       [self _appendName:@"UID"    andValue:tmp       toVCard:_vCard];
  113       [self _appendName:@"SOURCE" andValue:sourceUrl toVCard:_vCard];
  114     }
  115   }
  116   else {
  117     /* add internal OGo URL as UID _and_ SOURCE */
  118     sourceUrl = [skyrixId stringByAppendingString:
  119                 [[_contact valueForKey:@"companyId"] stringValue]];
  120     [self _appendName:@"UID"    andValue:sourceUrl toVCard:_vCard];
  121     [self _appendName:@"SOURCE" andValue:sourceUrl toVCard:_vCard];
  122   }
  123 }
  124 
  125 - (void)_appendContactData:(id)_contact toVCard:(NSMutableString *)_vCard {
  126   // COMMENT, CATEGORIES, CLASS, URL
  127   id tmp;
  128   
  129   tmp = [[NSString alloc] initWithFormat:
  130                     @"vCard for contact with id %@ (v%@)",
  131                     [_contact valueForKey:@"companyId"],
  132                     [_contact valueForKey:@"objectVersion"]];
  133   [self _appendName:@"NAME" andValue:tmp toVCard:_vCard];
  134   [tmp release]; tmp = nil;
  135   
  136   /* COMMENT */
  137   tmp = [[_contact valueForKey:@"comment"] valueForKey:@"comment"];
  138   if ([tmp isNotNull])
  139     [self _appendName:@"NOTE" andValue:tmp toVCard:_vCard];
  140 
  141   /* CATEGORIES */
  142   if ([(tmp = [_contact valueForKey:@"keywords"]) isNotNull]) {
  143     tmp = [tmp componentsSeparatedByString:@","];
  144     [self _appendName:@"CATEGORIES" andValue:tmp toVCard:_vCard];
  145   }
  146 
  147   /* CLASS */
  148   
  149   if ([(tmp = [_contact valueForKey:@"sensitivity"]) isNotNull]) {
  150     NSString *v;
  151     
  152     if ([tmp intValue] == 2 /* private*/ || [tmp intValue] == 1 /* Personal */)
  153       v = @"PRIVATE";
  154     else if ([tmp intValue] == 3)
  155       v = @"CONFIDENTIAL";
  156     else if ([tmp intValue] == 0)
  157       v = @"PUBLIC";
  158     else {
  159       [self errorWithFormat:@"unknown sensitivity, using private: %@", tmp];
  160       v = @"PRIVATE";
  161     }
  162     [self _appendName:@"CLASS" andValue:v toVCard:_vCard];
  163   }
  164 
  165   /* URL */
  166   if ([(tmp = [_contact valueForKey:@"url"]) isNotEmpty])
  167     [self _appendName:@"URL" andValue:tmp toVCard:_vCard];
  168   
  169   /* X-EVOLUTION-FILE-AS */
  170   if ([(tmp = [_contact valueForKey:@"fileas"]) isNotEmpty])
  171     [self _appendName:@"X-EVOLUTION-FILE-AS" andValue:tmp toVCard:_vCard];
  172   
  173   /* X-EVOLUTION-MANAGER */
  174   if ([(tmp = [_contact valueForKey:@"bossName"]) isNotEmpty])
  175     [self _appendName:@"X-EVOLUTION-MANAGER" andValue:tmp toVCard:_vCard];
  176   
  177   /* X-EVOLUTION-ASSISTANT */
  178   if ([(tmp = [_contact valueForKey:@"assistantName"]) isNotEmpty])
  179     [self _appendName:@"X-EVOLUTION-ASSISTANT" andValue:tmp toVCard:_vCard];
  180   
  181   /* X-EVOLUTION-SPOUSE */
  182   if ([(tmp = [_contact valueForKey:@"partnerName"]) isNotEmpty])
  183     [self _appendName:@"X-EVOLUTION-SPOUSE" andValue:tmp toVCard:_vCard];
  184   
  185   /* ROLE */
  186   if ([(tmp = [_contact valueForKey:@"occupation"]) isNotEmpty])
  187     /* 'profession' in Evo UI */
  188     [self _appendName:@"ROLE" andValue:tmp toVCard:_vCard];
  189   
  190   /* X-AIM or X-ICQ or X-JABBER */
  191   if ([(tmp = [_contact valueForKey:@"imAddress"]) isNotEmpty]) {
  192     if (isdigit([tmp characterAtIndex:0]))
  193       [self _appendName:@"X-ICQ" andValue:tmp toVCard:_vCard];
  194     else if ([tmp rangeOfString:@"@"].length > 0)
  195       [self _appendName:@"X-JABBER" andValue:tmp toVCard:_vCard];
  196     else
  197       [self _appendName:@"X-AIM" andValue:tmp toVCard:_vCard];
  198   }
  199   
  200   /* X-EVOLUTION-ANNIVERSARY */
  201   if ([(tmp = [_contact valueForKey:@"anniversary"]) isNotNull]) {
  202     tmp = [[NSString alloc] initWithFormat:@"%04"PRIiPTR"-%02"PRIiPTR"-%02"PRIiPTR,
  203                 [tmp yearOfCommonEra], [tmp monthOfYear],
  204                 [tmp dayOfMonth]];
  205     [self _appendName:@"X-EVOLUTION-ANNIVERSARY" andValue:tmp 
  206       toVCard:_vCard];
  207     [tmp release];
  208   }
  209 }
  210 
  211 - (NSString *)typeFromVCardTypeHack:(NSString *)type {
  212   if (![type isNotNull])       return nil;
  213   if (![type hasPrefix:@"V:"]) return nil;
  214 
  215   /* a vCard specific type */
  216   type = [type substringFromIndex:2];
  217       
  218   // remove counter (eg V:1work, V:2work)
  219   if ([type isNotEmpty] && isdigit([type characterAtIndex:0]))
  220     type = [type substringFromIndex:1];
  221       
  222   if ([type hasSuffix:@"untyped"]) /* imported VCF had no ADR type */
  223     type = nil;
  224   return type;
  225 }
  226 
  227 - (void)_appendAddressData:(id)_contact toVCard:(NSMutableString *)_vCard {
  228   // ADR, LABEL
  229   NSArray    *addrs;
  230   NSUInteger i, cnt;
  231   
  232   if (![(addrs = [_contact valueForKey:@"addresses"]) isNotNull]) {
  233     [self warnWithFormat:@"got no addresses for contact with id: %@",
  234           [_contact valueForKey:@"companyId"]];
  235     return;
  236   }
  237   
  238   for (i = 0, cnt = [addrs count]; i < cnt; i++) {
  239     NSString *s;
  240     NSString *type;
  241     id address;
  242     
  243     address = [addrs objectAtIndex:i];
  244     
  245     type = [address valueForKey:@"type"];
  246     
  247     type = ([type isNotNull] && [type hasPrefix:@"V:"])
  248       ? [self typeFromVCardTypeHack:type]
  249       : (NSString *)[addressMapping valueForKey:type];
  250     
  251     s = [[LSVCardAddressFormatter formatter] stringForObjectValue:address];
  252     if (s != nil) {
  253       [_vCard appendString:@"ADR"];
  254       if ([type isNotEmpty]) [_vCard appendFormat:@";TYPE=%@", type];
  255       [_vCard appendString:@":"];
  256       [_vCard appendString:s];
  257       [_vCard appendString:@"\r\n"];
  258     }
  259     
  260     s = [[LSVCardLabelFormatter formatter] stringForObjectValue:address];
  261     if ([s isNotEmpty]) {
  262       [_vCard appendString:@"LABEL"];
  263       if ([type isNotEmpty]) [_vCard appendFormat:@";TYPE=%@", type];
  264       [_vCard appendString:@":"];
  265       [_vCard appendString:s];
  266       [_vCard appendString:@"\r\n"];
  267     }
  268   }
  269 }
  270 
  271 - (NSString *)vCardStringForTelInfo:(NSString *)info {
  272   NSMutableString *ms;
  273   
  274   if (![info isNotNull])  return nil;
  275   if (![info isNotEmpty]) return nil;
  276   
  277   if (![info hasPrefix:@"V:"]) {
  278     info = [info stringByEscapingUnsafeVCardCharacters];
  279     return [@";X-OGO-INFO=" stringByAppendingString:info];
  280   }
  281   
  282   info = [info substringFromIndex:2];
  283   ms   = [NSMutableString stringWithCapacity:32];
  284 
  285   if ([info characterAtIndex:0] == '{') {
  286     NSDictionary *plist;
  287     NSEnumerator *e;
  288     NSString     *k;
  289     
  290     plist = [info propertyList];
  291     e  = [plist keyEnumerator];
  292     while ((k = [e nextObject]) != nil) {
  293       [ms appendString:@";"];
  294       [ms appendString:k];
  295       [ms appendString:@"="];
  296       [ms appendString:[[plist objectForKey:k] stringValue]];
  297     }
  298   }
  299   else {
  300     [ms appendString:@";"];
  301     [ms appendString:info];
  302   }
  303   return ms;
  304 }
  305 
  306 static int compareKey(id o1, id o2, void *ctx) {
  307   if (o1 == o2) return NSOrderedSame;
  308   return [(NSString *)[o1 valueForKey:ctx] compare:[o2 valueForKey:ctx]];
  309 }
  310 
  311 - (void)_appendTelephoneData:(id)_company toVCard:(NSMutableString *)_vCard {
  312   /* TEL property */
  313   NSArray    *telephones;
  314   NSUInteger i, cnt;
  315   
  316   /* always render phones in the same ordering */
  317   telephones = [_company valueForKey:@"telephones"];
  318   telephones = [telephones sortedArrayUsingFunction:compareKey
  319                            context:@"type"];
  320   for (i = 0, cnt = [telephones count]; i < cnt; i++) {
  321     NSMutableString *name;
  322     NSString *num, *type, *info;
  323     id telephoneEO;
  324     
  325     telephoneEO = [telephones objectAtIndex:i];
  326     type        = [telephoneEO valueForKey:@"type"];
  327     info        = [telephoneEO valueForKey:@"info"];
  328     num         = [telephoneEO valueForKey:@"number"];
  329     
  330     if (![num isNotNull])  continue;
  331     if (![num isNotEmpty]) continue;
  332     
  333     name = [[NSMutableString alloc] initWithCapacity:128];
  334     [name appendString:@"TEL"];
  335     
  336     if ([type isNotEmpty]) {
  337       if (renderOGoPhoneType) {
  338         [name appendString:@";X-OGO-TYPE="];
  339         [name appendString:type];
  340       }
  341       
  342       if ([type hasPrefix:@"V:"]) {
  343         if ((type = [self typeFromVCardTypeHack:type]) != nil) {
  344           [name appendString:@";TYPE="];
  345           [name appendString:type];
  346         }
  347       }
  348       else if ((type = [telephoneMapping valueForKey:type]) != nil) {
  349         NSEnumerator *e;
  350         
  351         e = [type isKindOfClass:[NSArray class]]
  352           ? [(id)type objectEnumerator]
  353           : [[type componentsSeparatedByString:@","] objectEnumerator];
  354         while ((type = [e nextObject]) != nil) {
  355           if (![type isNotEmpty]) continue;
  356           [name appendString:@";TYPE="];
  357           [name appendString:type];
  358         }
  359       }
  360       else {
  361         [self logWithFormat:@"Note: did not find a mapping for phone: %@ / %@",
  362               [telephoneEO valueForKey:@"telephoneId"],
  363               [telephoneEO valueForKey:@"type"]];
  364       }
  365     }
  366     else {
  367       [self errorWithFormat:@"phone has no type: %@",
  368               [telephoneEO valueForKey:@"telephoneId"]];
  369     }
  370     
  371     if ((info = [self vCardStringForTelInfo:info]) != nil)
  372       [name appendString:info]; /* includes the preceding semicolon */
  373     
  374     [self _appendName:name andValue:num toVCard:_vCard];
  375     [name release]; name = nil;
  376   }
  377 }
  378 
  379 - (void)_appendExtraEmails:(id)_person markFirstAsPreferred:(BOOL)_pref
  380   toVCard:(NSMutableString *)_vCard
  381 {
  382   id tmp;
  383   
  384   if ([(tmp = [_person valueForKey:@"email1"]) isNotNull]) {
  385     [self _appendName:(_pref ? @"EMAIL;TYPE=PREF" : @"EMAIL") andValue:tmp
  386           toVCard:_vCard];  
  387   }
  388   if ([(tmp = [_person valueForKey:@"email2"]) isNotNull]) 
  389     [self _appendName:@"EMAIL" andValue:tmp toVCard:_vCard];  
  390   if ([(tmp = [_person valueForKey:@"email3"]) isNotNull]) 
  391     [self _appendName:@"EMAIL" andValue:tmp toVCard:_vCard];
  392   if ([(tmp = [_person valueForKey:@"email4"]) isNotNull]) 
  393     [self _appendName:@"EMAIL" andValue:tmp toVCard:_vCard];
  394 }
  395 
  396 - (void)_appendExtendedAttributes:(id)_contact
  397   toVCard:(NSMutableString *)_vCard
  398 {
  399   // todo: deliver company values eg as Kontact X- attributes
  400 }
  401 
  402 /* main entry */
  403 
  404 - (void)appendContentForObject:(id)_company toString:(NSMutableString *)_ms {
  405   // override in subclasses
  406 }
  407 
  408 - (NSString *)stringForObjectValue:(id)_company {
  409   NSMutableString *ms;
  410 
  411   ms = [NSMutableString stringWithCapacity:1024];
  412   [self appendPreambleToString:ms];
  413   [self appendContentForObject:_company toString:ms];
  414   [self appendPostambleToString:ms];
  415   return ms;
  416 }
  417 
  418 @end /* LSVCardCompanyFormatter */
  419 
  420 
  421 @implementation LSVCardTeamFormatter
  422 
  423 - (void)_appendTeamData:(id)_team toVCard:(NSMutableString *)_vCard {
  424   id tmp;
  425   
  426   tmp  = [_team valueForKey:@"description"];
  427   if (![tmp isNotNull] || ![tmp isNotEmpty]) {
  428     tmp = [NSString stringWithFormat:@"Team: %@",
  429                     [_team valueForKey:@"companyId"]];
  430   }
  431   // FN, formatted name
  432   [self _appendName:@"FN" andValue:tmp toVCard:_vCard];
  433   // N
  434   [_vCard appendString:@"N:"];
  435   [self _appendTextValue:tmp toVCard:_vCard];
  436   if ([tmp = ([_team valueForKey:@"number"]) isNotNull]) {
  437     [_vCard appendString:@";"];
  438     [self _appendTextValue:tmp toVCard:_vCard];
  439   }
  440   [_vCard appendString:@"\r\n"];
  441   
  442   /* EMAIL */
  443 
  444   if ([(tmp = [_team valueForKey:@"email"]) isNotNull])
  445     [self _appendName:@"EMAIL;TYPE=PREF" andValue:tmp toVCard:_vCard];
  446   [self _appendExtraEmails:_team markFirstAsPreferred:NO toVCard:_vCard];
  447 }
  448 
  449 - (void)appendContentForObject:(id)_comp toString:(NSMutableString *)_ms {
  450   [self _appendTeamData:_comp    toVCard:_ms];
  451   [self _appendIdentifier:_comp  toVCard:_ms];
  452   [self _appendContactData:_comp toVCard:_ms];
  453 }
  454 
  455 @end /* LSVCardTeamFormatter */
  456 
  457 
  458 @implementation LSVCardPersonFormatter
  459 
  460 - (void)_appendPersonName:(id)_person toVCard:(NSMutableString *)_vCard {
  461   // FN and N are required
  462   NSString *s;
  463   
  464   // N:lastname;givenname;additional names;honorific prefixes;
  465   //   honorifix suffixes
  466   s = [[LSVCardNameFormatter formatter] stringForObjectValue:_person];
  467   [_vCard appendString:@"N:"];
  468   [_vCard appendString:s];
  469   [_vCard appendString:@"\r\n"];
  470   
  471   s = [[LSVCardFormattedNameFormatter formatter] stringForObjectValue:_person];
  472   [_vCard appendString:@"FN:"];
  473   [_vCard appendString:s];
  474   [_vCard appendString:@"\r\n"];
  475 }
  476 
  477 - (void)_appendOrg:(id)_person toVCard:(NSMutableString *)_vCard {
  478   // sequence: company,department,office
  479   NSString *org, *dep, *office;
  480   
  481   // thats tricky, we might want to map 'org' to the first company?
  482   org    = [_person valueForKey:@"associatedCompany"]; // TODO: CSV?
  483   dep    = [_person valueForKey:@"department"];
  484   office = [_person valueForKey:@"office"];
  485   if (![org    isNotNull] || ![org    isNotEmpty]) org    = nil;
  486   if (![dep    isNotNull] || ![dep    isNotEmpty]) dep    = nil;
  487   if (![office isNotNull] || ![office isNotEmpty]) office = nil;
  488   
  489   if (org == nil && dep == nil && office == nil)
  490     return;
  491   
  492   [_vCard appendString:@"ORG:"];
  493   [_vCard appendString:(org != nil) 
  494           ? [org stringByEscapingUnsafeVCardCharacters] : (NSString *)@""];
  495   if (dep != nil || office != nil) {
  496     [_vCard appendString:@";"];
  497     [_vCard appendString:(dep != nil) 
  498             ? [dep stringByEscapingUnsafeVCardCharacters] : (NSString *)@""];
  499   }
  500   if (office != nil) {
  501     [_vCard appendString:@";"];
  502     [_vCard appendString:[office stringByEscapingUnsafeVCardCharacters]];
  503   }
  504   
  505   [_vCard appendString:@"\r\n"];
  506 }
  507 
  508 - (void)_appendPersonData:(id)_person toVCard:(NSMutableString *)_vCard {
  509   // FN, N, EMAIL, NICKNAME, BDAY, TITLE, FBURL
  510   id tmp;
  511   
  512   [self _appendPersonName:_person  toVCard:_vCard]; // FN, N
  513   
  514   // EMAIL
  515   [self _appendExtraEmails:_person markFirstAsPreferred:YES toVCard:_vCard];
  516   if ([(tmp = [_person valueForKey:@"email"]) isNotNull])
  517     [self _appendName:@"EMAIL" andValue:tmp toVCard:_vCard];  
  518   
  519   // NICKNAME
  520   if ([(tmp = [_person valueForKey:@"description"]) isNotNull])
  521     [self _appendName:@"NICKNAME" andValue:tmp toVCard:_vCard];
  522   // BDAY
  523   if ([(tmp = [_person valueForKey:@"birthday"]) isNotNull]) {
  524     tmp = [[NSString alloc] initWithFormat:@"%04"PRIiPTR"-%02"PRIiPTR"-%02"PRIiPTR,
  525                 [tmp yearOfCommonEra], [tmp monthOfYear],
  526                 [tmp dayOfMonth]];
  527     [self _appendName:@"BDAY" andValue:tmp toVCard:_vCard];
  528     [tmp release];
  529   }
  530   // TITLE
  531   if ([(tmp = [_person valueForKey:@"job_title"]) isNotNull])
  532     [self _appendName:@"TITLE" andValue:tmp toVCard:_vCard];
  533   
  534   // FBURL
  535   tmp = [_person valueForKey:@"isAccount"];
  536   if ([tmp isNotNull] && ([tmp intValue] == 1)) {
  537     tmp = [NSString stringWithFormat:@"http://%@/zidestore/so/%@.vfb",
  538                                      [[NSHost currentHost] name],
  539                                      [_person valueForKey:@"login"]];
  540     [self _appendName:@"FBURL" andValue:tmp toVCard:_vCard];
  541   } else {
  542       if ([(tmp = [_person valueForKey:@"freebusyUrl"]) isNotNull]) 
  543         [self _appendName:@"FBURL" andValue:tmp toVCard:_vCard];
  544     }
  545 }
  546 
  547 - (void)appendContentForObject:(id)_comp toString:(NSMutableString *)_ms {
  548   [self _appendPersonData:_comp toVCard:_ms];
  549   
  550   [self _appendIdentifier:_comp         toVCard:_ms];
  551   [self _appendContactData:_comp        toVCard:_ms];
  552   [self _appendAddressData:_comp        toVCard:_ms];
  553   [self _appendTelephoneData:_comp      toVCard:_ms];
  554   [self _appendOrg:_comp                toVCard:_ms];
  555   [self _appendExtendedAttributes:_comp toVCard:_ms];
  556 }
  557 
  558 @end /* LSVCardPersonFormatter */
  559 
  560 
  561 @implementation LSVCardEnterpriseFormatter
  562 
  563 - (void)_appendOrg:(id)_contact toVCard:(NSMutableString *)_vCard {
  564   // sequence: company,department,office
  565   NSString *org, *dep, *office;
  566   
  567   org    = [_contact valueForKey:@"description"];
  568   dep    = [_contact valueForKey:@"department"];
  569   office = [_contact valueForKey:@"office"];
  570   if (![org    isNotNull] || ![org    isNotEmpty]) org    = nil;
  571   if (![dep    isNotNull] || ![dep    isNotEmpty]) dep    = nil;
  572   if (![office isNotNull] || ![office isNotEmpty]) office = nil;
  573   
  574   if (org == nil && dep == nil && office == nil)
  575     return;
  576   
  577   [_vCard appendString:@"ORG:"];
  578   [_vCard appendString:(org != nil) 
  579           ? [org stringByEscapingUnsafeVCardCharacters] : (NSString *)@""];
  580   if (dep != nil || office != nil) {
  581     [_vCard appendString:@";"];
  582     [_vCard appendString:(dep != nil) 
  583             ? [dep stringByEscapingUnsafeVCardCharacters] : (NSString *)@""];
  584   }
  585   if (office != nil) {
  586     [_vCard appendString:@";"];
  587     [_vCard appendString:[office stringByEscapingUnsafeVCardCharacters]];
  588   }
  589   
  590   [_vCard appendString:@"\r\n"];
  591 }
  592 
  593 - (void)_appendEnterpriseData:(id)_e toVCard:(NSMutableString *)_vCard{
  594   // FN, N, ORG, EMAIL
  595   id tmp;
  596   
  597   tmp  = [_e valueForKey:@"description"];
  598   if (![tmp isNotEmpty]) {
  599     tmp = [NSString stringWithFormat:@"Enterprise: %@",
  600                     [_e valueForKey:@"companyId"]];
  601   }
  602   // FN, formatted name
  603   [self _appendName:@"FN" andValue:tmp toVCard:_vCard];
  604 
  605   // ORG
  606   [self _appendOrg:_e toVCard:_vCard];
  607   
  608   // N
  609   [_vCard appendString:@"N:"];
  610   [self _appendTextValue:tmp toVCard:_vCard];
  611   if ([tmp = ([_e valueForKey:@"number"]) isNotNull]) {
  612     [_vCard appendString:@";"];
  613     [self _appendTextValue:tmp toVCard:_vCard];
  614   }
  615   [_vCard appendString:@"\r\n"];
  616   
  617   if ([(tmp = [_e valueForKey:@"email"]) isNotNull])
  618     [self _appendName:@"EMAIL;TYPE=PREF" andValue:tmp toVCard:_vCard];
  619   
  620   [self _appendExtraEmails:_e markFirstAsPreferred:NO toVCard:_vCard];
  621 }
  622 
  623 - (void)appendContentForObject:(id)_comp toString:(NSMutableString *)_ms {
  624   [self _appendEnterpriseData:_comp toVCard:_ms];
  625   
  626   [self _appendIdentifier:_comp         toVCard:_ms];
  627   [self _appendContactData:_comp        toVCard:_ms];
  628   [self _appendAddressData:_comp        toVCard:_ms];
  629   [self _appendTelephoneData:_comp      toVCard:_ms];
  630   [self _appendExtendedAttributes:_comp toVCard:_ms];
  631 }
  632 
  633 @end /* LSVCardEnterpriseFormatter */