+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.18
+
+ * OCSiCalFieldExtractor.m: extract participants' state
+
+ * sql/generate-folderinfo-sql-for-user.sh, sql/appointment-create.psql,
+ sql/foldertablecreate-helge-privcal.psql: updated with new schema.
+
2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
* OCSiCalFieldExtractor.m: partmails + cn's are concatenated by '\n'
- (NSMutableDictionary *)extractQuickFieldsFromEvent:(iCalEvent *)_event {
NSMutableDictionary *row;
- NSCalendarDate *startDate, *endDate;
- NSString *uid, *title, *location, *status, *accessClass;
- NSNumber *sequence;
- id organizer;
- id participants, partmails;
-
+ NSCalendarDate *startDate, *endDate;
+ NSArray *attendees;
+ NSString *uid, *title, *location, *status, *accessClass;
+ NSNumber *sequence;
+ id organizer;
+ id participants, partmails;
+ NSMutableString *partstates;
+ unsigned i, count;
+
if (_event == nil)
return nil;
accessClass = [[_event accessClass] uppercaseString];
status = [[_event status] uppercaseString];
- participants = [_event attendees];
- partmails = [participants valueForKey:@"rfc822Email"];
+ attendees = [_event attendees];
+ partmails = [attendees valueForKey:@"rfc822Email"];
partmails = [partmails componentsJoinedByString:@"\n"];
- participants = [participants valueForKey:@"cn"];
+ participants = [attendees valueForKey:@"cn"];
participants = [participants componentsJoinedByString:@"\n"];
/* build row */
if ([status isNotNull]) {
int code = 1;
-
- if ([status isEqualToString:@"TENTATIVE"])
- code = 0;
- else if ([status isEqualToString:@"CANCELLED"])
- code = 2;
+
+ if ([status isEqualToString:@"TENTATIVE"])
+ code = 0;
+ else if ([status isEqualToString:@"CANCELLED"])
+ code = 2;
[row setObject:[NSNumber numberWithInt:code] forKey:@"status"];
}
else {
- /* confirmed by default */
- [row setObject:[NSNumber numberWithInt:1] forKey:@"status"];
+ /* confirmed by default */
+ [row setObject:[NSNumber numberWithInt:1] forKey:@"status"];
}
if([accessClass isNotNull] && ![accessClass isEqualToString:@"PUBLIC"]) {
organizer = [_event organizer];
if (organizer) {
- NSString *email;
-
- email = [organizer valueForKey:@"rfc822Email"];
- if (email)
- [row setObject:email forKey:@"orgmail"];
+ NSString *email;
+
+ email = [organizer valueForKey:@"rfc822Email"];
+ if (email)
+ [row setObject:email forKey:@"orgmail"];
+ }
+
+ /* construct partstates */
+ count = [attendees count];
+ partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
+ for ( i = 0; i < count; i++) {
+ iCalPerson *p;
+ iCalPersonPartStat stat;
+
+ p = [attendees objectAtIndex:i];
+ stat = [p participationStatus];
+ if(i != 0)
+ [partstates appendString:@"\n"];
+ [partstates appendFormat:@"%d", stat];
}
+ [row setObject:partstates forKey:@"partstates"];
+ [partstates release];
return row;
}
};
AD0CF94D071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = common.h;
};
AD0CF951071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = "EOAdaptorChannel+OCS.h";
};
AD0CF952071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = "EOAdaptorChannel+OCS.m";
};
AD0CF953071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = "EOQualifier+OCS.h";
};
AD0CF954071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = "EOQualifier+OCS.m";
};
AD0CF95F071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = "NSURL+OCS.h";
};
AD0CF960071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = "NSURL+OCS.m";
};
AD0CF961071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = ocs_cat.m;
};
AD0CF962071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = ocs_ls.m;
};
AD0CF963071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = ocs_mkdir.m;
};
AD0CF964071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = ocs_recreatequick.m;
};
AD0CF965071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSChannelManager.h;
};
AD0CF966071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSChannelManager.m;
};
AD0CF967071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSContactFieldExtractor.m;
};
AD0CF968071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSContext.h;
};
AD0CF969071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSContext.m;
};
AD0CF96A071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSFieldExtractor.h;
};
AD0CF96B071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSFieldExtractor.m;
};
AD0CF96C071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSFieldInfo.h;
};
AD0CF96D071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSFieldInfo.m;
};
AD0CF96E071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSFolder.h;
};
AD0CF96F071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSFolder.m;
};
AD0CF970071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSFolderManager.h;
};
AD0CF971071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSFolderManager.m;
};
AD0CF972071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSFolderType.h;
};
AD0CF973071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSFolderType.m;
};
AD0CF974071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSiCalFieldExtractor.h;
};
AD0CF975071FE18800E72147 = {
fileEncoding = 5;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.objc;
path = OCSiCalFieldExtractor.m;
AD0CF952071FE18800E72147,
AD0CF954071FE18800E72147,
);
+ indentWidth = 2;
isa = PBXGroup;
name = Classes;
refType = 4;
AD0CF951071FE18800E72147,
AD0CF953071FE18800E72147,
);
+ indentWidth = 2;
isa = PBXGroup;
name = Headers;
path = "";
};
AD0CF9CC071FE4A400E72147 = {
fileEncoding = 4;
+ indentWidth = 2;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = OCSStringFormatter.h;
MAJOR_VERSION=0
MINOR_VERSION=9
-SUBMINOR_VERSION:=17
+SUBMINOR_VERSION:=18
+# v0.9.18 requires libNGiCal v4.5.38
# v0.9.17 requires libNGiCal v4.5.37
# v0.9.11 requires libFoundation v1.0.63
# v0.9.11 requires libNGExtensions v4.3.125
participants VARCHAR(100000) NOT NULL, -- the CNs of the participants
isallday INT NULL,
iscycle INT NULL, -- client needs to fetch to resolve
+ ispublic INT NOT NULL,
+ status INT NOT NULL,
+ isopaque INT NULL,
location VARCHAR(256) NULL,
+ orgmail VARCHAR(256) NULL,
partmails VARCHAR(100000) NOT NULL, -- the emails of the participants
+ partstates VARCHAR(256) NOT NULL, -- the status of each participant
sequence INT NULL -- the iCal sequence
);
location VARCHAR(256) NULL,
orgmail VARCHAR(256) NULL,
partmails VARCHAR(100000) NOT NULL, -- the emails of the participants
+ partstates VARCHAR(256) NOT NULL, -- the status of each participant
sequence INT NULL -- the iCal sequence
);
location VARCHAR(256) NULL,
orgmail VARCHAR(256) NULL,
partmails VARCHAR(100000) NOT NULL, -- the emails of the participants
+ partstates VARCHAR(256) NOT NULL, -- the status of each participant
sequence INT NULL -- the iCal sequence
);
E87208F40692E3D30099CBBD,
E87208FA0692E3D30099CBBD,
E87208FB0692E3D30099CBBD,
+ AD273D4D077315640064794B,
+ AD273D4C077315640064794B,
);
isa = PBXGroup;
name = Appointment;
sourceTree = "<group>";
tabWidth = 2;
};
+ AD273D4C077315640064794B = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = text.xml;
+ path = UIxCalParticipationStatusView.wox;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD273D4D077315640064794B = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = UIxCalParticipationStatusView.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
AD2C74A1071A9FF70087E027 = {
fileEncoding = 5;
isa = PBXFileReference;
refType = 4;
sourceTree = "<group>";
};
- E87208F50692E3D30099CBBD = {
- fileEncoding = 5;
- indentWidth = 2;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.c.h;
- path = UIxAppointmentFormatter.h;
- refType = 4;
- sourceTree = "<group>";
- };
- E87208F60692E3D30099CBBD = {
- fileEncoding = 5;
- indentWidth = 2;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.c.objc;
- path = UIxAppointmentFormatter.m;
- refType = 4;
- sourceTree = "<group>";
- };
E87208F70692E3D30099CBBD = {
fileEncoding = 5;
indentWidth = 2;
children = (
E87208900692E3D30099CBBD,
E87208E40692E3D30099CBBD,
- E87208F50692E3D30099CBBD,
- E87208F60692E3D30099CBBD,
E87209060692E3D30099CBBD,
E87209070692E3D30099CBBD,
AD5ED1AE06B1768900E3EC4B,
- (NSArray *)fetchCoreInfosFrom:(NSCalendarDate *)_startDate
to:(NSCalendarDate *)_endDate;
+- (NSArray *)fetchOverviewInfosFromFolder:(OCSFolder *)_folder
+ from:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate;
+
+- (NSArray *)fetchOverviewInfosFrom:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate;
+
/* URL generation */
- (NSString *)baseURLForAptWithUID:(NSString *)_uid inContext:(id)_ctx;
return records;
}
+- (NSArray *)fetchOverviewInfosFromFolder:(OCSFolder *)_folder
+ from:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate
+{
+ static NSArray *infos = nil;
+ if(!infos) {
+ infos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate",
+ @"title", @"location", @"orgmail",
+ @"status", @"ispublic", @"iscycle",
+ @"isallday",
+ nil] retain];
+ }
+ return [self fetchFields:infos
+ fromFolder:_folder
+ from:_startDate
+ to:_endDate];
+}
+
+- (NSArray *)fetchOverviewInfosFrom:(NSCalendarDate *)_startDate
+ to:(NSCalendarDate *)_endDate
+{
+ OCSFolder *folder;
+
+ if ((folder = [self ocsFolder]) == nil) {
+ [self errorWithFormat:@"(%s): missing folder for fetch!",
+ __PRETTY_FUNCTION__];
+ return nil;
+ }
+ return [self fetchOverviewInfosFromFolder:folder from:_startDate to:_endDate];
+}
+
- (NSArray *)fetchCoreInfosFromFolder:(OCSFolder *)_folder
from:(NSCalendarDate *)_startDate
to:(NSCalendarDate *)_endDate
{
- static NSArray *coreInfos = nil;
- if(!coreInfos) {
- coreInfos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate",
- @"title", @"participants",
- @"location", @"isallday",
- @"iscycle", @"partmails",
- @"sequence", @"ispublic",
- @"isopaque", @"status", @"orgmail",
- nil] retain];
+ static NSArray *infos = nil;
+ if(!infos) {
+ infos = [[NSArray arrayWithObjects:@"uid", @"startdate", @"enddate",
+ @"title", @"location", @"orgmail",
+ @"status", @"ispublic", @"iscycle",
+ @"isallday", @"isopaque",
+ @"participants", @"partmails",
+ @"partstates", @"sequence",
+ nil] retain];
}
- return [self fetchFields:coreInfos
+ return [self fetchFields:infos
fromFolder:_folder
from:_startDate
to:_endDate];
if ((folder = [self ocsFolder]) == nil) {
[self errorWithFormat:@"(%s): missing folder for fetch!",
- __PRETTY_FUNCTION__];
+ __PRETTY_FUNCTION__];
return nil;
}
return [self fetchCoreInfosFromFolder:folder from:_startDate to:_endDate];
# Version file
-SUBMINOR_VERSION:=22
+SUBMINOR_VERSION:=23
# v0.9.19 requires NGiCal v4.5.36
# v0.9.13 requires libSOGo v0.9.26
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * Appointments/SOGoAppointmentFolder.[hm]: added "partstates" to
+ coreInfos for fetching. Also added new API to fetch a stripped down
+ version of coreInfos, suitable for overviews. (v0.9.23)
+
2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
* Appointments/SOGoAppointmentFolder.m: default redirect is now
return [self hasNoAttendees] && [[self emailForUser] length] > 0 ? YES : NO;
}
-/* email, cn */
+/* email, stat, cn */
-- (NSString *)combinedCNAndEmailForUser {
+- (NSString *)combinedInfoForUser {
NSString *e, *c;
e = [self emailForUser];
c = [self cnForUser];
- return [[e stringByAppendingString:@";"] stringByAppendingString:c];
+ return [[e stringByAppendingString:@";0;"] stringByAppendingString:c];
}
-- (NSString *)combinedCNAndEmail {
- NSString *e, *c;
+- (NSString *)combinedInfo {
+ iCalPerson *p;
+ NSString *e, *stat, *c;
- e = [[self attendee] rfc822Email];
- c = [[self attendee] cnForDisplay];
- return [[e stringByAppendingString:@";"] stringByAppendingString:c];
+ p = [self attendee];
+ e = [p rfc822Email];
+ c = [p cnForDisplay];
+ stat = [NSString stringWithFormat:@";%d;", [p participationStatus]];
+ return [[e stringByAppendingString:stat] stringByAppendingString:c];
}
/* id accessors */
pString = [_values objectAtIndex:i];
if ([pString length] == 0)
continue;
-
- /* delimiter between email and cn */
+
+ p = [[iCalPerson alloc] init];
+
+ /* sample: 'test.et.di.cete-lyon@equipement.gouv.fr;1;test' */
+ /* delimiter between email and partStat */
r = [pString rangeOfString:@";"];
if (r.length > 0) {
- /* sample: 'test.et.di.cete-lyon@equipement.gouv.fr;test' */
email = [pString substringToIndex:r.location];
- cn = [pString substringFromIndex:NSMaxRange(r)];
- if ([cn length] == 0) cn = nil;
+
+ pString = [pString substringFromIndex:NSMaxRange(r)];
+ /* delimiter between partStat and CN */
+ r = [pString rangeOfString:@";"];
+ if (r.length > 0) {
+ NSString *stat;
+
+ stat = [pString substringToIndex:r.location];
+ [p setParticipationStatus:[stat intValue]];
+ cn = [pString substringFromIndex:NSMaxRange(r)];
+ if ([cn length] == 0) cn = nil;
+ }
}
else {
email = pString;
cn = [um getCNForUID:[um getUIDForEmail:email]];
}
- p = [[iCalPerson alloc] init];
[p setEmail:[@"mailto:" stringByAppendingString:email]];
if ([cn isNotNull]) [p setCn:cn];
@" checkbox = document.createElement('input');\n"
@" checkbox.setAttribute('type', 'checkbox');\n"
@" checkbox.setAttribute('checked', 'checked');\n"
- @" checkbox.setAttribute('value', email + ';' + cn);\n"
+ @" checkbox.setAttribute('value', email + ';0;' + cn);\n"
@" checkbox.setAttribute('id', email);\n"
@" checkbox.setAttribute('name', tableId);\n"
@" td.appendChild(checkbox);\n"
<tr>
<td><input type="checkbox"
checked="YES"
- var:value="combinedCNAndEmailForUser"
+ var:value="combinedInfoForUser"
var:id="emailForUser"
var:name="checkboxId"
/></td>
<var:if condition="withCN">
<td><input type="checkbox"
checked="YES"
- var:value="combinedCNAndEmail"
+ var:value="combinedInfo"
var:id="attendee.rfc822Email"
var:name="checkboxId"
/></td>
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * AnaisAttendeeSelector.m: preserve participation status (v0.9.18)
+
2004-10-20 Marcus Mueller <znek@mulle-kybernetik.com>
* AnaisAttendeeSelector.m: provide proper unescaping of special HTML
# $Id: Version 165 2004-08-05 17:55:50Z znek $
-SUBMINOR_VERSION:=17
+SUBMINOR_VERSION:=18
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * SOGoAptFormatter.m: remove "appointment" prefix from tooltips
+ (v0.9.22)
+
2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
* SOGoAptFormatter.[hm]: new option for formatter (v0.9.21)
spansRange = ![endDate isEqualToDate:startDate];
}
aptDescr = [NSMutableString stringWithCapacity:60];
- [aptDescr appendString:@"appointment"];
- [aptDescr appendFormat:@"\n%02i:%02i",
+ [aptDescr appendFormat:@"%02i:%02i",
[startDate hourOfDay],
[startDate minuteOfHour]];
if (spansRange) {
spansRange = ![endDate isEqualToDate:startDate];
}
aptDescr = [NSMutableString stringWithCapacity:25];
- [aptDescr appendString:@"appointment"];
- [aptDescr appendFormat:@"\n%02i:%02i",
+ [aptDescr appendFormat:@"%02i:%02i",
[startDate hourOfDay],
[startDate minuteOfHour]];
if (spansRange) {
# $Id$
-SUBMINOR_VERSION:=21
+SUBMINOR_VERSION:=22
# v0.9.18 requires NGExtensions v4.5.136
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.104
+
+ * GNUmakefile: removed UIxAppointmentFormatter
+
+ * UIxAppointmentFormatter.[hm]: removed, never in use
+
+ * UIxCalParticipationStatusView.[wox,m]: new view to display localized
+ participation status
+
+ * UIxCalView.m: fetch OverviewInfos instead of CoreInfos (should be
+ faster)
+
+ * UIxCalScheduleOverview.[wox,m]: completed the schedule overview,
+ display participation state for all participants.
+
+ * product.plist: renamed "reject" to "decline" and moved from
+ UIxAppointmentView to UIxAppointmentEditor.
+
+ * UIxAppointmentView.m: removed "accept" and "reject" action stubs
+
+ * UIxAppointmentEditor.[wox,m]: implemented "accept" and "decline".
+ Changed iCal template a bit, particularly removed getOrganizer -
+ with every save the organizer was changed also which was horribly
+ wrong.
+
2004-12-16 Marcus Mueller <znek@mulle-kybernetik.com>
* v0.9.103
"sched_startDateFormat" = "%d.%m. %H:%M";
"action" = "Actions";
"accept" = "Accept";
-"reject" = "Reject";
+"decline" = "Decline";
+"more participants" = "more participants";
/* Appointments */
"Title" = "Title";
"Name" = "Name";
"Email" = "Email";
+"Status" = "Status";
"Location" = "Location";
"Priority" = "Priority";
"Categories" = "Categories";
/* text used in overviews and tooltips */
"private appointment" = "Private appointment";
+/* Appointments (participation state) */
+
+"partStat_NEEDS-ACTION" = "Not decided, yet";
+"partStat_ACCEPTED" = "Accepted";
+"partStat_DECLINED" = "Declined";
+"partStat_TENTATIVE" = "Tentative";
+"partStat_DELEGATED" = "Delegated";
+"partStat_OTHER" = "???";
+
/* Searching */
SchedulerUI_OBJC_FILES = \
SchedulerUIProduct.m \
- UIxAppointmentFormatter.m \
iCalPerson+UIx.m \
NSCalendarDate+UIx.m \
SOGoAppointment+UIx.m \
UIxCalYearOverview.m \
UIxCalInlineMonthOverview.m \
UIxAppointmentView.m \
- UIxAppointmentPrintview.m \
+ UIxAppointmentPrintview.m \
UIxAppointmentEditor.m \
UIxCalSelectTab.m \
UIxCalDateLabel.m \
UIxTimeDateControl.m \
UIxCalInlineAptView.m \
UIxCalScheduleOverview.m \
+ UIxCalParticipationStatusView.m \
SchedulerUI_RESOURCE_FILES += \
Version \
product.plist
-SchedulerUI_RESOURCE_FILES += \
- UIxAptTableView.wox \
- UIxCalDayOverview.wox \
- UIxCalDayChartview.wox \
- UIxCalDayListview.wox \
- UIxCalDayPrintview.wox \
- UIxCalWeekOverview.wox \
- UIxCalWeekChartview.wox \
- UIxCalWeekListview.wox \
- UIxCalWeekColumnsview.wox \
- UIxCalWeekPrintview.wox \
- UIxCalMonthOverview.wox \
- UIxCalMonthPrintview.wox \
- UIxCalYearOverview.wox \
- UIxCalInlineMonthOverview.wox \
- UIxAppointmentView.wox \
+SchedulerUI_RESOURCE_FILES += \
+ UIxAptTableView.wox \
+ UIxCalDayOverview.wox \
+ UIxCalDayChartview.wox \
+ UIxCalDayListview.wox \
+ UIxCalDayPrintview.wox \
+ UIxCalWeekOverview.wox \
+ UIxCalWeekChartview.wox \
+ UIxCalWeekListview.wox \
+ UIxCalWeekColumnsview.wox \
+ UIxCalWeekPrintview.wox \
+ UIxCalMonthOverview.wox \
+ UIxCalMonthPrintview.wox \
+ UIxCalYearOverview.wox \
+ UIxCalInlineMonthOverview.wox \
+ UIxAppointmentView.wox \
UIxAppointmentPrintview.wox \
- UIxAppointmentEditor.wox \
- UIxCalSelectTab.wox \
- UIxCalDateLabel.wox \
- UIxCalBackForthNavView.wox \
- UIxAppointmentProposal.wox \
- UIxDatePicker.wox \
- UIxDatePickerScript.wox \
- UIxTimeSelector.wox \
- UIxTimeDateControl.wox \
- UIxCalInlineAptView.wox \
- UIxCalScheduleOverview.wox \
+ UIxAppointmentEditor.wox \
+ UIxCalSelectTab.wox \
+ UIxCalDateLabel.wox \
+ UIxCalBackForthNavView.wox \
+ UIxAppointmentProposal.wox \
+ UIxDatePicker.wox \
+ UIxDatePickerScript.wox \
+ UIxTimeSelector.wox \
+ UIxTimeDateControl.wox \
+ UIxCalInlineAptView.wox \
+ UIxCalScheduleOverview.wox \
+ UIxCalParticipationStatusView.wox \
SchedulerUI_RESOURCE_FILES += \
images/next_week.gif \
- (NSString *)_completeURIForMethod:(NSString *)_method;
-- (iCalPerson *)getOrganizer;
- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
treatAsResource:(BOOL)_isResource;
- (NSString *)iCalResourcesStringFromQueryParameters;
- (NSString *)iCalStringFromQueryParameter:(NSString *)_qp
format:(NSString *)_format;
+- (NSString *)iCalOrganizerString;
+
+- (id)acceptOrDeclineAction:(BOOL)_accept;
+
@end
#include "common.h"
@"BEGIN:VEVENT\r\n"
@"UID:%@\r\n"
@"CLASS:PUBLIC\r\n"
- @"STATUS:CONFIRMED\r\n"
+ @"STATUS:CONFIRMED\r\n" /* confirmed by default */
@"DTSTAMP:%@\r\n"
@"DTSTART:%@\r\n"
@"DTEND:%@\r\n"
@"TRANSP:%@\r\n"
@"SEQUENCE:1\r\n"
@"PRIORITY:5\r\n"
- @"%@"
+ @"%@" /* organizer */
+ @"%@" /* participants and resources */
@"END:VEVENT\r\n"
@"END:VCALENDAR";
lEndDate = [lStartDate dateByAddingYears:0 months:0 days:0
hours:0 minutes:minutes seconds:0];
- s = [self iCalParticipantsAndResourcesStringFromQueryParameters];
- template = [NSString stringWithFormat:iCalStringTemplate,
- [[self clientObject] nameInContainer],
- [[NSCalendarDate date] icalString],
- [lStartDate icalString],
- [lEndDate icalString],
- [self transparency],
- s];
+ s = [self iCalParticipantsAndResourcesStringFromQueryParameters];
+ template = [NSString stringWithFormat:iCalStringTemplate,
+ [[self clientObject] nameInContainer],
+ [[NSCalendarDate date] icalString],
+ [lStartDate icalString],
+ [lEndDate icalString],
+ [self transparency],
+ [self iCalOrganizerString],
+ s];
return template;
}
return iCalRep;
}
+- (NSString *)iCalOrganizerString {
+ static NSString *fmt = @"ORGANIZER;CN=\"%@\":mailto:%@\r\n";
+ return [NSString stringWithFormat:fmt,
+ [self cnForUser],
+ [self emailForUser]];
+}
+
+#if 0
+- (iCalPerson *)getOrganizer {
+ iCalPerson *p;
+ NSString *emailProp;
+
+ emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
+ p = [[[iCalPerson alloc] init] autorelease];
+ [p setEmail:emailProp];
+ [p setCn:[self cnForUser]];
+ return p;
+}
+#endif
+
+
/* helper */
- (NSString *)_completeURIForMethod:(NSString *)_method {
return date;
}
-- (iCalPerson *)getOrganizer {
- iCalPerson *p;
- NSString *emailProp;
-
- emailProp = [@"mailto:" stringByAppendingString:[self emailForUser]];
- p = [[[iCalPerson alloc] init] autorelease];
- [p setEmail:emailProp];
- [p setCn:[self cnForUser]];
- return p;
-}
-
- (NSArray *)getICalPersonsFromFormValues:(NSArray *)_values
treatAsResource:(BOOL)_isResource
{
: lResources;
}
[_appointment setAttendees:attendees];
-
+
+#if 0
[_appointment setOrganizer:[self getOrganizer]];
+#endif
}
- (void)loadValuesFromICalString:(NSString *)_ical {
- (id)saveAction {
SOGoAppointment *apt;
- NSException *ex;
+ iCalPerson *p;
NSString *content;
+ NSException *ex;
if (![self isWriteableClientObject]) {
/* return 400 == Bad Request */
}
[self saveValuesIntoAppointment:apt];
+ p = [apt findParticipantWithEmail:[self emailForUser]];
+ if (p) {
+ [p setParticipationStatus:iCalPersonPartStatAccepted];
+ }
+
content = [apt iCalString];
[apt release]; apt = nil;
return [self redirectToLocation:[self _completeURIForMethod:@".."]];
}
+- (id)acceptAction {
+ return [self acceptOrDeclineAction:YES];
+}
+
+- (id)declineAction {
+ return [self acceptOrDeclineAction:NO];
+}
+
+- (id)acceptOrDeclineAction:(BOOL)_accept {
+ SOGoAppointment *apt;
+ iCalPerson *p;
+ NSString *iCal, *content;
+ NSException *ex;
+
+ if (![self isWriteableClientObject]) {
+ /* 400 == Bad Request */
+ return [NSException exceptionWithHTTPStatus:400
+ reason:@"method cannot be invoked on "
+ @"the specified object"];
+ }
+ iCal = [[self clientObject] valueForKey:@"iCalString"];
+ apt = [[SOGoAppointment alloc] initWithICalString:iCal];
+ if (apt == nil) {
+ /* 500 == Internal Server Error */
+ return [NSException exceptionWithHTTPStatus:500
+ reason:@"unable to parse appointment"];
+ }
+
+ p = [apt findParticipantWithEmail:[self emailForUser]];
+ if (!p) {
+ /* 404 == Not found */
+ return [NSException exceptionWithHTTPStatus:404
+ reason:@"user does not participate in this "
+ @"appointment"];
+ }
+ if(_accept)
+ [p setParticipationStatus:iCalPersonPartStatAccepted];
+ else
+ [p setParticipationStatus:iCalPersonPartStatDeclined];
+
+ content = [apt iCalString];
+ [apt release];
+
+ if (content == nil) {
+ /* 500 == Internal Server Error */
+ return [NSException exceptionWithHTTPStatus:500
+ reason:@"Could not create iCalendar data ..."];
+ }
+
+ ex = [[self clientObject] saveContentString:content];
+ if (ex != nil) {
+ /* 500 == Internal Server Error */
+ return [NSException exceptionWithHTTPStatus:500
+ reason:[ex reason]];
+ }
+
+ return [self redirectToLocation:[self _completeURIForMethod:@".."]];
+}
+
@end /* UIxAppointmentEditor */
+++ /dev/null
-/*
- Copyright (C) 2004 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-// $Id$
-
-#ifndef __UIxAppointmentFormatter_H__
-#define __UIxAppointmentFormatter_H__
-
-#import <Foundation/NSFormatter.h>
-
-/*
- Formatter to format appointment-dicts to readable strings:
-
- %[(dateFormat)]S
- - startDate formatted with the dateFormat string
- - if no dateFormat is defined, the default formats are used
- - !!! dont forget the ()brakes !!!
- %[(dateFormat)]E
- - endDate formatted with the dateFormat string
- - if no dateFormat is defined, the default formats are used
- - !!! dont forget the ()brakes !!!
-
- %[length]T - title with the specified length
- - if title has more chars than length overLengthString is appended
- - if no length is defined, no limit is set
-
- %[max]P - participants
- - if more than max participants at appointment
- moreParticipantsString is appended
- - if no max defined no limit is set
-
- %[length]L - location
- - if location has more chars than length overLengthString is
- appended
- - if no length is defined, no limit is set
-
- Example:
- format: @"%S - %E, \n%T";
-*/
-
-@class NSCalendarDate;
-
-@interface UIxAppointmentFormatter : NSFormatter
-{
-@protected
- NSString *formatString; // default: @"%S - %E, \n%T"
-
- NSString *dateFormat; // default: @"%H:%M"
- NSString *otherDayDateFormat; // default: @"%H:%M(%m-%d)"
- NSString *otherYearDateFormat; // default: @"%H:%M(%Y-%m-%d)"
-
- NSString *toLongString; // default: @".."
- NSString *moreParticipantsString; // default: @"..."
- NSString *participantsSeparator; // default: @", "
-
- NSCalendarDate *relationDate; // if nil, dateFormat used as format
- // to know whether its the same day, the same year or another year
-
- BOOL showFullNames; // try to show full names of participants
-}
-
-/* init */
-
-- (id)initWithFormat:(NSString *)_format;
-+ (UIxAppointmentFormatter *)formatterWithFormat:(NSString *)_format;
-+ (UIxAppointmentFormatter *)formatter;
-
-/* accessors */
-
-- (void)setFormat:(NSString *)_format;
-- (NSString *)format;
-
-- (void)setDateFormat:(NSString *)_format;
-- (NSString *)dateFormat;
-
-- (void)setOtherDayDateFormat:(NSString *)_format;
-- (NSString *)otherDayDateFormat;
-
-- (void)setOtherYearDateFormat:(NSString *)_format;
-- (NSString *)otherYearDateFormat;
-
-- (void)setToLongString:(NSString *)_toLong;
-- (NSString *)toLongString;
-
-- (void)setMoreParticipantsString:(NSString *)_more;
-- (NSString *)moreParticipantsString;
-
-- (void)setParticipantsSeparator:(NSString *)_sep;
-- (NSString *)participantsSeparator;
-
-- (void)setRelationDate:(NSCalendarDate *)_relation;
-- (NSCalendarDate *)relationDate;
-
-- (void)setShowFullNames:(BOOL)_flag;
-- (BOOL)showFullNames;
-
-/* easy switches */
-
-/* this resets the date format */
-- (void)switchToAMPMTimes:(BOOL)_showAMPM;
-
-@end
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2004 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-// $Id$
-
-#include "UIxAppointmentFormatter.h"
-#import <Foundation/Foundation.h>
-#import <NGExtensions/NGExtensions.h>
-#import <EOControl/EOControl.h>
-
-@implementation UIxAppointmentFormatter
-
-- (id)init {
- if ((self = [super init])) {
- [self setFormat:@"%S - %E, \n%T"];
- [self setDateFormat:@"%H:%M"];
- [self setOtherDayDateFormat:@"%H:%M(%m-%d)"];
- [self setOtherYearDateFormat:@"%H:%M(%Y-%m-%d)"];
- [self setToLongString:@".."];
- [self setMoreParticipantsString:@"..."];
- [self setParticipantsSeparator:@", "];
- [self setRelationDate:nil];
- self->showFullNames = NO;
- }
- return self;
-}
-
-- (id)initWithFormat:(NSString *)_format {
- if ((self = [self init])) {
- [self setFormat:_format];
- }
- return self;
-}
-
-+ (UIxAppointmentFormatter *)formatterWithFormat:(NSString *)_format {
- return AUTORELEASE([(UIxAppointmentFormatter *)[UIxAppointmentFormatter alloc]
- initWithFormat:_format]);
-}
-
-+ (UIxAppointmentFormatter *)formatter {
- return AUTORELEASE([[UIxAppointmentFormatter alloc] init]);
-}
-
-- (void)dealloc {
- RELEASE(self->formatString);
- RELEASE(self->dateFormat);
- RELEASE(self->otherDayDateFormat);
- RELEASE(self->otherYearDateFormat);
- RELEASE(self->toLongString);
- RELEASE(self->moreParticipantsString);
- RELEASE(self->participantsSeparator);
- RELEASE(self->relationDate);
-
- [super dealloc];
-}
-
-// accessors
-
-- (void)setFormat:(NSString *)_format {
- ASSIGN(self->formatString,_format);
-}
-- (NSString *)format {
- return self->formatString;
-}
-
-- (void)setDateFormat:(NSString *)_format {
- ASSIGN(self->dateFormat,_format);
-}
-- (NSString *)dateFormat {
- return self->dateFormat;
-}
-
-- (void)setOtherDayDateFormat:(NSString *)_format {
- ASSIGN(self->otherDayDateFormat,_format);
-}
-- (NSString *)otherDayDateFormat {
- return self->otherDayDateFormat;
-}
-
-- (void)setOtherYearDateFormat:(NSString *)_format {
- ASSIGN(self->otherYearDateFormat,_format);
-}
-- (NSString *)otherYearDateFormat {
- return self->otherYearDateFormat;
-}
-
-- (void)setToLongString:(NSString *)_toLong {
- ASSIGN(self->toLongString,_toLong);
-}
-- (NSString *)toLongString {
- return self->toLongString;
-}
-
-- (void)setMoreParticipantsString:(NSString *)_more {
- ASSIGN(self->moreParticipantsString,_more);
-}
-- (NSString *)moreParticipantsString {
- return self->moreParticipantsString;
-}
-
-- (void)setParticipantsSeparator:(NSString *)_sep {
- ASSIGN(self->participantsSeparator,_sep);
-}
-- (NSString *)participantsSeparator {
- return self->participantsSeparator;
-}
-
-- (void)setRelationDate:(NSCalendarDate *)_relation {
- ASSIGN(self->relationDate,_relation);
-}
-- (NSCalendarDate *)relationDate {
- return self->relationDate;
-}
-
-- (void)setShowFullNames:(BOOL)_flag {
- self->showFullNames = _flag;
-}
-- (BOOL)showFullNames {
- return self->showFullNames;
-}
-
-// easy switching
-- (void)switchToAMPMTimes:(BOOL)_showAMPM {
- if (_showAMPM) {
- [self setDateFormat:@"%I:%M %p"];
- [self setOtherDayDateFormat:@"%I:%M %p(%m-%d)"];
- [self setOtherYearDateFormat:@"%I:%M %p(%Y-%m-%d)"];
- }
- else {
- [self setDateFormat:@"%H:%M"];
- [self setOtherDayDateFormat:@"%H:%M(%m-%d)"];
- [self setOtherYearDateFormat:@"%H:%M(%Y-%m-%d)"];
- }
-}
-
-// formatting helpers
-
-- (NSString *)formatDate:(NSCalendarDate *)_date
- withFormat:(NSString *)_format
-{
- NSString *f;
- NSCalendarDate *rel;
-
- rel = self->relationDate;
-
- if (_format == nil) {
- if (rel == nil) {
- f = self->dateFormat;
- }
- else if ([_date isDateOnSameDay:rel]) {
- f = self->dateFormat;
- }
- else if ([_date yearOfCommonEra] == [rel yearOfCommonEra]) {
- f = self->otherDayDateFormat;
- }
- else {
- f = self->otherYearDateFormat;
- }
- }
- else {
- f = _format;
- }
- return [_date descriptionWithCalendarFormat:f];
-}
-
-- (NSString *)formatStartDateFromApt:(id)_apt
- withFormat:(NSString *)_format
-{
- return [self formatDate:[_apt valueForKey:@"startDate"]
- withFormat:_format];
-}
-
-- (NSString *)formatEndDateFromApt:(id)_apt
- withFormat:(NSString *)_format
-{
- return [self formatDate:[_apt valueForKey:@"endDate"]
- withFormat:_format];
-}
-
-- (NSString *)stringForParticipant:(id)_part {
- id label = nil;
-
- if ([[_part valueForKey:@"isTeam"] boolValue]) {
- if ((label = [_part valueForKey:@"info"]) == nil)
- label = [_part valueForKey:@"description"];
- }
- else if (self->showFullNames) {
- label = [_part valueForKey:@"firstname"];
- label = ([label length])
- ? [label stringByAppendingFormat:@" %@", [_part valueForKey:@"name"]]
- : [_part valueForKey:@"name"];
- }
- else if ([[_part valueForKey:@"isAccount"] boolValue]) {
- label = [_part valueForKey:@"login"];
- }
- else {
- if ((label = [_part valueForKey:@"name"]) == nil) {
- if ((label = [_part valueForKey:@"info"]) == nil)
- label = [_part valueForKey:@"description"];
- }
- }
-
- if (![label isNotNull])
- label = @"*";
-
- return label;
-}
-
-- (NSString *)participantsForApt:(id)_apt
- withMaxCount:(NSString *)_cnt {
- NSArray *p;
- int max;
- int cnt;
- NSMutableString *pString;
-
- pString = [NSMutableString stringWithCapacity:255];
-
- if (_cnt == nil) {
- max = -1; // no limit
- }
- else {
- max = [_cnt intValue];
- }
-
- p = [_apt valueForKey:@"participants"];
-
- p = [p sortedArrayUsingKeyOrderArray:
- [NSArray arrayWithObjects:
- [EOSortOrdering sortOrderingWithKey:@"isAccount"
- selector:EOCompareAscending],
- [EOSortOrdering sortOrderingWithKey:@"login"
- selector:EOCompareAscending],
- nil]];
-
- max = ((max > [p count]) || (max == -1))
- ? [p count]
- : max;
-
- for (cnt = 0; cnt < max; cnt++) {
-
- if (cnt != 0)
- [pString appendString:self->participantsSeparator];
-
- [pString appendString:
- [self stringForParticipant:[p objectAtIndex:cnt]]];
- }
-
- if (max < [p count]) {
- [pString appendString:self->moreParticipantsString];
- }
-
- return pString;
-}
-
-- (NSString *)titleForApt:(id)_apt withMaxLength:(NSString *)_length {
- NSString *t = nil;
- int l;
-
- l = (_length == nil)
- ? -1
- : [_length intValue];
-
- t = [_apt valueForKey:@"title"];
- if (!t) return @"*";
-
- if (l > 1) {
- if ([t length] > l) {
- t = [t substringToIndex:(l - 2)];
- t = [t stringByAppendingString:self->toLongString];
- }
- }
-
- if (l == 0)
- t = @"*";
-
- return t;
-}
-
-- (NSString *)locationForApt:(id)_apt withMaxLength:(NSString *)_length {
- NSString *t = nil;
- int l;
-
- l = (_length == nil)
- ? -1
- : [_length intValue];
-
- t = [_apt valueForKey:@"location"];
- if (![t isNotNull] ||
- [t length] == 0 ||
- [t isEqualToString:@" "])
- return @"";
-
- if (l > 1) {
- if ([t length] > l) {
- t = [t substringToIndex:(l - 2)];
- t = [t stringByAppendingString:self->toLongString];
- }
- }
-
- if (l == 0)
- t = @"";
-
- return t;
-}
-
-- (NSString *)resourcesForApt:(id)_apt withMaxLength:(NSString *)_length {
- NSString *t = nil;
- int l;
-
- l = (_length == nil)
- ? -1
- : [_length intValue];
-
- t = [_apt valueForKey:@"resourceNames"];
- if (![t isNotNull] ||
- [t length] == 0 ||
- [t isEqualToString:@" "])
- return @"";
-
- if (l > 1) {
- if ([t length] > l) {
- t = [t substringToIndex:(l - 2)];
- t = [t stringByAppendingString:self->toLongString];
- }
- }
-
- if (l == 0)
- t = @"";
-
- return t;
-}
-
-// NSFormatter stuff
-
-- (NSString *)stringForObjectValue:(id)_obj {
- NSMutableString *newString;
- int cnt;
- int length;
- BOOL replaceMode = NO;
- NSString *helper = nil;
- NSCharacterSet *digits;
-
- newString = [NSMutableString stringWithCapacity:255];
- length = [self->formatString length];
- digits = [NSCharacterSet decimalDigitCharacterSet];
-
- // NSLog(@"Formatting with format: %@", self->formatString);
-
- for (cnt = 0; cnt < length; cnt++) {
- unichar c;
- c = [self->formatString characterAtIndex:cnt];
- // NSLog(@"Character is: %c mode is: %@", c,
- // [NSNumber numberWithBool:replaceMode]);
- if (replaceMode) {
- if (c == 'S') {
- [newString appendString:
- [self formatStartDateFromApt:_obj withFormat:helper]];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == 'E') {
- [newString appendString:
- [self formatEndDateFromApt:_obj withFormat:helper]];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == 'P') {
- [newString appendString:
- [self participantsForApt:_obj withMaxCount:helper]];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == 'T') {
- [newString appendString:
- [self titleForApt:_obj withMaxLength:helper]];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == 'L') {
- NSString *l;
-
- l = [self locationForApt:_obj withMaxLength:helper];
-
- if ([l length] > 0)
- [newString appendString:l];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == 'R') {
- NSString *r;
-
- r = [self resourcesForApt:_obj withMaxLength:helper];
-
- if ([r length] > 0)
- [newString appendString:r];
- helper = nil;
- replaceMode = NO;
- }
- else if (c == '(') {
- int end;
- NSRange r = NSMakeRange(cnt,length-cnt);
-
- r = [self->formatString rangeOfString:@")"
- options:0 range:r];
-
- end = r.location - 1;
- r = NSMakeRange(cnt+1, end-cnt-1);
-
- helper = [self->formatString substringWithRange:r];
- cnt = end + 1;
- }
- else if ([digits characterIsMember:c]) {
- helper = (helper == nil)
- ? [NSString stringWithFormat:@"%c",c]
- : [NSString stringWithFormat:@"%@%c", helper, c];
- }
- else {
- NSLog(@"UNKNOWN FORMAT CHARACTER '%c'!!",c);
- replaceMode = NO;
- helper = nil;
- }
- } else {
- if (c == '%') {
- replaceMode = YES;
- }
- else {
- [newString appendFormat:@"%c", c];
- }
- }
- }
-
- return newString;
-}
-
-@end
return [self redirectToLocation:url];
}
-- (id)acceptAction {
- if ([self appointment] == nil) {
- return [NSException exceptionWithHTTPStatus:404
- reason:@"could not locate appointment"];
- }
- return self;
-}
-
-- (id)rejectAction {
- if ([self appointment] == nil) {
- return [NSException exceptionWithHTTPStatus:404
- reason:@"could not locate appointment"];
- }
- return self;
-}
-
@end /* UIxAppointmentView */
<table width="100%" border="0" cellpadding="4" cellspacing="0">
<tr valign="top">
<td align="left" bgcolor="#E8E8E0" class="aptview_title">
- <var:string label:value="Name" />
+ <var:string label:value="Name" />
</td>
<td align="left" bgcolor="#E8E8E0" class="aptview_title">
- <var:string label:value="Email" />
+ <var:string label:value="Email" />
+ </td>
+ <td align="left" bgcolor="#E8E8E0" class="aptview_title">
+ <var:string label:value="Status" />
</td>
</tr>
<var:foreach list="appointment.participants"
item="attendee"
>
- <tr valign="top">
- <td align="left" bgcolor="#FFFFF0" class="aptview_text">
+ <tr valign="top">
+ <td align="left" bgcolor="#FFFFF0" class="aptview_text">
<var:string value="attendee.cnForDisplay" />
- </td>
- <td align="left" bgcolor="#FFFFF0" class="aptview_text">
+ </td>
+ <td align="left" bgcolor="#FFFFF0" class="aptview_text">
<a var:href="attendee.email"
><var:string value="attendee.rfc822Email" /></a>
- </td>
- </tr>
+ </td>
+ <td align="left" bgcolor="#FFFFF0" class="aptview_text">
+ <var:component className="UIxCalParticipationStatusView"
+ partStat="attendee.participationStatus"
+ />
+ </td>
+ </tr>
</var:foreach>
</table>
</uix:tab>
--- /dev/null
+/*
+ Copyright (C) 2000-2004 SKYRIX Software AG
+
+ This file is part of OGo
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+// $Id$
+
+
+#include <NGObjWeb/NGObjWeb.h>
+
+
+@interface UIxCalParticipationStatusView : WOComponent
+{
+ int partStat;
+}
+
+- (NSString *)participationStatus;
+
+@end
+
+#include <NGiCal/NGiCal.h> /* for iCalPersonPartStat */
+#include "common.h"
+
+@implementation UIxCalParticipationStatusView
+
+- (void)setPartStat:(id)_partStat {
+ self->partStat = [_partStat intValue];
+}
+
+- (NSString *)participationStatus {
+ switch (self->partStat) {
+ case iCalPersonPartStatNeedsAction:
+ return @"NEEDS-ACTION";
+ case iCalPersonPartStatAccepted:
+ return @"ACCEPTED";
+ case iCalPersonPartStatDeclined:
+ return @"DECLINED";
+ case iCalPersonPartStatTentative:
+ return @"TENTATIVE";
+ case iCalPersonPartStatDelegated:
+ return @"DELEGATED";
+ }
+ return @"OTHER";
+}
+
+- (NSString *)participationStatusLabel {
+ return [NSString stringWithFormat:@"partStat_%@",
+ [self participationStatus]];
+}
+
+@end
--- /dev/null
+<?xml version='1.0' standalone='yes'?>
+
+<var:string xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:var="http://www.skyrix.com/od/binding"
+ xmlns:const="http://www.skyrix.com/od/constant"
+ xmlns:label="OGo:label"
+ label:value="$participationStatusLabel"
+/>
NSMutableArray *userApts;
NSMutableArray *foreignApts;
id item;
+ NSArray *partNames;
+ NSArray *partStates;
+ unsigned participantIndex;
}
- (NSCalendarDate *)startDate;
- (NSString *)appointmentBaseURL;
+- (unsigned)participantsCount;
+- (unsigned)maxRenderedParticipantsCount;
+- (unsigned)renderedParticipantsCount;
+- (unsigned)truncatedParticipantsCount;
+- (BOOL)didTruncateParticipants;
+
@end
#include <NGObjWeb/SoComponent.h>
- (void)dealloc {
[self->userApts release];
[self->foreignApts release];
+ [self->partNames release];
+ [self->partStates release];
[self->item release];
[super dealloc];
}
/* accessors */
- (void)setItem:(id)_item {
+ NSString *ps;
+
ASSIGN(self->item, _item);
+
+ [self->partNames release];
+ [self->partStates release];
+ ps = [self->item valueForKey:@"participants"];
+ self->partNames = [[ps componentsSeparatedByString:@"\n"] retain];
+ ps = [self->item valueForKey:@"partstates"];
+ self->partStates = [[ps componentsSeparatedByString:@"\n"] retain];
+ NSLog(@"new item:%@", self->item);
}
- (id)item {
return self->item;
}
+- (void)setParticipantIndex:(unsigned)_participantIndex {
+ NSLog(@"_participantIndex:%d", _participantIndex);
+ self->participantIndex = _participantIndex;
+}
+- (unsigned)participantIndex {
+ return self->participantIndex;
+}
+
- (BOOL)hasUserAppointments {
return [[self userAppointments] count] > 0;
}
[self hasForeignAppointments]) ? YES : NO;
}
-- (NSString *)participants {
- NSString *s;
+- (unsigned)participantsCount {
+ return [self->partNames count];
+}
+
+- (NSString *)participant {
+ [self logWithFormat:@"participant(%d):%@",
+ self->participantIndex,
+ [self->partNames objectAtIndex:self->participantIndex]];
+ return [self->partNames objectAtIndex:self->participantIndex];
+}
+
+- (NSString *)participationStatus {
+ [self logWithFormat:@"state(%d):%@",
+ self->participantIndex,
+ [self->partStates objectAtIndex:self->participantIndex]];
+ return [self->partStates objectAtIndex:self->participantIndex];
+}
+
+- (unsigned)maxRenderedParticipantsCount {
+ return 2;
+}
+
+// TODO: find the bug in WORepetition and get rid of this!
+- (NSArray *)renderedParticipants {
+ NSRange r;
- s = [self->item valueForKey:@"participants"];
- if (!s)
- return @"";
- if ([s length] > 100)
- s = [NSString stringWithFormat:@"%@...", [s substringToIndex:100]];
- return s;
+ r = NSMakeRange(0, [self renderedParticipantsCount]);
+ return [self->partNames subarrayWithRange:r];
+}
+- (void)setFoo:(id)_foo {
+}
+
+- (unsigned)renderedParticipantsCount {
+ if ([self didTruncateParticipants])
+ return [self maxRenderedParticipantsCount];
+ return [self participantsCount];
+}
+
+- (unsigned)truncatedParticipantsCount {
+ return [self participantsCount] - [self renderedParticipantsCount];
+}
+
+- (BOOL)didTruncateParticipants {
+ return [self participantsCount] > [self maxRenderedParticipantsCount] ? YES
+ : NO;
}
- (NSString *)acceptAppointmentURL {
return [[self appointmentBaseURL] stringByAppendingPathComponent:@"accept"];
}
-- (NSString *)rejectAppointmentURL {
- return [[self appointmentBaseURL] stringByAppendingPathComponent:@"reject"];
+- (NSString *)declineAppointmentURL {
+ return [[self appointmentBaseURL] stringByAppendingPathComponent:@"decline"];
}
currentDate="selectedDate"
>
<var:if condition="hasAnyAppointments" const:negate="YES">
- <var:string label:value="No appointments found" />
+ <var:string label:value="No appointments found"
+ const:style="schedoverview"
+ />
</var:if>
<var:if condition="hasAnyAppointments">
<table border="0"
/></a>
</td>
<td class="schedoverview" colspan="2">
- <var:string value="participants"
- const:escapeHTML="YES"
- const:insertBR="YES"
- />
+ <table border="0"
+ width="100%"
+ cellpadding="0"
+ cellspacing="0"
+ class="schedoverview"
+ >
+ <var:foreach list="renderedParticipants"
+ index="participantIndex"
+ item="foo"
+ >
+ <tr>
+ <td class="schedoverview">
+ <var:string value="participant" />
+ </td>
+ <td class="schedoverview">
+ <var:component className="UIxCalParticipationStatusView"
+ partStat="participationStatus"
+ />
+ </td>
+ </tr>
+ </var:foreach>
+ <var:if condition="didTruncateParticipants">
+ <tr>
+ <td colspan="2" class="schedoverview">
+ <a var:href="appointmentViewURL"
+ class="schedoverview"
+ ><var:string value="truncatedParticipantsCount" />
+ <var:string label:value="more participants" />...
+ </a>
+ </td>
+ </tr>
+ </var:if>
+ </table>
</td>
</tr>
</var:foreach>
/></a>
</td>
<td class="schedoverview">
- <var:string value="participants"
- const:escapeHTML="YES"
- const:insertBR="YES"
- />
+ <table border="0"
+ width="100%"
+ cellpadding="0"
+ cellspacing="2"
+ class="schedoverview"
+ >
+ <var:foreach list="renderedParticipants"
+ index="participantIndex"
+ item="foo"
+ >
+ <tr>
+ <td class="schedoverview">
+ <var:string value="participant" />
+ </td>
+ <td class="schedoverview">
+ <var:component className="UIxCalParticipationStatusView"
+ partStat="participationStatus"
+ />
+ </td>
+ </tr>
+ </var:foreach>
+ <var:if condition="didTruncateParticipants">
+ <tr>
+ <td colspan="2" class="schedoverview">
+ <a var:href="appointmentViewURL"
+ class="schedoverview"
+ ><var:string value="truncatedParticipantsCount" />
+ <var:string label:value="more participants" />...
+ </a>
+ </td>
+ </tr>
+ </var:if>
+ </table>
</td>
<td class="schedoverview">
<a var:href="acceptAppointmentURL"
class="schedoverview"
><var:string label:value="accept" /></a><br />
- <a var:href="rejectAppointmentURL"
+ <a var:href="declineAppointmentURL"
class="schedoverview"
- ><var:string label:value="reject" /></a>
+ ><var:string label:value="decline" /></a>
</td>
</tr>
</var:foreach>
</var:if>
</table>
</var:if>
- <p>
- workflow<br />
- ========<br />
-
- in fact, the workflow we wish to implement is :
- person A sets a meeting with B and C
- when he sets it, he clicks on a button : either "propose" or
- "propose and mail" (obvious)
- In both case, when B and C logs into SOGo, they see, in their
- 'news page', that a new meeting has beeing proposed
- then by clicking on it, they can accept it
- on the news, you have to show : meetings proposed to the person
- logging in, meetings proposed BY the person logging in,
- and their different acceptance
-
- if you reject the meeting, it still appears in the news page
- as refused
-
- We have still two issues : ergonomic and functionnal
-
- The ergonomic one : I propose two sections in the news page,
- with each being a table, containing, each line, an apt,
- with title, day, hour, participants (truncated), the line
- being green if it has been accepted by all, red if rejected
- by someone
- grey if in another state
-
- the functionnal : a meeting that is still not accepted by
- everyone must appear in each participant's view, and be
- counted in the conflict manager, or not ?
- it's an open point
- </p>
+ <var:if condition="isUIxDebugEnabled">
+ <p class="schedoverview">
+ workflow<br />
+ ========<br />
+
+ in fact, the workflow we wish to implement is :
+ person A sets a meeting with B and C
+ when he sets it, he clicks on a button : either "propose" or
+ "propose and mail" (obvious)
+ In both case, when B and C logs into SOGo, they see, in their
+ 'news page', that a new meeting has beeing proposed
+ then by clicking on it, they can accept it
+ on the news, you have to show : meetings proposed to the person
+ logging in, meetings proposed BY the person logging in,
+ and their different acceptance
+
+ if you reject the meeting, it still appears in the news page
+ as refused
+
+ We have still two issues : ergonomic and functionnal
+
+ The ergonomic one : I propose two sections in the news page,
+ with each being a table, containing, each line, an apt,
+ with title, day, hour, participants (truncated), the line
+ being green if it has been accepted by all, red if rejected
+ by someone
+ grey if in another state
+
+ the functionnal : a meeting that is still not accepted by
+ everyone must appear in each participant's view, and be
+ counted in the conflict manager, or not ?
+ it's an open point
+ </p>
+ </var:if>
</var:component>
</td>
</tr>
#include "UIxCalView.h"
#include "common.h"
-#include "UIxAppointmentFormatter.h"
#include <OGoContentStore/OCSFolder.h>
#include "SoObjects/Appointments/SOGoAppointmentFolder.h"
#include <NGObjWeb/SoUser.h>
continue;
}
- res = [[self clientObject] fetchCoreInfosFromFolder:folder
- from:[self startDate] to:[self endDate]];
+ res = [[self clientObject] fetchOverviewInfosFromFolder:folder
+ from:[self startDate]
+ to:[self endDate]];
if (res == nil) {
[self errorWithFormat:@"fetch failed for user: %@", uid];
continue;
aptFolder = [self clientObject];
self->appointments =
- [[aptFolder fetchCoreInfosFrom:[self startDate] to:[self endDate]] retain];
+ [[aptFolder fetchOverviewInfosFrom:[self startDate]
+ to:[self endDate]] retain];
uids = [[[[self context] request] formValueForKey:@"uids"] stringValue];
uids = [uids length] > 0 ? [uids componentsSeparatedByString:@","] : nil;
# $Id$
-SUBMINOR_VERSION:=103
+SUBMINOR_VERSION:=104
# v0.9.101 requires NGiCal v4.5.36
# v0.9.100 requires SOGoUI v0.9.21
pageName = "UIxAppointmentView";
actionName = "delete";
};
- accept = {
+ edit = {
protectedBy = "View";
- pageName = "UIxAppointmentView";
- actionName = "accept";
+ pageName = "UIxAppointmentEditor";
};
- reject = {
+ save = {
protectedBy = "View";
- pageName = "UIxAppointmentView";
- actionName = "reject";
+ pageName = "UIxAppointmentEditor";
+ actionName = "save";
};
- edit = {
+ accept = {
protectedBy = "View";
pageName = "UIxAppointmentEditor";
+ actionName = "accept";
};
- save = {
+ decline = {
protectedBy = "View";
pageName = "UIxAppointmentEditor";
- actionName = "save";
+ actionName = "decline";
};
test = {
protectedBy = "View";
+2004-12-17 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.32
+
+ * SOGoAppointmentICalRenderer.m: add correct participant state
+
+ * SOGoAppointment.[hm]: find participants with specific email
+ address.
+
2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
* SOGoAppointment.m: removed UIx_Privates declaration (no longer
- (BOOL)isOrganizer:(id)_email;
- (BOOL)isParticipant:(id)_email;
-
+
+/* searching */
+
+- (iCalPerson *)findParticipantWithEmail:(id)_email;
+
@end
#endif /* __SOGoAppointment_H_ */
return [partEmails containsObject:_email];
}
+- (iCalPerson *)findParticipantWithEmail:(id)_email {
+ NSArray *ps;
+ unsigned i, count;
+
+ ps = [self participants];
+ count = [ps count];
+
+ for (i = 0; i < count; i++) {
+ iCalPerson *p;
+
+ p = [ps objectAtIndex:i];
+ if ([[p rfc822Email] isEqualToString:_email])
+ return p;
+ }
+ return nil; /* not found */
+}
+
/* description */
- (void)appendAttributesToDescription:(NSMutableString *)_ms {
[s appendString:@";"];
}
+ if ((x = [p partStat])) {
+ if ([p participationStatus] != iCalPersonPartStatNeedsAction) {
+ [s appendString:@"PARTSTAT="];
+ [s appendString:[x iCalSafeString]];
+ [s appendString:@";"];
+ }
+ }
+
[s appendString:@"CN=\""];
- if ((x = [p cn])) {
+ if ((x = [p cnWithoutQuotes])) {
[s appendString:[x iCalDQUOTESafeString]];
}
[s appendString:@"\""];
# Version file
-SUBMINOR_VERSION:=31
+SUBMINOR_VERSION:=32
+# v0.9.32 requires NGiCal v4.5.37
# v0.9.31 requires NGiCal v4.5.36
# v0.9.26 requires NGExtensions v4.5.136
# v0.9.23 requires NGiCal v4.3.32