]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1317 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 16 Jan 2008 19:02:28 +0000 (19:02 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 16 Jan 2008 19:02:28 +0000 (19:02 +0000)
52 files changed:
ChangeLog
Main/SOGo.m
NEWS
SOPE/GDLContentStore/ChangeLog
SOPE/GDLContentStore/GCSFolder.h
SOPE/GDLContentStore/GCSFolder.m
SOPE/NGCards/iCalDailyRecurrenceCalculator.m
SoObjects/Appointments/SOGoAppointmentFolder.m
SoObjects/Appointments/SOGoCalendarComponent.m
SoObjects/Contacts/SOGoContactGCSEntry.m
SoObjects/Mailer/SOGoMailAccounts.m
SoObjects/Mailer/SOGoMailObject.m
SoObjects/SOGo/GNUmakefile
SoObjects/SOGo/SOGoContentObject.h
SoObjects/SOGo/SOGoContentObject.m
SoObjects/SOGo/SOGoObject.m
SoObjects/SOGo/SOGoUser.m
UI/MailerUI/UIxMailAccountActions.m
UI/Scheduler/English.lproj/Localizable.strings
UI/Scheduler/French.lproj/Localizable.strings
UI/Scheduler/GNUmakefile
UI/Scheduler/German.lproj/Localizable.strings
UI/Scheduler/UIxAppointmentEditor.h
UI/Scheduler/UIxAppointmentEditor.m
UI/Scheduler/UIxCalFilterPanel.m
UI/Scheduler/UIxCalendarSelector.h
UI/Scheduler/UIxCalendarSelector.m
UI/Scheduler/UIxComponentEditor.h
UI/Scheduler/UIxComponentEditor.m
UI/Scheduler/UIxTaskEditor.h
UI/Scheduler/UIxTaskEditor.m
UI/Scheduler/product.plist
UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox
UI/Templates/SchedulerUI/UIxAppointmentEditor.wox
UI/Templates/SchedulerUI/UIxAttendeesEditor.wox
UI/Templates/SchedulerUI/UIxComponentEditor.wox
UI/Templates/SchedulerUI/UIxTaskEditor.wox
UI/Templates/UIxPageFrame.wox
UI/WebServerResources/ContactsUI.css
UI/WebServerResources/HTMLElement.js
UI/WebServerResources/MailerUI.css
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxAppointmentEditor.css
UI/WebServerResources/UIxAppointmentEditor.js
UI/WebServerResources/UIxAttendeesEditor.css
UI/WebServerResources/UIxAttendeesEditor.js
UI/WebServerResources/UIxComponentEditor.js
UI/WebServerResources/UIxContactsUserFolders.js
UI/WebServerResources/generic.js
UI/WebServerResources/iefixes.css
UI/WebServerResources/skycalendar.js

index 8832f35d1f236d245aea23bee367d8105d531d33..09f2ec57c224c5c0bcf47a3107c3e1b61c66e138 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+2008-01-16  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/SOGo/SOGoUser.m ([SOGoUser
+       +userWithLogin:newLoginroles:newRoles]): try to retrieve the
+       specified user from the SOGoCache.
+
+       * SoObjects/SOGo/SOGoObject.m ([SOGoObject
+       -lookupName:lookupNameinContext:localContextacquire:acquire]): try
+       to retrieve the object specified from the cache and returns it if
+       it exists.
+
+       * SoObjects/Appointments/SOGoAppointmentFolder.m
+       ([SOGoAppointmentFolder -lookupName:inContext:acquire:]): cache
+       the result in the SOGoCache.
+
+       * Main/SOGo.m ([SOGo -dispatchRequest:_request]): initialize and
+       kill the shared cache before and after the connection processing.
+
+       * SoObjects/SOGo/SOGoCache.[hm]: new cache module implementing a
+       per-connection cache mechanism.
+
+2008-01-16  Ludovic Marcotte <ludovic@inverse.ca>
+       
+       * Minor adjustments / bug fixes to previous commit.
+
+2008-01-14  Ludovic Marcotte <ludovic@inverse.ca>
+
+       * Added files related to the custom recurrence
+         editor of the SOGo Web interface. The CSS
+         needs to be done correctly.
+
+       * Fixed a bug in the daily recurrence generator.
+         We now consider the byDayMask, if any.
+
+       * Moved the repeat/reminder code to the
+         UIxComponentEditor class / template.
+
+       * Added a few JavaScript methods to HTMLElement.js
+
+2008-01-08  Francis Lachapelle  <flachapelle@inverse.ca>
+
+       * UI/MailerUI/UIxMailAccountActions.m ([UIxMailAccountActions
+       -composeAction]): the mailto form parameter can now be a
+       comma-separated list of email addresses.
+
 2007-12-21  Ludovic Marcotte <ludovic@inverse.ca>
        
        * UI/Contacts/UIxContactView.m
index ac530d5619665c9a020a5fb91206e80acaaf086a..afb19b095bf06301840945c788400c385a64d516 100644 (file)
@@ -42,6 +42,7 @@
 
 #import <WEExtensions/WEResourceManager.h>
 
+#import <SoObjects/SOGo/SOGoCache.h>
 #import <SoObjects/SOGo/SOGoDAVAuthenticator.h>
 #import <SoObjects/SOGo/SOGoPermissions.h>
 #import <SoObjects/SOGo/SOGoUserFolder.h>
@@ -56,6 +57,7 @@
 @interface SOGo : SoApplication
 {
   NSMutableDictionary *localeLUT;
+  SOGoCache *cache;
 }
 
 - (NSDictionary *) currentLocaleConsideringLanguages:(NSArray *)_langs;
@@ -390,7 +392,9 @@ static BOOL debugObjectAllocation = NO;
   static NSArray *runLoopModes = nil;
   WOResponse *resp;
 
+  cache = [SOGoCache sharedCache];
   resp = [super dispatchRequest: _request];
+  [SOGoCache killCache];
 
   if (![self isTerminating])
     {
diff --git a/NEWS b/NEWS
index 89447f0b272720d060890cc06516882c75551d9a..da5414179d9a6a7b3649a0ee8b50790dad37e5e8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+0.9.0-2008MMDD (1.0 rc4)
+------------------------
+- improved the attendees window;
+
 0.9.0-20071217 (1.0 rc3)
 ------------------------
 - mail folders state is now saved;
index af7d8047ce8ca98127d74fa67f1fce40f74558df..eff43347e9fcd8d400adb929786b0fbc734d75ac 100644 (file)
@@ -1,3 +1,8 @@
+2008-01-16  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * GCSFolder.m: fetch the content, version and dates at the same
+       time per record, to avoid multiple queries.
+
 2007-12-12  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * GCSFolder.m ([GCSFolder -creationDateOfEntryWithName:]): new
index 73b608cc4abef211b9f005753a268a9393f78bc5..7e5ea61bda1a27a980b150fef882ea498997d6ae 100644 (file)
 - (NSArray *)subFolderNames;
 - (NSArray *)allSubFolderNames;
 
-- (NSNumber *)versionOfContentWithName:(NSString *)_name;
-- (NSCalendarDate *)creationDateOfEntryWithName:(NSString *)_name;
-- (NSCalendarDate *)lastModificationOfEntryWithName:(NSString *)_name;
+- (NSDictionary *) recordOfEntryWithName: (NSString *) name;
 
-- (NSString *)fetchContentWithName:(NSString *)_name;
 - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name
   baseVersion:(unsigned int)_baseVersion;
 - (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name;
index f47591e7d2b9d9d6883ee5e0cb87eb46be93463f..552c322b023859d4ed66f01c03d121912261892a 100644 (file)
@@ -251,15 +251,14 @@ static GCSStringFormatter *stringFormatter = nil;
                               recursive:YES];
 }
 
-- (id) _fetchValueOfColumn: (NSString *)_col
-        inContentWithName: (NSString *)_name
-            ignoreDeleted: (BOOL) ignoreDeleted
+- (NSDictionary *) _fetchValueOfColumns: (NSArray *) _cols
+                     inContentWithName: (NSString *) _name
+                         ignoreDeleted: (BOOL) ignoreDeleted
 {
   EOAdaptorChannel *channel;
   NSException  *error;
   NSDictionary *row;
   NSArray      *attrs;
-  NSString     *result;
   NSMutableString     *sql;
   
   if ((channel = [self acquireStoreChannel]) == nil) {
@@ -271,12 +270,13 @@ static GCSStringFormatter *stringFormatter = nil;
   sql = [NSMutableString stringWithFormat: @"SELECT %@"
                         @" FROM %@"
                         @" WHERE c_name = '%@'",
-                        _col, [self storeTableName], _name];
+                        [_cols componentsJoinedByString: @","],
+                        [self storeTableName], _name];
   if (ignoreDeleted)
     [sql appendString: @" AND (c_deleted != 1 OR c_deleted IS NULL)"];
 
   /* run SQL */
-  
+
   if ((error = [channel evaluateExpressionX:sql]) != nil) {
     [self errorWithFormat:@"%s: cannot execute SQL '%@': %@", 
          __PRETTY_FUNCTION__, sql, error];
@@ -286,51 +286,64 @@ static GCSStringFormatter *stringFormatter = nil;
   
   /* fetch results */
   
-  result = nil;
   attrs  = [channel describeResults:NO /* do not beautify names */];
-  if ((row = [channel fetchAttributes:attrs withZone:NULL]) != nil) {
-    result = [[[row objectForKey:_col] copy] autorelease];
-    if (![result isNotNull]) result = nil;
+  row = [channel fetchAttributes: attrs withZone: NULL];
+  if (row)
     [channel cancelFetch];
-  }
   
   /* release and return result */
   
   [self releaseChannel:channel];
-  return result;
-}
 
-- (NSNumber *)versionOfContentWithName:(NSString *)_name {
-  return [self _fetchValueOfColumn:@"c_version" inContentWithName:_name
-              ignoreDeleted: YES];
+  return row;
 }
 
-- (NSCalendarDate *)creationDateOfEntryWithName:(NSString *)_name {
-  int seconds;
-
-  seconds = [[self _fetchValueOfColumn:@"c_creationdate" inContentWithName:_name
-                  ignoreDeleted: YES] intValue];
+- (NSDictionary *) recordOfEntryWithName: (NSString *) name
+{
+  NSDictionary *row;
+  NSMutableDictionary *record;
+  NSArray *columns;
+  NSString *strValue;
+  int intValue;
+
+  columns = [NSArray arrayWithObjects: @"c_content", @"c_version",
+                    @"c_creationdate", @"c_lastmodified", nil];
+  row = [self _fetchValueOfColumns: columns
+             inContentWithName: name
+             ignoreDeleted: YES];
+  if (row)
+    {
+      record = [NSMutableDictionary dictionaryWithCapacity: 5];
+      strValue = [row objectForKey: @"c_content"];
+      if (![strValue isNotNull])
+       strValue = @"";
+      [record setObject: strValue forKey: @"c_content"];
+      [record setObject: [row objectForKey: @"c_version"]
+             forKey: @"c_version"];
+      intValue = [[row objectForKey: @"c_creationdate"] intValue];
+      [record
+       setObject: [NSCalendarDate dateWithTimeIntervalSince1970: intValue]
+       forKey: @"c_creationdate"];
+      intValue = [[row objectForKey: @"c_lastmodified"] intValue];
+      [record
+       setObject: [NSCalendarDate dateWithTimeIntervalSince1970: intValue]
+       forKey: @"c_lastmodified"];
+    }
+  else
+    record = nil;
 
-  return [NSCalendarDate dateWithTimeIntervalSince1970: seconds];
+  return record;
 }
 
-- (NSCalendarDate *)lastModificationOfEntryWithName:(NSString *)_name {
-  int seconds;
-
-  seconds = [[self _fetchValueOfColumn:@"c_lastmodified" inContentWithName:_name
-                  ignoreDeleted: YES] intValue];
-
-  return [NSCalendarDate dateWithTimeIntervalSince1970: seconds];
-}
+- (NSNumber *) deletionOfEntryWithName: (NSString *) name
+{
+  NSDictionary *row;
 
-- (NSNumber *)deletionOfContentWithName:(NSString *)_name {
-  return [self _fetchValueOfColumn:@"c_deleted" inContentWithName:_name
-              ignoreDeleted: NO];
-}
+  row = [self _fetchValueOfColumns: [NSArray arrayWithObject: @"c_deleted"]
+             inContentWithName: name
+             ignoreDeleted: NO];
 
-- (NSString *)fetchContentWithName:(NSString *)_name {
-  return [self _fetchValueOfColumn:@"c_content" inContentWithName:_name
-              ignoreDeleted: YES];
+  return [row objectForKey: @"c_deleted"];
 }
 
 - (NSDictionary *)fetchContentsOfAllFiles {
@@ -571,6 +584,7 @@ static GCSStringFormatter *stringFormatter = nil;
 {
   EOAdaptorChannel    *storeChannel, *quickChannel;
   NSMutableDictionary *quickRow, *contentRow;
+  NSDictionary       *currentRow;
   GCSFieldExtractor   *extractor;
   NSException         *error;
   NSNumber            *storedVersion;
@@ -598,19 +612,24 @@ static GCSStringFormatter *stringFormatter = nil;
   if (doLogStore)
     [self logWithFormat:@"should store content: '%@'\n%@", _name, _content];
   
-  storedVersion = [self versionOfContentWithName:_name];
+  currentRow = [self _fetchValueOfColumns: [NSArray arrayWithObjects:
+                                                     @"c_version",
+                                                   @"c_deleted", nil]
+                    inContentWithName: _name
+                    ignoreDeleted: NO];
+  storedVersion = [currentRow objectForKey: @"c_version"];
   if (doLogStore)
     [self logWithFormat:@"  version: %@", storedVersion];
   isNewRecord = [storedVersion isNotNull] ? NO : YES;
   if (!isNewRecord)
     {
-      if ([[self deletionOfContentWithName:_name] intValue] > 0)
+      if ([[currentRow objectForKey: @"c_deleted"] intValue] > 0)
        {
          [self _purgeRecordWithName: _name];
          isNewRecord = YES;
        }
     }
-  
+
   /* check whether sequence matches */  
   if (_baseVersion != 0 /* use 0 to override check */) {
     if (_baseVersion != [storedVersion unsignedIntValue]) {
index 17a1d483e92f67bb44e340e05a2b5e71e71d9ad7..7c7d2656c01eeeffc790de9d52e36a14df408557 100644 (file)
@@ -33,6 +33,8 @@
 #import "iCalRecurrenceRule.h"
 #import "NSCalendarDate+ICal.h"
 
+#include <math.h>
+
 @interface iCalRecurrenceCalculator(PrivateAPI)
 - (NSCalendarDate *)lastInstanceStartDate;
 @end
       if ((jnTest % interval) == 0) {
         NSCalendarDate      *start, *end;
         NGCalendarDateRange *r;
-      
+       unsigned int mask;
+
         start = [NSCalendarDate dateForJulianNumber:jnCurrent];
         [start setTimeZone:[firStart timeZone]];
         start = [start hour:  [firStart hourOfDay]
                        minute:[firStart minuteOfHour]
                        second:[firStart secondOfMinute]];
         end   = [start addTimeInterval:[self->firstRange duration]];
+
+       // We check if our start date is within the byDayMask 
+       // FIXME: Should we also check the end date? We might want
+       //        to check if the end date is also within it.
+       if ([self->rrule byDayMask]) {
+         mask = [start dayOfWeek] == 0 ? iCalWeekDaySunday : (unsigned int)exp2([start dayOfWeek]-1);
+         if (([self->rrule byDayMask]&mask) != mask) continue;
+       }
+
         r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
                                      endDate:end];
         if ([_r containsDateRange:r])
index 5be6e558889fc3ea424363505f4befc62c1ba08d..da40ac840eefc43a17d64c5d091bb06d8794f819 100644 (file)
@@ -48,6 +48,7 @@
 #import <SaxObjC/XMLNamespaces.h>
 
 // #import <NGObjWeb/SoClassSecurityInfo.h>
+#import <SOGo/SOGoCache.h>
 #import <SOGo/SOGoCustomGroupFolder.h>
 #import <SOGo/LDAPUserManager.h>
 #import <SOGo/SOGoPermissions.h>
@@ -403,6 +404,11 @@ static NSNumber   *sharedYes = nil;
         obj = [NSException exceptionWithHTTPStatus:404 /* Not Found */];
     }
 
+  if (obj)
+    [[SOGoCache sharedCache] registerObject: obj
+                            withName: _key
+                            inContainer: container];
+
   return obj;
 }
 
index c8ed4eb3a8ea70a3778dce28940751eaef6d9f99..33dd2b6e63ffc0d8501c38f91fdf6a51d23a6f53 100644 (file)
@@ -126,7 +126,7 @@ static BOOL sendEMailNotifications = NO;
   sm = [SoSecurityManager sharedSecurityManager];
   if (![sm validatePermission: SOGoCalendarPerm_ViewAllComponent
           onObject: self inContext: context])
-    iCalString = content;
+    iCalString = [record objectForKey: @"c_content"];
   else if (![sm validatePermission: SOGoCalendarPerm_ViewDAndT
                onObject: self inContext: context])
     {
@@ -165,7 +165,7 @@ static BOOL sendEMailNotifications = NO;
   if (secure)
     iCalString = [self secureContentAsString];
   else
-    iCalString = content;
+    iCalString = [record objectForKey: @"c_content"];
 
   if ([iCalString length] > 0)
     calendar = [iCalCalendar parseSingleFromSource: iCalString];
index b56e5a92868368105e0535c6bf4f58c088610631..b4b8be092490e9eb98f1867b9dab5b2ebf71a236 100644 (file)
 
 - (NGVCard *) vCard
 {
+  NSString *content;
+
   if (!card)
     {
+      content = [record objectForKey: @"c_content"];
       if ([[content uppercaseString] hasPrefix: @"BEGIN:VCARD"])
         card = [NGVCard parseSingleFromSource: content];
       else
index b09e9092b1b3f09111c7c8af041070c840ff18b7..515c665236e7cf1b2e6b0e284dba8aed1f78cc95 100644 (file)
@@ -104,7 +104,7 @@ static NSString *AgenorShareLoginMarker  = @".-.";
   /* first check attributes directly bound to the application */
   if ((obj = [super lookupName:_key inContext:_ctx acquire:NO]))
     return obj;
-  
+
   if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
     [self warnWithFormat:@ "User %@ tried to access mail hierarchy of %@",
          userLogin, [container nameInContainer]];
index 6c38725f0a65d8a3c5e32fdcd442c8570140810d..ec0c280368132996161e72cf2829ce47d6754b56 100644 (file)
@@ -850,7 +850,7 @@ static BOOL debugSoParts       = NO;
     }
   else
     clazz = Nil;
-    
+
   return [clazz objectWithName:_key inContainer: self];
 }
 
index c99c714e0b052ccf1985d96722c2e17b9aa83ff4..526cb2264c92cd3e9a23c3cf3b55555035116400 100644 (file)
@@ -20,6 +20,7 @@ libSOGo_HEADER_FILES_INSTALL_DIR = /SOGo
 FHS_HEADER_DIRS = SOGo
 
 libSOGo_HEADER_FILES = \
+       SOGoCache.h                     \
        SOGoObject.h                    \
        SOGoContentObject.h             \
        SOGoFolder.h                    \
@@ -56,6 +57,7 @@ libSOGo_HEADER_FILES = \
        WORequest+SOGo.h
 
 libSOGo_OBJC_FILES = \
+       SOGoCache.m                     \
        SOGoObject.m                    \
        SOGoContentObject.m             \
        SOGoFolder.m                    \
index 807b98745437ca19d856a6beed65641da540dd4a..3f4cb53ef4910bddfa0a3bee9d1662b6ae06199e 100644 (file)
@@ -31,7 +31,7 @@
 @interface SOGoContentObject : SOGoObject
 {
   NSString *ocsPath;
-  NSString *content;
+  NSDictionary *record;
   BOOL isNew;
 }
 
index 9665eda90ef1b5a98f1d8cc53d694160b7adea69..4aa48503560e8f876c604fff06102bcefd116477 100644 (file)
@@ -49,9 +49,9 @@
   if ((self = [super initWithName: newName inContainer: newContainer]))
     {
       ocsPath = nil;
-      content = [[self ocsFolder] fetchContentWithName: newName];
-      [content retain];
-      isNew = (!content);
+      record = [[self ocsFolder] recordOfEntryWithName: newName];
+      [record retain];
+      isNew = (!record);
     }
 
   return self;
 
 - (void) dealloc
 {
-  [content release];
+  [record release];
   [ocsPath release];
   [super dealloc];
 }
 
-/* notifications */
-
-- (void) sleep
-{
-  [content release]; content = nil;
-  [super sleep];
-}
-
 /* accessors */
 
 - (BOOL) isFolderish
 
 - (NSString *) contentAsString
 {
-  return content;
+  return [record objectForKey: @"c_content"];
 }
 
 - (NSException *) saveContentString: (NSString *) newContent
                         baseVersion: (unsigned int) newBaseVersion
 {
   /* Note: "iCal multifolder saves" are implemented in the apt subclass! */
-  GCSFolder   *folder;
+  GCSFolder *folder;
   NSException *ex;
+  NSMutableDictionary *newRecord;
 
   ex = nil;
 
-  ASSIGN (content, newContent);
+  if (record)
+    newRecord = [NSMutableDictionary dictionaryWithDictionary: record];
+  else
+    newRecord = [NSMutableDictionary dictionary];
+  [newRecord setObject: newContent forKey: @"c_content"];
+  ASSIGN (record, newRecord);
 
   folder = [container ocsFolder];
   if (folder)
   folder = [self ocsFolder];
   if (folder)
     {
-      versionValue = [folder versionOfContentWithName: [self nameInContainer]];
+      versionValue = [record objectForKey: @"c_version"];
       sprintf (buf, "\"gcs%08d\"", [versionValue unsignedIntValue]);
       entityTag = [NSString stringWithCString: buf];
     }
 {
   NSCalendarDate *date;
 
-  date = [[self ocsFolder] creationDateOfEntryWithName: nameInContainer];
+  date = [record objectForKey: @"c_creationdate"];
 
   return [date rfc822DateString];
 }
 {
   NSCalendarDate *date;
 
-  date = [[self ocsFolder] lastModificationOfEntryWithName: nameInContainer];
+  date = [record objectForKey: @"c_lastmodified"];
 
   return [date rfc822DateString];
 }
 
 - (NSString *) davContentLength
 {
+  NSString *content;
+
+  content = [record objectForKey: @"c_content"];
+
   return [NSString stringWithFormat: @"%u",
-                  [content
-                    lengthOfBytesUsingEncoding: NSISOLatin1StringEncoding]];
+                  [content lengthOfBytesUsingEncoding: NSISOLatin1StringEncoding]];
 }
 
 - (NSException *) davMoveToTargetObject: (id) _target
index 8bb510661e65ee54f916f9fd4300633634df1c5b..9c5fbcee648b727c7c22d745046f25f7f599f978 100644 (file)
@@ -62,6 +62,7 @@
 #import "NSDictionary+Utilities.h"
 #import "NSString+Utilities.h"
 
+#import "SOGoCache.h"
 #import "SOGoObject.h"
 
 @interface SOGoObject(Content)
@@ -560,10 +561,19 @@ static BOOL kontactGroupDAV = YES;
           acquire: (BOOL) acquire
 {
   id obj;
+  SOGoCache *cache;
 
-  obj = [[self soClass] lookupKey: lookupName inContext: localContext];
-  if (obj)
-    [obj bindToObject: self inContext: localContext];
+  cache = [SOGoCache sharedCache];
+  obj = [cache objectNamed: lookupName inContainer: self];
+  if (!obj)
+    {
+      obj = [[self soClass] lookupKey: lookupName inContext: localContext];
+      if (obj)
+       {
+         [obj bindToObject: self inContext: localContext];
+         [cache registerObject: obj withName: lookupName inContainer: self];
+       }
+    }
 
   return obj;
 }
index 9c7bd9d3d99a73084b8522f17d799edc7f92aa3e..fe0aa2a6aedbea247e190bba8076fd9a53c9e154 100644 (file)
 
 #import "AgenorUserDefaults.h"
 #import "LDAPUserManager.h"
+#import "NSArray+Utilities.h"
+#import "SOGoCache.h"
 #import "SOGoDateFormatter.h"
 #import "SOGoObject.h"
 #import "SOGoPermissions.h"
-#import "NSArray+Utilities.h"
 
 #import "SOGoUser.h"
 
@@ -124,10 +125,18 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
 + (SOGoUser *) userWithLogin: (NSString *) newLogin
                       roles: (NSArray *) newRoles
 {
+  SOGoCache *cache;
   SOGoUser *user;
 
-  user = [[self alloc] initWithLogin: newLogin roles: newRoles];
-  [user autorelease];
+  cache = [SOGoCache sharedCache];
+  user = [cache userNamed: newLogin];
+  if (!user)
+    {
+      user = [[self alloc] initWithLogin: newLogin roles: newRoles];
+      [user autorelease];
+      [cache registerUser: user];
+    }
+  [user setPrimaryRoles: newRoles];
 
   return user;
 }
@@ -181,6 +190,11 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
   [super dealloc];
 }
 
+- (void) setPrimaryRoles: (NSArray *) newRoles
+{
+  ASSIGN (roles, newRoles);
+}
+
 - (void) setCurrentPassword: (NSString *) newPassword
 {
   ASSIGN (currentPassword, newPassword);
index ea243bca77aa617dccc606cb6c5b9edafec958d9..cb54d667b8039afebe286a7b29a6790964b2b43e 100644 (file)
@@ -1,6 +1,6 @@
 /* UIxMailAccountActions.m - this file is part of SOGo
  *
- * Copyright (C) 2007 Inverse groupe conseil
+ * Copyright (C) 2007, 2008 Inverse groupe conseil
  *
  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
  *
   value = [[self request] formValueForKey: @"mailto"];
   if ([value length] > 0)
     {
-      mailTo = [NSArray arrayWithObject: value];
+      mailTo = [value componentsSeparatedByString: @","];
       [newDraftMessage
        setHeaders: [NSDictionary dictionaryWithObject: mailTo
                                  forKey: @"to"]];
index c6e1c5b0bde20b533b6addf85898a49aaca62b52..21caedce7f0c7c045656af1394529a3ed2b2a006 100644 (file)
 "Friday" = "Friday";
 "Saturday" = "Saturday";
 
+"Sun" = "Sun";
+"Mon" = "Mon";
+"Tue" = "Tue";
+"Wed" = "Wed";
+"Thu" = "Thu";
+"Fri" = "Fri";
+"Sat" = "Sat";
+
 "a2_Sunday" = "Su";
 "a2_Monday" = "Mo";
 "a2_Tuesday" = "Tu";
@@ -87,6 +95,9 @@
 "Reload Remote Calendars" = "Reload Remote Calendars";
 "Properties" = "Properties";
 
+"Compose E-Mail to All Attendees" = "Compose E-Mail to All Attendees";
+"Compose E-Mail to Undecided Attendees" = "Compose E-Mail to Undecided Attendees";
+
 /* Folders */
 "Personal calendar" = "Personal calendar";
 
 "cycle_end_never" = "cycle_end_never";
 "cycle_end_until" = "cycle_end_until";
 
+"Recurrence pattern" = "Recurrence pattern";
+"Range of recurrence" = "Range of recurrence";
+
+"Repeat" = "Repeat";
+"Daily" = "Daily";
+"Weekly" = "Weekly";
+"Monthly" = "Monthly";
+"Yearly" = "Yearly";
+"Every" = "Every";
+"Days" = "Days";
+"Week(s)" = "Week(s)";
+"On" = "On";
+"Month(s)" = "Month(s)";
+"The" = "The";
+"Recur on day(s)" = "Recur on day(s)";
+"Year(s)" = "Year(s)";
+"cycle_of" = "of";
+"No end date" = "No end date";
+"Create" = "Create";
+"Appointment(s)" = "Appointment(s)";
+"Repeat until" = "Repeat until";
+
+"First" = "First";
+"Second" = "Second";
+"Third" = "Third";
+"Fourth" = "Fourth";
+"Fift" = "Fift";
+"Last" = "Last";
+
 /* Appointment categories */
 
 "category_NONE" = "None";
 validate_notitle           = "No title is set, continue?";
 validate_invalid_startdate = "Incorrect startdate field!";
 validate_invalid_enddate   = "Incorrect enddate field!";
-validate_endbeforestart    = "The end date that you enteterd occurs before the start date.";
+validate_endbeforestart    = "The end date that you entered occurs before the start date.";
 
 "Tasks" = "Tasks";
 "Show completed tasks" = "Show completed tasks";
@@ -424,4 +464,4 @@ vtodo_class2 = "(Confidential task)";
 "closeThisWindowMessage" = "Thank you! You may now close this window or view your ";
 "Multicolumn Day View" = "Multicolumn Day View";
 
-"Please select an event or a task." = "Please select an event or a task.";
\ No newline at end of file
+"Please select an event or a task." = "Please select an event or a task.";
index 725f035841ff47cd6426f53760b25733f90c5172..2ade48742f9c6613429f2d20f318ac8f7f63e6b6 100644 (file)
 "Friday" = "Vendredi";
 "Saturday" = "Samedi";
 
+"Sun" = "Dim";
+"Mon" = "Lun";
+"Tue" = "Mar";
+"Wed" = "Mer";
+"Thu" = "Jeu";
+"Fri" = "Ven";
+"Sat" = "Sam";
+
 "a2_Sunday" = "Di";
 "a2_Monday" = "Lu";
 "a2_Tuesday" = "Ma";
@@ -88,6 +96,9 @@
 "Reload Remote Calendars" = "Recharger les agendas distants";
 "Properties" = "Propriétés";
 
+"Compose E-Mail to All Attendees" = "Rédiger un courriel pour tous les participants";
+"Compose E-Mail to Undecided Attendees" = "Rédiger un courriel pour les participants indécis";
+
 /* Folders */
 "Personal calendar" = "Agenda personnel";
 
 "cycle_end_never" = "Jamais";
 "cycle_end_until" = "À la date :";
 
+"Recurrence pattern" = "Définir la fréquence";
+"Range of recurrence" = "Fenêtre de répétition";
+
+"Repeat" = "Répétition";
+"Daily" = "quotidienne";
+"Weekly" = "hebdomadaire";
+"Monthly" = "mensuelle";
+"Yearly" = "annuelle";
+"Every" = "Chaque";
+"Days" = "Jours";
+"Week(s)" = "Semaine(s)";
+"On" = "Le";
+"Month(s)" = "Mois";
+"The" = "Le";
+"Recur on day(s)" = "Se répète le(s) jour(s)";
+"Year(s)" = "Année(s)";
+"cycle_of" = "de";
+"No end date" = "Pas de date de fin";
+"Create" = "Créer";
+"Appointment(s)" = "Rendez-vous";
+"Repeat until" = "Répéter jusqu'à";
+
+"First" = "premier";
+"Second" = "deuxieme";
+"Third" = "troisieme";
+"Fourth" = "quatrieme";
+"Fift" = "cinquieme";
+"Last" = "dernier";
+
 /* Appointment categories */
 
 "category_NONE" = "Aucune";
 "category_VACATION" = "Absence";
 
 "repeat_NEVER" = "Ne se répète pas";
-"repeat_DAILY" = "quotidiennement";
-"repeat_WEEKLY" = "hebdomadairement";
-"repeat_BI-WEEKLY" = "bi-hebdomadairement";
-"repeat_EVERY WEEKDAY" = "les jours ouvrables";
-"repeat_MONTHLY" = "mensuellement";
-"repeat_YEARLY" = "annuellement";
+"repeat_DAILY" = "Quotidiennement";
+"repeat_WEEKLY" = "Hebdomadairement";
+"repeat_BI-WEEKLY" = "Bi-hebdomadairement";
+"repeat_EVERY WEEKDAY" = "Chaque jour ouvrable";
+"repeat_MONTHLY" = "Mensuellement";
+"repeat_YEARLY" = "Annuellement";
 "repeat_CUSTOM" = "Personnaliser...";
 
 "reminder_NONE" = "Pas de rappel";
index aab7f02884120cae44391e9f326f95fafe57bf96..92f59324f634aaf4bf1d5fcf46bf0c890af25f5e 100644 (file)
@@ -41,7 +41,8 @@ SchedulerUI_OBJC_FILES =              \
        UIxTimeDateControl.m            \
        UIxCalParticipationStatusView.m \
        UIxCalMonthOverview.m   \
-       UIxCalMonthViewOld.m
+       UIxCalMonthViewOld.m \
+       UIxRecurrenceEditor.m
 
 SchedulerUI_RESOURCE_FILES += \
        Version         \
index 15ab314545721bab705c1421c904825a409b37bf..2991e7718eacfed54a5f39fd0586caf97c8f320b 100644 (file)
 "Friday" = "Freitag";
 "Saturday" = "Samstag";
 
+"Sun" = "Son";
+"Mon" = "Mon";
+"Tue" = "Die";
+"Wed" = "Mit";
+"Thu" = "Don";
+"Fri" = "Fre";
+"Sat" = "Sam";
+
 "a2_Sunday" = "So";
 "a2_Monday" = "Mo";
 "a2_Tuesday" = "Di";
@@ -76,6 +84,9 @@
 "Reload Remote Calendars" = "Externe Kalender neu laden";
 "Properties" = "Einstellungen";
 
+"Compose E-Mail to All Attendees" = "E-Mail an alle Teilnehmer erstellen";
+"Compose E-Mail to Undecided Attendees" = "E-Mail an unentschlossene Teilnehmer erstellen";
+
 /* Folders */
 "Personal calendar" = "Personal calendar";
 
 "cycle_end_never" = "Unendlich oft wiederholen";
 "cycle_end_until" = "Wiederholen bis :";
 
+"Recurrence pattern" = "Wiederholungsschema";
+"Range of recurrence" = "Bereich der Wiederholung";
+
+"Repeat" = "Wiederhole";
+"Daily" = "Daily";
+"Weekly" = "Weekly";
+"Monthly" = "Monthly";
+"Yearly" = "Yearly";
+"Every" = "Jeden";
+"Days" = "Tage";
+"Week(s)" = "Woche(n)";
+"On" = "On";
+"Month(s)" = "Monat(e)";
+"The" = "Der";
+"Recur on day(s)" = "Wiederholung an Tag(en) ";
+"Year(s)" = "Jahr(e)";
+"cycle_of" = "of";
+"No end date" = "Kein Enddatum";
+"Create" = "Erstellen";
+"Appointment(s)" = "Verabredung(en)";
+"Repeat until" = "Wiederhole bis";
+
+"First" = "Erste";
+"Second" = "Zweite";
+"Third" = "Dritte";
+"Fourth" = "Vierte";
+"Fift" = "Fünfte";
+"Last" = "Letzter";
+
 /* Appointment categories */
 
 "category_NONE" = "Keine";
index c3ead685618170fd679d15bc0710fe83ee619e18..fba6ed39b3f55c634fb2a985df578e859b3c9bd0 100644 (file)
@@ -35,7 +35,6 @@
   NSCalendarDate *aptStartDate;
   NSCalendarDate *aptEndDate;
   NSString *item;
-  NSString *repeat;
 }
 
 /* template values */
 - (void) setAptEndDate: (NSCalendarDate *) _date;
 - (NSCalendarDate *) aptEndDate;
 
-- (NSString *) repeat;
-- (void) setRepeat: (NSString *) newRepeat;
-
-- (NSString *) reminder;
-- (void) setReminder: (NSString *) newReminder;
-
 @end
 
 #endif /* UIXAPPOINTMENTEDITOR_H */
index 382ff96d17a9c2dc19a05cef005f4730a5acf462..a1c33099d83649d6d544c5e29c9b82e96f80f833 100644 (file)
@@ -51,7 +51,6 @@
       aptEndDate = nil;
       item = nil;
       event = nil;
-      repeat = nil;
       isAllDay = NO;
     }
 
@@ -61,7 +60,6 @@
 - (void) dealloc
 {
   [item release];
-  [repeat release];
   [aptStartDate release];
   [aptEndDate release];
   [super dealloc];
   return aptEndDate;
 }
 
-- (NSArray *) repeatList
-{
-  static NSArray *repeatItems = nil;
-
-  if (!repeatItems)
-    {
-      repeatItems = [NSArray arrayWithObjects: @"DAILY",
-                             @"WEEKLY",
-                             @"BI-WEEKLY",
-                             @"EVERY WEEKDAY",
-                             @"MONTHLY",
-                             @"YEARLY",
-                             @"-",
-                             @"CUSTOM",
-                             nil];
-      [repeatItems retain];
-    }
-
-  return repeatItems;
-}
-
-- (NSString *) itemRepeatText
-{
-  NSString *text;
-
-  if ([item isEqualToString: @"-"])
-    text = item;
-  else
-    text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]];
-
-  return text;
-}
-
 - (void) setItem: (NSString *) newItem
 {
   ASSIGN (item, newItem);
   return item;
 }
 
-- (NSArray *) reminderList
-{
-  static NSArray *reminderItems = nil;
-
-  if (!reminderItems)
-    {
-      reminderItems = [NSArray arrayWithObjects: @"5_MINUTES_BEFORE",
-                               @"10_MINUTES_BEFORE",
-                               @"15_MINUTES_BEFORE",
-                               @"30_MINUTES_BEFORE",
-                               @"45_MINUTES_BEFORE",
-                               @"-",
-                               @"1_HOUR_BEFORE",
-                               @"2_HOURS_BEFORE",
-                               @"5_HOURS_BEFORE",
-                               @"15_HOURS_BEFORE",
-                               @"-",
-                               @"1_DAY_BEFORE",
-                               @"2_DAYS_BEFORE",
-                               @"1_WEEK_BEFORE",
-                               @"-",
-                               @"CUSTOM",
-                               nil];
-      [reminderItems retain];
-    }
-
-  return reminderItems;
-}
-
-// - (void) setReminder: (NSString *) reminder
-// {
-//   ASSIGN(reminder, _reminder);
-// }
-
-// - (NSString *) reminder
-// {
-//   return reminder;
-// }
-
-- (NSString *) reminder
-{
-  return @"";
-}
-
-- (void) setReminder: (NSString *) newReminder
-{
-}
-
-- (NSString *) itemReminderText
-{
-  NSString *text;
-
-  if ([item isEqualToString: @"-"])
-    text = item;
-  else
-    text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]];
-
-  return text;
-}
-
-- (NSString *) repeat
-{
-  return repeat;
-}
-
-- (void) setRepeat: (NSString *) newRepeat
-{
-  ASSIGN (repeat, newRepeat);
-}
-
 /* actions */
 - (NSCalendarDate *) newStartDate
 {
   NSCalendarDate *startDate, *endDate;
   NSString *duration;
   unsigned int minutes;
-  iCalRecurrenceRule *rule;
 
   [self event];
   if ([[self clientObject] isNew])
   ASSIGN (aptStartDate, startDate);
   ASSIGN (aptEndDate, endDate);
 
-  // We initialize our repeat ivars
-  if ([event hasRecurrenceRules])
-    {
-      repeat = @"CUSTOM";
-
-      rule = [[event recurrenceRules] lastObject];
-
-      if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
-       {
-         if ([rule repeatInterval] == 1)
-           repeat = @"WEEKLY";
-         else if ([rule repeatInterval] == 2)
-           repeat = @"BI-WEEKLY";
-       }
-      else if ([rule frequency] == iCalRecurrenceFrequenceDaily)
-       {
-         if ([rule byDayMask] == (iCalWeekDayMonday
-                                  | iCalWeekDayTuesday
-                                  | iCalWeekDayWednesday
-                                  | iCalWeekDayThursday
-                                  | iCalWeekDayFriday))
-           repeat = @"EVERY WEEKDAY";
-         else if (![rule byDayMask])
-           repeat = @"DAILY";
-       }
-      else if ([rule frequency] == iCalRecurrenceFrequenceMonthly
-              && [rule repeatInterval] == 1)
-       repeat = @"MONTHLY";
-      else if ([rule frequency] == iCalRecurrenceFrequenceYearly
-              && [rule repeatInterval] == 1)
-       repeat = @"YEARLY";
-    }
-  else
-    DESTROY(repeat);
-
   return self;
 }
 
 {
   SOGoAppointmentObject *clientObject;
   int nbrDays;
-  iCalRecurrenceRule *rule;
 
   clientObject = [self clientObject];
   [self event];
     }
   if ([clientObject isNew])
     [event setTransparency: @"OPAQUE"];
-
-  // We remove any repeat rules
-  if (!repeat && [event hasRecurrenceRules])
-    [event removeAllRecurrenceRules];
-  else if (!([repeat caseInsensitiveCompare: @"-"] == NSOrderedSame
-            || [repeat caseInsensitiveCompare: @"CUSTOM"] == NSOrderedSame))
-    {
-      rule = [iCalRecurrenceRule new];
-
-      [rule setInterval: @"1"];
-      if ([repeat caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame)
-       {
-         [rule setFrequency: iCalRecurrenceFrequenceWeekly];
-         [rule setInterval: @"2"];
-       }
-      else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
-       {
-         [rule setByDayMask: (iCalWeekDayMonday
-                              |iCalWeekDayTuesday
-                              |iCalWeekDayWednesday
-                              |iCalWeekDayThursday
-                              |iCalWeekDayFriday)];
-         [rule setFrequency: iCalRecurrenceFrequenceDaily];
-       }
-      else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame)
-       {
-         [rule setNamedValue: @"bymonthday"
-               to: [NSString stringWithFormat: @"%d", [aptStartDate dayOfMonth]]];
-         [rule setFrequency: iCalRecurrenceFrequenceMonthly];
-       }
-      else
-       [rule setFrequency:
-               (iCalRecurrenceFrequency) [rule valueForFrequency: repeat]];
-      [event setRecurrenceRules: [NSArray arrayWithObject: rule]];
-      [rule release];
-    }
 }
 
 // TODO: add tentatively
index 43437336a55d9447c948c500e2490ed77c371ee7..f29c65941adfb9f9bc24baf32d62d4f3edbd59f8 100644 (file)
@@ -104,11 +104,7 @@ static NSArray *filters = nil;
 
 - (NSString *) filterLabel
 {
-#if 1
-  return [[[self context] page] labelForKey: [self valueForKey:@"filter"]];
-#else
-  return [self valueForKey: @"filter"];
-#endif
+  return [self labelForKey: [self valueForKey:@"filter"]];
 }
 
 - (NSString *) selectedFilter
index 21feda2f89b7fa5affbd7f7361fed5a5f3e08c31..05ffa06f6202cfbbcb545d6d50b5f7a75fe256e4 100644 (file)
@@ -1,6 +1,6 @@
 /* UIxCalendarSelector.h - this file is part of SOGo
  *
- * Copyright (C) 2007 Inverse groupe conseil
+ * Copyright (C) 2007, 2008 Inverse groupe conseil
  *
  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
  *
@@ -41,6 +41,8 @@
 - (void) setCurrentCalendar: (NSDictionary *) newCalendar;
 - (NSDictionary *) currentCalendar;
 
+- (WOResponse *) calendarsListAction;
+
 @end
 
 #endif /* UIXCALENDARSELECTOR_H */
index 1e49c8e247c12303c553623ff12f8a42f0ef130e..743375ee7d6952ed8194e21ee0e9c3f66ebaaefe 100644 (file)
@@ -1,6 +1,6 @@
 /* UIxCalendarSelector.m - this file is part of SOGo
  *
- * Copyright (C) 2007 Inverse groupe conseil
+ * Copyright (C) 2007, 2008 Inverse groupe conseil
  *
  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
  *
 #import <Foundation/NSDictionary.h>
 #import <Foundation/NSValue.h>
 
+#import <NGObjWeb/WOResponse.h>
+
 #import <SOGo/NSDictionary+Utilities.h>
 
+#import <SoObjects/SOGo/NSArray+Utilities.h>
 #import <SoObjects/SOGo/SOGoUser.h>
 
 #import <Appointments/SOGoAppointmentFolder.h>
@@ -171,4 +174,16 @@ colorForNumber (unsigned int number)
           keysWithFormat: @"color: %{color}; background-color: %{color};"];
 }
 
+- (WOResponse *) calendarsListAction
+{
+  WOResponse *response;
+
+  response = [self responseWithStatus: 200];
+  [response setHeader: @"text/plain; charset=utf-8"
+           forKey: @"content-type"];
+  [response appendContentString: [[self calendars] jsonRepresentation]];
+
+  return response;
+}
+
 @end /* UIxCalendarSelector */
index e6172e002e97fc7e0fdd1ce2ef81ee7f77aa1058..55bf57396b6a0c0b05b5fedc246dc125cf64cf18 100644 (file)
   NSString *attendeesUIDs;
   NSString *attendeesEmails;
   NSString *attendeesStates;
+
+  NSString *repeat;
+  NSString *reminder;
+  
+  /* ugly */
+  NSString *repeatType;
+  NSString *repeat1;
+  NSString *repeat2;
+  NSString *repeat3;
+  NSString *repeat4;
+  NSString *repeat5;
+  NSString *repeat6;
+  NSString *repeat7;
+  
+  NSString *range1;
+  NSString *range2;
 }
 
 - (NSString *) toolbar;
 - (void) setAttendeesEmails: (NSString *) newAttendeesEmails;
 - (NSString *) attendeesEmails;
 
+- (NSString *) repeat;
+- (void) setRepeat: (NSString *) newRepeat;
+
+- (NSString *) reminder;
+- (void) setReminder: (NSString *) newReminder;
+
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
 - (NSArray *) cycles;
 - (void) setCycle: (NSDictionary *) _cycle;
 - (NSDictionary *) cycle;
 - (BOOL) isCycleEndUntil;
 - (void) setIsCycleEndUntil;
 - (void) setIsCycleEndNever;
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
 
 /* access */
 - (BOOL) isMyComponent;
index cddb44ee5b9386a057d555cfa4936b21f3a94fcc..789f21f7b530a4c7a696afa282f2ed9bb6553c9f 100644 (file)
 
 #import "UIxComponentEditor.h"
 
+#define REPEAT(X) \
+- (NSString *) repeat##X { return repeat##X; } \
+- (void) setRepeat##X: (NSString *) theValue { NSLog(@"setRepeat %@", theValue); ASSIGN(repeat##X, theValue); } \
+
+#define RANGE(X) \
+- (NSString *) range##X { return range##X; } \
+- (void) setRange##X: (NSString *) theValue { ASSIGN(range##X, theValue);  }
+
 @implementation UIxComponentEditor
 
 - (id) init
       attendeesEmails = nil;
       attendeesStates = nil;
       calendarList = nil;
+      repeat = nil;
+      reminder = nil;
+      repeatType = nil;
+      repeat1 = nil;
+      repeat2 = nil;
+      repeat3 = nil;
+      repeat4 = nil;
+      repeat5 = nil;
+      repeat6 = nil;
+      repeat7 = nil;
+      range1 = nil;
+      range2 = nil;
     }
 
   return self;
   [attendeesStates release];
   [calendarList release];
 
+  [repeat release];
+  [reminder release];
+
+  [repeatType release];
+  [repeat1 release];
+  [repeat2 release];
+  [repeat3 release];
+  [repeat4 release];
+  [repeat5 release];
+  [repeat6 release];
+  [repeat7 release];
+  [range1 release];
+  [range2 release];
+  
   [component release];
 
   [super dealloc];
     }
 }
 
+- (NSString *) _dayMaskToInteger: (unsigned int) theMask
+{
+  NSMutableString *s;
+  unsigned int i;
+
+  unsigned char maskDays[] = { iCalWeekDayMonday, iCalWeekDayTuesday,
+                               iCalWeekDayWednesday, iCalWeekDayThursday,
+                               iCalWeekDayFriday, iCalWeekDaySaturday,
+                               iCalWeekDaySunday };
+  
+  s = [NSMutableString string];
+  
+  for (i = 0; i < 7; i++)
+    {
+      if ((theMask&maskDays[i]) == maskDays[i])
+       [s appendFormat: @"%d,", i+1];
+    }
+
+  if ([s length])
+    return [s substringToIndex: [s length]-1];
+
+  return s;
+}
+
+- (void) _loadRRules
+{
+  // We initialize our repeat ivars
+  if ([component hasRecurrenceRules])
+    {
+      iCalRecurrenceRule *rule;
+
+      [self setRepeat: @"CUSTOM"];
+      
+      rule = [[component recurrenceRules] lastObject];
+
+      if ([rule frequency] == iCalRecurrenceFrequenceDaily)
+       {
+         repeatType = @"0";
+         
+         if ([rule byDayMask] == (iCalWeekDayMonday
+                                  | iCalWeekDayTuesday
+                                  | iCalWeekDayWednesday
+                                  | iCalWeekDayThursday
+                                  | iCalWeekDayFriday))
+           {
+             if ([rule isInfinite])
+               {
+                 repeat = @"EVERY WEEKDAY";
+               }
+             repeat1 = @"1";
+           }
+         else
+           {
+             repeat1 = @"0";
+             
+             if ([rule repeatInterval] == 1 && [rule isInfinite])
+               {
+                 repeat = @"DAILY";
+               }
+
+             [self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
+           }
+       }
+      else if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
+       {
+         repeatType = @"1";
+
+         if (![rule byDayMask])
+           {
+             if ([rule repeatInterval] == 1)
+               repeat = @"WEEKLY";
+             else if ([rule repeatInterval] == 2)
+               repeat = @"BI-WEEKLY";
+           }
+         else
+           {
+             [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
+             [self setRepeat2: [self _dayMaskToInteger: [rule byDayMask]]];
+           }
+       }
+      else if ([rule frequency] == iCalRecurrenceFrequenceMonthly)
+       {
+         repeatType = @"2";
+
+         if ([rule byDayMask])
+           {
+             // TODO
+             [self setRepeat2: @"0"];
+           }
+         else if ([[rule byMonthDay] count])
+           {
+             [self setRepeat2: @"1"];
+             [self setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]];
+           }
+         else if ([rule repeatInterval] == 1)
+           repeat = @"MONTHLY";
+
+         [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
+       }
+      else
+       {
+         repeatType = @"3";
+        
+         if ([rule namedValue: @"bymonth"])
+           {
+             if (![rule byDayMask])
+               {
+                 [self setRepeat2: @"0"];
+                 [self setRepeat3: [rule namedValue: @"bymonthday"]];
+                 [self setRepeat4: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]];
+               }
+             else
+               {
+                 // TODO
+                 [self setRepeat2: @"1"];
+               }
+           }
+         else if ([rule repeatInterval] == 1)
+           repeat = @"YEARLY";
+
+         [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
+       }
+
+      // We decode the proper end date, recurrences count, etc.
+      if ([rule repeatCount])
+       {
+         [self setRange1: @"1"];
+         [self setRange2: [rule namedValue: @"count"]];
+       }
+      else if ([rule untilDate])
+       {
+         [self setRange1: @"2"];
+         [self setRange2: [[rule untilDate] descriptionWithCalendarFormat: @"%Y-%m-%d"]];
+       }
+      else
+       {
+         [self setRange1: @"0"];
+       }
+    }
+  else
+    DESTROY(repeat);
+}
+
 /* warning: we use this method which will be triggered by the template system
    when the page is instantiated, but we should find another and cleaner way of
    doing this... for example, when the clientObject is set */
          ASSIGN (organizer, [component organizer]);
          [self _loadCategories];
          [self _loadAttendees];
+         [self _loadRRules];
        }
     }
 //   /* cycles */
                 [NSString stringWithFormat: @"category_%@", item]];
 }
 
+- (NSArray *) repeatList
+{
+  static NSArray *repeatItems = nil;
+
+  if (!repeatItems)
+    {
+      repeatItems = [NSArray arrayWithObjects: @"DAILY",
+                             @"WEEKLY",
+                             @"BI-WEEKLY",
+                             @"EVERY WEEKDAY",
+                             @"MONTHLY",
+                             @"YEARLY",
+                             @"-",
+                             @"CUSTOM",
+                             nil];
+      [repeatItems retain];
+    }
+
+  return repeatItems;
+}
+
+- (NSString *) itemRepeatText
+{
+  NSString *text;
+
+  if ([item isEqualToString: @"-"])
+    text = item;
+  else
+    text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]];
+
+  return text;
+}
+
+- (NSArray *) reminderList
+{
+  static NSArray *reminderItems = nil;
+
+  if (!reminderItems)
+    {
+      reminderItems = [NSArray arrayWithObjects: @"5_MINUTES_BEFORE",
+                               @"10_MINUTES_BEFORE",
+                               @"15_MINUTES_BEFORE",
+                               @"30_MINUTES_BEFORE",
+                               @"45_MINUTES_BEFORE",
+                               @"-",
+                               @"1_HOUR_BEFORE",
+                               @"2_HOURS_BEFORE",
+                               @"5_HOURS_BEFORE",
+                               @"15_HOURS_BEFORE",
+                               @"-",
+                               @"1_DAY_BEFORE",
+                               @"2_DAYS_BEFORE",
+                               @"1_WEEK_BEFORE",
+                               @"-",
+                               @"CUSTOM",
+                               nil];
+      [reminderItems retain];
+    }
+
+  return reminderItems;
+}
+
+// - (void) setReminder: (NSString *) reminder
+// {
+//   ASSIGN(reminder, _reminder);
+// }
+
+// - (NSString *) reminder
+// {
+//   return reminder;
+// }
+
+- (NSString *) reminder
+{
+  return @"";
+}
+
+- (void) setReminder: (NSString *) newReminder
+{
+}
+
+- (NSString *) itemReminderText
+{
+  NSString *text;
+
+  if ([item isEqualToString: @"-"])
+    text = item;
+  else
+    text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]];
+
+  return text;
+}
+
+- (NSString *) repeat
+{
+  return repeat;
+}
+
+- (void) setRepeat: (NSString *) newRepeat
+{
+  ASSIGN(repeat, newRepeat);
+}
+
 - (NSString *) _permissionForEditing
 {
   NSString *perm;
   return status;
 }
 
+- (void) setRepeatType: (NSString *) theValue
+{
+  ASSIGN (repeatType, theValue);
+}
+
+- (NSString *) repeatType
+{
+  return repeatType;
+}
+
+REPEAT(1);
+REPEAT(2);
+REPEAT(3);
+REPEAT(4);
+REPEAT(5);
+REPEAT(6);
+REPEAT(7);
+RANGE(1);
+RANGE(2);
+
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
 - (NSArray *) cycles
 {
   NSBundle *bundle;
 {
   [self setCycleEnd: @"cycle_end_never"];
 }
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
+////////////////////////////////// JUNK ////////////////////////////////////////
+
 
 /* helpers */
 - (NSString *) completeURIForMethod: (NSString *) _method
     }
 }
 
+- (void) _handleCustomRRule: (iCalRecurrenceRule *) theRule
+
+{
+  int type, range;
+
+  // We decode the range
+  range = [[self range1] intValue];
+
+  // Create X appointments
+  if (range == 1)
+    {
+      [theRule setRepeatCount: [[self range2] intValue]];
+    }
+  // Repeat until date
+  else if (range == 2)
+    {
+      [theRule setUntilDate: [NSCalendarDate dateWithString: [self range2]
+                                            calendarFormat: @"%Y-%m-%d"]];
+    }
+  // No end date.
+  else
+    {
+      // Do nothing?
+    }
+
+
+  // We decode the type and the rest accordingly.
+  type = [[self repeatType] intValue];
+
+  switch (type)
+    {
+      // DAILY:
+      //
+      // repeat1 holds the value of the radio button:
+      //   0 -> Every X days
+      //   1 -> Every weekday
+      //
+      // repeat2 holds the value of X when repeat1 equals 0
+      //
+    case 0:
+      {
+       [theRule setFrequency: iCalRecurrenceFrequenceDaily];
+
+       if ([[self repeat1] intValue] == 0)
+         {
+           [theRule setInterval: [self repeat2]];
+         }
+       else
+         {
+           [theRule setByDayMask: (iCalWeekDayMonday
+                                   |iCalWeekDayTuesday
+                                   |iCalWeekDayWednesday
+                                   |iCalWeekDayThursday
+                                   |iCalWeekDayFriday)];
+         }
+      }
+      break;
+      
+      // WEEKLY
+      //
+      // repeat1 holds the value of "Every X week(s)" 
+      //
+      // repeat2 holds which days are part of the recurrence rule
+      //  1 -> Monday
+      //  2 -> Tuesday .. and so on.
+      //  The list is separated by commas, like: 1,3,4
+    case 1:
+      {
+       NSArray *v;
+       int c, mask;
+
+       [theRule setFrequency: iCalRecurrenceFrequenceWeekly];
+       [theRule setInterval: [self repeat1]];
+
+       v = [[self repeat2] componentsSeparatedByString: @","];
+       c = [v count];
+       mask = 0;
+       
+       while (c--)
+         {
+           mask |= (unsigned int)exp2([[v objectAtIndex: c] intValue]-1);
+         }
+       
+       [theRule setByDayMask: mask];
+      }
+      break;
+      
+      // MONTHLY
+      //
+      // repeat1 holds the value of "Every X month(s)"
+      //
+      // repeat2 holds the value of the radio-button "The" / "Recur on day(s)"
+      //  0 -> The
+      //  1 -> Recur on day(s)
+      //
+      // repeat3 holds the value of the first popup
+      //  0 -> First
+      //  1 -> Second ... and so on.
+      //
+      // repeat4 holds the value of the second popop
+      //  0 -> Sunday
+      //  1 -> Monday ... and so on.
+      //  7 -> Day of the month
+      //
+      // repeat5 holds the selected days when "Recur on day(s)"
+      // is chosen. The value starts at 1.
+      //
+    case 2:
+      {
+       [theRule setFrequency: iCalRecurrenceFrequenceMonthly];
+       [theRule setInterval: [self repeat1]];
+
+       // We recur on specific days...
+       if ([[self repeat2] intValue] == 1)
+         {
+           [theRule setNamedValue: @"bymonthday"  to: [self repeat5]];
+         }
+       else
+         {
+           // TODO
+         }
+      }
+      break;
+
+      // YEARLY
+      //
+      // repeat1 holds the value of "Every X year(s)"
+      //
+      // repeat2 holds the value of the radio-button "Every" / "Every .. of .."
+      //  0 -> Every
+      //  1 -> Every .. of ..
+      //
+      // repeat3 holds the value of the DAY parameter
+      // repeat4 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
+      //  ex: 3 February
+      //
+      // repeat5 holds the value of the OCCURENCE parameter (0 -> First, 1 -> Second ..)
+      // repeat6 holds the value of the DAY parameter (0 -> Sunday, 1 -> Monday, etc..)
+      // repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
+      // 
+    case 3:
+    default:
+      {
+       [theRule setFrequency: iCalRecurrenceFrequenceYearly];
+       [theRule setInterval: [self repeat1]];
+
+       // We recur Every .. of ..
+       if ([[self repeat2] intValue] == 1)
+         {
+           // TODO
+         }
+       else
+         {
+           [theRule setNamedValue: @"bymonthday"  to: [self repeat3]];
+           [theRule setNamedValue: @"bymonth" 
+                    to: [NSString stringWithFormat: @"%d", ([[self repeat4] intValue]+1)]];
+         }
+      }
+      break;
+    }
+}
+
 - (void) takeValuesFromRequest: (WORequest *) _rq
                      inContext: (WOContext *) _ctx
 {
-  NSCalendarDate *now;
   SOGoCalendarComponent *clientObject;
+  iCalRecurrenceRule *rule;
+  NSCalendarDate *now;
 
   [super takeValuesFromRequest: _rq inContext: _ctx];
 
     }
   [component setPriority: priority];
   [component setLastModified: now];
+
+  // We remove any repeat rules
+  if (!repeat && [component hasRecurrenceRules])
+    [component removeAllRecurrenceRules];
+  else if ([repeat caseInsensitiveCompare: @"-"] != NSOrderedSame)
+    {
+      rule = [iCalRecurrenceRule new];
+
+      [rule setInterval: @"1"];
+
+      if ([repeat caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame)
+       {
+         [rule setFrequency: iCalRecurrenceFrequenceWeekly];
+         [rule setInterval: @"2"];
+       }
+      else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
+       {
+         [rule setByDayMask: (iCalWeekDayMonday
+                              |iCalWeekDayTuesday
+                              |iCalWeekDayWednesday
+                              |iCalWeekDayThursday
+                              |iCalWeekDayFriday)];
+         [rule setFrequency: iCalRecurrenceFrequenceDaily];
+       }
+      else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame)
+       {
+         [rule setNamedValue: @"bymonthday"
+               to: [NSString stringWithFormat: @"%d", [[component startDate] dayOfMonth]]];
+         [rule setFrequency: iCalRecurrenceFrequenceMonthly];
+       }
+      else if ([repeat caseInsensitiveCompare: @"DAILY"] == NSOrderedSame ||
+              [repeat caseInsensitiveCompare: @"WEEKLY"] == NSOrderedSame ||
+              [repeat caseInsensitiveCompare: @"YEARLY"] == NSOrderedSame)
+       {
+         [rule setFrequency:
+                 (iCalRecurrenceFrequency) [rule valueForFrequency: repeat]];
+       }
+      else
+       {
+         // We have a CUSTOM recurrence. Let's decode what kind of custome recurrence
+         // we have and set that.
+         [self _handleCustomRRule: rule];
+       }
+
+      [component setRecurrenceRules: [NSArray arrayWithObject: rule]];
+      [rule release];
+    }
 }
 
 #warning the following methods probably share some code...
index 871593c41b0e5a95a52f87991a7f10daa444be07..1b503d34f4e3c246b55da786dbe6cbcd463f0b19 100644 (file)
@@ -52,9 +52,6 @@
 - (void) setTaskDueDate: (NSCalendarDate *) _date;
 - (NSCalendarDate *) taskDueDate;
 
-- (NSString *) repeat;
-- (void) setRepeat: (NSString *) newRepeat;
-
 @end
 
 #endif /* UIXAPPOINTMENTEDITOR_H */
index 295bbcc753ba1f3af561b435617c0c04d9829e3b..6b129716cf2830dd59628f875f705889b363508e 100644 (file)
   return !hasDueDate;
 }
 
-- (NSArray *) repeatList
-{
-  static NSArray *repeatItems = nil;
-
-  if (!repeatItems)
-    {
-      repeatItems = [NSArray arrayWithObjects: @"DAILY",
-                             @"WEEKLY",
-                             @"BI-WEEKLY",
-                             @"EVERY WEEKDAY",
-                             @"MONTHLY",
-                             @"YEARLY",
-                             @"-",
-                             @"CUSTOM",
-                             nil];
-      [repeatItems retain];
-    }
-
-  return repeatItems;
-}
-
-- (NSString *) itemRepeatText
-{
-  NSString *text;
-
-  if ([item isEqualToString: @"-"])
-    text = item;
-  else
-    text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]];
-
-  return text;
-}
-
 - (NSArray *) statusList
 {
   static NSArray *statusItems = nil;
   return item;
 }
 
-- (NSString *) repeat
-{
-  return @"";
-}
-
-- (void) setRepeat: (NSString *) newRepeat
-{
-}
-
 - (NSString *) status
 {
   return status;
index 734735ff860975434f7ca21a307a6a3dc8855f83..5b1c524869b6ab237286296a342c7a0014a50076 100644 (file)
              protectedBy = "View";
              pageName    = "UIxCalDateSelector"; 
           };
+          calendarslist = {
+             protectedBy = "View";
+             pageName = "UIxCalendarSelector";
+             actionName = "calendarsList";
+          };
           eventslist = {
              protectedBy = "View";
              actionClass = "UIxCalListingActions";
              protectedBy = "View";
              pageName    = "UIxAttendeesEditor";
           };
+          editRecurrence = {
+             protectedBy = "View";
+             pageName    = "UIxRecurrenceEditor";
+          };
        };
      };
 
index 14c3cf60f26639c58d4f70a9a810d89f4e7a2db1..701eef9d47a7bbbb2210e04005d89b3f64e3ce18 100644 (file)
@@ -10,7 +10,7 @@
   <!-- TODO: add iMIP actions -->
 
   <input id="iCalendarAttachment" type="hidden"
-    var:value="pathToAttachmentObject"/>
+    var:value="pathToAttachment"/>
 
   <var:if condition="couldParseCalendar" const:negate="1">
     <fieldset>
index 909b253a4588672ef0474105ab15b6be5ca02244..342634679e960d7daa73ddb1ff7bf6ffa9ae74c2 100644 (file)
   var:component="event"
   var:saveURL="saveURL">
 
+  <div class="menu" id="attendeesMenu">
+    <ul>
+      <li><var:string label:value="Invite Attendees"/>...</li>
+      <li class="separator"><!-- separator --></li>
+      <li><var:string label:value="Compose E-Mail to All Attendees" />...</li>
+      <li id="composeToUndecidedAttendees"><var:string label:value="Compose E-Mail to Undecided Attendees" />...</li>
+      <li class="separator"><!-- separator --></li>
+    </ul>
+  </div>
+
   <label><span id="allDay" class="content"><input class="checkBox"
         type="checkbox" var:selection="isAllDay"
         var:checked="isAllDay"
         const:dayStartHour="0"
         const:dayEndHour="23"
         /></span></span>
-  <hr />
-  <label><var:string label:value="Repeat:" />
-    <span class="content"><var:popup list="repeatList" item="item"
-        label:noSelectionString="repeat_NEVER"
-        const:disabledValue="-"
-        string="itemRepeatText" selection="repeat"
-        /></span></label>
-  <hr />
-  <label><var:string label:value="Reminder:" />
-    <span class="content"><var:popup list="reminderList" item="item"
-        const:disabledValue="-"
-        label:noSelectionString="reminder_NONE"
-        string="itemReminderText" selection="reminder"
-        /></span></label>
 </var:component>
index 8895bcafcbe81927e64e507a9f3eb711922cad59..540fd2660f9b6b0b4ad33d97bdd1aadcd1bc0751 100644 (file)
@@ -29,9 +29,8 @@
        string="itemZoomText" selection="zoom"/>
       <a href="#" class="button _disabled"><var:string label:value="+" /></a>
     </div>
-    <div/>
     <div id="freeBusyView">
-      <table id="freeBusy"
+      <table id="freeBusy" cellspacing="0" cellpadding="0"
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:var="http://www.skyrix.com/od/binding"
        xmlns:const="http://www.skyrix.com/od/constant"
        xmlns:rsrc="OGo:url"
        xmlns:label="OGo:label">
        <thead>
-         <tr class="freeBusyHeader1"
-           ><th class="attendees"><!--space --></th
-             ></tr>
-         <tr class="freeBusyHeader2"
-           ><th class="attendees"><!--space --></th
-             ></tr>
-         <tr class="freeBusyHeader3"
-           ><th class="attendees"><!--space --></th
-             ></tr>
+          <tr>
+            <td><!--space --></td>
+            <td class="freeBusyHeader">
+              <div><table id="freeBusyHeader" cellspacing="0" cellpadding="0">
+               <tr class="freeBusyHeader1"><!--space --></tr>
+               <tr class="freeBusyHeader2"><!--space --></tr>
+               <tr class="freeBusyHeader3"><!--space --></tr>
+              </table></div>
+            </td>
+          </tr>
        </thead>
-
        <tbody>
-         <tr class="futureAttendee"
-           ><td class="attendees"><input type="text" class="textField"
-               readonly="readonly" /></td
-             ></tr>
-         <tr class="attendeeModel"
-           ><td class="attendees"><input type="text" class="textField" /></td
-             ></tr>
+          <tr>
+            <td class="freeBusyAttendees">
+              <div><table id="freeBusyAttendees" cellspacing="0" cellpadding="0">
+                <tbody>
+                 <tr class="futureAttendee"
+                   ><td class="attendees"><input type="text" class="textField"
+                   readonly="readonly" /></td
+                 ></tr>
+                 <tr class="attendeeModel"
+                   ><td class="attendees"><input type="text" class="textField" /></td
+                 ></tr>
+                </tbody>
+              </table></div>
+            </td>
+            <td class="freeBusyData">
+              <div><table id="freeBusyData" cellspacing="0" cellpadding="0">
+                <tbody>
+                  <tr class="futureData"></tr>
+                  <tr class="dataModel"></tr>
+                </tbody>
+              </table></div>
+            </td>
+          </tr>
        </tbody>
       </table>
     </div>
index 044a1d0e9a19cfae043127a04c42bdabfe0e9dfd..f9f1a7bad6725d2357744bb425025a19376df9b1 100644 (file)
       <hr />
       <var:component-content />
       <hr />
+      <label><var:string label:value="Repeat:" />
+       <span class="content"><var:popup list="repeatList" item="item"
+          label:noSelectionString="repeat_NEVER"
+          const:disabledValue="-"
+          const:name="repeatList"
+         const:id="repeatList"
+          string="itemRepeatText" var:selection="repeat"
+          /> <a href="#" id="repeatHref" style="display: none;"
+             ><var:string label:value="Edit"/></a></span></label>
+      <hr />
+      <label><var:string label:value="Reminder:" />
+      <span class="content"><var:popup list="reminderList" item="item"
+         const:disabledValue="-"
+         label:noSelectionString="reminder_NONE"
+         string="itemReminderText" selection="reminder"
+         /></span></label>
+      <hr />
       <label id="commentArea"><var:string label:value="Description:"
          /><textarea rows="20" name="comment" var:value="comment" /></label>
       <label id="documentLabel" style="display: none;"><var:string label:value="Document:"
       <input type="hidden" name="calendarFoldersList"
        id="calendarFoldersList"
        var:value="calendarsFoldersList"/>
+
+      <input type="hidden" name="repeatType"
+       id="repeatType"
+       var:value="repeatType"/>
+      <input type="hidden" name="repeat1"
+       id="repeat1"
+       var:value="repeat1"/>
+      <input type="hidden" name="repeat2"
+       id="repeat2"
+       var:value="repeat2"/>
+      <input type="hidden" name="repeat3"
+       id="repeat3"
+       var:value="repeat3"/>
+      <input type="hidden" name="repeat4"
+       id="repeat4"
+       var:value="repeat4"/>
+      <input type="hidden" name="repeat5"
+       id="repeat5"
+       var:value="repeat5"/>
+      <input type="hidden" name="repeat6"
+       id="repeat6"
+       var:value="repeat6"/>
+      <input type="hidden" name="repeat7"
+       id="repeat7"
+       var:value="repeat7"/>
+
+     <input type="hidden" name="range1"
+       id="range1"
+       var:value="range1"/>
+     <input type="hidden" name="range2"
+       id="range2"
+       var:value="range2"/>
+
+
     </div>
   </form>
 </var:component>
index a7a506ff254cff23cb2c316b13abfa5c266edfd4..2e71d2f6b7a2e73d026b2bbf881321fd200870a6 100644 (file)
        var:disabled="statusPercentDisabled"
        /><var:string label:value="% complete"
        /></span></span>
-  <hr />
-  <label><var:string label:value="Repeat:" />
-    <span class="content"><var:popup list="repeatList" item="item"
-        label:noSelectionString="repeat_NEVER"
-        const:disabledValue="-"
-        string="itemRepeatText" selection="repeat"
-        /></span></label>
 </var:component>
index 3d936ae62423854c0797072c95c7a83fe50b49af..7aefda2c97c7d6f0608c72a38bd209d22a01c72f 100644 (file)
          <script type="text/javascript" rsrc:src="HTMLElement.js"><!-- space --></script>
          <script type="text/javascript" rsrc:src="HTMLInputElement.js"><!-- space --></script>
          <script type="text/javascript" rsrc:src="HTMLTableElement.js"><!-- space --></script>
-         <script type="text/javascript" rsrc:src="HTMLUListElement.js"><!-- space --></script>
          <script type="text/javascript" rsrc:src="generic.js"><!-- space --></script>
          <script type="text/javascript" rsrc:src="SOGoDragAndDrop.js"><!-- space --></script>
          <script type="text/javascript" rsrc:src="SOGoDragHandles.js"><!-- space --></script>
index 65f6b3dc8e29eca01cee82f3ef35935d27987e5a..dc355914e4537d8f28e6515ef989664809644abd 100644 (file)
@@ -169,7 +169,8 @@ DIV#folderTreeContent TABLE TD
   padding: 0px; }
 
 TABLE#contactsList
-{ width: 100%; }
+{ -khtml-user-select: none;
+  width: 100%; }
 
 TABLE#contactsList TD,
 TABLE#contactsList TH
index 744510e583ffd502920c7580deb2c215323f2ef6..8fdcb00a18e2ed21ffe07e2bbf115b70a259e17a 100644 (file)
@@ -226,6 +226,54 @@ Element.addMethods({
     else {
       element.select();
     }
- }
+  },
+
+  getRadioValue: function(element, radioName) {
+    element = $(element);
+    var radioValue;
+    Form.getInputs(element, 'radio', radioName).each(function(input) {
+       if (input.checked)
+         radioValue = input.value;
+      });
+    return radioValue;
+  },
+
+  setRadioValue: function(element, radioName, value) {
+    element = $(element);
+    var i = 0;
+
+    Form.getInputs(element, 'radio', radioName).each(function(input) {
+      if (i == value)
+       input.checked = 1;
+      i++;
+      });
+  },
+
+  getCheckBoxListValues: function(element, checkboxName) {
+    element = $(element);
+    var values = new Array();
+    var i = 0;
+
+    Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {
+       if (input.checked)
+         values.push(i+1);
+       
+       i++;
+      });
+    return values.join(",");
+  },
+
+  setCheckBoxListValues: function(element, checkboxName, values) {
+    element = $(element);
+    var v = values.split(',');
+    var i = 1;
+
+    Form.getInputs(element, 'checkbox', checkboxName).each(function(input) {
+      
+      if ($(v).indexOf(i+"") != -1)
+       input.checked = 1;
+      i++;
+      });
+  }
 
 });
index be7fca9bc4c6bdf6aa4651e6a887e1f6fb003d01..64c0f727fa5ca366d6cf3351c031ec6645ee3f3a 100644 (file)
@@ -521,6 +521,9 @@ DIV[datatype~="additional"] > A.node > SPAN.nodeName
 { color: #777;
   font-style: italic; }
 
+DIV[datatype~="additional"] > A.node._selected > SPAN.nodeName
+{ color: #fff; }
+
 /* drag-n-drop */
 IMG.dragMessage
 { position: absolute;
index f2568018bc7b9f031b1227ae8d589d8fc7e85b90..f93362544079fd269cede1ae2f4d3c22522f1772 100644 (file)
@@ -268,16 +268,6 @@ function deleteSelectedMessagesCallback(http) {
     log ("deleteSelectedMessagesCallback: problem during ajax request " + http.status);
 }
 
-function deleteDraft(url) {
-  /* this is called by UIxMailEditor with window.opener */
-  new Ajax.Request(url, {
-    method: 'post',
-    onFailure: function(transport) {
-       log("draftDeleteCallback: problem during ajax request: " + transport.status);
-      }
-    });
-}
-
 function moveMessages(rowIds, folder) {
   var failCount = 0;
 
index 3ac2a758ba8ab725aa5897c79a17e4e6f848f71c..012688b25f724c8a2993891a7d1335f284a57d35 100644 (file)
@@ -216,19 +216,25 @@ function modifyEventCallback(http) {
 function deleteEventCallback(http) {
   if (http.readyState == 4) {
     if (isHttpStatus204(http.status)) {
+      var isTask = false;
       var nodes = http.callbackData;
       for (var i = 0; i < nodes.length; i++) {
        var node = $(nodes[i]);
-       if (node)
+       if (node) {
+         isTask = isTask || (node.parentNode.id == 'tasksList');
          node.parentNode.removeChild(node);
+       }
       }
       if (eventsToDelete.length)
        _batchDeleteEvents();
       else {
        document.deleteEventAjaxRequest = null;
-       refreshEvents();
-       refreshTasks();
-       changeCalendarDisplay();
+       if (isTask)
+         refreshTasks();
+       else {
+         refreshEvents();
+         changeCalendarDisplay();
+       }
       }
     }
     else
@@ -267,6 +273,7 @@ function onDaySelect(node) {
   document.selectedDate = td;
 
   changeCalendarDisplay( { "day": day } );
+  currentDay = day;
   if (needRefresh)
     refreshEvents();
 
@@ -283,9 +290,13 @@ function onDateSelectorGotoMonth(event) {
 
 function onCalendarGotoDay(node) {
    var day = node.getAttribute("date");
+   var needRefresh = (listFilter == 'view_selectedday'
+                     && day != currentDay);
    
    changeDateSelectorDisplay(day);
    changeCalendarDisplay( { "day": day } );
+   if (needRefresh)
+     refreshEvents();
   
    return false;
 }
@@ -1594,6 +1605,8 @@ function appendCalendar(folderName, folderPath) {
     li.appendChild(checkBox);
     li.appendChild(document.createTextNode(" "));
     $(checkBox).addClassName("checkBox");
+    if (owner == UserLogin)
+      checkBox.checked = 1;
 
     var colorBox = document.createElement("div");
     li.appendChild(colorBox);
index f9a5f5c6ea471e0b87cb52c86e10f201d05a1b1b..8533cb008c59f9513b777655dbb38b359cdc0302 100644 (file)
@@ -93,7 +93,7 @@ TEXTAREA
   padding-bottom: 0em; }
 
 SELECT#calendarList
-{ width: 17em; }
+{ width: 16em; }
 
 A#changeUrlButton
 { margin-left: 1em; }
@@ -143,3 +143,21 @@ LABEL#urlArea INPUT
 A#attendeesHref
 { color: #00f;
   text-decoration: underline; }
+
+DIV#attendeesMenu LI
+{ padding-left: 20px; }
+
+DIV#attendeesMenu LI.accepted
+{ background-image: url("accepted.png");
+  background-repeat: no-repeat;
+  background-position: 5px center; }
+
+DIV#attendeesMenu LI.needs-action
+{ background-image: url("needs-action.png");
+  background-repeat: no-repeat;
+  background-position: 5px center; }
+
+DIV#attendeesMenu LI.declined
+{ background-image: url("declined.png");
+  background-repeat: no-repeat;
+  background-position: 5px center; }
\ No newline at end of file
index 12a3fe2818a7c15052d81e977a7430c12612f1a0..c444e8b32c03164888623d22ccaa586cb4aaab31 100644 (file)
@@ -20,6 +20,9 @@
 */
 
 var contactSelectorAction = 'calendars-contacts';
+var AppointmentEditor = {
+ attendeesMenu: null
+};
 
 function uixEarlierDate(date1, date2) {
   // can this be done in a sane way?
@@ -132,6 +135,42 @@ function toggleCycleVisibility(node, nodeName, hiddenValue) {
   }
 }
 
+function onAttendeesMenuPrepareVisibility()
+{
+  var composeToUndecidedAttendees = $('composeToUndecidedAttendees');
+  var attendeesStates = $('attendeesStates').value;
+  
+  if (attendeesStates.indexOf("needs-action") < 0)
+    composeToUndecidedAttendees.addClassName("disabled");
+  else
+    composeToUndecidedAttendees.removeClassName("disabled");
+}
+
+function onComposeToAllAttendees()
+{
+  var attendees = $$("DIV#attendeesMenu LI.attendee");
+  var addresses = new Array();
+  attendees.each(function(item) {
+      addresses.push(item.readAttribute("email"));
+  });
+  if (window.opener)
+    window.opener.openMailTo(addresses.join(","));
+}
+
+function onComposeToUndecidedAttendees()
+{
+  if ($(this).hasClassName("disabled"))
+    return;
+  var attendees = $$("DIV#attendeesMenu LI.attendee.needs-action");
+  var addresses = new Array();
+  attendees.each(function(item) {
+      addresses.push(item.readAttribute("email"));
+  });
+  if (window.opener)
+    window.opener.openMailTo(addresses.join(","));
+}
+
 function addContact(tag, fullContactName, contactId, contactName, contactEmail) {
   var uids = $('uixselector-participants-uidList');
 
@@ -291,6 +330,81 @@ function initTimeWidgets(widgets) {
   }
 }
 
+function refreshAttendees() {
+  var attendeesLabel = $("attendeesLabel");
+  var attendeesNames = $("attendeesNames").value;
+  var attendeesEmails = $("attendeesEmails").value.split(",");
+  var attendeesStates = $("attendeesStates").value.split(",");
+  var attendeesMenu = $("attendeesMenu").down("ul");
+  var attendeesHref = $("attendeesHref");
+  
+  // Remove link of attendees
+  for (var i = 0; i < attendeesHref.childNodes.length; i++)
+    attendeesHref.removeChild(attendeesHref.childNodes[i]);
+
+  // Remove attendees from menu
+  var menuItems = $$("DIV#attendeesMenu LI.attendee");
+  if (menuItems)
+    for (var i = 0; i < menuItems.length; i++)
+      attendeesMenu.removeChild(menuItems[i]);
+  
+  if (attendeesNames.length > 0) {
+    // Update attendees link and show label
+    attendeesHref.appendChild(document.createTextNode(attendeesNames));
+    attendeesLabel.setStyle({ display: "block" });
+
+    // Update attendees in menu
+    attendeesNames = attendeesNames.split(",");
+    for (var i = 0; i < attendeesEmails.length; i++) {
+      var node = document.createElement("li");
+      attendeesMenu.appendChild(node);
+      $(node).writeAttribute("email", attendeesEmails[i]);
+      $(node).addClassName("attendee");
+      $(node).addClassName(attendeesStates[i]);
+      node.appendChild(document.createTextNode(attendeesNames[i]));
+      $(node).observe("click", onMailTo);
+    }
+  }
+  else {
+    // Hide link of attendees
+    attendeesLabel.setStyle({ display: "none" });
+  }
+}
+
+function initializeAttendeesHref() {
+  var attendeesHref = $("attendeesHref");
+  var attendeesLabel = $("attendeesLabel");
+  var attendeesNames = $("attendeesNames");
+
+  Event.observe(attendeesHref, "click", onAttendeesHrefClick, false);
+  refreshAttendees();
+}
+
+function onAttendeesHrefClick(event) {
+  popupToolbarMenu(this, 'attendeesMenu');
+  preventDefault(event);
+  return false;
+}
+
+function onMailTo(event) {
+  var target = getTarget(event);
+  openMailTo(target.readAttribute("email"));
+}
+
+function getMenus() {
+  AppointmentEditor.attendeesMenu = new Array(onPopupAttendeesWindow,
+                                             "-",
+                                             onComposeToAllAttendees,
+                                             onComposeToUndecidedAttendees,
+                                             "-",
+                                             null);
+  
+  var attendeesMenu = $('attendeesMenu');
+  attendeesMenu.prepareVisibility = onAttendeesMenuPrepareVisibility;
+
+  return { "attendeesMenu": AppointmentEditor.attendeesMenu };
+}
+
 function onAppointmentEditorLoad() {
   assignCalendar('startTime_date');
   assignCalendar('endTime_date');
@@ -302,6 +416,7 @@ function onAppointmentEditorLoad() {
                         'hour': $("endTime_time_hour"),
                         'minute': $("endTime_time_minute")}};
   initTimeWidgets(widgets);
+  initializeAttendeesHref();
 }
 
 FastInit.addOnLoad(onAppointmentEditorLoad);
index 76e5bb819ab2e548ed2cf4ee4668de1d141d0a6b..28f3bdceeb20f92cd207ff023e5614609ea95198 100644 (file)
@@ -18,9 +18,9 @@ DIV#freeBusyView
   margin-top: 0.5em;
   top: 2em;
   bottom: 14.5em;
-  left: 15em;
+  left: 0px;
   right: 0px;
-  overflow: auto;
+  overflow: hidden;
   border-top: 2px solid #222;
   border-left: 2px solid #222;
   border-right: 1px solid #fff;
@@ -28,96 +28,97 @@ DIV#freeBusyView
   -moz-border-top-colors: #9c9a94 #000;
   -moz-border-left-colors: #9c9a94 #000; }
 
-TABLE#freeBusy
-{ border-collapse: collapse;
-  table-layout: auto; }
+TABLE
+{ border-collapse: separated;
+  table-layout: auto;
+  border-spacing: 0px 0px; }
 
-TABLE#freeBusy THEAD TH
+TABLE#freeBusyHeader TH
 { white-space: nowrap; }
 
 TABLE#freeBusy TD,
 TABLE#freeBusy TH
 { padding: 0px;
   margin: 0px;
-  border: 0px; }
+  border: 0px;
+  vertical-align: top; }
 
-TABLE#freeBusy TH.attendees,
-TABLE#freeBusy TD.attendees
-{ position: fixed;
-  padding: 0px .5em;
-  padding-right: 0px;
-  margin: 0px;
-  width: 15em;
-  border: 0px !important;
-  background-color: #d4d0c8;
-  overflow: hidden;
-  left: 0px; }
+TABLE#freeBusy TD.freeBusyHeader DIV,
+TABLE#freeBusy TD.freeBusyAttendees DIV
+{ overflow: hidden; }
 
-TABLE#freeBusy TD.attendees IMG
-{ position: absolute;
-  left: 0em;
-  top: 0.5em; }
-
-TABLE#freeBusy TD.attendees INPUT
-{ background-image: url("abcard.gif");
-  background-repeat: no-repeat;
-  background-position: 2px center;
-  width: 12em;
-  margin: 0px;
-  padding-left: 24px;
-  margin-left: 5px;
-  margin-left: 1.5em; }
+TABLE#freeBusy TD.freeBusyData DIV
+{ overflow: scroll; }
 
-TABLE#freeBusy TR.needs-action TD.attendees
+TABLE#freeBusyAttendees TR.needs-action TD.attendees
 { background-image: url("needs-action.png");
   background-repeat: no-repeat;
   background-position: 5px center; }
 
-TABLE#freeBusy TR.declined TD.attendees
+TABLE#freeBusyAttendees TR.declined TD.attendees
 { background-image: url("declined.png");
   background-repeat: no-repeat;
   background-position: 5px center; }
 
-TABLE#freeBusy TR.accepted TD.attendees
+TABLE#freeBusyAttendees TR.accepted TD.attendees
 { background-image: url("accepted.png");
   background-repeat: no-repeat;
   background-position: 5px center; }
 
-TABLE#freeBusy TR.futureAttendee INPUT
-{ background-image: none; }
-
-TABLE#freeBusy TR.freeBusyHeader2 TH
+TABLE#freeBusyHeader TR.freeBusyHeader2 TH
 { font-weight: normal; }
 
-TABLE#freeBusy TR.freeBusyHeader1 TH,
-TABLE#freeBusy TR.freeBusyHeader2 TH,
-TABLE#freeBusy TR.freeBusyHeader3 TH
+TABLE#freeBusyHeader TR.freeBusyHeader1 TH,
+TABLE#freeBusyHeader TR.freeBusyHeader2 TH,
+TABLE#freeBusyHeader TR.freeBusyHeader3 TH
 { text-align: left;
   color: #777;
   background: #fff; }
 
-TABLE#freeBusy TR.freeBusyHeader2 TH
+TABLE#freeBusyHeader TR.freeBusyHeader2 TH
 { padding-right: 2em; }
 
-TABLE#freeBusy TR.freeBusyHeader3 TH
+TABLE#freeBusyHeader TR.freeBusyHeader3 TH
 { border-left: 1px solid #fff;
   border-bottom: 1px solid #cecbff; }
 
-TABLE#freeBusy TR.attendeeModel
+TABLE#freeBusyAttendees TR.attendeeModel,
+TABLE#freeBusyData TR.dataModel
 { display: none; }
 
-TABLE#freeBusy TR.futureAttendee TD
-{ border-right: none; }
+TABLE#freeBusy TD.freeBusyHeader,
+TABLE#freeBusy TD.freeBusyData
+{ border-left: 2px solid #bbb; }
 
-TABLE#freeBusy TD
+TABLE#freeBusyAttendees TD,
+TABLE#freeBusyData TD
 { border-bottom: 1px solid #cecbff;
-  border-right: 1px solid #cecbff;
+  border-left: 1px solid #cecbff;
+  border-top: 0px;
+  border-right: 0px;
   height: 2em;
   background-color: #fff; }
 
-TABLE#freeBusy TD.noFreeBusy
-{ background-color: #559;
-  border-right: 0px; }
+TABLE#freeBusyAttendees TD
+{ border-left: 1px solid #fff; }
+
+TABLE#freeBusyData TR.futureData TD
+{ border-left: none; }
+
+TABLE#freeBusy TD.freeBusyAttendees DIV
+{ width: 16em; }
+
+TABLE#freeBusyAttendees TD.attendees INPUT
+{ background-image: url("abcard.gif");
+  background-repeat: no-repeat;
+  background-position: 4px center;
+  border: 0px;
+  width: 12em;
+  padding-left: 24px;
+  margin-left: 2em; }
+
+TABLE#freeBusyAttendees TR.futureAttendee INPUT
+{ background-image: none; }
 
 SPAN.freeBusyZoneElement
 { display: block;
@@ -128,17 +129,17 @@ SPAN.freeBusyZoneElement
   padding: 0px;
   border: 0px; }
 
-TABLE#freeBusy TR.freeBusyHeader3 SPAN.freeBusyZoneElement
+TABLE#freeBusyHeader TR.freeBusyHeader3 SPAN.freeBusyZoneElement
 { height: .25em; }
 
-TABLE#freeBusy TD SPAN.freeBusyZoneElement
-{ height: 100%; }
+TABLE#freeBusyData TD SPAN.freeBusyZoneElement
+{ height: 98%; /* not nice under Safari */ }
 
-SPAN[class~="colorBox"].free,
-TABLE#freeBusy TD SPAN.freeBusyZoneElement
+SPAN.colorBox.free,
+TABLE#freeBusyData TD SPAN.freeBusyZoneElement
 { background-color: #8ca6bd; }
 
-TABLE#freeBusy TH SPAN[class~="freeBusyZoneElement"].busy
+TABLE#freeBusyHeader TH SPAN.freeBusyZoneElement.busy
 { background-color: #c55 !important; }
 
 DIV#freeBusyFooter
@@ -184,15 +185,16 @@ SPAN.colorBox
   width: 1em;
   height: .75em; }
 
-SPAN[class~="colorBox"].busy,
-SPAN[class~="freeBusyZoneElement"].busy
+SPAN.colorBox.busy,
+SPAN.freeBusyZoneElement.busy
 { background-color: #5a6b79 !important; }
 
-SPAN[class~="colorBox"].maybe-busy,
-SPAN[class~="freeBusyZoneElement"].maybe-busy
+SPAN.colorBox.maybe-busy,
+SPAN.freeBusyZoneElement.maybe-busy
 { background-color: #adc0d0 !important; }
 
-SPAN[class~="colorBox"].noFreeBusy
+SPAN.colorBox.noFreeBusy,
+TABLE#freeBusyData TD.noFreeBusy
 { background-color: #559; }
 
 DIV#freeBusyViewButtons
@@ -202,7 +204,7 @@ DIV#freeBusyViewButtons
   padding: 3px;
   height: 2em;
   top: 0px;
-  left: 15em; }
+  left: 16em; }
 
 DIV#freeBusyZoomButtons
 { position: absolute;
index 14aae456e4826992029a630a51d9ba6741ee2704..21b4ae462facc0bc07ab1ac4b71271ac14e8b008 100644 (file)
@@ -99,12 +99,9 @@ function performSearchCallback(http) {
        }
 
        // Show popup menu
-       var offset;
-       if (isSafari())
-         offset = Position.positionedOffset(currentField);
-       else
-         offset = Position.cumulativeOffset(currentField);
-       var top = offset[1] + node.offsetHeight + 3;
+       var offsetScroll = Element.cumulativeScrollOffset(currentField);
+       var offset = Element.cumulativeOffset(currentField);
+       var top = offset[1] - offsetScroll[1] + node.offsetHeight + 3;
        var height = 'auto';
        if (data.length > 5) {
          height = 5 * node.getHeight() + 'px';
@@ -161,9 +158,9 @@ function onAttendeeResultClick(event) {
 }
 
 function resetFreeBusyZone() {
-  var table = $("freeBusy");
-  var row = table.tHead.rows[2];
-  for (var i = 1; i < row.cells.length; i++) {
+  var table = $("freeBusyHeader");
+  var row = table.rows[2];
+  for (var i = 0; i < row.cells.length; i++) {
     var nodes = $(row.cells[i]).childNodesWithTag("span");
     for (var j = 0; j < nodes.length; j++)
       nodes[j].removeClassName("busy");
@@ -171,8 +168,8 @@ function resetFreeBusyZone() {
 }
 
 function redisplayFreeBusyZone() {
-  var table = $("freeBusy");
-  var row = table.tHead.rows[2];
+  var table = $("freeBusyHeader");
+  var row = table.rows[2];
   var stDay = $("startTime_date").valueAsDate();
   var etDay = $("endTime_date").valueAsDate();
 
@@ -215,7 +212,7 @@ function redisplayFreeBusyZone() {
 
   var deltaCells = (etHour - stHour) + (11 * addDays);
   var deltaSpans = (deltaCells * 4 ) + (etMinute - stMinute);
-  var currentCellNbr = stHour - 7;
+  var currentCellNbr = stHour - 7 - 1;
   var currentCell = row.cells[currentCellNbr];
   var currentSpanNbr = stMinute;
   var spans = $(currentCell).childNodesWithTag("span");
@@ -235,12 +232,12 @@ function redisplayFreeBusyZone() {
 }
 
 function newAttendee(event) {
-   var table = $("freeBusy");
+   var table = $("freeBusyAttendees");
    var tbody = table.tBodies[0];
    var model = tbody.rows[tbody.rows.length - 1];
-   var newAttendeeRow = tbody.rows[tbody.rows.length - 2];
+   var futureRow = tbody.rows[tbody.rows.length - 2];
    var newRow = model.cloneNode(true);
-   tbody.insertBefore(newRow, newAttendeeRow);
+   tbody.insertBefore(newRow, futureRow);
   
    $(newRow).removeClassName("attendeeModel");
  
@@ -250,6 +247,19 @@ function newAttendee(event) {
 
    input.focussed = true;
    input.activate();
+
+   table = $("freeBusyData");
+   tbody = table.tBodies[0];
+   model = tbody.rows[tbody.rows.length - 1];
+   futureRow = tbody.rows[tbody.rows.length - 2];
+   newRow = model.cloneNode(true);
+   tbody.insertBefore(newRow, futureRow);
+   $(newRow).removeClassName("dataModel");
+
+   var attendeesDiv = $$('TABLE#freeBusy TD.freeBusyAttendees DIV').first();
+   var dataDiv = $$('TABLE#freeBusy TD.freeBusyData DIV').first();
+   
+   dataDiv.scrollTop = attendeesDiv.scrollTop;
 }
 
 function checkAttendee() {
@@ -267,8 +277,12 @@ function checkAttendee() {
   this.focussed = false;
   var row = this.parentNode.parentNode;
   var tbody = row.parentNode;
-  if (tbody && this.value.trim().length == 0)
+  if (tbody && this.value.trim().length == 0) {
+    var dataTable = $("freeBusyData").tBodies[0];
+    var dataRow = dataTable.rows[row.sectionRowIndex];
     tbody.removeChild(row);
+    dataTable.removeChild(dataRow);
+  }
   else if (this.readAttribute("modified") == "1") {
     if (!$(row).hasClassName("needs-action")) {
       $(row).addClassName("needs-action");
@@ -287,13 +301,14 @@ function checkAttendee() {
   currentField = null;
 }
 
-function displayFreeBusyForNode(node) {
-  var nodes = node.parentNode.parentNode.cells;
-  if (node.uid) {
+function displayFreeBusyForNode(input) {
+  var rowIndex = input.parentNode.parentNode.sectionRowIndex;
+  var nodes = $("freeBusyData").tBodies[0].rows[rowIndex].cells;
+  if (input.uid) {
     if (document.contactFreeBusyAjaxRequest)
-      awaitingFreeBusyRequests.push(node);
+      awaitingFreeBusyRequests.push(input);
     else {
-      for (var i = 1; i < nodes.length; i++) {
+      for (var i = 0; i < nodes.length; i++) {
        $(nodes[i]).removeClassName("noFreeBusy");
        $(nodes[i]).innerHTML = ('<span class="freeBusyZoneElement"></span>'
                                 + '<span class="freeBusyZoneElement"></span>'
@@ -307,16 +322,16 @@ function displayFreeBusyForNode(node) {
       }
       var sd = $('startTime_date').valueAsShortDateString();
       var ed = $('endTime_date').valueAsShortDateString();
-      var urlstr = ( UserFolderURL + "../" + node.uid + "/freebusy.ifb/ajaxRead?"
+      var urlstr = ( UserFolderURL + "../" + input.uid + "/freebusy.ifb/ajaxRead?"
                     + "sday=" + sd + "&eday=" + ed + "&additional=" +
                     additionalDays );
       document.contactFreeBusyAjaxRequest
        = triggerAjaxRequest(urlstr,
                             updateFreeBusyDataCallback,
-                            node);
+                            input);
     }
   } else {
-    for (var i = 1; i < nodes.length; i++) {
+    for (var i = 0; i < nodes.length; i++) {
       $(nodes[i]).addClassName("noFreeBusy");
       $(nodes[i]).update();
     }
@@ -333,7 +348,7 @@ function setSlot(tds, nbr, status) {
   }
   if (tdnbr > 7 && tdnbr < 19) {
     var i = (days * 11 + tdnbr - 7);
-    var td = tds[i];
+    var td = tds[i - 1];
     var spans = $(td).childNodesWithTag("span");
     if (status == '2')
       $(spans[spannbr]).addClassName("maybe-busy");
@@ -345,12 +360,13 @@ function setSlot(tds, nbr, status) {
 function updateFreeBusyDataCallback(http) {
   if (http.readyState == 4) {
     if (http.status == 200) {
-      var node = http.callbackData;
+      var input = http.callbackData;
       var slots = http.responseText.split(",");
-      var tds = node.parentNode.parentNode.cells;
+      var rowIndex = input.parentNode.parentNode.sectionRowIndex;
+      var nodes = $("freeBusyData").tBodies[0].rows[rowIndex].cells;
       for (var i = 0; i < slots.length; i++) {
         if (slots[i] != '0')
-         setSlot(tds, i, slots[i]);
+         setSlot(nodes, i, slots[i]);
       }
     }
     document.contactFreeBusyAjaxRequest = null;
@@ -516,7 +532,7 @@ function prepareTableHeaders() {
    var endDate = endTimeDate.valueAsDate();
    endDate.setTime(endDate.getTime() + (additionalDays * 86400000));
 
-   var rows = $("freeBusy").tHead.rows;
+   var rows = $("freeBusyHeader").rows;
    var days = startDate.daysUpTo(endDate);
    for (var i = 0; i < days.length; i++) {
       var header1 = document.createElement("th");
@@ -550,29 +566,37 @@ function prepareTableRows() {
    var endDate = endTimeDate.valueAsDate();
    endDate.setTime(endDate.getTime() + (additionalDays * 86400000));
 
-   var rows = $("freeBusy").tBodies[0].rows;
+   var rows = $("freeBusyData").tBodies[0].rows;
    var days = startDate.daysUpTo(endDate);
+   var width = $('freeBusyHeader').getWidth();
+   $("freeBusyData").setStyle({ width: width + 'px' });
    for (var i = 0; i < days.length; i++)
       for (var rowNbr = 0; rowNbr < rows.length; rowNbr++)
-        for (var hour = dayStartHour; hour < (dayEndHour + 1); hour++)
-           rows[rowNbr].appendChild(document.createElement("td"));
+       for (var hour = dayStartHour; hour < (dayEndHour + 1); hour++)
+         rows[rowNbr].appendChild(document.createElement("td"));
 }
 
 function prepareAttendees() {
    var value = parent$("attendeesNames").value;
-   var table = $("freeBusy");
+   var tableAttendees = $("freeBusyAttendees");
+   var tableData = $("freeBusyData");
    if (value.length > 0) {
       attendeesEditor.names = parent$("attendeesNames").value.split(",");
       attendeesEditor.UIDs = parent$("attendeesUIDs").value.split(",");
       attendeesEditor.emails = parent$("attendeesEmails").value.split(",");
       attendeesEditor.states = parent$("attendeesStates").value.split(",");
 
-      var tbody = table.tBodies[0];
-      var model = tbody.rows[tbody.rows.length - 1];
-      var newAttendeeRow = tbody.rows[tbody.rows.length - 2];
+      var tbodyAttendees = tableAttendees.tBodies[0];
+      var modelAttendee = tbodyAttendees.rows[tbodyAttendees.rows.length - 1];
+      var newAttendeeRow = tbodyAttendees.rows[tbodyAttendees.rows.length - 2];
+
+      var tbodyData = tableData.tBodies[0];
+      var modelData = tbodyData.rows[tbodyData.rows.length - 1];
+      var newDataRow = tbodyData.rows[tbodyData.rows.length - 2];
+
       for (var i = 0; i < attendeesEditor.names.length; i++) {
-        var row = model.cloneNode(true);
-        tbody.insertBefore(row, newAttendeeRow);
+        var row = modelAttendee.cloneNode(true);
+        tbodyAttendees.insertBefore(row, newAttendeeRow);
         $(row).removeClassName("attendeeModel");
         $(row).addClassName(attendeesEditor.states[i]);
         var input = $(row).down("input");
@@ -587,6 +611,11 @@ function prepareAttendees() {
         input.setAttribute("modified", "0");
         input.observe("blur", checkAttendee);
         input.observe("keydown", onContactKeydown);
+        
+        row = modelData.cloneNode(true);
+        tbodyData.insertBefore(row, newDataRow);
+        $(row).removeClassName("dataModel");
+        
         displayFreeBusyForNode(input);
       }
    }
@@ -596,11 +625,35 @@ function prepareAttendees() {
       attendeesEditor.emails = new Array();
    }
 
-   var inputs = table.getElementsByTagName("input");
+   var inputs = tableAttendees.getElementsByTagName("input");
    inputs[inputs.length - 2].setAttribute("autocomplete", "off");
    Event.observe(inputs[inputs.length - 2], "click", newAttendee);
 }
 
+function onWindowResize(event) {
+  var view = $('freeBusyView');
+  var attendeesCell = $$('TABLE#freeBusy TD.freeBusyAttendees').first();
+  var headerDiv = $$('TABLE#freeBusy TD.freeBusyHeader DIV').first();
+  var attendeesDiv = $$('TABLE#freeBusy TD.freeBusyAttendees DIV').first();
+  var dataDiv = $$('TABLE#freeBusy TD.freeBusyData DIV').first();
+  var width = view.getWidth() - attendeesCell.getWidth();
+  var height = view.getHeight() - headerDiv.getHeight();
+
+  attendeesDiv.setStyle({ height: (height - 20) + 'px' });
+  headerDiv.setStyle({ width: (width - 20) + 'px' });
+  dataDiv.setStyle({ width: (width - 4) + 'px',
+                    height: (height - 2) + 'px' });
+}
+
+function onScroll(event) {
+  var headerDiv = $$('TABLE#freeBusy TD.freeBusyHeader DIV').first();
+  var attendeesDiv = $$('TABLE#freeBusy TD.freeBusyAttendees DIV').first();
+  var dataDiv = $$('TABLE#freeBusy TD.freeBusyData DIV').first();
+
+  headerDiv.scrollLeft = dataDiv.scrollLeft;
+  attendeesDiv.scrollTop = dataDiv.scrollTop;
+}
+
 function onFreeBusyLoadHandler() {
    initializeWindowButtons();
    initializeTimeWidgets();
@@ -608,6 +661,9 @@ function onFreeBusyLoadHandler() {
    prepareTableRows();
    redisplayFreeBusyZone();
    prepareAttendees();
+   onWindowResize(null);
+   Event.observe(window, "resize", onWindowResize);
+   Event.observe($$('TABLE#freeBusy TD.freeBusyData DIV').first(), "scroll", onScroll);
 }
 
 FastInit.addOnLoad(onFreeBusyLoadHandler);
index 59dfff2d35491a20b1b347f793c233e5e6e7b05e..d44b1b89db8d6f51cdb4304a8f95f353ca0b584d 100644 (file)
@@ -77,36 +77,6 @@ function onChangeCalendar(event) {
   form.setAttribute("action", urlElems.join("/"));
 }
 
-function refreshAttendees() {
-  var attendeesLabel = $("attendeesLabel");
-  var attendeesNames = $("attendeesNames");
-  var attendeesHref = $("attendeesHref");
-
-  for (var i = 0; i < attendeesHref.childNodes.length; i++)
-    attendeesHref.removeChild(attendeesHref.childNodes[i]);
-
-  if (attendeesNames.value.length > 0) {
-    attendeesHref.appendChild(document.createTextNode(attendeesNames.value));
-    attendeesLabel.setStyle({ display: "block" });
-  }
-  else {
-    attendeesLabel.setStyle({ display: "none" });
-  }
-}
-
-function initializeAttendeesHref() {
-  var attendeesHref = $("attendeesHref");
-  var attendeesLabel = $("attendeesLabel");
-  var attendeesNames = $("attendeesNames");
-
-  Event.observe(attendeesHref, "click", onPopupAttendeesWindow, false);
-  if (attendeesNames.value.length > 0) {
-    attendeesHref.setStyle({ textDecoration: "underline", color: "#00f" });
-    attendeesHref.appendChild(document.createTextNode(attendeesNames.value));
-    attendeesLabel.setStyle({ display: "block" });
-  }
-}
-
 function initializeDocumentHref() {
   var documentHref = $("documentHref");
   var documentLabel = $("documentLabel");
@@ -141,8 +111,6 @@ function initializePrivacyMenu() {
 }
 
 function onComponentEditorLoad(event) {
-  if (!$("statusPercent"))
-    initializeAttendeesHref();
   initializeDocumentHref();
   initializePrivacyMenu();
   var list = $("calendarList");
@@ -156,6 +124,28 @@ function onComponentEditorLoad(event) {
     Event.observe(menuItems[i], "mousedown",
                  onMenuSetClassification.bindAsEventListener(menuItems[i]),
                  false);
+
+  $("repeatHref").observe("click", onPopupRecurrenceWindow);
+  $("repeatList").observe("change", onPopupRecurrenceWindow);
+  onPopupRecurrenceWindow(null);
+}
+
+function onPopupRecurrenceWindow(event) {
+  if (event)
+    preventDefault(event);
+
+  var repeatHref = $("repeatHref");
+
+  if ($("repeatList").value == 7) {
+    repeatHref.show();
+    if (event)
+      window.open(ApplicationBaseURL + "editRecurrence", null, 
+                 "width=500,height=400");
+  }
+  else
+    repeatHref.hide();
+
+  return false;
 }
 
 FastInit.addOnLoad(onComponentEditorLoad);
index 7ad0908694824b8867f52562f636689b437c44cb..57fc1a342f53b8b07dd26c0fb851f0e83aba7e95 100644 (file)
@@ -133,8 +133,10 @@ function onConfirmFolderSelection(event) {
         folderName = spans1[0].innerHTML + ' (' + email + ')';
       }
       var data = { folderName: folderName, folder: folder, window: window };
-      if (!window.opener.subscribeToFolder(window.opener.userFolderCallback, data))
+      if (parent$(accessToSubscribedFolder(folder)))
        window.alert(clabels["You have already subscribed to that folder!"]);
+      else
+       window.opener.subscribeToFolder(window.opener.userFolderCallback, data);
    }
 }
 
index 054005e0a257c051322baf7b47dfe37d0b1884c6..edf3f909df9788ef966b7d90e2aa6e75105fd48f 100644 (file)
@@ -257,8 +257,16 @@ function openMailComposeWindow(url, wId) {
 }
 
 function openMailTo(senderMailTo) {
-  var mailto = sanitizeMailTo(senderMailTo);
+  var addresses = senderMailTo.split(",");
+  var sanitizedAddresses = new Array();
+  for (var i = 0; i < addresses.length; i++) {
+    var sanitizedAddress = sanitizeMailTo(addresses[i]);
+    if (sanitizedAddress.length > 0)
+      sanitizedAddresses.push(sanitizedAddress);
+  }
 
+  var mailto = sanitizedAddresses.join(",");
   if (mailto.length > 0)
     openMailComposeWindow(ApplicationBaseURL
                          + "../Mail/compose?mailto=" + mailto);
@@ -266,6 +274,16 @@ function openMailTo(senderMailTo) {
   return false; /* stop following the link */
 }
 
+function deleteDraft(url) {
+  /* this is called by UIxMailEditor with window.opener */
+  new Ajax.Request(url, {
+    method: 'post',
+    onFailure: function(transport) {
+       log("draftDeleteCallback: problem during ajax request: " + transport.status);
+      }
+    });
+}
+
 function createHTTPClient() {
   // http://developer.apple.com/internet/webcontent/xmlhttpreq.html
   if (typeof XMLHttpRequest != "undefined")
@@ -579,10 +597,10 @@ function popupMenu(event, menuId, target) {
                  - (menuLeft + popup.offsetWidth));
   if (leftDiff < 0)
     menuLeft -= popup.offsetWidth;
-
+  
   if (popup.prepareVisibility)
     popup.prepareVisibility();
-  
+
   popup.setStyle({ top: menuTop + "px",
                   left: menuLeft + "px",
                   visibility: "visible" });
@@ -614,6 +632,8 @@ function getParentMenu(node) {
 function onBodyClickMenuHandler(event) {
   hideMenu(document.currentPopupMenu);
   document.body.stopObserving("click", onBodyClickMenuHandler);
+  document.body.stopObserving("mouseup", onBodyClickMenuHandler);
+  document.currentPopupMenu = null;
 
   if (event)
     preventDefault(event);
@@ -965,13 +985,18 @@ function popupToolbarMenu(node, menuId) {
     hideMenu(document.currentPopupMenu);
 
   var popup = $(menuId);
-  var top = ($(node).getStyle('top') || 0) + node.offsetHeight - 2;
+
+  if (popup.prepareVisibility)
+    popup.prepareVisibility();
+
+  var offset = $(node).cumulativeOffset();
+  var top = offset.top + node.offsetHeight;
   popup.setStyle({ top: top + "px",
-       left: $(node).cascadeLeftOffset() + "px",
+       left: offset.left + "px",
        visibility: "visible" });
 
   document.currentPopupMenu = popup;
-  $(document.body).observe("click", onBodyClickMenuHandler);
+  $(document.body).observe("mouseup", onBodyClickMenuHandler);
 }
 
 /* contact selector */
index 0dd08ca0dc755d3928143b069867a375f0f6b18e..8fcecab358d07291ef5c93a9db9f8518e2fe7f9c 100644 (file)
@@ -78,23 +78,20 @@ DIV#attendeesMenu
   overflow-x: hidden; }
 
 DIV#attendeesView
-{ left: 0px; }
+{ left: 0.5em; }
 
 DIV#freeBusyView
 { border-bottom: 1px solid #fff;
   border-right: 1px solid #fff;
   border-top: 2px solid #222;
   border-left: 2px solid #222;
-  left: 0.5em;
-  margin-left: 0.5em; }
-
-TABLE#freeBusy TH.attendees,
-TABLE#freeBusy TD.attendees
-{ background-color: #fff !important;
-  padding-right: 5px;
-  padding-left: 0px; }
-
-TABLE#freeBusy TD.attendees INPUT
-{ border-bottom: 1px solid #ccc;
-  border-right: 1px solid #ccc;
-  margin-left: 2.0em; }
+  left: 0.5em; }
+
+TABLE#freeBusyData TD.noFreeBusy
+{ height: 2.2em; }
+
+TABLE#freeBusyData TD SPAN.freeBusyZoneElement
+{ height: 103%; }
+
+TABLE
+{ empty-cells: show; }
\ No newline at end of file
index 897aa236b97935835cd64845f0829baddb68f5d9..776e597b90c9be8065f8cca038b9e54a16eae67f 100644 (file)
@@ -62,7 +62,7 @@ function cal_popup1(str_datetime) {
 
   var obj_calwindow = window.open(
     this.calpage+'?datetime=' + this.dt_current.valueOf()+ '&id=' + this.id,
-      'Calendar', 'width=200,height=190'+
+      'Calendar', 'width=253,height=190'+
       ',status=no,resizable=no,top=200,left=200,dependent=yes,alwaysRaised=yes'
     );
   obj_calwindow.opener = window;