]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1233 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Mon, 5 Nov 2007 16:32:09 +0000 (16:32 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Mon, 5 Nov 2007 16:32:09 +0000 (16:32 +0000)
36 files changed:
SoObjects/Contacts/GNUmakefile
SoObjects/Contacts/NSObject+CardDAV.h [new file with mode: 0644]
SoObjects/Contacts/NSObject+CardDAV.m [new file with mode: 0644]
SoObjects/Contacts/SOGoContactFolder.h
SoObjects/Contacts/SOGoContactGCSFolder.h
SoObjects/Contacts/SOGoContactGCSFolder.m
SoObjects/Contacts/SOGoContactLDAPFolder.h
SoObjects/Contacts/SOGoContactLDAPFolder.m
SoObjects/Mailer/NSData+Mail.m
SoObjects/Mailer/NSString+Mail.h
SoObjects/Mailer/NSString+Mail.m
SoObjects/Mailer/SOGoMailObject+Draft.m
SoObjects/Mailer/SOGoMailObject.h
SoObjects/Mailer/SOGoMailObject.m
UI/MailPartViewers/English.lproj/Localizable.strings
UI/MailPartViewers/French.lproj/Localizable.strings
UI/MailPartViewers/German.lproj/Localizable.strings
UI/MailPartViewers/UIxMailPartMessageViewer.m
UI/MailerUI/English.lproj/Localizable.strings
UI/MailerUI/French.lproj/Localizable.strings
UI/MailerUI/GNUmakefile
UI/MailerUI/German.lproj/Localizable.strings
UI/MailerUI/UIxMailFormatter.h
UI/MailerUI/UIxMailListView.m
UI/MailerUI/UIxMailView.m
UI/MailerUI/UIxSubjectFormatter.m [deleted file]
UI/MailerUI/WOContext+UIxMailer.h
UI/MailerUI/WOContext+UIxMailer.m
UI/Templates/MailPartViewers/UIxMailPartMessageViewer.wox
UI/Templates/MailerUI/UIxMailView.wox
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/UIxMailToSelection.js
UI/WebServerResources/generic.js

index a68f8212ec3f88cfcb662fac3fbb8e592621ac8e..37c9ad23fd2589049bd9e6a6e63fcee9568498c1 100644 (file)
@@ -1,3 +1,4 @@
+
 # GNUstep makefile
 
 include ../common.make
@@ -7,8 +8,8 @@ BUNDLE_NAME = Contacts
 Contacts_PRINCIPAL_CLASS = SOGoContactsProduct
 
 Contacts_OBJC_FILES =                  \
+       NSObject+CardDAV.m              \
        Product.m                       \
-                                       \
        SOGoContactFolders.m            \
        SOGoContactGCSEntry.m           \
        SOGoContactGCSFolder.m          \
diff --git a/SoObjects/Contacts/NSObject+CardDAV.h b/SoObjects/Contacts/NSObject+CardDAV.h
new file mode 100644 (file)
index 0000000..d87dff4
--- /dev/null
@@ -0,0 +1,32 @@
+/* NSObject+CardDAV.h - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Ludovic Marcotte <ludovic@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __Contacts_NSObject_CardDAV_H__
+#define __Contacts_NSObject_CardDAV_H__
+
+@interface NSObject (CardDAV)
+
+- (id) davAddressbookQuery: (id) queryContext;
+
+@end
+
+#endif
diff --git a/SoObjects/Contacts/NSObject+CardDAV.m b/SoObjects/Contacts/NSObject+CardDAV.m
new file mode 100644 (file)
index 0000000..1a1df88
--- /dev/null
@@ -0,0 +1,164 @@
+/* NSObject+CardDAV.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Ludovic Marcotte <ludovic@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import "SOGoContactFolder.h"
+#import "SOGoContactGCSEntry.h"
+
+#import <DOM/DOMProtocols.h>
+#import <Foundation/NSArray.h>
+#import <NGExtensions/NSString+misc.h>
+#import <SaxObjC/SaxObjC.h>
+#import <SaxObjC/XMLNamespaces.h>
+#import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WORequest.h>
+#import <NGObjWeb/WOResponse.h>
+
+@implementation NSObject (CardDAV)
+
+- (void) _appendComponentsMatchingFilters: (NSArray *) filters
+                               toResponse: (WOResponse *) response
+                                 context: (id) context
+{
+  unsigned int count, max;
+  NSDictionary *currentFilter, *contact;
+  NSEnumerator *contacts;
+  NSString *baseURL;
+  id<SOGoContactFolder> o;
+
+  o = (id<SOGoContactFolder>)self;
+  baseURL = [o baseURLInContext: context];
+  
+  max = [filters count];
+  for (count = 0; count < max; count++)
+    {
+      currentFilter = [filters objectAtIndex: count];
+      contacts = [[o lookupContactsWithFilter: [[currentFilter allValues] lastObject]
+                    sortBy: @"c_givenname"
+                    ordering: NSOrderedDescending]
+                  objectEnumerator];
+      
+      while ((contact = [contacts nextObject]))
+      {
+       [o appendObject: contact
+          withBaseURL: baseURL
+          toREPORTResponse: response];
+        }
+    }
+}
+
+- (BOOL) _isValidFilter: (NSString *) theString
+{
+  if ([theString caseInsensitiveCompare: @"sn"] == NSOrderedSame)
+    return YES;
+
+  if ([theString caseInsensitiveCompare: @"givenname"] == NSOrderedSame)
+    return YES;
+
+  if ([theString caseInsensitiveCompare: @"mail"] == NSOrderedSame)
+    return YES;
+
+  if ([theString caseInsensitiveCompare: @"telephonenumber"] == NSOrderedSame)
+    return YES;
+
+  return NO;
+}
+
+- (NSDictionary *) _parseContactFilter: (id <DOMElement>) filterElement
+{
+  NSMutableDictionary *filterData;
+  id <DOMNode> parentNode;
+  id <DOMNodeList> ranges;
+
+  parentNode = [filterElement parentNode];
+
+  if ([[parentNode tagName] isEqualToString: @"filter"] &&
+      [self _isValidFilter: [filterElement attribute: @"name"]])
+    {
+      ranges = [filterElement getElementsByTagName: @"text-match"];
+     
+      if ([(NSArray *)ranges count] && [(NSArray *)[[ranges objectAtIndex: 0] childNodes] count])
+       {
+         filterData = [NSMutableDictionary new];
+         [filterData autorelease];
+         [filterData setObject: [[(NSArray *)[[ranges objectAtIndex: 0] childNodes] lastObject] data]
+                     forKey: [filterElement attribute: @"name"]];
+       }
+    }
+  else
+    filterData = nil;
+
+  return filterData;
+}
+
+- (NSArray *) _parseContactFilters: (id <DOMElement>) parentNode
+{
+  NSEnumerator *children;
+  id<DOMElement> node;
+  NSMutableArray *filters;
+  NSDictionary *filter;
+
+  filters = [[NSMutableArray new] autorelease];
+
+  children = [[parentNode getElementsByTagName: @"prop-filter"]
+              objectEnumerator];
+
+  node = [children nextObject];
+
+  while (node)
+    {
+      filter = [self _parseContactFilter: node];
+      if (filter)
+        [filters addObject: filter];
+      node = [children nextObject];
+    }
+
+  return filters;
+}
+
+- (id) davAddressbookQuery: (id) queryContext
+{
+  WOResponse *r;
+  NSArray *filters;
+  id <DOMDocument> document;
+
+  r = [queryContext response];
+  [r setStatus: 207];
+  [r setContentEncoding: NSUTF8StringEncoding];
+  [r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
+  [r setHeader: @"no-cache" forKey: @"pragma"];
+  [r setHeader: @"no-cache" forKey: @"cache-control"];
+  [r appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
+  [r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
+     @" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\r\n"];
+
+  document = [[queryContext request] contentAsDOMDocument];
+  filters = [self _parseContactFilters: [document documentElement]];
+
+  [self _appendComponentsMatchingFilters: filters
+        toResponse: r
+       context: queryContext];
+  [r appendContentString:@"</D:multistatus>\r\n"];
+
+  return r;
+}
+
+@end
index c1ff89685a18aa4e47168d71c359cfd678e785dd..709e4ae620c2b22af57bd381be4dff4a8a1b9ffe 100644 (file)
@@ -36,6 +36,7 @@
 @class NSString, NSArray;
 @class SOGoContactObject;
 @class SOGoObject;
+@class WOResponse;
 
 @protocol SOGoContactObject;
 
 
 @protocol SOGoContactFolder <NSObject>
 
+- (void) appendObject: (NSDictionary *) object
+          withBaseURL: (NSString *) baseURL
+     toREPORTResponse: (WOResponse *) r;
+
 - (NSArray *) lookupContactsWithFilter: (NSString *) filter
                                 sortBy: (NSString *) sortKey
                               ordering: (NSComparisonResult) sortOrdering;
index d10913d0b3010685805893febd7c0e547354614d..9ac8fb1fe0f85834f6712e63782b3363c18b6a98 100644 (file)
@@ -25,6 +25,7 @@
 #import <SoObjects/SOGo/SOGoFolder.h>
 
 #import "SOGoContactFolder.h"
+#import "NSObject+CardDAV.h"
 
 @class NSArray;
 @class NSString;
index 26e0e974cd12c59a63deb8ba5850e6f047f4d78d..f248f51461656534782a8786f17e796d890153c8 100644 (file)
 #import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject+SoDAV.h>
 #import <NGObjWeb/WOContext.h>
-#import <NGObjWeb/WOResponse.h>
 #import <NGObjWeb/WORequest.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NSString+misc.h>
 #import <EOControl/EOQualifier.h>
 #import <EOControl/EOSortOrdering.h>
 #import <GDLContentStore/GCSFolder.h>
-#import <DOM/DOMProtocols.h>
-#import <SaxObjC/SaxObjC.h>
-#import <SaxObjC/XMLNamespaces.h>
 
 #import <SoObjects/SOGo/NSDictionary+Utilities.h>
 #import "SOGoContactGCSEntry.h"
@@ -71,6 +67,7 @@
 
   isPut = NO;
   obj = [super lookupName:_key inContext:_ctx acquire:NO];
+  
   if (!obj)
     {
       if ([[[_ctx request] method] isEqualToString: @"PUT"])
   return newRecords;
 }
 
-- (BOOL) _isValidFilter: (NSString *) theString
-{
-  if ([theString caseInsensitiveCompare: @"sn"] == NSOrderedSame)
-    return YES;
-
-  if ([theString caseInsensitiveCompare: @"givenname"] == NSOrderedSame)
-    return YES;
-
-  if ([theString caseInsensitiveCompare: @"mail"] == NSOrderedSame)
-    return YES;
-
-  if ([theString caseInsensitiveCompare: @"telephonenumber"] == NSOrderedSame)
-    return YES;
-
-  return NO;
-}
-
-- (NSDictionary *) _parseContactFilter: (id <DOMElement>) filterElement
+- (NSArray *) lookupContactsWithFilter: (NSString *) filter
+                                sortBy: (NSString *) sortKey
+                              ordering: (NSComparisonResult) sortOrdering
 {
-  NSMutableDictionary *filterData;
-  id <DOMNode> parentNode;
-  id <DOMNodeList> ranges;
+  NSArray *fields, *dbRecords, *records;
+  EOQualifier *qualifier;
+  EOSortOrdering *ordering;
 
-  parentNode = [filterElement parentNode];
+  fields = folderListingFields;
+  qualifier = [self _qualifierForFilter: filter];
+  dbRecords = [[self ocsFolder] fetchFields: fields
+                               matchingQualifier: qualifier];
 
-  if ([[parentNode tagName] isEqualToString: @"filter"] &&
-      [self _isValidFilter: [filterElement attribute: @"name"]])
+  if ([dbRecords count] > 0)
     {
-      ranges = [filterElement getElementsByTagName: @"text-match"];
-     
-      if ([ranges count] && [[[ranges objectAtIndex: 0] childNodes] count])
-       {
-         filterData = [NSMutableDictionary new];
-         [filterData autorelease];
-         [filterData setObject: [[[[ranges objectAtIndex: 0] childNodes] lastObject] data]
-                     forKey: [filterElement attribute: @"name"]];
-       }
+      records = [self _flattenedRecords: dbRecords];
+      ordering
+        = [EOSortOrdering sortOrderingWithKey: sortKey
+                          selector: ((sortOrdering == NSOrderedDescending)
+                                     ? EOCompareCaseInsensitiveDescending
+                                     : EOCompareCaseInsensitiveAscending)];
+      records
+        = [records sortedArrayUsingKeyOrderArray:
+                     [NSArray arrayWithObject: ordering]];
     }
   else
-    filterData = nil;
-
-  return filterData;
-}
-
-#warning filters is leaked here
-- (NSArray *) _parseContactFilters: (id <DOMElement>) parentNode
-{
-  NSEnumerator *children;
-  id<DOMElement> node;
-  NSMutableArray *filters;
-  NSDictionary *filter;
-
-  filters = [NSMutableArray new];
-
-  children = [[parentNode getElementsByTagName: @"prop-filter"]
-              objectEnumerator];
-
-  node = [children nextObject];
-
-  while (node)
-    {
-      filter = [self _parseContactFilter: node];
-      if (filter)
-        [filters addObject: filter];
-      node = [children nextObject];
-    }
+    records = nil;
+  //  else
+  //[self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
 
-  return filters;
+  [self debugWithFormat:@"fetched %i records.", [records count]];
+  return records;
 }
 
 - (void) appendObject: (NSDictionary *) object
   [r appendContentString: @"  </D:response>\r\n"];
 }
 
-- (void) _appendComponentsMatchingFilters: (NSArray *) filters
-                               toResponse: (WOResponse *) response
-{
-  unsigned int count, max;
-  NSDictionary *currentFilter, *contact;
-  NSEnumerator *contacts;
-  NSString *baseURL;
-
-  baseURL = [self baseURLInContext: context];
-
-  max = [filters count];
-  for (count = 0; count < max; count++)
-    {
-      currentFilter = [filters objectAtIndex: count];
-      contacts = [[self lookupContactsWithFilter: [[currentFilter allValues] lastObject]
-                       sortBy: @"c_givenname"
-                       ordering: NSOrderedDescending]
-                  objectEnumerator];
-      
-      while ((contact = [contacts nextObject]))
-      {
-          [self appendObject: contact
-                withBaseURL: baseURL
-                toREPORTResponse: response];
-        }
-    }
-}
-
-- (NSArray *) lookupContactsWithFilter: (NSString *) filter
-                                sortBy: (NSString *) sortKey
-                              ordering: (NSComparisonResult) sortOrdering
-{
-  NSArray *fields, *dbRecords, *records;
-  EOQualifier *qualifier;
-  EOSortOrdering *ordering;
-
-  fields = folderListingFields;
-  qualifier = [self _qualifierForFilter: filter];
-  dbRecords = [[self ocsFolder] fetchFields: fields
-                               matchingQualifier: qualifier];
-
-  if ([dbRecords count] > 0)
-    {
-      records = [self _flattenedRecords: dbRecords];
-      ordering
-        = [EOSortOrdering sortOrderingWithKey: sortKey
-                          selector: ((sortOrdering == NSOrderedDescending)
-                                     ? EOCompareCaseInsensitiveDescending
-                                     : EOCompareCaseInsensitiveAscending)];
-      records
-        = [records sortedArrayUsingKeyOrderArray:
-                     [NSArray arrayWithObject: ordering]];
-    }
-  else
-    records = nil;
-//   else
-//     [self errorWithFormat:@"(%s): fetch failed!", __PRETTY_FUNCTION__];
-
-  [self debugWithFormat:@"fetched %i records.", [records count]];
-  return records;
-}
-
-- (NSArray *) davNamespaces
-{
-  return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"];
-}
-
-- (id) davAddressbookQuery: (id) queryContext
-{
-  WOResponse *r;
-  NSArray *filters;
-  id <DOMDocument> document;
-
-  r = [context response];
-  [r setStatus: 207];
-  [r setContentEncoding: NSUTF8StringEncoding];
-  [r setHeader: @"text/xml; charset=\"utf-8\"" forKey: @"content-type"];
-  [r setHeader: @"no-cache" forKey: @"pragma"];
-  [r setHeader: @"no-cache" forKey: @"cache-control"];
-  [r appendContentString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"];
-  [r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
-     @" xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\r\n"];
-
-  document = [[context request] contentAsDOMDocument];
-  filters = [self _parseContactFilters: [document documentElement]];
-
-  [self _appendComponentsMatchingFilters: filters
-        toResponse: r];
-  [r appendContentString:@"</D:multistatus>\r\n"];
-
-  return r;
-}
-
 - (NSArray *) davComplianceClassesInContext: (id)_ctx
 {
   NSMutableArray *classes;
   return classes;
 }
 
+- (NSArray *) davNamespaces
+{
+  return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"];
+}
+
 - (NSString *) groupDavResourceType
 {
   return @"vcard-collection";
index e405a3faf5c00f85db40d6617472523a30d1d750..7cddf7e60da8d38f24e39bd4793510a79aa0ae90 100644 (file)
@@ -24,6 +24,7 @@
 #define SOGOCONTACTLDAPFOLDER_H
 
 #import "SOGoContactFolder.h"
+#import "NSObject+CardDAV.h"
 
 @class NSMutableDictionary;
 
index 4a266c4b3c75961f67bca839858eb41bb171bdcc..13772500ef2e74967ee15d21de61f9eeda46ce82 100644 (file)
 #import <SaxObjC/XMLNamespaces.h>
 
 #import <SoObjects/SOGo/LDAPSource.h>
+#import <SoObjects/SOGo/NSString+Utilities.h>
 #import "SOGoContactLDIFEntry.h"
 #import "SOGoContactLDAPFolder.h"
+#import <NGExtensions/NSString+misc.h>
+#import <NGObjWeb/WORequest.h>
+#import <NGObjWeb/SoSelectorInvocation.h>
 
 @class WOContext;
 
+
+
 @implementation SOGoContactLDAPFolder
 
+- (void) appendObject: (NSDictionary *) object
+          withBaseURL: (NSString *) baseURL
+     toREPORTResponse: (WOResponse *) r
+{
+  SOGoContactLDIFEntry *component;
+  Class componentClass;
+  NSString *name, *etagLine, *contactString;
+
+  name = [object objectForKey: @"c_name"];
+  componentClass = [SOGoContactLDIFEntry class];
+
+  
+  component = [componentClass contactEntryWithName: name  withLDIFEntry: object  inContainer: self];
+
+  [r appendContentString: @"  <D:response>\r\n"];
+  [r appendContentString: @"    <D:href>"];
+  [r appendContentString: baseURL];
+  if (![baseURL hasSuffix: @"/"])
+    [r appendContentString: @"/"];
+  [r appendContentString: name];
+  [r appendContentString: @"</D:href>\r\n"];
+
+  [r appendContentString: @"    <D:propstat>\r\n"];
+  [r appendContentString: @"      <D:prop>\r\n"];
+  etagLine = [NSString stringWithFormat: @"        <D:getetag>%@</D:getetag>\r\n",
+                       [component davEntityTag]];
+  [r appendContentString: etagLine];
+  [r appendContentString: @"      </D:prop>\r\n"];
+  [r appendContentString: @"      <D:status>HTTP/1.1 200 OK</D:status>\r\n"];
+  [r appendContentString: @"    </D:propstat>\r\n"];
+  [r appendContentString: @"    <C:addressbook-data>"];
+  contactString = [[component contentAsString] stringByEscapingXMLString];
+  [r appendContentString: contactString];
+  [r appendContentString: @"</C:addressbook-data>\r\n"];
+  [r appendContentString: @"  </D:response>\r\n"];
+}
+
 + (id) folderWithName: (NSString *) aName
        andDisplayName: (NSString *) aDisplayName
          inContainer: (id) aContainer
   return displayName;
 }
 
+- (NSArray *) davNamespaces
+{
+  return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"];
+}
+
 - (id) lookupName: (NSString *) objectName
         inContext: (WOContext *) lookupContext
           acquire: (BOOL) acquire
   id obj;
   NSDictionary *ldifEntry;
 
-//   NSLog (@"looking up name '%@'...", name);
+  //NSLog (@"looking up name '%@'...", objectName);
 
   /* first check attributes directly bound to the application */
   ignoreSoObjectHunger = YES;
   obj = [super lookupName: objectName inContext: lookupContext acquire: NO];
   ignoreSoObjectHunger = NO;
+
   if (!obj)
     {
       ldifEntry = [ldapSource lookupContactEntry: objectName];
+#if 0
       obj = ((ldifEntry)
             ? [SOGoContactLDIFEntry contactEntryWithName: objectName
                                     withLDIFEntry: ldifEntry
                                     inContainer: self]
             : [NSException exceptionWithHTTPStatus: 404]);
+#else
+      if (ldifEntry)
+       obj = [SOGoContactLDIFEntry contactEntryWithName: objectName
+                                   withLDIFEntry: ldifEntry
+                                   inContainer: self];
+      else
+       {
+         NSArray *davNamespaces;
+         NSDictionary *davInvocation;
+         NSString *objcMethod;
+         
+         davNamespaces = [self davNamespaces];
+         if ([davNamespaces count] > 0)
+           {
+             davInvocation = [objectName asDavInvocation];
+             if (davInvocation
+                 && [davNamespaces
+                      containsObject: [davInvocation objectForKey: @"ns"]])
+               {
+                 objcMethod = [[davInvocation objectForKey: @"method"]
+                            davMethodToObjC];
+                 obj = [[SoSelectorInvocation alloc]
+                         initWithSelectorNamed:
+                           [NSString stringWithFormat: @"%@:", objcMethod]
+                         addContextParameter: YES];
+                 [obj autorelease];
+               }
+           }
+       }
+#endif
     }
 
   return obj;
index eb3051f6f43b68961a7af4bf211386b19ee75865..5245532093e29446b6412bf86e3db2ca6abe42c8 100644 (file)
   return decodedData;
 }
 
+- (NSString *) decodedSubject
+{
+  const char *cData, *endFlag;
+  unsigned int len;
+  NSString *converted, *decodedSubject;
+
+  cData = [self bytes];
+  len = [self length];
+
+  if (len)
+    {
+      decodedSubject = nil;
+      if (len > 6)
+       {
+         endFlag = cData + len - 2;
+         if (*cData == '=' && *(cData + 1) == '?'
+             && *endFlag == '?' && *(endFlag + 1) == '=')
+           {
+             converted
+               = [[NSString alloc] initWithData: self
+                                   encoding: NSASCIIStringEncoding];
+             if (converted)
+               {
+                 [converted autorelease];
+                 decodedSubject = [converted stringByDecodingQuotedPrintable];
+               }
+           }
+       }
+      if (!decodedSubject)
+       {
+         decodedSubject
+           = [[NSString alloc] initWithData: self
+                               encoding: NSUTF8StringEncoding];
+         if (!decodedSubject)
+           decodedSubject
+             = [[NSString alloc] initWithData: self
+                                 encoding: NSISOLatin1StringEncoding];
+         [decodedSubject autorelease];
+       }
+    }
+  else
+    decodedSubject = @"";
+
+  return decodedSubject;
+}
+
 @end
index 2547110150b083aef6f1b82880c418d19a2c8f5a..72bc984a986f1c0b45d8c2a3c1b50c246a8f2b47 100644 (file)
@@ -29,6 +29,8 @@
 
 - (NSString *) htmlToText;
 
+- (NSString *) decodedSubject;
+
 @end
 
 #endif /* NSSTRING_MAIL_H */
index 494becdc07418bf400234cc8d7a2506c67103171..084280a26ee196ee0f58868528c882ce00f2b7c1 100644 (file)
@@ -28,6 +28,7 @@
 #import <SaxObjC/SaxLexicalHandler.h>
 #import <SaxObjC/SaxXMLReader.h>
 #import <SaxObjC/SaxXMLReaderFactory.h>
+#import <NGExtensions/NGQuotedPrintableCoding.h>
 #import <NGExtensions/NSString+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 
   return [handler result];
 }
 
+- (NSString *) decodedSubject
+{
+  NSString *decodedSubject;
+
+  if ([self hasPrefix: @"=?"] && [self hasSuffix: @"?="])
+    {
+      decodedSubject = [self stringByDecodingQuotedPrintable];
+      if (!decodedSubject)
+       decodedSubject = self;
+    }
+  else
+    decodedSubject = self;
+
+  return decodedSubject;
+}
+
 @end
index 0ce4da0d9e43b3da26956a49dd339037f06bda3c..28b31f558b401d8cfc619c72cd8836e9aaa4df47 100644 (file)
@@ -54,7 +54,7 @@
 
   hasPrefix = NO;
 
-  subject = [[self envelope] subject];
+  subject = [self decodedSubject];
   i = 0;
   while (!hasPrefix && replyPrefixes[i])
     if ([subject hasPrefix: replyPrefixes[i]])
   };
   unsigned int count, length;
 
-  subject = [[self envelope] subject];
+  subject = [self decodedSubject];
   length = [subject length];
   if (!length)
     {
 {
   NSString *subject, *newSubject;
 
-  subject = [[self envelope] subject];
+  subject = [self decodedSubject];
   if ([subject length] > 0)
     newSubject = [NSString stringWithFormat: @"[Fwd: %@]", subject];
   else
index 80e0953805ef0ce6c57526081e6a4f4313a4883f..e5d11e821ac2f502ee958c0eb6793b9e1b96539d 100644 (file)
@@ -58,6 +58,7 @@
 
 - (NGImap4Envelope *)envelope;
 - (NSString *)subject;
+- (NSString *)decodedSubject;
 - (NSCalendarDate *)date;
 - (NSArray *)fromEnvelopeAddresses;
 - (NSArray *)toEnvelopeAddresses;
index 15d531375cae2514eab9b34b07fb3cd52bdd8bf5..934f1c61ced1b5765dc0b69ab78ac3b200c50636 100644 (file)
@@ -261,6 +261,11 @@ static BOOL debugSoParts       = NO;
   return [[self envelope] subject];
 }
 
+- (NSString *) decodedSubject
+{
+  return [[self subject] decodedSubject];
+}
+
 - (NSCalendarDate *) date
 {
   NSTimeZone *userTZ;
index 0dc239af74f4645ecba978490a2e3179b17b74a9..c120183f84c7827ea8a2dca251c212b06263a67e 100644 (file)
@@ -11,7 +11,11 @@ add_info_text       = "iMIP 'ADD' requests are not yet supported by SOGo.";
 publish_info_text   = "The sender informs you of the attached event.";
 cancel_info_text    = "Your invitation or the whole event was canceled.";
 request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant.";
-Organizer           = "Organizer";
+Appointment         = "Appointment";
+
+Organizer           = "Organisateur";
+Time                = "Time";
+Attendees           = "Attendees";
 request_info        = "invites you to participate in a meeting.";
 do_add_to_cal       = "add to calendar";
 do_del_from_cal     = "delete from calendar";
@@ -21,3 +25,7 @@ do_tentative        = "tentative";
 do_update_status    = "update status in calendar";
 reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant.";
 reply_info          = "This is a reply to an event invitation done by you.";
+
+"Untitled" = "Untitled";
+
+"Size" = "Size";
index 6b90b000947a4782675a94b94805096c209b83f6..ad6492f83793b82eaeaa1731a4654b92661a9f91 100644 (file)
@@ -26,4 +26,6 @@ do_update_status    = "mettre l'agenda à jour";
 reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité.";
 reply_info          = "Ceci est une réponse à un événement que vous avez organisé.";
 
+"Untitled" = "Sans titre";
+
 "Size" = "Taille";
index 6b90b000947a4782675a94b94805096c209b83f6..c120183f84c7827ea8a2dca251c212b06263a67e 100644 (file)
@@ -1,29 +1,31 @@
-ACCEPTED     = "Accepté";
-COMPLETED    = "Terminé";
-DECLINED     = "Refusé";
-DELEGATED    = "Délégué";
-IN-PROCESS   = "En cours de traitement";
-NEEDS-ACTION = "Prise de décision nécessaire";
-TENTATIVE    = "Proposition";
-organized_by_you    = "vous êtes l'organisateur";
-you_are_an_attendee = "vous êtes invité";
+ACCEPTED            = "accepted";
+COMPLETED           = "completed";
+DECLINED            = "declined";
+DELEGATED           = "delegated";
+IN-PROCESS          = "in process";
+NEEDS-ACTION        = "needs action";
+TENTATIVE           = "tentative";
+organized_by_you    = "organized by you";
+you_are_an_attendee = "you are an attendee";
 add_info_text       = "iMIP 'ADD' requests are not yet supported by SOGo.";
-publish_info_text   = "L'expéditeur vous informe de l'événement attaché.";
-cancel_info_text    = "Votre invitation ou l'événement au complet a été annulé.";
-request_info_no_attendee = "propose une réunion entre les invités. Ce message tint seulement lieu d'avis, vous n'êtes pas indiqué comme invité.";
-Appointment         = "Événement";
+publish_info_text   = "The sender informs you of the attached event.";
+cancel_info_text    = "Your invitation or the whole event was canceled.";
+request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant.";
+Appointment         = "Appointment";
 
 Organizer           = "Organisateur";
-Time                = "Date";
-Attendees           = "Invités";
-request_info        = "vous invite à une réunion.";
-do_add_to_cal       = "ajouter à l'agenda";
-do_del_from_cal     = "effacer de l'agenda";
-do_accept           = "accepter";
-do_decline          = "decliner";
+Time                = "Time";
+Attendees           = "Attendees";
+request_info        = "invites you to participate in a meeting.";
+do_add_to_cal       = "add to calendar";
+do_del_from_cal     = "delete from calendar";
+do_accept           = "accept";
+do_decline          = "decline";
 do_tentative        = "tentative";
-do_update_status    = "mettre l'agenda à jour";
-reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité.";
-reply_info          = "Ceci est une réponse à un événement que vous avez organisé.";
+do_update_status    = "update status in calendar";
+reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant.";
+reply_info          = "This is a reply to an event invitation done by you.";
 
-"Size" = "Taille";
+"Untitled" = "Untitled";
+
+"Size" = "Size";
index 3a18cb76fdedb7c7dc98a72bba5651e903191b83..123941cbefd982cd257e0f2bc335b846108594c8 100644 (file)
@@ -22,6 +22,9 @@
 #import <NGImap4/NGImap4Envelope.h>
 #import <NGImap4/NGImap4EnvelopeAddress.h>
 
+#import <SoObjects/Mailer/NSData+Mail.h>
+#import <SoObjects/Mailer/NSString+Mail.h>
+
 #import <UI/MailerUI/WOContext+UIxMailer.h>
 #import "UIxMailRenderingContext.h"
 
   return [formattedComponents componentsJoinedByString: @", "];
 }
 
+- (NSString *) messageSubject
+{
+  id baseSubject;
+  NSString *subject;
+
+  baseSubject = [[self envelope] subject];
+  subject = [baseSubject decodedSubject];
+  if (![subject length])
+    subject = [self labelForKey: @"Untitled"];
+
+  return subject;
+}
+
 - (NSString *) fromAddresses
 {
   NSArray *from;
index d3d7eb06ad4d2a6bf05879b68a211a428c814564..3bfff1730ff21b353c663c2b25f30a0d94e6e8f2 100644 (file)
 "Mark Unread" = "Mark Unread";
 "Mark Read"   = "Mark Read";
 
+"Untitled" = "Untitled";
+
 /* Tree */
 
 "SentFolderName"   = "Sent";
index a818ceb0a54ac92da07c2f11234bd58614a4080a..8a3d11b344d81e06cc56847649a1588112633319 100644 (file)
 "next"          = "Suivant";
 "last"          = "Dernier";
 
+"msgnumber_to"  = "à";
+"msgnumber_of"  = "de";
+
 "Mark Unread" = "Marquer comme non lu"; 
 "Mark Read"   = "Marquer comme lu";
 
-"msgnumber_to"  = "à";
-"msgnumber_of"  = "de";
+"Untitled" = "Sans titre";
 
 /* Tree */
 
index 544e1b15821d0b0bb792db4976ba4ef5fc746374..08c96e702214480dbe28690632b469d8a9ba8ad1 100644 (file)
@@ -12,7 +12,6 @@ MailerUI_OBJC_FILES += \
        MailerUIProduct.m       \
        \
        UIxMailFormatter.m              \
-       UIxSubjectFormatter.m           \
        UIxEnvelopeAddressFormatter.m   \
        WOContext+UIxMailer.m           \
        \
index 4dd0652b303ec99291795b4e41e9528e0d6d19df..6812036f17deb7d5aed0beb4b9e6c9fd28468412 100644 (file)
 "next"          = "Nächste";
 "last"          = "Letzter";
 
+"msgnumber_to"  = "bis";
+"msgnumber_of"  = "von";
+
 "Mark Unread" = "Als ungelesen markieren"; 
 "Mark Read"   = "Als gelesen markieren";
 
-"msgnumber_to"  = "bis";
-"msgnumber_of"  = "von";
+"Untitled" = "Untitled";
 
 /* Tree */
 
index 2270e2870c47201c6160742fadf2c702a2b53dfc..847cda67253dd046319a6b2bdf8969cabc114c8a 100644 (file)
 
 @end
 
-/*
-  TODO: the subject formatter should deal with the various 're:' like prefixes
-        and translate them into the native languages?
-        (or something like Re(5): ?)
-*/
-
-@interface UIxSubjectFormatter : UIxMailFormatter
-{
-  unsigned maxLength;
-}
-
-/* configuration */
-
-- (unsigned int)maxLength;
-
-/* labels */
-
-- (NSString *)missingSubjectLabel;
-
-/* specific formatters */
-
-- (NSString *)stringForStringValue:(NSString *)_subject;
-- (NSString *)stringForDataValue:(NSData *)_subject;
-
-@end
-
 @interface UIxEnvelopeAddressFormatter : UIxMailFormatter
 {
   NSString     *separator;
index 2ccc79b262ebaba7550ed534d0a966b79689b5b3..be29109412247561d917b8cbdeaed21bc80966a9 100644 (file)
 
 - (NSString *) messageSubject
 {
+  id baseSubject;
   NSString *subject;
-  id envSubject;
 
-  envSubject = [[message valueForKey: @"envelope"] subject];
-  if ([envSubject isKindOfClass: [NSData class]])
-    {
-      subject = [[NSString alloc] initWithData: envSubject
-                                 encoding: NSUTF8StringEncoding];
-      [subject autorelease];
-    }
-  else
-    subject = envSubject;
+  baseSubject = [[message valueForKey: @"envelope"] subject];
+  subject = [baseSubject decodedSubject];
+  if (![subject length])
+    subject = [self labelForKey: @"Untitled"];
 
   return subject;
 }
index e3da019f6edb000216e79d29301fcabb6411efc7..986b267494e344c1798900cdb948165ee34e2127 100644 (file)
@@ -88,16 +88,22 @@ static NSString *mailETag = nil;
   return currentAddress;
 }
 
-- (NSString *) objectTitle
+- (NSString *) messageSubject
 {
-  return [[self clientObject] subject];
+  NSString *subject;
+
+  subject = [[self clientObject] decodedSubject];
+  if (![subject length])
+    subject = [self labelForKey: @"Untitled"];
+
+  return subject;
 }
 
 - (NSString *) panelTitle
 {
   return [NSString stringWithFormat: @"%@: %@",
                    [self labelForKey: @"View Mail"],
-                   [self objectTitle]];
+                   [self messageSubject]];
 }
 
 /* links (DUP to UIxMailPartViewer!) */
diff --git a/UI/MailerUI/UIxSubjectFormatter.m b/UI/MailerUI/UIxSubjectFormatter.m
deleted file mode 100644 (file)
index 784c3a9..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSObject+Logs.h>
-#import <NGMail/NGMimeMessageParser.h>
-
-#import "UIxMailFormatter.h"
-
-@implementation UIxSubjectFormatter
-
-static Class StrClass  = Nil;
-static Class DataClass = Nil;
-
-+ (void)initialize {
-  StrClass  = [NSString class];
-  DataClass = [NSData   class];
-}
-
-- (id)init {
-  if ((self = [super init])) {
-    self->maxLength = 64;
-  }
-  return self;
-}
-
-/* configuration */
-
-- (unsigned int)maxLength {
-  return self->maxLength;
-}
-
-- (BOOL)shouldDecodeQP {
-  return YES;
-}
-
-/* labels */
-
-- (NSString *)missingSubjectLabel {
-  return [self labelForKey:@"no_subject"];
-}
-
-/* specific formatters */
-
-- (NSString *)stringForStringValue:(NSString *)_subject {
-  NSString *s;
-  
-  /* quoted printable */
-  if ([self shouldDecodeQP] && [_subject hasPrefix:@"=?"]) {
-    /* 
-       Now this is interesting. An NSString should not contain QP markers since
-       it is already 'charset decoded'. This is also why the NGMime parser
-       expects an NSData.
-       
-       Sample:
-         =?iso-8859-1?q?Yannick=20DAmboise?=
-
-       Note: -stringByDecodingQuotedPrintable only expands =D0 like charcodes!
-    */
-    NSData *data;
-    
-    /* header field data should always be ASCII */
-    data = [_subject dataUsingEncoding:NSUTF8StringEncoding];
-    return [self stringForDataValue:data];
-  }
-  
-  if ([_subject length] == 0)
-    return [self missingSubjectLabel];
-  
-  if ([_subject length] <= [self maxLength])
-    return _subject;
-  
-  s = [_subject substringToIndex:([self maxLength] - 3)];
-  return [s stringByAppendingString:@"..."];
-}
-
-- (NSString *)stringForDataValue:(NSData *)_subject {
-  NSString *s, *r;
-  unsigned len;
-  
-  if ((len = [_subject length]) == 0)
-    return [self missingSubjectLabel];
-  
-  /* check for quoted printable */
-  
-  if (len > 6 && [self shouldDecodeQP]) {
-    const unsigned char *b;
-    
-    b = [_subject bytes];
-    if (b[0] == '=' && b[1] == '?') {
-      /* eg: '=?iso-8859-1?q?Yannick=20DAmboise?=' */
-      id t;
-      
-      t = [_subject decodeQuotedPrintableValueOfMIMEHeaderField:@"subject"];
-      if ([t isNotNull])
-       return [self stringForObjectValue:t];
-      else
-       [self warnWithFormat:@"decoding QP failed: '%@'", t];
-    }
-  }
-  
-  /* continue NSData processing */
-  
-  [self warnWithFormat:@"NSData subject, using UTF-8 to decode."];
-  
-  // TODO: exception handler?
-  s = [[NSString alloc] initWithData:_subject encoding:NSUTF8StringEncoding];
-  if (s == nil) {
-    [self errorWithFormat:@"could do not decode NSData subject!"];
-    return [self labelForKey:@"Error_CouldNotDecodeSubject"];
-  }
-  
-  if ([s hasPrefix:@"=?"]) { // TODO: this should never happen?
-    [self warnWithFormat:@"subject still has QP signature: '%@'", s];
-    r = [s copy];
-  }
-  else
-    r = [[self stringForStringValue:s] copy];
-  [s release];
-  return [r autorelease];
-}
-
-/* formatter entry function */
-
-- (NSString *)stringForObjectValue:(id)_subject {
-  if (![_subject isNotNull])
-    return [self missingSubjectLabel];
-  
-  if ([_subject isKindOfClass:StrClass])
-    return [self stringForStringValue:_subject];
-  if ([_subject isKindOfClass:DataClass])
-    return [self stringForDataValue:_subject];
-  
-  return [self stringForStringValue:[_subject stringValue]];
-}
-
-@end /* UIxSubjectFormatter */
index 05e94d29f37997a9cf23625289d1162b6c133833..281a693d941e06a93c41cf4eebe17cbe8bed8565 100644 (file)
 
 @class NSFormatter;
 
-@interface WOContext(UIxMailer)
+@interface WOContext (UIxMailer)
 
-- (NSFormatter *)mailSubjectFormatter;
-- (NSFormatter *)mailDateFormatter;
-- (NSFormatter *)mailEnvelopeAddressFormatter;
-- (NSFormatter *)mailEnvelopeFullAddressFormatter;
+- (NSFormatter *) mailDateFormatter;
+- (NSFormatter *) mailEnvelopeAddressFormatter;
+- (NSFormatter *) mailEnvelopeFullAddressFormatter;
 
 @end
 
index fbe50299711fc759169f6c85bee9de26d14f5d5e..716d14b9da0c6e7e9a820ac51f67c193260b5f6b 100644 (file)
 // TODO: make configurable
 // TODO: cache!
 
-- (NSFormatter *) mailSubjectFormatter
-{
-  return [[[UIxSubjectFormatter alloc] init] autorelease];
-}
-
 - (NSFormatter *) mailDateFormatter
 {
   return [[self activeUser] dateFormatterInContext: self];
index 490114775f1597819c649f77353233f7e1ddf3dd..f762c68200b4b1b6a7dc8776f929aaedb2925ca3 100644 (file)
@@ -9,8 +9,7 @@
     <!-- TODO: the table is a DUP to UIxMailView, own component? -->
     <div class="bodyFields">
       <span class="fieldName"><var:string label:value="Subject"/>:</span>
-      <var:string value="envelope.subject"
-       formatter="context.mailSubjectFormatter"/><br/>
+      <var:string value="messageSubject"/><br/>
       <span class="fieldName"><var:string label:value="From"/>:</span>
       <var:string value="fromAddresses"/><br/>
       <span class="fieldName"><var:string label:value="Date"/>:</span>
index 74f5637831cef4d72c928d9393659d6154f387c3..de0d652674a2c9f55ad41810da6b695df7d926fb 100644 (file)
@@ -13,8 +13,7 @@
       <tr class="mailer_fieldrow">
         <td class="mailer_fieldname" ><var:string label:value="Subject"/>:</td>
         <td class="mailer_subjectfieldvalue">
-          <var:string value="clientObject.subject"
-            formatter="context.mailSubjectFormatter"/>
+          <var:string value="messageSubject"/>
         </td>
       </tr>
       <tr class="mailer_fieldrow">
index ba7d525e4210d64f8e5d866e63b7b55c15734830..dc2a9a021c79529335115fa8bf1d720489ad1b63 100644 (file)
@@ -14,7 +14,7 @@ function validateEditorInput(sender) {
   if (field.value == "")
     errortext = errortext + labels.error_missingsubject + "\n";
 
-  if (!UIxRecipientSelectorHasRecipients())
+  if (!hasRecipients())
     errortext = errortext + labels.error_missingrecipients + "\n";
   
   if (errortext.length > 0) {
index 6e4d62e042bfc605e2c88ab620c5147a4f5aa2db..a87b84a811e21edf91af41459fa56b86707a3c0d 100644 (file)
@@ -405,7 +405,7 @@ function openMailbox(mailbox, reload, idx) {
     var url = ApplicationBaseURL + encodeURI(mailbox) + "/view?noframe=1";
     var messageContent = $("messageContent");
     messageContent.update();
-    lastClickedRow = null; // from generic.js
+    lastClickedRow = -1; // from generic.js
 
     var currentMessage;
     if (!idx) {
index e896992a63cc998decfb65acb49a23b843edb7c7..361c0708dd8a4a8d6d0e2927f50180a342344ee2 100644 (file)
@@ -317,7 +317,7 @@ function eventsListCallback(http) {
     var params = parseQueryParameters(http.callbackData);
     sortKey = params["sort"];
     sortOrder = params["desc"];
-    lastClickedRow = null; // from generic.js
+    lastClickedRow = -1; // from generic.js
 
     if (http.responseText.length > 0) {
       var data = http.responseText.evalJSON(true);
index 2c8b26b7760b85edc2a7f0760ab60c02bf05998e..212f171528ca76d1f6e4902bdd284732bbfcaf06 100644 (file)
@@ -129,7 +129,7 @@ function validateEditorInput(sender) {
   if (field.value == "")
     errortext = errortext + labels["error_missingsubject"] + "\n";
 
-  if (!UIxRecipientSelectorHasRecipients())
+  if (!hasRecipients())
     errortext = errortext + labels["error_missingrecipients"] + "\n";
    
   if (errortext.length > 0) {
index 8c04320c01ef2bdc3ba548b6e4bb6b6c403f7f22..df7c1a68c82fc4941f8c88addf853150e63bf1d6 100644 (file)
@@ -23,7 +23,7 @@
  * It's required that "currentIndex" is defined in a top level context.
  *
  * Exports:
- * defines UIxRecipientSelectorHasRecipients() returning a bool for the
+ * defines hasRecipients() returning a bool for the
  * surrounding context to check.
  */
 
@@ -179,16 +179,16 @@ function getAddressIDs() {
 
   addressIDs = new Array();
 
-  addressList = $("addressList");
+  addressList = $("addressList").tBodies[0];
   rows  = addressList.childNodes;
   count = rows.length;
 
   for (i = 0; i < count; i++) {
     var row, rowId;
     
-    row = addressList.childNodes[i];
+    row = rows[i];
     rowId = row.id;
-    if (rowId && rowId != 'row_last') {
+    if (rowId && rowId != 'lastRow') {
       var idx;
 
       idx = this.getIndexFromIdentifier(rowId);
@@ -215,7 +215,7 @@ function getAddressCount() {
   return addressCount;
 }
 
-function UIxRecipientSelectorHasRecipients() {
+function hasRecipients() {
   var count;
   
   count = this.getAddressCount();
index 044347e60953a19b7932eb33eb59722f6bbe8e8c..5103916e34e2b9de2e58d8c1bbf8246806a6f281 100644 (file)
@@ -31,7 +31,7 @@ var menus = new Array();
 var search = {};
 var sorting = {};
 
-var lastClickedRow = null;
+var lastClickedRow = -1;
 
 var weekStartIsMonday = true;
 
@@ -495,7 +495,7 @@ function onRowClick(event) {
 
   var initialSelection = $(node.parentNode).getSelectedNodes();
   if ((event.shiftKey == 1 || event.ctrlKey == 1)
-      && lastClickedRow
+      && (lastClickedRow >= 0)
       && (acceptMultiSelect(node.parentNode)
          || acceptMultiSelect(node.parentNode.parentNode))) {
     if (event.shiftKey)