+2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * OCSiCalFieldExtractor.m: partmails + cn's are concatenated by '\n'
+ now - this directly eliminates any ambiguities. Also, instead of
+ using 'email' for partmails and orgmail, the extractor uses the
+ 'rfc822Email' value which strips away any preceeding 'mailto:'
+ prefix, compacting the representation and speeding up comparison.
+ Also, "iscycle", "isallday" and "isopaque" are now provided by
+ NGiCal and thus always extracted (v0.9.17)
+
2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
* sql/generate-folderinfo-sql-for-user.sh: fixed critical error in
status = [[_event status] uppercaseString];
participants = [_event attendees];
- partmails = [participants valueForKey:@"email"];
- partmails = [partmails componentsJoinedByString:@", "];
+ partmails = [participants valueForKey:@"rfc822Email"];
+ partmails = [partmails componentsJoinedByString:@"\n"];
participants = [participants valueForKey:@"cn"];
- participants = [participants componentsJoinedByString:@", "];
+ participants = [participants componentsJoinedByString:@"\n"];
- // TODO: cyclic/allday (not supported by NGiCal)
-
/* build row */
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject:uid forKey:@"uid"];
else
[self logWithFormat:@"WARNING: could not extract a uid from event!"];
-
+
+ [row setObject:[NSNumber numberWithBool:[_event isAllDay]]
+ forKey:@"isallday"];
+ [row setObject:[NSNumber numberWithBool:[_event isRecurrent]]
+ forKey:@"iscycle"];
+ [row setObject:[NSNumber numberWithBool:[_event isOpaque]]
+ forKey:@"isopaque"];
+
if ([title isNotNull]) [row setObject:title forKey:@"title"];
if ([location isNotNull]) [row setObject:location forKey:@"location"];
if ([sequence isNotNull]) [row setObject:sequence forKey:@"sequence"];
[row setObject:[NSNumber numberWithBool:YES] forKey:@"ispublic"];
}
- // TODO: fix transparency when it's supported in iCalEvent
- [row setObject:[NSNumber numberWithBool:NO] forKey:@"isopaque"];
-
organizer = [_event organizer];
if (organizer) {
NSString *email;
- email = [organizer valueForKey:@"email"];
+ email = [organizer valueForKey:@"rfc822Email"];
if (email)
[row setObject:email forKey:@"orgmail"];
}
MAJOR_VERSION=0
MINOR_VERSION=9
-SUBMINOR_VERSION:=16
+SUBMINOR_VERSION:=17
+# v0.9.17 requires libNGiCal v4.5.37
# v0.9.11 requires libFoundation v1.0.63
# v0.9.11 requires libNGExtensions v4.3.125
# v0.9.7 requires libGDLAccess v1.1.35
+2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * SOGoUserHomePage.m: added defaultAction to redirect to
+ "Calendar/" (v0.9.17)
+
2004-12-08 Marcus Mueller <znek@mulle-kybernetik.com>
* SOGoProductLoader.m, sogod.m: changed to use NGLogging (v0.9.16)
return [self relativePathToUserFolderSubPath:@"Mail/"];
}
+/* actions */
+
+- (id)defaultAction {
+ return [self redirectToLocation:[self relativeCalendarPath]];
+}
+
@end /* SOGoUserHomePage */
# $Id$
-SUBMINOR_VERSION:=16
+SUBMINOR_VERSION:=17
# v0.9.16 requires NGExtensions v4.5.136
refType = 4;
sourceTree = "<group>";
};
+ AD7BA88A0770A62B008F4F3D = {
+ children = (
+ AD7BA88D0770A67D008F4F3D,
+ AD7BA88C0770A67D008F4F3D,
+ );
+ isa = PBXGroup;
+ name = News;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD7BA88C0770A67D008F4F3D = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = text.xml;
+ path = UIxCalScheduleOverview.wox;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD7BA88D0770A67D008F4F3D = {
+ fileEncoding = 5;
+ indentWidth = 2;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = UIxCalScheduleOverview.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
AD88394906EF807D00981A3E = {
fileEncoding = 5;
indentWidth = 2;
E87208FF0692E3D30099CBBD,
E87209040692E3D30099CBBD,
E87209050692E3D30099CBBD,
+ AD7BA88A0770A62B008F4F3D,
AD0137A406DF8875000910D8,
AD73BEE206CFA17700226A2D,
ADE2C28F06B7C7EA0065D56F,
to:(NSCalendarDate *)_endDate
{
EOQualifier *qualifier;
- NSArray *fields, *records;
+ NSArray *records;
NSString *sql;
if (_folder == nil) {
uri = [[_ctx request] uri];
if (![uri hasSuffix:@"/"]) uri = [uri stringByAppendingString:@"/"];
- uri = [uri stringByAppendingString:@"weekoverview"];
-
+ uri = [uri stringByAppendingString:@"schedule"];
+
r = [_ctx response];
[r setStatus:302 /* moved */];
[r setHeader:uri forKey:@"location"];
# Version file
-SUBMINOR_VERSION:=20
+SUBMINOR_VERSION:=22
# v0.9.19 requires NGiCal v4.5.36
# v0.9.13 requires libSOGo v0.9.26
2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
+ * Appointments/SOGoAppointmentFolder.m: default redirect is now
+ "schedule" instead of "weekoverview" (0.9.22)
+
+ * Appointments/SOGoAppointmentFolder.m: cleanup (v0.9.21)
+
* Appointments/SOGoAppointmentFolder.m: compile fix (v0.9.20)
2004-12-13 Marcus Mueller <znek@mulle-kybernetik.com>
+2004-12-16 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * calendar.css: added CSS for "Schedule" (v0.9.31)
+
2004-12-09 Marcus Mueller <znek@mulle-kybernetik.com>
* UIxPageFrame.[wox,m]: added -ownerInContext debug info (v0.9.30)
# Version file
-SUBMINOR_VERSION:=30
+SUBMINOR_VERSION:=31
# v0.9.28 requires NGExtensions v4.5.136
}
+/* schedule */
+
+.schedoverview {
+ font-family: Arial, Helvetica, Verdana, Geneva, Tahoma, sans-serif;
+ letter-spacing: 0pt;
+ font-size: 10pt;
+}
+
+th.schedoverview_title {
+ font-size: 10pt;
+ font-weight: bold;
+ text-align: left;
+ background-color: #e8e8e0;
+ vertical-align: top;
+}
+
+th.schedoverview {
+ font-size: 10pt;
+ font-weight: bold;
+ text-align: left;
+ vertical-align: top;
+}
+
+td.schedoverview {
+ font-size: 10pt;
+ text-align: left;
+ vertical-align: top;
+}
+
+.schedoverview a {
+ color: #000000;
+ font-family: Arial, Helvetica, Verdana, Geneva, Tahoma, sans-serif;
+ font-size: 10pt;
+ letter-spacing: 0pt;
+ padding: 0px;
+ text-decoration: none;
+}
+.schedoverview a:hover {
+ text-decoration: underline;
+}
+
+
/* day overview */
.dayoverview_content {
-2004-12-15 Marcus Mueller <znek@mulle-kybernetik.com>
+2004-12-16 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.103
+
+ * UIxCalScheduleOverview.[wox,m]: the entry point for agenor,
+ presenting the upcoming schedule to users. This is still incomplete,
+ but nevertheless functional.
+
+ * English.lproj/default.strings: new localizations for the schedule
+ view.
+
+ * product.plist: added new actions and schedule view
+
+ * UIxCalSelectTab.[wox,m]: added new "Schedule" tab
+
+ * UIxAppointmentEditor.m: default participants are selected from
+ current calendarUIDs - that is, if you've selected multiple calendars
+ and then create a new appointment, all currently viewed calendar
+ owners are added to the new appointment as default participants.
+
+ * UIxAppointmentView.m: added "accept" and "reject" actions - no
+ function yet.
* UIxAppointmentProposal.m: compile fix (v0.9.102)
/* Misc */
"OpenGroupware.org" = "OpenGroupware.org";
-
+"Forbidden" = "Forbidden";
/* Button titles */
"Cancel" = "Cancel";
+/* Schedule */
+
+"Schedule" = "Schedule";
+"No appointments found" = "You don't have any appointments in the near future.";
+"Meetings proposed by you" = "Meetings proposed by you";
+"Meetings proposed to you" = "Meetings proposed to you";
+"sched_startDateFormat" = "%d.%m. %H:%M";
+"action" = "Actions";
+"accept" = "Accept";
+"reject" = "Reject";
+
+
/* Appointments */
"Appointment viewer" = "Appointment Viewer";
UIxTimeSelector.m \
UIxTimeDateControl.m \
UIxCalInlineAptView.m \
+ UIxCalScheduleOverview.m \
SchedulerUI_RESOURCE_FILES += \
Version \
UIxTimeSelector.wox \
UIxTimeDateControl.wox \
UIxCalInlineAptView.wox \
+ UIxCalScheduleOverview.wox \
SchedulerUI_RESOURCE_FILES += \
images/next_week.gif \
Update: remember that there are group folders as well.
*/
NSString *uri, *objectId, *method;
-
+
objectId = [NSClassFromString(@"SOGoAppointmentFolder")
globallyUniqueObjectId];
if ([objectId length] == 0) {
method = [NSString stringWithFormat:@"Calendar/%@/edit", objectId];
method = [[self userFolderPath] stringByAppendingPathComponent:method];
+ /* add all current calendarUIDs as default participants */
+ if ([[self clientObject] respondsToSelector:@selector(calendarUIDs)]) {
+ AgenorUserManager *um;
+ NSArray *uids;
+ NSMutableArray *emails;
+ NSString *ps;
+ unsigned i, count;
+
+ um = [AgenorUserManager sharedUserManager];
+ uids = [[self clientObject] calendarUIDs];
+ count = [uids count];
+ emails = [NSMutableArray arrayWithCapacity:count];
+
+ for (i = 0; i < count; i++) {
+ NSString *email;
+
+ email = [um getEmailForUID:[uids objectAtIndex:i]];
+ if (email)
+ [emails addObject:email];
+ }
+ ps = [emails componentsJoinedByString:@","];
+ [self setQueryParameter:ps forKey:@"ps"];
+ }
uri = [self completeHrefForMethod:method];
return [self redirectToLocation:uri];
}
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 */
--- /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.
+ */
+
+#include <SOGoUI/UIxComponent.h>
+
+@class NSMutableArray;
+
+@interface UIxCalScheduleOverview : UIxComponent
+{
+ NSMutableArray *userApts;
+ NSMutableArray *foreignApts;
+ id item;
+}
+
+- (NSCalendarDate *)startDate;
+- (NSCalendarDate *)endDate;
+
+- (NSArray *)userAppointments;
+- (NSArray *)foreignAppointments;
+
+- (BOOL)hasUserAppointments;
+- (BOOL)hasForeignAppointments;
+- (BOOL)hasAnyAppointments;
+
+- (void)fetchInfos;
+
+- (NSString *)appointmentBaseURL;
+
+@end
+
+#include <NGObjWeb/SoComponent.h>
+#include "UIxComponent+Agenor.h"
+#include "SoObjects/Appointments/SOGoAppointmentFolder.h"
+#include "common.h"
+
+@implementation UIxCalScheduleOverview
+
+- (void)dealloc {
+ [self->userApts release];
+ [self->foreignApts release];
+ [self->item release];
+ [super dealloc];
+}
+
+
+/* accessors */
+
+- (void)setItem:(id)_item {
+ ASSIGN(self->item, _item);
+}
+- (id)item {
+ return self->item;
+}
+
+- (BOOL)hasUserAppointments {
+ return [[self userAppointments] count] > 0;
+}
+- (BOOL)hasForeignAppointments {
+ return [[self foreignAppointments] count] > 0;
+}
+- (BOOL)hasAnyAppointments {
+ return ([self hasUserAppointments] ||
+ [self hasForeignAppointments]) ? YES : NO;
+}
+
+- (NSString *)participants {
+ NSString *s;
+
+ s = [self->item valueForKey:@"participants"];
+ if (!s)
+ return @"";
+ if ([s length] > 100)
+ s = [NSString stringWithFormat:@"%@...", [s substringToIndex:100]];
+ return s;
+}
+
+
+/* fetching */
+
+- (NSCalendarDate *)startDate {
+ return [[NSCalendarDate date] beginOfDay];
+}
+
+/* ZNeK: is a month ok? */
+- (NSCalendarDate *)endDate {
+ NSCalendarDate *date;
+
+ date = [NSCalendarDate date];
+ date = [date dateByAddingYears:0 months:1 days:0
+ hours:0 minutes:0 seconds:0];
+ date = [date endOfDay];
+ return date;
+}
+
+- (NSArray *)userAppointments {
+ if (!self->userApts) {
+ [self fetchInfos];
+ }
+ return self->userApts;
+}
+
+- (NSArray *)foreignAppointments {
+ if (!self->foreignApts) {
+ [self fetchInfos];
+ }
+ return self->foreignApts;
+}
+
+- (void)fetchInfos {
+ static NSArray *orders = nil;
+ id aptFolder;
+ NSArray *apts;
+ NSString *userEmail;
+ unsigned i, count;
+
+ if (!orders) {
+ orders = [[NSArray alloc] initWithObjects:@"startDate", nil];
+ }
+
+ aptFolder = [self clientObject];
+ apts = [aptFolder fetchCoreInfosFrom:[self startDate]
+ to:[self endDate]];
+ userEmail = [self emailForUser];
+ count = [apts count];
+
+ self->userApts = [[NSMutableArray alloc] initWithCapacity:count];
+ self->foreignApts = [[NSMutableArray alloc] initWithCapacity:count];
+
+ for (i = 0; i < count; i++) {
+ id apt;
+ NSString *orgEmail;
+
+ apt = [apts objectAtIndex:i];
+ orgEmail = [apt objectForKey:@"orgmail"];
+ if (orgEmail && [orgEmail isEqualToString:userEmail])
+ [self->userApts addObject:apt];
+ else
+ [self->foreignApts addObject:apt];
+ }
+ [self->userApts sortedArrayUsingKeyOrderArray:orders];
+ [self->foreignApts sortedArrayUsingKeyOrderArray:orders];
+}
+
+
+/* URLs */
+
+- (NSString *)appointmentBaseURL {
+ id pkey;
+
+ if (![(pkey = [self->item valueForKey:@"uid"]) isNotNull])
+ return nil;
+
+ return [[self clientObject] baseURLForAptWithUID:[pkey stringValue]
+ inContext:[self context]];
+}
+- (NSString *)appointmentViewURL {
+ return [[self appointmentBaseURL] stringByAppendingPathComponent:@"view"];
+}
+- (NSString *)acceptAppointmentURL {
+ return [[self appointmentBaseURL] stringByAppendingPathComponent:@"accept"];
+}
+- (NSString *)rejectAppointmentURL {
+ return [[self appointmentBaseURL] stringByAppendingPathComponent:@"reject"];
+}
+
+
+/* access protection */
+
+- (BOOL)canAccess {
+ NSString *owner;
+
+ owner = [[self clientObject] ownerInContext:[self context]];
+ if (!owner)
+ return NO;
+ return [[[[self context] activeUser] login] isEqualToString:owner];
+}
+
+@end
--- /dev/null
+<?xml version='1.0' standalone='yes'?>
+
+<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:rsrc="OGo:url"
+ xmlns:label="OGo:label"
+ className="UIxPageFrame"
+ title="name"
+>
+ <var:if condition="canAccess" const:negate="YES">
+ <var:string label:value="Forbidden" />
+ </var:if>
+ <var:if condition="canAccess">
+ <table id="skywintable"
+ class="wintable"
+ cellspacing="0"
+ cellpadding="5"
+ width="100%"
+ >
+ <tr>
+ <td class="wintitle">
+ <table cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td width="5"/>
+ <td class="wintitle">
+ <span class="window_label">
+ <var:string label:value="Schedule"/>
+ <var:string value="startDate" label:dateformat="dayLabelFormat" /> -
+ <var:string value="endDate" label:dateformat="dayLabelFormat" />
+ </span>
+ </td>
+ <td width="36" align="right" valign="center">
+ <var:component className="UIxWinClose"/>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="skywinbodycell" class="wincontent">
+ <table border="0" width="100%" cellpadding="0" cellspacing="0">
+ <tr>
+ <td colspan="2">
+ <var:component className="UIxCalSelectTab"
+ const:selection="schedule"
+ currentDate="selectedDate"
+ >
+ <var:if condition="hasAnyAppointments" const:negate="YES">
+ <var:string label:value="No appointments found" />
+ </var:if>
+ <var:if condition="hasAnyAppointments">
+ <table border="0"
+ width="100%"
+ cellpadding="2"
+ cellspacing="0"
+ class="schedoverview"
+ >
+ <var:if condition="hasUserAppointments">
+ <tr>
+ <!-- meetings proposed by user -->
+ <th colspan="4" class="schedoverview_title">
+ <var:string label:value="Meetings proposed by you" />
+ </th>
+ </tr>
+ <tr>
+ <th class="schedoverview"
+ ><var:string label:value="Start date" /></th>
+ <th class="schedoverview"
+ ><var:string label:value="Title" /></th>
+ <th class="schedoverview"
+ ><var:string label:value="participants"/></th>
+ </tr>
+ <var:foreach list="userAppointments" item="item">
+ <tr>
+ <td class="schedoverview">
+ <var:string value="item.startDate"
+ label:dateformat="sched_startDateFormat"
+ />
+ </td>
+ <td class="schedoverview">
+ <a var:href="appointmentViewURL"
+ class="schedoverview"
+ ><var:string value="item.title"
+ const:escapeHTML="YES"
+ /></a>
+ </td>
+ <td class="schedoverview" colspan="2">
+ <var:string value="participants"
+ const:escapeHTML="YES"
+ const:insertBR="YES"
+ />
+ </td>
+ </tr>
+ </var:foreach>
+ </var:if>
+ <var:if condition="hasForeignAppointments">
+ <var:if condition="hasUserAppointments">
+ <tr>
+ <td><var:entity const:name="nbsp" /></td>
+ </tr>
+ </var:if>
+ <tr>
+ <!-- meetings proposed to user -->
+ <th colspan="4" class="schedoverview_title">
+ <var:string label:value="Meetings proposed to you" />
+ </th>
+ </tr>
+ <tr>
+ <th class="schedoverview"
+ ><var:string label:value="Start date" /></th>
+ <th class="schedoverview"
+ ><var:string label:value="Title" /></th>
+ <th class="schedoverview"
+ ><var:string label:value="participants" /></th>
+ <th class="schedoverview"
+ ><var:string label:value="action" /></th>
+ </tr>
+ <var:foreach list="foreignAppointments" item="item">
+ <tr>
+ <td class="schedoverview">
+ <var:string value="item.startDate"
+ label:dateformat="sched_startDateFormat"
+ />
+ </td>
+ <td class="schedoverview">
+ <a var:href="appointmentViewURL"
+ class="schedoverview"
+ ><var:string value="item.title"
+ const:escapeHTML="YES"
+ /></a>
+ </td>
+ <td class="schedoverview">
+ <var:string value="participants"
+ const:escapeHTML="YES"
+ const:insertBR="YES"
+ />
+ </td>
+ <td class="schedoverview">
+ <a var:href="acceptAppointmentURL"
+ class="schedoverview"
+ ><var:string label:value="accept" /></a><br />
+ <a var:href="rejectAppointmentURL"
+ class="schedoverview"
+ ><var:string label:value="reject" /></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:component>
+ </td>
+ </tr>
+ <tr bgcolor="#F5F5E9">
+ <td align="left" width="10">
+ <var:entity const:name="nbsp"/>
+ </td>
+ <td align="right">
+ <img border="0"
+ alt=""
+ src="/sogod.woa/so/ControlPanel/Products/CommonUI/Resources/corner_right.gif"
+ />
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2" bgcolor="#F5F5E9">
+ <table border="0" width="100%" cellpadding="10" cellspacing="0">
+ <tr/>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </var:if>
+</var:component>
\ No newline at end of file
/* hrefs */
+- (NSString *)scheduletabLink {
+ return [self completeHrefForMethod:@"schedule"];
+}
- (NSString *)daytabLink {
return [self completeHrefForMethod:@"dayoverview"];
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
+ xmlns:label="OGo:label"
xmlns:uix="OGo:uix"
var:selection="selection"
const:tabStyle="tab"
const:selectedTabStyle="tab_selected"
const:bodyStyle="tabview_body"
>
+ <uix:tab const:key="schedule"
+ label:label="Schedule"
+ var:href="scheduletabLink"
+ >
+ <var:component-content />
+ </uix:tab>
<uix:tab const:key="day" var:label="dayLabel" var:href="daytabLink">
<var:component-content />
</uix:tab>
protectedBy = "View";
pageName = "UIxCalYearOverview";
};
+ schedule = {
+ protectedBy = "View";
+ pageName = "UIxCalScheduleOverview";
+ };
new = {
protectedBy = "View";
pageName = "UIxAppointmentEditor";
pageName = "UIxAppointmentView";
actionName = "delete";
};
+ accept = {
+ protectedBy = "View";
+ pageName = "UIxAppointmentView";
+ actionName = "accept";
+ };
+ reject = {
+ protectedBy = "View";
+ pageName = "UIxAppointmentView";
+ actionName = "reject";
+ };
edit = {
protectedBy = "View";
pageName = "UIxAppointmentEditor";