+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
#import <WEExtensions/WEResourceManager.h>
+#import <SoObjects/SOGo/SOGoCache.h>
#import <SoObjects/SOGo/SOGoDAVAuthenticator.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import <SoObjects/SOGo/SOGoUserFolder.h>
@interface SOGo : SoApplication
{
NSMutableDictionary *localeLUT;
+ SOGoCache *cache;
}
- (NSDictionary *) currentLocaleConsideringLanguages:(NSArray *)_langs;
static NSArray *runLoopModes = nil;
WOResponse *resp;
+ cache = [SOGoCache sharedCache];
resp = [super dispatchRequest: _request];
+ [SOGoCache killCache];
if (![self isTerminating])
{
+0.9.0-2008MMDD (1.0 rc4)
+------------------------
+- improved the attendees window;
+
0.9.0-20071217 (1.0 rc3)
------------------------
- mail folders state is now saved;
+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
- (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;
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) {
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];
/* 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 {
{
EOAdaptorChannel *storeChannel, *quickChannel;
NSMutableDictionary *quickRow, *contentRow;
+ NSDictionary *currentRow;
GCSFieldExtractor *extractor;
NSException *error;
NSNumber *storedVersion;
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]) {
#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])
#import <SaxObjC/XMLNamespaces.h>
// #import <NGObjWeb/SoClassSecurityInfo.h>
+#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoCustomGroupFolder.h>
#import <SOGo/LDAPUserManager.h>
#import <SOGo/SOGoPermissions.h>
obj = [NSException exceptionWithHTTPStatus:404 /* Not Found */];
}
+ if (obj)
+ [[SOGoCache sharedCache] registerObject: obj
+ withName: _key
+ inContainer: container];
+
return obj;
}
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])
{
if (secure)
iCalString = [self secureContentAsString];
else
- iCalString = content;
+ iCalString = [record objectForKey: @"c_content"];
if ([iCalString length] > 0)
calendar = [iCalCalendar parseSingleFromSource: iCalString];
- (NGVCard *) vCard
{
+ NSString *content;
+
if (!card)
{
+ content = [record objectForKey: @"c_content"];
if ([[content uppercaseString] hasPrefix: @"BEGIN:VCARD"])
card = [NGVCard parseSingleFromSource: content];
else
/* 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]];
}
else
clazz = Nil;
-
+
return [clazz objectWithName:_key inContainer: self];
}
FHS_HEADER_DIRS = SOGo
libSOGo_HEADER_FILES = \
+ SOGoCache.h \
SOGoObject.h \
SOGoContentObject.h \
SOGoFolder.h \
WORequest+SOGo.h
libSOGo_OBJC_FILES = \
+ SOGoCache.m \
SOGoObject.m \
SOGoContentObject.m \
SOGoFolder.m \
@interface SOGoContentObject : SOGoObject
{
NSString *ocsPath;
- NSString *content;
+ NSDictionary *record;
BOOL isNew;
}
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
#import "NSDictionary+Utilities.h"
#import "NSString+Utilities.h"
+#import "SOGoCache.h"
#import "SOGoObject.h"
@interface SOGoObject(Content)
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;
}
#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"
+ (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;
}
[super dealloc];
}
+- (void) setPrimaryRoles: (NSArray *) newRoles
+{
+ ASSIGN (roles, newRoles);
+}
+
- (void) setCurrentPassword: (NSString *) newPassword
{
ASSIGN (currentPassword, newPassword);
/* 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"]];
"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";
"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";
"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.";
"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";
"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";
UIxTimeDateControl.m \
UIxCalParticipationStatusView.m \
UIxCalMonthOverview.m \
- UIxCalMonthViewOld.m
+ UIxCalMonthViewOld.m \
+ UIxRecurrenceEditor.m
SchedulerUI_RESOURCE_FILES += \
Version \
"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";
"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";
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 */
aptEndDate = nil;
item = nil;
event = nil;
- repeat = nil;
isAllDay = NO;
}
- (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
- (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
/* 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>
*
- (void) setCurrentCalendar: (NSDictionary *) newCalendar;
- (NSDictionary *) currentCalendar;
+- (WOResponse *) calendarsListAction;
+
@end
#endif /* UIXCALENDARSELECTOR_H */
/* 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>
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 */
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;
#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...
- (void) setTaskDueDate: (NSCalendarDate *) _date;
- (NSCalendarDate *) taskDueDate;
-- (NSString *) repeat;
-- (void) setRepeat: (NSString *) newRepeat;
-
@end
#endif /* UIXAPPOINTMENTEDITOR_H */
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;
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";
+ };
};
};
<!-- TODO: add iMIP actions -->
<input id="iCalendarAttachment" type="hidden"
- var:value="pathToAttachmentObject"/>
+ var:value="pathToAttachment"/>
<var:if condition="couldParseCalendar" const:negate="1">
<fieldset>
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>
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>
<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>
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>
<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>
padding: 0px; }
TABLE#contactsList
-{ width: 100%; }
+{ -khtml-user-select: none;
+ width: 100%; }
TABLE#contactsList TD,
TABLE#contactsList TH
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++;
+ });
+ }
});
{ color: #777;
font-style: italic; }
+DIV[datatype~="additional"] > A.node._selected > SPAN.nodeName
+{ color: #fff; }
+
/* drag-n-drop */
IMG.dragMessage
{ position: absolute;
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;
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
document.selectedDate = td;
changeCalendarDisplay( { "day": day } );
+ currentDay = day;
if (needRefresh)
refreshEvents();
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;
}
li.appendChild(checkBox);
li.appendChild(document.createTextNode(" "));
$(checkBox).addClassName("checkBox");
+ if (owner == UserLogin)
+ checkBox.checked = 1;
var colorBox = document.createElement("div");
li.appendChild(colorBox);
padding-bottom: 0em; }
SELECT#calendarList
-{ width: 17em; }
+{ width: 16em; }
A#changeUrlButton
{ margin-left: 1em; }
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
*/
var contactSelectorAction = 'calendars-contacts';
+var AppointmentEditor = {
+ attendeesMenu: null
+};
function uixEarlierDate(date1, date2) {
// can this be done in a sane way?
}
}
+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');
}
}
+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');
'hour': $("endTime_time_hour"),
'minute': $("endTime_time_minute")}};
initTimeWidgets(widgets);
+ initializeAttendeesHref();
}
FastInit.addOnLoad(onAppointmentEditorLoad);
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;
-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;
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
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
padding: 3px;
height: 2em;
top: 0px;
- left: 15em; }
+ left: 16em; }
DIV#freeBusyZoomButtons
{ position: absolute;
}
// 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';
}
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");
}
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();
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");
}
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");
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() {
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");
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>'
}
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();
}
}
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");
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;
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");
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");
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);
}
}
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();
prepareTableRows();
redisplayFreeBusyZone();
prepareAttendees();
+ onWindowResize(null);
+ Event.observe(window, "resize", onWindowResize);
+ Event.observe($$('TABLE#freeBusy TD.freeBusyData DIV').first(), "scroll", onScroll);
}
FastInit.addOnLoad(onFreeBusyLoadHandler);
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");
}
function onComponentEditorLoad(event) {
- if (!$("statusPercent"))
- initializeAttendeesHref();
initializeDocumentHref();
initializePrivacyMenu();
var list = $("calendarList");
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);
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);
}
}
}
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);
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")
- (menuLeft + popup.offsetWidth));
if (leftDiff < 0)
menuLeft -= popup.offsetWidth;
-
+
if (popup.prepareVisibility)
popup.prepareVisibility();
-
+
popup.setStyle({ top: menuTop + "px",
left: menuLeft + "px",
visibility: "visible" });
function onBodyClickMenuHandler(event) {
hideMenu(document.currentPopupMenu);
document.body.stopObserving("click", onBodyClickMenuHandler);
+ document.body.stopObserving("mouseup", onBodyClickMenuHandler);
+ document.currentPopupMenu = null;
if (event)
preventDefault(event);
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 */
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
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;