diff --git a/NEWS b/NEWS index 4e224c610..41654e1d2 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Enhancements - [eas] use the preferred email identity in EAS if valid (#3698) - [eas] handle inline attachments during EAS content generation - [web] all batch operations can now be performed on selected messages in advanced search mode + - [web] add date picker to change date, week, or month of current Calendar view - [oc] better handling of nested attachments with OpenChange Bug fixes diff --git a/UI/Templates/SchedulerUI/UIxCalDayView.wox b/UI/Templates/SchedulerUI/UIxCalDayView.wox index 2a2a2d798..02dd6ea58 100644 --- a/UI/Templates/SchedulerUI/UIxCalDayView.wox +++ b/UI/Templates/SchedulerUI/UIxCalDayView.wox @@ -9,21 +9,27 @@ - chevron_left - chevron_left + chevron_right -
- chevron_right + +
+ - - today + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink"> + chevron_left + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink">chevron_left chevron_right -
- chevron_right + +
+ - - today + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink"> +
chevron_left + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink">chevron_left chevron_right -
- - - today + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink">chevron_right + +
+ +
chevron_left + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink">chevron_left chevron_right -
- chevron_right + +
+ - - today + ng-click="calendar.changeDate($event)" + md-no-ink="md-no-ink"> +
+ */ + function sgDatepickerReadonlyInput() { + return { + link: postLink, + require: 'mdDatepicker', + restrict: 'A' + }; + + function postLink(scope, element, attrs, datepickerCtrl) { + function getInput() { + return element.find('input').eq(0); + } + + // We need to wait for the autocomplete directive to be compiled + var listener = scope.$watch(getInput, function (input) { + if (input.length) { + listener(); // self release + input.prop('disabled', true); + input.parent().addClass('sg-datepicker-readonly-input-container'); + } + }); + } + } + + angular + .module('SOGo.Common') + .directive('sgDatepickerReadonlyInput', sgDatepickerReadonlyInput); +})(); diff --git a/UI/WebServerResources/js/Common/utils.js b/UI/WebServerResources/js/Common/utils.js index 839565b1c..9523ea934 100644 --- a/UI/WebServerResources/js/Common/utils.js +++ b/UI/WebServerResources/js/Common/utils.js @@ -299,6 +299,8 @@ Date.prototype.addDays = function(nbrDays) { milliSeconds = this.getTime() + dstOffset*60*1000; this.setTime(milliSeconds); } + + return this; }; Date.prototype.addHours = function(nbrHours) { @@ -323,6 +325,56 @@ Date.prototype.beginOfDay = function() { return beginOfDay; }; +Date.prototype.firstWeekOfYearForDate = function(localeProvider) { + var firstWeekRule, dayOfWeek, januaryFirst, firstWeek; + + firstWeekRule = localeProvider.firstWeekOfYear; + + januaryFirst = new Date(this.getTime()); + januaryFirst.setMonth(0); + januaryFirst.setDate(1); + dayOfWeek = januaryFirst.getDay(); + + if (firstWeekRule == 'First4DayWeek') { + if ((dayOfWeek + localeProvider.firstDayOfWeek) % 7 < 4) + firstWeek = januaryFirst.beginOfWeek(localeProvider.firstDayOfWeek); + else + firstWeek = januaryFirst.addDays(7).beginOfWeek(localeProvider.firstDayOfWeek); + } + else if (firstWeekRule == 'FirstFullWeek') { + if (dayOfWeek === 0) + firstWeek = januaryFirst.beginOfWeek(localeProvider.firstDayOfWeek); + else + firstWeek = januaryFirst.addDays(7).beginOfWeek(localeProvider.firstDayOfWeek); + } + else { + firstWeek = januaryFirst.beginOfWeek(localeProvider.firstDayOfWeek); + } + + return firstWeek; +}; + +Date.prototype.getWeek = function(localeProvider) { + var firstWeek, previousWeek, weekNumber; + + firstWeek = this.firstWeekOfYearForDate(localeProvider); + if (firstWeek.getTime() < this.getTime()) { + weekNumber = 1 + Math.floor((this.getTime() - firstWeek.getTime()) / (86400000 * 7)); + } + else + { + // Date is within the last week of the previous year; + // Compute the previous week number to find the week number of the requested date. + // The number will either be 52 or 53. + previousWeek = new Date(this.getTime()); + previousWeek.addDays(-7); + firstWeek = previousWeek.firstWeekOfYearForDate(localeProvider); + weekNumber = 2 + Math.floor((previousWeek.getTime() - firstWeek.getTime()) / (86400000 * 7)); + } + + return weekNumber; +}; + Date.prototype.beginOfWeek = function(firstDayOfWeek) { var offset = firstDayOfWeek - this.getDay(); if (offset > 0) @@ -375,7 +427,7 @@ Date.prototype.getHourString = function() { Date.prototype.format = function(localeProvider, format) { var separators, parts, i, max, date = [], - validParts = /%[deaAmbByYHIMp]/g, + validParts = /%[deaAmbByYUHIMp]/g, val = { '%d': this.getDate(), // day of month (e.g., 01) '%e': this.getDate(), // day of month, space padded @@ -386,6 +438,7 @@ Date.prototype.format = function(localeProvider, format) { '%B': localeProvider.months[this.getMonth()], // locale's full month name (e.g., January) '%y': this.getFullYear().toString().substring(2), // last two digits of year (00..99) '%Y': this.getFullYear(), // year + '%U': this.getWeek(localeProvider), // week of the year '%H': this.getHours(), // hour (00..23) '%M': this.getMinutes() }; // minute (00..59) val['%I'] = val['%H'] > 12 ? val['%H'] % 12 : val['%H']; // hour (01..12) diff --git a/UI/WebServerResources/js/Preferences/Preferences.service.js b/UI/WebServerResources/js/Preferences/Preferences.service.js index 4a6f7e18b..f9b488932 100644 --- a/UI/WebServerResources/js/Preferences/Preferences.service.js +++ b/UI/WebServerResources/js/Preferences/Preferences.service.js @@ -91,6 +91,10 @@ // Configure date locale _this.$mdDateLocaleProvider = Preferences.$mdDateLocaleProvider; angular.extend(_this.$mdDateLocaleProvider, data.locale); + angular.extend(_this.$mdDateLocaleProvider, { + firstDayOfWeek: data.SOGoFirstDayOfWeek, + firstWeekOfYear: data.SOGoFirstWeekOfYear + }); _this.$mdDateLocaleProvider.firstDayOfWeek = parseInt(data.SOGoFirstDayOfWeek); _this.$mdDateLocaleProvider.weekNumberFormatter = function(weekNumber) { return l('Week %d', weekNumber); @@ -101,7 +105,7 @@ return dateString? dateString.parseDate(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : new Date(NaN); }; _this.$mdDateLocaleProvider.formatDate = function(date) { - return date? date.format(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : ''; + return date? date.format(_this.$mdDateLocaleProvider, date.$dateFormat || data.SOGoShortDateFormat) : ''; }; _this.$mdDateLocaleProvider.parseTime = function(timeString) { return timeString? timeString.parseDate(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : new Date(NaN); diff --git a/UI/WebServerResources/js/Scheduler/CalendarController.js b/UI/WebServerResources/js/Scheduler/CalendarController.js index ac33b4fae..50321df8a 100644 --- a/UI/WebServerResources/js/Scheduler/CalendarController.js +++ b/UI/WebServerResources/js/Scheduler/CalendarController.js @@ -6,25 +6,46 @@ /** * @ngInject */ - CalendarController.$inject = ['$scope', '$rootScope', '$state', '$stateParams', 'Calendar', 'Component', 'stateEventsBlocks']; - function CalendarController($scope, $rootScope, $state, $stateParams, Calendar, Component, stateEventsBlocks) { + CalendarController.$inject = ['$scope', '$rootScope', '$state', '$stateParams', 'Calendar', 'Component', 'Preferences', 'stateEventsBlocks']; + function CalendarController($scope, $rootScope, $state, $stateParams, Calendar, Component, Preferences, stateEventsBlocks) { var vm = this, deregisterCalendarsList; // Make the toolbar state of all-day events persistent if (angular.isUndefined(CalendarController.expandedAllDays)) CalendarController.expandedAllDays = false; + vm.selectedDate = $stateParams.day.asDate(); vm.expandedAllDays = CalendarController.expandedAllDays; vm.toggleAllDays = toggleAllDays; vm.views = stateEventsBlocks; vm.changeDate = changeDate; vm.changeView = changeView; + Preferences.ready().then(function() { + _formatDate(vm.selectedDate); + }); + // Refresh current view when the list of calendars is modified deregisterCalendarsList = $rootScope.$on('calendars:list', updateView); + // Destroy event listener when the controller is being deactivated $scope.$on('$destroy', deregisterCalendarsList); + function _formatDate(date) { + if ($stateParams.view == 'month') { + date.setDate(1); + date.setHours(12); + date.$dateFormat = '%B %Y'; + } + else if ($stateParams.view == 'week') { + date.setTime(date.beginOfWeek(Preferences.defaults.SOGoFirstDayOfWeek).getTime()); + date.$dateFormat = l('Week %d').replace('%d', '%U'); + } + else { + date.$dateFormat = '%A'; + } + } + // Expand or collapse all-day events function toggleAllDays() { CalendarController.expandedAllDays = !CalendarController.expandedAllDays; @@ -46,8 +67,10 @@ } // Change calendar's date - function changeDate($event) { - var date = angular.element($event.currentTarget).attr('date'); + function changeDate($event, newDate) { + var date = newDate? newDate.getDayString() : angular.element($event.currentTarget).attr('date'); + if (newDate) + _formatDate(newDate); $state.go('calendars.view', { day: date }); } diff --git a/UI/WebServerResources/js/Scheduler/CalendarsController.js b/UI/WebServerResources/js/Scheduler/CalendarsController.js index 680fe36d1..7383c96be 100644 --- a/UI/WebServerResources/js/Scheduler/CalendarsController.js +++ b/UI/WebServerResources/js/Scheduler/CalendarsController.js @@ -25,7 +25,7 @@ vm.showLinks = showLinks; vm.showProperties = showProperties; vm.subscribeToFolder = subscribeToFolder; - vm.today = today; + // vm.today = today; vm.filter = { name: '' }; vm.toggleSortableMode = toggleSortableMode; @@ -412,14 +412,14 @@ }); } - function today() { - var fragments = $window.location.hash.split('/'), - state = fragments[1], - view = fragments[2], - now = new Date(), - path = ['#', state, view, now.getDayString()]; - $window.location = path.join('/'); - } + // function today() { + // var fragments = $window.location.hash.split('/'), + // state = fragments[1], + // view = fragments[2], + // now = new Date(), + // path = ['#', state, view, now.getDayString()]; + // $window.location = path.join('/'); + // } } angular diff --git a/UI/WebServerResources/scss/components/datepicker/datePicker.scss b/UI/WebServerResources/scss/components/datepicker/datePicker.scss index b28ff445d..354a43063 100644 --- a/UI/WebServerResources/scss/components/datepicker/datePicker.scss +++ b/UI/WebServerResources/scss/components/datepicker/datePicker.scss @@ -12,3 +12,6 @@ margin-left: 0; } +.sg-datepicker-readonly-input-container { + border-bottom: 0; +} \ No newline at end of file