diff --git a/NEWS b/NEWS
index 695bf3c68..246cf8ac1 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Enhancements
- [web] now able to copy/move events and also duplicate them (#3196)
- [web] improve preferences validation and check for unsaved changes
- [web] display events and tasks priorities in list and day/week views
+ - [web] style events depending on the user participation state
Bug fixes
- [web] fixed missing columns in SELECT statements (PostgreSQL)
diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m
index 2855439b2..0f7310315 100644
--- a/UI/Scheduler/UIxCalListingActions.m
+++ b/UI/Scheduler/UIxCalListingActions.m
@@ -868,7 +868,7 @@ static inline void _feedBlockWithMonthBasedData (NSMutableDictionary *block, uns
number: (NSNumber *) number
onDay: (unsigned int) dayStart
recurrenceTime: (unsigned int) recurrenceTime
- userState: (iCalPersonPartStat) userState
+ userState: (NSString *) userState
{
NSMutableDictionary *block;
@@ -882,22 +882,22 @@ static inline void _feedBlockWithMonthBasedData (NSMutableDictionary *block, uns
if (recurrenceTime)
[block setObject: [NSNumber numberWithInt: recurrenceTime]
forKey: @"recurrenceTime"];
- if (userState != iCalPersonPartStatOther)
- [block setObject: [NSNumber numberWithInt: userState]
+ if (userState != nil)
+ [block setObject: userState
forKey: @"userState"];
return block;
}
-static inline iCalPersonPartStat _userStateInEvent (NSArray *event)
+static inline NSString* _userStateInEvent (NSArray *event)
{
unsigned int count, max;
- iCalPersonPartStat state;
+ NSString *state;
NSArray *participants, *states;
SOGoUser *user;
participants = nil;
- state = iCalPersonPartStatOther;
+ state = nil;
participants = [event objectAtIndex: eventPartMailsIndex];
@@ -909,10 +909,10 @@ static inline iCalPersonPartStat _userStateInEvent (NSArray *event)
max = [participants count];
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
roles: nil];
- while (state == iCalPersonPartStatOther && count < max)
+ while (state == nil && count < max)
{
if ([user hasEmail: [participants objectAtIndex: count]])
- state = [[states objectAtIndex: count] intValue];
+ state = [states objectAtIndex: count];
else
count++;
}
@@ -929,7 +929,7 @@ static inline iCalPersonPartStat _userStateInEvent (NSArray *event)
eventEnd, computedEventEnd, offset, recurrenceTime, swap;
NSMutableArray *currentDay;
NSMutableDictionary *eventBlock;
- iCalPersonPartStat userState;
+ NSString *userState;
eventStart = [[event objectAtIndex: eventStartDateIndex] intValue];
if (eventStart < 0)
diff --git a/UI/WebServerResources/js/Common/sgFolderStylesheet.directive.js b/UI/WebServerResources/js/Common/sgFolderStylesheet.directive.js
index a84bb02a1..50e540ad7 100644
--- a/UI/WebServerResources/js/Common/sgFolderStylesheet.directive.js
+++ b/UI/WebServerResources/js/Common/sgFolderStylesheet.directive.js
@@ -55,6 +55,9 @@
' .bdr-folder{{ cssCtrl.ngModel.id }} {',
' border-color: {{ cssCtrl.ngModel.color }} !important;',
' }',
+ ' .contrast-bdr-folder{{ cssCtrl.ngModel.id }} {',
+ ' border-color: {{ cssCtrl.contrast(cssCtrl.ngModel.color) }} !important;',
+ ' }',
/* Checkbox color */
' .checkbox-folder{{ cssCtrl.ngModel.id }} ._md-icon {',
' background-color: {{ cssCtrl.ngModel.color }} !important;',
@@ -69,7 +72,7 @@
function sgFolderStylesheetController() {
var vm = this;
- vm.contrast = contrast;
+ vm.contrast = contrast; // defined in Common/utils.js
}
}
diff --git a/UI/WebServerResources/js/Scheduler/sgCalendarDayBlock.directive.js b/UI/WebServerResources/js/Scheduler/sgCalendarDayBlock.directive.js
index dda675140..2d02589ef 100644
--- a/UI/WebServerResources/js/Scheduler/sgCalendarDayBlock.directive.js
+++ b/UI/WebServerResources/js/Scheduler/sgCalendarDayBlock.directive.js
@@ -28,73 +28,91 @@
clickBlock: '&sgClick'
},
replace: true,
- template: [
+ template: template,
+ link: link
+ };
+
+ function template(tElem, tAttrs) {
+ var p = _.has(tAttrs, 'sgCalendarGhost')? '' : '::';
+
+ return [
'
',
'
',
// Categories color stripes
- '
',
'
',
- '
{{block.component.c_priority}}',
- ' {{ block.component.summary }}',
+ // Priority
+ '
{{'+p+'block.component.c_priority}}',
+ // Summary
+ ' {{ '+p+'block.component.summary }}',
'
',
// Component is reccurent
- ' ',
+ ' ',
// Component has an alarm
- ' ',
+ ' ',
// Component is confidential
- ' ',
+ ' ',
// Component is private
- ' ',
+ ' ',
' ',
// Location
- '
',
- '
place {{block.component.c_location}}',
+ '
',
+ ' place {{'+p+'block.component.c_location}}',
'
',
'
',
'
',
'
{{ block.startHour }}
',
'
{{ block.endHour }}
',
'
'
- ].join(''),
- link: link
- };
+ ].join('');
+ }
function link(scope, iElement, attrs) {
var pc, left, right;
- // Compute overlapping (2%)
- pc = 100 / scope.block.siblings;
- left = scope.block.position * pc;
- right = 100 - (scope.block.position + 1) * pc;
- if (pc < 100) {
- if (left > 0)
- left -= 2;
- if (right > 0)
- right -= 2;
+ if (!_.has(attrs, 'sgCalendarGhost')) {
+
+ // Compute overlapping (2%)
+ pc = 100 / scope.block.siblings;
+ left = scope.block.position * pc;
+ right = 100 - (scope.block.position + 1) * pc;
+ if (pc < 100) {
+ if (left > 0)
+ left -= 2;
+ if (right > 0)
+ right -= 2;
+ }
+
+ // Add some padding (2%)
+ if (left === 0)
+ left = 2;
+ if (right === 0)
+ right = 2;
+
+ // Set position
+ iElement.css('left', left + '%');
+ iElement.css('right', right + '%');
+ if (!scope.block.component || !scope.block.component.c_isallday) {
+ iElement.addClass('starts' + scope.block.start);
+ iElement.addClass('lasts' + scope.block.length);
+ }
+
+ // Add class for user's participation state
+ if (scope.block.userState)
+ iElement.addClass('sg-event--' + scope.block.userState);
+
+ // Set background color
+ if (scope.block.component) {
+ iElement.addClass('bg-folder' + scope.block.component.pid);
+ iElement.addClass('contrast-bdr-folder' + scope.block.component.pid);
+ }
+
}
-
- // Add some padding (2%)
- if (left === 0)
- left = 2;
- if (right === 0)
- right = 2;
-
- // Set position
- iElement.css('left', left + '%');
- iElement.css('right', right + '%');
- if (!scope.block.component || !scope.block.component.c_isallday) {
- iElement.addClass('starts' + scope.block.start);
- iElement.addClass('lasts' + scope.block.length);
- }
-
- // Set background color
- if (scope.block.component)
- iElement.addClass('bg-folder' + scope.block.component.pid);
}
}
diff --git a/UI/WebServerResources/js/Scheduler/sgCalendarGhost.directive.js b/UI/WebServerResources/js/Scheduler/sgCalendarGhost.directive.js
index 37142fbf6..f55077989 100644
--- a/UI/WebServerResources/js/Scheduler/sgCalendarGhost.directive.js
+++ b/UI/WebServerResources/js/Scheduler/sgCalendarGhost.directive.js
@@ -46,7 +46,7 @@
});
function initGhost() {
- var pid, calendarData;
+ var pid, calendarData, userState;
// Expose ghost block to the scope
scope.block = Component.$ghost;
@@ -62,6 +62,11 @@
if (!pid)
pid = scope.block.component.pid;
+ // Add class for user's participation state
+ userState = scope.block.component.blocks[0].userState;
+ if (userState)
+ iElement.addClass('sg-event--' + userState);
+
// Set background color
iElement.addClass('bg-folder' + pid);
}
diff --git a/UI/WebServerResources/js/Scheduler/sgCalendarMonthEvent.directive.js b/UI/WebServerResources/js/Scheduler/sgCalendarMonthEvent.directive.js
index cae44a2a3..153f4a922 100644
--- a/UI/WebServerResources/js/Scheduler/sgCalendarMonthEvent.directive.js
+++ b/UI/WebServerResources/js/Scheduler/sgCalendarMonthEvent.directive.js
@@ -23,31 +23,49 @@
clickBlock: '&sgClick'
},
replace: true,
- template: [
+ template: template,
+ link: link
+ };
+
+ function template(tElem, tAttrs) {
+ var p = _.has(tAttrs, 'sgCalendarGhost')? '' : '::';
+
+ return [
'
',
- ' {{ block.starthour }}',
- ' {{ block.component.summary }}',
+ ' {{ '+p+'block.starthour }}',
+ // Priority
+ ' {{'+p+'block.component.c_priority}}',
+ // Summary
+ ' {{ '+p+'block.component.summary }}',
' ',
// Component is reccurent
- ' ',
+ ' ',
// Component has an alarm
- ' ',
+ ' ',
// Component is confidential
- ' ',
+ ' ',
// Component is private
- ' ',
+ ' ',
' ',
'
'
- ].join(''),
- link: link
- };
+ ].join('');
+ }
function link(scope, iElement, attrs) {
- if (scope.block.component)
- iElement.addClass('bg-folder' + scope.block.component.pid);
+ if (!_.has(attrs, 'sgCalendarGhost')) {
+
+ // Add class for user's participation state
+ if (scope.block.userState)
+ iElement.addClass('sg-event--' + scope.block.userState);
+
+ // Set background color
+ if (scope.block.component)
+ iElement.addClass('bg-folder' + scope.block.component.pid);
+
+ }
}
}
diff --git a/UI/WebServerResources/scss/views/SchedulerUI.scss b/UI/WebServerResources/scss/views/SchedulerUI.scss
index 2cb8d4117..19c603267 100644
--- a/UI/WebServerResources/scss/views/SchedulerUI.scss
+++ b/UI/WebServerResources/scss/views/SchedulerUI.scss
@@ -290,6 +290,23 @@ $quarter_height: 10px;
}
}
+ // User participation status is "needs action"
+ &--needs-action {
+ border-width: 1px;
+ border-style: dashed;
+ opacity: 0.7;
+ }
+
+ // User participation status is "tentative"
+ &--tentative {
+ opacity: 0.7;
+ }
+
+ // User has declined the invitation
+ &--declined {
+ opacity: 0.4;
+ }
+
.eventInside {
overflow: hidden;
}