From 068b32b1b44689eb375dd7583f2f2174ac8264ae Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 2 Feb 2016 10:56:10 -0500 Subject: [PATCH] updates to scheduler - form and perspective next scheduler runs are in single list form view for projects and job templates - submit/cancel/x at top of form project panel work --- awx/ui/client/src/app.js | 34 +- awx/ui/client/src/controllers/JobTemplates.js | 2 +- awx/ui/client/src/helpers/Schedules.js | 74 +---- awx/ui/client/src/lists/Schedules.js | 1 + awx/ui/client/src/scheduler/main.js | 84 +++++ .../src/scheduler/scheduler.controller.js | 95 ++++++ .../src/scheduler/scheduler.partial.html | 6 + .../src/scheduler/schedulerAdd.controller.js | 32 ++ .../src/scheduler/schedulerEdit.controller.js | 33 ++ .../src/scheduler/schedulerForm.partial.html | 307 ++++++++++++++++++ .../src/shared/stateExtender.provider.js | 3 +- 11 files changed, 579 insertions(+), 92 deletions(-) create mode 100644 awx/ui/client/src/scheduler/main.js create mode 100644 awx/ui/client/src/scheduler/scheduler.controller.js create mode 100644 awx/ui/client/src/scheduler/scheduler.partial.html create mode 100644 awx/ui/client/src/scheduler/schedulerAdd.controller.js create mode 100644 awx/ui/client/src/scheduler/schedulerEdit.controller.js create mode 100644 awx/ui/client/src/scheduler/schedulerForm.partial.html diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 40f874cf6b..00b19eda61 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -68,6 +68,7 @@ import './job-templates/main'; import './shared/features/main'; import './login/authenticationServices/pendo/ng-pendo'; import footer from './footer/main'; +import scheduler from './scheduler/main'; /*#if DEBUG#*/ import {__deferLoadIfEnabled} from './debug'; @@ -184,6 +185,7 @@ var tower = angular.module('Tower', [ 'pendolytics', 'ui.router', 'ncy-angular-breadcrumb', + 'scheduler' ]) .constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/') @@ -412,22 +414,6 @@ var tower = angular.module('Tower', [ }] } }). - - state('jobTemplateSchedules', { - url: '/job_templates/:id/schedules', - templateUrl: urlPrefix + 'partials/schedule_detail.html', - controller: ScheduleEditController, - data: { - activityStream: true, - activityStreamTarget: 'schedule' - }, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - state('projects', { url: '/projects', templateUrl: urlPrefix + 'partials/projects.html', @@ -474,22 +460,6 @@ var tower = angular.module('Tower', [ }] } }). - - state('projectSchedules', { - url: '/projects/:id/schedules', - templateUrl: urlPrefix + 'partials/schedule_detail.html', - controller: ScheduleEditController, - data: { - activityStream: true, - activityStreamTarget: 'schedule' - }, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - state('projectOrganizations', { url: '/projects/:project_id/organizations', templateUrl: urlPrefix + 'partials/projects.html', diff --git a/awx/ui/client/src/controllers/JobTemplates.js b/awx/ui/client/src/controllers/JobTemplates.js index b8b37c5a72..6c1969ea53 100644 --- a/awx/ui/client/src/controllers/JobTemplates.js +++ b/awx/ui/client/src/controllers/JobTemplates.js @@ -238,7 +238,7 @@ export function JobTemplatesList($scope, $rootScope, $location, $log, }; $scope.scheduleJob = function (id) { - $state.transitionTo('jobTemplateSchedules', {id: id}); + $state.go('jobTemplateSchedules', {id: id}); } } diff --git a/awx/ui/client/src/helpers/Schedules.js b/awx/ui/client/src/helpers/Schedules.js index 59af9229a2..f73a7ee042 100644 --- a/awx/ui/client/src/helpers/Schedules.js +++ b/awx/ui/client/src/helpers/Schedules.js @@ -20,7 +20,7 @@ export default angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ModalDialog', 'GeneratorHelpers']) - .factory('ShowSchedulerModal', ['Wait', 'CreateDialog', function(Wait, CreateDialog) { + .factory('ShowSchedulerModal', ['$rootScope', 'Wait', 'CreateDialog', function($rootScope, Wait, CreateDialog) { return function(params) { // Set modal dimensions based on viewport width @@ -50,28 +50,7 @@ export default "class": "btn btn-primary", "id": "schedule-save-button" }]; - - CreateDialog({ - id: 'scheduler-modal-dialog', - scope: scope, - buttons: buttons, - title: title, - width: 700, - height: 725, - minWidth: 400, - onClose: function() { - $('#scheduler-modal-dialog #form-container').empty(); - }, - onOpen: function() { - Wait('stop'); - $('#scheduler-tabs a:first').tab('show'); - $('#schedulerName').focus(); - $('#rrule_nlp_description').dblclick(function() { - setTimeout(function() { scope.$apply(function() { scope.showRRule = (scope.showRRule) ? false : true; }); }, 100); - }); - }, - callback: callback - }); + $rootScope.$broadcast("ScheduleFormCreated", scope); }; }]) @@ -175,7 +154,11 @@ export default } schedule.rrule = schedule.rrule.replace(/ RRULE:/,';'); schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART='); - ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Edit Schedule' }); + scope.$on("htmlDetailReady", function() { + scheduler.setRRule(schedule.rrule); + scheduler.setName(schedule.name); + ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Edit Schedule' }); + }); scope.showRRuleDetail = false; }); @@ -185,14 +168,12 @@ export default } scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) { Wait('stop'); - $('#scheduler-modal-dialog').dialog('close'); if (callback) { scope.$emit(callback, data); } }); scope.saveSchedule = function() { - $('#scheduler-tabs a:first').tab('show'); SchedulePost({ scope: scope, url: url, @@ -203,16 +184,6 @@ export default }); }; - - - $('#scheduler-tabs li a').on('shown.bs.tab', function(e) { - if ($(e.target).text() === 'Details') { - if (!scheduler.isValid()) { - $('#scheduler-tabs a:first').tab('show'); - } - } - }); - Wait('start'); // Get the existing record @@ -290,21 +261,15 @@ export default } } - if (scope.removeDialogReady) { - scope.removeDialogReady(); - } - scope.removeDialogReady = scope.$on('DialogReady', function() { - $('#scheduler-modal-dialog').dialog('open'); - $('#schedulerName').focus(); - }); - Wait('start'); $('#form-container').empty(); scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false }); scheduler.inject('form-container', false); scheduler.injectDetail('occurrences', false); scheduler.clear(); - ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Add Schedule' }); + scope.$on("htmlDetailReady", function() { + ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Add Schedule' }); + }); scope.showRRuleDetail = false; if (scope.removeScheduleSaved) { @@ -312,14 +277,12 @@ export default } scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) { Wait('stop'); - $('#scheduler-modal-dialog').dialog('close'); if (callback) { scope.$emit(callback, data); } }); scope.saveSchedule = function() { - $('#scheduler-tabs a:first').tab('show'); SchedulePost({ scope: scope, url: url, @@ -565,9 +528,9 @@ export default }]) - .factory('SchedulesControllerInit', ['$location', 'ToggleSchedule', + .factory('SchedulesControllerInit', ['$state', '$location', 'ToggleSchedule', 'DeleteSchedule', 'EditSchedule', 'AddSchedule', - function($location, ToggleSchedule, DeleteSchedule, EditSchedule, + function($state, $location, ToggleSchedule, DeleteSchedule, EditSchedule, AddSchedule) { return function(params) { var scope = params.scope, @@ -598,18 +561,13 @@ export default }; scope.editSchedule = function(id) { - EditSchedule({ - scope: scope, - id: id, - callback: 'SchedulesRefresh' - }); + var base = $state.current.name.split(".")[0]; + $state.go(base + ".edit", {schedule_id: id}); }; scope.addSchedule = function() { - AddSchedule({ - scope: scope, - callback: 'SchedulesRefresh' - }); + var base = $state.current.name.split(".")[0]; + $state.go(base + ".add", {passedScope: scope}); }; scope.refreshSchedules = function() { diff --git a/awx/ui/client/src/lists/Schedules.js b/awx/ui/client/src/lists/Schedules.js index 2e47d6a03d..c6bed3edfa 100644 --- a/awx/ui/client/src/lists/Schedules.js +++ b/awx/ui/client/src/lists/Schedules.js @@ -50,6 +50,7 @@ export default awToolTip: "Refresh the page", ngClick: "refreshSchedules()", actionClass: 'btn List-buttonDefault', + ngShow: "socketStatus == 'error'", buttonContent: 'REFRESH' }, add: { diff --git a/awx/ui/client/src/scheduler/main.js b/awx/ui/client/src/scheduler/main.js new file mode 100644 index 0000000000..46fa46d94e --- /dev/null +++ b/awx/ui/client/src/scheduler/main.js @@ -0,0 +1,84 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import controller from './scheduler.controller'; +import addController from './schedulerAdd.controller'; +import editController from './schedulerEdit.controller'; +import {templateUrl} from '../shared/template-url/template-url.factory'; + +export default + angular.module('scheduler', []) + .controller('schedulerController', controller) + .controller('schedulerAddController', addController) + .controller('schedulerEditController', editController) + .run(['$stateExtender', function($stateExtender) { + $stateExtender.addState({ + name: 'jobTemplateSchedules', + route: '/job_templates/:id/schedules', + templateUrl: templateUrl("scheduler/scheduler"), + controller: 'schedulerController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + $stateExtender.addState({ + name: 'jobTemplateSchedules.add', + route: '/add', + templateUrl: templateUrl("scheduler/schedulerForm"), + controller: 'schedulerAddController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + $stateExtender.addState({ + name: 'jobTemplateSchedules.edit', + route: '/:schedule_id', + templateUrl: templateUrl("scheduler/schedulerForm"), + controller: 'schedulerEditController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + $stateExtender.addState({ + name: 'projectSchedules', + route: '/projects/:id/schedules', + templateUrl: templateUrl("scheduler/scheduler"), + controller: 'schedulerController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + $stateExtender.addState({ + name: 'projectSchedules.add', + route: '/add', + templateUrl: templateUrl("scheduler/schedulerForm"), + controller: 'schedulerAddController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + $stateExtender.addState({ + name: 'projectSchedules.edit', + route: '/:schedule_id', + templateUrl: templateUrl("scheduler/schedulerForm"), + controller: 'schedulerEditController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } + }); + }]); diff --git a/awx/ui/client/src/scheduler/scheduler.controller.js b/awx/ui/client/src/scheduler/scheduler.controller.js new file mode 100644 index 0000000000..e014eae18b --- /dev/null +++ b/awx/ui/client/src/scheduler/scheduler.controller.js @@ -0,0 +1,95 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +/** + * @ngdoc function + * @name controllers.function:Schedules + * @description This controller's for schedules +*/ + + +export default [ + '$scope', '$compile', '$location', '$stateParams', 'SchedulesList', 'Rest', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', + 'GetBasePath', 'Wait', 'Find', 'LoadDialogPartial', 'LoadSchedulesScope', 'GetChoices', 'Stream', + function ($scope, $compile, $location, $stateParams, + SchedulesList, Rest, ProcessErrors, ReturnToCaller, ClearScope, + GetBasePath, Wait, Find, LoadDialogPartial, LoadSchedulesScope, GetChoices, + Stream) { + + ClearScope(); + + var base, e, id, url, parentObject; + + base = $location.path().replace(/^\//, '').split('/')[0]; + + if ($scope.removePostRefresh) { + $scope.removePostRefresh(); + } + $scope.removePostRefresh = $scope.$on('PostRefresh', function() { + var list = $scope.schedules; + list.forEach(function(element, idx) { + list[idx].play_tip = (element.enabled) ? 'Schedule is Active. Click to temporarily stop.' : 'Schedule is temporarily stopped. Click to activate.'; + }); + }); + + if ($scope.removeParentLoaded) { + $scope.removeParentLoaded(); + } + $scope.removeParentLoaded = $scope.$on('ParentLoaded', function() { + url += "schedules/"; + SchedulesList.well = true; + + // include name of item in listTitle + SchedulesList.listTitle = parentObject.name + "
Schedules"; + + LoadSchedulesScope({ + parent_scope: $scope, + scope: $scope, + list: SchedulesList, + id: 'schedule-list-target', + url: url, + pageSize: 20 + }); + }); + + + if ($scope.removeChoicesReady) { + $scope.removeChocesReady(); + } + $scope.removeChoicesReady = $scope.$on('choicesReady', function() { + // Load the parent object + id = $stateParams.id; + url = GetBasePath(base) + id + '/'; + Rest.setUrl(url); + Rest.get() + .success(function(data) { + parentObject = data; + $scope.$emit('ParentLoaded'); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + url + ' failed. GET returned: ' + status }); + }); + }); + + $scope.refreshJobs = function() { + $scope.search(SchedulesList.iterator); + }; + + $scope.showActivity = function () { + Stream({ scope: $scope }); + }; + + Wait('start'); + + GetChoices({ + scope: $scope, + url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json' + field: 'type', + variable: 'type_choices', + callback: 'choicesReady' + }); + }]; diff --git a/awx/ui/client/src/scheduler/scheduler.partial.html b/awx/ui/client/src/scheduler/scheduler.partial.html new file mode 100644 index 0000000000..b4b87d1f92 --- /dev/null +++ b/awx/ui/client/src/scheduler/scheduler.partial.html @@ -0,0 +1,6 @@ +
+
+
+
+ +
diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js new file mode 100644 index 0000000000..c04db19cab --- /dev/null +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -0,0 +1,32 @@ +export default ['$compile', '$state', '$stateParams', 'AddSchedule', 'Wait', '$scope', function($compile, $state, $stateParams, AddSchedule, Wait, $scope) { + $scope.$on("ScheduleFormCreated", function(e, scope) { + $scope.hideForm = false; + $scope = angular.extend($scope, scope); + $scope.$watchGroup(["schedulerStartDt", + "schedulerStartHour", + "schedulerStartMinute", + "schedulerStartSecond", + "schedulerTimeZone", + "schedulerFrequency", + "schedulerInterval"], function(val) { + if (!$scope.scheduler_form.$invalid) { + $scope.schedulerIsValid = true; + } else { + $scope.schedulerIsValid = false; + } + return val; + }); + Wait('stop'); + }); + + $scope.hideForm = true; + + $scope.formCancel = function() { + $state.go("^"); + } + + AddSchedule({ + scope: $scope, + callback: 'SchedulesRefresh' + }); +}]; diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js new file mode 100644 index 0000000000..b23836f98a --- /dev/null +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -0,0 +1,33 @@ +export default ['$compile', '$state', '$stateParams', 'EditSchedule', 'Wait', '$scope', function($compile, $state, $stateParams, EditSchedule, Wait, $scope) { + $scope.$on("ScheduleFormCreated", function(e, scope) { + $scope.hideForm = false; + $scope = angular.extend($scope, scope); + $scope.$watchGroup(["schedulerStartDt", + "schedulerStartHour", + "schedulerStartMinute", + "schedulerStartSecond", + "schedulerTimeZone", + "schedulerFrequency", + "schedulerInterval"], function(val) { + if (!$scope.scheduler_form.$invalid) { + $scope.schedulerIsValid = true; + } else { + $scope.schedulerIsValid = false; + } + return val; + }); + Wait('stop'); + }); + + $scope.hideForm = true; + + $scope.formCancel = function() { + $state.go("^"); + } + + EditSchedule({ + scope: $scope, + id: parseInt($stateParams.schedule_id), + callback: 'SchedulesRefresh' + }); +}]; diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html new file mode 100644 index 0000000000..6c545ab941 --- /dev/null +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -0,0 +1,307 @@ +
+
+
{{ schedulerName || "Add Schedule"}}
+
+ +
+
+
+
+
+ +
+ +
+ + +
A schedule name is required.
+
+ +
Note: For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept. + Caution: Setting both numerical variables to "0" will delete all facts.
+ +
+ + +
A value is required.
+
This is not a valid number.
+
+ +
+
+ +
+
+ +
Please enter the number of days you would like to keep this data.
+
Please enter a valid number.
+
Please enter a non-negative number.
+
Please enter a number smaller than 9999.
+
+
+ +
+
+ +
+
+ +
+
+ +
Please enter the number of days you would like to keep this data.
+
Please enter a valid number.
+
Please enter a non-negative number.
+
Please enter a number smaller than 9999.
+
+
+ +
+
+ +
+
+
+ +
+ + + + +
+
+
+
+
+ +
+ + : + : +
+
The time must be in HH24:MM:SS format.
+
+
+
+ +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
+
+ + + +
Please provide a value between 1 and 999.
+
+
+
+ +
+
+
+
+ +
+
+ +
The day must be between 1 and 31.
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
The day must be between 1 and 31.
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+ + + + + + + +
+
+
Please select one or more days.
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+ + +
+
Please provide a value between 1 and 999.
+
+
+
+
+
+ + + + +
+
Please provide a valid date.
+
+
+
+
+
+
+

The scheduler options are invalid or incomplete. Make the needed changes on the options tab, then come back here to see details.

+
+
+
+ + +
+
+ + +
+
+ +
+ + + +
+
    +
  • {{ occurrence.utc }}
  • +
+
    +
  • {{ occurrence.local }}
  • +
+
+
+
+ +
+ + +
+ +
+
+
+
diff --git a/awx/ui/client/src/shared/stateExtender.provider.js b/awx/ui/client/src/shared/stateExtender.provider.js index aa435b7c42..f899f00c32 100644 --- a/awx/ui/client/src/shared/stateExtender.provider.js +++ b/awx/ui/client/src/shared/stateExtender.provider.js @@ -3,8 +3,9 @@ export default function($stateProvider){ this.$get = function(){ return { addState: function(state) { + var route = state.route || state.url; $stateProvider.state(state.name , { - url: state.route, + url: route, controller: state.controller, templateUrl: state.templateUrl, resolve: state.resolve,