Allow user to choose which weekdays to display

Closes #1841
pull/207/head
Francis Lachapelle 2016-05-06 14:14:45 -04:00
parent 3c6da09ff8
commit c5eeadf041
29 changed files with 393 additions and 146 deletions

1
NEWS
View File

@ -7,6 +7,7 @@ New features
- [web] toolbar of all-day events can be expanded to display all events
- [web] added AngularJS's XSRF support (#3246)
- [web] calendars list can be reordered and filtered
- [web] user can limit the calendars view to specific week days (#1841)
Enhancements
- [web] updated Angular Material to version 1.0.6

View File

@ -199,6 +199,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setCalendarShouldDisplayWeekend: (BOOL) newValue;
- (BOOL) calendarShouldDisplayWeekend;
- (void) setCalendarWeekdays: (NSArray *) newValues;
- (NSArray *) calendarWeekdays;
- (void) setCalendarEventsDefaultClassification: (NSString *) newValue;
- (NSString *) calendarEventsDefaultClassification;

View File

@ -741,6 +741,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self objectForKey: @"SOGoCalendarCategoriesColors"];
}
- (void) setCalendarWeekdays: (NSArray *) newValues
{
[self setObject: newValues forKey: @"SOGoCalendarWeekdays"];
}
- (NSArray *) calendarWeekdays
{
return [self stringArrayForKey: @"SOGoCalendarWeekdays"];
}
- (void) setCalendarShouldDisplayWeekend: (BOOL) newValue
{
[self setBool: newValue forKey: @"SOGoCalendarShouldDisplayWeekend"];

View File

@ -107,6 +107,7 @@
"Day start time" = "Day start time";
"Day end time" = "Day end time";
"Day start time must be prior to day end time." = "Day start time must be prior to day end time.";
"Week days to display" = "Week days to display";
"Show time as busy outside working hours" = "Show time as busy outside working hours";
"First week of year" = "First week of year";
"Enable reminders for Calendar items" = "Enable reminders for Calendar items";

View File

@ -26,6 +26,8 @@
#import <NGExtensions/NSObject+Logs.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoUser.h>
@ -123,6 +125,9 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoDayEndTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayEndHour]] forKey: @"SOGoDayEndTime"];
if (![[defaults source] objectForKey: @"SOGoCalendarWeekdays"])
[[defaults source] setObject: [NSArray arrayWithObjects: iCalWeekDayString count: 7] forKey: @"SOGoCalendarWeekdays"];
if (![[defaults source] objectForKey: @"SOGoFirstWeekOfYear"])
[[defaults source] setObject: [defaults firstWeekOfYear] forKey: @"SOGoFirstWeekOfYear"];

View File

@ -1,6 +1,6 @@
/* UIxPreferences.m - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -30,6 +30,8 @@
#import <NGCards/iCalTimeZone.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
@ -721,6 +723,28 @@ static NSArray *reminderValues = nil;
// return [[moduleSettings objectForKey: @"PreventInvitations"] boolValue];
// }
- (NSArray *) shortWeekDaysList
{
static NSArray *shortWeekDaysList = nil;
if (!shortWeekDaysList)
{
shortWeekDaysList = [locale objectForKey: NSShortWeekDayNameArray];
[shortWeekDaysList retain];
}
return shortWeekDaysList;
}
- (NSString *) valueForWeekDay
{
unsigned int i;
i = [[self shortWeekDaysList] indexOfObject: item];
return iCalWeekDayString[i];
}
//
// Used by wox template
//

View File

@ -42,6 +42,7 @@
NSMutableArray *daysToDisplay, *calendarsToDisplay, *hoursToDisplay;
NSMutableDictionary *currentCalendar;
unsigned int numberOfDays;
unsigned int *daysNumbersToDisplay;
}
- (void) setNumberOfDays: (NSNumber *) aNumber;

View File

@ -1,8 +1,6 @@
/* UIxCalDayTable.m - this file is part of SOGo
*
* Copyright (C) 2006-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,6 +24,8 @@
#import <NGExtensions/NSCalendarDate+misc.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h>
@ -61,7 +61,7 @@
currentCalendar = nil;
currentTableDay = nil;
currentTableHour = nil;
weekDays = [locale objectForKey: NSShortWeekDayNameArray];
weekDays = [locale objectForKey: NSWeekDayNameArray];
[weekDays retain];
dateFormatter = [user dateFormatterInContext: context];
[dateFormatter retain];
@ -81,6 +81,7 @@
[hoursToDisplay release];
[dateFormatter release];
[timeFormat release];
free(daysNumbersToDisplay);
[super dealloc];
}
@ -135,7 +136,7 @@
- (NSArray *) hoursToDisplay
{
unsigned int currentHour, lastHour;
if (!hoursToDisplay)
{
hoursToDisplay = [NSMutableArray new];
@ -157,24 +158,36 @@
return [NSString stringWithFormat: @"hour%d", [currentTableHour intValue]];
}
/**
* Return an array of NSCalendarDate instances matching the requested time period
* and the week days enabled in the user's defaults.
*/
- (NSArray *) daysToDisplay
{
NSCalendarDate *currentDate;
int count;
NSString *weekDay;
int count, enabledCount;
if (!daysToDisplay)
{
daysToDisplay = [NSMutableArray new];
daysNumbersToDisplay = malloc (numberOfDays * sizeof (unsigned int));
currentDate = [[self startDate] hour: [self dayStartHour]
minute: 0];
for (count = 0; count < numberOfDays; count++)
for (count = 0, enabledCount = 0; count < numberOfDays; count++)
{
[daysToDisplay addObject: currentDate];
weekDay = iCalWeekDayString[[currentDate dayOfWeek]];
if ([enabledWeekDays count] == 0 || [enabledWeekDays containsObject: weekDay])
{
[daysToDisplay addObject: currentDate];
daysNumbersToDisplay[enabledCount] = count;
enabledCount++;
}
currentDate = [currentDate tomorrow];
}
}
return daysToDisplay;
}
@ -218,7 +231,7 @@
}
}
}
return calendarsToDisplay;
}
@ -276,7 +289,9 @@
- (int) currentDayNumber
{
return [daysToDisplay indexOfObject: currentTableDay];
int i = [daysToDisplay indexOfObject: currentTableDay];
return daysNumbersToDisplay[i];
}
- (NSNumber *) currentAppointmentHour
@ -354,7 +369,7 @@
// {
// newMutableAppointment
// = [NSMutableDictionary dictionaryWithDictionary: anAppointment];
// if (startIsEarlier)
// [newMutableAppointment setObject: start
// forKey: @"startDate"];
@ -430,13 +445,13 @@
// - (NSString *) daysViewClasses
// {
// NSString *daysView;
// if ([currentView isEqualToString:@"multicolumndayview"])
// daysView = @"daysView daysViewForMultipleDays";
// else
// daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
// return daysView;
//}
@ -448,14 +463,11 @@
- (NSString *) dayClasses
{
NSMutableString *classes;
unsigned int currentDayNbr, realDayOfWeek;
classes = [NSMutableString string];
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
unsigned int realDayOfWeek;
classes = [NSMutableString stringWithString: @"day"];
realDayOfWeek = [currentTableDay dayOfWeek];
[classes appendFormat: @"day day%d", currentDayNbr];
if (numberOfDays > 1)
{
if (realDayOfWeek == 0 || realDayOfWeek == 6)
@ -463,7 +475,7 @@
if ([currentTableDay isToday])
[classes appendString: @" dayOfToday"];
}
return classes;
}
@ -487,7 +499,7 @@
{
if ([currentView isEqualToString:@"multicolumndayview"])
return YES;
return NO;
}
@ -495,7 +507,7 @@
{
if ([currentView isEqualToString:@"dayview"] || [currentView isEqualToString:@"weekview"])
return YES;
return NO;
}

View File

@ -1,30 +1,27 @@
/*
Copyright (C) 2004 SKYRIX Software AG
Copyright (C) 2006-2016 Inverse inc.
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
SOGo 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
SOGo 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
License along with SOGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <Foundation/NSDictionary.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import "UIxCalDayView.h"
// @interface UIxCalDayView (PrivateAPI)
@ -66,7 +63,7 @@
// {
// NSCalendarDate *dateStart, *dateEnd, *aptStart, *aptEnd;
// NGCalendarDateRange *dateRange, *aptRange;
// dateStart = self->currentDate;
// dateEnd = [dateStart dateByAddingYears:0 months:0 days:0
// hours:1 minutes:0 seconds:0];
@ -104,7 +101,7 @@
// if ([d isDateOnSameDay:max])
// max = (NSCalendarDate *)[d laterDate:max];
// }
// return [self _getDatesFrom:min to:max];
// }
@ -119,7 +116,7 @@
// dates = [[NSMutableArray alloc] initWithCapacity:count];
// for(i = 0; i < count; i++) {
// NSCalendarDate *date;
// date = [_from hour:offset + i minute:0];
// [dates addObject:date];
// }
@ -135,12 +132,12 @@
- (NSDictionary *) prevDayQueryParameters
{
return [self _dateQueryParametersWithOffset: -1];
return [self _dateQueryParametersWithOffset: [self _nextValidOffset: -1]];
}
- (NSDictionary *) nextDayQueryParameters
{
return [self _dateQueryParametersWithOffset: 1];
return [self _dateQueryParametersWithOffset: [self _nextValidOffset: +1]];
}
- (NSDictionary *) dayAfterNextDayQueryParameters
@ -153,7 +150,7 @@
NSMutableDictionary *qp;
NSString *hmString;
NSCalendarDate *date;
date = [self selectedDate];
hmString = [NSString stringWithFormat:@"%.2d%.2d",
(int)[date hourOfDay], (int)[date minuteOfHour]];
@ -177,7 +174,7 @@
// NSArray *apts;
// NSMutableArray *filtered;
// unsigned i, count;
// if (self->allDayApts)
// return self->allDayApts;
@ -188,7 +185,7 @@
// for (i = 0; i < count; i++) {
// id apt;
// NSNumber *bv;
// apt = [apts objectAtIndex:i];
// bv = [apt valueForKey:@"isallday"];
// if ([bv boolValue]) {
@ -203,7 +200,7 @@
// }
// }
// }
// ASSIGN(self->allDayApts, filtered);
// [filtered release];
// return self->allDayApts;
@ -219,8 +216,8 @@
NSCalendarDate *date;
date = [[self selectedDate] dateByAddingYears: 0
months: 0
days: offset];
months: 0
days: offset];
return [self localizedNameForDayOfWeek: [date dayOfWeek]];
}
@ -232,7 +229,7 @@
- (NSString *) yesterdayName
{
return [self _dayNameWithOffsetFromToday: -1];
return [self _dayNameWithOffsetFromToday: [self _nextValidOffset: -1]];
}
- (NSString *) currentDayName
@ -242,7 +239,7 @@
- (NSString *) tomorrowName
{
return [self _dayNameWithOffsetFromToday: 1];
return [self _dayNameWithOffsetFromToday: [self _nextValidOffset: +1]];
}
- (NSString *) dayAfterTomorrowName

View File

@ -35,6 +35,7 @@
@interface UIxCalListingActions : SOGoDirectAction
{
NSArray *enabledWeekDays;
NSMutableDictionary *componentsData;
NSCalendarDate *startDate;
NSCalendarDate *endDate;

View File

@ -35,6 +35,8 @@
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
@ -114,6 +116,7 @@ static NSArray *tasksFields = nil;
ASSIGN (userTimeZone, [[user userDefaults] timeZone]);
dayBasedView = NO;
currentView = nil;
ASSIGN (enabledWeekDays, [[user userDefaults] calendarWeekdays]);
}
return self;
@ -125,6 +128,7 @@ static NSArray *tasksFields = nil;
[request release];
[componentsData release];
[userTimeZone release];
[enabledWeekDays release];
[super dealloc];
}
@ -350,7 +354,7 @@ static NSArray *tasksFields = nil;
NSMutableDictionary *newInfo;
NSMutableArray *infos, *quickInfos, *allInfos, *quickInfosName;
NSNull *marker;
NSString *owner, *role, *calendarName, *iCalString, *recurrenceTime, *categories;
NSString *owner, *role, *calendarName, *iCalString, *recurrenceTime, *categories, *weekDay;
NSRange match;
iCalCalendar *calendar;
iCalEntityObject *master;
@ -451,6 +455,11 @@ static NSArray *tasksFields = nil;
while ((newInfo = [currentInfos nextObject]))
{
// Skip components that appear on disabled weekdays
weekDay = iCalWeekDayString[[[newInfo objectForKey: @"startDate"] dayOfWeek]];
if ([enabledWeekDays count] && ![enabledWeekDays containsObject: weekDay])
continue;
if ([fields containsObject: @"viewable"])
{
if ([owner isEqualToString: userLogin])
@ -770,26 +779,35 @@ static NSArray *tasksFields = nil;
*/
- (WOResponse *) eventsListAction
{
BOOL isAllDay;
NSArray *oldEvent;
NSCalendarDate *date;
NSDictionary *data;
NSEnumerator *events;
NSMutableArray *fields, *newEvents, *newEvent;
NSString *sort, *ascending, *weekDay;
unsigned int interval;
BOOL isAllDay;
NSString *sort, *ascending;
[self _setupContext];
[self saveFilterValue: @"EventsFilterState"];
[self saveSortValue: @"EventsSortingState"];
newEvents = [NSMutableArray array];
events = [[self _fetchFields: eventsFields
forComponentOfType: @"vevent"] objectEnumerator];
while ((oldEvent = [events nextObject]))
{
interval = [[oldEvent objectAtIndex: eventStartDateIndex] intValue];
date = [NSCalendarDate dateWithTimeIntervalSince1970: interval];
[date setTimeZone: userTimeZone];
// Skip components that appear on disabled weekdays
weekDay = iCalWeekDayString[[date dayOfWeek]];
if ([enabledWeekDays count] && ![enabledWeekDays containsObject: weekDay])
continue;
newEvent = [NSMutableArray arrayWithArray: oldEvent];
isAllDay = [[oldEvent objectAtIndex: eventIsAllDayIndex] boolValue];
interval = [[oldEvent objectAtIndex: eventStartDateIndex] intValue];
[newEvent addObject: [self _formattedDateForSeconds: interval
forAllDay: isAllDay]];
interval = [[oldEvent objectAtIndex: eventEndDateIndex] intValue];
@ -967,8 +985,7 @@ static inline NSString* _userStateInEvent (NSArray *event)
offset = 0;
}
else
offset = ((currentStart - startSecs)
/ dayLength);
offset = ((currentStart - startSecs) / dayLength);
if (offset >= [blocks count])
[self errorWithFormat: @"event '%@' has a computed offset that"
@" overflows the amount of blocks (skipped)",
@ -1309,15 +1326,16 @@ _computeBlocksPosition (NSArray *blocks)
{
int count, max;
NSArray *events, *event, *calendars;
NSCalendarDate *currentDate;
NSDictionary *eventsBlocks, *calendar;
NSMutableArray *allDayBlocks, *blocks, *currentDay, *eventsForCalendar, *eventsByCalendars;
NSMutableArray *allDayBlocks, *blocks, *days, *currentDay, *eventsForCalendar, *eventsByCalendars;
NSNumber *eventNbr;
NSString *calendarName, *calendarId;
BOOL isAllDay;
int i, j;
[self _setupContext];
events = [self _fetchFields: eventsFields forComponentOfType: @"vevent"];
if ([currentView isEqualToString: @"multicolumndayview"])
@ -1329,6 +1347,7 @@ _computeBlocksPosition (NSArray *blocks)
calendar = [calendars objectAtIndex:i];
calendarName = [calendar objectForKey: @"name"];
calendarId = [calendar objectForKey: @"id"];
days = [NSMutableArray array];
eventsForCalendar = [NSMutableArray array];
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
for (j = 0; j < [events count]; j++) {
@ -1343,7 +1362,8 @@ _computeBlocksPosition (NSArray *blocks)
eventsFields, @"eventsFields",
eventsForCalendar, @"events",
allDayBlocks, @"allDayBlocks",
blocks, @"blocks", nil];
blocks, @"blocks",
days, @"days", nil];
max = [eventsForCalendar count];
for (count = 0; count < max; count++)
{
@ -1355,12 +1375,20 @@ _computeBlocksPosition (NSArray *blocks)
else
[self _fillBlocks: blocks withEvent: event withNumber: eventNbr];
}
currentDate = [[startDate copy] autorelease];
[currentDate setTimeZone: userTimeZone];
max = [blocks count];
for (count = 0; count < max; count++)
{
currentDay = [blocks objectAtIndex: count];
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
[days addObject: [NSDictionary dictionaryWithObjectsAndKeys:
[currentDate shortDateString], @"date",
[NSNumber numberWithInt: count], @"number", nil]];
currentDate = [currentDate addYear:0 month:0 day:1 hour:0 minute:0 second:0];
}
[eventsByCalendars addObject: eventsBlocks];
}
@ -1368,12 +1396,14 @@ _computeBlocksPosition (NSArray *blocks)
}
else
{
days = [NSMutableArray array];
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
eventsBlocks = [NSDictionary dictionaryWithObjectsAndKeys:
eventsFields, @"eventsFields",
events, @"events",
allDayBlocks, @"allDayBlocks",
blocks, @"blocks", nil];
blocks, @"blocks",
days, @"days", nil];
max = [events count];
for (count = 0; count < max; count++)
{
@ -1390,14 +1420,44 @@ _computeBlocksPosition (NSArray *blocks)
else
[self _fillBlocks: blocks withEvent: event withNumber: eventNbr];
}
currentDate = [[startDate copy] autorelease];
[ setTimeZone: userTimeZone];
max = [blocks count];
for (count = 0; count < max; count++)
{
currentDay = [blocks objectAtIndex: count];
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
[days addObject: [NSDictionary dictionaryWithObjectsAndKeys:
[currentDate shortDateString], @"date",
[NSNumber numberWithInt: count], @"number", nil]];
currentDate = [currentDate addYear:0 month:0 day:1 hour:0 minute:0 second:0];
}
if ([enabledWeekDays count] > 0 && [enabledWeekDays count] < 7)
{
// Remove the days that are disabled in the user's defaults
int weekDay, weekDayCount;
weekDayCount= [currentDate dayOfWeek];
for (count = max - 1; count >= 0; count--)
{
weekDayCount--;
if (weekDayCount < 0)
weekDay = ((weekDayCount % 7) + 7) % 7;
else
weekDay = weekDayCount;
if (![enabledWeekDays containsObject: iCalWeekDayString[weekDay]])
{
[allDayBlocks removeObjectAtIndex: count];
[blocks removeObjectAtIndex: count];
[days removeObjectAtIndex: count];
}
}
}
return [self _responseWithData: [NSArray arrayWithObject: eventsBlocks]];
}
}

View File

@ -1,6 +1,6 @@
/* UIxCalMonthView.h - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -44,6 +44,8 @@
NSArray *dayNames;
NSArray *monthNames;
unsigned int *daysNumbersToDisplay;
}
- (NSDictionary *) monthBeforePrevMonthQueryParameters;

View File

@ -1,6 +1,6 @@
/* UIxCalMonthView.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,8 +23,11 @@
#import <NGExtensions/NSCalendarDate+misc.h>
#import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGoUI/SOGoAptFormatter.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import "UIxCalMonthView.h"
@ -59,6 +62,8 @@
[weeksToDisplay release];
[currentTableDay release];
[currentWeek release];
[enabledWeekDays release];
free(daysNumbersToDisplay);
[super dealloc];
}
@ -71,16 +76,19 @@
- (NSArray *) headerDaysToDisplay
{
NSMutableArray *headerDaysToDisplay;
unsigned int counter;
NSCalendarDate *currentDate;
NSMutableArray *headerDaysToDisplay;
NSString *weekDay;
unsigned int counter;
headerDaysToDisplay = [NSMutableArray arrayWithCapacity: 7];
currentDate
= [[context activeUser] firstDayOfWeekForDate: [self selectedDate]];
currentDate = [[context activeUser] firstDayOfWeekForDate: [self selectedDate]];
for (counter = 0; counter < 7; counter++)
{
[headerDaysToDisplay addObject: currentDate];
// Skip days that match disabled weekdays in user's defaults
weekDay = iCalWeekDayString[[currentDate dayOfWeek]];
if ([enabledWeekDays count] == 0 || [enabledWeekDays containsObject: weekDay])
[headerDaysToDisplay addObject: currentDate];
currentDate = [currentDate tomorrow];
}
@ -89,31 +97,37 @@
- (NSArray *) weeksToDisplay
{
NSMutableArray *week;
unsigned int counter, day;
NSCalendarDate *currentDate, *selectedDate, *lastDayOfMonth, *firstOfAllDays;
NSMutableArray *week;
NSString *weekDay;
unsigned int counter, day, enabledCounter;
unsigned int firstToLast, weeks;
if (!weeksToDisplay)
{
selectedDate = [self selectedDate];
firstOfAllDays
= [[context activeUser] firstDayOfWeekForDate:
[selectedDate firstDayOfMonth]];
firstOfAllDays = [[context activeUser] firstDayOfWeekForDate:
[selectedDate firstDayOfMonth]];
lastDayOfMonth = [selectedDate lastDayOfMonth];
firstToLast = ([lastDayOfMonth timeIntervalSinceDate: firstOfAllDays]
/ 86400) + 1;
firstToLast = ([lastDayOfMonth timeIntervalSinceDate: firstOfAllDays] / 86400) + 1;
weeks = firstToLast / 7;
if ((firstToLast % 7))
weeks++;
weeksToDisplay = [NSMutableArray arrayWithCapacity: weeks];
daysNumbersToDisplay = malloc (weeks * 7 * sizeof (unsigned int));
currentDate = firstOfAllDays;
for (counter = 0; counter < weeks; counter++)
for (counter = 0, enabledCounter = 0; counter < weeks; counter++)
{
week = [NSMutableArray arrayWithCapacity: 7];
for (day = 0; day < 7; day++)
{
[week addObject: currentDate];
weekDay = iCalWeekDayString[[currentDate dayOfWeek]];
if ([enabledWeekDays count] == 0 || [enabledWeekDays containsObject: weekDay])
{
[week addObject: currentDate];
daysNumbersToDisplay[enabledCounter] = (counter * 7) + day;
enabledCounter++;
}
currentDate = [currentDate tomorrow];
}
[weeksToDisplay addObject: week];
@ -137,7 +151,7 @@
date = [firstDay dateByAddingYears: 0 months: monthsOffset
days: 0 hours: 0 minutes: 0 seconds: 0];
return [self queryParametersBySettingSelectedDate: date];
return [self queryParametersBySettingSelectedDate: [self _nextValidDate: date]];
}
- (NSDictionary *) monthBeforePrevMonthQueryParameters
@ -214,8 +228,10 @@
- (int) currentDayNumber
{
return ([currentWeek indexOfObject: currentTableDay]
+ [weeksToDisplay indexOfObject: currentWeek] * 7);
int i = [currentWeek indexOfObject: currentTableDay] +
([weeksToDisplay indexOfObject: currentWeek] * [currentWeek count]);
return daysNumbersToDisplay[i];
}
- (void) setCurrentWeek: (NSArray *) newCurrentWeek
@ -285,8 +301,9 @@
NSCalendarDate *firstDayOfMonth;
firstDayOfMonth = [[self selectedDate] firstDayOfMonth];
firstDayOfMonth = [[context activeUser] firstDayOfWeekForDate: firstDayOfMonth];
return [[context activeUser] firstDayOfWeekForDate: firstDayOfMonth];
return [self _nextValidDate: firstDayOfMonth];
}
- (NSCalendarDate *) endDate

View File

@ -1,8 +1,6 @@
/* UIxCalView.h - this file is part of SOGo
*
* Copyright (C) 2006-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -48,6 +46,7 @@
NSMutableDictionary *componentsData;
NSArray *tasks;
NSArray *allDayApts;
NSArray *enabledWeekDays;
id appointment;
NSCalendarDate *currentDay;
NSTimeZone *timeZone;
@ -130,6 +129,8 @@
- (NSString *) aptStyle;
/* protected methods */
- (NSCalendarDate *) _nextValidDate: (NSCalendarDate *) date;
- (int) _nextValidOffset: (int) daysOffset;
- (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset;
@end

View File

@ -1,6 +1,6 @@
/* UIxCalView.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2016 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,6 +26,8 @@
#import <NGExtensions/NSString+misc.h>
#import <NGCards/NGCards.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <Appointments/SOGoAppointmentFolder.h>
#import <Appointments/SOGoAppointmentFolders.h>
#import <SOGo/NSArray+Utilities.h>
@ -55,6 +57,7 @@
{
ud = [[context activeUser] userDefaults];
ASSIGN (timeZone, [ud timeZone]);
ASSIGN (enabledWeekDays, [ud calendarWeekdays]);
aptFormatter
= [[SOGoAptFormatter alloc] initWithDisplayTimeZone: timeZone];
aptTooltipFormatter
@ -253,7 +256,7 @@
title = [appointment valueForKey: @"title"];
if ([title length] > 12)
title = [[title substringToIndex: 11] stringByAppendingString: @"..."];
return title;
}
@ -396,7 +399,7 @@
if ([bv boolValue])
[filtered addObject: apt];
}
ASSIGN(allDayApts, filtered);
[filtered release];
return allDayApts;
@ -448,10 +451,10 @@
- (NSString *) appointmentViewURL
{
id pkey;
if (![(pkey = [[self appointment] valueForKey: @"uid"]) isNotNull])
return nil;
return [[[self clientObject] baseURLForAptWithUID: [pkey stringValue]
inContext: [self context]]
stringByAppendingString: @"/view"];
@ -474,7 +477,7 @@
- (BOOL) shouldDisplayRejectedAppointments
{
NSString *bv;
bv = [self queryParameterForKey: @"dr"];
if (!bv) return NO;
return [bv boolValue];
@ -484,7 +487,7 @@
{
NSMutableDictionary *qp;
BOOL shouldDisplay;
shouldDisplay = ![self shouldDisplayRejectedAppointments];
qp = [[[self queryParameters] mutableCopy] autorelease];
[qp setObject: shouldDisplay ? @"1" : @"0" forKey: @"dr"];
@ -500,19 +503,68 @@
/* date selection & conversion */
- (NSCalendarDate *) _nextValidDate: (NSCalendarDate *) date
{
NSCalendarDate *validDate;
NSString *weekDay;
validDate = [[date copy] autorelease];
if ([enabledWeekDays count])
{
weekDay = iCalWeekDayString[[validDate dayOfWeek]];
while (![enabledWeekDays containsObject: weekDay])
{
validDate = [validDate dateByAddingYears:0 months:0 days:1 hours:0 minutes:0 seconds:0];
weekDay = iCalWeekDayString[[validDate dayOfWeek]];
}
}
return validDate;
}
- (int) _nextValidOffset: (int) daysOffset
{
NSCalendarDate *date;
NSString *weekDay;
int count, offset;
count = (daysOffset < 0)? -1 : +1;
offset = daysOffset;
if ([enabledWeekDays count])
{
date = [[self startDate] dateByAddingYears:0 months:0 days:offset hours:0 minutes:0 seconds:0];
weekDay = iCalWeekDayString[[date dayOfWeek]];
while (![enabledWeekDays containsObject: weekDay])
{
offset += count;
date = [date dateByAddingYears:0 months:0 days:count hours:0 minutes:0 seconds:0];
weekDay = iCalWeekDayString[[date dayOfWeek]];
}
}
return offset;
}
- (NSDictionary *) _dateQueryParametersWithOffset: (int) daysOffset
{
NSCalendarDate *date;
date = [[self startDate] dateByAddingYears: 0 months: 0
days: daysOffset
hours: 0 minutes: 0 seconds: 0];
return [self queryParametersBySettingSelectedDate: date];
}
- (NSDictionary *) todayQueryParameters
{
return [self queryParametersBySettingSelectedDate: [NSCalendarDate date]];
NSCalendarDate *today;
today = [NSCalendarDate date];
[today setTimeZone: timeZone];
return [self queryParametersBySettingSelectedDate: [self _nextValidDate: today]];
}
- (NSDictionary *) currentDayQueryParameters
@ -581,7 +633,7 @@
&& (![userFolderID isEqualToString: uidsString])))
{
NSRange r;
/* uri ends with an '/', so we have to adjust the range a little */
r = NSMakeRange(0, [uri length] - 1);
r = [uri rangeOfString: @"/"

View File

@ -44,7 +44,7 @@
date = [[context activeUser] firstDayOfWeekForDate: [super startDate]];
return [date beginOfDay];
return [self _nextValidDate: [date beginOfDay]];
}
- (NSCalendarDate *) endDate

View File

@ -290,7 +290,19 @@
</md-input-container>
</div>
<md-checkbox
<div class="pseudo-input-container"
layout="row" layout-align="center center">
<label class="pseudo-input-label"><var:string label:value="Week days to display"/></label>
<md-grid-list md-cols="7" md-row-height="2em" md-gutter="0.5em"
flex="50" flex-sm="80" flex-xs="100"
sg-toggle-grid="app.preferences.defaults.SOGoCalendarWeekdays">
<var:foreach list="shortWeekDaysList" item="item">
<md-grid-tile var:value="valueForWeekDay"><var:string value="item"/></md-grid-tile>
</var:foreach>
</md-grid-list>
</div>
<md-checkbox layout-padding="layout-padding"
ng-model="app.preferences.defaults.SOGoBusyOffHours"
ng-true-value="1"
ng-false-value="0">

View File

@ -61,8 +61,8 @@
<div class="days" sg-calendar-scroll-view="multiday-allday">
<var:foreach list="daysToDisplay" item="currentTableDay">
<sg-calendar-day var:class="dayClasses"
var:day="currentTableDay.shortDateString"
var:id="currentAllDayId"
var:day="currentTableDay.shortDateString"
hour="allday"
var:sg-day-number="currentDayNumber"
var:sg-day="currentTableDay.shortDateString"
@ -118,9 +118,8 @@
</div>
</var:foreach>
<sg-calendar-day-table
sg-click="list.openEvent(event, component)"
sg-blocks="view.blocks"
var:sg-day-number="currentDayNumber"
sg-click="list.openEvent(event, component)"
var:sg-day="currentTableDay.shortDateString"><!-- blocks for current day --></sg-calendar-day-table>
<sg-calendar-day-block
sg-calendar-ghost="sg-calendar-ghost"

View File

@ -10,17 +10,18 @@
<md-card>
<md-card-actions flex-none="flex-none" layout="row" layout-align="start center">
<md-button class="md-icon-button"
label:aria-label="Previous Day"
var:aria-label="yesterdayName"
var:date="prevDayQueryParameters.day"
ng-click="calendar.changeDate($event)"><md-icon>chevron_left</md-icon></md-button>
<md-button class="md-icon-button"
label:aria-label="Next Day"
var:aria-label="tomorrowName"
var:date="nextDayQueryParameters.day"
ng-click="calendar.changeDate($event)"><md-icon>chevron_right</md-icon></md-button>
<div class="md-flex"><var:string value="currentDayName" /></div>
<md-button class="md-icon-button md-accent"
label:aria-label="Today"
ng-click="app.today()">
var:date="todayQueryParameters.day"
ng-click="calendar.changeDate($event)">
<md-tooltip><var:string label:value="Today"/></md-tooltip>
<md-icon>today</md-icon>
</md-button>

View File

@ -20,7 +20,8 @@
<div class="md-flex"><var:string value="monthNameOfThisMonth" /> <var:string value="selectedDate.yearOfCommonEra" /></div>
<md-button class="md-icon-button md-accent"
label:aria-label="Today"
ng-click="app.today()">
var:date="todayQueryParameters.day"
ng-click="calendar.changeDate($event)">
<md-tooltip><var:string label:value="Today"/></md-tooltip>
<md-icon>today</md-icon>
</md-button>
@ -51,7 +52,7 @@
<md-content class="md-flex" sg-calendar-scroll-view="monthly">
<div class="calendarView monthView">
<md-grid-list
md-cols="7"
var:md-cols="headerDaysToDisplay.count"
md-row-height="1:1"
md-gutter="0">
<var:foreach list="weeksToDisplay" item="currentWeek">

View File

@ -10,11 +10,11 @@
<md-card>
<md-card-actions flex-none="flex-none" layout="row" layout-align="start center">
<md-button class="md-icon-button"
label:aria-label="Previous Day"
var:aria-label="yesterdayName"
var:date="prevDayQueryParameters.day"
ng-click="calendar.changeDate($event)"><md-icon>chevron_left</md-icon></md-button>
<md-button class="md-icon-button"
label:aria-label="Next Day"
var:aria-label="tomorrowName"
var:date="nextDayQueryParameters.day"
ng-click="calendar.changeDate($event)"><md-icon>chevron_right</md-icon></md-button>
<div class="md-flex"><var:string value="currentDayName" /></div>

View File

@ -20,7 +20,8 @@
<div class="md-flex"><var:string value="currentWeekName" /></div>
<md-button class="md-icon-button md-accent"
label:aria-label="Today"
ng-click="app.today()">
var:date="todayQueryParameters.day"
ng-click="calendar.changeDate($event)">
<md-tooltip><var:string label:value="Today"/></md-tooltip>
<md-icon>today</md-icon>
</md-button>

View File

@ -51,8 +51,8 @@
}
angular.module('SOGo.SchedulerUI')
.value('CalendarSettings', {
EventDragDayLength: 24 * 4,
EventDragHorizontalOffset: 3
EventDragDayLength: 24 * 4, // hour quarters
EventDragHorizontalOffset: 3 // pixels
})
.factory('Calendar', Calendar.$factory);

View File

@ -281,7 +281,7 @@
* @returns a promise of a collection of objects describing the events blocks
*/
Component.$eventsBlocks = function(view, startDate, endDate) {
var params, futureComponentData, i, j, dates = [],
var params, futureComponentData, i, j, dayDates = [], dayNumbers = [],
deferred = Component.$q.defer();
params = { view: view.toLowerCase(), sd: startDate.getDayString(), ed: endDate.getDayString() };
@ -306,7 +306,7 @@
Component.$views = [];
Component.$timeout(function() {
_.forEach(views, function(data) {
_.forEach(views, function(data, viewIndex) {
var components = [], blocks = {}, allDayBlocks = {}, viewData;
// Change some attributes names
@ -325,24 +325,27 @@
_.forEach(_.flatten(data.allDayBlocks), _.bind(associateComponent, components));
// Build array of dates
if (dates.length === 0)
for (i = 0; i < data.blocks.length; i++) {
dates.push(startDate.getDayString());
startDate.addDays(1);
}
if (dayDates.length === 0) {
dayDates = _.flatMap(data.days, 'date');
dayNumbers = _.flatMap(data.days, 'number');
}
// Convert array of blocks to object with days as keys
// Convert array of blocks to an object literal with date strings as keys
for (i = 0; i < data.blocks.length; i++) {
for (j = 0; j < data.blocks[i].length; j++)
data.blocks[i][j].dayNumber = i;
blocks[dates[i]] = data.blocks[i];
for (j = 0; j < data.blocks[i].length; j++) {
data.blocks[i][j].dayIndex = i + (viewIndex * data.blocks.length);
data.blocks[i][j].dayNumber = dayNumbers[i];
}
blocks[dayDates[i]] = data.blocks[i];
}
// Convert array of all-day blocks to object with days as keys
for (i = 0; i < data.allDayBlocks.length; i++) {
for (j = 0; j < data.allDayBlocks[i].length; j++)
data.allDayBlocks[i][j].dayNumber = i;
allDayBlocks[dates[i]] = data.allDayBlocks[i];
for (j = 0; j < data.allDayBlocks[i].length; j++) {
data.allDayBlocks[i][j].dayIndex = i + (viewIndex * data.allDayBlocks.length);
data.allDayBlocks[i][j].dayNumber = dayNumbers[i];
}
allDayBlocks[dayDates[i]] = data.allDayBlocks[i];
}
// "blocks" is now an object literal with the following structure:
@ -1057,7 +1060,7 @@
var _this = this, options, path, component, date, dlp;
component = this.$omit();
dlp = Component.$Preferences.constructor.$mdDateLocaleProvider;
dlp = Component.$Preferences.$mdDateLocaleProvider;
// Format dates and times
component.startDate = component.start ? component.start.format(dlp, '%Y-%m-%d') : '';

View File

@ -123,6 +123,17 @@
url += view[1];
else
url += 'week';
// Append today's date or next enabled weekday
var now = new Date();
if (Preferences.defaults.SOGoCalendarWeekdays) {
var weekDays = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
var weekDay = weekDays[now.getDay()];
while (Preferences.defaults.SOGoCalendarWeekdays.indexOf(weekDay) < 0) {
now.addDays(1);
weekDay = weekDays[now.getDay()];
}
}
url += '/' + now.getDayString();
$location.replace().url(url);
});
}

View File

@ -83,7 +83,7 @@
function updateGhost() {
// From SOGoEventDragGhostController._updateGhosts
var showGhost, isRelative, originalDay, currentDay, wasOtherBlock,
var showGhost, isRelative, currentDay, wasOtherBlock,
start, duration, durationLeft, maxDuration, enableTransition;
showGhost = false;
@ -95,7 +95,6 @@
// The view of the dragging block is the scrolling view of this ghost block
isRelative = scrollViewCtrl.type === 'multiday-allday';
originalDay = scope.block.pointerHandler.originalEventCoordinates.dayNumber;
currentDay = scope.block.pointerHandler.currentEventCoordinates.dayNumber;
start = scope.block.pointerHandler.currentEventCoordinates.start;
durationLeft = scope.block.pointerHandler.currentEventCoordinates.duration;
@ -103,7 +102,6 @@
if (angular.isUndefined(durationLeft))
return;
duration = durationLeft;
if (duration > maxDuration)
duration = maxDuration;
@ -118,7 +116,7 @@
)) {
// This ghost block (day) is the first of the dragging event
showGhost = true;
if (!isRelative) {
if (!isRelative) {
// Show start hour and set the vertical position
scope.block.startHour = getStartTime(start);
wasOtherBlock = parseInt(iElement.css('top')) === 0;

View File

@ -25,11 +25,13 @@
},
controller: sgCalendarScrollViewController,
link: function(scope, element, attrs, controller) {
var view, scrollView, type, lastScroll, deregisterDragStart, deregisterDragStop;
var view, scrollView, type, lastScroll, days, deregisterDragStart, deregisterDragStop;
view = null;
scrollView = element[0];
type = scope.type; // multiday, multiday-allday, monthly, unknown?
lastScroll = 0;
days = null;
// Listen to dragstart and dragend events
deregisterDragStart = $rootScope.$on('calendar:dragstart', onDragStart);
@ -56,6 +58,7 @@
type: type,
quarterHeight: quarterHeight,
scrollStep: 6 * quarterHeight,
dayNumbers: getDayNumbers(),
maxX: getMaxColumns(),
// Expose a reference of the view element
@ -94,7 +97,7 @@
var width, height, leftOffset, topOffset, nodes, domRect, tileHeader;
height = width = leftOffset = topOffset = 0;
nodes = scrollView.getElementsByClassName('day0');
nodes = scrollView.getElementsByClassName('day');
if (nodes.length > 0) {
domRect = nodes[0].getBoundingClientRect();
@ -109,6 +112,22 @@
return { height: height, width: width, offset: { left: leftOffset, top: topOffset } };
}
function getDayNumbers() {
var viewType = null, isMultiColumn, days, total, sum;
if (scrollView.attributes['sg-view'])
viewType = scrollView.attributes['sg-view'].value;
isMultiColumn = (viewType == 'multicolumndayview');
days = scrollView.getElementsByTagName('sg-calendar-day');
return _.map(days, function(element, index) {
if (isMultiColumn)
return index;
else
return parseInt(element.attributes['sg-day-number'].value);
});
}
function getMaxColumns() {
var max = 0;
@ -153,9 +172,9 @@
function updateFromPointerHandler() {
var scrollStep, pointerHandler, pointerCoordinates, now, scrollY, minY, delta;
scrollStep = view.scrollStep;
pointerHandler = Component.$ghost.pointerHandler;
if (pointerHandler) {
if (view && pointerHandler) {
scrollStep = view.scrollStep;
pointerCoordinates = pointerHandler.getContainerBasedCoordinates(view);
if (pointerCoordinates) {

View File

@ -42,12 +42,12 @@
});
function initGrips() {
var component, dayNumber, blockIndex, isFirstBlock, isLastBlock,
var component, dayIndex, blockIndex, isFirstBlock, isLastBlock,
dragGrip, leftGrip, rightGrip, topGrip, bottomGrip;
component = scope.block.component;
dayNumber = scope.block.dayNumber;
blockIndex = _.findIndex(component.blocks, ['dayNumber', dayNumber]);
dayIndex = scope.block.dayIndex;
blockIndex = _.findIndex(component.blocks, ['dayIndex', dayIndex]);
isFirstBlock = (blockIndex === 0);
isLastBlock = (blockIndex === component.blocks.length - 1);
@ -78,12 +78,12 @@
}
function onDragDetect(ev) {
var block, dragMode, eventType, startDate, newData, newComponent, pointerHandler;
var dragMode, pointerHandler;
ev.stopPropagation();
dragMode = 'move-event';
if (scope.block && scope.block.component) {
// Move or resize existing component
if (ev.target.className == 'dragGrip-top' ||
@ -220,6 +220,10 @@
delta.x = this.x - otherCoordinates.x;
delta.y = this.y - otherCoordinates.y;
if (Calendar.$view) {
delta.days = Calendar.$view.dayNumbers[this.x] - Calendar.$view.dayNumbers[otherCoordinates.x];
}
return delta;
},
@ -247,6 +251,7 @@
SOGoEventDragEventCoordinates.prototype = {
dayNumber: -1,
weekDay: -1,
start: -1,
duration: -1,
@ -257,21 +262,29 @@
},
initFromBlock: function(block) {
var prevDayNumber = -1;
if (this.eventType === 'monthly') {
this.start = 0;
this.duration = block.component.blocks.length * 96;
this.duration = block.component.blocks.length * CalendarSettings.EventDragDayLength;
}
else {
// Get the start (first quarter) from the event's first block
// Compute overall length
this.start = block.component.blocks[0].start;
this.duration = _.sumBy(block.component.blocks, function(b) {
return b.length;
var delta, currentDayNumber;
currentDayNumber = b.dayNumber;
if (prevDayNumber < 0)
delta = 0;
else
delta = currentDayNumber - prevDayNumber - 1;
prevDayNumber = currentDayNumber;
return b.length + delta * CalendarSettings.EventDragDayLength;
});
}
// Get the dayNumber from the event's first block
this.dayNumber = block.component.blocks[0].dayNumber;
},
initFromCalendar: function(calendarNumber) {
@ -411,13 +424,17 @@
// Compute delta wrt to position of mouse at dragstart on the day/quarter grid
var delta = this.currentViewCoordinates.getDelta(this.originalViewCoordinates);
var deltaQuarters = delta.x * CalendarSettings.EventDragDayLength + delta.y;
var deltaQuarters = delta.days * CalendarSettings.EventDragDayLength + delta.y;
$log.debug('quarters delta ' + deltaQuarters);
if (angular.isUndefined(this.originalEventCoordinates.start)) {
this.originalEventCoordinates.dayNumber = this.originalViewCoordinates.x;
// Creating new appointment from DnD
this.originalEventCoordinates.dayNumber = Calendar.$view.dayNumbers[this.originalViewCoordinates.x];
this.originalEventCoordinates.start = this.originalViewCoordinates.y;
}
else if (this.originalEventCoordinates.dayNumber < 0) {
this.originalEventCoordinates.dayNumber = Calendar.$view.dayNumbers[scope.block.component.blocks[0].dayIndex];
}
// if (currentView == "multicolumndayview")
// this._updateMulticolumnViewDayNumber_SEDGC();
// else
@ -461,12 +478,9 @@
else if (this.currentEventCoordinates.start >= CalendarSettings.EventDragDayLength) {
deltaDays = Math.floor(this.currentEventCoordinates.start / CalendarSettings.EventDragDayLength);
this.currentEventCoordinates.start -= deltaDays * CalendarSettings.EventDragDayLength;
// This dayNumber needs to be updated with the calendar number.
// if (currentView == "multicolumndayview")
// this._updateMulticolumnViewDayNumber_SEDGC();
this.currentEventCoordinates.dayNumber += deltaDays;
}
$log.debug('event coordinates ' + JSON.stringify(this.currentEventCoordinates));
$rootScope.$emit('calendar:drag');
},

View File

@ -155,6 +155,7 @@ $quarter_height: 10px;
// Contains the toggle button to collapse/expand the toolbar
.allDaysView--sidenav {
border-bottom: 1px solid sg-color($sogoPaper, 300);
min-width: $hours_margin;
width: $hours_margin;
.md-icon-button {
position: absolute;