New events list view
parent
614a137128
commit
77f9f60276
1
NEWS
1
NEWS
|
@ -5,6 +5,7 @@ New features
|
|||
- [core] can now invite attendees to exceptions only (#2561)
|
||||
- [web] display freebusy information of owner in appointment editor
|
||||
- [web] register SOGo as a handler for the mailto scheme (#1223)
|
||||
- [web] new events list view where events are grouped by day
|
||||
|
||||
Enhancements
|
||||
- [web] follow requested URL after user authentication
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
@class WOResponse, WOResourceManager;
|
||||
|
||||
@interface SOGoDirectAction : WODirectAction
|
||||
{
|
||||
NSDictionary *locale;
|
||||
}
|
||||
|
||||
- (WOResponse *) responseWithStatus: (unsigned int) status;
|
||||
- (WOResponse *) responseWithStatus: (unsigned int) status
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
|
||||
#import <Foundation/NSKeyValueCoding.h>
|
||||
#import <Foundation/NSUserDefaults.h> /* for locale strings */
|
||||
|
||||
#import <NGObjWeb/SoObjects.h>
|
||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||
|
@ -30,7 +31,9 @@
|
|||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoSession.h>
|
||||
#import <SoObjects/SOGo/SOGoSystemDefaults.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
#import <SoObjects/SOGo/SOGoWebAuthenticator.h>
|
||||
#import <SoObjects/SOGo/WOResourceManager+SOGo.h>
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
|
@ -50,6 +53,31 @@ static SoProduct *commonProduct = nil;
|
|||
}
|
||||
}
|
||||
|
||||
- (id) initWithContext: (WOContext *)_context;
|
||||
{
|
||||
NSString *language;
|
||||
SOGoUserDefaults *userDefaults;
|
||||
WOResourceManager *resMgr;
|
||||
|
||||
if ((self = [super initWithContext: _context]))
|
||||
{
|
||||
userDefaults = [[_context activeUser] userDefaults];
|
||||
if (!userDefaults)
|
||||
userDefaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
language = [userDefaults language];
|
||||
resMgr = [[WOApplication application] resourceManager];
|
||||
ASSIGN (locale, [resMgr localeForLanguageNamed: language]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[locale release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (WOResponse *) responseWithStatus: (unsigned int) status
|
||||
{
|
||||
WOResponse *response;
|
||||
|
|
|
@ -104,7 +104,7 @@ static NSArray *tasksFields = nil;
|
|||
- (id) initWithRequest: (WORequest *) newRequest
|
||||
{
|
||||
SOGoUser *user;
|
||||
|
||||
|
||||
if ((self = [super initWithRequest: newRequest]))
|
||||
{
|
||||
componentsData = [NSMutableDictionary new];
|
||||
|
@ -120,7 +120,7 @@ static NSArray *tasksFields = nil;
|
|||
currentView = nil;
|
||||
ASSIGN (enabledWeekDays, [[user userDefaults] calendarWeekdays]);
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ static NSArray *tasksFields = nil;
|
|||
{
|
||||
NSCalendarDate *newDate;
|
||||
NSString *param;
|
||||
|
||||
|
||||
if ([popupValue isEqualToString: @"view_today"])
|
||||
{
|
||||
newDate = [NSCalendarDate calendarDate];
|
||||
|
@ -204,10 +204,10 @@ static NSArray *tasksFields = nil;
|
|||
{
|
||||
SOGoUser *user;
|
||||
NSString *param;
|
||||
|
||||
|
||||
user = [context activeUser];
|
||||
userLogin = [user login];
|
||||
|
||||
|
||||
criteria = [request formValueForKey: @"search"];
|
||||
value = [request formValueForKey: @"value"];
|
||||
param = [request formValueForKey: @"filterpopup"];
|
||||
|
@ -224,7 +224,7 @@ static NSArray *tasksFields = nil;
|
|||
inTimeZone: userTimeZone] beginOfDay];
|
||||
else
|
||||
startDate = nil;
|
||||
|
||||
|
||||
param = [request formValueForKey: @"ed"];
|
||||
if ([param length] > 0)
|
||||
endDate = [[NSCalendarDate dateFromShortDateString: param
|
||||
|
@ -232,7 +232,7 @@ static NSArray *tasksFields = nil;
|
|||
inTimeZone: userTimeZone] endOfDay];
|
||||
else
|
||||
endDate = nil;
|
||||
|
||||
|
||||
param = [request formValueForKey: @"view"];
|
||||
currentView = param;
|
||||
dayBasedView = ![param isEqualToString: @"monthview"];
|
||||
|
@ -243,7 +243,7 @@ static NSArray *tasksFields = nil;
|
|||
withType: (NSString *) type
|
||||
{
|
||||
NSString *labelKey;
|
||||
|
||||
|
||||
labelKey = [NSString stringWithFormat: @"%@_class%@",
|
||||
type, [component objectForKey: @"c_classification"]];
|
||||
[component setObject: [self labelForKey: labelKey inContext: context]
|
||||
|
@ -264,23 +264,23 @@ static NSArray *tasksFields = nil;
|
|||
unsigned int count;
|
||||
|
||||
static NSString *fields[] = { @"startDate", @"c_startdate", @"endDate", @"c_enddate" };
|
||||
|
||||
|
||||
/* WARNING: This condition has been put and removed many times, please leave
|
||||
it. Here is the story...
|
||||
If _fixDates: is conditional to dayBasedView, the recurrences are computed
|
||||
properly but the display time is wrong.
|
||||
If _fixDates: is non-conditional, the reverse occurs.
|
||||
If only this part of _fixDates: is conditional, both are right.
|
||||
|
||||
|
||||
Regarding all day events, we need to execute this code no matter what the
|
||||
date and the view are, otherwise the event will span on two days.
|
||||
|
||||
|
||||
ref bugs:
|
||||
http://www.sogo.nu/bugs/view.php?id=909
|
||||
http://www.sogo.nu/bugs/view.php?id=678
|
||||
...
|
||||
*/
|
||||
|
||||
|
||||
//NSLog(@"***[UIxCalListingActions _fixDates:] %@", [theRecord objectForKey: @"c_title"]);
|
||||
if (dayBasedView || [[theRecord objectForKey: @"c_isallday"] boolValue])
|
||||
{
|
||||
|
@ -362,10 +362,10 @@ static NSArray *tasksFields = nil;
|
|||
SOGoAppointmentFolder *currentFolder;
|
||||
SOGoAppointmentFolders *clientObject;
|
||||
SOGoUser *ownerUser;
|
||||
|
||||
|
||||
BOOL isErasable, folderIsRemote, quickInfosFlag, searchByTitleOrContent;
|
||||
int i;
|
||||
|
||||
|
||||
infos = [NSMutableArray array];
|
||||
marker = [NSNull null];
|
||||
clientObject = [self clientObject];
|
||||
|
@ -377,7 +377,7 @@ static NSArray *tasksFields = nil;
|
|||
if ([currentFolder isActive])
|
||||
{
|
||||
folderIsRemote = [currentFolder isKindOfClass: [SOGoWebAppointmentFolder class]];
|
||||
|
||||
|
||||
//
|
||||
// criteria can have the following values: "title", "title_Category_Location" and "entireContent"
|
||||
//
|
||||
|
@ -400,7 +400,7 @@ static NSArray *tasksFields = nil;
|
|||
additionalFilters: criteria];
|
||||
|
||||
searchByTitleOrContent = ([value length] > 0);
|
||||
|
||||
|
||||
// Save the c_name in another array to compare with
|
||||
if ([quickInfos count] > 0)
|
||||
{
|
||||
|
@ -409,7 +409,7 @@ static NSArray *tasksFields = nil;
|
|||
for (i = 0; i < [quickInfos count]; i++)
|
||||
[quickInfosName addObject:[[quickInfos objectAtIndex:i] objectForKey:@"c_name"]];
|
||||
}
|
||||
|
||||
|
||||
// Second research : Every objects except for those already in the quickInfos array
|
||||
allInfos = (NSMutableArray *)[currentFolder fetchCoreInfosFrom: startDate
|
||||
to: endDate
|
||||
|
@ -438,7 +438,7 @@ static NSArray *tasksFields = nil;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
currentInfos = [quickInfos objectEnumerator];
|
||||
} // else if ([criteria isEqualToString:@"entireContent"])
|
||||
else
|
||||
|
@ -449,7 +449,7 @@ static NSArray *tasksFields = nil;
|
|||
component: component]
|
||||
objectEnumerator];
|
||||
}
|
||||
|
||||
|
||||
owner = [currentFolder ownerInContext: context];
|
||||
ownerUser = [SOGoUser userWithLogin: owner];
|
||||
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
|
||||
|
@ -511,7 +511,7 @@ static NSArray *tasksFields = nil;
|
|||
// Identifies whether the owner is the organizer of this event.
|
||||
NSString *c_orgmail;
|
||||
c_orgmail = [newInfo objectForKey: @"c_orgmail"];
|
||||
|
||||
|
||||
if ([c_orgmail isKindOfClass: [NSString class]] && [ownerUser hasEmail: c_orgmail])
|
||||
[newInfo setObject: [NSNumber numberWithInt: 1]
|
||||
forKey: @"ownerIsOrganizer"];
|
||||
|
@ -603,9 +603,6 @@ static NSArray *tasksFields = nil;
|
|||
}
|
||||
else if (abs(delta = [date dayOfCommonEra] - [now dayOfCommonEra]) < 7)
|
||||
{
|
||||
WOResourceManager *resMgr = [[WOApplication application] resourceManager];
|
||||
NSString *language = [[[context activeUser] userDefaults] language];
|
||||
NSDictionary *locale = [resMgr localeForLanguageNamed: language];
|
||||
NSString *dayOfWeek = [[locale objectForKey: NSWeekDayNameArray] objectAtIndex: [date dayOfWeek]];
|
||||
if (delta < 7)
|
||||
// Wihtin the next 7 days
|
||||
|
@ -658,7 +655,7 @@ static NSArray *tasksFields = nil;
|
|||
laterTime = browserTime + 60*60*48;
|
||||
clientObject = [self clientObject];
|
||||
allAlarms = [NSMutableArray array];
|
||||
|
||||
|
||||
folders = [[clientObject subFolders] objectEnumerator];
|
||||
while ((currentFolder = [folders nextObject]))
|
||||
{
|
||||
|
@ -667,14 +664,14 @@ static NSArray *tasksFields = nil;
|
|||
NSDictionary *entry;
|
||||
NSArray *alarms;
|
||||
int i;
|
||||
|
||||
|
||||
alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime]
|
||||
to: [NSNumber numberWithInt: laterTime]];
|
||||
|
||||
|
||||
for (i = 0; i < [alarms count]; i++)
|
||||
{
|
||||
entry = [alarms objectAtIndex: i];
|
||||
|
||||
|
||||
[allAlarms addObject: [NSArray arrayWithObjects:
|
||||
[currentFolder nameInContainer],
|
||||
[entry objectForKey: @"c_name"],
|
||||
|
@ -687,7 +684,7 @@ static NSArray *tasksFields = nil;
|
|||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSArray arrayWithObjects: @"c_folder", @"c_name", @"c_nextalarm", nil], @"fields",
|
||||
allAlarms, @"alarms", nil];
|
||||
|
||||
|
||||
return [self _responseWithData: data];
|
||||
}
|
||||
|
||||
|
@ -696,7 +693,7 @@ static NSArray *tasksFields = nil;
|
|||
NSString *filter;
|
||||
SOGoUserSettings *us;
|
||||
NSMutableDictionary *calendarSettings;
|
||||
|
||||
|
||||
filter = [[context request] formValueForKey: @"filterpopup"];
|
||||
if ([filter length]
|
||||
&& ![filter isEqualToString: @"view_all"]
|
||||
|
@ -721,7 +718,7 @@ static NSArray *tasksFields = nil;
|
|||
NSString *sort, *ascending;
|
||||
SOGoUserSettings *us;
|
||||
NSMutableDictionary *calendarSettings;
|
||||
|
||||
|
||||
sort = [[context request] formValueForKey: @"sort"];
|
||||
ascending = [[context request] formValueForKey: @"asc"];
|
||||
if ([sort length])
|
||||
|
@ -740,6 +737,38 @@ static NSArray *tasksFields = nil;
|
|||
}
|
||||
}
|
||||
|
||||
static inline NSString* _userStateInEvent (NSArray *event)
|
||||
{
|
||||
unsigned int count, max;
|
||||
NSString *state;
|
||||
NSArray *participants, *states;
|
||||
SOGoUser *user;
|
||||
|
||||
participants = nil;
|
||||
state = nil;
|
||||
|
||||
participants = [event objectAtIndex: eventPartMailsIndex];
|
||||
|
||||
// We guard ourself against bogus value coming from the quick tables
|
||||
if ([participants isKindOfClass: [NSArray class]])
|
||||
{
|
||||
states = [event objectAtIndex: eventPartStatesIndex];
|
||||
count = 0;
|
||||
max = [participants count];
|
||||
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
|
||||
roles: nil];
|
||||
while (state == nil && count < max)
|
||||
{
|
||||
if ([user hasEmail: [participants objectAtIndex: count]])
|
||||
state = [states objectAtIndex: count];
|
||||
else
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /so/:username/Calendar/eventslist List events
|
||||
* @apiVersion 1.0.0
|
||||
|
@ -755,34 +784,39 @@ static NSArray *tasksFields = nil;
|
|||
* @apiParam {String} [search] Search field criteria. Either title_Category_Location or entireContent.
|
||||
* @apiParam {String} [value] String to match
|
||||
*
|
||||
* @apiSuccess (Success 200) {String[]} fields List of fields for each event definition
|
||||
* @apiSuccess (Success 200) {String[]} events List of events
|
||||
* @apiSuccess (Success 200) {String} events.c_name Event UID
|
||||
* @apiSuccess (Success 200) {String} events.c_folder Calendar ID
|
||||
* @apiSuccess (Success 200) {String} events.calendarName Human readable name of calendar
|
||||
* @apiSuccess (Success 200) {Number} events.c_status 0: Cancelled, 1: Normal, 2: Tentative
|
||||
* @apiSuccess (Success 200) {Number} events.c_isopaque 1 if event is opaque (not transparent)
|
||||
* @apiSuccess (Success 200) {String} events.c_title Title
|
||||
* @apiSuccess (Success 200) {String} events.c_startdate Epoch time of start date
|
||||
* @apiSuccess (Success 200) {String} events.c_enddate Epoch time of end date
|
||||
* @apiSuccess (Success 200) {String} events.c_location Event's location
|
||||
* @apiSuccess (Success 200) {Number} events.c_isallday 1 if event lasts all day
|
||||
* @apiSuccess (Success 200) {Number} events.c_classification 0: Public, 1: Private, 2: Confidential
|
||||
* @apiSuccess (Success 200) {String} events.c_category Category
|
||||
* @apiSuccess (Success 200) {Number} events.c_priority Priority (0 to 9)
|
||||
* @apiSuccess (Success 200) {String[]} events.c_partmails Participants email addresses
|
||||
* @apiSuccess (Success 200) {String[]} events.c_partstates Participants states
|
||||
* @apiSuccess (Success 200) {String} events.c_owner Event's owner
|
||||
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic/recurring
|
||||
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
|
||||
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
|
||||
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
|
||||
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
|
||||
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
|
||||
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
|
||||
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
|
||||
* @apiSuccess (Success 200) {String} events.formatted_startdate Localized start date
|
||||
* @apiSuccess (Success 200) {String} events.formatted_enddate Localized end date
|
||||
* @apiSuccess (Success 200) {String[]} fields List of fields for each event definition
|
||||
* @apiSuccess (Success 200) {String} events.<month>.month Full month and year
|
||||
* @apiSuccess (Success 200) {Object} events.<month>.days List of days
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.month Abbreviated month name
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.monthDay Day of month
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.weekDay Abbreviated weeday name
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.events List of events
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_name Event UID
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_folder Calendar ID
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.calendarName Human readable name of calendar
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_status 0: Cancelled, 1: Normal, 2: Tentative
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_isopaque 1 if event is opaque (not transparent)
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_title Title
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_startdate Epoch time of start date
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_enddate Epoch time of end date
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_location Event's location
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_isallday 1 if event lasts all day
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_classification 0: Public, 1: Private, 2: Confidential
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_category Category
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_priority Priority (0 to 9)
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.events.c_partmails Participants email addresses
|
||||
* @apiSuccess (Success 200) {String[]} events.<month>.days.<date>.events.c_partstates Participants states
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.c_owner Event's owner
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_iscycle 1 if the event is cyclic/recurring
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.c_nextalarm Epoch time of next alarm
|
||||
* @apiSuccess (Success 200) {String} [events.<month>.days.<date>.events.c_recurrence_id] Recurrence ID if event is cyclic
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.isException 1 if recurrence is an exception
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.viewable 1 if active user can view the event
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.editable 1 if active user can edit the event
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.erasable 1 if active user can erase the event
|
||||
* @apiSuccess (Success 200) {Number} events.<month>.days.<date>.events.ownerIsOrganizer 1 if owner is also the organizer
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.formatted_startdate Localized start date
|
||||
* @apiSuccess (Success 200) {String} events.<month>.days.<date>.events.formatted_enddate Localized end date
|
||||
*/
|
||||
- (WOResponse *) eventsListAction
|
||||
{
|
||||
|
@ -791,15 +825,18 @@ static NSArray *tasksFields = nil;
|
|||
NSCalendarDate *date;
|
||||
NSDictionary *data;
|
||||
NSEnumerator *events;
|
||||
NSMutableArray *fields, *newEvents, *newEvent;
|
||||
NSString *sort, *ascending, *weekDay;
|
||||
unsigned int interval;
|
||||
NSMutableArray *fields, *dayEvents, *newEvent, *allDayEvents;
|
||||
NSMutableDictionary *allEvents, *monthData, *monthEvents, *dayData;
|
||||
NSString *sort, *ascending, *day, *weekDay, *month, *userState;
|
||||
unsigned int interval, count, max;
|
||||
SEL sortSelector;
|
||||
|
||||
[self _setupContext];
|
||||
[self saveFilterValue: @"EventsFilterState"];
|
||||
[self saveSortValue: @"EventsSortingState"];
|
||||
|
||||
newEvents = [NSMutableArray array];
|
||||
allEvents = [NSMutableDictionary dictionary];
|
||||
allDayEvents = [NSMutableArray array];
|
||||
events = [[self _fetchFields: eventsFields
|
||||
forComponentOfType: @"vevent"] objectEnumerator];
|
||||
while ((oldEvent = [events nextObject]))
|
||||
|
@ -813,6 +850,33 @@ static NSArray *tasksFields = nil;
|
|||
if ([enabledWeekDays count] && ![enabledWeekDays containsObject: weekDay])
|
||||
continue;
|
||||
|
||||
month = [date descriptionWithCalendarFormat: @"%Y%m" locale: locale];
|
||||
day = [date shortDateString];
|
||||
if (!(monthData = [allEvents objectForKey: month]))
|
||||
{
|
||||
monthEvents = [NSMutableDictionary dictionary];
|
||||
monthData = [NSMutableDictionary
|
||||
dictionaryWithObjectsAndKeys:
|
||||
[date descriptionWithCalendarFormat: @"%B %Y" locale: locale], @"month",
|
||||
monthEvents, @"days",
|
||||
nil];
|
||||
[allEvents setObject: monthData forKey: month];
|
||||
}
|
||||
monthEvents = [monthData objectForKey: @"days"];
|
||||
if (!(dayData = [monthEvents objectForKey: day]))
|
||||
{
|
||||
dayEvents = [NSMutableArray array];
|
||||
[allDayEvents addObject: dayEvents];
|
||||
dayData = [NSMutableDictionary
|
||||
dictionaryWithObjectsAndKeys:
|
||||
[date descriptionWithCalendarFormat: @"%b" locale: locale], @"month",
|
||||
[date descriptionWithCalendarFormat: @"%e" locale: locale], @"monthDay",
|
||||
[date descriptionWithCalendarFormat: @"%a" locale: locale], @"weekDay",
|
||||
dayEvents, @"events",
|
||||
nil];
|
||||
[monthEvents setObject: dayData forKey: day];
|
||||
}
|
||||
dayEvents = [dayData objectForKey: @"events"];
|
||||
newEvent = [NSMutableArray arrayWithArray: oldEvent];
|
||||
isAllDay = [[oldEvent objectAtIndex: eventIsAllDayIndex] boolValue];
|
||||
[newEvent addObject: [self _formattedDateForSeconds: interval
|
||||
|
@ -820,31 +884,40 @@ static NSArray *tasksFields = nil;
|
|||
interval = [[oldEvent objectAtIndex: eventEndDateIndex] intValue];
|
||||
[newEvent addObject: [self _formattedDateForSeconds: interval
|
||||
forAllDay: isAllDay]];
|
||||
[newEvents addObject: newEvent];
|
||||
userState = _userStateInEvent(oldEvent);
|
||||
if (userState != nil) [newEvent addObject: userState];
|
||||
[dayEvents addObject: newEvent];
|
||||
}
|
||||
|
||||
|
||||
// Sort affects events of each day (but not the days)
|
||||
sort = [[context request] formValueForKey: @"sort"];
|
||||
if ([sort isEqualToString: @"title"])
|
||||
[newEvents sortUsingSelector: @selector (compareEventsTitleAscending:)];
|
||||
sortSelector = @selector (compareEventsTitleAscending:);
|
||||
else if ([sort isEqualToString: @"end"])
|
||||
[newEvents sortUsingSelector: @selector (compareEventsEndDateAscending:)];
|
||||
sortSelector = @selector (compareEventsEndDateAscending:);
|
||||
else if ([sort isEqualToString: @"location"])
|
||||
[newEvents sortUsingSelector: @selector (compareEventsLocationAscending:)];
|
||||
sortSelector = @selector (compareEventsLocationAscending:);
|
||||
else if ([sort isEqualToString: @"calendarName"])
|
||||
[newEvents sortUsingSelector: @selector (compareEventsCalendarNameAscending:)];
|
||||
sortSelector = @selector (compareEventsCalendarNameAscending:);
|
||||
else
|
||||
[newEvents sortUsingSelector: @selector (compareEventsStartDateAscending:)];
|
||||
|
||||
sortSelector = @selector (compareEventsStartDateAscending:);
|
||||
ascending = [[context request] formValueForKey: @"asc"];
|
||||
if (![ascending boolValue])
|
||||
[newEvents reverseArray];
|
||||
|
||||
max = [allDayEvents count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
dayEvents = [allDayEvents objectAtIndex: count];
|
||||
[dayEvents sortUsingSelector: sortSelector];
|
||||
if (![ascending boolValue])
|
||||
[dayEvents reverseArray];
|
||||
}
|
||||
|
||||
// Fields names
|
||||
fields = [NSMutableArray arrayWithArray: eventsFields];
|
||||
[fields addObject: @"formatted_startdate"];
|
||||
[fields addObject: @"formatted_enddate"];
|
||||
[fields addObject: @"userState"];
|
||||
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys: fields, @"fields", newEvents, @"events", nil];
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys: fields, @"fields", allEvents, @"events", nil];
|
||||
|
||||
return [self _responseWithData: data];
|
||||
}
|
||||
|
@ -853,7 +926,7 @@ static inline void _feedBlockWithDayBasedData (NSMutableDictionary *block, unsig
|
|||
unsigned int end, unsigned int dayStart)
|
||||
{
|
||||
unsigned int delta, quarterStart, length, swap;
|
||||
|
||||
|
||||
if (start > end)
|
||||
{
|
||||
swap = end;
|
||||
|
@ -879,7 +952,7 @@ static inline void _feedBlockWithDayBasedData (NSMutableDictionary *block, unsig
|
|||
// {
|
||||
// NSCalendarDate *eventStartDate;
|
||||
// NSString *startHour;
|
||||
|
||||
|
||||
// eventStartDate = [NSCalendarDate dateWithTimeIntervalSince1970: start];
|
||||
// [eventStartDate setTimeZone: userTimeZone];
|
||||
// startHour = [dateFormatter formattedTime: eventStartDate];
|
||||
|
@ -909,9 +982,9 @@ static inline void _feedBlockWithDayBasedData (NSMutableDictionary *block, unsig
|
|||
userState: (NSString *) userState
|
||||
{
|
||||
NSMutableDictionary *block;
|
||||
|
||||
|
||||
block = [NSMutableDictionary dictionary];
|
||||
|
||||
|
||||
if (dayBasedView)
|
||||
_feedBlockWithDayBasedData (block, start, end, dayStart);
|
||||
// else
|
||||
|
@ -923,42 +996,10 @@ static inline void _feedBlockWithDayBasedData (NSMutableDictionary *block, unsig
|
|||
if (userState != nil)
|
||||
[block setObject: userState
|
||||
forKey: @"userState"];
|
||||
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static inline NSString* _userStateInEvent (NSArray *event)
|
||||
{
|
||||
unsigned int count, max;
|
||||
NSString *state;
|
||||
NSArray *participants, *states;
|
||||
SOGoUser *user;
|
||||
|
||||
participants = nil;
|
||||
state = nil;
|
||||
|
||||
participants = [event objectAtIndex: eventPartMailsIndex];
|
||||
|
||||
// We guard ourself against bogus value coming from the quick tables
|
||||
if ([participants isKindOfClass: [NSArray class]])
|
||||
{
|
||||
states = [event objectAtIndex: eventPartStatesIndex];
|
||||
count = 0;
|
||||
max = [participants count];
|
||||
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
|
||||
roles: nil];
|
||||
while (state == nil && count < max)
|
||||
{
|
||||
if ([user hasEmail: [participants objectAtIndex: count]])
|
||||
state = [states objectAtIndex: count];
|
||||
else
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
- (void) _fillBlocks: (NSArray *) blocks
|
||||
withEvent: (NSArray *) event
|
||||
withNumber: (NSNumber *) number
|
||||
|
@ -968,7 +1009,7 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
NSMutableArray *currentDay;
|
||||
NSMutableDictionary *eventBlock;
|
||||
NSString *userState;
|
||||
|
||||
|
||||
eventStart = [[event objectAtIndex: eventStartDateIndex] intValue];
|
||||
if (eventStart < 0)
|
||||
[self errorWithFormat: @"event '%@' has negative start: %d (skipped)",
|
||||
|
@ -989,15 +1030,15 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
[self warnWithFormat: @"event '%@' has end < start: %d < %d",
|
||||
[event objectAtIndex: eventNameIndex], eventEnd, eventStart];
|
||||
}
|
||||
|
||||
|
||||
startSecs = (unsigned int) [startDate timeIntervalSince1970];
|
||||
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
|
||||
|
||||
|
||||
if ([[event objectAtIndex: eventIsCycleIndex] boolValue])
|
||||
recurrenceTime = [[event objectAtIndex: eventRecurrenceIdIndex] unsignedIntValue];
|
||||
else
|
||||
recurrenceTime = 0;
|
||||
|
||||
|
||||
currentStart = eventStart;
|
||||
if (currentStart < startSecs)
|
||||
{
|
||||
|
@ -1014,15 +1055,15 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
{
|
||||
currentDay = [blocks objectAtIndex: offset];
|
||||
currentDayStart = startSecs + dayLength * offset;
|
||||
|
||||
|
||||
if (eventEnd > endsSecs)
|
||||
eventEnd = endsSecs;
|
||||
|
||||
|
||||
if (eventEnd < startSecs)
|
||||
// The event doesn't end in the covered period.
|
||||
// This special case occurs with a DST change.
|
||||
return;
|
||||
|
||||
|
||||
userState = _userStateInEvent(event);
|
||||
while (currentDayStart + dayLength < eventEnd)
|
||||
{
|
||||
|
@ -1038,14 +1079,14 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
offset++;
|
||||
currentDay = [blocks objectAtIndex: offset];
|
||||
}
|
||||
|
||||
|
||||
computedEventEnd = eventEnd;
|
||||
|
||||
|
||||
// We add 5 mins to the end date of an event if the end date
|
||||
// is equal or smaller than the event's start date.
|
||||
if (eventEnd <= currentStart)
|
||||
computedEventEnd = currentStart + (5*60);
|
||||
|
||||
|
||||
eventBlock = [self _eventBlockWithStart: currentStart
|
||||
end: computedEventEnd
|
||||
number: number
|
||||
|
@ -1063,7 +1104,7 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
{
|
||||
unsigned int count, nbrDays;
|
||||
int seconds;
|
||||
|
||||
|
||||
seconds = [endDate timeIntervalSinceDate: startDate];
|
||||
if (seconds > 0)
|
||||
{
|
||||
|
@ -1089,11 +1130,11 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
NSMutableArray *currentBlock, *blocks;
|
||||
NSDictionary *currentEvent;
|
||||
unsigned int count, max, qCount, qMax, qOffset;
|
||||
|
||||
|
||||
blocks = [NSMutableArray array];
|
||||
|
||||
|
||||
bzero (quarters, 96 * sizeof (NSMutableArray *));
|
||||
|
||||
|
||||
max = [day count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
|
@ -1112,7 +1153,7 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
|||
[currentBlock addObject: currentEvent];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
|
@ -1120,7 +1161,7 @@ static inline unsigned int _computeMaxBlockSiblings (NSArray *block)
|
|||
{
|
||||
unsigned int count, max, maxSiblings, siblings;
|
||||
NSNumber *nbrEvents;
|
||||
|
||||
|
||||
max = [block count];
|
||||
maxSiblings = max;
|
||||
for (count = 0; count < max; count++)
|
||||
|
@ -1133,7 +1174,7 @@ static inline unsigned int _computeMaxBlockSiblings (NSArray *block)
|
|||
maxSiblings = siblings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return maxSiblings;
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1183,7 @@ static inline void _propagateBlockSiblings (NSArray *block, NSNumber *maxSibling
|
|||
unsigned int count, max;
|
||||
NSMutableDictionary *event;
|
||||
NSNumber *realSiblings;
|
||||
|
||||
|
||||
max = [block count];
|
||||
realSiblings = [NSNumber numberWithUnsignedInt: max];
|
||||
for (count = 0; count < max; count++)
|
||||
|
@ -1158,7 +1199,7 @@ static inline void _computeBlocksSiblings (NSArray *blocks)
|
|||
{
|
||||
NSArray *currentBlock;
|
||||
unsigned int count, max, maxSiblings;
|
||||
|
||||
|
||||
max = [blocks count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
|
@ -1175,12 +1216,12 @@ static inline void _computeBlockPosition (NSArray *block)
|
|||
NSNumber *position;
|
||||
NSMutableDictionary *event;
|
||||
NSMutableDictionary **positions;
|
||||
|
||||
|
||||
max = [block count];
|
||||
event = [block objectAtIndex: 0];
|
||||
siblings = [[event objectForKey: @"siblings"] unsignedIntValue];
|
||||
positions = NSZoneCalloc (NULL, siblings, sizeof (NSMutableDictionary *));
|
||||
|
||||
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
event = [block objectAtIndex: count];
|
||||
|
@ -1197,7 +1238,7 @@ static inline void _computeBlockPosition (NSArray *block)
|
|||
forKey: @"position"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSZoneFree (NULL, positions);
|
||||
}
|
||||
|
||||
|
@ -1242,7 +1283,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
NSArray *block;
|
||||
unsigned int count, max;
|
||||
// NSMutableDictionary **positions;
|
||||
|
||||
|
||||
max = [blocks count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
|
@ -1256,7 +1297,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
- (void) _addBlocksWidth: (NSMutableArray *) day
|
||||
{
|
||||
NSArray *blocks;
|
||||
|
||||
|
||||
blocks = [self _horizontalBlocks: day];
|
||||
_computeBlocksSiblings (blocks);
|
||||
_computeBlocksSiblings (blocks);
|
||||
|
@ -1273,7 +1314,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
NSString *fUID, *fName;
|
||||
NSNumber *isActive;
|
||||
unsigned int count, foldersCount;
|
||||
|
||||
|
||||
co = [self clientObject];
|
||||
folders = [co subFolders];
|
||||
foldersCount = [folders count];
|
||||
|
@ -1306,6 +1347,9 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
* @apiParam {Number} [ed] Period end date (YYYYMMDD)
|
||||
* @apiParam {String} [view] Formatting view. Either dayview, multicolumndayview, weekview or monthview.
|
||||
*
|
||||
* @apiSuccess (Success 200) {Object[]} days
|
||||
* @apiSuccess (Success 200) {String} days.date
|
||||
* @apiSuccess (Success 200) {Number} days.number
|
||||
* @apiSuccess (Success 200) {String[]} eventsFields List of fields for each event definition
|
||||
* @apiSuccess (Success 200) {String[]} events List of events
|
||||
* @apiSuccess (Success 200) {String} events.c_name Event UID
|
||||
|
@ -1357,7 +1401,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
[self _setupContext];
|
||||
|
||||
events = [self _fetchFields: eventsFields forComponentOfType: @"vevent"];
|
||||
|
||||
|
||||
if ([currentView isEqualToString: @"multicolumndayview"])
|
||||
{
|
||||
calendars = (NSMutableArray *)[self _selectedCalendars];
|
||||
|
@ -1487,7 +1531,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
{
|
||||
NSCalendarDate *taskDate;
|
||||
NSString *statusClass;
|
||||
|
||||
|
||||
if (statusCode == 1)
|
||||
statusClass = @"completed";
|
||||
else
|
||||
|
@ -1509,7 +1553,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
else
|
||||
statusClass = @"noduedate";
|
||||
}
|
||||
|
||||
|
||||
return statusClass;
|
||||
}
|
||||
|
||||
|
@ -1558,29 +1602,29 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
SOGoUserSettings *us;
|
||||
NSEnumerator *tasks;
|
||||
NSArray *task;
|
||||
|
||||
|
||||
unsigned int endDateStamp;
|
||||
BOOL showCompleted;
|
||||
int statusCode;
|
||||
int startSecs;
|
||||
|
||||
filteredTasks = [NSMutableArray array];
|
||||
|
||||
|
||||
[self _setupContext];
|
||||
[self saveFilterValue: @"TasksFilterState"];
|
||||
[self saveSortValue: @"TasksSortingState"];
|
||||
|
||||
|
||||
startSecs = (unsigned int) [startDate timeIntervalSince1970];
|
||||
tasksView = [request formValueForKey: @"filterpopup"];
|
||||
|
||||
|
||||
showCompleted = [[request formValueForKey: @"show_completed"] intValue];
|
||||
us = [[context activeUser] userSettings];
|
||||
[us setBool: showCompleted forKey: @"ShowCompletedTasks"];
|
||||
[us synchronize];
|
||||
|
||||
|
||||
tasks = [[self _fetchFields: tasksFields
|
||||
forComponentOfType: @"vtodo"] objectEnumerator];
|
||||
|
||||
|
||||
while ((task = [tasks nextObject]))
|
||||
{
|
||||
statusCode = [[task objectAtIndex: taskStatusIndex] intValue];
|
||||
|
@ -1635,7 +1679,7 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
[filteredTasks sortUsingSelector: @selector (compareTasksCalendarNameAscending:)];
|
||||
else
|
||||
[filteredTasks sortUsingSelector: @selector (compareTasksAscending:)];
|
||||
|
||||
|
||||
ascending = [[context request] formValueForKey: @"asc"];
|
||||
if (![ascending boolValue])
|
||||
[filteredTasks reverseArray];
|
||||
|
@ -1665,11 +1709,11 @@ _computeBlocksPosition (NSArray *blocks)
|
|||
for (i = 0; i < [folders count]; i++)
|
||||
{
|
||||
folder = [folders objectAtIndex: i];
|
||||
|
||||
|
||||
[activeTasksByCalendars setObject: [folder activeTasks]
|
||||
forKey: [folder nameInContainer]];
|
||||
}
|
||||
|
||||
|
||||
return [self _responseWithData: activeTasksByCalendars];
|
||||
}
|
||||
|
||||
|
|
|
@ -383,40 +383,39 @@
|
|||
<span ng-bind="list.filterpopup() | loc"><!-- active filter --></span>
|
||||
</md-subheader>
|
||||
<md-list class="sg-section-list"
|
||||
ng-class="{ 'sg-list-selectable': list.mode.multiple }">
|
||||
<md-list-item ng-repeat="event in list.component.$events"
|
||||
aria-label="{{::event.c_title}}"
|
||||
ng-click="list.openEvent($event, event)">
|
||||
<div class="md-secondary sg-avatar-selectable"
|
||||
label:aria-label="Toggle item"
|
||||
ng-class="[event.getClassName('fg'), { 'sg-avatar-selected' : event.selected }]"
|
||||
ng-click="list.toggleComponentSelection($event, event)">
|
||||
<div class="sg-color-chip"
|
||||
ng-class="::event.getClassName('bg')"><!-- calendar color --></div>
|
||||
</div>
|
||||
<div class="sg-category"
|
||||
ng-repeat="category in ::event.categories | limitTo:5"
|
||||
ng-class="::'bg-category' + category"
|
||||
ng-style="::{ left: ($index * 3) + 'px' }"><!-- calendar color --></div>
|
||||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead">
|
||||
<div>
|
||||
<span ng-show="::event.c_priority" class="sg-priority" ng-bind="::event.c_priority"><!-- priority --></span>
|
||||
<span ng-bind="::event.c_title"><!-- title --></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sg-md-body">
|
||||
<div ng-bind="::event.c_location"><!-- location --></div>
|
||||
<div class="sg-tile-date" ng-bind="::event.formatted_startdate"><!-- start --></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sg-tile-icons">
|
||||
<md-icon ng-show="::event.c_iscycle">repeat</md-icon>
|
||||
<md-icon ng-show="::event.c_nextalarm">alarm</md-icon>
|
||||
</div>
|
||||
<md-divider><!-- divider --></md-divider>
|
||||
ng-class="{ 'sg-list-selectable': list.mode.multiple }"
|
||||
ng-repeat="(key, monthData) in list.component.$events">
|
||||
<md-list-item aria-label="{{::monthData.month}}"
|
||||
md-colors="::{ color: 'default-primary-800' }">
|
||||
<span ng-bind="::monthData.month"><!-- month name --></span>
|
||||
</md-list-item>
|
||||
<md-list-item disabled="disabled" ng-if="list.component.$events.length == 0">
|
||||
<md-list-item layout="row" layout-align="start start"
|
||||
ng-repeat="dayData in monthData.days">
|
||||
<div class="sg-calendar-date">
|
||||
<span class="sg-calendar-day sg-md-display-1--narrow"
|
||||
md-colors="::{ color: 'default-primary-800' }"
|
||||
ng-bind="::dayData.monthDay"><!-- month day --></span>
|
||||
<span>
|
||||
<div md-colors="::{ color: 'default-primary-400' }"
|
||||
ng-bind="::dayData.weekDay"><!-- week day --></div>
|
||||
<div class="md-caption"
|
||||
md-colors="::{ color: 'default-primary-800' }"
|
||||
ng-bind="::dayData.month"><!-- month --></div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sg-calendar-list md-flex">
|
||||
<sg-calendar-list-event
|
||||
ng-repeat="event in dayData.events"
|
||||
sg-component="event"
|
||||
sg-click="list.openEvent($event, clickComponent)">
|
||||
<!-- directive -->
|
||||
</sg-calendar-list-event>
|
||||
<md-divider><!-- divider --></md-divider>
|
||||
</div>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<md-list ng-if="!list.component.$events">
|
||||
<md-list-item disabled="disabled">
|
||||
<p class="sg-md-caption"><var:string label:value="No events for selected criteria"/></p>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
|
@ -447,8 +446,8 @@
|
|||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead">
|
||||
<div>
|
||||
<span ng-show="::task.c_priority" class="sg-priority">{{::task.c_priority}}</span>
|
||||
{{::task.c_title}}
|
||||
<span ng-show="::task.c_priority" class="sg-priority" ng-bind="::task.c_priority"><!-- priority --></span>
|
||||
<span ng-bind="::task.c_title"><!-- title --></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sg-md-body">
|
||||
|
|
|
@ -419,12 +419,27 @@
|
|||
fields.splice(_.indexOf(fields, 'c_recurrence_id'), 1, 'occurrenceId');
|
||||
|
||||
// Instanciate Component objects
|
||||
_.reduce(data[type], function(components, componentData, i) {
|
||||
var data = _.zipObject(fields, componentData), component;
|
||||
component = new Component(data);
|
||||
components.push(component);
|
||||
return components;
|
||||
}, components);
|
||||
|
||||
if (type == 'events') {
|
||||
_.forEach(data[type], function(monthData, month) {
|
||||
_.forEach(monthData.days, function(dayData, day) {
|
||||
_.forEach(dayData.events, function(componentData, i) {
|
||||
var data = _.zipObject(fields, componentData), component;
|
||||
component = new Component(data);
|
||||
dayData.events[i] = component;
|
||||
});
|
||||
});
|
||||
});
|
||||
components = data[type];
|
||||
}
|
||||
else if (type == 'tasks') {
|
||||
_.reduce(data[type], function(components, componentData, i) {
|
||||
var data = _.zipObject(fields, componentData), component;
|
||||
component = new Component(data);
|
||||
components.push(component);
|
||||
return components;
|
||||
}, components);
|
||||
}
|
||||
|
||||
Component.$log.debug('list of ' + type + ' ready (' + components.length + ')');
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* sgCalendarListEvent - An event block to be displayed in a list
|
||||
* @memberof SOGo.SchedulerUI
|
||||
* @restrict element
|
||||
* @param {object} sgComponent - the Component object.
|
||||
* @param {function} sgClick - the function to call when clicking on the event.
|
||||
* Two variables are available: clickEvent (the event that triggered the mouse click),
|
||||
* and clickComponent (a Component object)
|
||||
*
|
||||
* @example:
|
||||
|
||||
<sg-calendar-list-event
|
||||
ng-repeat="event in dayData.events"
|
||||
sg-component="event"
|
||||
sg-click="list.openEvent($event, clickComponent)" />
|
||||
*/
|
||||
sgCalendarListEvent.$inject = ['CalendarSettings'];
|
||||
function sgCalendarListEvent(CalendarSettings) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
component: '=sgComponent',
|
||||
clickComponent: '&sgClick'
|
||||
},
|
||||
replace: true,
|
||||
template: template,
|
||||
link: link
|
||||
};
|
||||
|
||||
function template(tElem, tAttrs) {
|
||||
return [
|
||||
'<div class="sg-event"',
|
||||
' ng-click="clickComponent({clickEvent: $event, clickComponent: component})">',
|
||||
// Categories color stripes
|
||||
' <div class="sg-category" ng-repeat="category in ::component.categories"',
|
||||
' ng-class="::(\'bg-category\' + category)"',
|
||||
' ng-style="::{ right: ($index * 3) + \'px\' }"></div>',
|
||||
// Priority
|
||||
' <span ng-show="::component.c_priority" class="sg-priority" ng-bind="::component.c_priority"></span>',
|
||||
// Summary
|
||||
' {{ ::component.c_title }}',
|
||||
' <span class="icons">',
|
||||
// Component is reccurent
|
||||
' <md-icon ng-if="::component.occurrenceId" class="material-icons icon-repeat"></md-icon>',
|
||||
// Component has an alarm
|
||||
' <md-icon ng-if="::component.c_nextalarm" class="material-icons icon-alarm"></md-icon>',
|
||||
// Component is confidential
|
||||
' <md-icon ng-if="::component.c_classification == 2" class="material-icons icon-visibility-off"></md-icon>',
|
||||
// Component is private
|
||||
' <md-icon ng-if="::component.c_classification == 1" class="material-icons icon-vpn-key"></md-icon>',
|
||||
' </span>',
|
||||
// Time
|
||||
' <div class="secondary" ng-if="::!component.c_isallday">',
|
||||
' <md-icon>access_time</md-icon> {{::component.starthour}}',
|
||||
' </div>',
|
||||
// Location
|
||||
' <div class="secondary" ng-if="::component.c_location">',
|
||||
' <md-icon>place</md-icon> {{::component.c_location}}',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('');
|
||||
}
|
||||
|
||||
function link(scope, iElement, attrs) {
|
||||
/**
|
||||
* No data binding here since the view is completely redraw when
|
||||
* a change is detected.
|
||||
*/
|
||||
|
||||
if (scope.component.viewable)
|
||||
iElement.addClass('md-clickable');
|
||||
|
||||
// Add class for user's participation state
|
||||
if (scope.component.userstate)
|
||||
iElement.addClass('sg-event--' + scope.component.userstate);
|
||||
|
||||
// Set background color
|
||||
iElement.addClass('bg-folder' + scope.component.pid);
|
||||
iElement.addClass('contrast-bdr-folder' + scope.component.pid);
|
||||
|
||||
// Add class for transparency
|
||||
if (scope.component.c_isopaque === 0)
|
||||
iElement.addClass('sg-event--transparent');
|
||||
|
||||
// Add class for cancelled event
|
||||
if (scope.component.c_status === 0)
|
||||
iElement.addClass('sg-event--cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('SOGo.SchedulerUI')
|
||||
.directive('sgCalendarListEvent', sgCalendarListEvent);
|
||||
})();
|
|
@ -337,6 +337,10 @@ html p {
|
|||
line-height: $lineHeight;
|
||||
font-weight: $sg-font-regular;
|
||||
}
|
||||
.#{$md}-display-1--narrow {
|
||||
@extend .#{$md}-display-1;
|
||||
letter-spacing: -0.1em;
|
||||
}
|
||||
.#{$md}-display-2 {
|
||||
$lineHeight: $sg-line-height-7;
|
||||
font-size: $sg-font-size-7;
|
||||
|
|
|
@ -494,6 +494,43 @@ $quarter_height: 10px;
|
|||
.ghostEndHour {
|
||||
bottom: -14px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Middle list view of events
|
||||
|
||||
.view-list {
|
||||
.sg-calendar-date {
|
||||
white-space: nowrap;
|
||||
width: 72px;
|
||||
min-width: 72px;
|
||||
> * {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.sg-calendar-day {
|
||||
font-weight: 200;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.sg-calendar-list {
|
||||
padding-bottom: 16px;
|
||||
md-divider {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
.sg-event {
|
||||
margin: 0 0 4px 0;
|
||||
padding: $bl;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.eventInside {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Multicolumn day cell that contains the calendar name
|
||||
|
|
Loading…
Reference in New Issue