Monotone-Parent: 52de90085e2d18ded0d0c315e7148e1c72feb785

Monotone-Revision: 99d14630f5294d40da6680a39d69b6f1b7853938

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2008-07-17T21:12:43
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2008-07-17 21:12:43 +00:00
parent e1beed1ba0
commit bdc02c26ab
46 changed files with 1595 additions and 521 deletions

View File

@ -1,3 +1,48 @@
2008-07-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions
-eventsBlocksAction]): event blocks now that the "c_recurrence_id"
virtual field into account.
* UI/Common/UIxObjectActions.m ([UIxObjectActions -deleteAction]):
new action method, moved from UIxContactView so that it is
generalized to all SOGoObjects.
* SoObjects/Appointments/SOGoCalendarComponent.m
([SOGoCalendarComponent -init]): we now save the calendar to avoid
multiple parsing and instantiation of children components.
([SOGoCalendarComponent -toOneRelationshipKeys]): declare
components as children.
([SOGoCalendarComponent
-lookupName:lookupNameinContext:localContextacquire:acquire]):
handle the search for components and their occurences.
([SOGoCalendarComponent -saveComponent:newObject]): added code to
update recurrence ids whenever the user saves a new version of the
master component.
* SoObjects/Appointments/SOGoAppointmentObject.m ([SOGoAppointmentObject -occurence:occ])
([SOGoAppointmentObject -newOccurenceWithID:recID])
([SOGoAppointmentObject -prepareDeleteOccurence:occurence]): new
methods to handle occurences.
* SoObjects/Appointments/SOGoAppointmentFolder.m
([SOGoAppointmentFolder -fixupCycleRecord:_recordcycleRange:_r]):
declare a new "c_recurrence_id" field, that will be overwritten
with further exception occurences.
* UI/Scheduler/UIxOccurenceDialog.[hm]: new dialog asking whether
to edit/delete all occurences of a recurrent event or only the one
selected.
* SoObjects/Appointments/SOGoComponentOccurence.[hm]: super class
of new classes belows.
* SoObjects/Appointments/SOGoAppointmentOccurence.[hm]: new class
module that handle occurences within events.
* SoObjects/Appointments/SOGoTaskOccurence.[hm]: new class
module that handle occurences within todos.
2008-07-16 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions

1
NEWS
View File

@ -14,6 +14,7 @@ address book in the Address Book module
- fixed various bugs occuring with Firefox 3
- fixed bug where selecting the current day cell would not select the header day cell and vice-versa in the daily and weekly views
- the events are now computed in the server code again, in order to speedup the drawing of events as well as to fix the bug where events would be shifted back or forth of one day, depending on how their start time would be compared to UTC time
- implemented the handling of exceptional occurences of recurrent events
0.9.0-20080520 (1.0 rc6)
------------------------

View File

@ -20,6 +20,9 @@ Appointments_OBJC_FILES = \
SOGoCalendarComponent.m \
SOGoAppointmentObject.m \
SOGoTaskObject.m \
SOGoComponentOccurence.m \
SOGoAppointmentOccurence.m \
SOGoTaskOccurence.m \
SOGoAppointmentFolder.m \
SOGoAppointmentFolders.m \
SOGoFreeBusyObject.m \

View File

@ -618,6 +618,7 @@ static Class sogoAppointmentFolderKlass = Nil;
cycleRange: (NGCalendarDateRange *) _r
{
NSMutableDictionary *md;
NSNumber *dateSecs;
id tmp;
md = [[_record mutableCopy] autorelease];
@ -627,7 +628,9 @@ static Class sogoAppointmentFolderKlass = Nil;
tmp = [_r startDate];
[tmp setTimeZone: timeZone];
[md setObject:tmp forKey:@"startDate"];
[md setObject: [NSNumber numberWithInt: [tmp timeIntervalSince1970]] forKey: @"c_startdate"];
dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]];
[md setObject: dateSecs forKey: @"c_startdate"];
[md setObject: dateSecs forKey: @"c_recurrence_id"];
tmp = [_r endDate];
[tmp setTimeZone: timeZone];
[md setObject:tmp forKey:@"endDate"];
@ -682,11 +685,11 @@ static Class sogoAppointmentFolderKlass = Nil;
{
NSCalendarDate *startDate, *recurrenceId;
NSMutableDictionary *newRecord;
NSDictionary *oldRecord;
NGCalendarDateRange *newRecordRange;
int recordIndex;
newRecord = nil;
recurrenceId = [component recurrenceId];
if ([dateRange containsDate: recurrenceId])
{
@ -698,6 +701,11 @@ static Class sogoAppointmentFolderKlass = Nil;
if ([dateRange containsDate: startDate])
{
newRecord = [self fixupRecord: [component quickRecord]];
[newRecord setObject: [NSNumber numberWithInt: 1]
forKey: @"c_iscycle"];
oldRecord = [ma objectAtIndex: recordIndex];
[newRecord setObject: [oldRecord objectForKey: @"c_recurrence_id"]
forKey: @"c_recurrence_id"];
[ma replaceObjectAtIndex: recordIndex withObject: newRecord];
}
else
@ -754,6 +762,7 @@ static Class sogoAppointmentFolderKlass = Nil;
intoArray: (NSMutableArray *) _ma
{
NSMutableDictionary *row, *fixedRow;
NSMutableArray *recordArray;
NSDictionary *cycleinfo;
NSCalendarDate *startDate, *endDate;
NGCalendarDateRange *fir, *rRange;
@ -761,6 +770,8 @@ static Class sogoAppointmentFolderKlass = Nil;
unsigned i, count;
NSString *content;
recordArray = [NSMutableArray array];
content = [_row objectForKey: @"c_cycleinfo"];
if (![content isNotNull])
{
@ -800,12 +811,14 @@ static Class sogoAppointmentFolderKlass = Nil;
rRange = [ranges objectAtIndex: i];
fixedRow = [self fixupCycleRecord: row cycleRange: rRange];
if (fixedRow)
[_ma addObject:fixedRow];
[recordArray addObject: fixedRow];
}
[self _appendCycleExceptionsFromRow: row
forRange: _r
toArray: _ma];
toArray: recordArray];
[_ma addObjectsFromArray: recordArray];
}
- (NSArray *) fixupCyclicRecords: (NSArray *) _records

View File

@ -44,11 +44,12 @@
#import <SoObjects/SOGo/SOGoWebDAVValue.h>
#import <SoObjects/SOGo/WORequest+SOGo.h>
#import "NSArray+Appointments.h"
#import "SOGoAppointmentFolder.h"
#import "iCalEventChanges+SOGo.h"
#import "iCalEntityObject+SOGo.h"
#import "iCalPerson+SOGo.h"
#import "NSArray+Appointments.h"
#import "SOGoAppointmentFolder.h"
#import "SOGoAppointmentOccurence.h"
#import "SOGoAppointmentObject.h"
@ -129,6 +130,35 @@
return @"vevent";
}
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) occ
{
return [SOGoAppointmentOccurence occurenceWithComponent: occ
withMasterComponent: [self component: NO
secure: NO]
inContainer: self];
}
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID
{
iCalEvent *newOccurence;
NSCalendarDate *date;
unsigned int interval;
newOccurence = (iCalEvent *) [super newOccurenceWithID: recID];
date = [newOccurence recurrenceId];
interval = [[newOccurence endDate]
timeIntervalSinceDate: [newOccurence startDate]];
[newOccurence setStartDate: date];
[newOccurence setEndDate: [date addYear: 0
month: 0
day: 0
hour: 0
minute: 0
second: interval]];
return newOccurence;
}
- (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID
forUID: (NSString *) uid
{
@ -195,6 +225,7 @@
}
}
#warning what about occurences?
- (void) _handleRemovedUsers: (NSArray *) attendees
{
NSEnumerator *enumerator;
@ -573,7 +604,7 @@
return ex;
}
- (void) prepareDelete
- (void) prepareDeleteOccurence: (iCalEvent *) occurence
{
iCalEvent *event;
SOGoUser *currentUser;
@ -583,23 +614,32 @@
{
currentUser = [context activeUser];
event = [self component: NO secure: NO];
if (!occurence)
occurence = event;
if ([event userIsOrganizer: currentUser])
{
attendees = [event attendeesWithoutUser: currentUser];
attendees = [occurence attendeesWithoutUser: currentUser];
if (![attendees count] && event != occurence)
attendees = [event attendeesWithoutUser: currentUser];
if ([attendees count])
{
[self _handleRemovedUsers: attendees];
[self sendEMailUsingTemplateNamed: @"Deletion"
forOldObject: nil
andNewObject: [event itipEntryWithMethod: @"cancel"]
andNewObject: [occurence itipEntryWithMethod: @"cancel"]
toAttendees: attendees];
}
}
else if ([event userIsParticipant: currentUser])
else if ([occurence userIsParticipant: currentUser])
[self changeParticipationStatus: @"DECLINED"];
}
}
- (void) prepareDelete
{
[self prepareDeleteOccurence: nil];
}
/* message type */
- (NSString *) outlookMessageClass

View File

@ -0,0 +1,32 @@
/* SOGoAppointmentOccurence.h - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOAPPOINTMENTOCCURENCE_H
#define SOGOAPPOINTMENTOCCURENCE_H
#import "SOGoComponentOccurence.h"
@interface SOGoAppointmentOccurence : SOGoComponentOccurence
@end
#endif /* SOGOAPPOINTMENTOCCURENCE_H */

View File

@ -0,0 +1,27 @@
/* SOGoAppointmentOccurence.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "SOGoAppointmentOccurence.h"
@implementation SOGoAppointmentOccurence
@end

View File

@ -25,6 +25,8 @@
#import <SOGo/SOGoContentObject.h>
#import "SOGoComponentOccurence.h"
@class NSArray;
@class NSString;
@ -33,8 +35,14 @@
@class iCalRepeatableEntityObject;
@class SOGoUser;
@class SOGoComponentOccurence;
@interface SOGoCalendarComponent : SOGoContentObject
@interface SOGoCalendarComponent : SOGoContentObject <SOGoComponentOccurence>
{
iCalCalendar *fullCalendar;
iCalCalendar *safeCalendar;
iCalCalendar *originalCalendar;
}
- (NSString *) componentTag;
@ -66,6 +74,11 @@
- (iCalPerson *) iCalPersonWithUID: (NSString *) uid;
- (NSArray *) getUIDsForICalPersons: (NSArray *) iCalPersons;
/* recurrences */
- (SOGoComponentOccurence *)
occurence: (iCalRepeatableEntityObject *) component;
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID;
@end
#endif /* SOGOCALENDARCOMPONENT_H */

View File

@ -53,7 +53,9 @@
#import "SOGoAptMailNotification.h"
#import "iCalEntityObject+SOGo.h"
#import "iCalPerson+SOGo.h"
#import "iCalRepeatableEntityObject+SOGo.h"
#import "SOGoCalendarComponent.h"
#import "SOGoComponentOccurence.h"
static BOOL sendEMailNotifications = NO;
@ -74,6 +76,26 @@ static BOOL sendEMailNotifications = NO;
}
}
- (id) init
{
if ((self = [super init]))
{
fullCalendar = nil;
safeCalendar = nil;
originalCalendar = nil;
}
return self;
}
- (void) dealloc
{
[fullCalendar release];
[safeCalendar release];
[originalCalendar release];
[super dealloc];
}
- (NSString *) davContentType
{
return @"text/calendar";
@ -146,6 +168,150 @@ static BOOL sendEMailNotifications = NO;
return iCalString;
}
static inline BOOL
_occurenceHasID (iCalRepeatableEntityObject *occurence, NSString *recID)
{
unsigned int seconds, recSeconds;
seconds = [recID intValue];
recSeconds = [[occurence recurrenceId] timeIntervalSince1970];
return (seconds == recSeconds);
}
- (iCalRepeatableEntityObject *) lookupOccurence: (NSString *) recID
{
iCalRepeatableEntityObject *component, *occurence, *currentOccurence;
NSArray *occurences;
unsigned int count, max;
occurence = nil;
component = [self component: NO secure: NO];
if ([component hasRecurrenceRules])
{
occurences = [[self calendar: NO secure: NO] allObjects];
max = [occurences count];
count = 1;
while (!occurence && count < max)
{
currentOccurence = [occurences objectAtIndex: count];
if (_occurenceHasID (currentOccurence, recID))
occurence = currentOccurence;
else
count++;
}
}
return occurence;
}
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) component
{
[self subclassResponsibility: _cmd];
return nil;
}
- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID
{
iCalRepeatableEntityObject *masterOccurence, *newOccurence;
iCalCalendar *calendar;
NSCalendarDate *recDate;
recDate = [NSCalendarDate dateWithTimeIntervalSince1970: [recID intValue]];
masterOccurence = [self component: NO secure: NO];
if ([masterOccurence doesOccurOnDate: recDate])
{
newOccurence = [masterOccurence mutableCopy];
[newOccurence autorelease];
[newOccurence removeAllRecurrenceRules];
[newOccurence removeAllExceptionRules];
[newOccurence removeAllExceptionDates];
[newOccurence setOrganizer: nil];
[newOccurence setRecurrenceId: recDate];
calendar = [newOccurence parent];
[calendar addChild: newOccurence];
}
else
newOccurence = nil;
return newOccurence;
}
- (BOOL) isFolderish
{
return YES;
}
- (id) toManyRelationshipKeys
{
return nil;
}
- (id) toOneRelationshipKeys
{
NSMutableArray *keys;
NSArray *occurences;
NSCalendarDate *recID;
unsigned int count, max, seconds;
keys = [NSMutableArray array];
[keys addObject: @"master"];
occurences = [[self calendar: NO secure: NO] allObjects];
max = [occurences count];
for (count = 1; count < max; count++)
{
recID = [[occurences objectAtIndex: count] recurrenceId];
if (recID)
{
seconds = [recID timeIntervalSince1970];
[keys addObject: [NSString stringWithFormat: @"occurence%d",
seconds]];
}
}
return keys;
}
- (id) lookupName: (NSString *) lookupName
inContext: (id) localContext
acquire: (BOOL) acquire
{
id obj;
iCalRepeatableEntityObject *occurence;
NSString *recID;
BOOL isNewOccurence;
obj = [super lookupName: lookupName
inContext: localContext
acquire: acquire];
if (!obj)
{
if ([lookupName isEqualToString: @"master"])
obj = [self occurence: [self component: NO secure: NO]];
else if ([lookupName hasPrefix: @"occurence"])
{
recID = [lookupName substringFromIndex: 9];
occurence = [self lookupOccurence: recID];
if (!occurence)
{
occurence = [self newOccurenceWithID: recID];
isNewOccurence = YES;
}
if (occurence)
{
obj = [self occurence: occurence];
if (isNewOccurence)
[obj setIsNew: isNewOccurence];
}
}
}
return obj;
}
- (NSString *) contentAsString
{
NSString *secureContent;
@ -166,34 +332,45 @@ static BOOL sendEMailNotifications = NO;
- (iCalCalendar *) calendar: (BOOL) create secure: (BOOL) secure
{
NSString *componentTag;
CardGroup *newComponent;
iCalCalendar *calendar;
iCalRepeatableEntityObject *newComponent;
iCalCalendar **calendar;
NSString *iCalString;
if (secure)
iCalString = [self secureContentAsString];
calendar = &safeCalendar;
else
iCalString = content;
calendar = &fullCalendar;
if ([iCalString length] > 0)
calendar = [iCalCalendar parseSingleFromSource: iCalString];
else
if (!*calendar)
{
if (create)
if (secure)
iCalString = [self secureContentAsString];
else
iCalString = content;
if ([iCalString length] > 0)
{
calendar = [iCalCalendar groupWithTag: @"vcalendar"];
[calendar setVersion: @"2.0"];
[calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"];
componentTag = [[self componentTag] uppercaseString];
newComponent = [[calendar classForTag: componentTag]
groupWithTag: componentTag];
[calendar addChild: newComponent];
ASSIGN (*calendar, [iCalCalendar parseSingleFromSource: iCalString]);
if (!secure)
originalCalendar = [*calendar copy];
}
else
calendar = nil;
{
if (create)
{
ASSIGN (*calendar, [iCalCalendar groupWithTag: @"vcalendar"]);
[*calendar setVersion: @"2.0"];
[*calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"];
componentTag = [[self componentTag] uppercaseString];
newComponent = [[*calendar classForTag: componentTag]
groupWithTag: componentTag];
[newComponent setUid: [self globallyUniqueObjectId]];
[*calendar addChild: newComponent];
}
}
}
return calendar;
return *calendar;
}
- (id) component: (BOOL) create secure: (BOOL) secure
@ -202,10 +379,36 @@ static BOOL sendEMailNotifications = NO;
firstChildWithTag: [self componentTag]];
}
- (void) _updateRecurrenceIDs
{
iCalRepeatableEntityObject *master, *oldMaster, *currentComponent;
int deltaSecs;
NSArray *components;
unsigned int count, max;
NSCalendarDate *recID;
master = [self component: NO secure: NO];
oldMaster = (iCalRepeatableEntityObject *)
[originalCalendar firstChildWithTag: [self componentTag]];
deltaSecs = [[master startDate]
timeIntervalSinceDate: [oldMaster startDate]];
components = [fullCalendar allObjects];
max = [components count];
for (count = 1; count < max; count++)
{
currentComponent = [components objectAtIndex: count];
recID = [[currentComponent recurrenceId] addTimeInterval: deltaSecs];
[currentComponent setRecurrenceId: recID];
}
}
- (void) saveComponent: (iCalRepeatableEntityObject *) newObject
{
NSString *newiCalString;
if (!isNew
&& [newObject isRecurrent])
[self _updateRecurrenceIDs];
newiCalString = [[newObject parent] versitString];
[self saveContentString: newiCalString];
@ -633,4 +836,11 @@ static BOOL sendEMailNotifications = NO;
return roles;
}
/* SOGoComponentOccurence protocol */
- (iCalRepeatableEntityObject *) occurence
{
return [self component: YES secure: NO];
}
@end

View File

@ -0,0 +1,56 @@
/* SOGoComponentOccurence.h - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOCOMPONENTOCCURENCE_H
#define SOGOCOMPONENTOCCURENCE_H
#import <SOGo/SOGoObject.h>
@class iCalRepeatableEntityObject;
@class SOGoCalendarComponent;
@protocol SOGoComponentOccurence
- (iCalRepeatableEntityObject *) occurence;
- (BOOL) isNew;
- (id) delete;
@end
@interface SOGoComponentOccurence : SOGoObject <SOGoComponentOccurence>
{
iCalRepeatableEntityObject *component;
iCalRepeatableEntityObject *master;
BOOL isNew;
}
+ (id) occurenceWithComponent: (iCalRepeatableEntityObject *) newComponent
withMasterComponent: (iCalRepeatableEntityObject *) newMaster
inContainer: (SOGoCalendarComponent *) newContainer;
- (void) setComponent: (iCalRepeatableEntityObject *) newComponent;
- (void) setMasterComponent: (iCalRepeatableEntityObject *) newMaster;
- (void) setIsNew: (BOOL) newIsNew;
@end
#endif /* SOGOCOMPONENTOCCURENCE_H */

View File

@ -0,0 +1,163 @@
/* SOGoComponentOccurence.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalRepeatableEntityObject.h>
#import "SOGoComponentOccurence.h"
#import "SOGoCalendarComponent.h"
@interface SOGoCalendarComponent (OccurenceExtensions)
- (void) prepareDeleteOccurence: (iCalRepeatableEntityObject *) component;
@end
@implementation SOGoComponentOccurence
+ (id) occurenceWithComponent: (iCalRepeatableEntityObject *) newComponent
withMasterComponent: (iCalRepeatableEntityObject *) newMaster
inContainer: (SOGoCalendarComponent *) newContainer
{
SOGoComponentOccurence *occurence;
unsigned int seconds;
NSString *newName;
if (newComponent == newMaster)
newName = @"master";
else
{
seconds = [[newComponent recurrenceId] timeIntervalSince1970];
newName = [NSString stringWithFormat: @"occurence%d", seconds];
};
occurence = [self objectWithName: newName inContainer: newContainer];
[occurence setComponent: newComponent];
[occurence setMasterComponent: newMaster];
return occurence;
}
- (id) init
{
if ((self = [super init]))
{
component = nil;
master = nil;
isNew = NO;
}
return self;
}
- (void) setIsNew: (BOOL) newIsNew
{
isNew = newIsNew;
}
- (BOOL) isNew
{
return isNew;
}
- (void) dealloc
{
[component release];
[master release];
[super dealloc];
}
/* SOGoObject overrides */
- (BOOL) isFolderish
{
return NO;
}
- (NSString *) contentAsString
{
return [component versitString];
}
- (NSString *) davContentLength
{
unsigned int length;
length = [[self contentAsString]
lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
return [NSString stringWithFormat: @"%u", length];
}
/* /SOGoObject overrides */
- (void) setComponent: (iCalRepeatableEntityObject *) newComponent
{
ASSIGN (component, newComponent);
}
- (void) setMasterComponent: (iCalRepeatableEntityObject *) newMaster
{
ASSIGN (master, newMaster);
}
- (NSArray *) aclsForUser: (NSString *) uid
{
return [container aclsForUser: uid];
}
/* SOGoComponentOccurence protocol */
- (iCalRepeatableEntityObject *) occurence
{
return component;
}
- (id) delete
{
NSException *error;
iCalCalendar *parent;
if (component == master)
error = [container delete];
else
{
if ([container respondsToSelector: @selector (prepareDeleteOccurence:)])
[container prepareDeleteOccurence: component];
[master addToExceptionDates: [component startDate]];
parent = [component parent];
[[parent children] removeObject: component];
[container saveComponent: master];
error = nil;
}
return error;
}
- (void) saveComponent: (id) newEvent
{
[container saveComponent: newEvent];
}
@end

View File

@ -35,6 +35,7 @@
#import "NSArray+Appointments.h"
#import "SOGoAptMailNotification.h"
#import "SOGoAppointmentFolder.h"
#import "SOGoTaskOccurence.h"
#import "SOGoTaskObject.h"
@ -45,6 +46,14 @@
return @"vtodo";
}
- (SOGoComponentOccurence *) occurence: (iCalRepeatableEntityObject *) occ
{
return [SOGoTaskOccurence occurenceWithComponent: occ
withMasterComponent: [self component: NO
secure: NO]
inContainer: self];
}
/* message type */
- (NSString *) outlookMessageClass

View File

@ -0,0 +1,32 @@
/* SOGoTaskOccurence.h - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef SOGOTASKOCCURENCE_H
#define SOGOTASKOCCURENCE_H
#import "SOGoComponentOccurence.h"
@interface SOGoTaskOccurence : SOGoComponentOccurence
@end
#endif /* SOGOTASKOCCURENCE_H */

View File

@ -0,0 +1,27 @@
/* SOGoTaskOccurence.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse groupe conseil
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "SOGoTaskOccurence.h"
@implementation SOGoTaskOccurence
@end

View File

@ -25,6 +25,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
@ -192,4 +193,15 @@
return row;
}
- (NGCalendarDateRange *) firstOccurenceRange
{
return [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate]
endDate: [self endDate]];
}
- (unsigned int) occurenceInterval
{
return [[self endDate] timeIntervalSinceDate: [self startDate]];
}
@end

View File

@ -29,5 +29,6 @@
@interface iCalRepeatableEntityObject (SOGoExtensions)
- (NSString *) cycleInfo;
- (BOOL) doesOccurOnDate: (NSCalendarDate *) occurenceDate;
@end

View File

@ -25,6 +25,8 @@
#import <Foundation/NSString.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalRecurrenceCalculator.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import "iCalRepeatableEntityObject+SOGo.h"
@ -86,4 +88,42 @@
return value;
}
- (NGCalendarDateRange *) firstOccurenceRange
{
[self subclassResponsibility: _cmd];
return nil;
}
- (unsigned int) occurenceInterval
{
[self subclassResponsibility: _cmd];
return 0;
}
- (BOOL) doesOccurOnDate: (NSCalendarDate *) occurenceDate
{
NSArray *ranges;
NGCalendarDateRange *checkRange;
NSCalendarDate *endDate;
BOOL doesOccur;
doesOccur = [self isRecurrent];
if (doesOccur)
{
endDate = [occurenceDate addTimeInterval: [self occurenceInterval]];
checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: occurenceDate
endDate: endDate];
ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange
firstInstanceCalendarDateRange: [self firstOccurenceRange]
recurrenceRules: [self recurrenceRules]
exceptionRules: [self exceptionRules]
exceptionDates: [self exceptionDates]];
doesOccur = [ranges dateRangeArrayContainsDate: occurenceDate];
}
return doesOccur;
}
@end

View File

@ -26,6 +26,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
@ -169,4 +170,15 @@
return row;
}
- (NGCalendarDateRange *) firstOccurenceRange
{
return [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate]
endDate: [self due]];
}
- (unsigned int) occurenceInterval
{
return [[self due] timeIntervalSinceDate: [self startDate]];
}
@end

View File

@ -52,6 +52,26 @@
SOGoTaskObject = {
superclass = "SOGoCalendarComponent";
};
SOGoComponentOccurence = {
superclass = "SOGoObject";
protectedBy = "Access Object";
defaultRoles = {
"ViewAllComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer" );
"ViewDAndT" = ( "Organizer", "Participant", "ComponentDAndTViewer" );
"ModifyComponent" = ( "Owner", "Organizer", "ComponentModifier" );
"RespondToComponent" = ( "Participant", "ComponentModifier", "ComponentResponder" );
"Access Object" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
"Change Images and Files" = ( "Owner", "Organizer", "ComponentModifier" );
"Access Contents Information" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
"WebDAV Access" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
};
};
SOGoAppointmentOccurence = {
superclass = "SOGoComponentOccurence";
};
SOGoTaskOccurence = {
superclass = "SOGoComponentOccurence";
};
SOGoFreeBusyObject = {
superclass = "SOGoContentObject";
protectedBy = "Access Contents Information";

View File

@ -692,8 +692,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
NSArray *sogoRoles;
NSString *rqMethod;
rolesForObject = [NSMutableArray new];
[rolesForObject autorelease];
rolesForObject = [NSMutableArray array];
sogoRoles = [super rolesForObject: object inContext: context];
if (sogoRoles)

View File

@ -31,6 +31,8 @@
#import <SoObjects/SOGo/SOGoObject.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import "WODirectAction+SOGo.h"
#import "UIxObjectActions.h"
@implementation UIxObjectActions
@ -71,4 +73,15 @@
return response;
}
- (WOResponse *) deleteAction
{
WOResponse *response;
response = (WOResponse *) [[self clientObject] delete];
if (!response)
response = [self responseWithStatus: 204];
return response;
}
@end

View File

@ -40,6 +40,11 @@
actionClass = "UIxObjectActions";
actionName = "removeUserFromAcls";
};
delete = {
protectedBy = "Delete Objects";
actionClass = "UIxObjectActions";
actionName = "delete";
};
acls = {
protectedBy = "ReadAcls";
pageName = "UIxAclEditor";

View File

@ -34,8 +34,6 @@
CardElement *workAdr;
}
- (BOOL)isDeletableClientObject;
@end
#endif /* UIXCONTACTVIEW_H */

View File

@ -516,32 +516,4 @@
return self;
}
- (BOOL) isDeletableClientObject
{
return [[self clientObject] respondsToSelector: @selector(delete)];
}
- (id) deleteAction
{
NSException *ex;
if (![self isDeletableClientObject])
/* return 400 == Bad Request */
return [NSException exceptionWithHTTPStatus: 400
reason:@"method cannot be invoked on "
@"the specified object"];
ex = [[self clientObject] delete];
if (ex)
{
// TODO: improve error handling
[self debugWithFormat: @"failed to delete: %@", ex];
return ex;
}
return [self responseWithStatus: 200
andString: [[self clientObject] nameInContainer]];
}
@end /* UIxContactView */

View File

@ -41,35 +41,26 @@
id currentAddress;
}
- (BOOL)isDeletableClientObject;
@end
@implementation UIxMailView
static NSString *mailETag = nil;
+ (int)version {
return [super version] + 0 /* v2 */;
}
+ (void)initialize {
+ (void) initialize
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSAssert2([super version] == 2,
@"invalid superclass (%@) version %i !",
NSStringFromClass([self superclass]), [super version]);
if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"]) {
if ([ud boolForKey:@"SOGoDontUseETagsForMailViewer"])
NSLog(@"Note: usage of constant etag for mailer viewer is disabled.");
}
else {
mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%d_%d_%03d\"",
UIX_MAILER_MAJOR_VERSION,
UIX_MAILER_MINOR_VERSION,
UIX_MAILER_SUBMINOR_VERSION];
NSLog(@"Note: using constant etag for mail viewer: '%@'", mailETag);
}
else
{
mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%d_%d_%03d\"",
UIX_MAILER_MAJOR_VERSION,
UIX_MAILER_MINOR_VERSION,
UIX_MAILER_SUBMINOR_VERSION];
NSLog(@"Note: using constant etag for mail viewer: '%@'", mailETag);
}
}
- (void)dealloc {
@ -193,11 +184,6 @@ static NSString *mailETag = nil;
return self;
}
- (BOOL) isDeletableClientObject
{
return [[self clientObject] respondsToSelector: @selector (delete)];
}
- (BOOL) isInlineViewer
{
return NO;

View File

@ -33,7 +33,8 @@
};
SOGoContentObject = {
superclass = "SOGoObject";
defaultAccess = "Access Contents Information";
defaultAccess = "allow";
/* defaultAccess = "Access Contents Information"; */
protectedBy = "Access Object";
defaultRoles = {
"Access Contents Information" = ( "Owner", "ObjectViewer", "ObjectEditor" );

View File

@ -107,7 +107,7 @@
- (WOResponse *) redirectToLocation: (NSString *) newLocation;
/* Debugging */
- (BOOL)isUIxDebugEnabled;
- (BOOL) isUIxDebugEnabled;
@end

View File

@ -456,6 +456,10 @@ vtodo_class2 = "(Vertrouwelijke taak)";
"Please select an event or a task." = "Selecteer een afspraak of een taak.";
"editRepeatingItem" = "Het item dat u bewerkt is een herhalend item. Wilt u alle items of enkel dit item bewerken?";
"button_thisOccurrenceOnly" = "Enkel dit item";
"button_allOccurrences" = "Alle herhalingen";
/* Properties dialog */
"Name:" = "Naam:";
"Color:" = "Kleur:";

View File

@ -473,6 +473,10 @@ vtodo_class2 = "(Confidential task)";
"Please select an event or a task." = "Please select an event or a task.";
"editRepeatingItem" = "The item you are editing is a repeating item. Do you want to edit all occurences of it or only this single instance?";
"button_thisOccurrenceOnly" = "This occurence only";
"button_allOccurrences" = "All occurences";
/* Properties dialog */
"Name:" = "Name:";
"Color:" = "Color:";

View File

@ -470,6 +470,10 @@ vtodo_class2 = "(Tâche confidentielle)";
"Please select an event or a task." = "Veuillez sélectionner un événement ou une tâche.";
"editRepeatingItem" = "L'élément que vous éditez est récurrent. Voulez-vous modifier toutes ses occurrences ou seulement celle-ci ?";
"button_thisOccurrenceOnly" = "Cette occurence seulement";
"button_allOccurrences" = "Toutes les occurences";
/* Properties dialog */
"Name:" = "Nom :";
"Color:" = "Couleur :";

View File

@ -43,7 +43,8 @@ SchedulerUI_OBJC_FILES = \
UIxCalParticipationStatusView.m \
UIxCalMonthOverview.m \
UIxCalMonthViewOld.m \
UIxRecurrenceEditor.m
UIxRecurrenceEditor.m \
UIxOccurenceDialog.m
# UIxAppointmentProposal.m
# UIxTaskProposal.m

View File

@ -457,6 +457,10 @@ vtodo_class2 = "(Confidential task)";
"Please select an event or a task." = "Bitte wählen Sie einen Termin oder eine Aufgabe aus!";
"editRepeatingItem" = "Der Eintrag, den Sie bearbeiten, ist ein wiederkehrender Eintrag. Wollen Sie alle seine Ereignisse bearbeiten oder nur diese einzelne Instanz?";
"button_thisOccurrenceOnly" = "Nur dieses Ereignis";
"button_allOccurrences" = "Alle Ereignisse";
/* Properties dialog */
"Name:" = "Name:";
"Color:" = "Farbe:";

View File

@ -471,6 +471,10 @@ vtodo_class2 = "(Attività confidenziale)";
"From" = "Da";
"To" = "A";
"editRepeatingItem" = "L'elemento che si sta modificando è un elemento ripetuto. Si vogliono modificare tutte le sue occorrenze o solo questa?";
"button_thisOccurrenceOnly" = "Solamente questa occorrenza";
"button_allOccurrences" = "Tutte le occorrenze";
/* Properties dialog */
"Name:" = "Nome:";
"Color:" = "Colore:";

View File

@ -477,6 +477,10 @@ vtodo_class2 = "(Tarea confidencial)";
"Please select an event or a task."
= "Seleccione un evento o tarea, por favor";
"editRepeatingItem" = "El elemento que está editando es un elemento repetitivo. ¿Quiere editar todas sus ocurrencias o sólo la que ha señalado?";
"button_thisOccurrenceOnly" = "Sólo esta ocurrencia";
"button_allOccurrences" = "Todas las ocurrencias";
/* Properties dialog */
"Name:" = "Nombre:";
"Color:" = "Color:";

View File

@ -38,6 +38,8 @@
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
#import <SoObjects/Appointments/SOGoAppointmentObject.h>
#import <SoObjects/Appointments/SOGoComponentOccurence.h>
#import "UIxComponentEditor.h"
#import "UIxAppointmentEditor.h"
@ -71,7 +73,7 @@
{
if (!event)
{
event = (iCalEvent *) [[self clientObject] component: YES secure: YES];
event = (iCalEvent *) [[self clientObject] occurence];
[event retain];
}
@ -165,9 +167,12 @@
NSCalendarDate *startDate, *endDate;
NSString *duration;
unsigned int minutes;
SOGoObject <SOGoComponentOccurence> *co;
[self event];
if ([[self clientObject] isNew])
co = [self clientObject];
if ([co isNew]
&& [co isKindOfClass: [SOGoCalendarComponent class]])
{
startDate = [self newStartDate];
duration = [self queryParameterForKey:@"dur"];
@ -231,7 +236,7 @@
actionName = [[request requestHandlerPath] lastPathComponent];
return ([[self clientObject] isKindOfClass: [SOGoAppointmentObject class]]
return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)]
&& [actionName hasPrefix: @"save"]);
}

View File

@ -65,7 +65,8 @@ static NSArray *tasksFields = nil;
@"c_status", @"c_title", @"c_startdate",
@"c_enddate", @"c_location", @"c_isallday",
@"c_classification", @"c_partmails",
@"c_partstates", @"c_owner", @"c_iscycle", nil];
@"c_partstates", @"c_owner", @"c_iscycle",
@"c_recurrence_id", nil];
[eventsFields retain];
}
if (!tasksFields)
@ -404,7 +405,7 @@ _feedBlockWithMonthBasedData(NSMutableDictionary *block, unsigned int start,
end: (unsigned int) end
cname: (NSString *) cName
onDay: (unsigned int) dayStart
recurrence: (BOOL) recurrence
recurrenceTime: (unsigned int) recurrenceTime
userState: (iCalPersonPartStat) userState
{
NSMutableDictionary *block;
@ -416,8 +417,9 @@ _feedBlockWithMonthBasedData(NSMutableDictionary *block, unsigned int start,
else
_feedBlockWithMonthBasedData (block, start, userTimeZone, dateFormatter);
[block setObject: cName forKey: @"cname"];
if (recurrence)
[block setObject: @"1" forKey: @"recurrence"];
if (recurrenceTime)
[block setObject: [NSNumber numberWithInt: recurrenceTime]
forKey: @"recurrenceTime"];
if (userState != iCalPersonPartStatOther)
[block setObject: [NSNumber numberWithInt: userState]
forKey: @"userState"];
@ -463,11 +465,10 @@ _userStateInEvent (NSArray *event)
withEvent: (NSArray *) event
{
unsigned int currentDayStart, startSecs, endsSecs, currentStart, eventStart,
eventEnd, offset;
eventEnd, offset, recurrenceTime;
NSMutableArray *currentDay;
NSMutableDictionary *eventBlock;
NSString *eventCName;
BOOL recurrence;
iCalPersonPartStat userState;
startSecs = (unsigned int) [startDate timeIntervalSince1970];
@ -475,7 +476,10 @@ _userStateInEvent (NSArray *event)
eventStart = [[event objectAtIndex: 4] unsignedIntValue];
eventEnd = [[event objectAtIndex: 5] unsignedIntValue];
recurrence = [[event objectAtIndex: 12] boolValue];
if ([[event objectAtIndex: 12] boolValue])
recurrenceTime = [[event objectAtIndex: 13] unsignedIntValue];
else
recurrenceTime = 0;
currentStart = eventStart;
if (currentStart < startSecs)
@ -500,7 +504,7 @@ _userStateInEvent (NSArray *event)
end: currentDayStart + dayLength - 1
cname: eventCName
onDay: currentDayStart
recurrence: recurrence
recurrenceTime: recurrenceTime
userState: userState];
[currentDay addObject: eventBlock];
currentDayStart += dayLength;
@ -512,7 +516,7 @@ _userStateInEvent (NSArray *event)
end: eventEnd
cname: eventCName
onDay: currentDayStart
recurrence: recurrence
recurrenceTime: recurrenceTime
userState: userState];
[currentDay addObject: eventBlock];
}

View File

@ -399,6 +399,11 @@ iRANGE(2);
/* accessors */
- (BOOL) isChildOccurence
{
return [[self clientObject] isKindOfClass: [SOGoComponentOccurence class]];
}
- (void) setItem: (id) _item
{
ASSIGN (item, _item);
@ -418,7 +423,7 @@ iRANGE(2);
{
NSString *tag;
tag = [[self clientObject] componentTag];
tag = [[component tag] lowercaseString];
return [self labelForKey: [NSString stringWithFormat: @"%@_%@", item, tag]];
}
@ -456,7 +461,7 @@ iRANGE(2);
- (BOOL) canBeOrganizer
{
NSString *owner;
SOGoCalendarComponent *co;
SOGoObject <SOGoComponentOccurence> *co;
SOGoUser *currentUser;
BOOL hasOrganizer;
SoSecurityManager *sm;
@ -801,7 +806,7 @@ iRANGE(2);
SOGoAppointmentFolders *parentFolder;
fDisplayName = [item displayName];
folder = [[self clientObject] container];
folder = [self componentCalendar];
parentFolder = [folder container];
if ([fDisplayName isEqualToString: [parentFolder defaultFolderName]])
fDisplayName = [self labelForKey: fDisplayName];
@ -823,7 +828,9 @@ iRANGE(2);
SOGoAppointmentFolder *calendar;
calendar = [[self clientObject] container];
if ([calendar isKindOfClass: [SOGoCalendarComponent class]])
calendar = [calendar container];
return calendar;
}
@ -1125,8 +1132,8 @@ RANGE(2);
- (BOOL) isWriteableClientObject
{
return [[self clientObject]
respondsToSelector: @selector(saveContentString:)];
return [[self clientObject]
respondsToSelector: @selector(saveContentString:)];
}
/* access */
@ -1485,54 +1492,56 @@ RANGE(2);
clientObject = [self clientObject];
if ([clientObject isNew])
{
[component setUid: [clientObject nameInContainer]];
[component setCreated: now];
[component setTimeStampAsDate: now];
}
[component setPriority: priority];
[component setLastModified: now];
// We remove any repeat rules
if (!repeat && [component hasRecurrenceRules])
[component removeAllRecurrenceRules];
else if ([repeat caseInsensitiveCompare: @"-"] != NSOrderedSame)
if (![self isChildOccurence])
{
rule = [iCalRecurrenceRule new];
// 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
|| [repeat caseInsensitiveCompare: @"DAILY"] == NSOrderedSame
|| [repeat caseInsensitiveCompare: @"WEEKLY"] == NSOrderedSame
|| [repeat caseInsensitiveCompare: @"YEARLY"] == NSOrderedSame)
{
[rule setInterval: @"1"];
[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];
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
|| [repeat caseInsensitiveCompare: @"DAILY"] == NSOrderedSame
|| [repeat caseInsensitiveCompare: @"WEEKLY"] == NSOrderedSame
|| [repeat caseInsensitiveCompare: @"YEARLY"] == NSOrderedSame)
{
[rule setInterval: @"1"];
[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];
}
}
}

View File

@ -0,0 +1,46 @@
/* UIxOccurenceDialog.h - this file is part of SOGo
*
* Copyright (C) 2008 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef UIXRECURRENCEEDITOR_H
#define UIXRECURRENCEEDITOR_H
#import <SOGoUI/UIxComponent.h>
@class NSString;
@interface UIxOccurenceDialog : UIxComponent
{
NSString *action;
}
- (NSString *) action;
- (NSString *) calendarFolder;
- (NSString *) componentName;
- (NSString *) recurrenceName;
- (id <WOActionResults>) defaultAction;
- (id <WOActionResults>) confirmDeletionAction;
@end
#endif /* UIXRECURRENCEEDITOR_H */

View File

@ -0,0 +1,90 @@
/* UIxOccurenceDialog.m - this file is part of SOGo
*
* Copyright (C) 2008 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <Common/UIxPageFrame.h>
#import <SoObjects/Appointments/SOGoCalendarComponent.h>
#import "UIxOccurenceDialog.h"
@implementation UIxOccurenceDialog
- (id) init
{
if ((self = [super init]))
action = nil;
return self;
}
- (void) dealloc
{
[action release];
[super dealloc];
}
- (NSString *) action
{
return action;
}
- (NSString *) calendarFolder
{
SOGoCalendarComponent *component;
component = [[self clientObject] container];
return [[component container] nameInContainer];
}
- (NSString *) componentName
{
SOGoCalendarComponent *component;
component = [[self clientObject] container];
return [component nameInContainer];
}
- (NSString *) recurrenceName
{
return [[self clientObject] nameInContainer];
}
- (id <WOActionResults>) defaultAction
{
ASSIGN (action, @"edit");
return self;
}
- (id <WOActionResults>) confirmDeletionAction
{
ASSIGN (action, @"delete");
return self;
}
@end

View File

@ -335,7 +335,7 @@
actionName = [[request requestHandlerPath] lastPathComponent];
return ([[self clientObject] isKindOfClass: [SOGoTaskObject class]]
return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)]
&& [actionName hasPrefix: @"save"]);
}

View File

@ -2,239 +2,293 @@
requires = ( MAIN, MainUI, CommonUI, Appointments, Contacts, ContactsUI );
publicResources = (
previous_week.gif,
next_week.gif,
icon_view_overview.gif,
icon_view_overview_inactive.gif,
icon_view_chart.gif,
icon_view_chart_inactive.gif,
icon_view_list.gif,
icon_view_list_inactive.gif,
icon_view_columns.gif,
icon_view_columns_inactive.gif,
icon_popupcalendar.gif,
first.gif,
previous.gif,
next.gif,
last.gif,
skycalendar.html,
skycalendar.js,
green_corner.gif,
invisible_space_2.gif,
cycles.plist,
);
previous_week.gif,
next_week.gif,
icon_view_overview.gif,
icon_view_overview_inactive.gif,
icon_view_chart.gif,
icon_view_chart_inactive.gif,
icon_view_list.gif,
icon_view_list_inactive.gif,
icon_view_columns.gif,
icon_view_columns_inactive.gif,
icon_popupcalendar.gif,
first.gif,
previous.gif,
next.gif,
last.gif,
skycalendar.html,
skycalendar.js,
green_corner.gif,
invisible_space_2.gif,
cycles.plist,
);
factories = {
};
categories = {
SOGoAppointmentFolders = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentFolders.toolbar";
};
SOGoAppointmentFolders = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentFolders.toolbar";
};
methods = {
view = {
protectedBy = "View";
pageName = "UIxCalMainView";
};
saveDragHandleState = {
protectedBy = "View";
pageName = "UIxCalMainView";
actionName = "saveDragHandleState";
};
dateselector = {
protectedBy = "View";
pageName = "UIxCalDateSelector";
};
calendarslist = {
protectedBy = "View";
pageName = "UIxCalendarSelector";
actionName = "calendarsList";
};
eventslist = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "eventsList";
};
eventsblocks = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "eventsBlocks";
};
taskslist = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "tasksList";
};
dayview = {
protectedBy = "View";
pageName = "UIxCalDayView";
};
multicolumndayview = {
protectedBy = "View";
pageName = "UIxCalMulticolumnDayView";
};
weekview = {
protectedBy = "View";
pageName = "UIxCalWeekView";
};
monthview = {
protectedBy = "View";
pageName = "UIxCalMonthView";
};
show = {
protectedBy = "View";
pageName = "UIxCalView";
actionName = "redirectForUIDs";
};
// proposal = {
// protectedBy = "View";
// pageName = "UIxAppointmentProposal";
// };
// proposalSearch = {
// protectedBy = "View";
// pageName = "UIxAppointmentProposal";
// actionName = "proposalSearch";
// };
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxCalUserRightsEditor";
};
saveUserRights = {
protectedBy = "Change Permissions";
pageName = "UIxCalUserRightsEditor";
actionName = "saveUserRights";
};
editAttendees = {
protectedBy = "View";
pageName = "UIxAttendeesEditor";
};
editRecurrence = {
protectedBy = "View";
pageName = "UIxRecurrenceEditor";
};
colorPicker = {
protectedBy = "View";
pageName = "UIxColorPicker";
};
};
methods = {
view = {
protectedBy = "View";
pageName = "UIxCalMainView";
};
};
saveDragHandleState = {
protectedBy = "View";
pageName = "UIxCalMainView";
actionName = "saveDragHandleState";
};
dateselector = {
protectedBy = "View";
pageName = "UIxCalDateSelector";
};
calendarslist = {
protectedBy = "View";
pageName = "UIxCalendarSelector";
actionName = "calendarsList";
};
eventslist = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "eventsList";
};
eventsblocks = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "eventsBlocks";
};
taskslist = {
protectedBy = "View";
actionClass = "UIxCalListingActions";
actionName = "tasksList";
};
dayview = {
protectedBy = "View";
pageName = "UIxCalDayView";
};
multicolumndayview = {
protectedBy = "View";
pageName = "UIxCalMulticolumnDayView";
};
weekview = {
protectedBy = "View";
pageName = "UIxCalWeekView";
};
monthview = {
protectedBy = "View";
pageName = "UIxCalMonthView";
};
show = {
protectedBy = "View";
pageName = "UIxCalView";
actionName = "redirectForUIDs";
};
// proposal = {
// protectedBy = "View";
// pageName = "UIxAppointmentProposal";
// };
// proposalSearch = {
// protectedBy = "View";
// pageName = "UIxAppointmentProposal";
// actionName = "proposalSearch";
// };
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxCalUserRightsEditor";
};
saveUserRights = {
protectedBy = "Change Permissions";
pageName = "UIxCalUserRightsEditor";
actionName = "saveUserRights";
};
editAttendees = {
protectedBy = "View";
pageName = "UIxAttendeesEditor";
};
editRecurrence = {
protectedBy = "View";
pageName = "UIxRecurrenceEditor";
};
colorPicker = {
protectedBy = "View";
pageName = "UIxColorPicker";
};
};
};
SOGoAppointmentFolder = {
methods = {
properties = {
protectedBy = "Access Contents Information";
pageName = "UIxCalendarProperties";
};
saveProperties = {
protectedBy = "Access Contents Information";
pageName = "UIxCalendarProperties";
actionName = "saveProperties";
};
show = {
protectedBy = "View";
pageName = "UIxCalView";
actionName = "redirectForUIDs";
};
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxCalUserRightsEditor";
};
saveUserRights = {
protectedBy = "Change Permissions";
pageName = "UIxCalUserRightsEditor";
actionName = "saveUserRights";
};
newevent = {
protectedBy = "Add Documents, Images, and Files";
pageName = "UIxAppointmentEditor";
actionName = "new";
};
newtask = {
protectedBy = "Add Documents, Images, and Files";
pageName = "UIxTaskEditor";
actionName = "new";
};
SOGoAppointmentFolder = {
methods = {
properties = {
protectedBy = "Access Contents Information";
pageName = "UIxCalendarProperties";
};
};
saveProperties = {
protectedBy = "Access Contents Information";
pageName = "UIxCalendarProperties";
actionName = "saveProperties";
};
show = {
protectedBy = "View";
pageName = "UIxCalView";
actionName = "redirectForUIDs";
};
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxCalUserRightsEditor";
};
saveUserRights = {
protectedBy = "Change Permissions";
pageName = "UIxCalUserRightsEditor";
actionName = "saveUserRights";
};
newevent = {
protectedBy = "Add Documents, Images, and Files";
pageName = "UIxAppointmentEditor";
actionName = "new";
};
newtask = {
protectedBy = "Add Documents, Images, and Files";
pageName = "UIxTaskEditor";
actionName = "new";
};
};
};
SOGoCalendarComponent = {
};
SOGoCalendarComponent = {
};
SOGoAppointmentObject = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentObject.toolbar";
};
SOGoAppointmentObject = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentObject.toolbar";
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxAppointmentEditor";
};
editAsAppointment = {
protectedBy = "ViewAllComponent";
pageName = "UIxAppointmentEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
actionName = "save";
};
saveAsAppointment = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
actionName = "save";
};
accept = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "accept";
};
decline = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "decline";
};
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxAppointmentEditor";
};
};
editAsAppointment = {
protectedBy = "ViewAllComponent";
pageName = "UIxAppointmentEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
actionName = "save";
};
saveAsAppointment = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
actionName = "save";
};
accept = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "accept";
};
decline = {
protectedBy = "RespondToComponent";
pageName = "UIxAppointmentEditor";
actionName = "decline";
};
};
};
SOGoTaskObject = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentObject.toolbar";
};
SOGoTaskObject = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentObject.toolbar";
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxTaskEditor";
};
editAsTask = {
protectedBy = "ViewAllComponent";
pageName = "UIxTaskEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "save";
};
saveAsTask = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "save";
};
changeStatus = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "changeStatus";
};
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxTaskEditor";
};
};
editAsTask = {
protectedBy = "ViewAllComponent";
pageName = "UIxTaskEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "save";
};
saveAsTask = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "save";
};
changeStatus = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "changeStatus";
};
};
};
SOGoComponentOccurence = {
methods = {
confirmEditing = {
protectedBy = "ModifyComponent";
pageName = "UIxOccurenceDialog";
};
confirmDeletion = {
protectedBy = "ModifyComponent";
pageName = "UIxOccurenceDialog";
actionName = "confirmDeletion";
};
};
};
SOGoAppointmentOccurence = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoAppointmentObject.toolbar";
};
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxAppointmentEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxAppointmentEditor";
actionName = "save";
};
};
};
SOGoTaskOccurence = {
slots = {
toolbar = {
protectedBy = "View";
value = "SOGoTaskObject.toolbar";
};
};
methods = {
edit = {
protectedBy = "ViewAllComponent";
pageName = "UIxTaskEditor";
};
save = {
protectedBy = "ModifyComponent";
pageName = "UIxTaskEditor";
actionName = "save";
};
};
};
};
}

View File

@ -46,6 +46,7 @@
string="itemCategoryText" selection="category"
/><var:string label:value="Calendar:" />
<var:popup const:id="calendarList"
var:disabled="isChildOccurence"
list="calendarList" item="item"
string="calendarDisplayName"
var:selection="componentCalendar"
@ -75,6 +76,7 @@
<label><var:string label:value="Repeat:" />
<span class="content"><var:popup list="repeatList" item="item"
label:noSelectionString="repeat_NEVER"
var:disabled="isChildOccurence"
const:disabledValue="-"
const:name="repeatList"
const:id="repeatList"

View File

@ -0,0 +1,32 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE var:component>
<var:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:uix="OGo:uix"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxPageFrame"
const:popup="YES"
title="name"
const:toolbar="none">
<script type="text/javascript">
var calendarFolder = '<var:string value="calendarFolder"/>';
var componentName = '<var:string value="componentName"/>';
var recurrenceName = '<var:string value="recurrenceName"/>';
var action = '<var:string value="action"/>';
</script>
<div id="message">
<var:string label:value="editRepeatingItem"/>
</div>
<div id="windowButtons">
<div id="leftButtons">
<input type="button" id="thisButton" const:class="button" label:value="button_thisOccurrenceOnly"/>
</div>
<div id="rightButtons">
<input type="submit" id="cancelButton" const:class="button" label:value="Cancel"/>
<input type="button" id="allButton" const:class="button" label:value="button_allOccurrences"/>
</div>
</div>
</var:component>

View File

@ -71,9 +71,14 @@ function onMenuNewTaskClick(event) {
newEvent(this, "task");
}
function _editEventId(id, calendar) {
var urlstr = ApplicationBaseURL + calendar + "/" + id + "/edit";
function _editEventId(id, calendar, recurrence) {
var targetname = "SOGo_edit_" + id;
var urlstr = ApplicationBaseURL + calendar + "/" + id;
if (recurrence) {
urlstr += "/" + recurrence;
targetname += recurrence;
}
urlstr += "/edit";
var win = window.open(urlstr, "_blank",
"width=490,height=470,resizable=0");
if (win)
@ -115,50 +120,60 @@ function _batchDeleteEvents() {
function deleteEvent() {
if (listOfSelection) {
var nodes = listOfSelection.getSelectedRows();
if (nodes.length > 0) {
var label = "";
if (listOfSelection == $("tasksList"))
label = labels["taskDeleteConfirmation"];
else
label = labels["eventDeleteConfirmation"];
if (confirm(label)) {
if (document.deleteEventAjaxRequest) {
if (nodes.length == 1
&& nodes[0].recurrenceTime) {
_editRecurrenceDialog(nodes[0], "confirmDeletion");
}
else {
if (confirm(label)) {
if (document.deleteEventAjaxRequest) {
document.deleteEventAjaxRequest.aborted = true;
document.deleteEventAjaxRequest.abort();
}
var sortedNodes = [];
var calendars = [];
for (var i = 0; i < nodes.length; i++) {
var calendar = nodes[i].calendar;
if (!sortedNodes[calendar]) {
sortedNodes[calendar] = [];
calendars.push(calendar);
}
sortedNodes[calendar].push(nodes[i].cname);
}
for (var i = 0; i < calendars.length; i++) {
calendarsOfEventsToDelete.push(calendars[i]);
eventsToDelete.push(sortedNodes[calendars[i]]);
}
_batchDeleteEvents();
}
var sortedNodes = [];
var calendars = [];
for (var i = 0; i < nodes.length; i++) {
var calendar = nodes[i].calendar;
if (!sortedNodes[calendar]) {
sortedNodes[calendar] = [];
calendars.push(calendar);
}
sortedNodes[calendar].push(nodes[i].cname);
}
for (var i = 0; i < calendars.length; i++) {
calendarsOfEventsToDelete.push(calendars[i]);
eventsToDelete.push(sortedNodes[calendars[i]]);
}
_batchDeleteEvents();
}
}
} else {
window.alert(labels["Please select an event or a task."]);
}
}
else if (selectedCalendarCell) {
var label = labels["eventDeleteConfirmation"];
if (confirm(label)) {
if (document.deleteEventAjaxRequest) {
document.deleteEventAjaxRequest.aborted = true;
document.deleteEventAjaxRequest.abort();
if (selectedCalendarCell[0].recurrenceTime) {
_editRecurrenceDialog(selectedCalendarCell[0], "confirmDeletion");
}
else {
var label = labels["eventDeleteConfirmation"];
if (confirm(label)) {
if (document.deleteEventAjaxRequest) {
document.deleteEventAjaxRequest.aborted = true;
document.deleteEventAjaxRequest.abort();
}
eventsToDelete.push([selectedCalendarCell[0].cname]);
calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar);
_batchDeleteEvents();
}
eventsToDelete.push([selectedCalendarCell[0].cname]);
calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar);
_batchDeleteEvents();
}
}
else
@ -268,13 +283,97 @@ function deleteEventsFromViews(events) {
}
}
function _editRecurrenceDialog(eventDiv, method) {
var targetname = "SOGo_edit_" + eventDiv.cname + eventDiv.recurrenceTime;
var urlstr = (ApplicationBaseURL + eventDiv.calendar + "/" + eventDiv.cname
+ "/occurence" + eventDiv.recurrenceTime + "/" + method);
var win = window.open(urlstr, "_blank",
"width=490,height=70,resizable=0");
if (win)
win.focus();
}
function editDoubleClickedEvent(event) {
_editEventId(this.cname, this.calendar);
if (this.recurrenceTime)
_editRecurrenceDialog(this, "confirmEditing");
else
_editEventId(this.cname, this.calendar);
preventDefault(event);
event.cancelBubble = true;
}
function performEventEdition(folder, event, recurrence) {
_editEventId(event, folder, recurrence);
}
function performEventDeletion(folder, event, recurrence) {
if (calendarEvents) {
var eventEntry = calendarEvents[event];
if (eventEntry) {
var urlstr = ApplicationBaseURL + folder + "/" + event;
var nodes;
if (recurrence) {
urlstr += "/" + recurrence;
var occurenceTime = recurrence.substring(9);
nodes = [];
for (var i = 0; i < eventEntry.siblings.length; i++) {
if (eventEntry.siblings[i].recurrenceTime
&& eventEntry.siblings[i].recurrenceTime == occurenceTime)
nodes.push(eventEntry.siblings[i]);
}
}
else
nodes = eventEntry.siblings;
urlstr += "/delete";
document.deleteEventAjaxRequest = triggerAjaxRequest(urlstr,
performDeleteEventCallback,
{ nodes: nodes,
recurrence: recurrence });
}
}
}
function performDeleteEventCallback(http) {
if (http.readyState == 4) {
if (isHttpStatus204(http.status)) {
var nodes = http.callbackData.nodes;
var recurrenceTime = 0;
if (http.callbackData.recurrence)
recurrenceTime = http.callbackData.recurrence.substring(9);
var cName = nodes[0].cname;
var eventEntry = calendarEvents[cName];
var node = nodes.pop();
while (node) {
node.parentNode.removeChild(node);
node = nodes.pop();
}
if (recurrenceTime) {
var row = $(cName + "-" + recurrenceTime);
if (row)
row.parentNode.removeChild(row);
}
else {
delete calendarEvents[cName];
var tables = [ "eventsList", "tasksList" ];
for (var i = 0; i < 2; i++) {
var table = $(tables[i]);
if (table.tBodies)
rows = table.tBodies[0].rows;
else
rows = $(table).childNodesWithTag("li");
for (var j = rows.length; j > 0; j--) {
var row = $(rows[j - 1]);
var id = row.getAttribute("id");
if (id.indexOf(cName) == 0)
row.parentNode.removeChild(row);
}
}
}
}
}
}
function onSelectAll() {
var list = $("eventsList");
list.selectRowsMatchingClass("eventRow");
@ -370,10 +469,15 @@ function eventsListCallback(http) {
var row = document.createElement("tr");
table.tBodies[0].appendChild(row);
$(row).addClassName("eventRow");
row.setAttribute("id", escape(data[i][0]));
var rTime = data[i][13];
var id = escape(data[i][0]);
if (rTime)
id += "-" + escape(rTime);
row.setAttribute("id", id);
row.cname = escape(data[i][0]);
row.calendar = data[i][1];
if (rTime)
row.recurrenceTime = escape(rTime);
var startDate = new Date();
startDate.setTime(data[i][4] * 1000);
row.day = startDate.getDayString();
@ -391,12 +495,12 @@ function eventsListCallback(http) {
td = $(document.createElement("td"));
row.appendChild(td);
td.observe("mousedown", listRowMouseDownHandler, true);
td.appendChild(document.createTextNode(data[i][13]));
td.appendChild(document.createTextNode(data[i][14]));
td = $(document.createElement("td"));
row.appendChild(td);
td.observe("mousedown", listRowMouseDownHandler, true);
td.appendChild(document.createTextNode(data[i][14]));
td.appendChild(document.createTextNode(data[i][15]));
td = $(document.createElement("td"));
row.appendChild(td);
@ -761,16 +865,17 @@ function _drawCalendarAllDaysEvents(events) {
}
}
function newAllDayEventDIV(eventRep) {
function newBaseEventDIV(eventRep, event, eventText) {
// cname, calendar, starts, lasts,
// startHour, endHour, title) {
var eventDiv = $(document.createElement("div"));
var event = calendarEvents[eventRep.cname];
if (!event.siblings)
event.siblings = [];
eventDiv.event = event;
eventDiv.cname = event[0];
eventDiv.calendar = event[1];
if (eventRep.recurrenceTime)
eventDiv.recurrenceTime = eventRep.recurrenceTime;
eventDiv.addClassName("event");
if (eventRep.userState && userStates[eventRep.userState])
@ -787,69 +892,6 @@ function newAllDayEventDIV(eventRep) {
innerDiv.addClassName("eventInside");
innerDiv.addClassName("calendarFolder" + event[1]);
var gradientDiv = $(document.createElement("div"));
innerDiv.appendChild(gradientDiv);
gradientDiv.addClassName("gradient");
var gradientImg = document.createElement("img");
gradientDiv.appendChild(gradientImg);
gradientImg.src = ResourcesURL + "/event-gradient.png";
var textDiv = $(document.createElement("div"));
innerDiv.appendChild(textDiv);
textDiv.addClassName("text");
textDiv.appendChild(document.createTextNode(event[3]));
eventDiv.observe("mousedown", listRowMouseDownHandler);
eventDiv.observe("click", onCalendarSelectEvent);
eventDiv.observe("dblclick", editDoubleClickedEvent);
event.siblings.push(eventDiv);
return eventDiv;
}
function _drawCalendarEvents(events) {
var daysView = $("daysView");
var subdivs = daysView.childNodesWithTag("div");
var days = subdivs[1].childNodesWithTag("div");
for (var i = 0; i < events.length; i++) {
var parentDiv = days[i].childNodesWithTag("div")[0];
for (var j = 0; j < events[i].length; j++) {
var eventRep = events[i][j];
var eventDiv = newEventDIV(eventRep);
parentDiv.appendChild(eventDiv);
}
}
}
function newEventDIV(eventRep) {
// cname, calendar, starts, lasts,
// startHour, endHour, title) {
var eventDiv = $(document.createElement("div"));
var event = calendarEvents[eventRep.cname];
if (!event.siblings)
event.siblings = [];
eventDiv.event = event;
eventDiv.cname = event[0];
eventDiv.calendar = event[1];
eventDiv.addClassName("event");
if (eventRep.userState && userStates[eventRep.userState])
eventDiv.addClassName(userStates[eventRep.userState]);
eventDiv.addClassName("starts" + eventRep.start);
eventDiv.addClassName("lasts" + eventRep.length);
for (var i = 1; i < 5; i++) {
var shadowDiv = $(document.createElement("div"));
eventDiv.appendChild(shadowDiv);
shadowDiv.addClassName("shadow");
shadowDiv.addClassName("shadow" + i);
}
var innerDiv = $(document.createElement("div"));
eventDiv.appendChild(innerDiv);
innerDiv.addClassName("eventInside");
innerDiv.addClassName("calendarFolder" + event[1]);
var gradientDiv = $(document.createElement("div"));
innerDiv.appendChild(gradientDiv);
gradientDiv.addClassName("gradient");
@ -860,20 +902,7 @@ function newEventDIV(eventRep) {
var textDiv = $(document.createElement("div"));
innerDiv.appendChild(textDiv);
textDiv.addClassName("text");
// if (startHour) {
// var headerSpan = document.createElement("span");
// textDiv.appendChild(headerSpan);
// $(headerSpan).addClassName("eventHeader");
// headerSpan.appendChild(document.createTextNode(startHour + " - "
// + endHour));
// textDiv.appendChild(document.createElement("br"));
// }
textDiv.appendChild(document.createTextNode(event[3]));
var pc = 100 / eventRep.siblings;
eventDiv.style.width = pc + "%";
var left = eventRep.position * pc;
eventDiv.style.left = left + "%";
textDiv.appendChild(document.createTextNode(eventText));
eventDiv.observe("mousedown", listRowMouseDownHandler);
eventDiv.observe("click", onCalendarSelectEvent);
@ -884,6 +913,43 @@ function newEventDIV(eventRep) {
return eventDiv;
}
function newAllDayEventDIV(eventRep) {
// cname, calendar, starts, lasts,
// startHour, endHour, title) {
var event = calendarEvents[eventRep.cname];
var eventDiv = newBaseEventDIV(eventRep, event, event[3]);
return eventDiv;
}
function _drawCalendarEvents(events) {
var daysView = $("daysView");
var subdivs = daysView.childNodesWithTag("div");
var days = subdivs[1].childNodesWithTag("div");
for (var i = 0; i < events.length; i++) {
var parentDiv = days[i].childNodesWithTag("div")[0];
for (var j = 0; j < events[i].length; j++) {
var eventRep = events[i][j];
var eventDiv = newEventDIV(eventRep);
parentDiv.appendChild(eventDiv);
}
}
}
function newEventDIV(eventRep) {
var event = calendarEvents[eventRep.cname];
var eventDiv = newBaseEventDIV(eventRep, event, event[3]);
var pc = 100 / eventRep.siblings;
eventDiv.style.width = pc + "%";
var left = eventRep.position * pc;
eventDiv.style.left = left + "%";
eventDiv.addClassName("starts" + eventRep.start);
eventDiv.addClassName("lasts" + eventRep.length);
return eventDiv;
}
function _drawMonthCalendarEvents(events) {
var daysView = $("monthDaysView");
var days = daysView.childNodesWithTag("div");
@ -898,56 +964,14 @@ function _drawMonthCalendarEvents(events) {
}
function newMonthEventDIV(eventRep) {
// cname, calendar, starts, lasts,
// startHour, endHour, title) {
var eventDiv = $(document.createElement("div"));
var event = calendarEvents[eventRep.cname];
if (!event.siblings)
event.siblings = [];
eventDiv.event = event;
eventDiv.cname = event[0];
eventDiv.calendar = event[1];
eventDiv.addClassName("event");
if (eventRep.userState && userStates[eventRep.userState]) {
eventDiv.addClassName(userStates[eventRep.userState]);
log (eventDiv.getAttribute("class"));
}
for (var i = 1; i < 5; i++) {
var shadowDiv = $(document.createElement("div"));
eventDiv.appendChild(shadowDiv);
shadowDiv.addClassName("shadow");
shadowDiv.addClassName("shadow" + i);
}
var innerDiv = $(document.createElement("div"));
eventDiv.appendChild(innerDiv);
innerDiv.addClassName("eventInside");
innerDiv.addClassName("calendarFolder" + event[1]);
var gradientDiv = $(document.createElement("div"));
innerDiv.appendChild(gradientDiv);
gradientDiv.addClassName("gradient");
var gradientImg = document.createElement("img");
gradientDiv.appendChild(gradientImg);
gradientImg.src = ResourcesURL + "/event-gradient.png";
var textDiv = $(document.createElement("div"));
innerDiv.appendChild(textDiv);
textDiv.addClassName("textw");
var eventText;
if (event[7])
eventText = event[3];
else
eventText = eventRep.starthour + " - " + event[3];
textDiv.appendChild(document.createTextNode(eventText));
eventDiv.observe("mousedown", listRowMouseDownHandler);
eventDiv.observe("click", onCalendarSelectEvent);
eventDiv.observe("dblclick", editDoubleClickedEvent);
event.siblings.push(eventDiv);
var eventDiv = newBaseEventDIV(eventRep, event, eventText);
return eventDiv;
}

View File

@ -0,0 +1,13 @@
DIV#message, DIV#windowButtons
{ margin: 10px; }
DIV#windowButtons INPUT
{ text-align: center; }
DIV#leftButtons
{ float: left;
width: 200px;
text-align: left; }
DIV#rightButtons
{ text-align: right; }

View File

@ -0,0 +1,40 @@
function onCancelButtonClick(event) {
window.close();
}
function onThisButtonClick(event) {
if (action == 'edit')
window.opener.performEventEdition(calendarFolder, componentName,
recurrenceName);
else if (action == 'delete')
window.opener.performEventDeletion(calendarFolder, componentName,
recurrenceName);
else
window.alert("Invalid action: " + action);
window.close();
}
function onAllButtonClick(event) {
if (action == 'edit')
window.opener.performEventEdition(calendarFolder, componentName);
else if (action == 'delete')
window.opener.performEventDeletion(calendarFolder, componentName);
else
window.alert("Invalid action: " + action);
window.close();
}
function onOccurenceDialogLoad() {
var thisButton = $("thisButton");
thisButton.observe("click", onThisButtonClick);
var allButton = $("allButton");
allButton.observe("click", onAllButtonClick);
var cancelButton = $("cancelButton");
cancelButton.observe("click", onCancelButtonClick);
}
FastInit.addOnLoad(onOccurenceDialogLoad);