Merge pull request #319 from jaredevantabor/backlog

Backlog of small/medium defects
This commit is contained in:
jaredevantabor
2015-07-28 14:38:40 -07:00
45 changed files with 1245 additions and 1283 deletions

View File

@@ -29,9 +29,12 @@ import {CredentialsAdd, CredentialsEdit, CredentialsList} from './controllers/Cr
import {JobsListController} from './controllers/Jobs'; import {JobsListController} from './controllers/Jobs';
import {PortalController} from './controllers/Portal'; import {PortalController} from './controllers/Portal';
import systemTracking from './system-tracking/main'; import systemTracking from './system-tracking/main';
import inventoryScripts from './inventory-scripts/main';
import managementJobs from './management-jobs/main';
import routeExtensions from './shared/route-extensions/main'; import routeExtensions from './shared/route-extensions/main';
import breadcrumbs from './shared/breadcrumbs/main'; import breadcrumbs from './shared/breadcrumbs/main';
// modules // modules
import setupMenu from './setup-menu/main'; import setupMenu from './setup-menu/main';
import mainMenu from './main-menu/main'; import mainMenu from './main-menu/main';
@@ -67,6 +70,7 @@ import './shared/Socket';
import './job-templates/main'; import './job-templates/main';
import './shared/features/main'; import './shared/features/main';
/*#if DEBUG#*/ /*#if DEBUG#*/
import {__deferLoadIfEnabled} from './debug'; import {__deferLoadIfEnabled} from './debug';
__deferLoadIfEnabled(); __deferLoadIfEnabled();
@@ -81,6 +85,8 @@ var tower = angular.module('Tower', [
browserData.name, browserData.name,
breadcrumbs.name, breadcrumbs.name,
systemTracking.name, systemTracking.name,
inventoryScripts.name,
managementJobs.name,
setupMenu.name, setupMenu.name,
mainMenu.name, mainMenu.name,
dashboard.name, dashboard.name,
@@ -104,7 +110,6 @@ var tower = angular.module('Tower', [
'PaginationHelpers', 'PaginationHelpers',
'RefreshHelper', 'RefreshHelper',
'AdminListDefinition', 'AdminListDefinition',
'CustomInventoryListDefinition',
'AWDirectives', 'AWDirectives',
'AdhocFormDefinition', 'AdhocFormDefinition',
'InventoriesListDefinition', 'InventoriesListDefinition',
@@ -181,10 +186,8 @@ var tower = angular.module('Tower', [
'AboutAnsibleHelpModal', 'AboutAnsibleHelpModal',
'SurveyQuestionFormDefinition', 'SurveyQuestionFormDefinition',
'PortalJobsListDefinition', 'PortalJobsListDefinition',
'ConfigureTowerHelper',
'ConfigureTowerJobsListDefinition',
'CreateCustomInventoryHelper',
'CustomInventoryListDefinition',
'AdhocHelper', 'AdhocHelper',
'features', 'features',
'longDateFilter' 'longDateFilter'
@@ -933,9 +936,9 @@ var tower = angular.module('Tower', [
}]) }])
.run(['$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'HideStream', 'Socket', .run(['$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'HideStream', 'Socket',
'LoadConfig', 'Store', 'ShowSocketHelp', 'AboutAnsibleHelp', 'ConfigureTower', 'CreateCustomInventory', 'LoadConfig', 'Store', 'ShowSocketHelp', 'AboutAnsibleHelp',
function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, HideStream, Socket, function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, HideStream, Socket,
LoadConfig, Store, ShowSocketHelp, AboutAnsibleHelp, ConfigureTower, CreateCustomInventory) { LoadConfig, Store, ShowSocketHelp, AboutAnsibleHelp) {
var sock; var sock;
@@ -1143,19 +1146,6 @@ var tower = angular.module('Tower', [
$location.path('/home/'); $location.path('/home/');
}; };
$rootScope.configureTower = function(){
ConfigureTower({
scope: $rootScope,
parent_scope: $rootScope
});
};
$rootScope.createCustomInv = function(){
CreateCustomInventory({
parent_scope: $rootScope
});
};
}); // end of 'ConfigReady' }); // end of 'ConfigReady'

View File

@@ -58,7 +58,7 @@
export function Authenticate($log, $cookieStore, $compile, $window, $rootScope, $location, Authorization, ToggleClass, Alert, Wait, export function Authenticate($log, $cookieStore, $compile, $window, $rootScope, $location, Authorization, ToggleClass, Alert, Wait,
Timer, Empty, ClearScope) { Timer, Empty, ClearScope) {
var setLoginFocus, lastPath, sessionExpired, loginAgain, var setLoginFocus, lastPath, lastUser, sessionExpired, loginAgain,
e, html, scope = $rootScope.$new(); e, html, scope = $rootScope.$new();
setLoginFocus = function () { setLoginFocus = function () {
@@ -83,6 +83,15 @@ export function Authenticate($log, $cookieStore, $compile, $window, $rootScope,
return (Empty($rootScope.lastPath)) ? $cookieStore.get('lastPath') : $rootScope.lastPath; return (Empty($rootScope.lastPath)) ? $cookieStore.get('lastPath') : $rootScope.lastPath;
}; };
lastUser = function(){
if(!Empty($rootScope.lastUser) && $rootScope.lastUser === $rootScope.current_user.id){
return true;
}
else {
return false;
}
};
$log.debug('User session expired: ' + sessionExpired); $log.debug('User session expired: ' + sessionExpired);
$log.debug('Last URL: ' + lastPath()); $log.debug('Last URL: ' + lastPath());
@@ -176,7 +185,7 @@ export function Authenticate($log, $cookieStore, $compile, $window, $rootScope,
.success(function (data) { .success(function (data) {
Authorization.setLicense(data); Authorization.setLicense(data);
Wait("stop"); Wait("stop");
if (lastPath()) { if (lastPath() && lastUser()) {
// Go back to most recent navigation path // Go back to most recent navigation path
$location.path(lastPath()); $location.path(lastPath());
} else { } else {

View File

@@ -176,6 +176,7 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $r
current_item: (!Empty($routeParams.team_id)) ? $routeParams.team_id : null, current_item: (!Empty($routeParams.team_id)) ? $routeParams.team_id : null,
list: TeamList, list: TeamList,
field: 'team', field: 'team',
input_type: 'radio',
autopopulateLookup: false autopopulateLookup: false
}); });
@@ -347,6 +348,7 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, $
form: form, form: form,
current_item: (!Empty($scope.team_id)) ? $scope.team_id : null, current_item: (!Empty($scope.team_id)) ? $scope.team_id : null,
list: TeamList, list: TeamList,
input_type: 'radio',
field: 'team' field: 'team'
}); });

View File

@@ -433,7 +433,7 @@ export function InventoriesAdd($scope, $rootScope, $compile, $location, $log, $r
.success(function (data) { .success(function (data) {
var inventory_id = data.id; var inventory_id = data.id;
Wait('stop'); Wait('stop');
$location.path('/inventories/' + inventory_id + '/'); $location.path('/inventories/' + inventory_id + '/manage');
}) })
.error(function (data, status) { .error(function (data, status) {
ProcessErrors( $scope, data, status, form, { hdr: 'Error!', ProcessErrors( $scope, data, status, form, { hdr: 'Error!',

View File

@@ -7,7 +7,6 @@
import ActivityDetail from "./forms/ActivityDetail"; import ActivityDetail from "./forms/ActivityDetail";
import Credentials from "./forms/Credentials"; import Credentials from "./forms/Credentials";
import Adhoc from "./forms/Adhoc"; import Adhoc from "./forms/Adhoc";
import CustomInventory from "./forms/CustomInventory";
import EventsViewer from "./forms/EventsViewer"; import EventsViewer from "./forms/EventsViewer";
import Groups from "./forms/Groups"; import Groups from "./forms/Groups";
import HostGroups from "./forms/HostGroups"; import HostGroups from "./forms/HostGroups";
@@ -37,7 +36,6 @@ export
{ ActivityDetail, { ActivityDetail,
Credentials, Credentials,
Adhoc, Adhoc,
CustomInventory,
EventsViewer, EventsViewer,
Groups, Groups,
HostGroups, HostGroups,

View File

@@ -1,78 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name forms.function:CustomInventory
* @description This form is for adding/editing an organization
*/
export default
angular.module('CustomInventoryFormDefinition', [])
.value('CustomInventoryForm', {
addTitle: 'Create Custom Inventory', //Title in add mode
editTitle: '{{ name }}', //Title in edit mode
name: 'custom_inventory', //entity or model name in singular form
well: false,
showActions: false,
fields: {
name: {
label: 'Name',
type: 'text',
addRequired: true,
editRequired: true,
capitalize: false
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
},
organization: {
label: 'Organization',
type: 'lookup',
awRequiredWhen: {
variable: "orgrequired",
init: true
},
sourceModel: 'organization',
sourceField: 'name',
ngClick: 'lookUpOrganization()'
},
script: {
label: 'Custom Script',
type: 'textarea',
hintText: "Drag and drop an inventory script on the field below",
addRequired: true,
editRequired: true,
awDropFile: true,
'class': 'ssh-key-field',
rows: 10,
awPopOver: "<p>Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
"<br><br> Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python</p>",
dataTitle: 'Custom Script',
dataPlacement: 'right',
dataContainer: "body"
},
},
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
},
}); //OrganizationForm

View File

@@ -10,9 +10,7 @@ import './lists';
import AboutAnsible from "./helpers/AboutAnsible"; import AboutAnsible from "./helpers/AboutAnsible";
import Access from "./helpers/Access"; import Access from "./helpers/Access";
import Children from "./helpers/Children"; import Children from "./helpers/Children";
import ConfigureTower from "./helpers/ConfigureTower";
import Credentials from "./helpers/Credentials"; import Credentials from "./helpers/Credentials";
import CustomInventory from "./helpers/CustomInventory";
import EventViewer from "./helpers/EventViewer"; import EventViewer from "./helpers/EventViewer";
import Events from "./helpers/Events"; import Events from "./helpers/Events";
import Groups from "./helpers/Groups"; import Groups from "./helpers/Groups";
@@ -51,9 +49,7 @@ export
{ AboutAnsible, { AboutAnsible,
Access, Access,
Children, Children,
ConfigureTower,
Credentials, Credentials,
CustomInventory,
EventViewer, EventViewer,
Events, Events,
Groups, Groups,

View File

@@ -1,657 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name helpers.function:ConfigureTower
* @description
* Schedules Helper
*
* Display the scheduler widget in a dialog
*
*/
import listGenerator from '../shared/list-generator/main';
export default
angular.module('ConfigureTowerHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ModalDialog',
'GeneratorHelpers'])
.factory('ConfigureTower', ['Wait', '$location' , '$compile', 'CreateDialog', 'ConfigureTowerJobsList', 'generateList', 'GetBasePath' , 'SearchInit' , 'PaginateInit', 'PlaybookRun', 'LoadSchedulesScope',
'SchedulesList', 'SchedulesControllerInit' , 'ConfigureTowerSchedule', 'Rest' , 'ProcessErrors',
function(Wait, $location, $compile, CreateDialog, ConfigureTowerJobsList, GenerateList, GetBasePath, SearchInit, PaginateInit, PlaybookRun, LoadSchedulesScope,
SchedulesList, SchedulesControllerInit, ConfigureTowerSchedule, Rest, ProcessErrors) {
return function(params) {
// Set modal dimensions based on viewport width
var scope = params.scope.$new(),
parent_scope = params.scope,
callback = 'OpenConfig',
defaultUrl = GetBasePath('system_job_templates'),
list = ConfigureTowerJobsList,
view = GenerateList, e,
scheduleUrl = GetBasePath('system_job_templates'),
buttons = [
{
"label": "Close",
"onClick": function() {
// $(this).dialog('close');
scope.cancelConfigure();
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "configure-close-button"
}
];
scope.cleanupJob = true;
if(scope.removeOpenConfig) {
scope.removeOpenConfig();
}
scope.removeOpenConfig = scope.$on('OpenConfig', function() {
$('#configure-tower-dialog').dialog('open');
$('#configure-close-button').focus();
$('#configure-close-button').blur();
});
view.inject( list, {
id : 'configure-jobs',
mode: 'edit',
scope: scope,
breadCrumbs: false,
showSearch: false
});
SearchInit({
scope: scope,
set: 'configure_jobs',
list: list,
url: defaultUrl
});
PaginateInit({
scope: scope,
list: list,
url: defaultUrl
});
scope.search(list.iterator);
SchedulesControllerInit({
scope: scope,
parent_scope: parent_scope,
// list: list
});
CreateDialog({
id: 'configure-tower-dialog',
title: 'Management Jobs',
target: 'configure-tower-dialog',
scope: scope,
buttons: buttons,
width: 670,
height: 800,
minWidth: 400,
callback: callback,
onClose: function () {
// Destroy on close
$('.tooltip').each(function () {
// Remove any lingering tooltip <div> elements
$(this).remove();
});
$('.popover').each(function () {
// remove lingering popover <div> elements
$(this).remove();
});
$("#configure-jobs").show();
$("#configure-schedules-form-container").hide();
$('#configure-schedules-list').empty();
$('#configure-schedules-form').empty();
$('#configure-schedules-detail').empty();
$('#configure-tower-dialog').hide();
$(this).dialog('destroy');
scope.cancelConfigure();
},
});
// Cancel
scope.cancelConfigure = function () {
try {
$('#configure-tower-dialog').dialog('close');
$("#configure-save-button").remove();
}
catch(e) {
//ignore
}
if (scope.searchCleanup) {
scope.searchCleanup();
}
// if (!Empty(parent_scope) && parent_scope.restoreSearch) {
// parent_scope.restoreSearch();
// }
else {
Wait('stop');
}
};
scope.submitCleanupJob = function(id, name){
defaultUrl = GetBasePath('system_job_templates')+id+'/launch/';
CreateDialog({
id: 'prompt-for-days-facts',
title: name,
scope: scope,
width: 500,
height: 470,
minWidth: 200,
callback: 'PromptForDaysFacts',
onOpen: function(){
scope.keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.granularity_keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
e = angular.element(document.getElementById('prompt_for_days_facts_form'));
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
$compile(e)(scope);
scope.keep_unit = scope.keep_unit_choices[0];
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
// this is a work-around for getting awMax to work (without
// clearing out the form)
scope.$watch('keep_amount', function(newVal) {
if (!newVal && newVal !== 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (isNaN(newVal)) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal < 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal > 9999) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else {
$('#prompt-for-days-facts-launch').prop("disabled", false);
}
});
scope.$watch('granularity_keep_amount', function(newVal2) {
if (!newVal2 && newVal2 !== 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (isNaN(newVal2)) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal2 < 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal2 > 9999) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else {
$('#prompt-for-days-facts-launch').prop("disabled", false);
}
});
},
buttons: [{
"label": "Cancel",
"onClick": function() {
$(this).dialog('close');
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "prompt-for-days-facts-cancel"
},{
"label": "Launch",
"onClick": function() {
var extra_vars = {
"older_than": scope.keep_amount+scope.keep_unit.value,
"granularity": scope.granularity_keep_amount+scope.granularity_keep_unit.value
},
data = {};
data.extra_vars = JSON.stringify(extra_vars);
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function() {
Wait('stop');
$("#prompt-for-days-facts").dialog("close");
$("#configure-tower-dialog").dialog('close');
$location.path('/jobs/');
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
});
},
"icon": "fa-rocket",
"class": "btn btn-primary",
"id": "prompt-for-days-facts-launch"
}]
});
if (scope.removePromptForDays) {
scope.removePromptForDays();
}
scope.removePromptForDays = scope.$on('PromptForDaysFacts', function() {
// $('#configure-tower-dialog').dialog('close');
$('#prompt-for-days-facts').show();
$('#prompt-for-days-facts').dialog('open');
Wait('stop');
});
};
scope.submitJob = function (id, name) {
Wait('start');
if(this.configure_job.job_type === "cleanup_facts"){
scope.submitCleanupJob(id, name);
}
else {
defaultUrl = GetBasePath('system_job_templates')+id+'/launch/';
CreateDialog({
id: 'prompt-for-days' ,
title: name,
scope: scope,
width: 500,
height: 300,
minWidth: 200,
callback: 'PromptForDays',
onOpen: function(){
e = angular.element(document.getElementById('prompt_for_days_form'));
scope.prompt_for_days_form.days_to_keep.$setViewValue(30);
$compile(e)(scope);
// this is a work-around for getting awMax to work (without
// clearing out the form)
scope.$watch('days_to_keep', function(newVal) { // oldVal, scope) { // unused params get caught by jshint
if (!newVal && newVal !== 0) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (isNaN(newVal)) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (newVal < 0) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (newVal > 9999) {
$('#prompt-for-days-launch').prop("disabled", true);
} else {
$('#prompt-for-days-launch').prop("disabled", false);
}
});
},
buttons: [{
"label": "Cancel",
"onClick": function() {
$(this).dialog('close');
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "prompt-for-days-cancel"
},{
"label": "Launch",
"onClick": function() {
var extra_vars = {"days": scope.days_to_keep },
data = {};
data.extra_vars = JSON.stringify(extra_vars);
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function() {
Wait('stop');
$("#prompt-for-days").dialog("close");
$("#configure-tower-dialog").dialog('close');
$location.path('/jobs/');
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
});
},
"icon": "fa-rocket",
"class": "btn btn-primary",
"id": "prompt-for-days-launch"
}]
});
if (scope.removePromptForDays) {
scope.removePromptForDays();
}
scope.removePromptForDays = scope.$on('PromptForDays', function() {
// $('#configure-tower-dialog').dialog('close');
$('#prompt-for-days').show();
$('#prompt-for-days').dialog('open');
Wait('stop');
});
}
};
scope.configureSchedule = function(id, name) {
if (id === 4) {
scope.isFactCleanup = true;
scope.keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.granularity_keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
scope.keep_unit = scope.keep_unit_choices[0];
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
} else {
scope.isFactCleanup = false;
}
Rest.setUrl(scheduleUrl+id+'/schedules/');
Rest.get()
.success(function(data) {
if(data.count>0){
scope.days=data.results[0].extra_data.days;
ConfigureTowerSchedule({
scope: scope,
mode: 'edit',
url: scheduleUrl+id+'/schedules/'
});
} else {
ConfigureTowerSchedule({
scope: scope,
mode: 'add',
url: scheduleUrl+id+'/schedules/',
name: name
});
}
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed getting schedule information' });
});
};
parent_scope.refreshJobs = function(){
scope.search(SchedulesList.iterator);
};
};
}])
.factory('ConfigureTowerSchedule', ['$compile','SchedulerInit', 'Rest', 'Wait', 'SetSchedulesInnerDialogSize', 'SchedulePost', 'ProcessErrors', 'GetBasePath', 'Empty', 'Prompt',
function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, SchedulePost, ProcessErrors, GetBasePath, Empty, Prompt) {
return function(params) {
var parent_scope = params.scope,
mode = params.mode, // 'add' or 'edit'
url = params.url,
scope = parent_scope.$new(),
id = params.id || undefined,
name = params.name || undefined,
schedule = {},
scheduler,
target,
showForm,
list,
detail,
restoreList,
container,
elem;
Wait('start');
// $('#configure-jobs').hide();
detail = $('#configure-schedules-detail').hide();
list = $('#configure-schedules-list');
target = $('#configure-schedules-form');
container = $('#configure-schedules-form-container');
$("#configure-jobs").show();
$("#configure-schedules-form-container").hide();
scope.mode = mode;
// Clean up any lingering stuff
container.hide();
target.empty();
$('.tooltip').each(function () {
$(this).remove();
});
$('.popover').each(function () {
$(this).remove();
});
$("#configure-cancel-button").after('<button type="button" class="btn btn-primary btn-sm" id="configure-save-button" ng-click="saveScheduleForm()" style="margin-left:5px"><i class="fa fa-check"></i> Save</button>');
elem = angular.element(document.getElementById('configure-schedules-form-container'));
$compile(elem)(scope);
if (scope.removeScheduleReady) {
scope.removeScheduleReady();
}
scope.removeScheduleReady = scope.$on('ScheduleReady', function() {
// Insert the scheduler widget into the hidden div
scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false });
scheduler.inject('configure-schedules-form', false);
scheduler.injectDetail('configure-schedules-detail', false);
scheduler.clear();
scope.formShowing = true;
scope.showRRuleDetail = false;
scope.schedulesTitle = (mode === 'edit') ? 'Edit Schedule' : 'Create Schedule';
// display the scheduler widget
showForm = function() {
$('#configure-jobs').show('slide', { direction: 'left' }, 500);
$('#configure-jobs').hide();
Wait('stop');
$('#configure-schedules-overlay').width($('#configure-schedules-tab')
.width()).height($('#configure-schedules-tab').height()).show();
container.width($('#configure-schedules-tab').width() - 18);
SetSchedulesInnerDialogSize();
container.show('slide', { direction: 'right' }, 300);
// scope.schedulerPurgeDays = (!Empty(scope.days)) ? Number(scope.days) : 30;
target.show();
if (scope.isFactCleanup) {
scope.$watch('scheduler_form.keep_amount.$modelValue', function(newVal) {
if (!newVal && newVal !== 0) {
$('#configure-save-button').prop("disabled", true);
} else if (isNaN(newVal)) {
$('#configure-save-button').prop("disabled", true);
} else if (newVal < 0) {
$('#configure-save-button').prop("disabled", true);
} else if (newVal > 9999) {
$('#configure-save-button').prop("disabled", true);
} else {
$('#configure-save-button').prop("disabled", false);
}
});
scope.$watch('scheduler_form.granularity_keep_amount.$modelValue', function(newVal2) {
if (!newVal2 && newVal2 !== 0) {
$('#configure-save-button').prop("disabled", true);
} else if (isNaN(newVal2)) {
$('#configure-save-button').prop("disabled", true);
} else if (newVal2 < 0) {
$('#configure-save-button').prop("disabled", true);
} else if (newVal2 > 9999) {
$('#configure-save-button').prop("disabled", true);
} else {
$('#configure-save-button').prop("disabled", false);
}
});
}
if(mode==="add"){
scope.$apply(function(){
scope.schedulerPurgeDays = 30;
scope.schedulerName = name+' Schedule';
});
}
if (mode === 'edit') {
scope.$apply(function() {
scheduler.setRRule(schedule.rrule);
scheduler.setName(schedule.name);
scope.schedulerPurgeDays = (!Empty(scope.days)) ? Number(scope.days) : 30;
});
}
};
setTimeout(function() { showForm(); }, 1000);
});
restoreList = function() {
// $('#group-save-button').prop('disabled', false);
$('#configure-jobs').show('slide', { direction: 'right' }, 500);
// $('#configure-jobs').width($('#configure-jobs').width()).height($('#configure-jobs').height()).hide();
// parent_scope.refreshSchedules();
list.show('slide', { direction: 'right' }, 500);
$('#configure-schedules-overlay').width($('#configure-schedules-tab').width()).height($('#configure-schedules-tab').height()).hide();
parent_scope.refreshSchedules();
};
scope.showScheduleDetail = function() {
if (scope.formShowing) {
if (scheduler.isValid()) {
detail.width($('#configure-schedules-form').width()).height($('#configure-schedules-form').height());
target.hide();
detail.show();
scope.formShowing = false;
}
}
else {
detail.hide();
target.show();
scope.formShowing = true;
}
};
if (scope.removeScheduleSaved) {
scope.removeScheduleSaved();
}
scope.removeScheduleSaved = scope.$on('ScheduleSaved', function() {
Wait('stop');
$("#configure-save-button").remove();
container.hide('slide', { direction: 'left' }, 500, restoreList);
scope.$destroy();
});
scope.saveScheduleForm = function() {
var extra_vars;
if (scheduler.isValid()) {
scope.schedulerIsValid = true;
url = (mode==="edit") ? GetBasePath('schedules')+id+'/' : url;
if (scope.isFactCleanup) {
extra_vars = {
"older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value,
"granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value
};
} else {
extra_vars = {
"days" : scope.scheduler_form.schedulerPurgeDays.$viewValue
};
}
schedule.extra_data = JSON.stringify(extra_vars);
SchedulePost({
scope: scope,
url: url,
scheduler: scheduler,
callback: 'ScheduleSaved',
mode: mode,
schedule: schedule
});
}
else {
scope.schedulerIsValid = false;
}
};
scope.deleteSystemSchedule = function(){
var hdr = 'Delete Schedule',
action = function () {
Wait('start');
Rest.setUrl(schedule.url);
Rest.destroy()
.success(function () {
$('#prompt-modal').modal('hide');
Wait('stop');
// scope.$emit(callback, id);
scope.cancelScheduleForm();
})
.error(function (data, status) {
try {
$('#prompt-modal').modal('hide');
}
catch(e) {
// ignore
}
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
' failed. DELETE returned: ' + status });
});
};
Prompt({
hdr: hdr,
body: "<div class=\"alert alert-info\">Are you sure you want to delete the <em>" + scope.schedulerName + "</em> schedule?</div>",
action: action,
backdrop: false
});
};
scope.cancelScheduleForm = function() {
container.hide('slide', { direction: 'right' }, 500, restoreList);
$("#configure-save-button").remove();
scope.$destroy();
};
if (mode === 'edit') {
// Get the existing record
Rest.setUrl(url); //GetBasePath('schedules')+id+'/');
Rest.get()
.success(function(data) {
schedule = data.results[0];
id = schedule.id;
if (!/DTSTART/.test(schedule.rrule)) {
schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z');
}
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
scope.$emit('ScheduleReady');
})
.error(function(data,status){
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get: ' + url + ' GET returned: ' + status });
});
}
else {
scope.$emit('ScheduleReady');
}
};
}]);

View File

@@ -1,362 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name helpers.function:CustomInventory
* @description
* Schedules Helper
*
* Display the scheduler widget in a dialog
*
*/
import listGenerator from '../shared/list-generator/main';
export default
angular.module('CreateCustomInventoryHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ModalDialog',
'GeneratorHelpers', 'CustomInventoryFormDefinition'])
.factory('CreateCustomInventory', ['Wait', 'CreateDialog', 'CustomInventoryList', 'generateList', 'GetBasePath' , 'SearchInit' , 'PaginateInit', 'PlaybookRun', 'CustomInventoryAdd',
'SchedulesList', 'CustomInventoryEdit', 'Rest' , 'ProcessErrors', 'CustomInventoryForm', 'GenerateForm', 'Prompt',
function(Wait, CreateDialog, CustomInventoryList, GenerateList, GetBasePath, SearchInit, PaginateInit, PlaybookRun, CustomInventoryAdd,
SchedulesList, CustomInventoryEdit, Rest, ProcessErrors, CustomInventoryForm, GenerateForm, Prompt) {
return function(params) {
// Set modal dimensions based on viewport width
var scope = params.parent_scope.$new(),
callback = 'OpenConfig',
defaultUrl = GetBasePath('inventory_scripts'),
list = CustomInventoryList,
view = GenerateList,
buttons = [
{
"label": "Close",
"onClick": function() {
// $(this).dialog('close');
scope.cancelConfigure();
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "script-close-button"
}
];
scope.cleanupJob = true;
if(scope.removeOpenConfig) {
scope.removeOpenConfig();
}
scope.removeOpenConfig = scope.$on('OpenConfig', function() {
$('#custom-script-dialog').dialog('open');
$('#script-close-button').focus();
$('#script-close-button').blur();
});
view.inject( list, {
id : 'custom-script-dialog',
mode: 'edit',
scope: scope,
breadCrumbs: false,
activityStream: false,
showSearch: true
});
SearchInit({
scope: scope,
set: 'source_scripts' , // 'custom_inventories',
list: list,
url: defaultUrl
});
PaginateInit({
scope: scope,
list: list,
url: defaultUrl
});
scope.search(list.iterator);
// SchedulesControllerInit({
// scope: scope,
// parent_scope: parent_scope,
// // list: list
// });
CreateDialog({
id: 'custom-script-dialog',
title: 'Inventory Scripts',
target: 'custom-script-dialog',
scope: scope,
buttons: buttons,
width: 700,
height: 800,
minWidth: 400,
callback: callback,
onClose: function () {
// Destroy on close
$('.tooltip').each(function () {
// Remove any lingering tooltip <div> elements
$(this).remove();
});
$('.popover').each(function () {
// remove lingering popover <div> elements
$(this).remove();
});
// $("#configure-jobs").show();
// $("#configure-schedules-form-container").hide();
// $('#configure-schedules-list').empty();
// $('#configure-schedules-form').empty();
// $('#configure-schedules-detail').empty();
// $('#configure-tower-dialog').hide();
$(this).dialog('destroy');
scope.cancelConfigure();
},
});
// Cancel
scope.cancelConfigure = function () {
try {
$('#custom-script-dialog').dialog('close');
}
catch(e) {
//ignore
}
if (scope.searchCleanup) {
scope.searchCleanup();
}
// if (!Empty(parent_scope) && parent_scope.restoreSearch) {
// parent_scope.restoreSearch();
// }
else {
Wait('stop');
}
};
scope.editCustomInv = function(id){
CustomInventoryEdit({
scope: scope,
id: id
});
};
scope.deleteCustomInv = function(id, name){
var action = function () {
$('#prompt-modal').modal('hide');
Wait('start');
var url = defaultUrl + id + '/';
Rest.setUrl(url);
Rest.destroy()
.success(function () {
scope.search(list.iterator);
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
var bodyHtml = "<div class=\"alert alert-info\">Are you sure you want to delete " + name + "?</div>";
Prompt({
hdr: 'Delete',
body: bodyHtml,
action: action
});
};
scope.addCustomInv = function(){
CustomInventoryAdd({
scope: scope
});
};
};
}])
.factory('CustomInventoryAdd', ['$compile','SchedulerInit', 'Rest', 'Wait', 'CustomInventoryList', 'CustomInventoryForm', 'ProcessErrors', 'GetBasePath', 'Empty', 'GenerateForm',
'SearchInit' , 'PaginateInit', 'generateList', 'LookUpInit', 'OrganizationList',
function($compile, SchedulerInit, Rest, Wait, CustomInventoryList, CustomInventoryForm, ProcessErrors, GetBasePath, Empty, GenerateForm,
SearchInit, PaginateInit, GenerateList, LookUpInit, OrganizationList) {
return function(params) {
var scope = params.scope,
generator = GenerateForm,
form = CustomInventoryForm,
view = GenerateList,
list = CustomInventoryList,
url = GetBasePath('inventory_scripts');
generator.inject(form, { id:'custom-script-dialog', mode: 'add' , scope:scope, related: false, breadCrumbs: false});
generator.reset();
LookUpInit({
url: GetBasePath('organization'),
scope: scope,
form: form,
// hdr: "Select Custom Inventory",
list: OrganizationList,
field: 'organization',
input_type: 'radio'
});
// Save
scope.formSave = function () {
generator.clearApiErrors();
Wait('start');
Rest.setUrl(url);
Rest.post({ name: scope.name, description: scope.description, organization: scope.organization, script: scope.script })
.success(function () {
view.inject( list, {
id : 'custom-script-dialog',
mode: 'edit',
scope: scope,
breadCrumbs: false,
activityStream: false,
showSearch: true
});
SearchInit({
scope: scope,
set: 'source_scripts', //'custom_inventories',
list: list,
url: url
});
PaginateInit({
scope: scope,
list: list,
url: url
});
scope.search(list.iterator);
Wait('stop');
Wait('stop');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new inventory script. POST returned status: ' + status });
});
};
// Cancel
scope.formReset = function () {
generator.reset();
};
};
}])
.factory('CustomInventoryEdit', ['$compile','CustomInventoryList', 'Rest', 'Wait', 'generateList', 'CustomInventoryForm', 'ProcessErrors', 'GetBasePath', 'Empty', 'GenerateForm',
'SearchInit', 'PaginateInit', '$routeParams', 'OrganizationList', 'LookUpInit',
function($compile, CustomInventoryList, Rest, Wait, GenerateList, CustomInventoryForm, ProcessErrors, GetBasePath, Empty, GenerateForm,
SearchInit, PaginateInit, $routeParams, OrganizationList, LookUpInit) {
return function(params) {
var scope = params.scope,
id = params.id,
generator = GenerateForm,
form = CustomInventoryForm,
view = GenerateList,
list = CustomInventoryList,
master = {},
url = GetBasePath('inventory_scripts');
generator.inject(form, {
id:'custom-script-dialog',
mode: 'edit' ,
scope:scope,
related: false,
breadCrumbs: false,
activityStream: false
});
generator.reset();
LookUpInit({
url: GetBasePath('organization'),
scope: scope,
form: form,
// hdr: "Select Custom Inventory",
list: OrganizationList,
field: 'organization',
input_type: 'radio'
});
// Retrieve detail record and prepopulate the form
Wait('start');
Rest.setUrl(url + id+'/');
Rest.get()
.success(function (data) {
var fld;
for (fld in form.fields) {
if (data[fld]) {
scope[fld] = data[fld];
master[fld] = data[fld];
}
if (form.fields[fld].sourceModel && data.summary_fields &&
data.summary_fields[form.fields[fld].sourceModel]) {
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
}
}
Wait('stop');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to retrieve inventory script: ' + $routeParams.id + '. GET status: ' + status });
});
scope.formSave = function () {
generator.clearApiErrors();
Wait('start');
Rest.setUrl(url+ id+'/');
Rest.put({ name: scope.name, description: scope.description, organization: scope.organization, script: scope.script })
.success(function () {
view.inject( list, {
id : 'custom-script-dialog',
mode: 'edit',
scope: scope,
breadCrumbs: false,
activityStream: false,
showSearch: true
});
SearchInit({
scope: scope,
set: 'source_scripts', //'custom_inventories',
list: list,
url: url
});
PaginateInit({
scope: scope,
list: list,
url: url
});
scope.search(list.iterator);
Wait('stop');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new inventory script. PUT returned status: ' + status });
});
};
scope.formReset = function () {
generator.reset();
for (var fld in master) {
scope[fld] = master[fld];
}
scope.organization_name = master.organization_name;
};
};
}]);

View File

@@ -235,8 +235,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
* TODO: Document * TODO: Document
* *
*/ */
.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', 'Wait', 'ParseTypeChange', 'CustomInventoryList', 'CreateSelect2', .factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', 'Wait', 'ParseTypeChange', 'inventoryScriptsListObject', 'CreateSelect2',
function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, ParseTypeChange, CustomInventoryList, CreateSelect2) { function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, ParseTypeChange, inventoryScriptsListObject, CreateSelect2) {
return function (params) { return function (params) {
var scope = params.scope, var scope = params.scope,
@@ -289,7 +289,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
scope: scope, scope: scope,
form: form, form: form,
hdr: "Select Custom Inventory", hdr: "Select Custom Inventory",
list: CustomInventoryList, list: inventoryScriptsListObject,
field: 'source_script', field: 'source_script',
input_type: 'radio' input_type: 'radio'
}); });

View File

@@ -28,7 +28,7 @@ export default
scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size'])); scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size']));
scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages']; scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages'];
scope[iterator + '_total_rows'] = count; scope[iterator + '_total_rows'] = count;
$('#pagination-links li:eq(1)').removeAttr('class'); $('#'+iterator+'-pagination #pagination-links li:eq(1)').removeAttr('class');
// Which page are we on? // Which page are we on?
if (Empty(next) && previous) { if (Empty(next) && previous) {
// no next page, but there is a previous page // no next page, but there is a previous page
@@ -36,7 +36,7 @@ export default
} else if (next && Empty(previous)) { } else if (next && Empty(previous)) {
// next page available, but no previous page // next page available, but no previous page
scope[iterator + '_page'] = 1; scope[iterator + '_page'] = 1;
$('#pagination-links li:eq(1)').attr('class', 'disabled'); $('#'+iterator+'-pagination #pagination-links li:eq(1)').attr('class', 'disabled');
} else if (next && previous) { } else if (next && previous) {
// we're in between next and previous // we're in between next and previous
scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/, '')) + 1; scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/, '')) + 1;

View File

@@ -75,8 +75,10 @@ export default
}; };
}]) }])
.factory('EditSchedule', ['SchedulerInit', 'ShowSchedulerModal', 'Wait', 'Rest', 'ProcessErrors', 'GetBasePath', 'SchedulePost', .factory('EditSchedule', ['SchedulerInit', 'ShowSchedulerModal', 'Wait',
function(SchedulerInit, ShowSchedulerModal, Wait, Rest, ProcessErrors, GetBasePath, SchedulePost) { 'Rest', 'ProcessErrors', 'GetBasePath', 'SchedulePost',
function(SchedulerInit, ShowSchedulerModal, Wait, Rest, ProcessErrors,
GetBasePath, SchedulePost) {
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
id = params.id, id = params.id,
@@ -84,6 +86,63 @@ export default
schedule, scheduler, schedule, scheduler,
url = GetBasePath('schedules') + id + '/'; url = GetBasePath('schedules') + id + '/';
delete scope.isFactCleanup;
delete scope.cleanupJob;
function setGranularity(){
var a,b, prompt_for_days,
keep_unit,
granularity,
granularity_keep_unit;
if(scope.cleanupJob){
scope.schedulerPurgeDays = Number(schedule.extra_data.days);
// scope.scheduler_form.schedulerPurgeDays.$setViewValue( Number(schedule.extra_data.days));
}
else if(scope.isFactCleanup){
scope.keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.granularity_keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
// the API returns something like 20w or 1y
a = schedule.extra_data.older_than; // "20y"
b = schedule.extra_data.granularity; // "1w"
prompt_for_days = Number(_.initial(a,1).join('')); // 20
keep_unit = _.last(a); // "y"
granularity = Number(_.initial(b,1).join('')); // 1
granularity_keep_unit = _.last(b); // "w"
scope.keep_amount = prompt_for_days;
scope.granularity_keep_amount = granularity;
scope.keep_unit = _.find(scope.keep_unit_choices, function(i){
return i.value === keep_unit;
});
scope.granularity_keep_unit =_.find(scope.granularity_keep_unit_choices, function(i){
return i.value === granularity_keep_unit;
});
}
}
if (scope.removeDialogReady) { if (scope.removeDialogReady) {
scope.removeDialogReady(); scope.removeDialogReady();
} }
@@ -94,6 +153,10 @@ export default
scope.$apply(function() { scope.$apply(function() {
scheduler.setRRule(schedule.rrule); scheduler.setRRule(schedule.rrule);
scheduler.setName(schedule.name); scheduler.setName(schedule.name);
if(scope.isFactCleanup || scope.cleanupJob){
setGranularity();
}
}); });
}, 300); }, 300);
}); });
@@ -112,7 +175,6 @@ export default
} }
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';'); schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART='); schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Edit Schedule' }); ShowSchedulerModal({ scope: scope, callback: 'DialogReady', title: 'Edit Schedule' });
scope.showRRuleDetail = false; scope.showRRuleDetail = false;
}); });
@@ -141,6 +203,8 @@ export default
}); });
}; };
$('#scheduler-tabs li a').on('shown.bs.tab', function(e) { $('#scheduler-tabs li a').on('shown.bs.tab', function(e) {
if ($(e.target).text() === 'Details') { if ($(e.target).text() === 'Details') {
if (!scheduler.isValid()) { if (!scheduler.isValid()) {
@@ -156,6 +220,13 @@ export default
Rest.get() Rest.get()
.success(function(data) { .success(function(data) {
schedule = data; schedule = data;
if(schedule.hasOwnProperty('extra_data')) {
if(schedule.extra_data.hasOwnProperty('granularity')){
scope.isFactCleanup = true;
} else {
scope.cleanupJob = true;
}
}
scope.$emit('ScheduleFound'); scope.$emit('ScheduleFound');
}) })
.error(function(data,status){ .error(function(data,status){
@@ -181,6 +252,43 @@ export default
else if (!Empty($routeParams.id)) { else if (!Empty($routeParams.id)) {
url += $routeParams.id + '/schedules/'; url += $routeParams.id + '/schedules/';
} }
else if (!Empty($routeParams.management_job)) {
url += $routeParams.management_job + '/schedules/';
if(scope.management_job.id === 4){
scope.isFactCleanup = true;
scope.keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.granularity_keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
scope.keep_unit = scope.keep_unit_choices[0];
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
}
else {
scope.cleanupJob = true;
}
}
if (scope.removeDialogReady) { if (scope.removeDialogReady) {
scope.removeDialogReady(); scope.removeDialogReady();
@@ -239,7 +347,7 @@ export default
mode = params.mode, mode = params.mode,
schedule = (params.schedule) ? params.schedule : {}, schedule = (params.schedule) ? params.schedule : {},
callback = params.callback, callback = params.callback,
newSchedule, rrule; newSchedule, rrule, extra_vars;
if (scheduler.isValid()) { if (scheduler.isValid()) {
Wait('start'); Wait('start');
@@ -248,6 +356,20 @@ export default
schedule.name = newSchedule.name; schedule.name = newSchedule.name;
schedule.rrule = RRuleToAPI(rrule.toString()); schedule.rrule = RRuleToAPI(rrule.toString());
schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText(); schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText();
if (scope.isFactCleanup) {
extra_vars = {
"older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value,
"granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value
};
} else if (scope.cleanupJob) {
extra_vars = {
"days" : scope.scheduler_form.schedulerPurgeDays.$viewValue
};
}
schedule.extra_data = JSON.stringify(extra_vars);
Rest.setUrl(url); Rest.setUrl(url);
if (mode === 'add') { if (mode === 'add') {
Rest.post(schedule) Rest.post(schedule)
@@ -442,8 +564,10 @@ export default
}]) }])
.factory('SchedulesControllerInit', ['$location', 'ToggleSchedule', 'DeleteSchedule', 'EditSchedule', 'AddSchedule', .factory('SchedulesControllerInit', ['$location', 'ToggleSchedule',
function($location, ToggleSchedule, DeleteSchedule, EditSchedule, AddSchedule) { 'DeleteSchedule', 'EditSchedule', 'AddSchedule',
function($location, ToggleSchedule, DeleteSchedule, EditSchedule,
AddSchedule) {
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
parent_scope = params.parent_scope, parent_scope = params.parent_scope,

View File

@@ -0,0 +1,67 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
[ '$compile','SchedulerInit', 'Rest', 'Wait',
'inventoryScriptsFormObject', 'ProcessErrors', 'GetBasePath', 'Empty',
'GenerateForm', 'SearchInit' , 'PaginateInit',
'LookUpInit', 'OrganizationList', '$scope', 'transitionTo',
function(
$compile, SchedulerInit, Rest, Wait,
inventoryScriptsFormObject, ProcessErrors, GetBasePath, Empty,
GenerateForm, SearchInit, PaginateInit,
LookUpInit, OrganizationList, $scope, transitionTo
) {
var scope = $scope,
generator = GenerateForm,
form = inventoryScriptsFormObject,
url = GetBasePath('inventory_scripts');
generator.inject(form, {
mode: 'add' ,
scope:scope,
related: false
});
generator.reset();
LookUpInit({
url: GetBasePath('organization'),
scope: scope,
form: form,
list: OrganizationList,
field: 'organization',
input_type: 'radio'
});
// Save
scope.formSave = function () {
generator.clearApiErrors();
Wait('start');
Rest.setUrl(url);
Rest.post({
name: scope.name,
description: scope.description,
organization: scope.organization,
script: scope.script
})
.success(function () {
transitionTo('inventoryScriptsList');
Wait('stop');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new inventory script. POST returned status: ' + status });
});
};
// Cancel
scope.formReset = function () {
generator.reset();
};
}
];

View File

@@ -0,0 +1,12 @@
<breadcrumbs>
<breadcrumb path="/setup" title="Setup"></breadcrumb>
<breadcrumb path="/inventory_scripts" title="Inventory Scripts"></breadcrumb>
<breadcrumb
path="/inventory_scripts/add"
title="Create Inventory Script">
</breadcrumb>
</breadcrumbs>
<div class="tab-pane" id="inventory_scripts_add">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@@ -0,0 +1,19 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'inventoryScriptsAdd',
route: '/inventory_scripts/add',
templateUrl: templateUrl('inventory-scripts/add/add'),
controller: 'addController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
}
};

View File

@@ -0,0 +1,17 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './add.route';
import controller from './add.controller';
export default
angular.module('inventoryScriptsAdd', [])
.controller('addController', controller)
.config(['$routeProvider', function($routeProvider) {
var url = route.route;
delete route.route;
$routeProvider.when(url, route);
}]);

View File

@@ -0,0 +1,103 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
[ 'Rest', 'Wait',
'inventoryScriptsFormObject', 'ProcessErrors', 'GetBasePath',
'GenerateForm', 'SearchInit' , 'PaginateInit',
'LookUpInit', 'OrganizationList', 'inventory_script',
'$scope', 'transitionTo',
function(
Rest, Wait,
inventoryScriptsFormObject, ProcessErrors, GetBasePath,
GenerateForm, SearchInit, PaginateInit,
LookUpInit, OrganizationList, inventory_script,
$scope, transitionTo
) {
var generator = GenerateForm,
id = inventory_script.id,
form = inventoryScriptsFormObject,
master = {},
url = GetBasePath('inventory_scripts');
$scope.inventory_script = inventory_script;
generator.inject(form, {
mode: 'edit' ,
scope:$scope,
breadCrumbs: true,
related: false,
activityStream: false
});
generator.reset();
LookUpInit({
url: GetBasePath('organization'),
scope: $scope,
form: form,
// hdr: "Select Custom Inventory",
list: OrganizationList,
field: 'organization',
input_type: 'radio'
});
// Retrieve detail record and prepopulate the form
Wait('start');
Rest.setUrl(url + id+'/');
Rest.get()
.success(function (data) {
var fld;
for (fld in form.fields) {
if (data[fld]) {
$scope[fld] = data[fld];
master[fld] = data[fld];
}
if (form.fields[fld].sourceModel && data.summary_fields &&
data.summary_fields[form.fields[fld].sourceModel]) {
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
}
}
Wait('stop');
})
.error(function (data, status) {
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to retrieve inventory script: ' + id + '. GET status: ' + status });
});
$scope.formSave = function () {
generator.clearApiErrors();
Wait('start');
Rest.setUrl(url+ id+'/');
Rest.put({
name: $scope.name,
description: $scope.description,
organization: $scope.organization,
script: $scope.script
})
.success(function () {
transitionTo('inventoryScriptsList');
Wait('stop');
})
.error(function (data, status) {
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to add new inventory script. PUT returned status: ' + status });
});
};
$scope.formReset = function () {
generator.reset();
for (var fld in master) {
$scope[fld] = master[fld];
}
$scope.organization_name = master.organization_name;
};
}
];

View File

@@ -0,0 +1,9 @@
<breadcrumbs>
<breadcrumb path="/setup" title="Setup"></breadcrumb>
<breadcrumb path="/inventory_scripts" title="Inventory Scripts"></breadcrumb>
<breadcrumb path="/inventory_scripts/{{inventory_script.id}}" title="{{inventory_script.name}}" current="true"></breadcrumb>
</breadcrumbs>
<div class="tab-pane" id="inventory_scripts_edit">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@@ -0,0 +1,46 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'inventoryScriptsEdit',
route: '/inventory_scripts/:inventory_script',
templateUrl: templateUrl('inventory-scripts/edit/edit'),
controller: 'editController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
inventory_script:
[ '$route',
'$q',
'Rest',
'GetBasePath',
'ProcessErrors',
function($route, $q, rest, getBasePath, ProcessErrors) {
if ($route.current.hasModelKey('inventory_script')) {
return $q.when($route.current.params.model.inventory_script);
}
var inventoryScriptId = $route.current.params.inventory_script;
var url = getBasePath('inventory_scripts') + inventoryScriptId + '/';
rest.setUrl(url);
return rest.get()
.then(function(data) {
return data.data;
}).catch(function (response) {
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get inventory script info. GET returned status: ' +
response.status
});
});
}
]
}
};

View File

@@ -0,0 +1,17 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './edit.route';
import controller from './edit.controller';
export default
angular.module('inventoryScriptsEdit', [])
.controller('editController', controller)
.config(['$routeProvider', function($routeProvider) {
var url = route.route;
delete route.route;
$routeProvider.when(url, route);
}]);

View File

@@ -0,0 +1,75 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name forms.function:CustomInventory
* @description This form is for adding/editing an organization
*/
export default function() {
return {
// addTitle: 'Create Custom Inventory',
// editTitle: '{{ name }}',
// name: 'custom_inventory',
well: true,
showActions: true,
fields: {
name: {
label: 'Name',
type: 'text',
addRequired: true,
editRequired: true,
capitalize: false
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
},
organization: {
label: 'Organization',
type: 'lookup',
awRequiredWhen: {
variable: "orgrequired",
init: true
},
sourceModel: 'organization',
sourceField: 'name',
ngClick: 'lookUpOrganization()'
},
script: {
label: 'Custom Script',
type: 'textarea',
hintText: "Drag and drop an inventory script on the field below",
addRequired: true,
editRequired: true,
awDropFile: true,
// 'class': 'ssh-key-field',
rows: 10,
awPopOver: "<p>Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
"<br><br> Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python</p>",
dataTitle: 'Custom Script',
dataPlacement: 'right',
dataContainer: "body"
},
},
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
}
};
}

View File

@@ -6,21 +6,16 @@
export default export default function(){
angular.module('CustomInventoryListDefinition', []) return {
.value('CustomInventoryList', { name: 'inventory_scripts' ,
iterator: 'inventory_script',
name: 'source_scripts' , // 'custom_inventories',
iterator: 'source_script', //'custom_inventory',
selectTitle: 'Add custom inventory',
editTitle: 'Custom Inventories',
index: false, index: false,
hover: false, hover: false,
fields: { fields: {
name: { name: {
key: true, key: true,
noLink: true,
label: 'Name', label: 'Name',
columnClass: 'col-md-3 col-sm-9 col-xs-9', columnClass: 'col-md-3 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-8' modalColumnClass: 'col-md-8'
@@ -32,8 +27,7 @@ export default
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
ngBind: 'source_script.summary_fields.organization.name', ngBind: 'inventory_script.summary_fields.organization.name',
// linkTo: '/#/organizations/{{ custom_inventory.organization }}',
sourceModel: 'organization', sourceModel: 'organization',
sourceField: 'name', sourceField: 'name',
excludeModal: true, excludeModal: true,
@@ -47,11 +41,18 @@ export default
ngClick: 'addCustomInv()', ngClick: 'addCustomInv()',
awToolTip: 'Create a new credential' awToolTip: 'Create a new credential'
}, },
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
icon: "icon-comments-alt",
mode: 'edit',
awFeature: 'activity_streams'
}
}, },
fieldActions: { fieldActions: {
edit: { edit: {
ngClick: "editCustomInv(source_script.id)", ngClick: "editCustomInv(inventory_script.id)",
icon: 'fa-edit', icon: 'fa-edit',
label: 'Edit', label: 'Edit',
"class": 'btn-sm', "class": 'btn-sm',
@@ -59,7 +60,7 @@ export default
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
ngClick: "deleteCustomInv(source_script.id, source_script.name)", ngClick: "deleteCustomInv(inventory_script.id, inventory_script.name)",
icon: 'fa-trash', icon: 'fa-trash',
label: 'Delete', label: 'Delete',
"class": 'btn-sm', "class": 'btn-sm',
@@ -67,4 +68,5 @@ export default
dataPlacement: 'top' dataPlacement: 'top'
} }
} }
}); };
}

View File

@@ -0,0 +1,80 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
[ '$rootScope','Wait', 'generateList', 'inventoryScriptsListObject',
'GetBasePath' , 'SearchInit' , 'PaginateInit',
'Rest' , 'ProcessErrors', 'Prompt', 'transitionTo', 'Stream',
function(
$rootScope,Wait, GenerateList, inventoryScriptsListObject,
GetBasePath, SearchInit, PaginateInit,
Rest, ProcessErrors, Prompt, transitionTo, Stream
) {
var scope = $rootScope.$new(),
defaultUrl = GetBasePath('inventory_scripts'),
list = inventoryScriptsListObject,
view = GenerateList;
view.inject( list, {
mode: 'edit',
scope: scope
});
SearchInit({
scope: scope,
set: 'inventory_scripts',
list: list,
url: defaultUrl
});
PaginateInit({
scope: scope,
list: list,
url: defaultUrl
});
scope.search(list.iterator);
scope.editCustomInv = function(){
transitionTo('inventoryScriptsEdit', {
inventory_script: this.inventory_script
});
};
scope.showActivity = function () {
Stream({ scope: scope });
};
scope.deleteCustomInv = function(id, name){
var action = function () {
$('#prompt-modal').modal('hide');
Wait('start');
var url = defaultUrl + id + '/';
Rest.setUrl(url);
Rest.destroy()
.success(function () {
scope.search(list.iterator);
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
var bodyHtml = "<div class=\"alert alert-info\">Are you sure you want to delete " + name + "?</div>";
Prompt({
hdr: 'Delete',
body: bodyHtml,
action: action
});
};
scope.addCustomInv = function(){
transitionTo('inventoryScriptsAdd');
};
}
];

View File

@@ -0,0 +1,8 @@
<breadcrumbs>
<breadcrumb path="/setup" title="Setup"></breadcrumb>
<breadcrumb path="/inventory_scripts" title="Inventory Scripts"></breadcrumb>
</breadcrumbs>
<div class="tab-pane" id="inventory_scripts">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@@ -0,0 +1,19 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'inventoryScriptsList',
route: '/inventory_scripts',
templateUrl: templateUrl('inventory-scripts/list/list'),
controller: 'inventoryScriptsListController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
}
};

View File

@@ -0,0 +1,17 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './list.route';
import controller from './list.controller';
export default
angular.module('inventoryScriptsList', [])
.controller('inventoryScriptsListController', controller)
.config(['$routeProvider', function($routeProvider) {
var url = route.route;
delete route.route;
$routeProvider.when(url, route);
}]);

View File

@@ -0,0 +1,20 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import inventoryScriptsList from './list/main';
import inventoryScriptsAdd from './add/main';
import inventoryScriptsEdit from './edit/main';
import list from './inventory-scripts.list';
import form from './inventory-scripts.form';
export default
angular.module('inventoryScripts', [
inventoryScriptsList.name,
inventoryScriptsAdd.name,
inventoryScriptsEdit.name
])
.factory('inventoryScriptsListObject', list)
.factory('inventoryScriptsFormObject', form);

View File

@@ -8,9 +8,7 @@ import Admins from "./lists/Admins";
import CloudCredentials from "./lists/CloudCredentials"; import CloudCredentials from "./lists/CloudCredentials";
import CompletedJobs from "./lists/CompletedJobs"; import CompletedJobs from "./lists/CompletedJobs";
import AllJobs from "./lists/AllJobs"; import AllJobs from "./lists/AllJobs";
import ConfigureTowerJobs from "./lists/ConfigureTowerJobs";
import Credentials from "./lists/Credentials"; import Credentials from "./lists/Credentials";
import CustomInventory from "./lists/CustomInventory";
import Groups from "./lists/Groups"; import Groups from "./lists/Groups";
import HomeGroups from "./lists/HomeGroups"; import HomeGroups from "./lists/HomeGroups";
import HomeHosts from "./lists/HomeHosts"; import HomeHosts from "./lists/HomeHosts";
@@ -39,9 +37,7 @@ export
CloudCredentials, CloudCredentials,
CompletedJobs, CompletedJobs,
AllJobs, AllJobs,
ConfigureTowerJobs,
Credentials, Credentials,
CustomInventory,
Groups, Groups,
HomeGroups, HomeGroups,
HomeHosts, HomeHosts,

View File

@@ -0,0 +1,287 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
// import listGenerator from 'tower/shared/list-generator/main';
export default
[ 'Wait', '$location' , '$compile', 'CreateDialog', 'generateList',
'GetBasePath' , 'SearchInit' , 'PaginateInit',
'SchedulesList',
'Rest' , 'ProcessErrors', 'managementJobsListObject', '$rootScope',
'transitionTo', 'Stream',
function( Wait, $location, $compile, CreateDialog, GenerateList,
GetBasePath, SearchInit, PaginateInit,
SchedulesList,
Rest, ProcessErrors, managementJobsListObject, $rootScope,
transitionTo, Stream) {
var scope = $rootScope.$new(),
parent_scope = scope,
defaultUrl = GetBasePath('system_job_templates'),
list = managementJobsListObject,
view = GenerateList, e;
scope.cleanupJob = true;
view.inject( list, {
mode: 'edit',
scope: scope,
showSearch: true
});
SearchInit({
scope: scope,
set: 'configure_jobs',
list: list,
url: defaultUrl
});
PaginateInit({
scope: scope,
list: list,
url: defaultUrl
});
scope.search(list.iterator);
scope.showActivity = function () {
Stream({ scope: scope });
};
// Cancel
scope.cancelConfigure = function () {
try {
$('#configure-tower-dialog').dialog('close');
$("#configure-save-button").remove();
}
catch(e) {
//ignore
}
if (scope.searchCleanup) {
scope.searchCleanup();
}
// if (!Empty(parent_scope) && parent_scope.restoreSearch) {
// parent_scope.restoreSearch();
// }
else {
Wait('stop');
}
};
scope.submitCleanupJob = function(id, name){
defaultUrl = GetBasePath('system_job_templates')+id+'/launch/';
CreateDialog({
id: 'prompt-for-days-facts',
title: name,
scope: scope,
width: 500,
height: 470,
minWidth: 200,
callback: 'PromptForDaysFacts',
onOpen: function(){
scope.keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
scope.granularity_keep_unit_choices = [{
"label" : "Days",
"value" : "d"
},
{
"label": "Weeks",
"value" : "w"
},
{
"label" : "Years",
"value" : "y"
}];
e = angular.element(document.getElementById('prompt_for_days_facts_form'));
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
$compile(e)(scope);
scope.keep_unit = scope.keep_unit_choices[0];
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
// this is a work-around for getting awMax to work (without
// clearing out the form)
scope.$watch('keep_amount', function(newVal) {
if (!newVal && newVal !== 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (isNaN(newVal)) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal < 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal > 9999) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else {
$('#prompt-for-days-facts-launch').prop("disabled", false);
}
});
scope.$watch('granularity_keep_amount', function(newVal2) {
if (!newVal2 && newVal2 !== 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (isNaN(newVal2)) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal2 < 0) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else if (newVal2 > 9999) {
$('#prompt-for-days-facts-launch').prop("disabled", true);
} else {
$('#prompt-for-days-facts-launch').prop("disabled", false);
}
});
},
buttons: [{
"label": "Cancel",
"onClick": function() {
$(this).dialog('close');
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "prompt-for-days-facts-cancel"
},{
"label": "Launch",
"onClick": function() {
var extra_vars = {
"older_than": scope.keep_amount+scope.keep_unit.value,
"granularity": scope.granularity_keep_amount+scope.granularity_keep_unit.value
},
data = {};
data.extra_vars = JSON.stringify(extra_vars);
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function() {
Wait('stop');
$("#prompt-for-days-facts").dialog("close");
$("#configure-tower-dialog").dialog('close');
$location.path('/jobs/');
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
});
},
"icon": "fa-rocket",
"class": "btn btn-primary",
"id": "prompt-for-days-facts-launch"
}]
});
if (scope.removePromptForDays) {
scope.removePromptForDays();
}
scope.removePromptForDays = scope.$on('PromptForDaysFacts', function() {
// $('#configure-tower-dialog').dialog('close');
$('#prompt-for-days-facts').show();
$('#prompt-for-days-facts').dialog('open');
Wait('stop');
});
};
scope.submitJob = function (id, name) {
Wait('start');
if(this.configure_job.job_type === "cleanup_facts"){
scope.submitCleanupJob(id, name);
}
else {
defaultUrl = GetBasePath('system_job_templates')+id+'/launch/';
CreateDialog({
id: 'prompt-for-days' ,
title: name,
scope: scope,
width: 500,
height: 300,
minWidth: 200,
callback: 'PromptForDays',
onOpen: function(){
e = angular.element(document.getElementById('prompt_for_days_form'));
scope.prompt_for_days_form.days_to_keep.$setViewValue(30);
$compile(e)(scope);
// this is a work-around for getting awMax to work (without
// clearing out the form)
scope.$watch('days_to_keep', function(newVal) { // oldVal, scope) { // unused params get caught by jshint
if (!newVal && newVal !== 0) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (isNaN(newVal)) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (newVal < 0) {
$('#prompt-for-days-launch').prop("disabled", true);
} else if (newVal > 9999) {
$('#prompt-for-days-launch').prop("disabled", true);
} else {
$('#prompt-for-days-launch').prop("disabled", false);
}
});
},
buttons: [{
"label": "Cancel",
"onClick": function() {
$(this).dialog('close');
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "prompt-for-days-cancel"
},{
"label": "Launch",
"onClick": function() {
var extra_vars = {"days": scope.days_to_keep },
data = {};
data.extra_vars = JSON.stringify(extra_vars);
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function() {
Wait('stop');
$("#prompt-for-days").dialog("close");
// $("#configure-tower-dialog").dialog('close');
$location.path('/jobs/');
})
.error(function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
});
},
"icon": "fa-rocket",
"class": "btn btn-primary",
"id": "prompt-for-days-launch"
}]
});
if (scope.removePromptForDays) {
scope.removePromptForDays();
}
scope.removePromptForDays = scope.$on('PromptForDays', function() {
// $('#configure-tower-dialog').dialog('close');
$('#prompt-for-days').show();
$('#prompt-for-days').dialog('open');
Wait('stop');
});
}
};
scope.configureSchedule = function() {
transitionTo('managementJobsSchedule', {
management_job: this.configure_job
});
};
parent_scope.refreshJobs = function(){
scope.search(SchedulesList.iterator);
};
}
];

View File

@@ -0,0 +1,8 @@
<breadcrumbs>
<breadcrumb path="/setup" title="Setup"></breadcrumb>
<breadcrumb path="/management_jobs" title="Management Jobs" current='true'></breadcrumb>
</breadcrumbs>
<div class="tab-pane" id="management_jobs">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@@ -0,0 +1,19 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'managementJobsList',
route: '/management_jobs',
templateUrl: templateUrl('management-jobs/list/list'),
controller: 'listController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
}
};

View File

@@ -0,0 +1,17 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './list.route';
import controller from './list.controller';
export default
angular.module('managementJobsList', [])
.controller('listController', controller)
.config(['$routeProvider', function($routeProvider) {
var url = route.route;
delete route.route;
$routeProvider.when(url, route);
}]);

View File

@@ -0,0 +1,16 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import managementJobsList from './list/main';
import managementJobsSchedule from './schedule/main';
import list from './management-jobs.list';
export default
angular.module('managementJobs', [
managementJobsList.name,
managementJobsSchedule.name
])
.factory('managementJobsListObject', list);

View File

@@ -4,23 +4,15 @@
* All Rights Reserved * All Rights Reserved
*************************************************/ *************************************************/
export default function(){
return {
export default
angular.module('ConfigureTowerJobsListDefinition', [])
.value('ConfigureTowerJobsList', {
name: 'configure_jobs', name: 'configure_jobs',
iterator: 'configure_job', iterator: 'configure_job',
selectTitle: 'Configure Tower Jobs',
editTitle: 'Configure Tower Jobs',
index: false, index: false,
hover: true, hover: true,
fields: { fields: {
name: { name: {
// key: true,
label: 'Name', label: 'Name',
columnClass: 'col-sm-4 col-xs-4' columnClass: 'col-sm-4 col-xs-4'
}, },
@@ -29,6 +21,15 @@ export default
columnClass: 'col-sm-6 col-xs-6 hidden-sm hidden-xs' columnClass: 'col-sm-6 col-xs-6 hidden-sm hidden-xs'
} }
}, },
actions: {
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
icon: "icon-comments-alt",
mode: 'edit',
awFeature: 'activity_streams'
}
},
fieldActions: { fieldActions: {
submit: { submit: {
label: 'Launch', label: 'Launch',
@@ -40,16 +41,10 @@ export default
schedule: { schedule: {
label: 'Schedule', label: 'Schedule',
mode: 'all', mode: 'all',
ngClick: 'configureSchedule(configure_job.id, configure_job.name)', // '#/job_templates/{{ configure_job.id }}/schedules', ngClick: 'configureSchedule()',
awToolTip: 'Schedule future job template runs', awToolTip: 'Schedule future job template runs',
dataPlacement: 'top', dataPlacement: 'top',
} }
// delete: {
// label: 'Delete Schedule',
// mode: 'all',
// ngClick: 'deleteSystemSchedule(configure_job.id)', // '#/job_templates/{{ configure_job.id }}/schedules',
// awToolTip: 'Delete the schedule ',
// dataPlacement: 'top'
// }
} }
}); };
}

View File

@@ -0,0 +1,17 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './schedule.route';
import controller from './schedule.controller';
export default
angular.module('managementJobsSchedule', [])
.controller('scheduleController', controller)
.config(['$routeProvider', function($routeProvider) {
var url = route.route;
delete route.route;
$routeProvider.when(url, route);
}]);

View File

@@ -0,0 +1,93 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name controllers.function:Schedules
* @description This controller's for schedules
*/
export default [
'$scope', '$location', '$routeParams', 'SchedulesList', 'Rest',
'ProcessErrors', 'GetBasePath', 'Wait','LoadSchedulesScope', 'GetChoices',
'Stream', 'management_job', '$rootScope',
function($scope, $location, $routeParams, SchedulesList, Rest,
ProcessErrors, GetBasePath, Wait, LoadSchedulesScope, GetChoices,
Stream, management_job, $rootScope) {
var base, id, url, parentObject;
$scope.management_job = management_job;
base = $location.path().replace(/^\//, '').split('/')[0];
// GetBasePath('management_job') must map to 'system_job_templates'
// to match the api syntax
$rootScope.defaultUrls.management_jobs = 'api/v1/system_job_templates/';
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;
LoadSchedulesScope({
parent_scope: $scope,
scope: $scope,
list: SchedulesList,
id: 'management_jobs_schedule',
url: url,
pageSize: 20
});
});
if ($scope.removeChoicesReady) {
$scope.removeChocesReady();
}
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
// Load the parent object
id = $routeParams.management_job;
url = GetBasePath('system_job_templates') + 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('system_jobs'),
field: 'type',
variable: 'type_choices',
callback: 'choicesReady'
});
}
];

View File

@@ -0,0 +1,12 @@
<breadcrumbs>
<breadcrumb path="/setup" title="Setup"></breadcrumb>
<breadcrumb path="/management_jobs" title="{{management_job.name}}"></breadcrumb>
<breadcrumb path="/management_jobs/{{management_job.id}}" title="Schedules" current='true'></breadcrumb>
</breadcrumbs>
<div class="tab-pane" id="management_jobs_schedule">
<div ng-cloak id="htmlTemplate"></div>
</div>
<div ng-include="'/static/partials/logviewer.html'"></div>
<div ng-include="'/static/partials/schedule_dialog.html'"></div>

View File

@@ -0,0 +1,46 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'managementJobsSchedule',
route: '/management_jobs/:management_job/schedules',
templateUrl: templateUrl('management-jobs/schedule/schedule'),
controller: 'scheduleController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
management_job:
[ '$route',
'$q',
'Rest',
'GetBasePath',
'ProcessErrors',
function($route, $q, rest, getBasePath, ProcessErrors) {
if ($route.current.hasModelKey('management_job')) {
return $q.when($route.current.params.model.management_job);
}
var managementJobId = $route.current.params.management_job;
var url = getBasePath('system_job_templates') + managementJobId + '/';
rest.setUrl(url);
return rest.get()
.then(function(data) {
return data.data;
}).catch(function (response) {
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get inventory script info. GET returned status: ' +
response.status
});
});
}
]
}
};

View File

@@ -1,31 +0,0 @@
<div id="configure-jobs" ><div>
<div class="tab-pane" id="configure-schedules-tab">
<div id="configure-schedules-overlay"></div>
<div id="configure-schedules-list"></div>
<div id="configure-schedules-form-container">
<div id="configure-schedules-title">
<h4 ng-bind="schedulesTitle"></h4>
<button type="button" class="close pull-right" ng-click="cancelScheduleForm()">x</button>
</div>
<div id="configure-schedules-form-container-body">
<div id="configure-schedules-form"></div>
<div id="configure-schedules-detail"></div>
</div>
<div id="configure-schedules-buttons" >
<a id="configure-schedules-flip-link" ng-show="formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-search-plus"></i> View Details</a>
<a id="configure-schedules-flip-link" ng-show="!formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-arrow-circle-left"></i> Back to options</a>
<button type="button" class="btn btn-default btn-sm" id="configure-schedule-delete-button" ng-show="mode==='edit'" ng-click="deleteSystemSchedule($event)"><i class="fa fa-trash-o"></i> Delete</button>
<button type="button" class="btn btn-default btn-sm" id="configure-reset-button" ng-click="cancelScheduleForm()"><i class="fa fa-times"></i> Cancel</button>
<button type="button" class="btn btn-primary btn-sm" id="configure-save-button" ng-click="saveScheduleForm()"><i class="fa fa-check"></i> Save</button>
</div>
</div>
</div>
<!-- </div> -->
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form">
How many days of data would you like to <b>keep</b>? <br>
<input type="number" min="0" id="days_to_keep" name="days_to_keep" value="30" ng-required="true" class="form-control ng-pristine ng-invalid-required ng-invalid" style="margin-top:10px;">
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && copy_form.new_copy_name.$error.required">Please enter the number of days you would like to keep this data.</div></input>
</form>
</div>

View File

@@ -1,4 +1,4 @@
<div id="scheduler-modal-dialog" title="Edit Schedule"> <div id="scheduler-modal-dialog">
<ul id="scheduler-tabs" class="nav nav-tabs"> <ul id="scheduler-tabs" class="nav nav-tabs">
<li class="active"><a href="#schedule" id="schedule-link" data-toggle="tab" ng-click="toggleTab($event, 'schedule-link', 'scheduler-tabs')">Options</a></li> <li class="active"><a href="#schedule" id="schedule-link" data-toggle="tab" ng-click="toggleTab($event, 'schedule-link', 'scheduler-tabs')">Options</a></li>
<li><a href="#occurrences" id="occurrence-link" data-toggle="tab" ng-click="toggleTab($event, 'occurrence-link', 'scheduler-tabs')">Details</a></li> <li><a href="#occurrences" id="occurrence-link" data-toggle="tab" ng-click="toggleTab($event, 'occurrence-link', 'scheduler-tabs')">Details</a></li>

View File

@@ -4,8 +4,6 @@ import icon from '../shared/icon/main';
export default export default
angular.module('setupMenu', angular.module('setupMenu',
[ 'AboutAnsibleHelpModal', [ 'AboutAnsibleHelpModal',
'ConfigureTowerHelper',
'CreateCustomInventoryHelper',
icon.name icon.name
]) ])
.config(['$routeProvider', function($routeProvider) { .config(['$routeProvider', function($routeProvider) {
@@ -13,4 +11,3 @@ export default
delete route.route; delete route.route;
$routeProvider.when(url, route); $routeProvider.when(url, route);
}]); }]);

View File

@@ -53,32 +53,26 @@
</p> </p>
</div> </div>
</a> </a>
<button class="SetupItem SetupItem--button SetupItem--aside HoverIcon Media" ng-click="showManagementJobsModal()" ng-if="user_is_superuser"> <a link-to="managementJobsList" class="SetupItem SetupItem--aside HoverIcon Media">
<div class="SetupItem-firefoxFlexButtonFix"> <i class="HoverIcon-icon HoverIcon-icon--opacity HoverIcon-icon--color Media-figure SetupItem-icon SetupItem-icon--aside" ng-if="user_is_superuser">
<i class="HoverIcon-icon HoverIcon-icon--opacity HoverIcon-icon--color Media-figure SetupItem-icon SetupItem-icon--aside "> <aw-icon name="ManagementJobs"></aw-icon>
<aw-icon name="ManagementJobs"></aw-icon> </i>
</i> <div class="Media-block">
<div class="Media-block"> <h4 class="SetupItem-title SetupItem-title--aside">Management Jobs</h4>
<h4 class="SetupItem-title SetupItem-title--aside">Management Jobs</h4> <p class="SetupItem-description SetupItem-description--aside">
<p class="SetupItem-description SetupItem-description--aside"> Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info.
Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info.
</p>
</div>
</div> </div>
</button> </a>
<button class="SetupItem SetupItem--button SetupItem--aside HoverIcon Media" ng-click="showInventoryScriptsModal()" ng-if="user_is_superuser"> <a link-to="inventoryScriptsList" class="SetupItem SetupItem--aside HoverIcon Media">
<div class="SetupItem-firefoxFlexButtonFix"> <i class="HoverIcon-icon HoverIcon-icon--opacity HoverIcon-icon--color Media-figure SetupItem-icon SetupItem-icon--aside ">
<i class="HoverIcon-icon HoverIcon-icon--opacity HoverIcon-icon--color Media-figure SetupItem-icon SetupItem-icon--aside "> <aw-icon name="InventoryScripts"></aw-icon>
<aw-icon name="InventoryScripts"></aw-icon> </i>
</i> <div class="Media-block">
<div class="Media-block"> <h4 class="SetupItem-title SetupItem-title--aside">Inventory Scripts</h4>
<h4 class="SetupItem-title SetupItem-title--aside">Inventory Scripts</h4> <p class="SetupItem-description SetupItem-description--aside">
<p class="SetupItem-description SetupItem-description--aside"> Create and edit scripts to dynamically load hosts from any source.
Create and edit scripts to dynamically load hosts from any source.
</p>
</div>
</div> </div>
</button> </a>
<a link-to="license" class="SetupItem SetupItem--button SetupItem--aside SetupItem--noIcon"> <a link-to="license" class="SetupItem SetupItem--button SetupItem--aside SetupItem--noIcon">
<h4 class="SetupItem-title SetupItem-title--aside">View Your License</h4> <h4 class="SetupItem-title SetupItem-title--aside">View Your License</h4>
</a> </a>

View File

@@ -2,26 +2,11 @@ export default
[ '$scope', [ '$scope',
'$rootScope', '$rootScope',
'AboutAnsibleHelp', 'AboutAnsibleHelp',
'ConfigureTower',
'CreateCustomInventory',
function( function(
$scope, $scope,
$rootScope, $rootScope,
showAboutModal, showAboutModal
configureTower,
showInventoryScriptsModal
) { ) {
$scope.showAboutModal = showAboutModal; $scope.showAboutModal = showAboutModal;
$scope.showManagementJobsModal =
configureTower.bind(null,
{ scope: $rootScope,
parent_scope: $rootScope
});
$scope.showInventoryScriptsModal = showInventoryScriptsModal.bind(null,
{ parent_scope: $rootScope
});
} }
]; ];

View File

@@ -67,8 +67,7 @@ angular.module('AuthService', ['ngCookies', Utilities.name])
var scope = angular.element(document.getElementById('main-view')).scope(); var scope = angular.element(document.getElementById('main-view')).scope();
scope.$destroy(); scope.$destroy();
//$rootScope.$destroy(); //$rootScope.$destroy();
$cookieStore.remove('token_expires');
$cookieStore.remove('current_user');
if($cookieStore.get('lastPath')==='/portal'){ if($cookieStore.get('lastPath')==='/portal'){
$cookieStore.put( 'lastPath', '/portal'); $cookieStore.put( 'lastPath', '/portal');
@@ -84,6 +83,9 @@ angular.module('AuthService', ['ngCookies', Utilities.name])
$rootScope.lastPath = '/home'; $rootScope.lastPath = '/home';
} }
$rootScope.lastUser = $cookieStore.get('current_user').id;
$cookieStore.remove('token_expires');
$cookieStore.remove('current_user');
$cookieStore.remove('token'); $cookieStore.remove('token');
$cookieStore.put('userLoggedIn', false); $cookieStore.put('userLoggedIn', false);
$cookieStore.put('sessionExpired', false); $cookieStore.put('sessionExpired', false);

View File

@@ -154,31 +154,7 @@
<div id="help-modal-dialog" style="display: none;"></div> <div id="help-modal-dialog" style="display: none;"></div>
<div id="about-modal-dialog" style="display: none;" ng-include=" '{{ STATIC_URL }}assets/cowsay-about.html ' "></div> <div id="about-modal-dialog" style="display: none;" ng-include=" '{{ STATIC_URL }}assets/cowsay-about.html ' "></div>
<div id="custom-script-dialog" style="display:none;" > </div>
<div id='configure-tower-dialog' style="display:none" >
<div id="configure-jobs" ></div>
<div class="tab-pane" id="configure-schedules-tab">
<div id="configure-schedules-overlay"></div>
<div id="configure-schedules-list"></div>
<div id="configure-schedules-form-container">
<div id="configure-schedules-title">
<h4 ng-bind="schedulesTitle"></h4>
<button type="button" class="close pull-right" ng-click="cancelScheduleForm()">x</button>
</div>
<div id="configure-schedules-form-container-body">
<div id="configure-schedules-form"></div>
<div id="configure-schedules-detail"></div>
</div>
<div id="configure-schedules-buttons" >
<a id="configure-schedules-flip-link" ng-show="formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-search-plus"></i> View Details</a>
<a id="configure-schedules-flip-link" ng-show="!formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-arrow-circle-left"></i> Back to options</a>
<button type="button" class="btn btn-default btn-sm" id="configure-schedule-delete-button" ng-show="mode==='edit'" ng-click="deleteSystemSchedule($event)"><i class="fa fa-trash-o"></i> Delete</button>
<button type="button" class="btn btn-default btn-sm" id="configure-cancel-button" ng-click="cancelScheduleForm()"><i class="fa fa-times"></i> Cancel</button>
<!-- <button type="button" class="btn btn-primary btn-sm" id="configure-save-button" ng-click="saveScheduleForm()"><i class="fa fa-check"></i> Save</button> -->
</div>
</div>
</div>
</div>
<div id="prompt-for-days" style="display:none"> <div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form"> <form name="prompt_for_days_form" id="prompt_for_days_form">
Set how many days of data should be retained. <br> Set how many days of data should be retained. <br>