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; }