diff --git a/awx/ui/client/src/scheduler/factories/r-rule-to-api.factory.js b/awx/ui/client/src/scheduler/factories/r-rule-to-api.factory.js index 1e3b251d8a..7698ed9ea5 100644 --- a/awx/ui/client/src/scheduler/factories/r-rule-to-api.factory.js +++ b/awx/ui/client/src/scheduler/factories/r-rule-to-api.factory.js @@ -1,9 +1,11 @@ export default function RRuleToAPI() { - return function(rrule) { - var response; - response = rrule.replace(/(^.*(?=DTSTART))(DTSTART=.*?;)(.*$)/, function(str, p1, p2, p3) { - return p2.replace(/\;/,'').replace(/=/,':') + ' ' + 'RRULE:' + p1 + p3; + return function(rrule, scope) { + let localTime = scope.schedulerLocalTime; + let timeZone = scope.schedulerTimeZone.name; + + let response = rrule.replace(/(^.*(?=DTSTART))(DTSTART.*?)(=.*?;)(.*$)/, (str, p1, p2, p3, p4) => { + return p2 + ';TZID=' + timeZone + ':' + localTime + ' ' + 'RRULE:' + p4; }); return response; }; diff --git a/awx/ui/client/src/scheduler/factories/schedule-post.factory.js b/awx/ui/client/src/scheduler/factories/schedule-post.factory.js index 3033219275..8911b51265 100644 --- a/awx/ui/client/src/scheduler/factories/schedule-post.factory.js +++ b/awx/ui/client/src/scheduler/factories/schedule-post.factory.js @@ -14,7 +14,7 @@ export default newSchedule = scheduler.getValue(); rrule = scheduler.getRRule(); schedule.name = newSchedule.name; - schedule.rrule = RRuleToAPI(rrule.toString()); + schedule.rrule = RRuleToAPI(rrule.toString(), scope); schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText(); if (scope.isFactCleanup) { diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index d7f82c32ca..661a708dd6 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -4,14 +4,14 @@ * All Rights Reserved *************************************************/ -export default ['$filter', '$state', '$stateParams', 'Wait', +export default ['$filter', '$state', '$stateParams', '$http', 'Wait', '$scope', '$rootScope', 'CreateSelect2', 'ParseTypeChange', 'GetBasePath', 'Rest', 'ParentObject', 'JobTemplateModel', '$q', 'Empty', 'SchedulePost', - 'ProcessErrors', 'SchedulerInit', '$location', 'PromptService', - function($filter, $state, $stateParams, Wait, + 'ProcessErrors', 'SchedulerInit', '$location', 'PromptService', 'RRuleToAPI', 'moment', + function($filter, $state, $stateParams, $http, Wait, $scope, $rootScope, CreateSelect2, ParseTypeChange, GetBasePath, Rest, ParentObject, JobTemplate, $q, Empty, SchedulePost, - ProcessErrors, SchedulerInit, $location, PromptService) { + ProcessErrors, SchedulerInit, $location, PromptService, RRuleToAPI, moment) { var base = $scope.base || $location.path().replace(/^\//, '').split('/')[0], scheduler, @@ -283,6 +283,16 @@ export default ['$filter', '$state', '$stateParams', 'Wait', Wait('start'); $('#form-container').empty(); scheduler = SchedulerInit({ scope: $scope, requireFutureStartTime: false }); + let timeZonesAPI = () => { + return $http.get(`/api/v2/schedules/zoneinfo/`); + }; + // set API timezones to scheduler object + timeZonesAPI().then(({data}) => { + scheduler.scope.timeZones = data; + scheduler.scope.schedulerTimeZone = _.find(data, (zone) => { + return zone.name === scheduler.scope.current_timezone.name; + }); + }); if($scope.schedulerUTCTime) { // The UTC time is already set processSchedulerEndDt(); @@ -298,6 +308,30 @@ export default ['$filter', '$state', '$stateParams', 'Wait', } }); } + + let previewList = _.debounce(function(req) { + $http.post('/api/v2/schedules/preview/', {'rrule': req}) + .then(({data}) => { + $scope.preview_list = data; + let parsePreviewList = (tz) => { + return data[tz].map(function(date) { + date = date.replace(/Z/, ''); + return moment.parseZone(date).format("MM-DD-YYYY HH:mm:ss"); + }); + }; + for (let tz in data) { + $scope.preview_list.isEmpty = data[tz].length === 0; + $scope.preview_list[tz] = parsePreviewList(tz); + } + }); + }, 300); + + $scope.$on("setPreviewPane", (event) => { + let rrule = event.currentScope.rrule.toString(); + let req = RRuleToAPI(rrule, $scope); + previewList(req); + }); + scheduler.inject('form-container', false); scheduler.injectDetail('occurrences', false); scheduler.clear(); @@ -306,7 +340,6 @@ export default ['$filter', '$state', '$stateParams', 'Wait', $scope.$on("formUpdated", function() { $rootScope.$broadcast("loadSchedulerDetailPane"); }); - $scope.$watchGroup(["schedulerName", "schedulerStartDt", "schedulerStartHour", diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index a762b06072..b0d8adcb83 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -1,9 +1,9 @@ -export default ['$filter', '$state', '$stateParams', 'Wait', '$scope', -'$rootScope', 'CreateSelect2', 'ParseTypeChange', 'ParentObject', 'ProcessErrors', 'Rest', -'GetBasePath', 'SchedulerInit', 'SchedulePost', 'JobTemplateModel', '$q', 'Empty', 'PromptService', -function($filter, $state, $stateParams, Wait, $scope, - $rootScope, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest, - GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService) { +export default ['$filter', '$state', '$stateParams', 'Wait', '$scope', 'moment', +'$rootScope', '$http', 'CreateSelect2', 'ParseTypeChange', 'ParentObject', 'ProcessErrors', 'Rest', +'GetBasePath', 'SchedulerInit', 'SchedulePost', 'JobTemplateModel', '$q', 'Empty', 'PromptService', 'RRuleToAPI', +function($filter, $state, $stateParams, Wait, $scope, moment, + $rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest, + GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI) { let schedule, scheduler; @@ -85,6 +85,29 @@ function($filter, $state, $stateParams, Wait, $scope, callSelect2(); }); + let previewList = _.debounce(function(req) { + $http.post('/api/v2/schedules/preview/', {'rrule': req}) + .then(({data}) => { + $scope.preview_list = data; + let parsePreviewList = (tz) => { + return data[tz].map(function(date) { + date = date.replace(/Z/, ''); + return moment.parseZone(date).format("MM-DD-YYYY HH:mm:ss"); + }); + }; + for (let tz in data) { + $scope.preview_list.isEmpty = data[tz].length === 0; + $scope.preview_list[tz] = parsePreviewList(tz); + } + }); + }, 300); + + $scope.$on("setPreviewPane", (event) => { + let rrule = event.currentScope.rrule.toString(); + let req = RRuleToAPI(rrule, $scope); + previewList(req); + }); + Wait('start'); // Get the existing record @@ -111,6 +134,14 @@ function($filter, $state, $stateParams, Wait, $scope, $('#form-container').empty(); scheduler = SchedulerInit({ scope: $scope, requireFutureStartTime: false }); + + $http.get('/api/v2/schedules/zoneinfo/').then(({data}) => { + scheduler.scope.timeZones = data; + scheduler.scope.schedulerTimeZone = _.find(data, function(x) { + let tz = $scope.schedule_obj.rrule.match(/TZID=\s*(.*?)\s*:/)[1]; + return x.name === tz; + }); + }); scheduler.inject('form-container', false); scheduler.injectDetail('occurrences', false); diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html index b06b1ea7eb..bea1d9eade 100644 --- a/awx/ui/client/src/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -578,7 +578,7 @@

+ ng-show="schedulerIsValid && !preview_list.isEmpty"> @@ -623,15 +623,15 @@