Finixhed applying jsHint linting to js files. Created initial Gruntfile.js build script and package.json script for keeping track of required node modules.

This commit is contained in:
vagrant
2014-02-11 04:52:15 +00:00
parent f891c30ebb
commit d25e712e21
94 changed files with 10589 additions and 11059 deletions

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@ awx/public/static
awx/ui/static/js/awx-min.js awx/ui/static/js/awx-min.js
awx/ui/static/css/awx.min.css awx/ui/static/css/awx.min.css
env/* env/*
node_modules/**
build build
deb-build deb-build
dist dist

32
Gruntfile.js Normal file
View File

@@ -0,0 +1,32 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('./package.json'),
jshint: {
options: {
jshintrc: '.jshintrc'
},
uses_defaults: ['awx/ui/static/js/*','awx/ui/static/lib/ansible/*', '!awx/ui/static/js/awx-min.js']
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */'
},
my_target: {
files: {
'awx/ui/static/js/awx-min.js': ['awx/ui/static/js/**/*.js', 'awx/ui/static/lib/ansible/*.js',
'!awx/ui/static/js/awx-min.js']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['jshint', 'uglify']);
}

View File

@@ -4,23 +4,22 @@
* Our main application mdoule. Declare application routes and perform initialization chores. * Our main application mdoule. Declare application routes and perform initialization chores.
* *
*/ */
var urlPrefix = $basePath; var urlPrefix = $basePath;
angular.module('ansible', [ angular.module('ansible', [
'RestServices', 'RestServices',
'AuthService', 'AuthService',
'Utilities', 'Utilities',
'OrganizationFormDefinition', 'OrganizationFormDefinition',
'UserFormDefinition', 'UserFormDefinition',
'FormGenerator', 'FormGenerator',
'OrganizationListDefinition', 'OrganizationListDefinition',
'UserListDefinition', 'UserListDefinition',
'UserHelper', 'UserHelper',
'ListGenerator', 'ListGenerator',
'PromptDialog', 'PromptDialog',
'ApiLoader', 'ApiLoader',
'RelatedSearchHelper', 'RelatedSearchHelper',
'SearchHelper', 'SearchHelper',
'PaginationHelpers', 'PaginationHelpers',
'RefreshHelper', 'RefreshHelper',
@@ -58,6 +57,7 @@ angular.module('ansible', [
'JobFormDefinition', 'JobFormDefinition',
'JobEventsListDefinition', 'JobEventsListDefinition',
'JobEventDataDefinition', 'JobEventDataDefinition',
'JobEventsFormDefinition',
'JobHostDefinition', 'JobHostDefinition',
'JobSummaryDefinition', 'JobSummaryDefinition',
'ParseHelper', 'ParseHelper',
@@ -75,9 +75,7 @@ angular.module('ansible', [
'ObjectCountWidget', 'ObjectCountWidget',
'StreamWidget', 'StreamWidget',
'JobsHelper', 'JobsHelper',
'InventoryStatusDefinition',
'InventoryGroupsHelpDefinition', 'InventoryGroupsHelpDefinition',
'InventoryHostsHelpDefinition',
'InventoryTree', 'InventoryTree',
'CredentialsHelper', 'CredentialsHelper',
'TimerService', 'TimerService',
@@ -85,253 +83,368 @@ angular.module('ansible', [
'HomeGroupListDefinition', 'HomeGroupListDefinition',
'HomeHostListDefinition', 'HomeHostListDefinition',
'ActivityDetailDefinition' 'ActivityDetailDefinition'
]) ])
.config(['$routeProvider', function($routeProvider) { .config(['$routeProvider',
$routeProvider. function ($routeProvider) {
when('/jobs', $routeProvider.
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsListCtrl }). when('/jobs', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobsListCtrl'
}).
when('/jobs/:id', when('/jobs/:id', {
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsEdit }). templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobsEdit'
}).
when('/jobs/:id/job_events', when('/jobs/:id/job_events', {
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsList }). templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobEventsList'
}).
when('/jobs/:id/job_host_summaries', when('/jobs/:id/job_host_summaries', {
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }). templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobHostSummaryList'
when('/jobs/:job_id/job_events/:event_id', }).
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsEdit }).
when('/job_templates', when('/jobs/:job_id/job_events/:event_id', {
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesList }). templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobEventsEdit'
when('/job_templates/add', }).
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesAdd }).
when('/job_templates/:id', when('/job_templates', {
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesEdit }). templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesList'
}).
when('/projects', when('/job_templates/add', {
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsList }). templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesAdd'
}).
when('/projects/add', when('/job_templates/:id', {
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsAdd }). templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesEdit'
}).
when('/projects/:id', when('/projects', {
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsEdit }). templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsList'
}).
when('/projects/:project_id/organizations', when('/projects/add', {
{ templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsList }). templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsAdd'
}).
when('/projects/:project_id/organizations/add', when('/projects/:id', {
{ templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsAdd }). templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsEdit'
}).
when('/hosts/:id/job_host_summaries', when('/projects/:project_id/organizations', {
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }). templateUrl: urlPrefix + 'partials/projects.html',
controller: 'OrganizationsList'
}).
when('/inventories', when('/projects/:project_id/organizations/add', {
{ templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesList }). templateUrl: urlPrefix + 'partials/projects.html',
controller: 'OrganizationsAdd'
}).
when('/inventories/add', when('/hosts/:id/job_host_summaries', {
{ templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesAdd }). templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobHostSummaryList'
}).
when('/inventories/:inventory_id', when('/inventories', {
{ templateUrl: urlPrefix + 'partials/inventory-edit.html', controller: InventoriesEdit }). templateUrl: urlPrefix + 'partials/inventories.html',
controller: 'InventoriesList'
}).
when('/organizations', { templateUrl: urlPrefix + 'partials/organizations.html', when('/inventories/add', {
controller: OrganizationsList }). templateUrl: urlPrefix + 'partials/inventories.html',
controller: 'InventoriesAdd'
}).
when('/organizations/add', { templateUrl: urlPrefix + 'partials/organizations.html', when('/inventories/:inventory_id', {
controller: OrganizationsAdd }). templateUrl: urlPrefix + 'partials/inventory-edit.html',
controller: 'InventoriesEdit'
}).
when('/organizations/:organization_id', { templateUrl: urlPrefix + 'partials/organizations.html', when('/organizations', {
controller: OrganizationsEdit }). templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsList'
}).
when('/organizations/:organization_id/admins', { templateUrl: urlPrefix + 'partials/organizations.html', when('/organizations/add', {
controller: AdminsList }). templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsAdd'
}).
when('/organizations/:organization_id/users', { templateUrl: urlPrefix + 'partials/users.html', when('/organizations/:organization_id', {
controller: UsersList }). templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsEdit'
}).
when('/organizations/:organization_id/users/add', { templateUrl: urlPrefix + 'partials/users.html', when('/organizations/:organization_id/admins', {
controller: UsersAdd }). templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'AdminsList'
}).
when('/organizations/:organization_id/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', when('/organizations/:organization_id/users', {
controller: UsersEdit }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersList'
}).
when('/teams', { templateUrl: urlPrefix + 'partials/teams.html', when('/organizations/:organization_id/users/add', {
controller: TeamsList }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersAdd'
}).
when('/teams/add', { templateUrl: urlPrefix + 'partials/teams.html', when('/organizations/:organization_id/users/:user_id', {
controller: TeamsAdd }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersEdit'
}).
when('/teams/:team_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams', {
controller: TeamsEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsList'
when('/teams/:team_id/permissions/add', { templateUrl: urlPrefix + 'partials/teams.html', }).
controller: PermissionsAdd }).
when('/teams/:team_id/permissions', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/add', {
controller: PermissionsList }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsAdd'
}).
when('/teams/:team_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id', {
controller: PermissionsEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsEdit'
}).
when('/teams/:team_id/users', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/permissions/add', {
controller: UsersList }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsAdd'
}).
when('/teams/:team_id/users/:user_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/permissions', {
controller: UsersEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsList'
}).
when('/teams/:team_id/projects', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/permissions/:permission_id', {
controller: ProjectsList }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsEdit'
}).
when('/teams/:team_id/projects/add', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/users', {
controller: ProjectsAdd }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'UsersList'
}).
when('/teams/:team_id/projects/:project_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/users/:user_id', {
controller: ProjectsEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'UsersEdit'
when('/teams/:team_id/credentials', { templateUrl: urlPrefix + 'partials/teams.html', }).
controller: CredentialsList }).
when('/teams/:team_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/projects', {
controller: CredentialsAdd }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsList'
}).
when('/teams/:team_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/teams/:team_id/projects/add', {
controller: CredentialsEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsAdd'
}).
when('/credentials', { templateUrl: urlPrefix + 'partials/credentials.html', when('/teams/:team_id/projects/:project_id', {
controller: CredentialsList }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsEdit'
}).
when('/credentials/add', { templateUrl: urlPrefix + 'partials/credentials.html', when('/teams/:team_id/credentials', {
controller: CredentialsAdd }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsList'
}).
when('/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/credentials.html', when('/teams/:team_id/credentials/add', {
controller: CredentialsEdit }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsAdd'
when('/users', { templateUrl: urlPrefix + 'partials/users.html', }).
controller: UsersList }).
when('/users/add', { templateUrl: urlPrefix + 'partials/users.html', when('/teams/:team_id/credentials/:credential_id', {
controller: UsersAdd }). templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsEdit'
}).
when('/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', when('/credentials', {
controller: UsersEdit }). templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsList'
}).
when('/users/:user_id/credentials', { templateUrl: urlPrefix + 'partials/users.html', when('/credentials/add', {
controller: CredentialsList }). templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsAdd'
}).
when('/users/:user_id/permissions/add', { templateUrl: urlPrefix + 'partials/users.html', when('/credentials/:credential_id', {
controller: PermissionsAdd }). templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsEdit'
}).
when('/users/:user_id/permissions', { templateUrl: urlPrefix + 'partials/users.html', when('/users', {
controller: PermissionsList }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersList'
}).
when('/users/:user_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/users.html', when('/users/add', {
controller: PermissionsEdit }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersAdd'
}).
when('/users/:user_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', when('/users/:user_id', {
controller: CredentialsAdd }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersEdit'
}).
when('/teams/:user_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', when('/users/:user_id/credentials', {
controller: CredentialsEdit }). templateUrl: urlPrefix + 'partials/users.html',
controller: 'CredentialsList'
}).
when('/login', { templateUrl: urlPrefix + 'partials/home.html', controller: Authenticate }). when('/users/:user_id/permissions/add', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'PermissionsAdd'
}).
when('/logout', { templateUrl: urlPrefix + 'partials/home.html', controller: Authenticate }). when('/users/:user_id/permissions', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'PermissionsList'
}).
when('/home', { templateUrl: urlPrefix + 'partials/home.html', controller: Home }). when('/users/:user_id/permissions/:permission_id', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'PermissionsEdit'
}).
when('/home/groups', { templateUrl: urlPrefix + 'partials/subhome.html', controller: HomeGroups }). when('/users/:user_id/credentials/add', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsAdd'
}).
when('/home/hosts', { templateUrl: urlPrefix + 'partials/subhome.html', controller: HomeHosts }). when('/teams/:user_id/credentials/:credential_id', {
templateUrl: urlPrefix + 'partials/teams.html',
otherwise({redirectTo: '/home'}); controller: 'CredentialsEdit'
}]) }).
.run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization','LoadBasePaths', 'ViewLicense',
'Timer', 'ClearScope', 'HideStream',
function($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense,
Timer, ClearScope, HideStream) {
LoadBasePaths();
$rootScope.breadcrumbs = new Array();
$rootScope.crumbCache = new Array();
$rootScope.sessionTimer = Timer.init();
$rootScope.$on("$routeChangeStart", function(event, next, current) { when('/login', {
templateUrl: urlPrefix + 'partials/home.html',
controller: 'Authenticate'
}).
// Before navigating away from current tab, make sure the primary view is visible when('/logout', {
if ($('#stream-container').is(':visible')) { templateUrl: urlPrefix + 'partials/home.html',
HideStream(); controller: 'Authenticate'
} }).
// On each navigation request, check that the user is logged in when('/home', {
if ( !/^\/(login|logout)/.test($location.path()) ) { templateUrl: urlPrefix + 'partials/home.html',
// capture most recent URL, excluding login/logout controller: 'Home'
$rootScope.lastPath = $location.path(); }).
$cookieStore.put('lastPath', $location.path());
}
if (Authorization.isUserLoggedIn() == false) { when('/home/groups', {
if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) { templateUrl: urlPrefix + 'partials/subhome.html',
$location.path('/login'); controller: 'HomeGroups'
} }).
}
else if ($rootScope.sessionTimer.isExpired()) { when('/home/hosts', {
if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) { templateUrl: urlPrefix + 'partials/subhome.html',
$rootScope.sessionTimer.expireSession(); controller: 'HomeHosts'
$location.path('/login'); }).
}
} otherwise({
else { redirectTo: '/home'
if ($rootScope.current_user == undefined || $rootScope.current_user == null) { });
Authorization.restoreUserInfo(); //user must have hit browser refresh }
} ])
CheckLicense(); .run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'ViewLicense',
} 'Timer', 'ClearScope', 'HideStream',
function ($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense,
// Make the correct tab active Timer, ClearScope, HideStream) {
var base = $location.path().replace(/^\//,'').split('/')[0];
if (base == '') { LoadBasePaths();
base = 'home';
} $rootScope.breadcrumbs = [];
else { $rootScope.crumbCache = [];
base.replace(/\_/g,' '); $rootScope.sessionTimer = Timer.init();
}
$('.nav-tabs a[href="#' + base + '"]').tab('show'); $rootScope.$on("$routeChangeStart", function (event, next) {
// Before navigating away from current tab, make sure the primary view is visible
if ($('#stream-container').is(':visible')) {
HideStream();
}
// On each navigation request, check that the user is logged in
if (!/^\/(login|logout)/.test($location.path())) {
// capture most recent URL, excluding login/logout
$rootScope.lastPath = $location.path();
$cookieStore.put('lastPath', $location.path());
}
if (Authorization.isUserLoggedIn() === false) {
if (next.templateUrl !== (urlPrefix + 'partials/login.html')) {
$location.path('/login');
}
} else if ($rootScope.sessionTimer.isExpired()) {
if (next.templateUrl !== (urlPrefix + 'partials/login.html')) {
$rootScope.sessionTimer.expireSession();
$location.path('/login');
}
} else {
if ($rootScope.current_user === undefined || $rootScope.current_user === null) {
Authorization.restoreUserInfo(); //user must have hit browser refresh
}
CheckLicense();
}
// Make the correct tab active
var base = $location.path().replace(/^\//, '').split('/')[0];
if (base === '') {
base = 'home';
} else {
base.replace(/\_/g, ' ');
}
$('.nav-tabs a[href="#' + base + '"]').tab('show');
}); });
if (!Authorization.getToken()) { if (!Authorization.getToken()) {
// When the app first loads, redirect to login page // When the app first loads, redirect to login page
$rootScope.sessionExpired = false; $rootScope.sessionExpired = false;
$cookieStore.put('sessionExpired', false); $cookieStore.put('sessionExpired', false);
$location.path('/login'); $location.path('/login');
} } else {
else { // If browser refresh, set the user_is_superuser value
// If browser refresh, set the user_is_superuser value $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser');
$rootScope['user_is_superuser'] = Authorization.getUserInfo('is_superuser');
}
// If browser refresh, activate the correct tab
var base = ($location.path().replace(/^\//,'').split('/')[0]);
if (base == '') {
base = 'home';
$location.path('/home');
}
else {
base.replace(/\_/g,' ');
}
$('.nav-tabs a[href="#' + base + '"]').tab('show');
$rootScope.viewCurrentUser = function() {
$location.path('/users/' + $rootScope.current_user.id);
} }
$rootScope.viewLicense = function() { // If browser refresh, activate the correct tab
//$location.path('/license'); var base = ($location.path().replace(/^\//, '').split('/')[0]);
ViewLicense(); if (base === '') {
} base = 'home';
}]); $location.path('/home');
} else {
base.replace(/\_/g, ' ');
}
$('.nav-tabs a[href="#' + base + '"]').tab('show');
$rootScope.viewCurrentUser = function () {
$location.path('/users/' + $rootScope.current_user.id);
};
$rootScope.viewLicense = function () {
//$location.path('/license');
ViewLicense();
};
}
]);

View File

@@ -1,4 +1,4 @@
/************************************ /**********************************************************************
* *
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
@@ -8,19 +8,21 @@
* *
*/ */
var $AnsibleConfig = /*jshint unused:false */
{
tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
debug_mode: true, // Enable console logging messages var $AnsibleConfig = {
password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong.
// This value controls progress bar colors:
// 0 to password_strength - 15 = red;
// password_strength - 15 to password_strength = yellow
// > password_strength = green
// It also controls password validation. Passwords are rejected if the score is not > password_strength.
session_timeout: 1800 // Number of seconds before an inactive session is automatically timed out and forced to log in again. tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
// Separate from time out value set in API.
} debug_mode: true, // Enable console logging messages
password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong.
// This value controls progress bar colors:
// 0 to password_strength - 15 = red;
// password_strength - 15 to password_strength = yellow
// > password_strength = green
// It also controls password validation. Passwords are rejected if the score is not > password_strength.
session_timeout: 1800 // Number of seconds before an inactive session is automatically timed out and forced to log in again.
// Separate from time out value set in API.
};

View File

@@ -1,12 +1,12 @@
/************************************ /*************************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
*
* Credentials.js * Credentials.js
* *
* Controller functions for the Credential model. * Controller functions for the Credential model.
* *
*/ */
'use strict'; 'use strict';
function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList, function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList,
@@ -114,10 +114,10 @@ function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success(function (data, status, headers, config) { .success(function () {
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error(function (data, status, headers, config) { .error(function (data, status) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, { ProcessErrors(scope, data, status, null, {
hdr: 'Error!', hdr: 'Error!',
@@ -143,7 +143,7 @@ CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$routeP
function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath, GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath,
GetChoices, Empty, KindChange, OwnerChange, FormSave, DebugForm) { GetChoices, Empty, KindChange, OwnerChange, FormSave) {
ClearScope('tree-form'); ClearScope('tree-form');
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -152,7 +152,6 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
var form = CredentialForm, var form = CredentialForm,
generator = GenerateForm, generator = GenerateForm,
scope = generator.inject(form, { mode: 'add', related: false }), scope = generator.inject(form, { mode: 'add', related: false }),
base = $location.path().replace(/^\//, '').split('/')[0],
defaultUrl = GetBasePath('credentials'), defaultUrl = GetBasePath('credentials'),
url; url;
@@ -193,10 +192,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
url = GetBasePath('users') + $routeParams.user_id + '/'; url = GetBasePath('users') + $routeParams.user_id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
.success(function (data, status, headers, config) { .success(function (data) {
scope.user_username = data.username; scope.user_username = data.username;
}) })
.error(function (data, status, headers, config) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status
}); });
}); });
@@ -210,10 +209,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
url = GetBasePath('teams') + $routeParams.team_id + '/'; url = GetBasePath('teams') + $routeParams.team_id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
.success(function (data, status, headers, config) { .success(function (data) {
scope.team_name = data.name; scope.team_name = data.name;
}) })
.error(function (data, status, headers, config) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status
}); });
}); });
@@ -289,7 +288,7 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm', CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'SearchInit', 'PaginateInit', 'LookUpInit', 'UserList', 'TeamList', 'GetBasePath', 'GetChoices', 'Empty', 'SearchInit', 'PaginateInit', 'LookUpInit', 'UserList', 'TeamList', 'GetBasePath', 'GetChoices', 'Empty',
'KindChange', 'OwnerChange', 'FormSave', 'DebugForm' 'KindChange', 'OwnerChange', 'FormSave'
]; ];

View File

@@ -249,13 +249,12 @@ JobEventsList.$inject = ['$filter', '$scope', '$rootScope', '$location', '$log',
'ProcessErrors', 'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Refresh', 'Wait' 'ProcessErrors', 'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Refresh', 'Wait'
]; ];
function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, GenerateForm, function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventsForm, GenerateForm,
Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView, Wait) {
Wait) {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate');
//scope.
// Inject dynamic view var form = JobEventsForm,
var form = JobEventForm,
generator = GenerateForm, generator = GenerateForm,
scope = GenerateForm.inject(form, { mode: 'edit', related: true }), scope = GenerateForm.inject(form, { mode: 'edit', related: true }),
defaultUrl = GetBasePath('base') + 'job_events/' + $routeParams.event_id + '/'; defaultUrl = GetBasePath('base') + 'job_events/' + $routeParams.event_id + '/';
@@ -348,7 +347,7 @@ function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routePara
} }
JobEventsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventForm', JobEventsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'FormatDate', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'FormatDate',
'EventView', 'Wait' 'EventView', 'Wait'
]; ];

View File

@@ -6,11 +6,10 @@
* *
*/ */
angular.module('ActivityDetailDefinition', []) angular.module('ActivityDetailDefinition', [])
.value( .value('ActivityDetailForm', {
'ActivityDetailForm', {
name: 'activity', name: 'activity',
editTitle: 'Activity Detail', editTitle: 'Activity Detail',
well: false, well: false,
'class': 'horizontal-narrow', 'class': 'horizontal-narrow',
formFieldSize: 'col-lg-10', formFieldSize: 'col-lg-10',
@@ -21,18 +20,18 @@ angular.module('ActivityDetailDefinition', [])
label: "Initiated by", label: "Initiated by",
type: 'text', type: 'text',
readonly: true readonly: true
}, },
operation: { operation: {
label: 'Action', label: 'Action',
type: 'text', type: 'text',
readonly: true readonly: true
}, },
changes: { changes: {
label: 'Changes', label: 'Changes',
type: 'textarea', type: 'textarea',
ngHide: "!changes || changes =='' || changes == 'null'", ngHide: "!changes || changes =='' || changes == 'null'",
readonly: true readonly: true
} }
} }
}); //Form }); //Form

View File

@@ -6,23 +6,22 @@
* *
*/ */
angular.module('CredentialFormDefinition', []) angular.module('CredentialFormDefinition', [])
.value( .value('CredentialForm', {
'CredentialForm', {
addTitle: 'Create Credential', //Legend in add mode
addTitle: 'Create Credential', //Legend in add mode editTitle: '{{ name }}', //Legend in edit mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'credential', name: 'credential',
well: true, well: true,
forceListeners: true, forceListeners: true,
actions: { actions: {
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'edit' mode: 'edit'
} }
}, },
fields: { fields: {
name: { name: {
label: 'Name', label: 'Name',
@@ -30,27 +29,31 @@ angular.module('CredentialFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
autocomplete: false autocomplete: false
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
owner: { owner: {
label: "Does this credential belong to a team or user?", label: "Does this credential belong to a team or user?",
type: 'radio_group', type: 'radio_group',
ngChange: "ownerChange()", ngChange: "ownerChange()",
options: [ options: [{
{ label: 'User', value: 'user', selected: true }, label: 'User',
{ label: 'Team', value: 'team' } value: 'user',
], selected: true
awPopOver: "<p>A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " + }, {
label: 'Team',
value: 'team'
}],
awPopOver: "<p>A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " +
"to the credential. Choosing a team shares the credential with all team members.</p>", "to the credential. Choosing a team shares the credential with all team members.</p>",
dataTitle: 'Owner', dataTitle: 'Owner',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
user: { user: {
label: 'User that owns this credential', label: 'User that owns this credential',
type: 'lookup', type: 'lookup',
@@ -58,8 +61,11 @@ angular.module('CredentialFormDefinition', [])
sourceField: 'username', sourceField: 'username',
ngClick: 'lookUpUser()', ngClick: 'lookUpUser()',
ngShow: "owner == 'user'", ngShow: "owner == 'user'",
awRequiredWhen: { variable: "user_required", init: "false" } awRequiredWhen: {
}, variable: "user_required",
init: "false"
}
},
team: { team: {
label: 'Team that owns this credential', label: 'Team that owns this credential',
type: 'lookup', type: 'lookup',
@@ -67,69 +73,84 @@ angular.module('CredentialFormDefinition', [])
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpTeam()', ngClick: 'lookUpTeam()',
ngShow: "owner == 'team'", ngShow: "owner == 'team'",
awRequiredWhen: { variable: "team_required", init: "false" } awRequiredWhen: {
}, variable: "team_required",
init: "false"
}
},
kind: { kind: {
label: 'Type', label: 'Type',
excludeModal: true, excludeModal: true,
type: 'select', type: 'select',
ngOptions: 'kind.label for kind in credential_kind_options', ngOptions: 'kind.label for kind in credential_kind_options',
ngChange: 'kindChange()', ngChange: 'kindChange()',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
helpCollapse: [ helpCollapse: [{
{ hdr: 'Select a Credential Type', hdr: 'Select a Credential Type',
content: '<dl>\n' + content: '<dl>\n' +
'<dt>AWS</dt>\n' + '<dt>AWS</dt>\n' +
'<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' + '<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' +
'<dt>Machine</dt>\n' + '<dt>Machine</dt>\n' +
'<dd>Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' + '<dd>Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' + 'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
'remote hosts.</dd>' + 'remote hosts.</dd>' +
'<dt>Rackspace</dt>\n' + '<dt>Rackspace</dt>\n' +
'<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' + '<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' +
'<dt>SCM</dt>\n' + '<dt>SCM</dt>\n' +
'<dd>Used to check out and synchronize playbook repositories with a remote source control ' + '<dd>Used to check out and synchronize playbook repositories with a remote source control ' +
'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' + 'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
'used on the Projects tab.</dd>\n' + 'used on the Projects tab.</dd>\n' +
'</dl>\n' '</dl>\n'
}] }]
}, },
access_key: { access_key: {
label: 'Access Key', label: 'Access Key',
type: 'text', type: 'text',
ngShow: "kind.value == 'aws'", ngShow: "kind.value == 'aws'",
awRequiredWhen: { variable: "aws_required", init: false }, awRequiredWhen: {
variable: "aws_required",
init: false
},
autocomplete: false, autocomplete: false,
apiField: 'username' apiField: 'username'
}, },
secret_key: { secret_key: {
label: 'Secret Key', label: 'Secret Key',
type: 'password', type: 'password',
ngShow: "kind.value == 'aws'", ngShow: "kind.value == 'aws'",
awRequiredWhen: { variable: "aws_required", init: false }, awRequiredWhen: {
variable: "aws_required",
init: false
},
autocomplete: false, autocomplete: false,
ask: false, ask: false,
clear: false, clear: false,
apiField: 'passwowrd' apiField: 'passwowrd'
}, },
"username": { "username": {
labelBind: 'usernameLabel', labelBind: 'usernameLabel',
type: 'text', type: 'text',
ngShow: "kind.value && kind.value !== 'aws'", ngShow: "kind.value && kind.value !== 'aws'",
awRequiredWhen: {variable: 'rackspace_required', init: false }, awRequiredWhen: {
autocomplete: false variable: 'rackspace_required',
init: false
}, },
autocomplete: false
},
"api_key": { "api_key": {
label: 'API Key', label: 'API Key',
type: 'password', type: 'password',
ngShow: "kind.value == 'rax'", ngShow: "kind.value == 'rax'",
awRequiredWhen: { variable: "rackspace_required", init: false }, awRequiredWhen: {
variable: "rackspace_required",
init: false
},
autocomplete: false, autocomplete: false,
ask: false, ask: false,
clear: false, clear: false,
apiField: 'passwowrd' apiField: 'passwowrd'
}, },
"password": { "password": {
label: 'Password', label: 'Password',
type: 'password', type: 'password',
@@ -141,7 +162,7 @@ angular.module('CredentialFormDefinition', [])
clear: false, clear: false,
associated: 'password_confirm', associated: 'password_confirm',
autocomplete: false autocomplete: false
}, },
"password_confirm": { "password_confirm": {
label: 'Confirm Password', label: 'Confirm Password',
type: 'password', type: 'password',
@@ -151,7 +172,7 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true, awPassMatch: true,
associated: 'password', associated: 'password',
autocomplete: false autocomplete: false
}, },
"ssh_password": { "ssh_password": {
label: 'SSH Password', label: 'SSH Password',
type: 'password', type: 'password',
@@ -163,7 +184,7 @@ angular.module('CredentialFormDefinition', [])
clear: true, clear: true,
associated: 'ssh_password_confirm', associated: 'ssh_password_confirm',
autocomplete: false autocomplete: false
}, },
"ssh_password_confirm": { "ssh_password_confirm": {
label: 'Confirm SSH Password', label: 'Confirm SSH Password',
type: 'password', type: 'password',
@@ -173,7 +194,7 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true, awPassMatch: true,
associated: 'ssh_password', associated: 'ssh_password',
autocomplete: false autocomplete: false
}, },
"ssh_key_data": { "ssh_key_data": {
labelBind: 'sshKeyDataLabel', labelBind: 'sshKeyDataLabel',
type: 'textarea', type: 'textarea',
@@ -182,7 +203,7 @@ angular.module('CredentialFormDefinition', [])
editRequired: false, editRequired: false,
'class': 'ssh-key-field', 'class': 'ssh-key-field',
rows: 10 rows: 10
}, },
"ssh_key_unlock": { "ssh_key_unlock": {
label: 'Key Password', label: 'Key Password',
type: 'password', type: 'password',
@@ -192,9 +213,9 @@ angular.module('CredentialFormDefinition', [])
ngChange: "clearPWConfirm('ssh_key_unlock_confirm')", ngChange: "clearPWConfirm('ssh_key_unlock_confirm')",
associated: 'ssh_key_unlock_confirm', associated: 'ssh_key_unlock_confirm',
ask: true, ask: true,
askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials
clear: true clear: true
}, },
"ssh_key_unlock_confirm": { "ssh_key_unlock_confirm": {
label: 'Confirm Key Password', label: 'Confirm Key Password',
type: 'password', type: 'password',
@@ -203,7 +224,7 @@ angular.module('CredentialFormDefinition', [])
editRequired: false, editRequired: false,
awPassMatch: true, awPassMatch: true,
associated: 'ssh_key_unlock' associated: 'ssh_key_unlock'
}, },
"sudo_username": { "sudo_username": {
label: 'Sudo Username', label: 'Sudo Username',
type: 'text', type: 'text',
@@ -211,7 +232,7 @@ angular.module('CredentialFormDefinition', [])
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
autocomplete: false autocomplete: false
}, },
"sudo_password": { "sudo_password": {
label: 'Sudo Password', label: 'Sudo Password',
type: 'password', type: 'password',
@@ -223,7 +244,7 @@ angular.module('CredentialFormDefinition', [])
clear: true, clear: true,
associated: 'sudo_password_confirm', associated: 'sudo_password_confirm',
autocomplete: false autocomplete: false
}, },
"sudo_password_confirm": { "sudo_password_confirm": {
label: 'Confirm Sudo Password', label: 'Confirm Sudo Password',
type: 'password', type: 'password',
@@ -233,24 +254,21 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true, awPassMatch: true,
associated: 'sudo_password', associated: 'sudo_password',
autocomplete: false autocomplete: false
}
},
buttons: { //for now always generates <button> tags
save: {
label: '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
}
},
related: { //related colletions (and maybe items?)
} }
},
}); //InventoryForm
buttons: {
save: {
label: '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
}
},
related: {}
});

View File

@@ -4,12 +4,11 @@
* Groups.js * Groups.js
* Form definition for Group model * Form definition for Group model
* *
* *
*/ */
angular.module('GroupFormDefinition', []) angular.module('GroupFormDefinition', [])
.value( .value('GroupForm', {
'GroupForm', {
addTitle: 'Create Group', addTitle: 'Create Group',
editTitle: 'Edit Group', editTitle: 'Edit Group',
showTitle: true, showTitle: true,
@@ -18,12 +17,15 @@ angular.module('GroupFormDefinition', [])
well: true, well: true,
formLabelSize: 'col-lg-3', formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9', formFieldSize: 'col-lg-9',
tabs: [ tabs: [{
{ name: 'properties', label: 'Properties'}, name: 'properties',
{ name: 'source', label: 'Source' } label: 'Properties'
], }, {
name: 'source',
label: 'Source'
}],
fields: { fields: {
name: { name: {
label: 'Name', label: 'Name',
@@ -31,19 +33,19 @@ angular.module('GroupFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
tab: 'properties' tab: 'properties'
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
tab: 'properties' tab: 'properties'
}, },
variables: { variables: {
label: 'Variables', label: 'Variables',
type: 'textarea', type: 'textarea',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
rows: 6, rows: 6,
'default': '---', 'default': '---',
dataTitle: 'Group Variables', dataTitle: 'Group Variables',
@@ -59,24 +61,27 @@ angular.module('GroupFormDefinition', [])
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>', '<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
dataContainer: 'body', dataContainer: 'body',
tab: 'properties' tab: 'properties'
}, },
source: { source: {
label: 'Source', label: 'Source',
type: 'select', type: 'select',
ngOptions: 'source.label for source in source_type_options', ngOptions: 'source.label for source in source_type_options',
ngChange: 'sourceChange()', ngChange: 'sourceChange()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
//'default': { label: 'Manual', value: '' }, //'default': { label: 'Manual', value: '' },
tab: 'source' tab: 'source'
}, },
source_path: { source_path: {
label: 'Script Path', label: 'Script Path',
ngShow: "source && source.value == 'file'", ngShow: "source && source.value == 'file'",
type: 'text', type: 'text',
awRequiredWhen: {variable: "sourcePathRequired", init: "false" }, awRequiredWhen: {
tab: 'source' variable: "sourcePathRequired",
init: "false"
}, },
tab: 'source'
},
credential: { credential: {
label: 'Cloud Credential', label: 'Cloud Credential',
type: 'lookup', type: 'lookup',
@@ -84,10 +89,10 @@ angular.module('GroupFormDefinition', [])
sourceModel: 'credential', sourceModel: 'credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCredential()', ngClick: 'lookUpCredential()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
tab: 'source' tab: 'source'
}, },
source_regions: { source_regions: {
label: 'Regions', label: 'Regions',
type: 'text', type: 'text',
@@ -102,13 +107,13 @@ angular.module('GroupFormDefinition', [])
"</p>", "</p>",
dataContainer: 'body', dataContainer: 'body',
tab: 'source' tab: 'source'
}, },
source_vars: { source_vars: {
label: 'Source Variables', label: 'Source Variables',
ngShow: "source && (source.value == 'file' || source.value == 'ec2')", ngShow: "source && (source.value == 'file' || source.value == 'ec2')",
type: 'textarea', type: 'textarea',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
rows: 6, rows: 6,
'default': '---', 'default': '---',
parseTypeName: 'envParseType', parseTypeName: 'envParseType',
@@ -126,80 +131,73 @@ angular.module('GroupFormDefinition', [])
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>', '<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
dataContainer: 'body', dataContainer: 'body',
tab: 'source' tab: 'source'
}, },
checkbox_group: { checkbox_group: {
label: 'Update Options', label: 'Update Options',
type: 'checkbox_group', type: 'checkbox_group',
ngShow: "source && (source.value !== '' && source.value !== null)", ngShow: "source && (source.value !== '' && source.value !== null)",
tab: 'source', tab: 'source',
fields: [ fields: [{
{ name: 'overwrite',
name: 'overwrite', label: 'Overwrite',
label: 'Overwrite', type: 'checkbox',
type: 'checkbox', ngShow: "source.value !== '' && source.value !== null",
ngShow: "source.value !== '' && source.value !== null", addRequired: false,
addRequired: false, editRequired: false,
editRequired: false, awPopOver: '<p>When checked all child groups and hosts not found on the remote source will be deleted from ' +
awPopOver: '<p>When checked all child groups and hosts not found on the remote source will be deleted from ' + 'the local inventory.</p><p>Unchecked any local child hosts and groups not found on the external source will ' +
'the local inventory.</p><p>Unchecked any local child hosts and groups not found on the external source will ' + 'remain untouched by the inventory update process.</p>',
'remain untouched by the inventory update process.</p>', dataTitle: 'Overwrite',
dataTitle: 'Overwrite', dataContainer: 'body',
dataContainer: 'body', dataPlacement: 'right',
dataPlacement: 'right', labelClass: 'checkbox-options'
labelClass: 'checkbox-options' }, {
}, name: 'overwrite_vars',
{ label: 'Overwrite Variables',
name: 'overwrite_vars', type: 'checkbox',
label: 'Overwrite Variables', ngShow: "source.value !== '' && source.value !== null",
type: 'checkbox', addRequired: false,
ngShow: "source.value !== '' && source.value !== null", editRequired: false,
addRequired: false, awPopOver: '<p>If checked, all variables for child groups and hosts will be removed and replaced by those ' +
editRequired: false, 'found on the external source.</p><p>When not checked a merge will be performed, combining local variables with ' +
awPopOver: '<p>If checked, all variables for child groups and hosts will be removed and replaced by those ' + 'those found on the external source.</p>',
'found on the external source.</p><p>When not checked a merge will be performed, combining local variables with ' + dataTitle: 'Overwrite Variables',
'those found on the external source.</p>', dataContainer: 'body',
dataTitle: 'Overwrite Variables', dataPlacement: 'right',
dataContainer: 'body', labelClass: 'checkbox-options'
dataPlacement: 'right', }, {
labelClass: 'checkbox-options' name: 'update_on_launch',
}, label: 'Update on Launch',
{ type: 'checkbox',
name: 'update_on_launch', ngShow: "source.value !== '' && source.value !== null",
label: 'Update on Launch', addRequired: false,
type: 'checkbox', editRequired: false,
ngShow: "source.value !== '' && source.value !== null", awPopOver: '<p>Each time a job runs using this inventory, refresh the inventory from the selected source before ' +
addRequired: false, 'executing job tasks.</p>',
editRequired: false, dataTitle: 'Update on Launch',
awPopOver: '<p>Each time a job runs using this inventory, refresh the inventory from the selected source before ' + dataContainer: 'body',
'executing job tasks.</p>', dataPlacement: 'right',
dataTitle: 'Update on Launch', labelClass: 'checkbox-options'
dataContainer: 'body', }]
dataPlacement: 'right', }
labelClass: 'checkbox-options' },
}
] buttons: {
}
},
buttons: { //for now always generates <button> tags
labelClass: 'col-lg-3', labelClass: 'col-lg-3',
controlClass: 'col-lg-5', controlClass: 'col-lg-5',
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
}, },
reset: {
related: { //related colletions (and maybe items?) ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
} }
},
}); related: { }
});

View File

@@ -7,14 +7,13 @@
* *
*/ */
angular.module('HostGroupsFormDefinition', []) angular.module('HostGroupsFormDefinition', [])
.value( .value('HostGroupsForm', {
'HostGroupsForm', {
editTitle: 'Host Groups', //Legend in edit mode editTitle: 'Host Groups',
name: 'host', //Form name attribute name: 'host',
well: false, //Wrap the form with TB well well: false,
formLabelSize: 'col-lg-3', formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9', formFieldSize: 'col-lg-9',
fields: { fields: {
groups: { groups: {
@@ -26,30 +25,28 @@ angular.module('HostGroupsFormDefinition', [])
editRequired: true, editRequired: true,
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" + awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
"<blockquote>myserver.domain.com<br/>" + "<blockquote>myserver.domain.com<br/>" +
"127.0.0.1<br />" + "127.0.0.1<br />" +
"10.1.0.140:25<br />" + "10.1.0.140:25<br />" +
"server.example.com:25" + "server.example.com:25" +
"</blockquote>", "</blockquote>",
dataTitle: 'Host Name', dataTitle: 'Host Name',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: '#form-modal .modal-content' dataContainer: '#form-modal .modal-content'
} }
}, },
buttons: { //for now always generates <button> tags buttons: { //for now always generates <button> tags
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
}, },
reset: {
related: { //related colletions (and maybe items?) ngClick: 'formReset()',
ngDisabled: true
} }
},
related: { }
}); //UserForm }); //UserForm

View File

@@ -4,18 +4,17 @@
* Hosts.js * Hosts.js
* Form definition for Host model * Form definition for Host model
* *
* *
*/ */
angular.module('HostFormDefinition', []) angular.module('HostFormDefinition', [])
.value( .value('HostForm', {
'HostForm', {
addTitle: 'Create Host',
addTitle: 'Create Host', //Legend in add mode editTitle: '{{ name }}',
editTitle: '{{ name }}', //Legend in edit mode name: 'host',
name: 'host', //Form name attribute well: false,
well: false, //Wrap the form with TB well
formLabelSize: 'col-lg-3', formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9', formFieldSize: 'col-lg-9',
fields: { fields: {
name: { name: {
@@ -24,21 +23,21 @@ angular.module('HostFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" + awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
"<blockquote>myserver.domain.com<br/>" + "<blockquote>myserver.domain.com<br/>" +
"127.0.0.1<br />" + "127.0.0.1<br />" +
"10.1.0.140:25<br />" + "10.1.0.140:25<br />" +
"server.example.com:25" + "server.example.com:25" +
"</blockquote>", "</blockquote>",
dataTitle: 'Host Name', dataTitle: 'Host Name',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: '#form-modal .modal-content' dataContainer: '#form-modal .modal-content'
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
enabled: { enabled: {
label: 'Enabled?', label: 'Enabled?',
type: 'checkbox', type: 'checkbox',
@@ -51,12 +50,12 @@ angular.module('HostFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: '#form-modal .modal-content', dataContainer: '#form-modal .modal-content',
ngDisabled: 'has_inventory_sources == true' ngDisabled: 'has_inventory_sources == true'
}, },
variables: { variables: {
label: 'Variables', label: 'Variables',
type: 'textarea', type: 'textarea',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
rows: 6, rows: 6,
"class": "modal-input-xlarge", "class": "modal-input-xlarge",
"default": "---", "default": "---",
@@ -70,28 +69,25 @@ angular.module('HostFormDefinition', [])
dataTitle: 'Host Variables', dataTitle: 'Host Variables',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: '#form-modal .modal-content' dataContainer: '#form-modal .modal-content'
}, },
inventory: { inventory: {
type: 'hidden', type: 'hidden',
includeOnEdit: true, includeOnEdit: true,
includeOnAdd: true includeOnAdd: true
} }
}, },
buttons: { //for now always generates <button> tags buttons: { //for now always generates <button> tags
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
}, },
reset: {
related: { //related colletions (and maybe items?) ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
} }
},
}); related: {}
});

View File

@@ -4,12 +4,11 @@
* Inventories.js * Inventories.js
* Form definition for User model * Form definition for User model
* *
* *
*/ */
angular.module('InventoryFormDefinition', []) angular.module('InventoryFormDefinition', [])
.value( .value('InventoryForm', {
'InventoryForm', {
addTitle: 'Create Inventory', addTitle: 'Create Inventory',
editTitle: '{{ inventory_name | capitalize }}', editTitle: '{{ inventory_name | capitalize }}',
name: 'inventory', name: 'inventory',
@@ -24,8 +23,8 @@ angular.module('InventoryFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
inventory_name: { inventory_name: {
@@ -35,29 +34,32 @@ angular.module('InventoryFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: false capitalize: false
}, },
inventory_description: { inventory_description: {
realName: 'description', realName: 'description',
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
type: 'lookup', type: 'lookup',
sourceModel: 'organization', sourceModel: 'organization',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpOrganization()', ngClick: 'lookUpOrganization()',
awRequiredWhen: {variable: "organizationrequired", init: "true" } awRequiredWhen: {
}, variable: "organizationrequired",
init: "true"
}
},
inventory_variables: { inventory_variables: {
realName: 'variables', realName: 'variables',
label: 'Variables', label: 'Variables',
type: 'textarea', type: 'textarea',
'class': 'span12', 'class': 'span12',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
parseTypeName: 'inventoryParseType', parseTypeName: 'inventoryParseType',
rows: 6, rows: 6,
"default": "---", "default": "---",
@@ -71,23 +73,22 @@ angular.module('InventoryFormDefinition', [])
dataTitle: 'Inventory Variables', dataTitle: 'Inventory Variables',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body' dataContainer: 'body'
} }
}, },
buttons: { //for now always generates <button> tags buttons: {
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
}, },
reset: {
ngClick: 'formReset()',
ngDisabled: true
}
},
related: { related: {
} }
}); //InventoryForm
});

View File

@@ -1,27 +0,0 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventoryGroups.js
* Form definition for Groups model
*
*
*/
angular.module('InventoryGroupsFormDefinition', [])
.value(
'InventoryGroupsForm', {
type: 'groupsview',
title: "groupTitle",
editTitle: 'Groups',
iterator: 'group',
fields: {
},
actions: {
},
fieldActions: {
}
}); //InventoryGroupsForm

View File

@@ -1,147 +0,0 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventoryHosts.js
* Form definition for Hosts model
*
*
*/
angular.module('InventoryHostsFormDefinition', [])
.value(
'InventoryHostsForm', {
type: 'hostsview',
title: "groupTitle",
editTitle: 'Hosts',
iterator: 'host',
fields: {
name: {
key: true,
label: 'Name',
ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')"
},
active_failures: {
label: 'Job Status',
ngHref: "\{\{ host.activeFailuresLink \}\}",
awToolTip: "\{\{ host.badgeToolTip \}\}",
dataPlacement: 'top',
badgeNgHref: '\{\{ host.activeFailuresLink \}\}',
badgeIcon: "\{\{ 'icon-failures-' + host.has_active_failures \}\}",
badgePlacement: 'left',
badgeToolTip: "\{\{ host.badgeToolTip \}\}",
badgeTipPlacement: 'top',
searchable: false,
nosort: true
},
enabled_flag: {
label: 'Enabled',
badgeIcon: "\{\{ 'icon-enabled-' + host.enabled \}\}",
badgePlacement: 'left',
badgeToolTip: "\{\{ host.enabledToolTip \}\}",
badgeTipPlacement: "top",
badgeTipWatch: "host.enabledToolTip",
ngClick: "toggle_host_enabled(\{\{ host.id \}\}, \{\{ host.has_inventory_sources \}\})",
searchable: false,
showValue: false
},
groups: {
label: 'Groups',
searchable: true,
sourceModel: 'groups',
sourceField: 'name',
nosort: true
},
enabled: {
label: 'Disabled?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'false',
searchOnly: true
},
has_active_failures: {
label: 'Has failed jobs?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
has_inventory_sources: {
label: 'Has external source?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
}
},
actions: {
add: {
label: 'Copy',
ngClick: "addHost()",
ngHide: "hostAddHide",
awToolTip: "Copy an existing host to the selected group",
dataPlacement: 'top',
'class': 'btn-xs btn-success',
icon: 'icon-check'
},
create: {
label: 'Create New',
ngClick: 'createHost()',
ngHide: 'hostCreateHide',
awToolTip: 'Create a new host and add it to the selected group',
dataPlacement: 'top',
'class': 'btn-xs btn-success',
icon: 'icon-plus'
},
help: {
dataPlacement: 'top',
icon: "icon-question-sign",
mode: 'all',
'class': 'btn-xs btn-info btn-help',
awToolTip: "<div style=\"padding-top:10px; text-align: left;\"><p>Need help getting started?</p>" +
"<p>Click here for help with this page</p></div>",
iconSize: 'large',
ngClick: "showHelp()",
id: "hosts-page-help"
},
stream: {
'class': "btn-primary btn-xs activity-btn",
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
dataPlacement: "top",
icon: "icon-comments-alt",
mode: 'edit',
iconSize: 'large'
}
},
fieldActions: {
ViewJobs: {
type: 'DropDown',
label: 'Jobs',
icon: 'icon-zoom-in',
"class": "btn-default btn-xs",
options: [
{ ngClick: "allJobs(\{\{ host.id \}\})", label: 'All', ngShow: 'host.last_job' },
{ ngClick: "allHostSummaries(\{\{ host.id \}\},'\{\{ host.name \}\}', \{\{ inventory_id \}\})", label: 'All summaries',
ngShow: 'host.last_job' },
{ ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest', ngShow: 'host.last_job' },
{ ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " +
"'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest events', ngShow: 'host.last_job' },
{ ngClick: "viewLastSummary(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " +
"'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest summary', ngShow: 'host.last_job' },
{ ngClick: "", label: 'No job data available', ngShow: 'host.last_job == null' }
]
},
"delete": {
ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete host'
}
}
}); //InventoryHostsForm

View File

@@ -1,57 +0,0 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventoryStatus.js
* Form definition for Inventory Status -JSON view
*
*
*/
angular.module('InventoryStatusDefinition', [])
.value(
'InventoryStatusForm', {
name: 'inventory_update',
editTitle: 'Inventory Status',
well: false,
'class': 'horizontal-narrow',
fields: {
license_error: {
type: 'alertblock',
'class': 'alert-info',
alertTxt: 'The invenvtory update process exceeded the available number of licensed hosts. ' +
'<strong><a ng-click=\"viewLicense()\" href=\"\">View your license</a></strong> ' +
'for more information.',
ngShow: 'license_error',
closeable: true
},
created: {
label: 'Created',
type: 'text',
readonly: true
},
status: {
label: 'Status',
type: 'text',
readonly: true,
'class': 'nowrap mono-space resizable',
rows: '{{ status_rows }}'
},
result_stdout: {
label: 'Std Out',
type: 'textarea',
ngShow: 'result_stdout',
'class': 'nowrap mono-space resizable',
readonly: true,
rows: '{{ stdout_rows }}'
},
result_traceback: {
label: 'Traceback',
type: 'textarea',
ngShow: 'result_traceback',
'class': 'nowrap mono-space resizable',
readonly: true,
rows: '{{ traceback_rows }}'
}
}
}); //Form

View File

@@ -7,10 +7,9 @@
* *
*/ */
angular.module('JobEventDataDefinition', []) angular.module('JobEventDataDefinition', [])
.value( .value('JobEventDataForm', {
'JobEventDataForm', {
editTitle: '{{ id }} - {{ event_display }}', //Legend in edit mode editTitle: '{{ id }} - {{ event_display }}',
name: 'job_events', name: 'job_events',
well: false, well: false,
'class': 'horizontal-narrow', 'class': 'horizontal-narrow',
@@ -22,7 +21,7 @@ angular.module('JobEventDataDefinition', [])
readonly: true, readonly: true,
rows: 18, rows: 18,
'class': 'modal-input-xxlarge' 'class': 'modal-input-xxlarge'
}
} }
}
}); //Form }); //Form

View File

@@ -0,0 +1,144 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* JobEventsForm.js
*
*/
angular.module('JobEventsFormDefinition', [])
.value('JobEventsForm', {
name: 'job_events',
well: false,
forceListeners: true,
fields: {
status: {
labelClass: 'job-{{ status }}',
type: 'custom',
section: 'Event',
control: "<div class=\"job-event-status job-{{ status }}\"><i class=\"fa icon-job-{{ status }}\"></i> {{ status }}</div>"
},
id: {
label: 'ID',
type: 'text',
readonly: true,
section: 'Event',
'class': 'span1'
},
created: {
label: 'Created On',
type: 'text',
section: 'Event',
readonly: true
},
host: {
label: 'Host',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "host !== ''"
},
play: {
label: 'Play',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "play !== ''"
},
task: {
label: 'Task',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "task !== ''"
},
rc: {
label: 'Return Code',
type: 'text',
readonly: true,
section: 'Results',
'class': 'span1',
ngShow: "rc !== ''"
},
msg: {
label: 'Msg',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "msg !== ''",
rows: 10
},
stdout: {
label: 'Std Out',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "stdout !== ''",
rows: 10
},
stderr: {
label: 'Std Err',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "stderr !== ''",
rows: 10
},
results: {
label: 'Results',
type: 'textarea',
section: 'Results',
readonly: true,
'class': 'nowrap',
ngShow: "results !== ''",
rows: 10
},
start: {
label: 'Start',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "start !== ''"
},
traceback: {
label: false,
type: 'textarea',
readonly: true,
section: 'Traceback',
'class': 'nowrap',
ngShow: "traceback !== ''",
rows: 10
},
end: {
label: 'End',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "end !== ''"
},
delta: {
label: 'Elapsed',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "delta !== ''"
},
module_name: {
label: 'Name',
type: 'text',
readonly: true,
section: 'Module',
ngShow: "module_name !== ''"
},
module_args: {
label: 'Args',
type: 'text',
readonly: true,
section: 'Module',
ngShow: "module_args !== ''"
}
}
});

View File

@@ -7,8 +7,7 @@
* *
*/ */
angular.module('JobSummaryDefinition', []) angular.module('JobSummaryDefinition', [])
.value( .value('JobSummary', {
'JobSummary', {
editTitle: '{{ id }} - {{ name }}', editTitle: '{{ id }} - {{ name }}',
name: 'jobs', name: 'jobs',
@@ -21,12 +20,12 @@ angular.module('JobSummaryDefinition', [])
control: '<div class=\"job-detail-status\"><span style="padding-right: 15px; font-weight: bold;">Status</span> ' + control: '<div class=\"job-detail-status\"><span style="padding-right: 15px; font-weight: bold;">Status</span> ' +
'<i class=\"fa icon-job-{{ status }}\"></i> {{ status }}</div>', '<i class=\"fa icon-job-{{ status }}\"></i> {{ status }}</div>',
readonly: true readonly: true
}, },
created: { created: {
label: 'Created On', label: 'Created On',
type: 'text', type: 'text',
readonly: true readonly: true
}, },
result_stdout: { result_stdout: {
label: 'Standard Out', label: 'Standard Out',
type: 'textarea', type: 'textarea',
@@ -35,7 +34,7 @@ angular.module('JobSummaryDefinition', [])
rows: '{{ stdout_rows }}', rows: '{{ stdout_rows }}',
'class': 'nowrap mono-space resizable', 'class': 'nowrap mono-space resizable',
ngShow: 'result_stdout != ""' ngShow: 'result_stdout != ""'
}, },
result_traceback: { result_traceback: {
label: 'Traceback', label: 'Traceback',
type: 'textarea', type: 'textarea',
@@ -44,7 +43,6 @@ angular.module('JobSummaryDefinition', [])
rows: '{{ traceback_rows }}', rows: '{{ traceback_rows }}',
'class': 'nowrap mono-space resizable', 'class': 'nowrap mono-space resizable',
ngShow: 'result_traceback != ""' ngShow: 'result_traceback != ""'
} }
} }
}); });

View File

@@ -7,11 +7,10 @@
* *
*/ */
angular.module('JobTemplateFormDefinition', []) angular.module('JobTemplateFormDefinition', [])
.value( .value('JobTemplateForm', {
'JobTemplateForm', {
addTitle: 'Create Job Templates', //Legend in add mode addTitle: 'Create Job Templates',
editTitle: '{{ name }}', //Legend in edit mode editTitle: '{{ name }}',
name: 'job_templates', name: 'job_templates',
twoColumns: true, twoColumns: true,
well: true, well: true,
@@ -25,8 +24,8 @@ angular.module('JobTemplateFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
name: { name: {
@@ -35,20 +34,20 @@ angular.module('JobTemplateFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
column: 1 column: 1
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1 column: 1
}, },
job_type: { job_type: {
label: 'Job Type', label: 'Job Type',
type: 'select', type: 'select',
ngOptions: 'type.label for type in job_type_options', ngOptions: 'type.label for type in job_type_options',
"default": 0, "default": 0,
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
column: 1, column: 1,
awPopOver: "<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " + awPopOver: "<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " +
@@ -57,7 +56,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Job Type', dataTitle: 'Job Type',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
type: 'lookup', type: 'lookup',
@@ -70,7 +69,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Inventory', dataTitle: 'Inventory',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
project: { project: {
label: 'Project', label: 'Project',
type: 'lookup', type: 'lookup',
@@ -83,7 +82,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Project', dataTitle: 'Project',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
playbook: { playbook: {
label: 'Playbook', label: 'Playbook',
type:'select', type:'select',
@@ -95,29 +94,29 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Playbook', dataTitle: 'Playbook',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
credential: { credential: {
label: 'Machine Credential', label: 'Machine Credential',
type: 'lookup', type: 'lookup',
sourceModel: 'credential', sourceModel: 'credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCredential()', ngClick: 'lookUpCredential()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " + awPopOver: "<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
" the username and SSH key or password that Ansbile will need to log into the remote hosts.</p>", " the username and SSH key or password that Ansbile will need to log into the remote hosts.</p>",
dataTitle: 'Credential', dataTitle: 'Credential',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
cloud_credential: { cloud_credential: {
label: 'Cloud Credential', label: 'Cloud Credential',
type: 'lookup', type: 'lookup',
sourceModel: 'cloud_credential', sourceModel: 'cloud_credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCloudcredential()', ngClick: 'lookUpCloudcredential()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " + awPopOver: "<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
@@ -125,16 +124,16 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Cloud Credential', dataTitle: 'Cloud Credential',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
forks: { forks: {
label: 'Forks', label: 'Forks',
id: 'forks-number', id: 'forks-number',
type: 'number', type: 'number',
integer: true, integer: true,
min: 0, min: 0,
spinner: true, spinner: true,
"default": '0', "default": '0',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
'class': "input-small", 'class': "input-small",
column: 1, column: 1,
@@ -144,11 +143,11 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Forks', dataTitle: 'Forks',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
limit: { limit: {
label: 'Limit', label: 'Limit',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " + awPopOver: "<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
@@ -157,26 +156,26 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Limit', dataTitle: 'Limit',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
verbosity: { verbosity: {
label: 'Verbosity', label: 'Verbosity',
type: 'select', type: 'select',
ngOptions: 'v.label for v in verbosity_options', ngOptions: 'v.label for v in verbosity_options',
"default": 0, "default": 0,
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
column: 1, column: 1,
awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>", awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>",
dataTitle: 'Verbosity', dataTitle: 'Verbosity',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
variables: { variables: {
label: 'Extra Variables', label: 'Extra Variables',
type: 'textarea', type: 'textarea',
rows: 6, rows: 6,
"class": 'span12', "class": 'span12',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
"default": "---", "default": "---",
column: 2, column: 2,
@@ -189,13 +188,13 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Extra Variables', dataTitle: 'Extra Variables',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
job_tags: { job_tags: {
label: 'Job Tags', label: 'Job Tags',
type: 'textarea', type: 'textarea',
rows: 1, rows: 1,
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
'class': 'span12', 'class': 'span12',
column: 2, column: 2,
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" + awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
@@ -203,26 +202,25 @@ angular.module('JobTemplateFormDefinition', [])
"<p>For example, you might have a task consisiting of a long list of actions. Tag values can be assigned to each action. " + "<p>For example, you might have a task consisiting of a long list of actions. Tag values can be assigned to each action. " +
"Suppose the actions have been assigned tag values of &quot;configuration&quot;, &quot;packages&quot; and &quot;install&quot;.</p>" + "Suppose the actions have been assigned tag values of &quot;configuration&quot;, &quot;packages&quot; and &quot;install&quot;.</p>" +
"<p>If you just want to run the &quot;configuration&quot; and &quot;packages&quot; actions, you would enter the following here " + "<p>If you just want to run the &quot;configuration&quot; and &quot;packages&quot; actions, you would enter the following here " +
"in the Job Tags field:<\p>\n" + "in the Job Tags field:</p>\n<blockquote>configuration,packages</blockquote>\n",
"<blockquote>configuration,packages</blockquote>\n",
dataTitle: "Job Tags", dataTitle: "Job Tags",
dataPlacement: "right", dataPlacement: "right",
dataContainer: "body" dataContainer: "body"
}, },
allow_callbacks: { allow_callbacks: {
label: 'Allow Callbacks', label: 'Allow Callbacks',
type: 'checkbox', type: 'checkbox',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
trueValue: 'true', trueValue: 'true',
falseValue: 'false', falseValue: 'false',
ngChange: "toggleCallback('host_config_key')", ngChange: "toggleCallback('host_config_key')",
column: 2, column: 2,
awPopOver: "<p>Create a callback URL a host can use to contact Tower and request a configuration update " + awPopOver: "<p>Create a callback URL a host can use to contact Tower and request a configuration update " +
"using the job template. The URL will look like the following:</p>\n" + "using the job template. The URL will look like the following:</p>\n" +
"<p class=\"code-breakable\">http://your.server.com:999/api/v1/job_templates/1/callback/</p>" + "<p class=\"code-breakable\">http://your.server.com:999/api/v1/job_templates/1/callback/</p>" +
"<p>The request from the host must be a POST. Here is an example using curl:</p>\n" + "<p>The request from the host must be a POST. Here is an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" + "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" +
"<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " + "<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " +
"in one of your defined inventories, the request will be denied.</p>" + "in one of your defined inventories, the request will be denied.</p>" +
@@ -230,11 +228,11 @@ angular.module('JobTemplateFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataTitle: 'Callback URL', dataTitle: 'Callback URL',
dataContainer: "body" dataContainer: "body"
}, },
callback_url: { callback_url: {
label: 'Callback URL', label: 'Callback URL',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
readonly: true, readonly: true,
column: 2, column: 2,
@@ -242,7 +240,7 @@ angular.module('JobTemplateFormDefinition', [])
'class': 'span12', 'class': 'span12',
awPopOver: "<p>Using this URL a host can contact Tower and request a configuration update using the job " + awPopOver: "<p>Using this URL a host can contact Tower and request a configuration update using the job " +
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" + "template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" + "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" +
"<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " + "<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " +
"in one of your defined inventories, the request will be denied.</p>" + "in one of your defined inventories, the request will be denied.</p>" +
@@ -250,7 +248,7 @@ angular.module('JobTemplateFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataTitle: 'Callback URL', dataTitle: 'Callback URL',
dataContainer: "body" dataContainer: "body"
}, },
host_config_key: { host_config_key: {
label: 'Host Config Key', label: 'Host Config Key',
type: 'text', type: 'text',
@@ -259,25 +257,25 @@ angular.module('JobTemplateFormDefinition', [])
column: 2, column: 2,
awPopOver: "<p>When contacting the Tower server using the callback URL, the calling host must authenticate by including " + awPopOver: "<p>When contacting the Tower server using the callback URL, the calling host must authenticate by including " +
"this key in the POST data of the request. Here's an example using curl:</p>\n" + "this key in the POST data of the request. Here's an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n", "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n",
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
} }
}, },
buttons: { //for now always generates <button> tags buttons: { //for now always generates <button> tags
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true //Disable when $pristine or $invalid, optional
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine ngDisabled: true //Disabled when $pristine
} }
}, },
related: { //related colletions (and maybe items?) related: {
jobs: { jobs: {
type: 'collection', type: 'collection',
@@ -295,54 +293,55 @@ angular.module('JobTemplateFormDefinition', [])
awToolTip: "Reset the search filter", awToolTip: "Reset the search filter",
ngClick: "resetSearch('job')", ngClick: "resetSearch('job')",
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
id: { id: {
label: 'Job ID', label: 'Job ID',
key: true, key: true,
desc: true, desc: true,
searchType: 'int' searchType: 'int'
}, },
created: { created: {
label: 'Date', label: 'Date',
link: false, link: false,
searchable: false searchable: false
}, },
status: { status: {
label: 'Status', label: 'Status',
"class": 'job-\{\{ job.status \}\}', "class": 'job-{{ job.status }}',
searchType: 'select', searchType: 'select',
linkTo: "\{\{ job.statusLinkTo \}\}", linkTo: "{{}} job.statusLinkTo }}",
searchOptions: [ searchOptions: [
{ name: "new", value: "new" }, { name: "new", value: "new" },
{ name: "waiting", value: "waiting" }, { name: "waiting", value: "waiting" },
{ name: "pending", value: "pending" }, { name: "pending", value: "pending" },
{ name: "running", value: "running" }, { name: "running", value: "running" },
{ name: "successful", value: "successful" }, { name: "successful", value: "successful" },
{ name: "error", value: "error" }, { name: "error", value: "error" },
{ name: "failed", value: "failed" }, { name: "failed", value: "failed" },
{ name: "canceled", value: "canceled" } ], { name: "canceled", value: "canceled" }
badgeIcon: 'fa icon-job-\{\{ job.status \}\}', ],
badgeIcon: 'fa icon-job-{{ job.status }}',
badgePlacement: 'left', badgePlacement: 'left',
badgeToolTip: "\{\{ job.statusBadgeToolTip \}\}", badgeToolTip: "{{ job.statusBadgeToolTip }}",
badgeTipPlacement: 'top', badgeTipPlacement: 'top',
badgeNgHref: "\{\{ job.statusLinkTo \}\}", badgeNgHref: "{{ job.statusLinkTo }}",
awToolTip: "\{\{ job.statusBadgeToolTip \}\}", awToolTip: "{{ job.statusBadgeToolTip }}",
dataPlacement: 'top' dataPlacement: 'top'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'View', label: 'View',
ngClick: "edit('jobs', \{\{ job.id \}\}, '\{\{ job.name \}\}')", ngClick: "edit('jobs', job.id, job.name)",
icon: 'icon-zoom-in' icon: 'icon-zoom-in'
}
} }
} }
} }
}
}); //InventoryForm }); //InventoryForm

View File

@@ -7,9 +7,8 @@
* @dict * @dict
*/ */
angular.module('JobFormDefinition', []) angular.module('JobFormDefinition', [])
.value( .value('JobForm', {
'JobForm', {
addTitle: 'Create Job', addTitle: 'Create Job',
editTitle: '{{ id }} - {{ name }}', editTitle: '{{ id }} - {{ name }}',
name: 'jobs', name: 'jobs',
@@ -26,19 +25,19 @@ angular.module('JobFormDefinition', [])
icon: 'icon-zoom-in', icon: 'icon-zoom-in',
active: true, active: true,
ngShow: "job_id !== null" ngShow: "job_id !== null"
}, },
events: { events: {
href: "/#/jobs/{{ job_id }}/job_events", href: "/#/jobs/{{ job_id }}/job_events",
label: 'Events', label: 'Events',
icon: 'icon-list-ul' icon: 'icon-list-ul'
}, },
hosts: { hosts: {
href: "/#/jobs/{{ job_id }}/job_host_summaries", href: "/#/jobs/{{ job_id }}/job_host_summaries",
label: 'Host Summary', label: 'Host Summary',
icon: 'icon-laptop' icon: 'icon-laptop'
} }
}, },
fields: { fields: {
name: { name: {
label: 'Job Template', label: 'Job Template',
@@ -47,20 +46,20 @@ angular.module('JobFormDefinition', [])
editRequired: false, editRequired: false,
readonly: true, readonly: true,
column: 1 column: 1
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1 column: 1
}, },
job_type: { job_type: {
label: 'Job Type', label: 'Job Type',
type: 'select', type: 'select',
ngOptions: 'type.label for type in job_type_options', ngOptions: 'type.label for type in job_type_options',
"default": 'run', "default": 'run',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
awPopOver: "<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " + awPopOver: "<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " +
" on the selected hosts.</p> <p>Setting the type to <em>check</em> will not execute the playbook. Instead, ansible will check playbook " + " on the selected hosts.</p> <p>Setting the type to <em>check</em> will not execute the playbook. Instead, ansible will check playbook " +
@@ -69,7 +68,7 @@ angular.module('JobFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body', dataContainer: 'body',
column: 1 column: 1
}, },
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
type: 'lookup', type: 'lookup',
@@ -83,7 +82,7 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Inventory', dataTitle: 'Inventory',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
project: { project: {
label: 'Project', label: 'Project',
type: 'lookup', type: 'lookup',
@@ -97,42 +96,42 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Project', dataTitle: 'Project',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
playbook: { playbook: {
label: 'Playbook', label: 'Playbook',
type:'select', type: 'select',
ngOptions: 'book for book in playbook_options', ngOptions: 'book for book in playbook_options',
id: 'playbook-select', id: 'playbook-select',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
column: 1, column: 1,
awPopOver: "<p>Select the playbook to be executed by this job.</p>", awPopOver: "<p>Select the playbook to be executed by this job.</p>",
dataTitle: 'Playbook', dataTitle: 'Playbook',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
credential: { // FIXME: Lookup only credentials with kind=ssh credential: { // FIXME: Lookup only credentials with kind=ssh
label: 'Credential', label: 'Credential',
type: 'lookup', type: 'lookup',
sourceModel: 'credential', sourceModel: 'credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCredential()', ngClick: 'lookUpCredential()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " + awPopOver: "<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
" the username and SSH key or password that Ansbile will need to log into the remote hosts.</p>", " the username and SSH key or password that Ansbile will need to log into the remote hosts.</p>",
dataTitle: 'Credential', dataTitle: 'Credential',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
cloud_credential: { // FIXME: Lookup only credentials with kind=aws/rax cloud_credential: { // FIXME: Lookup only credentials with kind=aws/rax
label: 'Cloud Credential', label: 'Cloud Credential',
type: 'lookup', type: 'lookup',
sourceModel: 'cloud_credential', sourceModel: 'cloud_credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCredential()', ngClick: 'lookUpCredential()',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " + awPopOver: "<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
@@ -140,17 +139,17 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Cloud Credential', dataTitle: 'Cloud Credential',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: "body" dataContainer: "body"
}, },
forks: { forks: {
label: 'Forks', label: 'Forks',
id: 'forks-number', id: 'forks-number',
type: 'number', type: 'number',
integer: true, integer: true,
min: 0, min: 0,
spinner: true, spinner: true,
"class": 'input-small', "class": 'input-small',
"default": '0', "default": '0',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
disabled: true, disabled: true,
@@ -158,43 +157,43 @@ angular.module('JobFormDefinition', [])
dataContainer: 'body', dataContainer: 'body',
dataTitle: 'Forks', dataTitle: 'Forks',
dataPlacement: 'right' dataPlacement: 'right'
}, },
limit: { limit: {
label: 'Limit', label: 'Limit',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 1, column: 1,
awPopOver: "<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " + awPopOver: "<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
"Multiple patterns can be separated by &#59; &#58; or &#44;</p><p>For more information and examples see the " + "Multiple patterns can be separated by &#59; &#58; or &#44;</p><p>For more information and examples see the " +
"<a href=\"http://ansible.cc/docs/patterns.html#selecting-targets\" target=\"_blank\">Selecting Targets section</a> under Inventory and Patterns " + "<a href=\"http://ansible.cc/docs/patterns.html#selecting-targets\" target=\"_blank\">Selecting Targets section</a> under Inventory and Patterns " +
" in the Ansible documentation.</p>", " in the Ansible documentation.</p>",
dataContainer: 'body', dataContainer: 'body',
dataTitle: 'Limit', dataTitle: 'Limit',
dataPlacement: 'right' dataPlacement: 'right'
}, },
verbosity: { verbosity: {
label: 'Verbosity', label: 'Verbosity',
type: 'select', type: 'select',
ngOptions: 'v.label for v in verbosity_options', ngOptions: 'v.label for v in verbosity_options',
"default": 0, "default": 0,
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
column: 1, column: 1,
awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>", awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>",
dataTitle: 'Verbosity', dataTitle: 'Verbosity',
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body' dataContainer: 'body'
}, },
variables: { variables: {
label: 'Extra Variables', label: 'Extra Variables',
type: 'textarea', type: 'textarea',
rows: 6, rows: 6,
"class": 'span12', "class": 'span12',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
column: 2, column: 2,
awPopOver: "<p>Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter " + awPopOver: "<p>Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter " +
"for ansible-playbook. Provide key/value pairs using either YAML or JSON.</p>" + "for ansible-playbook. Provide key/value pairs using either YAML or JSON.</p>" +
"JSON:<br />\n" + "JSON:<br />\n" +
"<blockquote>{<br />\"somevar\": \"somevalue\",<br />\"password\": \"magic\"<br /> }</blockquote>\n" + "<blockquote>{<br />\"somevar\": \"somevalue\",<br />\"password\": \"magic\"<br /> }</blockquote>\n" +
@@ -203,13 +202,13 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Extra Variables', dataTitle: 'Extra Variables',
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right' dataPlacement: 'right'
}, },
job_tags: { job_tags: {
label: 'Job Tags', label: 'Job Tags',
type: 'textarea', type: 'textarea',
rows: 1, rows: 1,
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
'class': 'span12', 'class': 'span12',
column: 2, column: 2,
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" + awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
@@ -217,27 +216,27 @@ angular.module('JobFormDefinition', [])
"<p>For example, you might have a task consisiting of a long list of actions. Tag values can be assigned to each action. " + "<p>For example, you might have a task consisiting of a long list of actions. Tag values can be assigned to each action. " +
"Suppose the actions have been assigned tag values of &quot;configuration&quot;, &quot;packages&quot; and &quot;install&quot;.</p>" + "Suppose the actions have been assigned tag values of &quot;configuration&quot;, &quot;packages&quot; and &quot;install&quot;.</p>" +
"<p>If you just want to run the &quot;configuration&quot; and &quot;packages&quot; actions, you would enter the following here " + "<p>If you just want to run the &quot;configuration&quot; and &quot;packages&quot; actions, you would enter the following here " +
"in the Job Tags field:<\p>\n" + "in the Job Tags field:</p>\n" +
"<blockquote>configuration,packages</blockquote>\n", "<blockquote>configuration,packages</blockquote>\n",
dataTitle: "Job Tags", dataTitle: "Job Tags",
dataContainer: 'body', dataContainer: 'body',
dataPlacement: "right" dataPlacement: "right"
}, },
allow_callbacks: { allow_callbacks: {
label: 'Allow Callbacks', label: 'Allow Callbacks',
type: 'checkbox', type: 'checkbox',
addRequired: false, addRequired: false,
editRequird: false, editRequird: false,
trueValue: 'true', trueValue: 'true',
falseValue: 'false', falseValue: 'false',
ngChange: "toggleCallback('host_config_key')", ngChange: "toggleCallback('host_config_key')",
"class": "span12", "class": "span12",
column: 2, column: 2,
awPopOver: "<p>Create a callback URL a host can use to contact Tower and request a configuration update " + awPopOver: "<p>Create a callback URL a host can use to contact Tower and request a configuration update " +
"using the job template. The URL will look like the following:</p>\n" + "using the job template. The URL will look like the following:</p>\n" +
"<p class=\"code-breakable\">http://your.server.com:999/api/v1/job_templates/1/callback/</p>" + "<p class=\"code-breakable\">http://your.server.com:999/api/v1/job_templates/1/callback/</p>" +
"<p>The request from the host must be a POST. Here is an example using curl:</p>\n" + "<p>The request from the host must be a POST. Here is an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" + "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" +
"<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " + "<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " +
"in one of your defined inventories, the request will be denied.</p>" + "in one of your defined inventories, the request will be denied.</p>" +
@@ -245,11 +244,11 @@ angular.module('JobFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body', dataContainer: 'body',
dataTitle: 'Callback URL' dataTitle: 'Callback URL'
}, },
callback_url: { callback_url: {
label: 'Callback URL', label: 'Callback URL',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
readonly: true, readonly: true,
column: 2, column: 2,
@@ -257,7 +256,7 @@ angular.module('JobFormDefinition', [])
'class': 'span12', 'class': 'span12',
awPopOver: "<p>Using this URL a host can contact Tower and request a configuration update using the job " + awPopOver: "<p>Using this URL a host can contact Tower and request a configuration update using the job " +
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" + "template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" + "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n" +
"<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " + "<p>Note the requesting host must be defined in your inventory. If ansible fails to locate the host either by name or IP address " +
"in one of your defined inventories, the request will be denied.</p>" + "in one of your defined inventories, the request will be denied.</p>" +
@@ -265,7 +264,7 @@ angular.module('JobFormDefinition', [])
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body', dataContainer: 'body',
dataTitle: 'Callback URL' dataTitle: 'Callback URL'
}, },
host_config_key: { host_config_key: {
label: 'Host Config Key', label: 'Host Config Key',
type: 'text', type: 'text',
@@ -274,64 +273,65 @@ angular.module('JobFormDefinition', [])
column: 2, column: 2,
awPopOver: "<p>When contacting Tower using the callback URL, the calling host must authenticate by including " + awPopOver: "<p>When contacting Tower using the callback URL, the calling host must authenticate by including " +
"this key in the POST data of the request. Here's an example using curl:</p>\n" + "this key in the POST data of the request. Here's an example using curl:</p>\n" +
"<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " + "<p class=\"code-breakable\">curl --data \"host_config_key=5a8ec154832b780b9bdef1061764ae5a\" " +
"http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n", "http://your.server.com:999/api/v1/job_templates/1/callback/</p>\n",
dataPlacement: 'right', dataPlacement: 'right',
dataContainer: 'body' dataContainer: 'body'
} }
}, },
buttons: { //for now always generates <button> tags buttons: {
save: { save: {
label: 'Save', label: 'Save',
icon: 'icon-ok', icon: 'icon-ok',
"class": 'btn-success', "class": 'btn-success',
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
label: 'Reset', label: 'Reset',
icon: 'icon-undo', icon: 'icon-undo',
'class': 'btn btn-default', 'class': 'btn btn-default',
ngDisabled: true //Disabled when $pristine ngDisabled: true
} }
}, },
statusFields: { statusFields: {
status: { status: {
//label: 'Job Status', //label: 'Job Status',
type: 'custom', type: 'custom',
control: '<div class=\"job-detail-status\"><span style="padding-right: 15px; font-weight: bold;">Status</span> <i class=\"fa icon-job-\{\{ status \}\}\"></i> \{\{ status \}\}</div>', control: "<div class=\"job-detail-status\"><span style=\"padding-right: 15px; font-weight: bold;\">Status</span> " +
"<i class=\"fa icon-job-{{ status }}\"></i> {{ status }}</div>",
readonly: true readonly: true
}, },
created: { created: {
label: 'Created On', label: 'Created On',
type: 'text', type: 'text',
readonly: true readonly: true
}, },
result_stdout: { result_stdout: {
label: 'Standard Out', label: 'Standard Out',
type: 'textarea', type: 'textarea',
readonly: true, readonly: true,
xtraWide: true, xtraWide: true,
rows: "\{\{ stdout_rows \}\}", rows: "{{ stdout_rows }}",
"class": 'nowrap mono-space', "class": 'nowrap mono-space',
ngShow: "result_stdout != ''" ngShow: "result_stdout != ''"
}, },
result_traceback: { result_traceback: {
label: 'Traceback', label: 'Traceback',
type: 'textarea', type: 'textarea',
xtraWide: true, xtraWide: true,
readonly: true, readonly: true,
rows: "\{\{ traceback_rows \}\}", rows: "{{ traceback_rows }}",
"class": 'nowrap mono-space', "class": 'nowrap mono-space',
ngShow: "result_traceback != ''" ngShow: "result_traceback != ''"
} }
}, },
statusActions: { statusActions: {
refresh: { refresh: {
dataPlacement: 'top', dataPlacement: 'top',
icon: "icon-refresh", icon: "icon-refresh",
iconSize: 'large', iconSize: 'large',
@@ -340,8 +340,7 @@ angular.module('JobFormDefinition', [])
'class': 'btn-xs btn-primary', 'class': 'btn-xs btn-primary',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()" ngClick: "refresh()"
}
} }
}
}); //Form
});

View File

@@ -0,0 +1,84 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* LicenseForm.js
*
*
*/
'use strict';
angular.module('LicenseFormDefinition', [])
.value('LicenseForm', {
name: 'license',
well: false,
forceListeners: true,
fields: {
license_status: {
type: 'custom',
control: "<div class=\"license-status\" ng-class=\"status_color\"><i class=\"fa fa-circle\"></i> " +
"{{ license_status }}</span></div>",
readonly: true,
section: 'License'
},
license_key: {
label: 'Key',
type: 'textarea',
'class': 'modal-input-xlarge',
readonly: true,
section: 'License'
},
license_date: {
label: 'Expires On',
type: 'text',
readonly: true,
section: 'License'
},
time_remaining: {
label: 'Time Left',
type: 'text',
readonly: true,
section: 'License'
},
available_instances: {
label: 'Available',
type: 'text',
readonly: true,
section: 'Managed Hosts'
},
current_instances: {
label: 'Used',
type: 'text',
readonly: true,
section: 'Managed Hosts'
},
free_instances: {
label: 'Remaining',
type: 'text',
readonly: true,
section: 'Managed Hosts',
controlNGClass: 'free_instances_class',
labelNGClass: 'free_instances_class'
},
company_name: {
label: 'Company',
type: 'text',
readonly: true,
section: 'Contact Info'
},
contact_name: {
label: 'Contact',
type: 'text',
readonly: true,
section: 'Contact Info'
},
contact_email: {
label: 'Contact Email',
type: 'text',
readonly: true,
section: 'Contact Info'
}
}
});

View File

@@ -4,16 +4,15 @@
* Organization.js * Organization.js
* Form definition for Organization model * Form definition for Organization model
* *
* *
*/ */
angular.module('OrganizationFormDefinition', []) angular.module('OrganizationFormDefinition', [])
.value( .value('OrganizationForm', {
'OrganizationForm', {
addTitle: 'Create Organization', //Title in add mode
addTitle: 'Create Organization', //Title in add mode editTitle: '{{ name }}', //Title in edit mode
editTitle: '{{ name }}', //Title in edit mode name: 'organization', //entity or model name in singular form
name: 'organization', //entity or model name in singular form well: true,
well: true, //Wrap the form with TB well/
actions: { actions: {
stream: { stream: {
@@ -24,8 +23,8 @@ angular.module('OrganizationFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
name: { name: {
@@ -34,120 +33,118 @@ angular.module('OrganizationFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: false capitalize: false
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
} }
}, },
buttons: { //for now always generates <button> tags buttons: { //for now always generates <button> tags
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true //Disable when $pristine or $invalid, optional
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine ngDisabled: true //Disabled when $pristine
} }
}, },
related: { //related colletions (and maybe items?) related: {
users: { users: {
type: 'collection', type: 'collection',
title: 'Users', title: 'Users',
iterator: 'user', iterator: 'user',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('users')", ngClick: "add('users')",
label: 'Add', label: 'Add',
icon: 'icon-plus', icon: 'icon-plus',
awToolTip: 'Add a new user' awToolTip: 'Add a new user'
} }
}, },
fields: { fields: {
username: { username: {
key: true, key: true,
label: 'Username' label: 'Username'
}, },
first_name: { first_name: {
label: 'First Name' label: 'First Name'
}, },
last_name: { last_name: {
label: 'Last Name' label: 'Last Name'
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "edit('users', \{\{ user.id \}\}, '\{\{ user.username \}\}')",
icon: 'icon-edit',
'class': 'btn-default',
awToolTip: 'Edit user'
},
"delete": {
label: 'Delete',
ngClick: "delete('users', \{\{ user.id \}\}, '\{\{ user.username \}\}', 'users')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Remove user'
}
} }
}, },
admins: { // Assumes a plural name (e.g. things) fieldActions: {
edit: {
label: 'Edit',
ngClick: "edit('users', user.id, user.username)",
icon: 'icon-edit',
'class': 'btn-default',
awToolTip: 'Edit user'
},
"delete": {
label: 'Delete',
ngClick: "delete('users', user.id, user.username, 'users')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Remove user'
}
}
},
admins: { // Assumes a plural name (e.g. things)
type: 'collection', type: 'collection',
title: 'Administrators', title: 'Administrators',
iterator: 'admin', // Singular form of name (e.g. thing) iterator: 'admin', // Singular form of name (e.g. thing)
open: false, // Open accordion on load? open: false, // Open accordion on load?
base: '/users', base: '/users',
actions: { // Actions displayed top right of list actions: { // Actions displayed top right of list
add: { add: {
ngClick: "add('admins')", ngClick: "add('admins')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add new administrator' awToolTip: 'Add new administrator'
} }
}, },
fields: { fields: {
username: { username: {
key: true, key: true,
label: 'Username' label: 'Username'
}, },
first_name: { first_name: {
label: 'First Name' label: 'First Name'
}, },
last_name: { last_name: {
label: 'Last Name' label: 'Last Name'
} }
}, },
fieldActions: { // Actions available on each row fieldActions: { // Actions available on each row
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('users', \{\{ admin.id \}\}, '\{\{ admin.username \}\}')", ngClick: "edit('users', admin.id, admin.username)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit administrator', awToolTip: 'Edit administrator',
'class': 'btn-default' 'class': 'btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('admins', \{\{ admin.id \}\}, '\{\{ admin.username \}\}', 'administrators')", ngClick: "delete('admins', admin.id, admin.username, 'administrators')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Remove administrator' awToolTip: 'Remove administrator'
}
} }
} }
} }
}); //OrganizationForm }
}); //OrganizationForm

View File

@@ -5,60 +5,63 @@
* *
* Form definition for Projects model * Form definition for Projects model
* *
* *
*/ */
angular.module('PermissionFormDefinition', []) angular.module('PermissionFormDefinition', [])
.value( .value('PermissionsForm', {
'PermissionsForm', {
addTitle: 'Add Permission', //Title in add mode
addTitle: 'Add Permission', //Title in add mode editTitle: '{{ name }}', //Title in edit mode
editTitle: '{{ name }}', //Title in edit mode name: 'permission', //entity or model name in singular form
name: 'permission', //entity or model name in singular form well: true, //Wrap the form with TB well
well: true, //Wrap the form with TB well forceListeners: true,
forceListeners: true,
stream: { stream: {
'class': "btn-primary btn-xs activity-btn", 'class': "btn-primary btn-xs activity-btn",
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
dataPlacement: "top", dataPlacement: "top",
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
}, },
fields: { fields: {
category: { category: {
label: 'Permission Type', label: 'Permission Type',
labelClass: 'prepend-asterisk', labelClass: 'prepend-asterisk',
type: 'radio_group', type: 'radio_group',
options: [ options: [{
{ label: 'Inventory', value: 'Inventory', selected: true }, label: 'Inventory',
{ label: 'Deployment', value: 'Deploy'} value: 'Inventory',
], selected: true
}, {
label: 'Deployment',
value: 'Deploy'
}],
ngChange: 'selectCategory()' ngChange: 'selectCategory()'
}, },
name: { name: {
label: 'Name', label: 'Name',
type: 'text', type: 'text',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: false capitalize: false
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
user: { user: {
label: 'User', label: 'User',
type: 'hidden' type: 'hidden'
}, },
team: { team: {
label: 'Team', label: 'Team',
type: 'hidden' type: 'hidden'
}, },
project: { project: {
label: 'Project', label: 'Project',
type: 'lookup', type: 'lookup',
@@ -66,44 +69,65 @@ angular.module('PermissionFormDefinition', [])
sourceField: 'name', sourceField: 'name',
ngShow: "category == 'Deploy'", ngShow: "category == 'Deploy'",
ngClick: 'lookUpProject()', ngClick: 'lookUpProject()',
awRequiredWhen: { variable: "projectrequired", init: "false" } awRequiredWhen: {
}, variable: "projectrequired",
init: "false"
}
},
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
type: 'lookup', type: 'lookup',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpInventory()', ngClick: 'lookUpInventory()',
awRequiredWhen: {variable: "inventoryrequired", init: "true" } awRequiredWhen: {
}, variable: "inventoryrequired",
init: "true"
}
},
permission_type: { permission_type: {
label: 'Permission', label: 'Permission',
labelClass: 'prepend-asterisk', labelClass: 'prepend-asterisk',
type: 'radio_group', type: 'radio_group',
options: [ options: [{
{label: 'Read', value: 'read', ngShow: "category == 'Inventory'" }, label: 'Read',
{label: 'Write', value: 'write', ngShow: "category == 'Inventory'" }, value: 'read',
{label: 'Admin', value: 'admin', ngShow: "category == 'Inventory'" }, ngShow: "category == 'Inventory'"
{label: 'Run', value: 'run', ngShow: "category == 'Deploy'" }, }, {
{label: 'Check', value: 'check', ngShow: "category == 'Deploy'" } label: 'Write',
], value: 'write',
helpCollapse: [{ hdr: 'Permission', ngBind: 'permissionTypeHelp' }] ngShow: "category == 'Inventory'"
} }, {
}, label: 'Admin',
value: 'admin',
buttons: { //for now always generates <button> tags ngShow: "category == 'Inventory'"
save: { }, {
ngClick: 'formSave()', //$scope.function to call on click, optional label: 'Run',
ngDisabled: true //Disable when $pristine or $invalid, optional value: 'run',
}, ngShow: "category == 'Deploy'"
reset: { }, {
ngClick: 'formReset()', label: 'Check',
ngDisabled: true //Disabled when $pristine value: 'check',
} ngShow: "category == 'Deploy'"
}, }],
helpCollapse: [{
related: { //related colletions (and maybe items?) hdr: 'Permission',
ngBind: 'permissionTypeHelp'
}]
} }
},
buttons: {
save: {
ngClick: 'formSave()',
ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true
}
},
related: { }
}); // Form }); // Form

View File

@@ -7,17 +7,16 @@
* *
*/ */
angular.module('ProjectStatusDefinition', []) angular.module('ProjectStatusDefinition', [])
.value( .value('ProjectStatusForm', {
'ProjectStatusForm', {
name: 'project_update', name: 'project_update',
editTitle: 'SCM Status', editTitle: 'SCM Status',
well: false, well: false,
'class': 'horizontal-narrow', 'class': 'horizontal-narrow',
fields: { fields: {
created: { created: {
label: 'Created', label: 'Created',
type: 'text', type: 'text',
readonly: true readonly: true
}, },
@@ -27,7 +26,7 @@ angular.module('ProjectStatusDefinition', [])
readonly: true readonly: true
}, },
result_stdout: { result_stdout: {
label: 'Std Out', label: 'Std Out',
type: 'textarea', type: 'textarea',
ngShow: "result_stdout", ngShow: "result_stdout",
'class': 'mono-space', 'class': 'mono-space',
@@ -35,7 +34,7 @@ angular.module('ProjectStatusDefinition', [])
rows: 15 rows: 15
}, },
result_traceback: { result_traceback: {
label: 'Traceback', label: 'Traceback',
type: 'textarea', type: 'textarea',
ngShow: "result_traceback", ngShow: "result_traceback",
'class': 'mono-space', 'class': 'mono-space',

View File

@@ -5,16 +5,15 @@
* *
* Form definition for Projects model * Form definition for Projects model
* *
* *
*/ */
angular.module('ProjectFormDefinition', []) angular.module('ProjectFormDefinition', [])
.value( .value('ProjectsForm', {
'ProjectsForm', {
addTitle: 'Create Project', // Title in add mode
addTitle: 'Create Project', // Title in add mode editTitle: '{{ name }}', // Title in edit mode
editTitle: '{{ name }}', // Title in edit mode name: 'project', // entity or model name in singular form
name: 'project', // entity or model name in singular form well: true, // Wrap the form with TB well
well: true, // Wrap the form with TB well
forceListeners: true, forceListeners: true,
actions: { actions: {
@@ -26,8 +25,8 @@ angular.module('ProjectFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
name: { name: {
@@ -36,13 +35,13 @@ angular.module('ProjectFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: false capitalize: false
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
type: 'lookup', type: 'lookup',
@@ -52,35 +51,38 @@ angular.module('ProjectFormDefinition', [])
editRequired: false, editRequired: false,
excludeMode: 'edit', excludeMode: 'edit',
ngClick: 'lookUpOrganization()', ngClick: 'lookUpOrganization()',
awRequiredWhen: {variable: "organizationrequired", init: "true" }, awRequiredWhen: {
variable: "organizationrequired",
init: "true"
},
awPopOver: '<p>A project must have at least one organization. Pick one organization now to create the project, and then after ' + awPopOver: '<p>A project must have at least one organization. Pick one organization now to create the project, and then after ' +
'the project is created you can add additional organizations.</p><p>Only super users and organization administrators are allowed ' + 'the project is created you can add additional organizations.</p><p>Only super users and organization administrators are allowed ' +
'to make changes to projects. Associating one or more organizations to a project determins which organizations admins have ' + 'to make changes to projects. Associating one or more organizations to a project determins which organizations admins have ' +
'access to modify the project.', 'access to modify the project.',
dataTitle: 'Organization', dataTitle: 'Organization',
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right' dataPlacement: 'right'
}, },
scm_type: { scm_type: {
label: 'SCM Type', label: 'SCM Type',
type: 'select', type: 'select',
ngOptions: 'type.label for type in scm_type_options', ngOptions: 'type.label for type in scm_type_options',
ngChange: 'scmChange()', ngChange: 'scmChange()',
addRequired: true, addRequired: true,
editRequired: true editRequired: true
}, },
missing_path_alert: { missing_path_alert: {
type: 'alertblock', type: 'alertblock',
"class": 'alert-info', "class": 'alert-info',
ngShow: "showMissingPlaybooksAlert && scm_type.value == ''", ngShow: "showMissingPlaybooksAlert && scm_type.value == ''",
alertTxt: '<p class=\"text-justify\"><strong>WARNING:</strong> There are no unassigned playbook directories in the base ' + alertTxt: '<p class=\"text-justify\"><strong>WARNING:</strong> There are no unassigned playbook directories in the base ' +
'project path {{ base_dir }}. Either the projects directory is empty, or all of the contents are already assigned to ' + 'project path {{ base_dir }}. Either the projects directory is empty, or all of the contents are already assigned to ' +
'other projects. New projects can be checked out from source control by ' + 'other projects. New projects can be checked out from source control by ' +
'changing the SCM type option rather than specifying checkout paths manually. To continue with manual setup, log into ' + 'changing the SCM type option rather than specifying checkout paths manually. To continue with manual setup, log into ' +
'the Tower host and ensure content is present in a subdirectory under {{ base_dir }}. Run "chown -R awx" on the content ' + 'the Tower host and ensure content is present in a subdirectory under {{ base_dir }}. Run "chown -R awx" on the content ' +
'directory to ensure Tower can read the playbooks.</p>', 'directory to ensure Tower can read the playbooks.</p>',
closeable: false closeable: false
}, },
base_dir: { base_dir: {
label: 'Project Base Path', label: 'Project Base Path',
type: 'textarea', type: 'textarea',
@@ -88,61 +90,69 @@ angular.module('ProjectFormDefinition', [])
showonly: true, showonly: true,
ngShow: "scm_type.value == ''", ngShow: "scm_type.value == ''",
awPopOver: '<p>Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' + awPopOver: '<p>Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
'Together the base path and selected playbook directory provide the full path used to locate playbooks.</p>' + 'Together the base path and selected playbook directory provide the full path used to locate playbooks.</p>' +
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>', '<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>',
dataTitle: 'Project Base Path', dataTitle: 'Project Base Path',
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right' dataPlacement: 'right'
}, },
local_path: { local_path: {
label: 'Playbook Directory', label: 'Playbook Directory',
type: 'select', type: 'select',
id: 'local-path-select', id: 'local-path-select',
ngOptions: 'path.label for path in project_local_paths', ngOptions: 'path.label for path in project_local_paths',
awRequiredWhen: { variable: "pathRequired", init: false }, awRequiredWhen: {
variable: "pathRequired",
init: false
},
ngShow: "scm_type.value == '' && !showMissingPlaybooksAlert", ngShow: "scm_type.value == '' && !showMissingPlaybooksAlert",
awPopOver: '<p>Select from the list of directories found in the base path.' + awPopOver: '<p>Select from the list of directories found in the base path.' +
'Together the base path and the playbook directory provide the full path used to locate playbooks.</p>' + 'Together the base path and the playbook directory provide the full path used to locate playbooks.</p>' +
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>', '<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>',
dataTitle: 'Project Path', dataTitle: 'Project Path',
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right' dataPlacement: 'right'
}, },
scm_url: { scm_url: {
label: 'SCM URL', label: 'SCM URL',
type: 'text', type: 'text',
ngShow: "scm_type && scm_type.value !== ''", ngShow: "scm_type && scm_type.value !== ''",
awRequiredWhen: { variable: "scmRequired", init: false }, awRequiredWhen: {
helpCollapse: [ variable: "scmRequired",
{ hdr: 'GIT URLs', init: false
content: '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
'<li>git@github.com:ansible/ansible.git</li><li>git://servername.example.com/ansible.git</li></ul>' +
'<p><strong>Note:</strong> If using SSH protocol for GitHub or Bitbucket, enter in the SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
'SSH protocol. GIT read only protocol (git://) does not use username or password information.',
show: "scm_type.value == 'git'" },
{ hdr: 'SVN URLs',
content: '<p>Example URLs for Subversion SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
'<li>svn+ssh://servername.example.com/path</li></ul>',
show: "scm_type.value == 'svn'" },
{ hdr: 'Mercurial URLs',
content: '<p>Example URLs for Mercurial SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://bitbucket.org/username/project</li><li>ssh://hg@bitbucket.org/username/project</li>' +
'<li>ssh://server.example.com/path</li></ul>' +
'<p><strong>Note:</strong> Mercurial does not support password authentication for SSH. ' +
'If applicable, add the username, password and key below. Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.',
show: "scm_type.value == 'hg'" }
]
}, },
helpCollapse: [{
hdr: 'GIT URLs',
content: '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
'<li>git@github.com:ansible/ansible.git</li><li>git://servername.example.com/ansible.git</li></ul>' +
'<p><strong>Note:</strong> If using SSH protocol for GitHub or Bitbucket, enter in the SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
'SSH protocol. GIT read only protocol (git://) does not use username or password information.',
show: "scm_type.value == 'git'"
}, {
hdr: 'SVN URLs',
content: '<p>Example URLs for Subversion SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
'<li>svn+ssh://servername.example.com/path</li></ul>',
show: "scm_type.value == 'svn'"
}, {
hdr: 'Mercurial URLs',
content: '<p>Example URLs for Mercurial SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://bitbucket.org/username/project</li><li>ssh://hg@bitbucket.org/username/project</li>' +
'<li>ssh://server.example.com/path</li></ul>' +
'<p><strong>Note:</strong> Mercurial does not support password authentication for SSH. ' +
'If applicable, add the username, password and key below. Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.',
show: "scm_type.value == 'hg'"
}]
},
scm_branch: { scm_branch: {
labelBind: "scmBranchLabel", labelBind: "scmBranchLabel",
type: 'text', type: 'text',
ngShow: "scm_type && scm_type.value !== ''", ngShow: "scm_type && scm_type.value !== ''",
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
credential: { credential: {
label: 'SCM Credential', label: 'SCM Credential',
type: 'lookup', type: 'lookup',
@@ -150,111 +160,105 @@ angular.module('ProjectFormDefinition', [])
sourceModel: 'credential', sourceModel: 'credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpCredential()', ngClick: 'lookUpCredential()',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
checkbox_group: { checkbox_group: {
label: 'SCM Update Options', label: 'SCM Update Options',
type: 'checkbox_group', type: 'checkbox_group',
ngShow: "scm_type && scm_type.value !== ''", ngShow: "scm_type && scm_type.value !== ''",
fields: [ fields: [{
{ name: 'scm_clean',
name: 'scm_clean', label: 'Clean',
label: 'Clean', type: 'checkbox',
type: 'checkbox', addRequired: false,
addRequired: false, editRequired: false,
editRequired: false, awPopOver: '<p>Remove any local modifications prior to performing an update.</p>',
awPopOver: '<p>Remove any local modifications prior to performing an update.</p>', dataTitle: 'SCM Clean',
dataTitle: 'SCM Clean', dataContainer: 'body',
dataContainer: 'body', dataPlacement: 'right',
dataPlacement: 'right', labelClass: 'checkbox-options'
labelClass: 'checkbox-options' }, {
}, name: 'scm_delete_on_update',
{ label: 'Delete on Update',
name: 'scm_delete_on_update', type: 'checkbox',
label: 'Delete on Update', addRequired: false,
type: 'checkbox', editRequired: false,
addRequired: false, awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
editRequired: false, 'repository this may significantly increase the amount of time required to complete an update.</p>',
awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' + dataTitle: 'SCM Delete',
'repository this may significantly increase the amount of time required to complete an update.</p>', dataContainer: 'body',
dataTitle: 'SCM Delete', dataPlacement: 'right',
dataContainer: 'body', labelClass: 'checkbox-options'
dataPlacement: 'right', }, {
labelClass: 'checkbox-options' name: 'scm_update_on_launch',
}, label: 'Update on Launch',
{ type: 'checkbox',
name: 'scm_update_on_launch', addRequired: false,
label: 'Update on Launch', editRequired: false,
type: 'checkbox', awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>',
addRequired: false, dataTitle: 'SCM Update',
editRequired: false, dataContainer: 'body',
awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>', dataPlacement: 'right',
dataTitle: 'SCM Update', labelClass: 'checkbox-options'
dataContainer: 'body', }]
dataPlacement: 'right', }
labelClass: 'checkbox-options' },
}
]
}
},
buttons: { //for now always generates <button> tags buttons: {
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine ngDisabled: true
} }
}, },
related: { //related colletions (and maybe items?) related: {
organizations: { organizations: {
type: 'collection', type: 'collection',
title: 'Organizations', title: 'Organizations',
iterator: 'organization', iterator: 'organization',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('organizations')", ngClick: "add('organizations')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add an organization' awToolTip: 'Add an organization'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('organizations', \{\{ organization.id \}\}, '\{\{ organization.name \}\}')", ngClick: "edit('organizations', organization.id, organization.name)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit the organization', awToolTip: 'Edit the organization',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('organizations', \{\{ organization.id \}\}, '\{\{ organization.name \}\}', 'organizations')", ngClick: "delete('organizations', organization.id, organization.name, 'organizations')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Delete the organization' awToolTip: 'Delete the organization'
}
} }
} }
} }
}
}); // Form }); // Form

View File

@@ -4,14 +4,13 @@
* Teams.js * Teams.js
* Form definition for Team model * Form definition for Team model
* *
* *
*/ */
angular.module('TeamFormDefinition', []) angular.module('TeamFormDefinition', [])
.value( .value('TeamForm', {
'TeamForm', {
addTitle: 'Create Team', //Legend in add mode
addTitle: 'Create Team', //Legend in add mode editTitle: '{{ name }}', //Legend in edit mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'team', name: 'team',
well: true, well: true,
collapse: true, collapse: true,
@@ -28,8 +27,8 @@ angular.module('TeamFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
name: { name: {
@@ -38,228 +37,230 @@ angular.module('TeamFormDefinition', [])
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: false capitalize: false
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
addRequired: false, addRequired: false,
editRequired: false editRequired: false
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
type: 'lookup', type: 'lookup',
sourceModel: 'organization', sourceModel: 'organization',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpOrganization()', ngClick: 'lookUpOrganization()',
awRequiredWhen: {variable: "teamrequired", init: "true" } awRequiredWhen: {
variable: "teamrequired",
init: "true"
} }
}, }
},
buttons: { //for now always generates <button> tags buttons: {
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine ngDisabled: true
} }
}, },
related: { //related colletions (and maybe items?) related: {
credentials: { credentials: {
type: 'collection', type: 'collection',
title: 'Credentials', title: 'Credentials',
iterator: 'credential', iterator: 'credential',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('credentials')", ngClick: "add('credentials')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
add: 'Add a new credential' add: 'Add a new credential'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('credentials', \{\{ credential.id \}\}, '\{\{ credential.name \}\}')", ngClick: "edit('credentials', credential.id, credential.name)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Modify the credential', awToolTip: 'Modify the credential',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('credentials', \{\{ credential.id \}\}, '\{\{ credential.name \}\}', 'credentials')", ngClick: "delete('credentials', credential.id, credential.name, 'credentials')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Remove the credential' awToolTip: 'Remove the credential'
}
} }
}, }
},
permissions: { permissions: {
type: 'collection', type: 'collection',
title: 'Permissions', title: 'Permissions',
iterator: 'permission', iterator: 'permission',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('permissions')", ngClick: "add('permissions')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add a permission for this user', awToolTip: 'Add a permission for this user',
ngShow: 'PermissionAddAllowed' ngShow: 'PermissionAddAllowed'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name', label: 'Name',
ngClick: "edit('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}')" ngClick: "edit('permissions', permission.id, permission.name)"
}, },
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.inventory.name' ngBind: 'permission.summary_fields.inventory.name'
}, },
project: { project: {
label: 'Project', label: 'Project',
sourceModel: 'project', sourceModel: 'project',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.project.name' ngBind: 'permission.summary_fields.project.name'
},
permission_type: {
label: 'Permission'
}
}, },
permission_type: {
label: 'Permission'
}
},
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}')", ngClick: "edit('permissions', permission.id, permission.name)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit the permission', awToolTip: 'Edit the permission',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}', 'permissions')", ngClick: "delete('permissions', permission.id, permission.name, 'permissions')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Delete the permission', awToolTip: 'Delete the permission',
ngShow: 'PermissionAddAllowed' ngShow: 'PermissionAddAllowed'
}
} }
}
}, },
projects: { projects: {
type: 'collection', type: 'collection',
title: 'Projects', title: 'Projects',
iterator: 'project', iterator: 'project',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('projects')", ngClick: "add('projects')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add' label: 'Add'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "edit('projects', \{\{ project.id \}\}, '\{\{ project.name \}\}')",
icon: 'icon-edit',
awToolTip: 'Modify the project',
'class': 'btn btn-default'
},
"delete": {
label: 'Delete',
ngClick: "delete('projects', \{\{ project.id \}\}, '\{\{ project.name \}\}', 'projects')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Remove the project'
}
} }
}, },
users: { fieldActions: {
edit: {
label: 'Edit',
ngClick: "edit('projects', project.id, project.name)",
icon: 'icon-edit',
awToolTip: 'Modify the project',
'class': 'btn btn-default'
},
"delete": {
label: 'Delete',
ngClick: "delete('projects', project.id, project.name, 'projects')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Remove the project'
}
}
},
users: {
type: 'collection', type: 'collection',
title: 'Users', title: 'Users',
iterator: 'user', iterator: 'user',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('users')", ngClick: "add('users')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add a user' awToolTip: 'Add a user'
} }
}, },
fields: { fields: {
username: { username: {
key: true, key: true,
label: 'Username' label: 'Username'
}, },
first_name: { first_name: {
label: 'First Name' label: 'First Name'
}, },
last_name: { last_name: {
label: 'Last Name' label: 'Last Name'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('users', \{\{ user.id \}\}, '\{\{ user.username \}\}')", ngClick: "edit('users', user.id, user.username)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit user', awToolTip: 'Edit user',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('users', \{\{ user.id \}\}, '\{\{ user.username \}\}', 'users')", ngClick: "delete('users', user.id, user.username, 'users')",
icon: 'icon-terash', icon: 'icon-terash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Remove user' awToolTip: 'Remove user'
}
} }
} }
} }
}); //InventoryForm
}
}); //InventoryForm

View File

@@ -4,16 +4,15 @@
* Users.js * Users.js
* Form definition for User model * Form definition for User model
* *
* *
*/ */
angular.module('UserFormDefinition', []) angular.module('UserFormDefinition', [])
.value( .value('UserForm', {
'UserForm', {
addTitle: 'Create User',
addTitle: 'Create User', //Legend in add mode editTitle: '{{ username }}',
editTitle: '{{ username }}', //Legend in edit mode name: 'user',
name: 'user', //Form name attribute well: true,
well: true, //Wrap the form with TB well
forceListeners: true, forceListeners: true,
actions: { actions: {
@@ -25,31 +24,31 @@ angular.module('UserFormDefinition', [])
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'edit', mode: 'edit',
iconSize: 'large' iconSize: 'large'
} }
}, },
fields: { fields: {
first_name: { first_name: {
label: 'First Name', label: 'First Name',
type: 'text', type: 'text',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: true capitalize: true
}, },
last_name: { last_name: {
label: 'Last Name', label: 'Last Name',
type: 'text', type: 'text',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
capitalize: true capitalize: true
}, },
email: { email: {
label: 'Email', label: 'Email',
type: 'email', type: 'email',
addRequired: true, addRequired: true,
editRequired: true, editRequired: true,
autocomplete: false autocomplete: false
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
type: 'lookup', type: 'lookup',
@@ -57,14 +56,20 @@ angular.module('UserFormDefinition', [])
sourceField: 'name', sourceField: 'name',
ngClick: 'lookUpOrganization()', ngClick: 'lookUpOrganization()',
excludeMode: 'edit', excludeMode: 'edit',
awRequiredWhen: { variable: "orgrequired", init: true } awRequiredWhen: {
}, variable: "orgrequired",
init: true
}
},
username: { username: {
label: 'Username', label: 'Username',
type: 'text', type: 'text',
awRequiredWhen: { variable: "not_ldap_user", init: true }, awRequiredWhen: {
autocomplete: false variable: "not_ldap_user",
init: true
}, },
autocomplete: false
},
password: { password: {
label: 'Password', label: 'Password',
type: 'password', type: 'password',
@@ -74,7 +79,7 @@ angular.module('UserFormDefinition', [])
ngChange: "clearPWConfirm('password_confirm')", ngChange: "clearPWConfirm('password_confirm')",
autocomplete: false, autocomplete: false,
chkPass: true chkPass: true
}, },
password_confirm: { password_confirm: {
label: 'Confirm Password', label: 'Confirm Password',
type: 'password', type: 'password',
@@ -84,7 +89,7 @@ angular.module('UserFormDefinition', [])
awPassMatch: true, awPassMatch: true,
associated: 'password', associated: 'password',
autocomplete: false autocomplete: false
}, },
is_superuser: { is_superuser: {
label: 'Superuser (User has full system administration privileges.)', label: 'Superuser (User has full system administration privileges.)',
type: 'checkbox', type: 'checkbox',
@@ -92,171 +97,169 @@ angular.module('UserFormDefinition', [])
falseValue: 'false', falseValue: 'false',
"default": 'false', "default": 'false',
ngShow: "current_user['is_superuser'] == true" ngShow: "current_user['is_superuser'] == true"
}, },
ldap_user: { ldap_user: {
label: 'Created by LDAP', label: 'Created by LDAP',
type: 'checkbox', type: 'checkbox',
readonly: true readonly: true
} }
}, },
buttons: { //for now always generates <button> tags buttons: {
save: { save: {
ngClick: 'formSave()', //$scope.function to call on click, optional ngClick: 'formSave()',
ngDisabled: true //Disable when $pristine or $invalid, optional ngDisabled: true
}, },
reset: { reset: {
ngClick: 'formReset()', ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine ngDisabled: true
} }
}, },
related: {
related: { //related colletions (and maybe items?)
credentials: { credentials: {
type: 'collection', type: 'collection',
title: 'Credentials', title: 'Credentials',
iterator: 'credential', iterator: 'credential',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('credentials')", ngClick: "add('credentials')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add a credential for this user' awToolTip: 'Add a credential for this user'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('credentials', \{\{ credential.id \}\}, '\{\{ credential.name \}\}')", ngClick: "edit('credentials', credential.id, credential.name)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit the credential', awToolTip: 'Edit the credential',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('credentials', \{\{ credential.id \}\}, '\{\{ credential.name \}\}', 'credentials')", ngClick: "delete('credentials', credential.id, credential.name, 'credentials')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Delete the credential' awToolTip: 'Delete the credential'
}
} }
}, }
},
permissions: { permissions: {
type: 'collection', type: 'collection',
title: 'Permissions', title: 'Permissions',
iterator: 'permission', iterator: 'permission',
open: false, open: false,
actions: { actions: {
add: { add: {
ngClick: "add('permissions')", ngClick: "add('permissions')",
icon: 'icon-plus', icon: 'icon-plus',
label: 'Add', label: 'Add',
awToolTip: 'Add a permission for this user', awToolTip: 'Add a permission for this user',
ngShow: 'PermissionAddAllowed' ngShow: 'PermissionAddAllowed'
} }
}, },
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name', label: 'Name',
ngClick: "edit('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}')" ngClick: "edit('permissions', permission.id, permission.name)"
}, },
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.inventory.name' ngBind: 'permission.summary_fields.inventory.name'
}, },
project: { project: {
label: 'Project', label: 'Project',
sourceModel: 'project', sourceModel: 'project',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.project.name' ngBind: 'permission.summary_fields.project.name'
}, },
permission_type: { permission_type: {
label: 'Permission' label: 'Permission'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "edit('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}')", ngClick: "edit('permissions', permission.id, permission.name)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit the permission', awToolTip: 'Edit the permission',
'class': 'btn btn-default' 'class': 'btn btn-default'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "delete('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}', 'permissions')", ngClick: "delete('permissions', permission.id, permission.name, 'permissions')",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger', "class": 'btn-danger',
awToolTip: 'Delete the permission', awToolTip: 'Delete the permission',
ngShow: 'PermissionAddAllowed' ngShow: 'PermissionAddAllowed'
}
} }
}
}, },
admin_of_organizations: { // Assumes a plural name (e.g. things) admin_of_organizations: { // Assumes a plural name (e.g. things)
type: 'collection', type: 'collection',
title: 'Admin of Organizations', title: 'Admin of Organizations',
iterator: 'adminof', // Singular form of name (e.g. thing) iterator: 'adminof', // Singular form of name (e.g. thing)
open: false, // Open accordion on load? open: false, // Open accordion on load?
base: '/organizations', base: '/organizations',
actions: { actions: {},
},
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}
} }
}, }
},
organizations: { organizations: {
type: 'collection', type: 'collection',
title: 'Organizations', title: 'Organizations',
iterator: 'organization', iterator: 'organization',
open: false, open: false,
actions: { actions: {},
},
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}
} }
}, }
},
teams: { teams: {
type: 'collection', type: 'collection',
@@ -264,19 +267,18 @@ angular.module('UserFormDefinition', [])
iterator: 'team', iterator: 'team',
open: false, open: false,
actions: { actions: {},
},
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}
} }
}, }
},
projects: { projects: {
type: 'collection', type: 'collection',
@@ -284,21 +286,19 @@ angular.module('UserFormDefinition', [])
iterator: 'project', iterator: 'project',
open: false, open: false,
actions: { actions: {},
},
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}
} }
} }
} }
}); //UserForm }
}); //UserForm

View File

@@ -1,65 +1,86 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* InventoryGroups.js * InventoryGroups.js
* *
* Help object for inventory groups/hosts page. * Help object for inventory groups/hosts page.
* *
* @dict * @dict
*/ */
'use strict';
angular.module('InventoryGroupsHelpDefinition', []) angular.module('InventoryGroupsHelpDefinition', [])
.value( .value('InventoryGroupsHelp', {
'InventoryGroupsHelp', {
story: { story: {
hdr: 'Building your inventory', hdr: 'Building your inventory',
width: 510, width: 510,
height: 560, height: 560,
steps: [ steps: [{
{
intro: 'Start by creating a group:', intro: 'Start by creating a group:',
img: { src: 'groups001.png', maxWidth: 338 , maxHeight: 222 }, img: {
src: 'groups001.png',
maxWidth: 338,
maxHeight: 222
},
box: "Click <i class=\"fa fa-plus\"></i> on the groups list (the left side of the page) to add a new group.", box: "Click <i class=\"fa fa-plus\"></i> on the groups list (the left side of the page) to add a new group.",
autoOffNotice: true autoOffNotice: true
}, }, {
{
intro: 'Enter group properties:', intro: 'Enter group properties:',
img: { src: 'groups002.png', maxWidth: 443, maxHeight: 251 }, img: {
src: 'groups002.png',
maxWidth: 443,
maxHeight: 251
},
box: 'Enter the group name, a description and any inventory variables. Variables can be entered using either JSON or YAML syntax. ' + box: 'Enter the group name, a description and any inventory variables. Variables can be entered using either JSON or YAML syntax. ' +
'For more on inventory variables, see <a href=\"http://docs.ansible.com/intro_inventory.html\" target="_blank"> ' + 'For more on inventory variables, see <a href=\"http://docs.ansible.com/intro_inventory.html\" target="_blank"> ' +
'docs.ansible.com/intro_inventory.html</a>' 'docs.ansible.com/intro_inventory.html</a>'
}, }, {
{
intro: 'Cloud inventory: select cloud source', intro: 'Cloud inventory: select cloud source',
img: { src: 'groups003.png', maxWidth: 412, maxHeight: 215 }, img: {
src: 'groups003.png',
maxWidth: 412,
maxHeight: 215
},
box: "For a cloud inventory, choose the cloud provider from the list and select your credentials. If you have not already setup " + box: "For a cloud inventory, choose the cloud provider from the list and select your credentials. If you have not already setup " +
"credentials for the provider, you will need to do that first on the <a href=\"/#/credentials\" " + "credentials for the provider, you will need to do that first on the <a href=\"/#/credentials\" " +
"target=\"_blank\">Credentials</a> tab." "target=\"_blank\">Credentials</a> tab."
}, }, {
{
intro: 'Cloud inventory: synchronize Tower with the cloud', intro: 'Cloud inventory: synchronize Tower with the cloud',
img: { src: 'groups004.png', maxWidth: 261, maxHeight: 221 }, img: {
src: 'groups004.png',
maxWidth: 261,
maxHeight: 221
},
box: "To pull the cloud inventory into Tower, initiate an inventory sync by clicking <i class=\"fa fa-exchange\"></i>." box: "To pull the cloud inventory into Tower, initiate an inventory sync by clicking <i class=\"fa fa-exchange\"></i>."
}, }, {
{
intro: "Groups can have subgroups:", intro: "Groups can have subgroups:",
img: { src: 'groups005.png', maxWidth: 430, maxHeight: 206 }, img: {
src: 'groups005.png',
maxWidth: 430,
maxHeight: 206
},
box: "<div class=\"text-left\">First, select a group. Then click <i class=\"fa fa-plus\"></i> to create a new group. The new group " + box: "<div class=\"text-left\">First, select a group. Then click <i class=\"fa fa-plus\"></i> to create a new group. The new group " +
"will be added to the selected group.</div>" "will be added to the selected group.</div>"
}, }, {
{
intro: 'Copy or move groups:', intro: 'Copy or move groups:',
img: { src: 'groups006.png', maxWidth: 263, maxHeight: 211 }, img: {
box: "<div class=\"text-left\">Copy or move a group by dragging and dropping its name onto another group name. A dialog will appear " + src: 'groups006.png',
"asking if the group should be coppied or moved.</div>" maxWidth: 263,
maxHeight: 211
}, },
{ box: "<div class=\"text-left\">Copy or move a group by dragging and dropping its name onto another group name. A dialog will appear " +
"asking if the group should be coppied or moved.</div>"
}, {
intro: 'Adding hosts:', intro: 'Adding hosts:',
img: { src: 'groups007.png', maxWidth: 466, maxHeight: 178 }, img: {
box: "<div class=\"text-left\"><p>First, select a Group. " + src: 'groups007.png',
"Then click <i class=\"fa fa-plus\"></i> on the hosts list (the right side of the page) to create a host. " + maxWidth: 466,
"The new host will be part of the selected group.</p><p>Note hosts cannot be added to the All Hosts group.</p></div>" maxHeight: 178
} },
] box: "<div class=\"text-left\"><p>First, select a Group. " +
} "Then click <i class=\"fa fa-plus\"></i> on the hosts list (the right side of the page) to create a host. " +
}); "The new host will be part of the selected group.</p><p>Note hosts cannot be added to the All Hosts group.</p></div>"
}]
}
});

View File

@@ -1,24 +0,0 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventoryHosts.js
* Help object for Inventory-> Hosts page.
*
*
*/
angular.module('InventoryHostsHelpDefinition', [])
.value(
'InventoryHostsHelp', {
story: {
hdr: 'Managing Hosts',
steps: {
step1: {
intro: 'Start by selecting a group:',
img: { src: 'help003.png', maxWidth: 315 , maxHeight: 198 },
box: "On the group selector, click the name of a group. Hosts contained in the group" +
" will appear on the right.",
height: 500
}
}
}
});

View File

@@ -4,102 +4,103 @@
* helpers/Access.js * helpers/Access.js
* *
* Routines for checking user access and license state * Routines for checking user access and license state
* *
*/ */
angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies']) 'use strict';
.factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath','ProcessErrors',
function($rootScope, Alert, Rest, GetBasePath, ProcessErrors) {
return function(params) {
// set PermissionAddAllowed to true or false based on user access. admins and org admins are granted
// accesss.
var me = $rootScope.current_user;
var scope = params.scope;
if (me.is_superuser) { angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
scope.PermissionAddAllowed = true; .factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath', 'ProcessErrors',
} function ($rootScope, Alert, Rest, GetBasePath, ProcessErrors) {
else { return function (params) {
if (me.related.admin_of_organizations) { // set PermissionAddAllowed to true or false based on user access. admins and org admins are granted
Rest.setUrl(me.related.admin_of_organizations); // accesss.
Rest.get() var me = $rootScope.current_user,
.success( function(data, status, headers, config) { scope = params.scope;
if (data.results.length > 0) {
scope.PermissionAddAllowed = true;
}
else {
scope.PermissionAddAllowed = false;
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + me.related.admin_of_organizations +
' failed. DELETE returned status: ' + status });
});
}
}
//if (!access) {
// Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.');
//}
//return access;
}
}])
.factory('CheckLicense', ['$rootScope', '$cookieStore', 'Alert', '$location', 'Authorization',
function($rootScope, $cookieStore, Alert, $location, Authorization) {
return function() {
// Check license status and alert the user, if needed
var status = 'success';
var hdr, msg;
var license = $cookieStore.get('license');
var purchase_msg = '<p>To purchase a license or extend an existing license ' +
'<a href="http://www.ansible.com/ansible-pricing" target="_blank"><strong>visit the Ansible online store</strong></a>, ' +
'or visit <strong><a href="https://support.ansible.com" target="_blank">support.ansible.com</a></strong> for assistance.</p>';
if (license && !Authorization.licenseTested()) { if (me.is_superuser) {
// This is our first time evaluating the license scope.PermissionAddAllowed = true;
license['tested'] = true; } else {
$cookieStore.remove('license'); if (me.related.admin_of_organizations) {
$cookieStore.put('license', license); Rest.setUrl(me.related.admin_of_organizations);
$rootScope.license_tested = true; Rest.get()
if (license['valid_key'] !== undefined && license['valid_key'] == false) { .success(function (data) {
// The license is invalid. Stop the user from logging in. if (data.results.length > 0) {
status = 'alert-danger'; scope.PermissionAddAllowed = true;
hdr = 'License Error'; } else {
msg = '<p>There is a problem with the /etc/awx/license file on your Tower server. Check to make sure Tower can access ' + scope.PermissionAddAllowed = false;
'the file.</p>' + purchase_msg; }
Alert(hdr, msg, status, null, false, true); })
} .error(function (data, status) {
else if (license['demo'] !== undefined && license['demo'] == true) { ProcessErrors(scope, data, status, null, {
// demo hdr: 'Error!',
status = 'alert-info'; msg: 'Call to ' + me.related.admin_of_organizations +
hdr = 'Tower Demo'; ' failed. DELETE returned status: ' + status
msg = '<p>Thank you for trying Ansible Tower. You can use this edition to manage up to 10 hosts free.</p>' + });
purchase_msg; });
Alert(hdr, msg, status); }
} }
if (license['date_expired'] !== undefined && license['date_expired'] == true) { //if (!access) {
// expired // Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.');
status = 'alert-info'; //}
hdr = 'License Expired'; //return access;
msg = '<p>Your Ansible Tower License has expired and is no longer compliant. You can continue, but you will be ' + };
'unable to add any additional hosts.</p>' + purchase_msg;
Alert(hdr, msg, status);
}
else if (license['date_warning'] !== undefined && license['date_warning'] == true) {
status = 'alert-info';
hdr = 'License Warning';
msg = '<p>Your Ansible Tower license is about to expire!</p>' + purchase_msg;
Alert(hdr, msg, status);
}
if (license['free_instances'] !== undefined && parseInt(license['free_instances']) <= 0) {
status = 'alert-info';
hdr = 'License Warning';
msg = '<p>Your Ansible Tower license has reached capacity for the number of managed ' +
'hosts allowed. You will not be able to add any additional hosts.</p>' + purchase_msg;
Alert(hdr, msg, status, null, true);
}
} }
])
.factory('CheckLicense', ['$rootScope', '$cookieStore', 'Alert', '$location', 'Authorization',
function ($rootScope, $cookieStore, Alert, $location, Authorization) {
return function () {
// Check license status and alert the user, if needed
var status = 'success',
hdr, msg,
license = $cookieStore.get('license'),
purchase_msg = '<p>To purchase a license or extend an existing license ' +
'<a href="http://www.ansible.com/ansible-pricing" target="_blank"><strong>visit the Ansible online store</strong></a>, ' +
'or visit <strong><a href="https://support.ansible.com" target="_blank">support.ansible.com</a></strong> for assistance.</p>';
if (license && !Authorization.licenseTested()) {
// This is our first time evaluating the license
license.tested = true;
$cookieStore.remove('license');
$cookieStore.put('license', license);
$rootScope.license_tested = true;
if (license.valid_key !== undefined && license.valid_key === false) {
// The license is invalid. Stop the user from logging in.
status = 'alert-danger';
hdr = 'License Error';
msg = '<p>There is a problem with the /etc/awx/license file on your Tower server. Check to make sure Tower can access ' +
'the file.</p>' + purchase_msg;
Alert(hdr, msg, status, null, false, true);
} else if (license.demo !== undefined && license.demo === true) {
// demo
status = 'alert-info';
hdr = 'Tower Demo';
msg = '<p>Thank you for trying Ansible Tower. You can use this edition to manage up to 10 hosts free.</p>' +
purchase_msg;
Alert(hdr, msg, status);
}
if (license.date_expired !== undefined && license.date_expired === true) {
// expired
status = 'alert-info';
hdr = 'License Expired';
msg = '<p>Your Ansible Tower License has expired and is no longer compliant. You can continue, but you will be ' +
'unable to add any additional hosts.</p>' + purchase_msg;
Alert(hdr, msg, status);
} else if (license.date_warning !== undefined && license.date_warning === true) {
status = 'alert-info';
hdr = 'License Warning';
msg = '<p>Your Ansible Tower license is about to expire!</p>' + purchase_msg;
Alert(hdr, msg, status);
}
if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) {
status = 'alert-info';
hdr = 'License Warning';
msg = '<p>Your Ansible Tower license has reached capacity for the number of managed ' +
'hosts allowed. You will not be able to add any additional hosts.</p>' + purchase_msg;
Alert(hdr, msg, status, null, true);
}
}
};
} }
}]); ]);

View File

@@ -3,78 +3,62 @@
* *
* ChildrenHelper * ChildrenHelper
* *
* Used in job_events to expand/collapse children by setting the * Used in job_events to expand/collapse children by setting the
* 'show' attribute of each job_event in the set of job_events. * 'show' attribute of each job_event in the set of job_events.
* See the filter in job_events.js list. * See the filter in job_events.js list.
* *
*/ */
angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
.factory('ToggleChildren', ['Alert', 'Rest', 'GetBasePath','ProcessErrors','FormatDate',
function(Alert, Rest, GetBasePath, ProcessErrors, FormatDate) {
return function(params) {
var scope = params.scope;
var list = params.list;
var id = params.id;
var set = scope[list.name]; // set is now a pointer to scope[list.name]
function expand(node) {
set[node]['ngicon'] = 'fa fa-minus-square-o node-toggle';
for (var i = node + 1; i < set.length; i++) {
if (set[i].parent == set[node].id) {
set[i]['show'] = true;
//if (set[i].related.children) {
// expand(i);
//}
}
}
}
function collapse(node) { 'use strict';
set[node]['ngicon'] = 'fa fa-plus-square-o node-toggle';
for (var i = node + 1; i < set.length; i++) {
if (set[i].parent == set[node].id) {
set[i]['show'] = false;
if (set[i]['related']['children']) {
collapse(i);
}
}
}
}
// Scan the array list and find the clicked element angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
var clicked; .factory('ToggleChildren', [ function () {
var found = false; return function (params) {
for (var i = 0; i < set.length && found == false; i++){
if (set[i].id == id) { var scope = params.scope,
clicked = i; list = params.list,
found = true; id = params.id,
set = scope[list.name],
i, clicked, found = false;
function expand(node) {
var i;
set[node].ngicon = 'fa fa-minus-square-o node-toggle';
for (i = node + 1; i < set.length; i++) {
if (set[i].parent === set[node].id) {
set[i].show = true;
}
}
} }
}
// Expand or collapse children based on clicked element's icon function collapse(node) {
if (/plus-square-o/.test(set[clicked]['ngicon'])) { var i;
// Expand: lookup and display children set[node].ngicon = 'fa fa-plus-square-o node-toggle';
expand(clicked); for (i = node + 1; i < set.length; i++) {
} if (set[i].parent === set[node].id) {
else if (/minus-square-o/.test(set[clicked]['ngicon'])) { set[i].show = false;
collapse(clicked); if (set[i].related.children) {
} collapse(i);
}
}
}
}
// Scan the array list and find the clicked element
for (i = 0; i < set.length && found === false; i++) {
if (set[i].id === id) {
clicked = i;
found = true;
}
}
// Expand or collapse children based on clicked element's icon
if (/plus-square-o/.test(set[clicked].ngicon)) {
// Expand: lookup and display children
expand(clicked);
} else if (/minus-square-o/.test(set[clicked].ngicon)) {
collapse(clicked);
}
};
} }
}]); ]);

View File

@@ -7,176 +7,185 @@
* *
*/ */
angular.module('CredentialsHelper', ['Utilities']) 'use strict';
.factory('KindChange', [ 'Empty', function(Empty) {
return function(params) {
var scope = params.scope;
var form = params.form;
var reset = params.reset;
// Put things in a default state angular.module('CredentialsHelper', ['Utilities'])
scope['usernameLabel'] = 'Username';
scope['aws_required'] = false; .factory('KindChange', ['Empty',
scope['rackspace_required'] = false; function (Empty) {
scope['sshKeyDataLabel'] = 'SSH Private Key'; return function (params) {
if (!Empty(scope['kind'])) { var scope = params.scope,
// Apply kind specific settings reset = params.reset,
switch(scope['kind'].value) { collapse, id;
// Put things in a default state
scope.usernameLabel = 'Username';
scope.aws_required = false;
scope.rackspace_required = false;
scope.sshKeyDataLabel = 'SSH Private Key';
if (!Empty(scope.kind)) {
// Apply kind specific settings
switch (scope.kind.value) {
case 'aws': case 'aws':
scope['aws_required'] = true; scope.aws_required = true;
break; break;
case 'rax': case 'rax':
scope['rackspace_required'] = true; scope.rackspace_required = true;
break; break;
case 'ssh': case 'ssh':
scope['usernameLabel'] = 'SSH Username'; scope.usernameLabel = 'SSH Username';
break; break;
case 'scm': case 'scm':
scope['sshKeyDataLabel'] = 'SCM Private Key'; scope.sshKeyDataLabel = 'SCM Private Key';
break; break;
}
}
// Reset all the field values related to Kind.
if (reset) {
scope['access_key'] = null;
scope['secret_key'] = null;
scope['api_key'] = null;
scope['username'] = null;
scope['password'] = null;
scope['password_confirm'] = null;
scope['ssh_key_data'] = null;
scope['ssh_key_unlock'] = null;
scope['ssh_key_unlock_confirm'] = null;
scope['sudo_username'] = null;
scope['sudo_password'] = null;
scope['sudo_password_confirm'] = null;
}
// Collapse or open help widget based on whether scm value is selected
var collapse = $('#credential_kind').parent().find('.panel-collapse').first();
var id = collapse.attr('id');
if (!Empty(scope.kind) && scope.kind.value !== '') {
if ( $('#' + id + '-icon').hasClass('icon-minus') ) {
scope.accordionToggle('#' + id);
}
}
else {
if ( $('#' + id + '-icon').hasClass('icon-plus') ) {
scope.accordionToggle('#' + id);
}
}
}
}])
.factory('OwnerChange', [ function() {
return function(params) {
var scope = params.scope;
var owner = scope['owner'];
if (owner == 'team') {
scope['team_required'] = true;
scope['user_required'] = false;
scope['user'] = null;
scope['user_username'] = null;
}
else {
scope['team_required'] = false;
scope['user_required'] = true;
scope['team'] = null;
scope['team_name'] = null;
}
}
}])
.factory('FormSave', ['$location', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait',
function($location, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait) {
return function(params) {
var scope = params.scope;
var mode = params.mode; // add or edit
var form = CredentialForm;
var data = {}
for (var fld in form.fields) {
if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' &&
fld !== 'ssh_password') {
if (scope[fld] === null) {
data[fld] = "";
}
else {
data[fld] = scope[fld];
} }
} }
}
if (!Empty(scope.team)) {
data.team = scope.team;
data.user = "";
}
else {
data.user = scope.user;
data.team = "";
}
data['kind'] = scope['kind'].value; // Reset all the field values related to Kind.
if (reset) {
scope.access_key = null;
scope.secret_key = null;
scope.api_key = null;
scope.username = null;
scope.password = null;
scope.password_confirm = null;
scope.ssh_key_data = null;
scope.ssh_key_unlock = null;
scope.ssh_key_unlock_confirm = null;
scope.sudo_username = null;
scope.sudo_password = null;
scope.sudo_password_confirm = null;
}
switch (data['kind']) { // Collapse or open help widget based on whether scm value is selected
case 'ssh': collapse = $('#credential_kind').parent().find('.panel-collapse').first();
data['password'] = scope['ssh_password']; id = collapse.attr('id');
break; if (!Empty(scope.kind) && scope.kind.value !== '') {
if ($('#' + id + '-icon').hasClass('icon-minus')) {
scope.accordionToggle('#' + id);
}
} else {
if ($('#' + id + '-icon').hasClass('icon-plus')) {
scope.accordionToggle('#' + id);
}
}
};
}
])
.factory('OwnerChange', [
function () {
return function (params) {
var scope = params.scope,
owner = scope.owner;
if (owner === 'team') {
scope.team_required = true;
scope.user_required = false;
scope.user = null;
scope.user_username = null;
} else {
scope.team_required = false;
scope.user_required = true;
scope.team = null;
scope.team_name = null;
}
};
}
])
.factory('FormSave', ['$location', 'Alert', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait',
function ($location, Alert, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait) {
return function (params) {
var scope = params.scope,
mode = params.mode,
form = CredentialForm,
data = {}, fld, url;
for (fld in form.fields) {
if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' &&
fld !== 'ssh_password') {
if (scope[fld] === null) {
data[fld] = "";
} else {
data[fld] = scope[fld];
}
}
}
if (!Empty(scope.team)) {
data.team = scope.team;
data.user = "";
} else {
data.user = scope.user;
data.team = "";
}
data.kind = scope.kind.value;
switch (data.kind) {
case 'ssh':
data.password = scope.ssh_password;
break;
case 'aws': case 'aws':
data['username'] = scope['access_key']; data.username = scope.access_key;
data['password'] = scope['secret_key']; data.password = scope.secret_key;
break; break;
case 'rax': case 'rax':
data['password'] = scope['api_key']; data.password = scope.api_key;
break; break;
}
if (Empty(data.team) && Empty(data.user)) {
Alert('Missing User or Team', 'You must provide either a User or a Team. If this credential will only be accessed by a specific ' +
'user, select a User. To allow a team of users to access this credential, select a Team.', 'alert-danger');
}
else {
Wait('start');
if (mode == 'add') {
var url = (!Empty(data.team)) ? GetBasePath('teams') + data.team + '/credentials/' :
GetBasePath('users') + data.user + '/credentials/';
Rest.setUrl(url);
Rest.post(data)
.success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1);
})
.error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to create new Credential. POST status: ' + status });
});
} }
else {
var url = GetBasePath('credentials') + scope.id + '/';
Rest.setUrl(url);
Rest.put(data)
.success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1);
})
.error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update Credential. PUT status: ' + status });
});
}
}
}
}]);
if (Empty(data.team) && Empty(data.user)) {
Alert('Missing User or Team', 'You must provide either a User or a Team. If this credential will only be accessed by a specific ' +
'user, select a User. To allow a team of users to access this credential, select a Team.', 'alert-danger');
} else {
Wait('start');
if (mode === 'add') {
url = (!Empty(data.team)) ? GetBasePath('teams') + data.team + '/credentials/' :
GetBasePath('users') + data.user + '/credentials/';
Rest.setUrl(url);
Rest.post(data)
.success(function () {
Wait('stop');
var base = $location.path().replace(/^\//, '').split('/')[0];
if (base === 'credentials') {
ReturnToCaller();
}
ReturnToCaller(1);
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to create new Credential. POST status: ' + status
});
});
} else {
url = GetBasePath('credentials') + scope.id + '/';
Rest.setUrl(url);
Rest.put(data)
.success(function () {
Wait('stop');
var base = $location.path().replace(/^\//, '').split('/')[0];
if (base === 'credentials') {
ReturnToCaller();
}
ReturnToCaller(1);
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to update Credential. PUT status: ' + status
});
});
}
}
};
}
]);

View File

@@ -6,194 +6,57 @@
* EventView - show the job_events form in a modal dialog * EventView - show the job_events form in a modal dialog
* *
*/ */
angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefinition'])
.factory('EventView', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm', 'use strict';
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'JobEventDataForm', 'Empty',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
FormatDate, JobEventDataForm, Empty) {
return function(params) {
// We're going to manipulate the form object each time the user clicks on View button. We can't rely on what's
// left of the form in memory each time. Instead we have to define the form from scratch, so for now we're
// keeping it here inline rather than a separate file.
//
// Form manipulation is done to remove any empty values. In order for a section (or accordion) to not be drawn,
// it needs to be removed prior to call jqueryui. Otherwise, attempting to hide accordion pieces after the
// the accordion is rendered creates undesired behavior.
var form = {
name: 'job_events',
well: false,
forceListeners: true,
fields: {
status: {
labelClass: 'job-\{\{ status \}\}',
type: 'custom',
section: 'Event',
control: '<div class=\"job-event-status job-\{\{ status \}\}\"><i class=\"fa icon-job-{{ status }}"></i> \{\{ status \}\}</div>'
},
id: {
label: 'ID',
type: 'text',
readonly: true,
section: 'Event',
'class': 'span1'
},
created: {
label: 'Created On',
type: 'text',
section: 'Event',
readonly: true
},
host: {
label: 'Host',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "host !== ''"
},
play: {
label: 'Play',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "play !== ''"
},
task: {
label: 'Task',
type: 'text',
readonly: true,
section: 'Event',
ngShow: "task !== ''"
},
rc: {
label: 'Return Code',
type: 'text',
readonly: true,
section: 'Results',
'class': 'span1',
ngShow: "rc !== ''"
},
msg: {
label: 'Msg',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "msg !== ''",
rows: 10
},
stdout: {
label: 'Std Out',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "stdout !== ''",
rows: 10
},
stderr: {
label: 'Std Err',
type: 'textarea',
readonly: true,
section: 'Results',
'class': 'nowrap',
ngShow: "stderr !== ''",
rows: 10
},
results: {
label: 'Results',
type: 'textarea',
section: 'Results',
readonly: true,
'class': 'nowrap',
ngShow: "results !== ''",
rows: 10
},
start: {
label: 'Start',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "start !== ''"
},
traceback: {
label: false,
type: 'textarea',
readonly: true,
section: 'Traceback',
'class': 'nowrap',
ngShow: "traceback !== ''",
rows: 10
},
end: {
label: 'End',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "end !== ''"
},
delta: {
label: 'Elapsed',
type: 'text',
readonly: true,
section: 'Timing',
ngShow: "delta !== ''"
},
module_name: {
label: 'Name',
type: 'text',
readonly: true,
section: 'Module',
ngShow: "module_name !== ''"
},
module_args: {
label: 'Args',
type: 'text',
readonly: true,
section: 'Module',
ngShow: "module_args !== ''"
}
}
};
var event_id = params.event_id; angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefinition', 'JobEventsFormDefinition'])
var generator = GenerateForm;
var scope; .factory('EventView', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
var defaultUrl = GetBasePath('base') + 'job_events/' + event_id + '/'; 'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'JobEventDataForm', 'Empty', 'JobEventsForm',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
// Retrieve detail record and prepopulate the form FormatDate, JobEventDataForm, Empty, JobEventsForm) {
Rest.setUrl(defaultUrl); return function (params) {
Rest.get()
.success( function(data, status, headers, config) { var event_id = params.event_id,
generator = GenerateForm,
// If event_data is not available, remove fields that depend on it form = angular.copy(JobEventsForm),
if ($.isEmptyObject(data['event_data']) || !data['event_data']['res'] || typeof data['event_data']['res'] == 'string') { scope,
for (var fld in form.fields) { defaultUrl = GetBasePath('base') + 'job_events/' + event_id + '/';
switch(fld) {
case 'start': // Retrieve detail record and prepopulate the form
case 'end': Rest.setUrl(defaultUrl);
case 'delta': Rest.get()
case 'msg': .success(function (data) {
case 'stdout': var i, n, fld, rows, txt, cDate;
case 'stderr':
case 'msg': // If event_data is not available, remove fields that depend on it
case 'results': if ($.isEmptyObject(data.event_data) || !data.event_data.res || typeof data.event_data.res === 'string') {
case 'module_name': for (fld in form.fields) {
case 'module_args': switch (fld) {
case 'rc': case 'start':
delete form.fields[fld]; case 'end':
break; case 'delta':
} case 'msg':
} case 'stdout':
} case 'stderr':
case 'msg':
if ($.isEmptyObject(data['event_data']) || !data['event_data']['res'] || typeof data['event_data']['res'] != 'string') { case 'results':
delete form.fields['traceback']; case 'module_name':
} case 'module_args':
case 'rc':
// Remove remaining form fields that do not have a corresponding data value delete form.fields[fld];
for (var fld in form.fields) { break;
switch (fld) { }
}
}
if ($.isEmptyObject(data.event_data) || !data.event_data.res || typeof data.event_data.res !== 'string') {
delete form.fields.traceback;
}
// Remove remaining form fields that do not have a corresponding data value
for (fld in form.fields) {
switch (fld) {
case 'start': case 'start':
case 'end': case 'end':
case 'delta': case 'delta':
@@ -202,114 +65,118 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefini
case 'stderr': case 'stderr':
case 'msg': case 'msg':
case 'rc': case 'rc':
if (data['event_data'] && data['event_data']['res'] && Empty(data['event_data']['res'][fld])) { if (data.event_data && data.event_data.res && Empty(data.event_data.res[fld])) {
delete form.fields[fld]; delete form.fields[fld];
} } else {
else { if (form.fields[fld].type === 'textarea') {
if (form.fields[fld].type == 'textarea') { n = data.event_data.res[fld].match(/\n/g);
var n = data['event_data']['res'][fld].match(/\n/g); rows = (n) ? n.length : 1;
var rows = (n) ? n.length : 1; rows = (rows > 10) ? 10 : rows;
rows = (rows > 10) ? 10 : rows; rows = (rows < 3) ? 3 : rows;
rows = (rows < 3) ? 3 : rows; form.fields[fld].rows = rows;
form.fields[fld].rows = rows; }
}
} }
break; break;
case 'results': case 'results':
if ( data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] == undefined) { if (data.event_data && data.event_data.res && data.event_data.res[fld] === undefined) {
// not defined // not defined
delete form.fields[fld]; delete form.fields[fld];
} } else if (!Array.isArray(data.event_data.res[fld]) || data.event_data.res[fld].length === 0) {
else if (!Array.isArray(data['event_data']['res'][fld]) || data['event_data']['res'][fld].length == 0) { // defined, but empty
// defined, but empty delete form.fields[fld];
delete form.fields[fld]; } else {
} // defined and not empty, so attempt to size the textarea field
else { txt = '';
// defined and not empty, so attempt to size the textarea field for (i = 0; i < data.event_data.res[fld].length; i++) {
var txt = ''; txt += data.event_data.res[fld][i];
for (var i=0; i < data['event_data']['res'][fld].length; i++) { }
txt += data['event_data']['res'][fld][i]; if (txt === '') {
} // there's an array, but the actual text is empty
if (txt == '') { delete form.fields[fld];
// there's an array, but the actual text is empty } else {
delete form.fields[fld]; n = txt.match(/\n/g);
} rows = (n) ? n.length : 1;
else { rows = (rows > 10) ? 10 : rows;
var n = txt.match(/\n/g); rows = (rows < 3) ? 3 : rows;
var rows = (n) ? n.length : 1; form.fields[fld].rows = rows;
rows = (rows > 10) ? 10 : rows; }
rows = (rows < 3) ? 3 : rows;
form.fields[fld].rows = rows;
}
} }
break; break;
case 'module_name': case 'module_name':
case 'module_args': case 'module_args':
if (data['event_data'] && data['event_data']['res']) { if (data.event_data && data.event_data.res) {
if (data['event_data']['res']['invocation'] === undefined || if (data.event_data.res.invocation === undefined ||
data['event_data']['res']['invocation'][fld] === undefined) { data.event_data.res.invocation[fld] === undefined) {
delete form.fields[fld]; delete form.fields[fld];
} }
} }
break; break;
}
}
// load the form
scope = generator.inject(form, { mode: 'edit', modal: true, related: false});
generator.reset();
scope.formModalAction = function() {
$('#form-modal').modal("hide");
}
scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false;
scope.formModalInfo = 'View JSON';
$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
$('#form-modal').addClass('skinny-modal');
scope.formModalHeader = data['event_display'].replace(/^\u00a0*/g,'');
// Respond to View JSON button
scope.formModalInfoAction = function() {
var generator = GenerateForm;
var scope = generator.inject(JobEventDataForm, { mode: 'edit', modal: true, related: false,
modal_selector: '#form-modal2', modal_body_id: 'form-modal2-body', modal_title_id: 'formModal2Header' });
generator.reset();
scope.formModal2Header = data['event_display'].replace(/^\u00a0*/g,'');
scope.event_data = JSON.stringify(data['event_data'], null, '\t');
scope.formModal2ActionLabel = 'OK';
scope.formModal2CancelShow = false;
scope.formModal2Info = false;
scope.formModalInfo = 'View JSON';
scope.formModal2Action = function() {
$('#form-modal2').modal("hide");
} }
$('#form-modal2 .btn-success').removeClass('btn-success').addClass('btn-none');
} }
if (typeof data['event_data']['res'] == 'string') { // load the form
scope['traceback'] = data['event_data']['res']; scope = generator.inject(form, {
} mode: 'edit',
modal: true,
for (var fld in form.fields) { related: false
switch(fld) { });
generator.reset();
scope.formModalAction = function () {
$('#form-modal').modal("hide");
};
scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false;
scope.formModalInfo = 'View JSON';
$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
$('#form-modal').addClass('skinny-modal');
scope.formModalHeader = data.event_display.replace(/^\u00a0*/g, '');
// Respond to View JSON button
scope.formModalInfoAction = function () {
var generator = GenerateForm,
scope = generator.inject(JobEventDataForm, {
mode: 'edit',
modal: true,
related: false,
modal_selector: '#form-modal2',
modal_body_id: 'form-modal2-body',
modal_title_id: 'formModal2Header'
});
generator.reset();
scope.formModal2Header = data.event_display.replace(/^\u00a0*/g, '');
scope.event_data = JSON.stringify(data.event_data, null, '\t');
scope.formModal2ActionLabel = 'OK';
scope.formModal2CancelShow = false;
scope.formModal2Info = false;
scope.formModalInfo = 'View JSON';
scope.formModal2Action = function () {
$('#form-modal2').modal("hide");
};
$('#form-modal2 .btn-success').removeClass('btn-success').addClass('btn-none');
};
if (typeof data.event_data.res === 'string') {
scope.traceback = data.event_data.res;
}
for (fld in form.fields) {
switch (fld) {
case 'status': case 'status':
if (data['failed']) { if (data.failed) {
scope['status'] = 'error'; scope.status = 'error';
} } else if (data.changed) {
else if (data['changed']) { scope.status = 'changed';
scope['status'] = 'changed'; } else {
} scope.status = 'success';
else {
scope['status'] = 'success';
} }
break; break;
case 'created': case 'created':
var cDate = new Date(data['created']); cDate = new Date(data.created);
scope['created'] = FormatDate(cDate); scope.created = FormatDate(cDate);
break; break;
case 'host': case 'host':
if (data['summary_fields'] && data['summary_fields']['host']) { if (data.summary_fields && data.summary_fields.host) {
scope['host'] = data['summary_fields']['host']['name']; scope.host = data.summary_fields.host.name;
} }
break; break;
case 'id': case 'id':
@@ -319,50 +186,50 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefini
break; break;
case 'start': case 'start':
case 'end': case 'end':
if (data['event_data'] && data['event_data']['res'] && !Empty(data['event_data']['res'][fld])) { if (data.event_data && data.event_data.res && !Empty(data.event_data.res[fld])) {
scope[fld] = data['event_data']['res'][fld]; scope[fld] = data.event_data.res[fld];
} }
break; break;
case 'results': case 'results':
if (Array.isArray(data['event_data']['res'][fld]) && data['event_data']['res'][fld].length > 0 ) { if (Array.isArray(data.event_data.res[fld]) && data.event_data.res[fld].length > 0) {
var txt = ''; txt = '';
for (var i=0; i < data['event_data']['res'][fld].length; i++) { for (i = 0; i < data.event_data.res[fld].length; i++) {
txt += data['event_data']['res'][fld][i]; txt += data.event_data.res[fld][i];
} }
if (txt !== '') { if (txt !== '') {
scope[fld] = txt; scope[fld] = txt;
} }
} }
break; break;
case 'msg': case 'msg':
case 'stdout': case 'stdout':
case 'stderr': case 'stderr':
case 'delta': case 'delta':
case 'rc': case 'rc':
if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) { if (data.event_data && data.event_data.res && data.event_data.res[fld] !== undefined) {
scope[fld] = data['event_data']['res'][fld]; scope[fld] = data.event_data.res[fld];
} }
break; break;
case 'module_name': case 'module_name':
case 'module_args': case 'module_args':
if (data['event_data']['res'] && data['event_data']['res']['invocation']) { if (data.event_data.res && data.event_data.res.invocation) {
scope[fld] = data['event_data']['res']['invocation'][fld]; scope[fld] = data.event_data.res.invocation[fld];
} }
break; break;
}
}
if (!scope.$$phase) {
scope.$digest();
} }
}
if (!scope.$$phase) {
scope.$digest();
}
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
$('#form-modal').modal("hide"); $('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Failed to retrieve event: ' + event_id + '. GET status: ' + status }); msg: 'Failed to retrieve event: ' + event_id + '. GET status: ' + status });
}); });
};
} }
}]); ]);

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@
/* jshint loopfunc: true */ /* jshint loopfunc: true */
'use strict';
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition', angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper', 'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',

View File

@@ -2,479 +2,463 @@
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* JobSubmission.js * JobSubmission.js
* *
*/ */
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper' ])
.factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors', 'use strict';
'GetBasePath', 'Alert', 'Empty', 'Wait',
function(CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, Wait) {
return function(params) {
var scope = params.scope;
var passwords = params.passwords;
var start_url = params.start_url;
var form = params.form;
var html = '';
var field, element, dialogScope, fld;
var base = $location.path().replace(/^\//,'').split('/')[0];
var extra_html = params.extra_html;
function navigate(canceled) { angular.module('JobSubmissionHelper', ['RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
//Decide where to send the user once the modal dialog closes 'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper'
if (!canceled) { ])
if (base == 'jobs') {
scope.refreshJob();
}
else {
$location.path('/jobs');
}
}
else {
$location.path('/' + base);
}
}
function cancel() { .factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors',
// Delete a job 'GetBasePath', 'Alert', 'Empty', 'Wait',
var url = GetBasePath('jobs') + scope.job_id +'/' function (CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, Wait) {
Rest.setUrl(url); return function (params) {
Rest.destroy()
.success ( function(data, status, headers, config) { var scope = params.scope,
if (form.name == 'credential') { passwords = params.passwords,
navigate(true); start_url = params.start_url,
form = params.form,
html = '',
field, element, fld, i, current_form,
base = $location.path().replace(/^\//, '').split('/')[0],
extra_html = params.extra_html;
function navigate(canceled) {
//Decide where to send the user once the modal dialog closes
if (!canceled) {
if (base === 'jobs') {
scope.refreshJob();
} else {
$location.path('/jobs');
} }
} else {
$location.path('/' + base);
}
}
function cancel() {
// Delete a job
var url = GetBasePath('jobs') + scope.job_id + '/';
Rest.setUrl(url);
Rest.destroy()
.success(function () {
if (form.name === 'credential') {
navigate(true);
}
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
}); });
} }
scope.cancelJob = function() { scope.cancelJob = function () {
// User clicked cancel button // User clicked cancel button
$('#password-modal').modal('hide'); $('#password-modal').modal('hide');
if (form.name == 'credential') { if (form.name === 'credential') {
cancel(); cancel();
} } else {
else { scope.$emit('UpdateSubmitted', 'canceled');
scope.$emit('UpdateSubmitted','canceled');
}
}
scope.startJob = function() {
$('#password-modal').modal('hide');
Wait('start');
var pswd = {};
var value_supplied = false;
$('.password-field').each(function(index) {
pswd[$(this).attr('name')] = $(this).val();
if ($(this).val() != '' && $(this).val() !== null) {
value_supplied = true;
} }
}); };
if (Empty(passwords) || passwords.length == 0 || value_supplied) {
Rest.setUrl(start_url);
Rest.post(pswd)
.success( function(data, status, headers, config) {
scope.$emit('UpdateSubmitted','started');
if (form.name == 'credential') {
navigate(false);
}
})
.error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'POST to ' + start_url + ' failed with status: ' + status });
});
}
else {
Wait('stop');
Alert('No Passwords', 'Required password(s) not provided. The request was not submitted.', 'alert-info');
if (form.name == 'credential') {
// No passwords provided, so we can't start the job. Rather than leave the job in a 'new'
// state, let's delete it.
cancelJob();
}
}
}
if (passwords && passwords.length > 0) {
Wait('stop');
// Prompt for passwords
html += "<form class=\"form-horizontal\" name=\"password_form\" novalidate>\n";
html += (extra_html) ? extra_html : "";
var current_form;
for (var i=0; i < passwords.length; i++) {
// Add the password field
if (form.name == 'credential') {
// this is a job. we could be prompting for inventory and/or SCM passwords
if (form.fields[passwords[i]]) {
current_form = form;
}
/*
else if (ProjectsForm.fields[passwords[i]]) {
current_form = ProjectsForm;
}
else if (GroupForm.fields[passwords[i]]) {
current_form = GroupForm;
}
*/
else {
// No match found. Abandon ship!
Alert('Form Not Found', 'Could not locate form for: ' + passwords[i], 'alert-danger');
$location('/#/jobs');
}
}
else {
current_form = form;
}
field = current_form.fields[passwords[i]];
fld = passwords[i];
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"" + fld + "\">* ";
html += (field.labelBind) ? scope[field.labelBind] : field.label;
html += "</label>\n";
html += "<div class=\"col-lg-9\">\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"password-field form-control\" ";
html += "required ";
html += "/>";
html += "<br />\n";
// Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
// Add the related confirm field
fld = field.associated;
field = current_form.fields[field.associated];
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"" + fld + "\">* ";
html += (field.labelBind) ? scope[field.labelBind] : field.label;
html += "</label>\n";
html += "<div class=\"col-lg-9\">\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control\" ";
html += "required ";
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
html += "/>";
html += "<br />\n";
// Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
if (field.awPassMatch) {
html += "<span class=\"error\" ng-show=\"password_form." + fld +
".$error.awpassmatch\">Must match Password value</span>\n";
}
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
}
html += "</form>\n";
var element = angular.element(document.getElementById('password-body'));
element.html(html);
$compile(element.contents())(scope);
$('#password-modal').modal();
$('#password-modal').on('shown.bs.modal', function() {
$('#password-body').find('input[type="password"]:first').focus();
});
}
else {
scope.startJob();
}
}
}])
.factory('SubmitJob',['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'CredentialList',
'LookUpInit', 'CredentialForm', 'ProcessErrors', 'JobTemplateForm', 'Wait',
function(PromptPasswords, $compile, Rest, $location, GetBasePath, CredentialList, LookUpInit, CredentialForm,
ProcessErrors, JobTemplateForm, Wait) {
return function(params) {
var scope = params.scope;
var id = params.id;
var template_name = (params.template) ? params.template : null;
var base = $location.path().replace(/^\//,'').split('/')[0];
var url = GetBasePath(base) + id + '/';
function postJob(data) { scope.startJob = function () {
// Create the job record var pswd = {}, value_supplied = false;
if (scope.credentialWatchRemove) { $('#password-modal').modal('hide');
scope.credentialWatchRemove(); Wait('start');
$('.password-field').each(function () {
pswd[$(this).attr('name')] = $(this).val();
if ($(this).val() !== '' && $(this).val() !== null) {
value_supplied = true;
}
});
if (Empty(passwords) || passwords.length === 0 || value_supplied) {
Rest.setUrl(start_url);
Rest.post(pswd)
.success(function () {
scope.$emit('UpdateSubmitted', 'started');
if (form.name === 'credential') {
navigate(false);
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'POST to ' + start_url + ' failed with status: ' + status });
});
} else {
Wait('stop');
Alert('No Passwords', 'Required password(s) not provided. The request was not submitted.', 'alert-info');
if (form.name === 'credential') {
// No passwords provided, so we can't start the job. Rather than leave the job in a 'new'
// state, let's delete it.
scope.cancelJob();
}
}
};
if (passwords && passwords.length > 0) {
Wait('stop');
// Prompt for passwords
html += "<form class=\"form-horizontal\" name=\"password_form\" novalidate>\n";
html += (extra_html) ? extra_html : "";
for (i = 0; i < passwords.length; i++) {
// Add the password field
if (form.name === 'credential') {
// this is a job. we could be prompting for inventory and/or SCM passwords
if (form.fields[passwords[i]]) {
current_form = form;
}
else {
// No match found. Abandon ship!
Alert('Form Not Found', 'Could not locate form for: ' + passwords[i], 'alert-danger');
$location('/#/jobs');
}
} else {
current_form = form;
}
field = current_form.fields[passwords[i]];
fld = passwords[i];
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"" + fld + "\">* ";
html += (field.labelBind) ? scope[field.labelBind] : field.label;
html += "</label>\n";
html += "<div class=\"col-lg-9\">\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"password-field form-control\" ";
html += "required ";
html += "/>";
html += "<br />\n";
// Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
// Add the related confirm field
fld = field.associated;
field = current_form.fields[field.associated];
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"" + fld + "\">* ";
html += (field.labelBind) ? scope[field.labelBind] : field.label;
html += "</label>\n";
html += "<div class=\"col-lg-9\">\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control\" ";
html += "required ";
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
html += "/>";
html += "<br />\n";
// Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
if (field.awPassMatch) {
html += "<span class=\"error\" ng-show=\"password_form." + fld +
".$error.awpassmatch\">Must match Password value</span>\n";
}
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
}
html += "</form>\n";
element = angular.element(document.getElementById('password-body'));
element.html(html);
$compile(element.contents())(scope);
$('#password-modal').modal();
$('#password-modal').on('shown.bs.modal', function () {
$('#password-body').find('input[type="password"]:first').focus();
});
} else {
scope.startJob();
} }
var dt = new Date().toISOString(); };
var url = (data.related.jobs) ? data.related.jobs : data.related.job_template + 'jobs/'; }
var name = (template_name) ? template_name : data.name; ])
Wait('start');
Rest.setUrl(url); .factory('SubmitJob', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'CredentialList',
Rest.post({ 'LookUpInit', 'CredentialForm', 'ProcessErrors', 'JobTemplateForm', 'Wait',
name: name + ' ' + dt, // job name required and unique function (PromptPasswords, $compile, Rest, $location, GetBasePath, CredentialList, LookUpInit, CredentialForm,
description: data.description, ProcessErrors, JobTemplateForm, Wait) {
job_template: data.id, return function (params) {
inventory: data.inventory, var scope = params.scope,
project: data.project, id = params.id,
playbook: data.playbook, template_name = (params.template) ? params.template : null,
credential: data.credential, base = $location.path().replace(/^\//, '').split('/')[0],
forks: data.forks, url = GetBasePath(base) + id + '/';
limit: data.limit,
verbosity: data.verbosity, function postJob(data) {
extra_vars: data.extra_vars var dt, url, name;
}) // Create the job record
.success( function(data, status, headers, config) { if (scope.credentialWatchRemove) {
scope.credentialWatchRemove();
}
dt = new Date().toISOString();
url = (data.related.jobs) ? data.related.jobs : data.related.job_template + 'jobs/';
name = (template_name) ? template_name : data.name;
Wait('start');
Rest.setUrl(url);
Rest.post({
name: name + ' ' + dt, // job name required and unique
description: data.description,
job_template: data.id,
inventory: data.inventory,
project: data.project,
playbook: data.playbook,
credential: data.credential,
forks: data.forks,
limit: data.limit,
verbosity: data.verbosity,
extra_vars: data.extra_vars
}).success(function (data) {
scope.job_id = data.id; scope.job_id = data.id;
if (data.passwords_needed_to_start.length > 0) { if (data.passwords_needed_to_start.length > 0) {
// Passwords needed. Prompt for passwords, then start job. // Passwords needed. Prompt for passwords, then start job.
PromptPasswords({ PromptPasswords({
scope: scope, scope: scope,
passwords: data.passwords_needed_to_start, passwords: data.passwords_needed_to_start,
start_url: data.related.start, start_url: data.related.start,
form: CredentialForm form: CredentialForm
}); });
} else {
// No passwords needed, start the job!
Rest.setUrl(data.related.start);
Rest.post()
.success(function () {
Wait('stop');
var base = $location.path().replace(/^\//, '').split('/')[0];
if (base === 'jobs') {
scope.refresh();
} else {
$location.path('/jobs');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to start job. POST returned status: ' + status });
});
} }
else { }).error(function (data, status) {
// No passwords needed, start the job!
Rest.setUrl(data.related.start);
Rest.post()
.success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0];
if (base == 'jobs') {
scope.refresh();
}
else {
$location.path('/jobs');
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to start job. POST returned status: ' + status });
});
}
})
.error( function(data, status, headers, config) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Failed to create job. POST returned status: ' + status }); msg: 'Failed to create job. POST returned status: ' + status });
}); });
}; }
// Get the job or job_template record // Get the job or job_template record
Wait('start'); Wait('start');
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success(function (data) {
// Create a job record // Create a job record
scope.credential = ''; scope.credential = '';
if (data.credential == '' || data.credential == null) { if (data.credential === '' || data.credential === null) {
// Template does not have credential, prompt for one // Template does not have credential, prompt for one
Wait('stop'); Wait('stop');
if (scope.credentialWatchRemove) { if (scope.credentialWatchRemove) {
scope.credentialWatchRemove(); scope.credentialWatchRemove();
} }
scope.credentialWatchRemove = scope.$watch('credential', function(newVal, oldVal) { scope.credentialWatchRemove = scope.$watch('credential', function (newVal, oldVal) {
if (newVal !== oldVal) { if (newVal !== oldVal) {
// After user selects a credential from the modal, // After user selects a credential from the modal,
// submit the job // submit the job
if (scope.credential != '' && scope.credential !== null && scope.credential !== undefined) { if (scope.credential !== '' && scope.credential !== null && scope.credential !== undefined) {
data.credential = scope.credential; data.credential = scope.credential;
postJob(data); postJob(data);
} }
} }
}); });
LookUpInit({ LookUpInit({
scope: scope, scope: scope,
form: JobTemplateForm, form: JobTemplateForm,
current_item: null, current_item: null,
list: CredentialList, list: CredentialList,
field: 'credential', field: 'credential',
hdr: 'Credential Required' hdr: 'Credential Required'
}); });
scope.lookUpCredential(); scope.lookUpCredential();
} } else {
else { // We have what we need, submit the job
// We have what we need, submit the job postJob(data);
postJob(data); }
} })
}) .error(function (data, status) {
.error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, { hdr: 'Error!',
ProcessErrors(scope, data, status, null, msg: 'Failed to get job template details. GET returned status: ' + status });
{ hdr: 'Error!', msg: 'Failed to get job template details. GET returned status: ' + status }); });
}); };
}; }
}]) ])
// Sumbit SCM Update request // Sumbit SCM Update request
.factory('ProjectUpdate',['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert', .factory('ProjectUpdate', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'ProjectsForm', 'Wait', 'ProjectsForm', 'Wait',
function(PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, ProjectsForm, Wait) { function (PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, ProjectsForm, Wait) {
return function(params) { return function (params) {
var scope = params.scope; var scope = params.scope,
var project_id = params.project_id; project_id = params.project_id,
var url = GetBasePath('projects') + project_id + '/update/'; url = GetBasePath('projects') + project_id + '/update/';
if (scope.removeUpdateSubmitted) { if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted(); scope.removeUpdateSubmitted();
} }
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function () {
// Refresh the project list after update request submitted // Refresh the project list after update request submitted
Wait('stop');
Alert('Update Started', 'The request to start the SCM update process was submitted. ' +
'To monitor the update status, refresh the page by clicking the <em>Refresh</em> button.', 'alert-info');
scope.refresh();
});
if (scope.removeSCMSubmit) {
scope.removeSCMSubmit();
}
scope.removeSCMSubmit = scope.$on('SCMSubmit', function(e, passwords_needed_to_update, extra_html) {
// After the call to update, kick off the job.
PromptPasswords({
scope: scope,
passwords: passwords_needed_to_update,
start_url: url,
form: ProjectsForm,
extra_html: extra_html
});
});
// Check to see if we have permission to perform the update and if any passwords are needed
Wait('start');
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
Wait('stop'); Wait('stop');
if (data.can_update) { Alert('Update Started', 'The request to start the SCM update process was submitted. ' +
var extra_html = ''; 'To monitor the update status, refresh the page by clicking the <em>Refresh</em> button.', 'alert-info');
for (var i=0; i < scope.projects.length; i++) { scope.refresh();
if (scope.projects[i].id == project_id) {
extra_html += "<div class=\"form-group\">\n";
extra_html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"scm_url\">SCM URL</label>\n";
extra_html += "<div class=\"col-lg-9\">\n";
extra_html += "<input type=\"text\" readonly";
extra_html += ' name=\"scm_url\" ';
extra_html += "class=\"form-control\" ";
extra_html += "value=\"" + scope.projects[i].scm_url + "\" ";
extra_html += "/>";
extra_html += "</div>\n";
extra_html += "</div>\n";
if (scope.projects[i].scm_username) {
extra_html += "<div class=\"form-group\">\n";
extra_html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"scm_username\">SCM Username</label>\n";
extra_html += "<div class=\"col-lg-9\">\n";
extra_html += "<input type=\"text\" readonly";
extra_html += ' name=\"scm_username\" ';
extra_html += "class=\"form-control\" ";
extra_html += "value=\"" + scope.projects[i].scm_username + "\" ";
extra_html += "/>";
extra_html += "</div>\n";
extra_html += "</div>\n";
}
break;
}
}
extra_html += "</p>";
scope.$emit('SCMSubmit', data.passwords_needed_to_update, extra_html);
}
else {
Alert('Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
'alert-danger');
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get project update details: ' + url + ' GET status: ' + status });
}); });
};
}]) if (scope.removeSCMSubmit) {
scope.removeSCMSubmit();
}
scope.removeSCMSubmit = scope.$on('SCMSubmit', function (e, passwords_needed_to_update, extra_html) {
// After the call to update, kick off the job.
PromptPasswords({
scope: scope,
passwords: passwords_needed_to_update,
start_url: url,
form: ProjectsForm,
extra_html: extra_html
});
});
// Check to see if we have permission to perform the update and if any passwords are needed
Wait('start');
Rest.setUrl(url);
Rest.get()
.success(function (data) {
var i, extra_html;
Wait('stop');
if (data.can_update) {
extra_html = '';
for (i = 0; i < scope.projects.length; i++) {
if (scope.projects[i].id === project_id) {
extra_html += "<div class=\"form-group\">\n";
extra_html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"scm_url\">SCM URL</label>\n";
extra_html += "<div class=\"col-lg-9\">\n";
extra_html += "<input type=\"text\" readonly";
extra_html += ' name=\"scm_url\" ';
extra_html += "class=\"form-control\" ";
extra_html += "value=\"" + scope.projects[i].scm_url + "\" ";
extra_html += "/>";
extra_html += "</div>\n";
extra_html += "</div>\n";
if (scope.projects[i].scm_username) {
extra_html += "<div class=\"form-group\">\n";
extra_html += "<label class=\"control-label col-lg-3 normal-weight\" for=\"scm_username\">SCM Username</label>\n";
extra_html += "<div class=\"col-lg-9\">\n";
extra_html += "<input type=\"text\" readonly";
extra_html += ' name=\"scm_username\" ';
extra_html += "class=\"form-control\" ";
extra_html += "value=\"" + scope.projects[i].scm_username + "\" ";
extra_html += "/>";
extra_html += "</div>\n";
extra_html += "</div>\n";
}
break;
}
}
extra_html += "</p>";
scope.$emit('SCMSubmit', data.passwords_needed_to_update, extra_html);
} else {
Alert('Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
'alert-danger');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get project update details: ' + url + ' GET status: ' + status
});
});
};
}
])
// Sumbit Inventory Update request // Sumbit Inventory Update request
.factory('InventoryUpdate',['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert', .factory('InventoryUpdate', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'GroupForm', 'BuildTree', 'Wait', 'GroupForm', 'BuildTree', 'Wait',
function(PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, GroupForm, BuildTree, Wait) { function (PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, GroupForm, BuildTree, Wait) {
return function(params) { return function (params) {
var scope = params.scope; var scope = params.scope,
var inventory_id = params.inventory_id; url = params.url,
var url = params.url; group_id = params.group_id,
var group_name = params.group_name; tree_id = params.tree_id;
var group_source = params.group_source;
var group_id = params.group_id;
var tree_id = params.tree_id;
if (scope.removeHostReloadComplete) {
scope.removeHostReloadComplete();
}
scope.removeHostReloadComplete = scope.$on('HostReloadComplete', function(e) {
Wait('stop');
Alert('Update Started', 'Your request to start the inventory sync process was submitted. Monitor progress ' +
'by clicking the <i class="fa fa-refresh fa-lg"></i> button.', 'alert-info');
if (scope.removeHostReloadComplete) { if (scope.removeHostReloadComplete) {
scope.removeHostReloadComplete(); scope.removeHostReloadComplete();
} }
}); scope.removeHostReloadComplete = scope.$on('HostReloadComplete', function () {
if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted();
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
if (action == 'started') {
if (scope.refreshGroups) {
scope.selected_tree_id = tree_id;
scope.selected_group_id = group_id;
scope.refreshGroups();
}
else if (scope.refresh) {
scope.refresh();
}
scope.$emit('HostReloadComplete');
}
});
if (scope.removeInventorySubmit) {
scope.removeInventorySubmit();
}
scope.removeInventorySubmit = scope.$on('InventorySubmit', function(e, passwords_needed_to_update, extra_html) {
// After the call to update, kick off the job.
PromptPasswords({
scope: scope,
passwords: passwords_needed_to_update,
start_url: url,
form: GroupForm,
extra_html: extra_html
});
});
// Check to see if we have permission to perform the update and if any passwords are needed
Wait('start');
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
if (data.can_update) {
//var extra_html = "<div class=\"inventory-passwd-msg\">Starting inventory update for <em>" + group_name +
// "</em>. Please provide the " + group_source + " credentials:</div>\n";
scope.$emit('InventorySubmit', data.passwords_needed_to_update);
}
else {
Wait('stop');
Alert('Permission Denied', 'You do not have access to run the update. Please contact your system administrator.',
'alert-danger');
}
})
.error( function(data, status, headers, config) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, Alert('Update Started', 'Your request to start the inventory sync process was submitted. Monitor progress ' +
{ hdr: 'Error!', msg: 'Failed to get inventory_source details. ' + url + 'GET status: ' + status }); 'by clicking the <i class="fa fa-refresh fa-lg"></i> button.', 'alert-info');
if (scope.removeHostReloadComplete) {
scope.removeHostReloadComplete();
}
}); });
};
}]);
if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted();
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function (e, action) {
if (action === 'started') {
if (scope.refreshGroups) {
scope.selected_tree_id = tree_id;
scope.selected_group_id = group_id;
scope.refreshGroups();
} else if (scope.refresh) {
scope.refresh();
}
scope.$emit('HostReloadComplete');
}
});
if (scope.removeInventorySubmit) {
scope.removeInventorySubmit();
}
scope.removeInventorySubmit = scope.$on('InventorySubmit', function (e, passwords_needed_to_update, extra_html) {
// After the call to update, kick off the job.
PromptPasswords({
scope: scope,
passwords: passwords_needed_to_update,
start_url: url,
form: GroupForm,
extra_html: extra_html
});
});
// Check to see if we have permission to perform the update and if any passwords are needed
Wait('start');
Rest.setUrl(url);
Rest.get()
.success(function (data) {
if (data.can_update) {
//var extra_html = "<div class=\"inventory-passwd-msg\">Starting inventory update for <em>" + group_name +
// "</em>. Please provide the " + group_source + " credentials:</div>\n";
scope.$emit('InventorySubmit', data.passwords_needed_to_update);
} else {
Wait('stop');
Alert('Permission Denied', 'You do not have access to run the update. Please contact your system administrator.',
'alert-danger');
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get inventory_source details. ' + url + 'GET status: ' + status });
});
};
}
]);

View File

@@ -4,15 +4,18 @@
* JobsHelper * JobsHelper
* *
* Routines shared by job related controllers * Routines shared by job related controllers
* *
*/ */
'use strict';
angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper']) angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper'])
.factory('JobStatusToolTip', [ function() { .factory('JobStatusToolTip', [
return function(status) { function () {
var toolTip; return function (status) {
switch (status) { var toolTip;
switch (status) {
case 'successful': case 'successful':
case 'success': case 'success':
toolTip = 'There were no failed tasks.'; toolTip = 'There were no failed tasks.';
@@ -35,118 +38,124 @@ angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinitio
case 'running': case 'running':
toolTip = 'Playbook tasks executing.'; toolTip = 'Playbook tasks executing.';
break; break;
} }
return toolTip; return toolTip;
}; };
}]) }
])
.factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary', .factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary',
'WatchInventoryWindowResize', 'WatchInventoryWindowResize',
function(Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary, WatchInventoryWindowResize) { function (Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary, WatchInventoryWindowResize) {
return function(params) { return function (params) {
// Display status info in a modal dialog- called from inventory edit page // Display status info in a modal dialog- called from inventory edit page
var job_id = params.job_id;
var generator = GenerateForm; var job_id = params.job_id,
var form = JobSummary; generator = GenerateForm,
form = JobSummary,
// Using jquery dialog for its expandable property scope, ww, wh, x, y, maxrows, url, html;
var html = '<div id=\"status-modal-dialog\" title=\"Job ' + job_id + '\">' + html = '<div id=\"status-modal-dialog\" title=\"Job ' + job_id + '\">' +
'<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n'; '<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n';
$('#inventory-modal-container').empty().append(html); $('#inventory-modal-container').empty().append(html);
var scope = generator.inject(form, { mode: 'edit', id: 'form-container', breadCrumbs: false, related: false });
scope = generator.inject(form, { mode: 'edit', id: 'form-container', breadCrumbs: false, related: false });
// Set modal dimensions based on viewport width
var ww = $(document).width(); // Set modal dimensions based on viewport width
var wh = $('body').height(); ww = $(document).width();
var x, y, maxrows; wh = $('body').height();
if (ww > 1199) { if (ww > 1199) {
// desktop // desktop
x = 675; x = 675;
y = (750 > wh) ? wh - 20 : 750; y = (750 > wh) ? wh - 20 : 750;
maxrows = 20; maxrows = 20;
} } else if (ww <= 1199 && ww >= 768) {
else if (ww <= 1199 && ww >= 768) { x = 550;
x = 550; y = (620 > wh) ? wh - 15 : 620;
y = (620 > wh) ? wh - 15 : 620; maxrows = 15;
maxrows = 15; } else {
} x = (ww - 20);
else { y = (500 > wh) ? wh : 500;
x = (ww - 20); maxrows = 10;
y = (500 > wh) ? wh : 500;
maxrows = 10;
}
// Create the modal
$('#status-modal-dialog').dialog({
buttons: { 'OK': function() { $( this ).dialog( 'close' ); } },
modal: true,
width: x,
height: y,
autoOpen: false,
create: function () {
// fix the close button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button')
.empty().attr({ 'class': 'close' }).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
.attr({ 'class': 'btn btn-primary' });
},
resizeStop: function() {
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]');
var content = dialog.find('#status-modal-dialog');
content.width( dialog.width() - 28 );
},
close: 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();
});
$('#status-modal-dialog').dialog('destroy');
$('#inventory-modal-container').empty();
WatchInventoryWindowResize();
},
open: function() {
Wait('stop');
}
});
function calcRows (content) {
var n = content.match(/\n/g);
var rows = (n) ? n.length : 1;
return (rows > maxrows) ? 20 : rows;
} }
Wait('start'); // Create the modal
var url = GetBasePath('jobs') + job_id + '/'; $('#status-modal-dialog').dialog({
Rest.setUrl(url); buttons: {
Rest.get() 'OK': function () {
.success( function(data) { $(this).dialog('close');
scope.id = data.id; }
scope.name = data.name; },
scope.status = data.status; modal: true,
scope.result_stdout = data.result_stdout; width: x,
scope.result_traceback = data.result_traceback; height: y,
scope.stdout_rows = calcRows(scope.result_stdout); autoOpen: false,
scope.traceback_rows = calcRows(scope.result_traceback); create: function () {
var cDate = new Date(data.created); // fix the close button
scope.created = FormatDate(cDate); $('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button')
$('#status-modal-dialog').dialog('open'); .empty().attr({
'class': 'close'
}).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
.attr({
'class': 'btn btn-primary'
});
},
resizeStop: function () {
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'),
content = dialog.find('#status-modal-dialog');
content.width(dialog.width() - 28);
},
close: 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();
});
$('#status-modal-dialog').dialog('destroy');
$('#inventory-modal-container').empty();
WatchInventoryWindowResize();
},
open: function () {
Wait('stop');
}
});
function calcRows(content) {
var n = content.match(/\n/g),
rows = (n) ? n.length : 1;
return (rows > maxrows) ? 20 : rows;
}
Wait('start');
url = GetBasePath('jobs') + job_id + '/';
Rest.setUrl(url);
Rest.get()
.success(function (data) {
var cDate;
scope.id = data.id;
scope.name = data.name;
scope.status = data.status;
scope.result_stdout = data.result_stdout;
scope.result_traceback = data.result_traceback;
scope.stdout_rows = calcRows(scope.result_stdout);
scope.traceback_rows = calcRows(scope.result_traceback);
cDate = new Date(data.created);
scope.created = FormatDate(cDate);
$('#status-modal-dialog').dialog('open');
}) })
.error( function(data, status) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Attempt to load job failed. GET returned status: ' + status }); msg: 'Attempt to load job failed. GET returned status: ' + status });
}); });
}; };
}]); }
]);

View File

@@ -3,7 +3,7 @@
* *
* LookupHelper * LookupHelper
* Build a lookup dialog * Build a lookup dialog
* *
* LookUpInit( { * LookUpInit( {
* scope: <form scope>, * scope: <form scope>,
* form: <form object>, * form: <form object>,
@@ -14,140 +14,150 @@
* }) * })
*/ */
angular.module('LookUpHelper', [ 'RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'ApiLoader' ]) 'use strict';
angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'ApiLoader'])
.factory('LookUpInit', ['Alert', 'Rest', 'GenerateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', .factory('LookUpInit', ['Alert', 'Rest', 'GenerateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty',
function(Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) { function (Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) {
return function(params) { return function (params) {
var scope = params.scope; // form scope
var form = params.form; // form object
var list = params.list; // list object
var field = params.field; // form field
var postAction = params.postAction //action to perform post user selection
var defaultUrl; var scope = params.scope,
if (params.url) { form = params.form,
// pass in a url value to override the default list = params.list,
defaultUrl = params.url; field = params.field,
} postAction = params.postAction,
else { defaultUrl, name, hdr, watchUrl;
defaultUrl = (list.name == 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name);
}
// Show pop-up
var name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);
var hdr = (params.hdr) ? params.hdr : 'Select ' + name;
var watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&'; if (params.url) {
watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value'; // pass in a url value to override the default
defaultUrl = params.url;
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl); } else {
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source',field); defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name);
scope['lookUp' + name] = function() {
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: hdr });
$('#lookup-modal').on('hidden.bs.modal', function() {
// If user clicks cancel without making a selection, make sure that field values are
// in synch.
if (listScope.searchCleanup) {
listScope.searchCleanup();
}
if (scope[field] == '' || scope[field] == null) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = '';
if (!scope.$$phase) {
scope.$digest();
}
}
});
listScope.selectAction = function() {
var found = false;
var name;
for (var i=0; i < listScope[list.name].length; i++) {
if (listScope[list.name][i]['checked'] == '1') {
found = true;
scope[field] = listScope[list.name][i].id;
if (scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
listScope[list.name][i][form.fields[field].sourceField];
if (scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]
.$setValidity('awlookup',true);
}
}
if (scope[form.name + '_form']) {
scope[form.name + '_form'].$setDirty();
}
listGenerator.hide();
}
}
if (found == false) {
Alert('Missing Selection', 'Oops, you failed to make a selection. Click on a row to make your selection, ' +
'and then click the Select button.');
}
else {
if (postAction) {
postAction();
}
}
} }
listScope['toggle_' + list.iterator] = function(id) { // Show pop-up
for (var i=0; i < scope[list.name].length; i++) { name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);
if (listScope[list.name][i]['id'] == id) { hdr = (params.hdr) ? params.hdr : 'Select ' + name;
listScope[list.name][i]['checked'] = '1';
listScope[list.name][i]['success_class'] = 'success'; watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&';
} watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value';
else {
listScope[list.name][i]['checked'] = '0'; $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
listScope[list.name][i]['success_class'] = ''; $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);
}
} scope['lookUp' + name] = function () {
} var listGenerator = GenerateList,
SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl }); listScope = listGenerator.inject(list, { mode: 'lookup', hdr: hdr });
PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' });
$('#lookup-modal').on('hidden.bs.modal', function () {
// If user made a selection previously, mark it as selected when modal loads // If user clicks cancel without making a selection, make sure that field values are
if (listScope.lookupPostRefreshRemove) { // in synch.
listScope.lookupPostRefreshRemove(); if (listScope.searchCleanup) {
} listScope.searchCleanup();
listScope.lookupPostRefreshRemove = scope.$on('PostRefresh', function() {
for (var fld in list.fields) {
if (list.fields[fld].type && list.fields[fld].type == 'date') {
//convert dates to our standard format
for (var i=0; i < scope[list.name].length; i++) {
scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld]));
}
}
}
// List generator creates the form, resetting it and losing the previously selected value.
// Put it back based on the value of sourceModel_sourceName
if (scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== '' &&
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== null) {
for (var i=0; i < listScope[list.name].length; i++) {
if (listScope[list.name][i][form.fields[field].sourceField] ==
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
scope[field] = listScope[list.name][i].id;
//scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
// listScope[list.name][i][form.fields[field].sourceField];
break;
} }
if (scope[field] === '' || scope[field] === null) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = '';
if (!scope.$$phase) {
scope.$digest();
}
}
});
listScope.selectAction = function () {
var found = false, i;
for (i = 0; i < listScope[list.name].length; i++) {
if (listScope[list.name][i].checked === '1') {
found = true;
scope[field] = listScope[list.name][i].id;
if (scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
listScope[list.name][i][form.fields[field].sourceField];
if (scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]
.$setValidity('awlookup', true);
}
}
if (scope[form.name + '_form']) {
scope[form.name + '_form'].$setDirty();
}
listGenerator.hide();
}
}
if (found === false) {
Alert('Missing Selection', 'Oops, you failed to make a selection. Click on a row to make your selection, ' +
'and then click the Select button.');
} else {
if (postAction) {
postAction();
}
}
};
listScope['toggle_' + list.iterator] = function (id) {
var i;
for (i = 0; i < scope[list.name].length; i++) {
if (listScope[list.name][i].id === id) {
listScope[list.name][i].checked = '1';
listScope[list.name][i].success_class = 'success';
} else {
listScope[list.name][i].checked = '0';
listScope[list.name][i].success_class = '';
}
}
};
SearchInit({
scope: listScope,
set: list.name,
list: list,
url: defaultUrl
});
PaginateInit({
scope: listScope,
list: list,
url: defaultUrl,
mode: 'lookup'
});
// If user made a selection previously, mark it as selected when modal loads
if (listScope.lookupPostRefreshRemove) {
listScope.lookupPostRefreshRemove();
} }
listScope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () {
} var fld, i;
for (fld in list.fields) {
if (!Empty(scope[field])) { if (list.fields[fld].type && list.fields[fld].type === 'date') {
listScope['toggle_' + list.iterator](scope[field]); //convert dates to our standard format
} for (i = 0; i < scope[list.name].length; i++) {
scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld]));
}
}
}
// List generator creates the form, resetting it and losing the previously selected value.
// Put it back based on the value of sourceModel_sourceName
if (scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== '' &&
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== null) {
for (i = 0; i < listScope[list.name].length; i++) {
if (listScope[list.name][i][form.fields[field].sourceField] ===
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
scope[field] = listScope[list.name][i].id;
//scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
// listScope[list.name][i][form.fields[field].sourceField];
break;
}
}
}); }
listScope.search(list.iterator); if (!Empty(scope[field])) {
listScope['toggle_' + list.iterator](scope[field]);
} }
});
listScope.search(list.iterator);
};
};
} }
}]); ]);

View File

@@ -5,165 +5,155 @@
* *
*/ */
angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelatedHelper']) 'use strict';
.factory('PageRangeSetup', ['Empty', function(Empty) { angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelatedHelper'])
return function(params) {
var scope = params.scope;
var count = params.count;
var next = params.next;
var previous = params.previous;
var iterator = params.iterator;
scope[iterator + '_page'] = 1; .factory('PageRangeSetup', ['Empty',
scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size'])); function (Empty) {
scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages']; return function (params) {
scope[iterator + '_total_rows'] = count;
var scope = params.scope,
count = params.count,
next = params.next,
previous = params.previous,
iterator = params.iterator,
i, first, last;
// Which page are we on?
if ( Empty(next) && previous ) {
// no next page, but there is a previous page
scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/,'')) + 1;
}
else if ( next && Empty(previous) ) {
// next page available, but no previous page
scope[iterator + '_page'] = 1; scope[iterator + '_page'] = 1;
} scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size']));
else if ( next && previous ) { scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages'];
// we're in between next and previous scope[iterator + '_total_rows'] = count;
scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/,'')) + 1;
}
// Calc the range of up to 10 pages to show // Which page are we on?
scope[iterator + '_page_range'] = new Array(); if (Empty(next) && previous) {
var first = (scope[iterator + '_page'] > 5) ? scope[iterator + '_page'] - 5 : 1; // no next page, but there is a previous page
if (scope[iterator + '_page'] < 6) { scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/, '')) + 1;
var last = (10 <= scope[iterator + '_num_pages']) ? 10 : scope[iterator + '_num_pages']; } else if (next && Empty(previous)) {
} // next page available, but no previous page
else { scope[iterator + '_page'] = 1;
var last = (scope[iterator + '_page'] + 4 < scope[iterator + '_num_pages']) ? } else if (next && previous) {
scope[iterator + '_page'] + 4 : scope[iterator + '_num_pages']; // we're in between next and previous
} scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/, '')) + 1;
for (var i=first; i <= last; i++) {
scope[iterator + '_page_range'].push(i);
}
}
}])
.factory('RelatedPaginateInit', [ 'RefreshRelated', '$cookieStore', 'Wait',
function(RefreshRelated, $cookieStore, Wait) {
return function(params) {
var scope = params.scope;
var relatedSets = params.relatedSets;
var pageSize = (params.pageSize) ? params.pageSize : 10;
for (var key in relatedSets){
cookieSize = $cookieStore.get(relatedSets[key].iterator + '_page_size');
scope[relatedSets[key].iterator + '_url'] = relatedSets[key].url;
if (cookieSize) {
// use the size found in session cookie, when available
scope[relatedSets[key].iterator + '_page_size'] = cookieSize;
}
else {
scope[relatedSets[key].iterator + '_page'] = 0;
scope[relatedSets[key].iterator + '_page_size'] = pageSize;
}
}
scope.getPage = function(page, set, iterator) {
var new_url = scope[iterator + '_url'].replace(/.page\=\d+/,'');
var connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += connect + 'page=' + page;
new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] +
'&page_size=' + scope[iterator + '_page_size' ] : 'page_size=' + scope[iterator + 'PageSize' ];
Wait('start');
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: new_url });
} }
scope.pageIsActive = function(page, iterator) { // Calc the range of up to 10 pages to show
return (page == scope[iterator + '_page']) ? 'active' : ''; scope[iterator + '_page_range'] = [];
first = (scope[iterator + '_page'] > 5) ? scope[iterator + '_page'] - 5 : 1;
if (scope[iterator + '_page'] < 6) {
last = (10 <= scope[iterator + '_num_pages']) ? 10 : scope[iterator + '_num_pages'];
} else {
last = (scope[iterator + '_page'] + 4 < scope[iterator + '_num_pages']) ?
scope[iterator + '_page'] + 4 : scope[iterator + '_num_pages'];
}
for (i = first; i <= last; i++) {
scope[iterator + '_page_range'].push(i);
} }
scope.changePageSize = function(set, iterator) { };
// Called when a new page size is selected }
])
.factory('RelatedPaginateInit', ['RefreshRelated', '$cookieStore', 'Wait',
function (RefreshRelated, $cookieStore, Wait) {
return function (params) {
var scope = params.scope,
relatedSets = params.relatedSets,
pageSize = (params.pageSize) ? params.pageSize : 10,
key;
for (key in relatedSets) {
scope[relatedSets[key].iterator + '_url'] = relatedSets[key].url;
scope[relatedSets[key].iterator + '_page'] = 0;
scope[relatedSets[key].iterator + '_page_size'] = pageSize;
}
scope.getPage = function (page, set, iterator) {
var new_url = scope[iterator + '_url'].replace(/.page\=\d+/, ''),
connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += connect + 'page=' + page;
new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] +
'&page_size=' + scope[iterator + '_page_size'] : 'page_size=' + scope[iterator + 'PageSize'];
Wait('start');
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: new_url });
};
scope.pageIsActive = function (page, iterator) {
return (page === scope[iterator + '_page']) ? 'active' : '';
};
scope.changePageSize = function (set, iterator) {
// Called when a new page size is selected
scope[iterator + '_page'] = 1;
var url = scope[iterator + '_url'];
// Using the session cookie, keep track of user rows per page selection
$cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']);
url = url.replace(/\/\?.*$/, '/');
url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size'] :
'?page_size=' + scope[iterator + '_page_size'];
RefreshRelated({
scope: scope,
set: set,
iterator: iterator,
url: url
});
};
};
}
])
.factory('PaginateInit', ['Refresh', '$cookieStore', 'Wait',
function (Refresh, $cookieStore, Wait) {
return function (params) {
var scope = params.scope,
list = params.list,
iterator = (params.iterator) ? params.iterator : list.iterator,
mode = (params.mode) ? params.mode : null;
scope[iterator + '_page'] = (params.page) ? params.page : 1;
scope[iterator + '_url'] = params.url;
scope[iterator + '_mode'] = mode;
if (params.pageSize) {
scope[iterator + '_page_size'] = params.pageSize;
} else if (mode === 'lookup') {
scope[iterator + '_page_size'] = 5;
} else {
scope[iterator + '_page_size'] = 20;
}
scope[iterator + '_page'] = 1; scope.getPage = function (page, set, iterator) {
var url = scope[iterator + '_url']; var new_url = scope[iterator + '_url'].replace(/.page\=\d+/, ''),
connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += connect + 'page=' + page;
new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] +
'&page_size=' + scope[iterator + '_page_size'] : 'page_size=' + scope[iterator + 'PageSize'];
Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
};
// Using the session cookie, keep track of user rows per page selection scope.pageIsActive = function (page, iterator) {
$cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']); return (page === scope[iterator + '_page']) ? 'active' : '';
};
url = url.replace(/\/\?.*$/,'/');
url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size' ] :
'?page_size=' + scope[iterator + '_page_size' ];
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
}
} scope.changePageSize = function (set, iterator) {
}]) // Called whenever a new page size is selected
// Using the session cookie, keep track of user rows per page selection
scope[iterator + '_page'] = 0;
.factory('PaginateInit', [ 'Refresh', '$cookieStore', 'Wait', var new_url = scope[iterator + '_url'].replace(/\?page_size\=\d+/, ''),
function(Refresh, $cookieStore, Wait) { connect = (/\/$/.test(new_url)) ? '?' : '&';
return function(params) { new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size'] :
connect + 'page_size=' + scope[iterator + '_page_size'];
var scope = params.scope; Wait('start');
var list = params.list; Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
var iterator = (params.iterator) ? params.iterator : list.iterator; };
var mode = (params.mode) ? params.mode : null; };
var cookieSize = $cookieStore.get(iterator + '_page_size'); }
]);
scope[iterator + '_page'] = (params.page) ? params.page : 1;
scope[iterator + '_url'] = params.url;
scope[iterator + '_mode'] = mode;
// Set the default page size
if (cookieSize && mode != 'lookup') {
// use the size found in session cookie, when available
scope[iterator + '_page_size'] = cookieSize;
}
else {
if (params.pageSize) {
scope[iterator + '_page_size'] = params.pageSize;
}
else if (mode == 'lookup') {
scope[iterator + '_page_size'] = 5;
}
else {
scope[iterator + '_page_size'] = 20;
}
}
scope.getPage = function(page, set, iterator) {
var new_url = scope[iterator + '_url'].replace(/.page\=\d+/,'');
var connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += connect + 'page=' + page;
new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] +
'&page_size=' + scope[iterator + '_page_size' ] : 'page_size=' + scope[iterator + 'PageSize' ];
Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
}
scope.pageIsActive = function(page, iterator) {
return (page == scope[iterator + '_page']) ? 'active' : '';
}
scope.changePageSize = function(set, iterator) {
// Called whenever a new page size is selected
// Using the session cookie, keep track of user rows per page selection
$cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']);
scope[iterator + '_page'] = 0;
var new_url = scope[iterator + '_url'].replace(/\?page_size\=\d+/,'');
var connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size' ] :
connect + 'page_size=' + scope[iterator + '_page_size' ];
Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
}
}
}]);

View File

@@ -3,61 +3,61 @@
* *
* ParseHelper * ParseHelper
* *
* Routines for parsing variable data and toggling * Routines for parsing variable data and toggling
* between JSON and YAML. * between JSON and YAML.
* *
*/ */
angular.module('ParseHelper', []) 'use strict';
.factory('ParseTypeChange', [function() {
return function(scope, varName, parseTypeName) {
// Toggle displayed variable string between JSON and YAML angular.module('ParseHelper', [])
.factory('ParseTypeChange', [
function () {
return function (scope, varName, parseTypeName) {
var fld = (varName) ? varName : 'variables'; // Toggle displayed variable string between JSON and YAML
var pfld = (parseTypeName) ? parseTypeName : 'parseType';
scope.blockParseTypeWatch = false;
scope.blockVariableDataWatch = false;
if (scope['remove' + fld + 'Watch']) { var fld = (varName) ? varName : 'variables',
scope['remove' + fld + 'Watch'](); pfld = (parseTypeName) ? parseTypeName : 'parseType';
scope.blockParseTypeWatch = false;
scope.blockVariableDataWatch = false;
if (scope['remove' + fld + 'Watch']) {
scope['remove' + fld + 'Watch']();
}
scope['remove' + fld + 'Watch'] = scope.$watch(pfld, function (newVal, oldVal) {
var json_obj;
if (newVal !== oldVal) {
if (newVal === 'json') {
if (scope[fld] && !/^---$/.test(scope[fld])) {
// convert YAML to JSON
try {
json_obj = jsyaml.load(scope[fld]); //parse yaml into an obj
scope[fld] = JSON.stringify(json_obj, null, " ");
} catch (err) {
// ignore parse errors. allow the user to paste values in and sync the
// radio button later. parse errors will be flagged on save.
}
} else {
scope[fld] = "{}";
}
} else {
if (scope[fld] && !/^\{\}$/.test(scope[fld])) {
// convert JSON to YAML
try {
json_obj = JSON.parse(scope[fld]);
scope[fld] = jsyaml.safeDump(json_obj);
} catch (err) {
// ignore the errors. allow the user to paste values in and sync the
// radio button later. parse errors will be flagged on save.
}
} else {
scope[fld] = "---";
}
}
}
});
};
} }
scope['remove' + fld + 'Watch'] = scope.$watch(pfld, function(newVal, oldVal) { ]);
if (newVal !== oldVal) {
if (newVal == 'json') {
if ( scope[fld] && !/^---$/.test(scope[fld])) {
// convert YAML to JSON
try {
var json_obj = jsyaml.load(scope[fld]); //parse yaml into an obj
scope[fld] = JSON.stringify(json_obj, null, " ");
}
catch (err) {
// ignore parse errors. allow the user to paste values in and sync the
// radio button later. parse errors will be flagged on save.
}
}
else {
scope[fld] = "\{\}";
}
}
else {
if ( scope[fld] && !/^\{\}$/.test(scope[fld]) ) {
// convert JSON to YAML
try {
var json_obj = JSON.parse(scope[fld]);
scope[fld] = jsyaml.safeDump(json_obj);
}
catch (err) {
// ignore the errors. allow the user to paste values in and sync the
// radio button later. parse errors will be flagged on save.
}
}
else {
scope[fld] = "---";
}
}
}
});
}
}]);

View File

@@ -6,43 +6,42 @@
* Functions shared amongst Permission related controllers * Functions shared amongst Permission related controllers
* *
*/ */
angular.module('PermissionsHelper', []) angular.module('PermissionsHelper', [])
// Handle category change event
.factory('PermissionCategoryChange', [ function() {
return function(params) {
var scope = params.scope;
var reset = params.reset;
if (scope.category == 'Inventory') { // Handle category change event
scope.projectrequired = false; .factory('PermissionCategoryChange', [
scope.permissionTypeHelp = function () {
"<dl>\n" + return function (params) {
"<dt>Read</dt>\n" + var scope = params.scope,
"<dd>Only allow the user or team to view the inventory.</dd>\n" + reset = params.reset;
"<dt>Write</dt>\n" +
"<dd>Allow the user or team to modify hosts and groups contained in the inventory, add new hosts and groups, and perform inventory sync operations.\n" +
"<dt>Admin</dt>\n" +
"<dd>Allow the user or team full access to the inventory. This includes reading, writing, deletion of the inventory and inventory sync operations.</dd>\n" +
"</dl>\n";
}
else {
scope.projectrequired = true;
scope.permissionTypeHelp =
"<dl>\n" +
"<dt>Run</dt>\n" +
"<dd>Allow the user or team to perform a live deployment of the project against the inventory. In Run mode modules will " +
"be executed, and changes to the inventory will occur.</dd>\n" +
"<dt>Check</dt>\n" +
"<dd>Only allow the user or team to deploy the project against the inventory as a dry-run operation. In Check mode, module operations " +
"will only be simulated. No changes will occur.</dd>\n" +
"</dl>\n";
}
if (reset) { if (scope.category === 'Inventory') {
scope.permission_type = (scope.category == 'Inventory') ? 'read' : 'run'; //default to the first option scope.projectrequired = false;
} scope.permissionTypeHelp =
"<dl>\n" +
} "<dt>Read</dt>\n" +
}]); "<dd>Only allow the user or team to view the inventory.</dd>\n" +
"<dt>Write</dt>\n" +
"<dd>Allow the user or team to modify hosts and groups contained in the inventory, add new hosts and groups, and perform inventory sync operations.\n" +
"<dt>Admin</dt>\n" +
"<dd>Allow the user or team full access to the inventory. This includes reading, writing, deletion of the inventory and inventory sync operations.</dd>\n" +
"</dl>\n";
} else {
scope.projectrequired = true;
scope.permissionTypeHelp =
"<dl>\n" +
"<dt>Run</dt>\n" +
"<dd>Allow the user or team to perform a live deployment of the project against the inventory. In Run mode modules will " +
"be executed, and changes to the inventory will occur.</dd>\n" +
"<dt>Check</dt>\n" +
"<dd>Only allow the user or team to deploy the project against the inventory as a dry-run operation. In Check mode, module operations " +
"will only be simulated. No changes will occur.</dd>\n" +
"</dl>\n";
}
if (reset) {
scope.permission_type = (scope.category === 'Inventory') ? 'read' : 'run'; //default to the first option
}
};
}
]);

View File

@@ -3,73 +3,79 @@
* *
* ProjectPathHelper * ProjectPathHelper
* *
* Use GetProjectPath({ scope: <scope>, master: <master obj> }) to * Use GetProjectPath({ scope: <scope>, master: <master obj> }) to
* load scope.project_local_paths (array of options for drop-down) and * load scope.project_local_paths (array of options for drop-down) and
* scope.base_dir (readonly field). * scope.base_dir (readonly field).
* *
*/ */
angular.module('ProjectPathHelper', ['RestServices', 'Utilities'])
.factory('GetProjectPath', ['Alert', 'Rest', 'GetBasePath', 'ProcessErrors',
function (Alert, Rest, GetBasePath, ProcessErrors) {
return function (params) {
angular.module('ProjectPathHelper', ['RestServices', 'Utilities']) var scope = params.scope,
.factory('GetProjectPath', ['Alert', 'Rest', 'GetBasePath','ProcessErrors', master = params.master;
function(Alert, Rest, GetBasePath, ProcessErrors) {
return function(params) { function arraySort(data) {
//Sort nodes by name
var scope = params.scope; var i, j, names = [],
var master = params.master; newData = [];
for (i = 0; i < data.length; i++) {
function arraySort(data) { names.push(data[i].value);
//Sort nodes by name
var names = [];
var newData = [];
for (var i=0; i < data.length; i++) {
names.push(data[i].value);
}
names.sort();
for (var j=0; j < names.length; j++) {
for (i=0; i < data.length; i++) {
if (data[i].value == names[j]) {
newData.push(data[i]);
} }
} names.sort();
} for (j = 0; j < names.length; j++) {
return newData; for (i = 0; i < data.length; i++) {
} if (data[i].value === names[j]) {
newData.push(data[i]);
scope.showMissingPlaybooksAlert = false; }
}
Rest.setUrl( GetBasePath('config') );
Rest.get()
.success( function(data, status, headers, config) {
var opts = [];
for (var i=0; i < data.project_local_paths.length; i++) {
opts.push({ label: data.project_local_paths[i], value: data.project_local_paths[i] });
}
if (scope.local_path) {
// List only includes paths not assigned to projects, so add the
// path assigned to the current project.
opts.push({ label: scope.local_path, value: scope.local_path });
}
scope.project_local_paths = arraySort(opts);
if (scope.local_path) {
for (var i=0; scope.project_local_paths.length; i++) {
if (scope.project_local_paths[i].value == scope.local_path) {
scope.local_path = scope.project_local_paths[i];
break;
}
} }
return newData;
} }
scope.base_dir = data.project_base_dir;
master.local_path = scope.local_path; scope.showMissingPlaybooksAlert = false;
master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get
// wiped out on form reset. Rest.setUrl(GetBasePath('config'));
if (opts.length == 0) { Rest.get()
// trigger display of alert block when scm_type == manual .success(function (data) {
scope.showMissingPlaybooksAlert = true; var opts = [], i;
} for (i = 0; i < data.project_local_paths.length; i++) {
}) opts.push({
.error( function(data, status, headers, config) { label: data.project_local_paths[i],
ProcessErrors(scope, data, status, null, value: data.project_local_paths[i]
{ hdr: 'Error!', msg: 'Failed to access API config. GET status: ' + status }); });
}); }
if (scope.local_path) {
// List only includes paths not assigned to projects, so add the
// path assigned to the current project.
opts.push({
label: scope.local_path,
value: scope.local_path
});
}
scope.project_local_paths = arraySort(opts);
if (scope.local_path) {
for (i = 0; scope.project_local_paths.length; i++) {
if (scope.project_local_paths[i].value === scope.local_path) {
scope.local_path = scope.project_local_paths[i];
break;
}
}
}
scope.base_dir = data.project_base_dir;
master.local_path = scope.local_path;
master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get
// wiped out on form reset.
if (opts.length === 0) {
// trigger display of alert block when scm_type == manual
scope.showMissingPlaybooksAlert = true;
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to access API config. GET status: ' + status });
});
};
} }
}]); ]);

View File

@@ -3,125 +3,133 @@
* *
* ProjectsHelper * ProjectsHelper
* *
* Use GetProjectPath({ scope: <scope>, master: <master obj> }) to * Use GetProjectPath({ scope: <scope>, master: <master obj> }) to
* load scope.project_local_paths (array of options for drop-down) and * load scope.project_local_paths (array of options for drop-down) and
* scope.base_dir (readonly field). * scope.base_dir (readonly field).
* *
*/ */
'use strict';
angular.module('ProjectsHelper', ['RestServices', 'Utilities', 'ProjectStatusDefinition', 'ProjectFormDefinition']) angular.module('ProjectsHelper', ['RestServices', 'Utilities', 'ProjectStatusDefinition', 'ProjectFormDefinition'])
.factory('ProjectStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm', .factory('ProjectStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ProjectStatusForm', 'Wait', 'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ProjectStatusForm', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, function ($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
FormatDate, ProjectStatusForm, Wait) { FormatDate, ProjectStatusForm, Wait) {
return function(params) { return function (params) {
var project_id = params.project_id; var project_id = params.project_id,
var last_update = params.last_update; last_update = params.last_update,
generator = GenerateForm,
var generator = GenerateForm; form = ProjectStatusForm,
var form = ProjectStatusForm; html, scope, ww, wh, x, y, maxrows;
Wait('start');
// Using jquery dialog for its expandable property Wait('start');
var html = "<div id=\"status-modal-dialog\"><div id=\"form-container\" style=\"width: 100%;\"></div></div>\n";
$('#projects-modal-container').empty().append(html);
var scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false, breadCrumbs: false }); // Using jquery dialog for its expandable property
generator.reset(); html = "<div id=\"status-modal-dialog\"><div id=\"form-container\" style=\"width: 100%;\"></div></div>\n";
$('#projects-modal-container').empty().append(html);
// Set modal dimensions based on viewport width scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false, breadCrumbs: false });
var ww = $(document).width(); generator.reset();
var wh = $('body').height();
var x, y, maxrows; // Set modal dimensions based on viewport width
if (ww > 1199) { ww = $(document).width();
// desktop wh = $('body').height();
x = 675; if (ww > 1199) {
y = (750 > wh) ? wh - 20 : 750; // desktop
maxrows = 20; x = 675;
} y = (750 > wh) ? wh - 20 : 750;
else if (ww <= 1199 && ww >= 768) { maxrows = 20;
x = 550; } else if (ww <= 1199 && ww >= 768) {
y = (620 > wh) ? wh - 15 : 620; x = 550;
maxrows = 15; y = (620 > wh) ? wh - 15 : 620;
} maxrows = 15;
else { } else {
x = (ww - 20); x = (ww - 20);
y = (500 > wh) ? wh : 500; y = (500 > wh) ? wh : 500;
maxrows = 10; maxrows = 10;
} }
// Create the modal // Create the modal
$('#status-modal-dialog').dialog({ $('#status-modal-dialog').dialog({
buttons: { "OK": function() { $( this ).dialog( "close" ); } }, buttons: {
modal: true, "OK": function () {
width: x, $(this).dialog("close");
height: y, }
autoOpen: false,
create: function (e, ui) {
// fix the close button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button').empty().attr({ 'class': 'close' }).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
.attr({ 'class': 'btn btn-primary' });
}, },
resizeStop: function(e, ui) { modal: true,
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100% width: x,
var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'); height: y,
var content = dialog.find('#status-modal-dialog'); autoOpen: false,
content.width( dialog.width() - 28 ); create: function () {
// fix the close button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button').empty().attr({
'class': 'close'
}).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
.attr({
'class': 'btn btn-primary'
});
}, },
close: function(e, ui) { resizeStop: function () {
// Destroy on close // for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
// Destroy on close var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'),
$('.tooltip').each( function(index) { content = dialog.find('#status-modal-dialog');
// Remove any lingering tooltip <div> elements content.width(dialog.width() - 28);
$(this).remove(); },
close: function () {
// Destroy on close
// Destroy on close
$('.tooltip').each(function () {
// Remove any lingering tooltip <div> elements
$(this).remove();
}); });
$('.popover').each(function(index) { $('.popover').each(function () {
// remove lingering popover <div> elements // remove lingering popover <div> elements
$(this).remove(); $(this).remove();
}); });
$('#status-modal-dialog').dialog('destroy'); $('#status-modal-dialog').dialog('destroy');
$('#projects-modal-container').empty(); $('#projects-modal-container').empty();
}, },
open: function(e, ui) { open: function () {
Wait('stop'); Wait('stop');
} }
}); });
// Retrieve detail record and prepopulate the form
Rest.setUrl(last_update);
Rest.get()
.success( function(data, status, headers, config) {
var results = data;
for (var fld in form.fields) {
if (results[fld]) {
if (fld == 'created') {
scope[fld] = FormatDate(new Date(results[fld]));
}
else {
scope[fld] = results[fld];
}
}
else {
if (results.summary_fields.project[fld]) {
scope[fld] = results.summary_fields.project[fld]
}
}
}
$('#status-modal-dialog')
.dialog({ title: results.summary_fields.project.name + ' Status'})
.dialog('open');
})
.error( function(data, status, headers, config) {
$('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve status of project: ' + project_id + '. GET status: ' + status });
});
}
}]);
// Retrieve detail record and prepopulate the form
Rest.setUrl(last_update);
Rest.get()
.success(function (data) {
var results = data, fld;
for (fld in form.fields) {
if (results[fld]) {
if (fld === 'created') {
scope[fld] = FormatDate(new Date(results[fld]));
} else {
scope[fld] = results[fld];
}
} else {
if (results.summary_fields.project[fld]) {
scope[fld] = results.summary_fields.project[fld];
}
}
}
$('#status-modal-dialog')
.dialog({
title: results.summary_fields.project.name + ' Status'
})
.dialog('open');
})
.error(function (data, status) {
$('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to retrieve status of project: ' + project_id + '. GET status: ' + status
});
});
};
}
]);

View File

@@ -3,167 +3,172 @@
* *
* SelectionHelper * SelectionHelper
* Used in list controllers where the list might also be used as a selection list. * Used in list controllers where the list might also be used as a selection list.
* *
* SelectionInit( { * SelectionInit( {
* scope: <list scope>, * scope: <list scope>,
* list: <list object> * list: <list object>
* }) * })
*/ */
'use strict';
angular.module('SelectionHelper', ['Utilities', 'RestServices']) angular.module('SelectionHelper', ['Utilities', 'RestServices'])
.factory('SelectionInit', [ 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait', .factory('SelectionInit', ['Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait',
function(Rest, Alert, ProcessErrors, ReturnToCaller, Wait) { function (Rest, Alert, ProcessErrors, ReturnToCaller, Wait) {
return function(params) { return function (params) {
var scope = params.scope; // current scope
var list = params.list; // list object
var target_url = params.url; // URL to POST selected objects
var returnToCaller = params.returnToCaller;
if (params.selected !== undefined) { var scope = params.scope,
var selected = params.selected; list = params.list,
} target_url = params.url,
else { returnToCaller = params.returnToCaller,
var selected = []; //array of selected row IDs selected;
}
scope.formModalActionDisabled = true; if (params.selected !== undefined) {
scope.disableSelectBtn = true; selected = params.selected;
} else {
selected = []; //array of selected row IDs
}
// toggle row selection
scope['toggle_' + list.iterator] = function(id, ischeckbox) {
for (var i=0; i < scope[list.name].length; i++) {
if (scope[list.name][i]['id'] == id) {
if ( (scope[list.name][i]['checked'] == '0' && !ischeckbox) || (scope[list.name][i]['checked'] == 1 && ischeckbox) ) {
// select the row
scope[list.name][i]['checked'] = '1';
scope[list.name][i]['success_class'] = 'success';
// add selected object to the array
var found = false;
for (var j=0; j < selected.length; j++) {
if (selected[j].id == id) {
found = true;
break;
}
}
if (!found) {
selected.push(scope[list.name][i]);
}
}
else {
// unselect the row
scope[list.name][i]['checked'] = '0';
scope[list.name][i]['success_class'] = '';
// remove selected object from the array
for (var j=0; j < selected.length; j++) {
if (selected[j].id == id) {
selected.splice(j,1);
break;
}
}
}
}
}
if (selected.length > 0) {
scope.formModalActionDisabled = false;
scope.disableSelectBtn = false;
}
else {
scope.formModalActionDisabled = true;
scope.disableSelectBtn = true;
}
}
// Add the selections
scope.finishSelection = function() {
Rest.setUrl(target_url);
var queue = [];
scope.formModalActionDisabled = true; scope.formModalActionDisabled = true;
scope.disableSelectBtn = true; scope.disableSelectBtn = true;
Wait('start');
function finished() { // toggle row selection
selected = []; scope['toggle_' + list.iterator] = function (id, ischeckbox) {
if (returnToCaller !== undefined) { var i, j, found;
ReturnToCaller(returnToCaller); for (i = 0; i < scope[list.name].length; i++) {
if (scope[list.name][i].id === id) {
if ((scope[list.name][i].checked === '0' && !ischeckbox) || (scope[list.name][i].checked === 1 && ischeckbox)) {
// select the row
scope[list.name][i].checked = '1';
scope[list.name][i].success_class = 'success';
// add selected object to the array
found = false;
for (j = 0; j < selected.length; j++) {
if (selected[j].id === id) {
found = true;
break;
}
}
if (!found) {
selected.push(scope[list.name][i]);
}
} else {
// unselect the row
scope[list.name][i].checked = '0';
scope[list.name][i].success_class = '';
// remove selected object from the array
for (j = 0; j < selected.length; j++) {
if (selected[j].id === id) {
selected.splice(j, 1);
break;
}
}
}
}
} }
else { if (selected.length > 0) {
$('#form-modal').modal('hide'); scope.formModalActionDisabled = false;
scope.$emit('modalClosed'); scope.disableSelectBtn = false;
} else {
scope.formModalActionDisabled = true;
scope.disableSelectBtn = true;
} }
};
// Add the selections
scope.finishSelection = function () {
Rest.setUrl(target_url);
var queue = [], j;
scope.formModalActionDisabled = true;
scope.disableSelectBtn = true;
Wait('start');
function finished() {
selected = [];
if (returnToCaller !== undefined) {
ReturnToCaller(returnToCaller);
} else {
$('#form-modal').modal('hide');
scope.$emit('modalClosed');
}
} }
if (scope.callFinishedRemove) {
scope.callFinishedRemove();
}
scope.callFinishedRemove = scope.$on('callFinished', function() {
// We call the API for each selected item. We need to hang out until all the api
// calls are finished.
if (queue.length == selected.length) {
Wait('stop');
var errors = 0;
for (var i=0; i < queue.length; i++) {
if (queue[i].result == 'error') {
ProcessErrors(scope, queue[i].data, queue[i].status, null,
{ hdr: 'POST Failure', msg: 'Failed to add ' + list.iterator +
'. POST returned status: ' + queue[i].status });
errors++;
}
}
if (errors == 0) {
finished();
}
}
});
if (selected.length > 0 ) { function postIt(data) {
for (var j=0; j < selected.length; j++) { Rest.post(data)
Rest.post(selected[j]) .success(function (data, status) {
.success( function(data, status, headers, config) { queue.push({ result: 'success', data: data, status: status });
queue.push({ result: 'success', data: data, status: status }); scope.$emit('callFinished');
scope.$emit('callFinished'); })
}) .error(function (data, status, headers) {
.error( function(data, status, headers, config) { queue.push({ result: 'error', data: data, status: status, headers: headers });
queue.push({ result: 'error', data: data, status: status, headers: headers }); scope.$emit('callFinished');
scope.$emit('callFinished'); });
}); }
}
}
else {
finished();
}
}
scope.formModalAction = scope.finishSelection; if (scope.callFinishedRemove) {
scope.callFinishedRemove();
}
scope.callFinishedRemove = scope.$on('callFinished', function () {
// We call the API for each selected item. We need to hang out until all the api
// calls are finished.
var i, errors=0;
if (queue.length === selected.length) {
Wait('stop');
for (i = 0; i < queue.length; i++) {
if (queue[i].result === 'error') {
ProcessErrors(scope, queue[i].data, queue[i].status, null, { hdr: 'POST Failure',
msg: 'Failed to add ' + list.iterator + '. POST returned status: ' + queue[i].status });
errors++;
}
}
if (errors === 0) {
finished();
}
}
});
// Initialize our data set after a refresh (page change or search) if (selected.length > 0) {
if (scope.SelectPostRefreshRemove) { for (j = 0; j < selected.length; j++) {
scope.SelectPostRefreshRemove(); postIt(selected[j]);
} }
scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function() { } else {
if (scope[list.name]) { finished();
for (var i=0; i < scope[list.name].length; i++) { }
var found = false; };
for (var j=0; j < selected.length; j++) {
if (selected[j].id == scope[list.name][i].id) { scope.formModalAction = scope.finishSelection;
found = true;
break; // Initialize our data set after a refresh (page change or search)
} if (scope.SelectPostRefreshRemove) {
} scope.SelectPostRefreshRemove();
if (found) {
scope[list.name][i]['checked'] = '1';
scope[list.name][i]['success_class'] = 'success';
}
else {
scope[list.name][i]['checked'] = '0';
scope[list.name][i]['success_class'] = '';
}
}
} }
scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function () {
var i, j, found;
if (scope[list.name]) {
for (i = 0; i < scope[list.name].length; i++) {
found = false;
for (j = 0; j < selected.length; j++) {
if (selected[j].id === scope[list.name][i].id) {
found = true;
break;
}
}
if (found) {
scope[list.name][i].checked = '1';
scope[list.name][i].success_class = 'success';
} else {
scope[list.name][i].checked = '0';
scope[list.name][i].success_class = '';
}
}
}
}); });
} };
}]); }
]);

View File

@@ -2,27 +2,38 @@
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* UserHelper * UserHelper
* Routines shared amongst the user controllers * Routines shared amongst the user controllers
*
*/ */
angular.module('UserHelper', [ 'UserFormDefinition' ])
.factory('ResetForm', ['UserForm', function(UserForm) {
return function() {
// Restore form to default conditions. Run before applying LDAP configuration.
// LDAP may manage some or all of these fields in which case the user cannot
// make changes to their values in AWX.
UserForm.fields['first_name'].readonly = false; 'use strict';
UserForm.fields['first_name'].editRequired = true;
UserForm.fields['last_name'].readonly = false; angular.module('UserHelper', ['UserFormDefinition'])
UserForm.fields['last_name'].editRequired = true; .factory('ResetForm', ['UserForm',
UserForm.fields['email'].readonly = false; function (UserForm) {
UserForm.fields['email'].editRequired = true; return function () {
UserForm.fields['organization'].awRequiredWhen = { variable: "orgrequired", init: true}; // Restore form to default conditions. Run before applying LDAP configuration.
UserForm.fields['organization'].readonly = false; // LDAP may manage some or all of these fields in which case the user cannot
UserForm.fields['username'].awRequiredWhen = { variable: "not_ldap_user", init: true }; // make changes to their values in AWX.
UserForm.fields['username'].readonly = false;
UserForm.fields['password'].editRequired = false; UserForm.fields.first_name.readonly = false;
UserForm.fields['password'].addRrequired = true; UserForm.fields.first_name.editRequired = true;
} UserForm.fields.last_name.readonly = false;
}]); UserForm.fields.last_name.editRequired = true;
UserForm.fields.email.readonly = false;
UserForm.fields.email.editRequired = true;
UserForm.fields.organization.awRequiredWhen = {
variable: "orgrequired",
init: true
};
UserForm.fields.organization.readonly = false;
UserForm.fields.username.awRequiredWhen = {
variable: "not_ldap_user",
init: true
};
UserForm.fields.username.readonly = false;
UserForm.fields.password.editRequired = false;
UserForm.fields.password.addRrequired = true;
};
}
]);

View File

@@ -3,87 +3,89 @@
* *
* APIDefaults * APIDefaults
* *
* *
*/ */
angular.module('APIDefaults', ['RestServices', 'Utilities']) 'use strict';
.factory('GetAPIDefaults', ['Alert', 'Rest', '$rootScope', function(Alert, Rest, $rootScope) {
return function(key) {
//Reload a related collection on pagination or search change
var answer;
var result = {};
var cnt=0;
function lookup(key) { angular.module('APIDefaults', ['RestServices', 'Utilities'])
var result = {}; .factory('GetAPIDefaults', ['Alert', 'Rest', '$rootScope',
for (id in $rootScope.apiDefaults) { function (Alert, Rest, $rootScope) {
if (id == key || id.iterator == key) { return function (key) {
result[id] = defaults[id];
break; //Reload a related collection on pagination or search change
}
var result = {}, cnt = 0, url;
function lookup(key) {
var id, result = {};
for (id in $rootScope.apiDefaults) {
if (id === key || id.iterator === key) {
result[id] = $rootScope.apiDefaults[id];
break;
}
}
return result;
}
function wait() {
if ($.isEmptyObject(result) && cnt < 5) {
cnt++;
setTimeout(1000, wait());
} else if (result.status === 'success') {
return lookup(key);
}
}
if ($rootScope.apiDefaults === null || $rootScope.apiDefaults === undefined) {
url = '/api/v1/';
Rest.setUrl(url);
Rest.get()
.success(function (data) {
var id, defaults = data;
for (id in defaults) {
switch (id) {
case 'organizations':
defaults[id].iterator = 'organization';
break;
case 'jobs':
defaults[id].iterator = 'job';
break;
case 'users':
defaults[id].iterator = 'user';
break;
case 'teams':
defaults[id].iterator = 'team';
break;
case 'hosts':
defaults[id].iterator = 'host';
break;
case 'groups':
defaults[id].iterator = 'group';
break;
case 'projects':
defaults[id].iterator = 'project';
break;
case 'inventories':
defaults[id].iterator = 'inventory';
break;
}
}
$rootScope.apiDefaults = defaults;
result = {
status: 'success'
};
})
.error(function (data, status) {
result = {
status: 'error',
msg: 'Call to ' + url + ' failed. GET returned status: ' + status
};
});
return wait();
} else {
return lookup(key);
}
};
} }
return result; ]);
}
function wait() {
var answer;
if ( result == {} && cnt < 5) {
cnt++;
setTimeout(1000, wait());
}
else {
if (result.status == 'success') {
return lookup(key);
}
}
}
if ($rootScope.apiDefaults == null || $rootScope.apiDefaults == undefined) {
var result = {};
var url = '/api/v1';
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
defaults = data;
for (var id in defaults) {
switch (id) {
case 'organizations':
dafaults[id].iterator = 'organization';
break;
case 'jobs':
defaults[id].iterator = 'job';
break;
case 'users':
defaults[id].iterator = 'user';
break;
case 'teams':
defaults[id].iterator = 'team';
break;
case 'hosts':
defaults[id].iterator = 'host';
break;
case 'groups':
defaults[id].iterator = 'group';
break;
case 'projects':
defaults[id].iterator = 'project';
break;
}
}
$rootScope.apiDefaults = defaults;
result = {status: 'success'};
})
.error( function(data, status, headers, config) {
result = {status: 'error', msg: 'Call to ' + url + ' failed. GET returned status: ' + status};
});
return wait();
}
else {
return lookup(key);
}
}
}]);

View File

@@ -5,250 +5,250 @@
* Routines for building the tree. Everything related to the tree is here except * Routines for building the tree. Everything related to the tree is here except
* for the menu piece. The routine for building the menu is in InventoriesEdit controller * for the menu piece. The routine for building the menu is in InventoriesEdit controller
* (controllers/Inventories.js) * (controllers/Inventories.js)
* *
*/ */
angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService',
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper'
])
.factory('WatchInventoryWindowResize', ['ApplyEllipsis', function(ApplyEllipsis) { /* globals console:false */
return function() {
// Call to set or restore window resize 'use strict';
var timeOut;
$(window).resize(function() { angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService',
clearTimeout(timeOut); 'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper'
timeOut = setTimeout(function() { ])
// Hack to stop group-name div slipping to a new line
$('#groups_table .name-column').each( function() { .factory('WatchInventoryWindowResize', ['ApplyEllipsis',
var td_width = $(this).width(); function (ApplyEllipsis) {
var level_width = $(this).find('.level').width(); return function () {
var level_padding = parseInt($(this).find('.level').css('padding-left').replace(/px/,'')); // Call to set or restore window resize
var level = level_width + level_padding; var timeOut;
var pct = ( 100 - Math.ceil((level / td_width)*100) ) + '%'; $(window).resize(function () {
$(this).find('.group-name').css({ width: pct }); clearTimeout(timeOut);
timeOut = setTimeout(function () {
// Hack to stop group-name div slipping to a new line
$('#groups_table .name-column').each(function () {
var td_width = $(this).width(),
level_width = $(this).find('.level').width(),
level_padding = parseInt($(this).find('.level').css('padding-left').replace(/px/, '')),
level = level_width + level_padding,
pct = (100 - Math.ceil((level / td_width) * 100)) + '%';
$(this).find('.group-name').css({ width: pct });
}); });
ApplyEllipsis('#groups_table .group-name a'); ApplyEllipsis('#groups_table .group-name a');
ApplyEllipsis('#hosts_table .host-name a'); ApplyEllipsis('#hosts_table .host-name a');
}, 100); }, 100);
}); });
} };
}]) }
])
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', .factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'Wait', 'GetBasePath', 'ParseTypeChange', 'Wait',
function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait) { function (InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait) {
return function(params) { return function (params) {
// Save inventory property modifications
var scope = params.scope; // Save inventory property modifications
var form = InventoryForm;
var defaultUrl=GetBasePath('inventory');
Wait('start'); var scope = params.scope,
form = InventoryForm,
try { defaultUrl = GetBasePath('inventory'),
// Make sure we have valid variable data fld, json_data, data;
if (scope.inventoryParseType == 'json') {
var json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses
}
else {
var json_data = jsyaml.load(scope.inventory_variables); //parse yaml
}
// Make sure our JSON is actually an object Wait('start');
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
var data = {} try {
for (var fld in form.fields) { // Make sure we have valid variable data
if (fld != 'inventory_variables') { if (scope.inventoryParseType === 'json') {
if (form.fields[fld].realName) { json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses
data[form.fields[fld].realName] = scope[fld]; } else {
} json_data = jsyaml.load(scope.inventory_variables); //parse yaml
else { }
data[fld] = scope[fld];
}
}
}
Rest.setUrl(defaultUrl + scope['inventory_id'] + '/'); // Make sure our JSON is actually an object
Rest.put(data) if (typeof json_data !== 'object') {
.success( function(data, status, headers, config) { throw "failed to return an object!";
if (scope.inventory_variables) { }
Rest.setUrl(data.related.variable_data);
Rest.put(json_data)
.success( function(data, status, headers, config) {
Wait('stop');
scope.$emit('InventorySaved');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update inventory varaibles. PUT returned status: ' + status });
});
}
else {
scope.$emit('InventorySaved');
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update inventory. POST returned status: ' + status });
});
}
catch(err) {
Wait('stop');
Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
}
}
}])
data = {};
.factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', for (fld in form.fields) {
'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', if (fld !== 'inventory_variables') {
function(InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory, if (form.fields[fld].realName) {
Wait, Store, SearchInit) { data[form.fields[fld].realName] = scope[fld];
return function(params) { } else {
data[fld] = scope[fld];
var parent_scope = params.scope
var inventory_id = params.inventory_id;
var generator = GenerateForm;
var form = InventoryForm;
var defaultUrl=GetBasePath('inventory');
var master = {};
// Hang onto current search params
var PreviousSearchParams = Store('CurrentSearchParams');
form.well = false;
var scope = generator.inject(form, {mode: 'edit', modal: true, related: false, modal_show: false });
/* Reset form properties. Otherwise it screws up future requests of the Inventories detail page */
form.well = true;
ParseTypeChange(scope,'inventory_variables', 'inventoryParseType');
scope.inventoryParseType = 'yaml';
scope.formModalActionLabel = 'Save';
scope.formModalCancelShow = true;
scope.formModalInfo = false;
scope.formModalHeader = 'Inventory Properties';
Wait('start');
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
for (var fld in form.fields) {
if (fld == 'inventory_variables') {
// Parse variables, converting to YAML.
if ($.isEmptyObject(data.variables) || data.variables == "\{\}" ||
data.variables == "null" || data.variables == "") {
scope.inventory_variables = "---";
} }
else {
try {
var json_obj = JSON.parse(data.variables);
scope.inventory_variables = jsyaml.safeDump(json_obj);
}
catch(err) {
Alert('Variable Parse Error', 'Attempted to parse variables for inventory: ' + inventory_id +
'. Parse returned: ' + err);
if (console) {
console.log(err);
console.log('data:');
console.log(data.variables);
}
scope.inventory_variables = '---';
}
}
master.inventory_variables = scope.variables;
}
else if (fld == 'inventory_name') {
scope[fld] = data.name;
master[fld] = scope[fld];
}
else if (fld == 'inventory_description') {
scope[fld] = data.description;
master[fld] = scope[fld];
}
else if (data[fld]) {
scope[fld] = data[fld];
master[fld] = scope[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];
} }
} }
LookUpInit({ Rest.setUrl(defaultUrl + scope.inventory_id + '/');
scope: scope, Rest.put(data)
form: form, .success(function (data) {
current_item: scope.organization, if (scope.inventory_variables) {
list: OrganizationList, Rest.setUrl(data.related.variable_data);
field: 'organization' Rest.put(json_data)
}); .success(function () {
Wait('stop');
scope.$emit('InventorySaved');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory varaibles. PUT returned status: ' + status
});
});
} else {
scope.$emit('InventorySaved');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to update inventory. POST returned status: ' + status });
});
} catch (err) {
Wait('stop'); Wait('stop');
$('#form-modal').modal('show'); Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
}
};
}
])
.factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit',
function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory,
Wait, Store, SearchInit) {
return function (params) {
var parent_scope = params.scope,
inventory_id = params.inventory_id,
generator = GenerateForm,
form = InventoryForm,
master = {},
PreviousSearchParams = Store('CurrentSearchParams'),
scope;
form.well = false;
scope = generator.inject(form, { mode: 'edit', modal: true, related: false, modal_show: false });
/* Reset form properties. Otherwise it screws up future requests of the Inventories detail page */
form.well = true;
ParseTypeChange(scope, 'inventory_variables', 'inventoryParseType');
scope.inventoryParseType = 'yaml';
scope.formModalActionLabel = 'Save';
scope.formModalCancelShow = true;
scope.formModalInfo = false;
scope.formModalHeader = 'Inventory Properties';
Wait('start');
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success(function (data) {
var fld, json_obj;
for (fld in form.fields) {
if (fld === 'inventory_variables') {
// Parse variables, converting to YAML.
if ($.isEmptyObject(data.variables) || data.variables === "{}" ||
data.variables === "null" || data.variables === "") {
scope.inventory_variables = "---";
} else {
try {
json_obj = JSON.parse(data.variables);
scope.inventory_variables = jsyaml.safeDump(json_obj);
} catch (err) {
Alert('Variable Parse Error', 'Attempted to parse variables for inventory: ' + inventory_id +
'. Parse returned: ' + err);
if (console) {
console.log(err);
console.log('data:');
console.log(data.variables);
}
scope.inventory_variables = '---';
}
}
master.inventory_variables = scope.variables;
} else if (fld === 'inventory_name') {
scope[fld] = data.name;
master[fld] = scope[fld];
} else if (fld === 'inventory_description') {
scope[fld] = data.description;
master[fld] = scope[fld];
} else if (data[fld]) {
scope[fld] = data[fld];
master[fld] = scope[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];
}
}
LookUpInit({
scope: scope,
form: form,
current_item: scope.organization,
list: OrganizationList,
field: 'organization'
});
Wait('stop');
$('#form-modal').modal('show');
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, {
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); hdr: 'Error!',
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
});
}); });
if (scope.removeInventorySaved) { if (scope.removeInventorySaved) {
scope.removeInventorySaved(); scope.removeInventorySaved();
}
scope.removeInventorySaved = scope.$on('InventorySaved', function() {
$('#form-modal').modal('hide');
// Restore prior search state
if (scope.searchCleanp) {
scope.searchCleanup();
} }
SearchInit({ scope.removeInventorySaved = scope.$on('InventorySaved', function () {
scope: parent_scope, $('#form-modal').modal('hide');
set: PreviousSearchParams.set, // Restore prior search state
list: PreviousSearchParams.list, if (scope.searchCleanp) {
url: PreviousSearchParams.defaultUrl, scope.searchCleanup();
iterator: PreviousSearchParams.iterator, }
sort_order: PreviousSearchParams.sort_order, SearchInit({
setWidgets: false scope: parent_scope,
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false
}); });
parent_scope.$emit('RefreshInventories'); parent_scope.$emit('RefreshInventories');
}); });
scope.cancelModal = function() { scope.cancelModal = function () {
// Restore prior search state // Restore prior search state
if (scope.searchCleanp) { if (scope.searchCleanp) {
scope.searchCleanup(); scope.searchCleanup();
} }
SearchInit({ SearchInit({
scope: parent_scope, scope: parent_scope,
set: PreviousSearchParams.set, set: PreviousSearchParams.set,
list: PreviousSearchParams.list, list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl, url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator, iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order, sort_order: PreviousSearchParams.sort_order,
setWidgets: false setWidgets: false
}); });
} };
scope.formModalAction = function() { scope.formModalAction = function () {
parent_scope.inventory_id = inventory_id; parent_scope.inventory_id = inventory_id;
parent_scope.inventory_name = scope.inventory_name; parent_scope.inventory_name = scope.inventory_name;
SaveInventory({ scope: scope }); SaveInventory({
} scope: scope
});
} };
}]);
};
}
]);

View File

@@ -8,33 +8,33 @@
* *
*/ */
angular.module('md5Helper', ['RestServices', 'Utilities']) 'use strict';
.factory('md5Setup', ['Alert', 'Rest', 'GetBasePath','ProcessErrors',
function(Alert, Rest, GetBasePath, ProcessErrors) {
return function(params) {
var scope = params.scope;
var master = params.master;
var check_field = params.check_field;
var default_val = params.default_val; //default(true/false) for the checkbox
scope[check_field] = default_val;
master[check_field] = default_val;
scope.genMD5 = function(fld) { angular.module('md5Helper', ['RestServices', 'Utilities'])
var now = new Date(); .factory('md5Setup', [ function () {
scope[fld] = $.md5('AnsibleWorks' + now.getTime()); return function (params) {
}
var scope = params.scope,
master = params.master,
check_field = params.check_field,
default_val = params.default_val;
scope.toggleCallback = function(fld) { scope[check_field] = default_val;
if (scope.allow_callbacks == 'false') { master[check_field] = default_val;
scope[fld] = '';
}
}
scope.selectAll = function(fld) { scope.genMD5 = function (fld) {
$('input[name="' + fld +'"]').focus().select(); var now = new Date();
} scope[fld] = $.md5('AnsibleWorks' + now.getTime());
};
}
}]); scope.toggleCallback = function (fld) {
if (scope.allow_callbacks === 'false') {
scope[fld] = '';
}
};
scope.selectAll = function (fld) {
$('input[name="' + fld + '"]').focus().select();
};
};
}]);

View File

@@ -9,35 +9,43 @@
* scope: <current scope>, * scope: <current scope>,
* set: <model>, * set: <model>,
* iterator: <model name in singular form (i.e. organization), * iterator: <model name in singular form (i.e. organization),
* url: <the api url to call> * url: <the api url to call>
* }); * });
* *
*/ */
angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities', 'PaginationHelpers']) 'use strict';
.factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup',
function(ProcessErrors, Rest, Wait, PageRangeSetup) { angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
return function(params) { .factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup',
function (ProcessErrors, Rest, Wait, PageRangeSetup) {
var scope = params.scope; return function (params) {
var set = params.set;
var iterator = params.iterator; var scope = params.scope,
var url = params.url; set = params.set,
iterator = params.iterator,
Rest.setUrl(url); url = params.url;
Rest.get()
.success( function(data, status, headers, config) { Rest.setUrl(url);
PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator }); Rest.get()
scope[set] = data['results']; .success(function (data) {
scope[iterator + 'Loading'] = false; PageRangeSetup({
scope[iterator + 'HoldInput'] = false; scope: scope,
Wait('stop'); count: data.count,
scope.$emit('related' + set); next: data.next,
previous: data.previous,
}) iterator: iterator
.error ( function(data, status, headers, config) { });
ProcessErrors(scope, data, status, null, scope[set] = data.results;
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); scope[iterator + 'Loading'] = false;
}); scope[iterator + 'HoldInput'] = false;
Wait('stop');
scope.$emit('related' + set);
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
});
};
} }
}]); ]);

View File

@@ -9,40 +9,52 @@
* scope: <current scope>, * scope: <current scope>,
* set: <model>, * set: <model>,
* iterator: <model name in singular form (i.e. organization), * iterator: <model name in singular form (i.e. organization),
* url: <the api url to call> * url: <the api url to call>
* }); * });
* *
*/ */
angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
.factory('Refresh', ['ProcessErrors', 'Rest', 'Wait', 'Empty', 'PageRangeSetup',
function(ProcessErrors, Rest, Wait, Empty, PageRangeSetup) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
var url = params.url;
scope.current_url = url; 'use strict';
Rest.setUrl(url);
Rest.get() angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
.success( function(data, status, headers, config) { .factory('Refresh', ['ProcessErrors', 'Rest', 'Wait', 'Empty', 'PageRangeSetup',
PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator }); function (ProcessErrors, Rest, Wait, Empty, PageRangeSetup) {
scope[iterator + 'Loading'] = false; return function (params) {
for (var i=1; i <= 3; i++) {
var modifier = (i == 1) ? '' : i; var scope = params.scope,
scope[iterator + 'HoldInput' + modifier] = false; set = params.set,
} iterator = params.iterator,
scope[set] = data['results']; url = params.url;
window.scrollTo(0,0);
Wait('stop'); scope.current_url = url;
scope.$emit('PostRefresh'); Rest.setUrl(url);
}) Rest.get()
.error ( function(data, status, headers, config) { .success(function (data) {
scope[iterator + 'HoldInput'] = false; var i, modifier;
ProcessErrors(scope, data, status, null, PageRangeSetup({
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); scope: scope,
}); count: data.count,
next: data.next,
previous: data.previous,
iterator: iterator
});
scope[iterator + 'Loading'] = false;
for (i = 1; i <= 3; i++) {
modifier = (i === 1) ? '' : i;
scope[iterator + 'HoldInput' + modifier] = false;
}
scope[set] = data.results;
window.scrollTo(0, 0);
Wait('stop');
scope.$emit('PostRefresh');
})
.error(function (data, status) {
scope[iterator + 'HoldInput'] = false;
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status
});
});
};
} }
}]); ]);

View File

@@ -3,247 +3,241 @@
* *
* RelatedSearchHelper * RelatedSearchHelper
* *
* All the parts for controlling the search widget on * All the parts for controlling the search widget on
* related collections. * related collections.
* *
* SearchInit({ * SearchInit({
* scope: <scope>, * scope: <scope>,
* relatedSets: <array of related collections {model_name, url, iterator}>, * relatedSets: <array of related collections {model_name, url, iterator}>,
* form: <form object used by FormGenerator> * form: <form object used by FormGenerator>
* }); * });
* *
* *
*/ */
angular.module('RelatedSearchHelper', ['RestServices', 'Utilities','RefreshRelatedHelper']) 'use strict';
angular.module('RelatedSearchHelper', ['RestServices', 'Utilities', 'RefreshRelatedHelper'])
.factory('RelatedSearchInit', ['$timeout', 'Alert', 'Rest', 'RefreshRelated', 'Wait', .factory('RelatedSearchInit', ['$timeout', 'Alert', 'Rest', 'RefreshRelated', 'Wait',
function($timeout, Alert, Rest, RefreshRelated, Wait) { function ($timeout, Alert, Rest, RefreshRelated, Wait) {
return function(params) { return function (params) {
var scope = params.scope; var scope = params.scope,
var relatedSets = params.relatedSets; relatedSets = params.relatedSets,
var form = params.form; form = params.form, f;
// Set default values // Set default values
function setDefaults(inIterator) { function setDefaults(inIterator) {
var iterator, f; var iterator, f, fld, set;
for (var set in form.related) { for (set in form.related) {
if (form.related[set].type != 'tree' && (inIterator === undefined || inIterator == form.related[set].iterator)) { if (form.related[set].type !== 'tree' && (inIterator === undefined || inIterator === form.related[set].iterator)) {
iterator = form.related[set].iterator; iterator = form.related[set].iterator;
for (var fld in form.related[set].fields) { for (fld in form.related[set].fields) {
if (form.related[set].fields[fld].key) { if (form.related[set].fields[fld].key) {
scope[iterator + 'SearchField'] = fld scope[iterator + 'SearchField'] = fld;
scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label; scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
break; break;
}
}
scope[iterator + 'SortOrder'] = null;
scope[iterator + 'SearchType'] = 'icontains';
scope[iterator + 'SearchTypeLabel'] = 'Contains';
scope[iterator + 'SearchValue'] = null;
scope[iterator + 'SelectShow'] = false;
//scope[iterator + 'HideSearchType'] = false;
scope[iterator + 'ShowStartBtn'] = true;
scope[iterator + 'HideAllStartBtn'] = false;
f = scope[iterator + 'SearchField'];
if (form.related[set].fields[f].searchType &&
(form.related[set].fields[f].searchType === 'boolean' || form.related[set].fields[f].searchType === 'select')) {
scope[iterator + 'SelectShow'] = true;
scope[iterator + 'SearchSelectOpts'] = form.fields[f].searchOptions;
}
if (form.related[set].fields[f].searchType && form.related[set].fields[f].searchType === 'gtzero') {
scope[iterator + "InputHide"] = true;
}
} }
} }
scope[iterator + 'SortOrder'] = null; }
scope[iterator + 'SearchType'] = 'icontains';
scope[iterator + 'SearchTypeLabel'] = 'Contains'; setDefaults();
scope[iterator + 'SearchValue'] = null;
scope.resetSearch = function (iterator) {
setDefaults(iterator);
scope.search(iterator);
};
// Functions to handle search widget changes
scope.setSearchField = function (iterator, fld, label) {
var f, related;
for (related in form.related) {
if (form.related[related].iterator === iterator) {
f = form.related[related].fields[fld];
}
}
scope[iterator + 'SearchFieldLabel'] = label;
scope[iterator + 'SearchField'] = fld;
scope[iterator + 'SearchValue'] = '';
scope[iterator + 'SelectShow'] = false; scope[iterator + 'SelectShow'] = false;
//scope[iterator + 'HideSearchType'] = false; //scope[iterator + 'HideSearchType'] = false;
scope[iterator + 'InputHide'] = false;
scope[iterator + 'ShowStartBtn'] = true; scope[iterator + 'ShowStartBtn'] = true;
scope[iterator + 'HideAllStartBtn'] = false;
f = scope[iterator + 'SearchField'] if (f.searchType !== undefined && f.searchType === 'gtzero') {
if (form.related[set].fields[f].searchType && ( form.related[set].fields[f].searchType == 'boolean' scope[iterator + "InputHide"] = true;
|| form.related[set].fields[f].searchType == 'select')) { scope[iterator + 'ShowStartBtn'] = false;
scope[iterator + 'SelectShow'] = true;
scope[iterator + 'SearchSelectOpts'] = list.fields[f].searchOptions;
} }
if (form.related[set].fields[f].searchType && form.related[set].fields[f].searchType == 'gtzero') { if (f.searchType !== undefined && (f.searchType === 'boolean' || f.searchType === 'select')) {
scope[iterator + "InputHide"] = true; scope[iterator + 'SelectShow'] = true;
scope[iterator + 'SearchSelectOpts'] = f.searchOptions;
}
if (f.searchType !== undefined && f.searchType === 'int') {
//scope[iterator + 'HideSearchType'] = true;
scope[iterator + 'SearchType'] = 'int';
} }
}
}
}
setDefaults(); scope.search(iterator);
scope.resetSearch = function(iterator) { };
setDefaults(iterator);
scope.search(iterator);
}
// Functions to handle search widget changes
scope.setSearchField = function(iterator, fld, label) {
for (var related in form.related) {
if ( form.related[related].iterator == iterator ) {
var f = form.related[related].fields[fld];
}
}
scope[iterator + 'SearchFieldLabel'] = label;
scope[iterator + 'SearchField'] = fld;
scope[iterator + 'SearchValue'] = '';
scope[iterator + 'SelectShow'] = false;
//scope[iterator + 'HideSearchType'] = false;
scope[iterator + 'InputHide'] = false;
scope[iterator + 'ShowStartBtn'] = true;
if (f.searchType !== undefined && f.searchType == 'gtzero') { scope.setSearchType = function (model, type, label) {
scope[iterator + "InputHide"] = true; scope[model + 'SearchTypeLabel'] = label;
scope[iterator + 'ShowStartBtn'] = false; scope[model + 'SearchType'] = type;
} scope.search(model);
if (f.searchType !== undefined && (f.searchType == 'boolean' };
|| f.searchType == 'select')) {
scope[iterator + 'SelectShow'] = true;
scope[iterator + 'SearchSelectOpts'] = f.searchOptions;
}
if (f.searchType !== undefined && f.searchType == 'int') {
//scope[iterator + 'HideSearchType'] = true;
scope[model + 'SearchType'] = 'int';
}
scope.search(iterator); scope.startSearch = function (e, iterator) {
// If use clicks enter while on input field, start the search
if (e.keyCode === 13) {
scope.search(iterator);
}
};
} scope.search = function (iterator) {
//scope[iterator + 'SearchSpin'] = true;
Wait('start');
scope[iterator + 'Loading'] = true;
scope[iterator + 'HoldInput'] = true;
scope.setSearchType = function(model, type, label) { if (scope[iterator + 'SearchValue']) {
scope[model + 'SearchTypeLabel'] = label; // User typed a value in input field
scope[model + 'SearchType'] = type; scope[iterator + 'ShowStartBtn'] = false;
scope.search(model); }
}
scope.startSearch = function(e,iterator) { if (iterator === 'host') {
// If use clicks enter while on input field, start the search if (scope.hostSearchField === 'has_active_failures') {
if (e.keyCode == 13) { if (scope.hostSearchSelectValue && scope.hostSearchSelectValue.value === 1) {
scope.search(iterator); scope.hostFailureFilter = true;
} } else {
} scope.hostFailureFilter = false;
}
}
}
scope.search = function(iterator) { var fld, key, set, url, sort_order;
//scope[iterator + 'SearchSpin'] = true; for (key in relatedSets) {
Wait('start'); if (relatedSets[key].iterator === iterator) {
scope[iterator + 'Loading'] = true; set = key;
scope[iterator + 'HoldInput'] = true; url = relatedSets[key].url;
for (fld in form.related[key].fields) {
if (scope[iterator + 'SearchValue']) { if (form.related[key].fields[fld].key) {
// User typed a value in input field if (form.related[key].fields[fld].desc) {
scope[iterator + 'ShowStartBtn'] = false; sort_order = '-' + fld;
} } else {
sort_order = fld;
}
}
}
break;
}
}
if (iterator == 'host') { sort_order = (scope[iterator + 'SortOrder'] === null) ? sort_order : scope[iterator + 'SortOrder'];
if (scope['hostSearchField'] == 'has_active_failures') {
if (scope['hostSearchSelectValue'] && scope['hostSearchSelectValue'].value == 1) {
scope['hostFailureFilter'] = true;
}
else {
scope['hostFailureFilter'] = false;
}
}
}
var set, url, iterator, sort_order; f = form.related[set].fields[scope[iterator + 'SearchField']];
for (var key in relatedSets) { if ((scope[iterator + 'SelectShow'] === false && scope[iterator + 'SearchValue'] !== '' &&
if (relatedSets[key].iterator == iterator) { scope[iterator + 'SearchValue'] !== undefined) || (scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) ||
set = key; (f.searchType && f.searchType === 'gtzero')) {
url = relatedSets[key].url; if (f.sourceModel) {
for (var fld in form.related[key].fields) { // handle fields whose source is a related model e.g. inventories.organization
if (form.related[key].fields[fld].key) { scope[iterator + 'SearchParams'] = f.sourceModel + '__' + f.sourceField + '__';
if (form.related[key].fields[fld].desc) { } else if (f.searchField) {
sort_order = '-' + fld; scope[iterator + 'SearchParams'] = f.searchField + '__';
} } else {
else { scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__';
sort_order = fld; }
}
}
}
break;
}
}
sort_order = (scope[iterator + 'SortOrder'] == null) ? sort_order : scope[iterator + 'SortOrder'];
var f = form.related[set].fields[scope[iterator + 'SearchField']]; if (f.searchType && (f.searchType === 'int' || f.searchType === 'boolean')) {
if ( (scope[iterator + 'SelectShow'] == false && scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) || scope[iterator + 'SearchParams'] += 'int=';
(scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) || (f.searchType && f.searchType == 'gtzero') ) { } else if (f.searchType && f.searchType === 'gtzero') {
if (f.sourceModel) { scope[iterator + 'SearchParams'] += 'gt=0';
// handle fields whose source is a related model e.g. inventories.organization } else {
scope[iterator + 'SearchParams'] = f.sourceModel + '__' + f.sourceField + '__'; scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '=';
} }
else if (f.searchField) {
scope[iterator + 'SearchParams'] = f.searchField + '__'; if (f.searchType && (f.searchType === 'boolean' || f.searchType === 'select')) {
} scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue'].value;
else { } else if (f.searchType === undefined || f.searchType === 'gtzero') {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__'; scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue']);
} }
scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + encodeURI(sort_order) : '';
if ( f.searchType && (f.searchType == 'int' || f.searchType == 'boolean' ) ) { } else {
scope[iterator + 'SearchParams'] += 'int='; scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + encodeURI(sort_order) : '';
} }
else if ( f.searchType && f.searchType == 'gtzero' ) { scope[iterator + 'Page'] = 0;
scope[iterator + 'SearchParams'] += 'gt=0'; url += (url.match(/\/$/)) ? '?' : '&';
} url += scope[iterator + 'SearchParams'];
else { url += (scope[iterator + 'PageSize']) ? '&page_size=' + scope[iterator + 'PageSize'] : "";
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '='; RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
} };
if ( f.searchType && (f.searchType == 'boolean' || f.searchType == 'select') ) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue'].value;
}
else if ( f.searchType == undefined || f.searchType == 'gtzero' ) {
scope[iterator + 'SearchParams'] += escape(scope[iterator + 'SearchValue']);
}
scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + escape(sort_order) : '';
}
else {
scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + escape(sort_order) : '';
}
scope[iterator + 'Page'] = 0;
url += (url.match(/\/$/)) ? '?' : '&';
url += scope[iterator + 'SearchParams'];
url += (scope[iterator + 'PageSize']) ? '&page_size=' + scope[iterator + 'PageSize'] : "";
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
}
scope.sort = function(iterator, fld) { scope.sort = function (iterator, fld) {
var sort_order; var sort_order, icon, direction, set;
// reset sort icons back to 'icon-sort' on all columns // reset sort icons back to 'icon-sort' on all columns
// except the one clicked // except the one clicked
$('.' + iterator + ' .list-header').each(function(index) { $('.' + iterator + ' .list-header').each(function () {
if ($(this).attr('id') != iterator + '-' + fld + '-header') { if ($(this).attr('id') !== iterator + '-' + fld + '-header') {
var icon = $(this).find('i'); var icon = $(this).find('i');
icon.attr('class','icon-sort'); icon.attr('class', 'icon-sort');
} }
}); });
// Toggle the icon for the clicked column
// and set the sort direction
var icon = $('#' + iterator + '-' + fld + '-header i');
var direction = '';
if (icon.hasClass('icon-sort')) {
icon.removeClass('icon-sort');
icon.addClass('icon-sort-up');
}
else if (icon.hasClass('icon-sort-up')) {
icon.removeClass('icon-sort-up');
icon.addClass('icon-sort-down');
direction = '-';
}
else if (icon.hasClass('icon-sort-down')) {
icon.removeClass('icon-sort-down');
icon.addClass('icon-sort-up');
}
// Set the sorder order value and call the API to refresh the list with the new order // Toggle the icon for the clicked column
for (var set in form.related) { // and set the sort direction
if (form.related[set].iterator == iterator) { icon = $('#' + iterator + '-' + fld + '-header i');
if (form.related[set].fields[fld].sourceModel) { direction = '';
sort_order = direction + form.related[set].fields[fld].sourceModel + '__' + if (icon.hasClass('icon-sort')) {
form.related[set].fields[fld].sourceField; icon.removeClass('icon-sort');
} icon.addClass('icon-sort-up');
else { } else if (icon.hasClass('icon-sort-up')) {
sort_order = direction + fld; icon.removeClass('icon-sort-up');
} icon.addClass('icon-sort-down');
} direction = '-';
} } else if (icon.hasClass('icon-sort-down')) {
scope[iterator + 'SortOrder'] = sort_order; icon.removeClass('icon-sort-down');
scope.search(iterator); icon.addClass('icon-sort-up');
} }
// Set the sorder order value and call the API to refresh the list with the new order
for (set in form.related) {
if (form.related[set].iterator === iterator) {
if (form.related[set].fields[fld].sourceModel) {
sort_order = direction + form.related[set].fields[fld].sourceModel + '__' +
form.related[set].fields[fld].sourceField;
} else {
sort_order = direction + fld;
}
}
}
scope[iterator + 'SortOrder'] = sort_order;
scope.search(iterator);
};
};
} }
}]); ]);

View File

@@ -3,7 +3,7 @@
* *
* SearchHelper * SearchHelper
* *
* All the parts for controlling the search widget on * All the parts for controlling the search widget on
* related collections. * related collections.
* *
* SearchInit({ * SearchInit({
@@ -11,26 +11,122 @@
* set: <model name (i.e. organizations) used in ng-repeat> * set: <model name (i.e. organizations) used in ng-repeat>
* url: <default api url used to load data> * url: <default api url used to load data>
* list: <list object used by ListGenerator> * list: <list object used by ListGenerator>
* }); * });
* *
*/ */
'use strict';
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store', .factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store',
function(Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) { function (Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) {
return function(params) { return function (params) {
var scope = params.scope, var scope = params.scope,
set = params.set, set = params.set,
defaultUrl = params.url, defaultUrl = params.url,
list = params.list, list = params.list,
iterator = (params.iterator) ? params.iterator : list.iterator, iterator = (params.iterator) ? params.iterator : list.iterator,
setWidgets = (params.setWidgets === false) ? false : true, setWidgets = (params.setWidgets === false) ? false : true,
sort_order = params.sort_order || '', sort_order = params.sort_order || '',
widgets, i, modifier, current_params; widgets, i, modifier, current_params;
/* function setDefaults(widget) {
// Set default values
var f, fld, fka, modifier;
modifier = (widget === undefined || widget === 1) ? '' : widget;
scope[iterator + 'SearchField' + modifier] = '';
scope[iterator + 'SearchFieldLabel' + modifier] = '';
for (fld in list.fields) {
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
list.fields[fld].searchWidget === widget) {
if (list.fields[fld].key) {
if (list.fields[fld].sourceModel) {
fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
sort_order = (list.fields[fld].desc) ? '-' + fka : fka;
} else {
sort_order = (list.fields[fld].desc) ? '-' + fld : fld;
}
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
}
break;
}
}
}
if (Empty(scope[iterator + 'SearchField' + modifier])) {
// A field marked as key may not be 'searchable'. Find the first searchable field.
for (fld in list.fields) {
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
list.fields[fld].searchWidget === widget) {
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
break;
}
}
}
}
scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'SearchTypeLabel' + modifier] = 'Contains';
scope[iterator + 'SearchParams' + modifier] = '';
scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'SelectShow' + modifier] = false; // show/hide the Select
scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputDisable' + modifier] = false;
scope[iterator + 'ExtraParms' + modifier] = '';
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
// if set to a scope variable
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder];
} else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder;
}
} else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
}
scope[iterator + 'InputDisable' + modifier] =
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject === 'all') ? true : false;
f = scope[iterator + 'SearchField' + modifier];
if (list.fields[f]) {
if (list.fields[f].searchType && (list.fields[f].searchType === 'boolean' ||
list.fields[f].searchType === 'select')) {
scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[f].searchOptions;
}
if (list.fields[f].searchType && list.fields[f].searchType === 'int') {
scope[iterator + 'HideSearchType' + modifier] = true;
}
if (list.fields[f].searchType && list.fields[f].searchType === 'gtzero') {
scope[iterator + 'InputHide' + modifier] = true;
}
}
}
if (setWidgets) {
// Set default values for each search widget on the page
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ($('#search-widget-container' + modifier)) {
setDefaults(i);
}
}
}
current_params = { current_params = {
set: set, set: set,
defaultUrl: defaultUrl, defaultUrl: defaultUrl,
@@ -38,471 +134,351 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
iterator: iterator, iterator: iterator,
sort_order: sort_order sort_order: sort_order
}; };
Store('CurrentSearchParams', current_params); // Save in case Activity Stream widget needs to restore */
function setDefaults(widget) { Store('CurrentSearchParams', current_params); // Save in case Activity Stream widget needs to restore
// Set default values
var f, fld, fka, modifier;
modifier = (widget === undefined || widget === 1) ? '' : widget; // Functions to handle search widget changes
scope[iterator + 'SearchField' + modifier] = ''; scope.setSearchField = function (iterator, fld, label, widget) {
scope[iterator + 'SearchFieldLabel' + modifier] = '';
for (fld in list.fields) { var modifier = (widget === undefined || widget === 1) ? '' : widget;
if (list.fields[fld].searchWidget === undefined && widget === 1 || scope[iterator + 'SearchFieldLabel' + modifier] = label;
list.fields[fld].searchWidget === widget) { scope[iterator + 'SearchField' + modifier] = fld;
if (list.fields[fld].key) { scope[iterator + 'SearchValue' + modifier] = '';
if (list.fields[fld].sourceModel) { scope[iterator + 'SelectShow' + modifier] = false;
fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField; scope[iterator + 'HideSearchType' + modifier] = false;
sort_order = (list.fields[fld].desc) ? '-' + fka : fka; scope[iterator + 'InputHide' + modifier] = false;
} scope[iterator + 'SearchType' + modifier] = 'icontains';
else { scope[iterator + 'InputDisable' + modifier] = (list.fields[fld].searchObject === 'all') ? true : false;
sort_order = (list.fields[fld].desc) ? '-' + fld : fld; scope[iterator + 'ShowStartBtn' + modifier] = true;
}
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) { if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
scope[iterator + 'SearchField' + modifier] = fld; list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label; if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
} // if set to a scope variable
break; scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' +
modifier]].searchPlaceholder];
} else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' +
modifier]].searchPlaceholder;
} }
} else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
} }
}
if (Empty(scope[iterator + 'SearchField' + modifier])) { if (list.fields[fld].searchType && list.fields[fld].searchType === 'gtzero') {
// A field marked as key may not be 'searchable'. Find the first searchable field. scope[iterator + "InputDisable" + modifier] = true;
for (fld in list.fields) { scope[iterator + 'ShowStartBtn' + modifier] = false;
if (list.fields[fld].searchWidget === undefined && widget === 1 || scope.search(iterator);
list.fields[fld].searchWidget === widget) { } else if (list.fields[fld].searchSingleValue) {
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) { // Query a specific attribute for one specific value
scope[iterator + 'SearchField' + modifier] = fld; // searchSingleValue: true
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label; // searchType: 'boolean|int|etc.'
break; // searchValue: < value to match for boolean use 'true'|'false' >
} scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
// For boolean type, SearchValue must be an object
if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'true') {
scope[iterator + "SearchSelectValue" + modifier] = {
value: 1
};
} else if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'false') {
scope[iterator + "SearchSelectValue" + modifier] = {
value: 0
};
} else {
scope[iterator + "SearchSelectValue" + modifier] = {
value: list.fields[fld].searchValue
};
} }
} scope[iterator + 'ShowStartBtn' + modifier] = false;
} } else if (list.fields[fld].searchType === 'in') {
scope[iterator + "SearchType" + modifier] = 'in';
scope[iterator + 'SearchType' + modifier] = 'icontains'; scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
scope[iterator + 'SearchTypeLabel' + modifier] = 'Contains'; scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'SearchParams' + modifier] = ''; scope[iterator + 'ShowStartBtn' + modifier] = false;
scope[iterator + 'SearchValue' + modifier] = ''; } else if (list.fields[fld].searchType && (list.fields[fld].searchType === 'boolean' ||
scope[iterator + 'SelectShow' + modifier] = false; // show/hide the Select list.fields[fld].searchType === 'select' || list.fields[fld].searchType === 'select_or')) {
scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputDisable' + modifier] = false;
scope[iterator + 'ExtraParms' + modifier] = '';
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
// if set to a scope variable
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder];
}
else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder;
}
}
else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
}
scope[iterator + 'InputDisable' + modifier] =
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject === 'all') ? true : false;
f = scope[iterator + 'SearchField' + modifier];
if (list.fields[f]) {
if ( list.fields[f].searchType && (list.fields[f].searchType === 'boolean' ||
list.fields[f].searchType === 'select') ) {
scope[iterator + 'SelectShow' + modifier] = true; scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[f].searchOptions; scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions;
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'int') {
//scope[iterator + 'HideSearchType' + modifier] = true;
scope[iterator + 'SearchType' + modifier] = 'int';
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'isnull') {
scope[iterator + 'SearchType' + modifier] = 'isnull';
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + 'SearchValue' + modifier] = 'true';
scope[iterator + 'ShowStartBtn' + modifier] = false;
} }
if (list.fields[f].searchType && list.fields[f].searchType === 'int') {
scope[iterator + 'HideSearchType' + modifier] = true; scope.search(iterator);
}
if (list.fields[f].searchType && list.fields[f].searchType === 'gtzero') { };
scope[iterator + 'InputHide' + modifier] = true;
} scope.resetSearch = function (iterator) {
} // Respdond to click of reset button
} var i,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
if (setWidgets) {
// Set default values for each search widget on the page for (i = 1; i <= widgets; i++) {
widgets = (list.searchWidgets) ? list.searchWidgets : 1; // Clear each search widget
for (i=1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ( $('#search-widget-container' + modifier) ) {
setDefaults(i); setDefaults(i);
} }
} // Force removal of search keys from the URL
} window.location = '/#' + $location.path();
current_params = {
set: set,
defaultUrl: defaultUrl,
list: list,
iterator: iterator,
sort_order: sort_order
};
Store('CurrentSearchParams', current_params); // Save in case Activity Stream widget needs to restore
// Functions to handle search widget changes
scope.setSearchField = function(iterator, fld, label, widget) {
var modifier = (widget === undefined || widget === 1) ? '' : widget;
scope[iterator + 'SearchFieldLabel' + modifier] = label;
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'SelectShow' + modifier] = false;
scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputHide' + modifier] = false;
scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'InputDisable' + modifier] = (list.fields[fld].searchObject === 'all') ? true : false;
scope[iterator + 'ShowStartBtn' + modifier] = true;
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
// if set to a scope variable
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder];
}
else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder;
}
}
else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
}
if (list.fields[fld].searchType && list.fields[fld].searchType === 'gtzero') {
scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'ShowStartBtn' + modifier] = false;
scope.search(iterator); scope.search(iterator);
} };
else if (list.fields[fld].searchSingleValue){
// Query a specific attribute for one specific value
// searchSingleValue: true
// searchType: 'boolean|int|etc.'
// searchValue: < value to match for boolean use 'true'|'false' >
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
// For boolean type, SearchValue must be an object
if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'true') {
scope[iterator + "SearchSelectValue" + modifier] = { value: 1 };
}
else if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'false') {
scope[iterator + "SearchSelectValue" + modifier] = { value: 0 };
}
else {
scope[iterator + "SearchSelectValue" + modifier] = { value: list.fields[fld].searchValue };
}
scope[iterator + 'ShowStartBtn' + modifier] = false;
}
else if (list.fields[fld].searchType === 'in') {
scope[iterator + "SearchType" + modifier] = 'in';
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'ShowStartBtn' + modifier] = false;
}
else if (list.fields[fld].searchType && (list.fields[fld].searchType === 'boolean' ||
list.fields[fld].searchType === 'select' || list.fields[fld].searchType === 'select_or')) {
scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions;
}
else if (list.fields[fld].searchType && list.fields[fld].searchType === 'int') {
//scope[iterator + 'HideSearchType' + modifier] = true;
scope[iterator + 'SearchType' + modifier] = 'int';
}
else if (list.fields[fld].searchType && list.fields[fld].searchType === 'isnull') {
scope[iterator + 'SearchType' + modifier] = 'isnull';
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + 'SearchValue' + modifier] = 'true';
scope[iterator + 'ShowStartBtn' + modifier] = false;
}
scope.search(iterator);
};
scope.resetSearch = function(iterator) { if (scope.removeDoSearch) {
// Respdond to click of reset button scope.removeDoSearch();
var i,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i=1; i <= widgets; i++) {
// Clear each search widget
setDefaults(i);
} }
// Force removal of search keys from the URL scope.removeDoSearch = scope.$on('doSearch', function (e, iterator, page, load) {
window.location = '/#' + $location.path(); //
scope.search(iterator); // Execute the search
}; //
scope[iterator + 'Loading'] = (load === undefined || load === true) ? true : false;
if (scope.removeDoSearch) { var url = defaultUrl,
scope.removeDoSearch(); connect;
}
scope.removeDoSearch = scope.$on('doSearch', function(e, iterator, page, load) {
//
// Execute the search
//
scope[iterator + 'Loading'] = (load === undefined || load === true) ? true : false;
var url = defaultUrl, connect;
//finalize and execute the query //finalize and execute the query
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0; scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
if (scope[iterator + 'SearchParams']) { if (scope[iterator + 'SearchParams']) {
if (/\/$/.test(url)) { if (/\/$/.test(url)) {
url += '?' + scope[iterator + 'SearchParams']; url += '?' + scope[iterator + 'SearchParams'];
} else {
url += '&' + scope[iterator + 'SearchParams'];
}
} }
else {
url += '&' + scope[iterator + 'SearchParams'];
}
}
connect = (/\/$/.test(url)) ? '?' : '&';
url += (scope[iterator + '_page_size']) ? connect + 'page_size=' + scope[iterator + '_page_size'] : "";
if (page) {
connect = (/\/$/.test(url)) ? '?' : '&'; connect = (/\/$/.test(url)) ? '?' : '&';
url += connect + 'page=' + page; url += (scope[iterator + '_page_size']) ? connect + 'page_size=' + scope[iterator + '_page_size'] : "";
} if (page) {
if (scope[iterator + 'ExtraParms']) { connect = (/\/$/.test(url)) ? '?' : '&';
connect = (/\/$/.test(url)) ? '?' : '&'; url += connect + 'page=' + page;
url += connect + scope[iterator + 'ExtraParms'];
}
url = url.replace(/\&\&/,'&');
Refresh({ scope: scope, set: set, iterator: iterator, url: url });
});
if (scope.removePrepareSearch) {
scope.removePrepareSearch();
}
scope.removePrepareSearch = scope.$on('prepareSearch', function(e, iterator, page, load, spin) {
//
// Start building the search key/value pairs. This will process each search widget, if the
// selected field is an object type (used on activity stream).
//
Wait('start');
scope[iterator + 'SearchParams'] = '';
var i, modifier,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i=1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ( $('#search-widget-container' + modifier) ) {
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
// Search field of object type
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchObject !== 'all') {
// An object type is selected
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (scope[iterator + 'SearchValue' + modifier]) {
// A search value was entered
scope[iterator + 'ShowStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchOnID) {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__id=' + scope[iterator + 'SearchValue' + modifier];
}
else {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__name__icontains=' +
scope[iterator + 'SearchValue' + modifier];
}
}
else {
// Search value is empty
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchField +
'=' + list.fields[scope[iterator + 'SearchField' + modifier]].searchObject;
}
}
else {
// Object Type set to All
scope[iterator + 'HideAllStartBtn' + modifier] = true;
}
}
} }
} if (scope[iterator + 'ExtraParms']) {
scope.$emit('prepareSearch2', iterator, page, load, spin); connect = (/\/$/.test(url)) ? '?' : '&';
}); url += connect + scope[iterator + 'ExtraParms'];
if (scope.removePrepareSearch2) {
scope.removePrepareSearch2();
}
scope.removePrepareSearch2 = scope.$on('prepareSearch2', function(e, iterator, page, load, spin) {
// Continue building the search by examining the remaining search widgets. If we're looking at activity_stream,
// there's more than one.
var i, modifier,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i=1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
scope[iterator + 'HoldInput' + modifier] = true;
if ($('#search-widget-container' + modifier) &&
list.fields[scope[iterator + 'SearchField' + modifier]] &&
!list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
// if the search widget exists and its value is not an object, add its parameters to the query
if (scope[iterator + 'SearchValue' + modifier]) {
// if user typed a value in the input box, show the reset link
scope[iterator + 'ShowStartBtn' + modifier] = false;
}
else {
scope[iterator + 'ShowStartBtn' + modifier] = true;
}
if ( (!scope[iterator + 'SelectShow' + modifier] && !Empty(scope[iterator + 'SearchValue' + modifier])) ||
(scope[iterator + 'SelectShow' + modifier] && scope[iterator + 'SearchSelectValue' + modifier]) ||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero') ) {
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchField) {
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '__';
}
else if (list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__';
}
else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value === '' ||
scope[iterator + 'SearchSelectValue' + modifier].value === null) ) {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
}
else {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
}
if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'int' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean' ) ) {
scope[iterator + 'SearchParams'] += 'int=';
}
else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero' ) {
scope[iterator + 'SearchParams'] += 'gt=0';
}
else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value === '' ||
scope[iterator + 'SearchSelectValue' + modifier].value === null) ) {
scope[iterator + 'SearchParams'] += 'iexact=';
}
else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '=';
}
if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') ) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue' + modifier].value;
}
else {
if ( (!list.fields[scope[iterator + 'SearchField' + modifier]].searchType) ||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'or' &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'gtzero') ) {
scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue' + modifier]);
}
}
}
}
}
if ( (iterator === 'inventory' && scope.inventoryFailureFilter) ||
(iterator === 'host' && scope.hostFailureFilter) ) {
//Things that bypass the search widget. Should go back and add a second widget possibly on
//inventory pages and eliminate this
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
}
if (sort_order) {
scope[iterator + 'SearchParams'] += (scope[iterator + 'SearchParams']) ? '&' : '';
scope[iterator + 'SearchParams'] += 'order_by=' + encodeURI(sort_order);
}
scope.$emit('doSearch', iterator, page, load, spin);
});
scope.startSearch = function(e,iterator) {
// If use clicks enter while on input field, start the search
if (e.keyCode === 13) {
scope.search(iterator);
}
};
scope.search = function(iterator, page, load) {
// Called to initiate a searh.
// Page is optional. Added to accomodate back function on Job Events detail.
// Spin optional -set to false if spin not desired.
// Load optional -set to false if loading message not desired
load = (load === undefined) ? true : false;
if (load) {
scope[set] = [];
}
scope.$emit('prepareSearch', iterator, page, load);
};
scope.sort = function(fld) {
// reset sort icons back to 'icon-sort' on all columns
// except the one clicked
$('.list-header').each(function() {
if ($(this).attr('id') !== fld + '-header') {
var icon = $(this).find('i');
icon.attr('class','fa fa-sort');
} }
url = url.replace(/\&\&/, '&');
Refresh({
scope: scope,
set: set,
iterator: iterator,
url: url
});
}); });
// Toggle the icon for the clicked column
// and set the sort direction
var icon = $('#' + fld + '-header i'),
direction = '';
if (icon.hasClass('fa-sort')) {
icon.removeClass('fa-sort');
icon.addClass('fa-sort-up');
}
else if (icon.hasClass('fa-sort-up')) {
icon.removeClass('fa-sort-up');
icon.addClass('fa-sort-down');
direction = '-';
}
else if (icon.hasClass('fa-sort-down')) {
icon.removeClass('fa-sort-down');
icon.addClass('fa-sort-up');
}
// Set the sorder order value and call the API to refresh the list with the new order
if (list.fields[fld].searchField) { if (scope.removePrepareSearch) {
sort_order = direction + list.fields[fld].searchField; scope.removePrepareSearch();
} }
else if (list.fields[fld].sortField) { scope.removePrepareSearch = scope.$on('prepareSearch', function (e, iterator, page, load, spin) {
sort_order = direction + list.fields[fld].sortField; //
} // Start building the search key/value pairs. This will process each search widget, if the
else { // selected field is an object type (used on activity stream).
if (list.fields[fld].sourceModel) { //
sort_order = direction + list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField; Wait('start');
} scope[iterator + 'SearchParams'] = '';
else { var i, modifier,
sort_order = direction + fld; widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ($('#search-widget-container' + modifier)) {
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
// Search field of object type
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchObject !== 'all') {
// An object type is selected
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (scope[iterator + 'SearchValue' + modifier]) {
// A search value was entered
scope[iterator + 'ShowStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchOnID) {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__id=' + scope[iterator + 'SearchValue' + modifier];
} else {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__name__icontains=' +
scope[iterator + 'SearchValue' + modifier];
}
} else {
// Search value is empty
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchField +
'=' + list.fields[scope[iterator + 'SearchField' + modifier]].searchObject;
}
} else {
// Object Type set to All
scope[iterator + 'HideAllStartBtn' + modifier] = true;
}
}
}
} }
scope.$emit('prepareSearch2', iterator, page, load, spin);
});
if (scope.removePrepareSearch2) {
scope.removePrepareSearch2();
} }
scope.search(list.iterator); scope.removePrepareSearch2 = scope.$on('prepareSearch2', function (e, iterator, page, load, spin) {
}; // Continue building the search by examining the remaining search widgets. If we're looking at activity_stream,
// there's more than one.
var i, modifier,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
// Call after modal dialogs to remove any lingering callbacks for (i = 1; i <= widgets; i++) {
scope.searchCleanup = function() { modifier = (i === 1) ? '' : i;
scope.removeDoSearch(); scope[iterator + 'HoldInput' + modifier] = true;
scope.removePrepareSearch(); if ($('#search-widget-container' + modifier) &&
scope.removePrepareSearch2(); list.fields[scope[iterator + 'SearchField' + modifier]] && !list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
};
}; // if the search widget exists and its value is not an object, add its parameters to the query
}]);
if (scope[iterator + 'SearchValue' + modifier]) {
// if user typed a value in the input box, show the reset link
scope[iterator + 'ShowStartBtn' + modifier] = false;
} else {
scope[iterator + 'ShowStartBtn' + modifier] = true;
}
if ((!scope[iterator + 'SelectShow' + modifier] && !Empty(scope[iterator + 'SearchValue' + modifier])) ||
(scope[iterator + 'SelectShow' + modifier] && scope[iterator + 'SearchSelectValue' + modifier]) ||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero')) {
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchField) {
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '__';
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__';
} else if ((list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value === '' ||
scope[iterator + 'SearchSelectValue' + modifier].value === null)) {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
} else {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
}
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'int' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean')) {
scope[iterator + 'SearchParams'] += 'int=';
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero') {
scope[iterator + 'SearchParams'] += 'gt=0';
} else if ((list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value === '' ||
scope[iterator + 'SearchSelectValue' + modifier].value === null)) {
scope[iterator + 'SearchParams'] += 'iexact=';
} else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '=';
}
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select')) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue' + modifier].value;
} else {
if ((!list.fields[scope[iterator + 'SearchField' + modifier]].searchType) ||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'or' &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'gtzero')) {
scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue' + modifier]);
}
}
}
}
}
if ((iterator === 'inventory' && scope.inventoryFailureFilter) ||
(iterator === 'host' && scope.hostFailureFilter)) {
//Things that bypass the search widget. Should go back and add a second widget possibly on
//inventory pages and eliminate this
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
}
if (sort_order) {
scope[iterator + 'SearchParams'] += (scope[iterator + 'SearchParams']) ? '&' : '';
scope[iterator + 'SearchParams'] += 'order_by=' + encodeURI(sort_order);
}
scope.$emit('doSearch', iterator, page, load, spin);
});
scope.startSearch = function (e, iterator) {
// If use clicks enter while on input field, start the search
if (e.keyCode === 13) {
scope.search(iterator);
}
};
scope.search = function (iterator, page, load) {
// Called to initiate a searh.
// Page is optional. Added to accomodate back function on Job Events detail.
// Spin optional -set to false if spin not desired.
// Load optional -set to false if loading message not desired
load = (load === undefined) ? true : false;
if (load) {
scope[set] = [];
}
scope.$emit('prepareSearch', iterator, page, load);
};
scope.sort = function (fld) {
// reset sort icons back to 'icon-sort' on all columns
// except the one clicked
$('.list-header').each(function () {
if ($(this).attr('id') !== fld + '-header') {
var icon = $(this).find('i');
icon.attr('class', 'fa fa-sort');
}
});
// Toggle the icon for the clicked column
// and set the sort direction
var icon = $('#' + fld + '-header i'),
direction = '';
if (icon.hasClass('fa-sort')) {
icon.removeClass('fa-sort');
icon.addClass('fa-sort-up');
} else if (icon.hasClass('fa-sort-up')) {
icon.removeClass('fa-sort-up');
icon.addClass('fa-sort-down');
direction = '-';
} else if (icon.hasClass('fa-sort-down')) {
icon.removeClass('fa-sort-down');
icon.addClass('fa-sort-up');
}
// Set the sorder order value and call the API to refresh the list with the new order
if (list.fields[fld].searchField) {
sort_order = direction + list.fields[fld].searchField;
} else if (list.fields[fld].sortField) {
sort_order = direction + list.fields[fld].sortField;
} else {
if (list.fields[fld].sourceModel) {
sort_order = direction + list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
} else {
sort_order = direction + fld;
}
}
scope.search(list.iterator);
};
// Call after modal dialogs to remove any lingering callbacks
scope.searchCleanup = function () {
scope.removeDoSearch();
scope.removePrepareSearch();
scope.removePrepareSearch2();
};
};
}
]);

View File

@@ -2,106 +2,126 @@
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* TeamHelper * TeamHelper
* Routines shared amongst the team controllers * Routines shared amongst the team controllers
*/ */
angular.module('TeamHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginationHelpers', 'ListGenerator' ])
.factory('SetTeamListeners', ['Alert', 'Rest', function(Alert, Rest) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
// Listeners to perform lookups after main inventory list loads 'use strict';
scope.$on('TeamResultFound', function(e, results, lookup_results) { angular.module('TeamHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'SearchHelper',
if ( lookup_results.length == results.length ) { 'PaginationHelpers', 'ListGenerator'
key = 'organization'; ])
property = 'organization_name'; .factory('SetTeamListeners', ['Alert', 'Rest',
for (var i=0; i < results.length; i++) { function (Alert, Rest) {
for (var j=0; j < lookup_results.length; j++) { return function (params) {
if (results[i][key] == lookup_results[j].id) {
results[i][property] = lookup_results[j].value;
}
}
}
scope[iterator + 'SearchSpin'] = false;
scope[set] = results;
}
});
scope.$on('TeamRefreshFinished', function(e, results) { var scope = params.scope,
// Loop through the result set (sent to us by the search helper) and set = params.set,
// lookup the id and name of each organization. After each lookup iterator = params.iterator;
// completes, call resultFound.
var lookup_results = [];
for (var i = 0; i < results.length; i++) {
Rest.setUrl('/api/v1/organizations/' + results[i].organization + '/');
Rest.get()
.success( function( data, status, headers, config) {
lookup_results.push({ id: data.id, value: data.name });
scope.$emit('TeamResultFound', results, lookup_results);
})
.error( function( data, status, headers, config) {
lookup_results.push({ id: 'error' });
scope.$emit('TeamResultFound', results, lookup_results);
});
}
});
}
}])
.factory('TeamLookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'GenerateList', 'SearchInit', 'PaginateInit',
function(Alert, Rest, OrganizationList, GenerateList, SearchInit, PaginateInit) {
return function(params) {
var scope = params.scope;
// Show pop-up to select organization // Listeners to perform lookups after main inventory list loads
scope.lookUpOrganization = function() {
var list = OrganizationList;
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Organization' });
var defaultUrl = '/api/v1/organizations/';
listScope.selectAction = function() { scope.$on('TeamResultFound', function (e, results, lookup_results) {
var found = false; var i, j, key, property;
for (var i=0; i < listScope[list.name].length; i++) { if (lookup_results.length === results.length) {
if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] == "success") { key = 'organization';
found = true; property = 'organization_name';
scope['organization'] = listScope[list.name][i].id; for (i = 0; i < results.length; i++) {
scope['organization_name'] = listScope[list.name][i].name; for (j = 0; j < lookup_results.length; j++) {
scope['team_form'].$setDirty(); if (results[i][key] === lookup_results[j].id) {
listGenerator.hide(); results[i][property] = lookup_results[j].value;
}
}
}
scope[iterator + 'SearchSpin'] = false;
scope[set] = results;
} }
} });
if (found == false) {
Alert('No Selection', 'Click on a row to select an Organization before clicking the Select button.');
}
}
listScope.toggle_organization = function(id) { scope.$on('TeamRefreshFinished', function (e, results) {
// when user clicks a row, remove 'success' class from all rows except clicked-on row // Loop through the result set (sent to us by the search helper) and
if (listScope[list.name]) { // lookup the id and name of each organization. After each lookup
for (var i=0; i < listScope[list.name].length; i++) { // completes, call resultFound.
listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = "";
}
}
if (id != null && id != undefined) {
listScope[list.iterator + "_" + id + "_class"] = "success";
}
}
SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl }); var i, lookup_results = [], url;
PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' });
scope.search(list.iterator); function getOrganization(url) {
listScope.toggle_organization(scope.organization); Rest.setUrl(url);
} Rest.get()
.success(function (data) {
lookup_results.push({ id: data.id, value: data.name });
scope.$emit('TeamResultFound', results, lookup_results);
})
.error(function () {
lookup_results.push({ id: 'error' });
scope.$emit('TeamResultFound', results, lookup_results);
});
}
for (i = 0; i < results.length; i++) {
url = '/api/v1/organizations/' + results[i].organization + '/';
getOrganization(url);
}
});
};
} }
}]); ])
.factory('TeamLookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'GenerateList', 'SearchInit', 'PaginateInit',
function (Alert, Rest, OrganizationList, GenerateList, SearchInit, PaginateInit) {
return function (params) {
var scope = params.scope;
// Show pop-up to select organization
scope.lookUpOrganization = function () {
var list = OrganizationList,
listGenerator = GenerateList,
listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Organization' }),
defaultUrl = '/api/v1/organizations/';
listScope.selectAction = function () {
var i, found = false;
for (i = 0; i < listScope[list.name].length; i++) {
if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] === "success") {
found = true;
scope.organization = listScope[list.name][i].id;
scope.organization_name = listScope[list.name][i].name;
scope.team_form.$setDirty();
listGenerator.hide();
}
}
if (found === false) {
Alert('No Selection', 'Click on a row to select an Organization before clicking the Select button.');
}
};
listScope.toggle_organization = function (id) {
// when user clicks a row, remove 'success' class from all rows except clicked-on row
if (listScope[list.name]) {
for (var i = 0; i < listScope[list.name].length; i++) {
listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = "";
}
}
if (id !== null && id !== undefined) {
listScope[list.iterator + "_" + id + "_class"] = "success";
}
};
SearchInit({
scope: listScope,
set: list.name,
list: list,
url: defaultUrl
});
PaginateInit({
scope: listScope,
list: list,
url: defaultUrl,
mode: 'lookup'
});
scope.search(list.iterator);
listScope.toggle_organization(scope.organization);
};
};
}
]);

View File

@@ -1,41 +1,41 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Admins.js * Admins.js
* List view object for Admins data model. * List view object for Admins data model.
* *
* @dict * @dict
*/ */
'use strict';
angular.module('AdminListDefinition', []) angular.module('AdminListDefinition', [])
.value( .value('AdminList', {
'AdminList', {
name: 'admins', name: 'admins',
iterator: 'admin', iterator: 'admin',
selectTitle: 'Add Administrators', selectTitle: 'Add Administrators',
editTitle: 'Admins', editTitle: 'Admins',
selectInstructions: '<p>Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' + selectInstructions: '<p>Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' +
'<em>Select</em> button, located bottom right.</p>', '<em>Select</em> button, located bottom right.</p>',
base: 'users', base: 'users',
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
username: { username: {
key: true, key: true,
label: 'Username' label: 'Username'
}, },
first_name: { first_name: {
label: 'First Name' label: 'First Name'
}, },
last_name: { last_name: {
label: 'Last Name' label: 'Last Name'
}
},
actions: {
},
fieldActions: {
} }
}); },
actions: {},
fieldActions: {}
});

View File

@@ -1,15 +1,17 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* CloudCredentials.js * CloudCredentials.js
* List view object for Credential data model. * List view object for Credential data model.
* *
* @dict * @dict
*/ */
'use strict';
angular.module('CloudCredentialsListDefinition', []) angular.module('CloudCredentialsListDefinition', [])
.value( .value('CloudCredentialList', {
'CloudCredentialList', {
name: 'cloudcredentials', name: 'cloudcredentials',
iterator: 'cloudcredential', iterator: 'cloudcredential',
selectTitle: 'Add Cloud Credentials', selectTitle: 'Add Cloud Credentials',
@@ -18,58 +20,58 @@ angular.module('CloudCredentialsListDefinition', [])
'<em>Select</em> button, located bottom right.</p> <p>Create a brand new credential by clicking the green <em>Create New</em> button.</p>', '<em>Select</em> button, located bottom right.</p> <p>Create a brand new credential by clicking the green <em>Create New</em> button.</p>',
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description', label: 'Description',
excludeModal: false excludeModal: false
}, },
team: { team: {
label: 'Team', label: 'Team',
ngBind: 'credential.team_name', ngBind: 'credential.team_name',
sourceModel: 'team', sourceModel: 'team',
sourceField: 'name', sourceField: 'name',
excludeModal: true excludeModal: true
}, },
user: { user: {
label: 'User', label: 'User',
ngBind: 'credential.user_username', ngBind: 'credential.user_username',
sourceModel: 'user', sourceModel: 'user',
sourceField: 'username', sourceField: 'username',
excludeModal: true excludeModal: true
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()', ngClick: 'addCredential()',
"class": 'btn-sm', "class": 'btn-sm',
awToolTip: 'Create a new credential' awToolTip: 'Create a new credential'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
ngClick: "editCredential(\{\{ credential.id \}\})", ngClick: "editCredential(credential.id)",
icon: 'fa-edit', icon: 'fa-edit',
label: 'Edit', label: 'Edit',
"class": 'btn-sm', "class": 'btn-sm',
awToolTip: 'Edit credential', awToolTip: 'Edit credential',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')", ngClick: "deleteCredential(credential.id, credential.name)",
icon: 'fa-trash-o', icon: 'fa-trash-o',
label: 'Delete', label: 'Delete',
"class": 'btn-sm', "class": 'btn-sm',
awToolTip: 'Delete credential', awToolTip: 'Delete credential',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,88 +1,75 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Credentials.js * Credentials.js
* List view object for Credential data model. * List view object for Credential data model.
* *
* @dict * @dict
*/ */
'use strict';
angular.module('CredentialsListDefinition', []) angular.module('CredentialsListDefinition', [])
.value( .value('CredentialList', {
'CredentialList', {
name: 'credentials', name: 'credentials',
iterator: 'credential', iterator: 'credential',
selectTitle: 'Add Credentials', selectTitle: 'Add Credentials',
editTitle: 'Credentials', editTitle: 'Credentials',
selectInstructions: '<p>Select existing credentials by clicking each credential or checking the related checkbox. When finished, click the blue ' + selectInstructions: "<p>Select existing credentials by clicking each credential or checking the related checkbox. When " +
'<em>Select</em> button, located bottom right.</p> <p>Create a brand new credential by clicking the green <em>Create New</em> button.</p>', "finished, click the blue <em>Select</em> button, located bottom right.</p> <p>Create a brand new credential by clicking " +
"the green <em>Create New</em> button.</p>",
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description', label: 'Description',
excludeModal: false excludeModal: false
}, },
kind: { kind: {
label: 'Type', label: 'Type',
searchType: 'select', searchType: 'select',
searchOptions: [], // will be set by Options call to credentials resource searchOptions: [], // will be set by Options call to credentials resource
excludeModal: true, excludeModal: true,
nosort: true nosort: true
} }
/* },
team: {
label: 'Team',
ngBind: 'credential.team_name',
sourceModel: 'team',
sourceField: 'name',
excludeModal: true
},
user: {
label: 'User',
ngBind: 'credential.user_username',
sourceModel: 'user',
sourceField: 'username',
excludeModal: true
}
*/
},
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()', ngClick: 'addCredential()',
awToolTip: 'Create a new credential' awToolTip: 'Create a new credential'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
ngClick: "editCredential(\{\{ credential.id \}\})", ngClick: "editCredential(credential.id)",
icon: 'fa-edit', icon: 'fa-edit',
label: 'Edit', label: 'Edit',
"class": 'btn-sm', "class": 'btn-sm',
awToolTip: 'Edit credential', awToolTip: 'Edit credential',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')", ngClick: "deleteCredential(credential.id, credential.name)",
icon: 'fa-trash', icon: 'fa-trash',
label: 'Delete', label: 'Delete',
"class": 'btn-sm', "class": 'btn-sm',
awToolTip: 'Delete credential', awToolTip: 'Delete credential',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,60 +1,62 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Groups.js * Groups.js
* List view object for Group data model. * List view object for Group data model.
* *
* *
*/ */
'use strict';
angular.module('GroupListDefinition', []) angular.module('GroupListDefinition', [])
.value( .value('GroupList', {
'GroupList', {
name: 'groups', name: 'groups',
iterator: 'group', iterator: 'group',
selectTitle: 'Copy Groups', selectTitle: 'Copy Groups',
editTitle: 'Groups', editTitle: 'Groups',
index: true, index: true,
well: false, well: false,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
actions: { actions: {
help: { help: {
awPopOver: "Choose groups by clicking on each group you wish to add. Click the <em>Select</em> button to add the groups to " + awPopOver: "Choose groups by clicking on each group you wish to add. Click the <em>Select</em> button to add the groups to " +
"the selected inventory group.", "the selected inventory group.",
dataContainer: '#form-modal .modal-content', dataContainer: '#form-modal .modal-content',
mode: 'all', mode: 'all',
awToolTip: 'Click for help', awToolTip: 'Click for help',
dataTitle: 'Adding Groups' dataTitle: 'Adding Groups'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editGroup(\{\{ group.id \}\})", ngClick: "editGroup(group.id)",
icon: 'icon-edit', icon: 'icon-edit',
"class": 'btn-xs', "class": 'btn-xs',
awToolTip: 'Edit group', awToolTip: 'Edit group',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteGroup(\{\{ group.id \}\},'\{\{ group.name \}\}')", ngClick: "deleteGroup(group.id, group.name)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-xs', "class": 'btn-xs',
awToolTip: 'Delete group', awToolTip: 'Delete group',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,16 +1,18 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* HomeGroups.js * HomeGroups.js
* *
* List view object for Group data model. Used * List view object for Group data model. Used
* on the home tab. * on the home tab.
* *
*/ */
'use strict';
angular.module('HomeGroupListDefinition', []) angular.module('HomeGroupListDefinition', [])
.value( .value('HomeGroupList', {
'HomeGroupList', {
name: 'home_groups', name: 'home_groups',
iterator: 'group', iterator: 'group',
editTitle: 'Groups', editTitle: 'Groups',
@@ -24,40 +26,46 @@ angular.module('HomeGroupListDefinition', [])
label: 'Group', label: 'Group',
ngClick: "editGroup(group.id, group.inventory)", ngClick: "editGroup(group.id, group.inventory)",
columnClass: 'col-lg-4 col-md3 col-sm-3 col-xs-6 ellipsis' columnClass: 'col-lg-4 col-md3 col-sm-3 col-xs-6 ellipsis'
}, },
inventory_name: { inventory_name: {
label: 'Inventory', label: 'Inventory',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
columnClass: 'col-lg-3 col-md2 col-sm-2 hidden-xs elllipsis', columnClass: 'col-lg-3 col-md2 col-sm-2 hidden-xs elllipsis',
linkTo: "\{\{ '/#/inventories/' + group.inventory + '/' \}\}" linkTo: "{{ /#/inventories/' + group.inventory + '/' }}"
}, },
source: { source: {
label: 'Source', label: 'Source',
searchType: 'select', searchType: 'select',
searchOptions: [ searchOptions: [{
{ name: "ec2", value: "ec2" }, name: "ec2",
{ name: "none", value: "" }, value: "ec2"
{ name: "rax", value: "rax" }], }, {
name: "none",
value: ""
}, {
name: "rax",
value: "rax"
}],
sourceModel: 'inventory_source', sourceModel: 'inventory_source',
sourceField: 'source', sourceField: 'source',
searchOnly: true searchOnly: true
}, },
has_external_source: { has_external_source: {
label: 'Has external source?', label: 'Has external source?',
searchType: 'in', searchType: 'in',
searchValue: 'ec2,rax', searchValue: 'ec2,rax',
searchOnly: true, searchOnly: true,
sourceModel: 'inventory_source', sourceModel: 'inventory_source',
sourceField: 'source' sourceField: 'source'
}, },
has_active_failures: { has_active_failures: {
label: 'Has failed hosts?', label: 'Has failed hosts?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
last_update_failed: { last_update_failed: {
label: 'Update failed?', label: 'Update failed?',
searchType: 'select', searchType: 'select',
@@ -66,66 +74,66 @@ angular.module('HomeGroupListDefinition', [])
searchOnly: true, searchOnly: true,
sourceModel: 'inventory_source', sourceModel: 'inventory_source',
sourceField: 'status' sourceField: 'status'
}, },
id: { id: {
label: 'ID', label: 'ID',
searchOnly: true searchOnly: true
} }
}, },
fieldActions: { fieldActions: {
sync_status: { sync_status: {
mode: 'all', mode: 'all',
ngClick: "viewUpdateStatus(group.id, group.group_id)", ngClick: "viewUpdateStatus(group.id, group.group_id)",
awToolTip: "\{\{ group.status_tooltip \}\}", awToolTip: "{{ group.status_tooltip }}",
ngClass: "group.status_class", ngClass: "group.status_class",
dataPlacement: "top" dataPlacement: "top"
}, },
failed_hosts: { failed_hosts: {
mode: 'all', mode: 'all',
awToolTip: "{{ group.hosts_status_tip }}", awToolTip: "{{ group.hosts_status_tip }}",
dataPlacement: "top", dataPlacement: "top",
ngHref: "/#/inventories/{{ group.inventory }}/", ngHref: "/#/inventories/{{ group.inventory }}/",
iconClass: "{{ 'fa icon-failures-' + group.hosts_status_class }}" iconClass: "{{ 'fa icon-failures-' + group.hosts_status_class }}"
}, },
group_update: { group_update: {
//label: 'Sync', //label: 'Sync',
mode: 'all', mode: 'all',
ngClick: 'updateGroup(\{\{ group.id \}\})', ngClick: 'updateGroup(group.id)',
awToolTip: "\{\{ group.launch_tooltip \}\}", awToolTip: "{{ group.launch_tooltip }}",
ngShow: "(group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating')", ngShow: "(group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating')",
ngClass: "group.launch_class", ngClass: "group.launch_class",
dataPlacement: "top" dataPlacement: "top"
}, },
cancel: { cancel: {
//label: 'Cancel', //label: 'Cancel',
mode: 'all', mode: 'all',
ngClick: "cancelUpdate(\{\{ group.id \}\})", ngClick: "cancelUpdate(group.id)",
awToolTip: "Cancel sync process", awToolTip: "Cancel sync process",
'class': 'red-txt', 'class': 'red-txt',
ngShow: "(group.status == 'running' || group.status == 'pending' || group.status == 'updating')", ngShow: "(group.status == 'running' || group.status == 'pending' || group.status == 'updating')",
dataPlacement: "top" dataPlacement: "top"
}, },
edit: { edit: {
label: 'Edit', label: 'Edit',
mode: 'all', mode: 'all',
ngClick: "editGroup(group.id)", ngClick: "editGroup(group.id)",
awToolTip: 'Edit group', awToolTip: 'Edit group',
dataPlacement: "top" dataPlacement: "top"
} }
}, },
actions: { actions: {
refresh: { refresh: {
mode: 'all', mode: 'all',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()" ngClick: "refresh()"
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
}
} }
}
}); });

View File

@@ -1,16 +1,18 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* HomeHosts.js * HomeHosts.js
* *
* List view object for Hosts data model. Used * List view object for Hosts data model. Used
* on the home tab. * on the home tab.
* *
*/ */
'use strict';
angular.module('HomeHostListDefinition', []) angular.module('HomeHostListDefinition', [])
.value( .value('HomeHostList', {
'HomeHostList', {
name: 'hosts', name: 'hosts',
iterator: 'host', iterator: 'host',
selectTitle: 'Add Existing Hosts', selectTitle: 'Add Existing Hosts',
@@ -24,41 +26,41 @@ angular.module('HomeHostListDefinition', [])
key: true, key: true,
label: 'Name', label: 'Name',
columnClass: 'col-lg-4 col-md3 col-sm-3 col-xs-7 ellipsis', columnClass: 'col-lg-4 col-md3 col-sm-3 col-xs-7 ellipsis',
ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')" ngClick: "editHost(host.id, host.name)"
}, },
inventory_name: { inventory_name: {
label: 'Inventory', label: 'Inventory',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
columnClass: 'col-lg-3 col-md2 col-sm-2 hidden-xs elllipsis', columnClass: 'col-lg-3 col-md2 col-sm-2 hidden-xs elllipsis',
linkTo: "\{\{ '/#/inventories/' + host.inventory \}\}" linkTo: "{{ '/#/inventories/' + host.inventory }}"
}, },
enabled: { enabled: {
label: 'Disabled?', label: 'Disabled?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'false', searchValue: 'false',
searchOnly: true searchOnly: true
}, },
has_active_failures: { has_active_failures: {
label: 'Has failed jobs?', label: 'Has failed jobs?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
has_inventory_sources: { has_inventory_sources: {
label: 'Has external source?', label: 'Has external source?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
id: { id: {
label: 'ID', label: 'ID',
searchOnly: true searchOnly: true
} }
}, },
fieldActions: { fieldActions: {
enabled_flag: { enabled_flag: {
@@ -68,7 +70,7 @@ angular.module('HomeHostListDefinition', [])
awToolTip: "{{ host.enabledToolTip }}", awToolTip: "{{ host.enabledToolTip }}",
dataTipWatch: "host.enabledToolTip", dataTipWatch: "host.enabledToolTip",
ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)" ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)"
}, },
active_failures: { active_failures: {
//label: 'Job Status', //label: 'Job Status',
//ngHref: "\{\{'/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + inventory_id \}\}", //ngHref: "\{\{'/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + inventory_id \}\}",
@@ -78,22 +80,22 @@ angular.module('HomeHostListDefinition', [])
awTipPlacement: 'top', awTipPlacement: 'top',
dataPlacement: 'left', dataPlacement: 'left',
iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}" iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}"
}, },
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editHost(host.id)", ngClick: "editHost(host.id)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit host', awToolTip: 'Edit host',
dataPlacement: 'top' dataPlacement: 'top'
} }
}, },
actions: { actions: {
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
}
} }
}
}); });

View File

@@ -1,15 +1,17 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Hosts.js * Hosts.js
* List view object for Users data model. * List view object for Users data model.
* *
* *
*/ */
'use strict';
angular.module('HostListDefinition', []) angular.module('HostListDefinition', [])
.value( .value('HostList', {
'HostList', {
name: 'hosts', name: 'hosts',
iterator: 'host', iterator: 'host',
selectTitle: 'Add Existing Hosts', selectTitle: 'Add Existing Hosts',
@@ -21,13 +23,13 @@ angular.module('HostListDefinition', [])
name: { name: {
key: true, key: true,
label: 'Host Name', label: 'Host Name',
linkTo: "/inventories/\{\{ inventory_id \}\}/hosts/\{\{ host.id \}\}" linkTo: "/inventories/{{ inventory_id }}/hosts/{{ host.id }}"
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
actions: { actions: {
help: { help: {
awPopOver: "Select hosts by clicking on each host you wish to add. Add the selected hosts to the group by clicking the <em>Select</em> button.", awPopOver: "Select hosts by clicking on each host you wish to add. Add the selected hosts to the group by clicking the <em>Select</em> button.",
@@ -35,26 +37,26 @@ angular.module('HostListDefinition', [])
mode: 'all', mode: 'all',
awToolTip: 'Click for help', awToolTip: 'Click for help',
dataTitle: 'Selecting Hosts' dataTitle: 'Selecting Hosts'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editHost(\{\{ host.id \}\})", ngClick: "editHost({{ host.id }})",
icon: 'icon-edit', icon: 'icon-edit',
"class": 'btn-xs', "class": 'btn-xs',
awToolTip: 'Edit host', awToolTip: 'Edit host',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')", ngClick: "deleteHost(host.id, host.name)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-xs', "class": 'btn-xs',
awToolTip: 'Delete host', awToolTip: 'Delete host',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,27 +1,30 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Inventories.js * Inventories.js
* List view object for Inventories data model. * List view object for Inventories data model.
* *
*/ */
'use strict';
angular.module('InventoriesListDefinition', []) angular.module('InventoriesListDefinition', [])
.value( .value('InventoryList', {
'InventoryList', {
name: 'inventories', name: 'inventories',
iterator: 'inventory', iterator: 'inventory',
selectTitle: 'Add Inventories', selectTitle: 'Add Inventories',
editTitle: 'Inventories', editTitle: 'Inventories',
selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> button to create a new row.', selectInstructions: "Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> " +
"button to create a new row.",
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
ngBind: 'inventory.summary_fields.organization.name', ngBind: 'inventory.summary_fields.organization.name',
@@ -29,69 +32,69 @@ angular.module('InventoriesListDefinition', [])
sourceModel: 'organization', sourceModel: 'organization',
sourceField: 'name', sourceField: 'name',
excludeModal: true excludeModal: true
}, },
has_inventory_sources: { has_inventory_sources: {
label: 'Cloud sourced?', label: 'Cloud sourced?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
has_active_failures: { has_active_failures: {
label: 'Failed hosts?', label: 'Failed hosts?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
inventory_sources_with_failures: { inventory_sources_with_failures: {
label: 'Sync failures?', label: 'Sync failures?',
searchType: 'gtzero', searchType: 'gtzero',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addInventory()', ngClick: 'addInventory()',
awToolTip: 'Create a new inventory' awToolTip: 'Create a new inventory'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
status: { status: {
//label: 'Status', //label: 'Status',
ngHref: "\{\{ inventory.status_link \}\}", ngHref: "inventory.status_link",
iconClass: "\{\{ 'fa fa-cloud icon-cloud-' + inventory.status_class \}\}", iconClass: "{{ 'fa fa-cloud icon-cloud-' + inventory.status_class }}",
awToolTip: "\{\{ inventory.status_tip \}\}", awToolTip: "{{ inventory.status_tip }}",
dataPlacement: "top" dataPlacement: "top"
}, },
failed_hosts: { failed_hosts: {
//label: 'Failures', //label: 'Failures',
ngHref: "\{\{ inventory.failed_hosts_link \}\}", ngHref: "inventory.failed_hosts_link",
iconClass: "\{\{ 'fa icon-failures-' + inventory.failed_hosts_class \}\}", iconClass: "{{ 'fa icon-failures-' + inventory.failed_hosts_class }}",
awToolTip: "\{\{ inventory.failed_hosts_tip \}\}", awToolTip: "{{ inventory.failed_hosts_tip }}",
dataPlacement: "top" dataPlacement: "top"
}, },
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: 'editInventoryProperties(inventory.id)', ngClick: 'editInventoryProperties(inventory.id)',
awToolTip: 'Edit inventory', awToolTip: 'Edit inventory',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteInventory(\{\{ inventory.id \}\},'\{\{ inventory.name \}\}')", ngClick: "deleteInventory(inventory.id, inventory.names')",
awToolTip: 'Delete inventory', awToolTip: 'Delete inventory',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,14 +1,16 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* InventoryHosts.js * InventoryHosts.js
* *
* Right side of /inventories/N page, showing hosts in the selected group. * Right side of /inventories/N page, showing hosts in the selected group.
* *
*/ */
'use strict';
angular.module('InventoryHostsDefinition', []) angular.module('InventoryHostsDefinition', [])
.value( .value('InventoryHosts', {
'InventoryHosts', {
name: 'hosts', name: 'hosts',
iterator: 'host', iterator: 'host',
@@ -19,34 +21,34 @@ angular.module('InventoryHostsDefinition', [])
hover: false, hover: false,
hasChildren: true, hasChildren: true,
'class': 'table-condensed table-no-border', 'class': 'table-condensed table-no-border',
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Hosts', label: 'Hosts',
ngClick: "editHost(\{\{ host.id \}\})", ngClick: "editHost(host.id)",
searchPlaceholder: "search_place_holder", searchPlaceholder: "search_place_holder",
columnClass: 'col-lg-9 col-md-9 col-sm-7 col-xs-7', columnClass: 'col-lg-9 col-md-9 col-sm-7 col-xs-7',
dataHostId: "\{\{ host.id \}\}", dataHostId: "{{ host.id }}",
dataType: "host", dataType: "host",
awDraggable: "true" awDraggable: "true"
}, },
enabled: { enabled: {
label: 'Disabled?', label: 'Disabled?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'false', searchValue: 'false',
searchOnly: true searchOnly: true
}, },
has_active_failures: { has_active_failures: {
label: 'Failed jobs?', label: 'Failed jobs?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
} }
}, },
fieldActions: { fieldActions: {
enabled_flag: { enabled_flag: {
iconClass: "{{ 'fa icon-enabled-' + host.enabled }}", iconClass: "{{ 'fa icon-enabled-' + host.enabled }}",
@@ -54,7 +56,7 @@ angular.module('InventoryHostsDefinition', [])
awToolTip: "{{ host.enabledToolTip }}", awToolTip: "{{ host.enabledToolTip }}",
dataTipWatch: "host.enabledToolTip", dataTipWatch: "host.enabledToolTip",
ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)" ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)"
}, },
active_failures: { active_failures: {
awPopOver: "{{ host.job_status_html }}", awPopOver: "{{ host.job_status_html }}",
dataTitle: "{{ host.job_status_title }}", dataTitle: "{{ host.job_status_title }}",
@@ -62,22 +64,22 @@ angular.module('InventoryHostsDefinition', [])
awTipPlacement: 'top', awTipPlacement: 'top',
dataPlacement: 'left', dataPlacement: 'left',
iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}" iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}"
}, },
edit: { edit: {
//label: 'Edit', //label: 'Edit',
ngClick: "editHost(\{\{ host.id \}\})", ngClick: "editHost(host.id)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit host', awToolTip: 'Edit host',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
//label: 'Delete', //label: 'Delete',
ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')", ngClick: "deleteHost(host.id, host.name)",
icon: 'icon-trash', icon: 'icon-trash',
awToolTip: 'Delete host', awToolTip: 'Delete host',
dataPlacement: 'top' dataPlacement: 'top'
} }
}, },
actions: { actions: {
@@ -86,15 +88,14 @@ angular.module('InventoryHostsDefinition', [])
create: { create: {
mode: 'all', mode: 'all',
ngClick: "createHost()", ngClick: "createHost()",
ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected
awToolTip: "Create a new host" awToolTip: "Create a new host"
}, },
stream: { stream: {
ngClick: "showHostActivity()", ngClick: "showHostActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
}
} }
}
}); });

View File

@@ -1,15 +1,17 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Jobs.js * Jobs.js
* List view object for Team data model. * List view object for Team data model.
* *
* *
*/ */
'use strict';
angular.module('JobEventsListDefinition', []) angular.module('JobEventsListDefinition', [])
.value( .value('JobEventList', {
'JobEventList', {
name: 'jobevents', name: 'jobevents',
iterator: 'jobevent', iterator: 'jobevent',
editTitle: 'Job Events', editTitle: 'Job Events',
@@ -37,7 +39,7 @@ angular.module('JobEventsListDefinition', [])
icon: 'icon-laptop' icon: 'icon-laptop'
} }
}, },
fields: { fields: {
created: { created: {
label: 'Created On', label: 'Created On',
@@ -53,7 +55,13 @@ angular.module('JobEventsListDefinition', [])
columnClass: 'col-sm-1 col-xs-2 text-center', columnClass: 'col-sm-1 col-xs-2 text-center',
searchField: 'failed', searchField: 'failed',
searchType: 'boolean', searchType: 'boolean',
searchOptions: [{ name: 'success', value: 0 }, { name: 'error', value: 1 }], searchOptions: [{
name: 'success',
value: 0
}, {
name: 'error',
value: 1
}],
nosort: true, nosort: true,
searchable: false, searchable: false,
ngClick: 'viewJobEvent({{ jobevent.id }})', ngClick: 'viewJobEvent({{ jobevent.id }})',
@@ -86,7 +94,7 @@ angular.module('JobEventsListDefinition', [])
columnClass: 'col-lg-2 hidden-sm hidden-xs' columnClass: 'col-lg-2 hidden-sm hidden-xs'
} }
}, },
actions: { actions: {
refresh: { refresh: {
mode: 'all', mode: 'all',
@@ -106,4 +114,4 @@ angular.module('JobEventsListDefinition', [])
dataPlacement: 'top' dataPlacement: 'top'
} }
} }
}); });

View File

@@ -1,20 +1,23 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* JobHosts.js * JobHosts.js
* List view object for Job Host Summary data model. * List view object for Job Host Summary data model.
* *
* *
*/ */
'use strict';
angular.module('JobHostDefinition', []) angular.module('JobHostDefinition', [])
.value('JobHostList', { .value('JobHostList', {
name: 'jobhosts', name: 'jobhosts',
iterator: 'jobhost', iterator: 'jobhost',
editTitle: 'All summaries', editTitle: 'All summaries',
index: true, index: true,
hover: true, hover: true,
navigationLinks: { navigationLinks: {
ngHide: 'host_id !== null', ngHide: 'host_id !== null',
details: { details: {
@@ -64,7 +67,13 @@ angular.module('JobHostDefinition', [])
dataPlacement: 'top', dataPlacement: 'top',
searchField: 'failed', searchField: 'failed',
searchType: 'boolean', searchType: 'boolean',
searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }] searchOptions: [{
name: "success",
value: 0
}, {
name: "error",
value: 1
}]
}, },
failed: { failed: {
label: 'Job failed?', label: 'Job failed?',
@@ -99,7 +108,7 @@ angular.module('JobHostDefinition', [])
searchable: false searchable: false
} }
}, },
actions: { actions: {
help: { help: {
awPopOver: "<dl>\n<dt>Success</dt><dd>Tasks successfully executed on the host.</dd>\n" + awPopOver: "<dl>\n<dt>Success</dt><dd>Tasks successfully executed on the host.</dd>\n" +
@@ -121,10 +130,10 @@ angular.module('JobHostDefinition', [])
'class': 'btn-xs', 'class': 'btn-xs',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()", ngClick: "refresh()",
ngShow: "host_id == null" //don't show when viewing from inventory->hosts ngShow: "host_id == null" //don't show when viewing from inventory->hosts
} }
}, },
fieldActions: {} fieldActions: {}
}); });

View File

@@ -1,73 +1,76 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* JobTemplates.js * JobTemplates.js
* List view object for Job Templates data model. * List view object for Job Templates data model.
* *
* *
*/ */
'use strict';
angular.module('JobTemplatesListDefinition', []) angular.module('JobTemplatesListDefinition', [])
.value( .value('JobTemplateList', {
'JobTemplateList', {
name: 'job_templates', name: 'job_templates',
iterator: 'job_template', iterator: 'job_template',
selectTitle: 'Add Job Template', selectTitle: 'Add Job Template',
editTitle: 'Job Templates', editTitle: 'Job Templates',
selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> button to create a new row.', selectInstructions: "Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> " +
"button to create a new row.",
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addJobTemplate()', ngClick: 'addJobTemplate()',
basePaths: ['job_templates'], basePaths: ['job_templates'],
awToolTip: 'Create a new template' awToolTip: 'Create a new template'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
icon: "icon-comments-alt", icon: "icon-comments-alt",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editJobTemplate(\{\{ job_template.id \}\})", ngClick: "editJobTemplate(job_template.id)",
icon: 'icon-edit', icon: 'icon-edit',
awToolTip: 'Edit template', awToolTip: 'Edit template',
"class": 'btn-default btn-xs', "class": 'btn-default btn-xs',
dataPlacement: 'top' dataPlacement: 'top'
}, },
submit: { submit: {
label: 'Launch', label: 'Launch',
icon: 'icon-rocket', icon: 'icon-rocket',
mode: 'all', mode: 'all',
"class": 'btn-xs btn-success', "class": 'btn-xs btn-success',
ngClick: 'submitJob(\{\{ job_template.id \}\})', ngClick: 'submitJob(job_template.id)',
awToolTip: 'Start a job using this template', awToolTip: 'Start a job using this template',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteJobTemplate(\{\{ job_template.id \}\},'\{\{ job_template.name \}\}')", ngClick: "deleteJobTemplate(job_template.id, job_template.name)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-danger btn-xs', "class": 'btn-danger btn-xs',
awToolTip: 'Delete template', awToolTip: 'Delete template',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -6,6 +6,9 @@
* *
* *
*/ */
'use strict';
angular.module('JobsListDefinition', []) angular.module('JobsListDefinition', [])
.value( 'JobList', { .value( 'JobList', {

View File

@@ -1,44 +1,46 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Organizations.js * Organizations.js
* List view object for Organizations data model. * List view object for Organizations data model.
* *
* *
*/ */
'use strict';
angular.module('OrganizationListDefinition', []) angular.module('OrganizationListDefinition', [])
.value( .value('OrganizationList', {
'OrganizationList', {
name: 'organizations', name: 'organizations',
iterator: 'organization', iterator: 'organization',
selectTitle: 'Add Organizations', selectTitle: 'Add Organizations',
editTitle: 'Organizations', editTitle: 'Organizations',
hover: true, hover: true,
index: true, index: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
},
description: {
label: 'Description'
}
}, },
description: {
label: 'Description'
}
},
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addOrganization()', ngClick: 'addOrganization()',
awToolTip: 'Create a new organization' awToolTip: 'Create a new organization'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
@@ -48,7 +50,7 @@ angular.module('OrganizationListDefinition', [])
"class": 'btn-xs btn-default', "class": 'btn-xs btn-default',
awToolTip: 'Edit organization', awToolTip: 'Edit organization',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
@@ -57,6 +59,6 @@ angular.module('OrganizationListDefinition', [])
"class": 'btn-xs btn-danger', "class": 'btn-xs btn-danger',
awToolTip: 'Delete organization', awToolTip: 'Delete organization',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,78 +1,81 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Permissions.js * Permissions.js
* List view object for Permissions data model. * List view object for Permissions data model.
* *
* *
*/ */
'use strict';
angular.module('PermissionListDefinition', []) angular.module('PermissionListDefinition', [])
.value( .value('PermissionList', {
'PermissionList', {
name: 'permissions', name: 'permissions',
iterator: 'permission', iterator: 'permission',
selectTitle: 'Add Permission', selectTitle: 'Add Permission',
selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> button to create a new row.', selectInstructions: "Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> " +
"button to create a new row.",
editTitle: 'Permissions', editTitle: 'Permissions',
index: true, index: true,
well: true, well: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name', label: 'Name',
ngClick: 'editPermission(\{\{ permission.id \}\})' ngClick: 'editPermission(permission.id)'
}, },
inventory: { inventory: {
label: 'Inventory', label: 'Inventory',
sourceModel: 'inventory', sourceModel: 'inventory',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.inventory.name' ngBind: 'permission.summary_fields.inventory.name'
}, },
project: { project: {
label: 'Project', label: 'Project',
sourceModel: 'project', sourceModel: 'project',
sourceField: 'name', sourceField: 'name',
ngBind: 'permission.summary_fields.project.name' ngBind: 'permission.summary_fields.project.name'
}, },
permission_type: { permission_type: {
label: 'Permission' label: 'Permission'
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addPermission()', ngClick: 'addPermission()',
awToolTip: 'Add a new permission', awToolTip: 'Add a new permission',
ngShow: 'PermissionAddAllowed' ngShow: 'PermissionAddAllowed'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editPermission(\{\{ permission.id \}\})", ngClick: "editPermission(permission.id)",
icon: 'icon-edit', icon: 'icon-edit',
"class": 'btn-xs btn-default', "class": 'btn-xs btn-default',
awToolTip: 'Edit permission', awToolTip: 'Edit permission',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deletePermission(\{\{ permission.id \}\},'\{\{ permission.name \}\}')", ngClick: "deletePermission(permission.id, permission.name)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-xs btn-danger', "class": 'btn-xs btn-danger',
awToolTip: 'Delete permission', awToolTip: 'Delete permission',
ngShow: 'PermissionAddAllowed', ngShow: 'PermissionAddAllowed',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,66 +1,68 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Projects.js * Projects.js
* List view object for Project data model. * List view object for Project data model.
* *
* *
*/ */
'use strict';
angular.module('ProjectsListDefinition', []) angular.module('ProjectsListDefinition', [])
.value( .value('ProjectList', {
'ProjectList', {
name: 'projects', name: 'projects',
iterator: 'project', iterator: 'project',
selectTitle: 'Add Project', selectTitle: 'Add Project',
editTitle: 'Projects', editTitle: 'Projects',
selectInstructions: '<p>Select existing projects by clicking each project or checking the related checkbox. When finished, click the blue ' + selectInstructions: '<p>Select existing projects by clicking each project or checking the related checkbox. When finished, click the blue ' +
'<em>Select</em> button, located bottom right.</p> <p>Create a brand new project by clicking the green <em>Create New</em> button.</p>', '<em>Select</em> button, located bottom right.</p> <p>Create a brand new project by clicking the green <em>Create New</em> button.</p>',
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description', label: 'Description',
columnClass: 'hidden-sm hidden-xs', columnClass: 'hidden-sm hidden-xs',
excludeModal: true excludeModal: true
}, },
scm_type: { scm_type: {
label: 'Type', label: 'Type',
searchType: 'select', searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource searchOptions: [], // will be set by Options call to projects resource
excludeModal: true, excludeModal: true,
nosort: true nosort: true
}, },
status: { status: {
label: 'Status', label: 'Status',
ngClick: 'showSCMStatus(\{\{ project.id \}\})', ngClick: 'showSCMStatus(project.id)',
awToolTip: 'View details of last SCM Update', awToolTip: 'View details of last SCM Update',
dataPlacement: 'top', dataPlacement: 'top',
badgeIcon: "\{\{ 'fa icon-failures-' + project.badge \}\}", badgeIcon: "{{ 'fa icon-failures-' + project.badge }}",
badgePlacement: 'left', badgePlacement: 'left',
searchType: 'select', searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource searchOptions: [], // will be set by Options call to projects resource
excludeModal: true excludeModal: true
}, },
last_updated: { last_updated: {
label: 'Last Updated', label: 'Last Updated',
type: 'date', type: 'date',
excludeModal: true, excludeModal: true,
searchable: false searchable: false
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addProject()', ngClick: 'addProject()',
awToolTip: 'Create a new project' awToolTip: 'Create a new project'
}, },
help: { help: {
awPopOver: "<dl>\n<dt>Updating</dt><dd>A source control update is in progress.</dd>\n" + awPopOver: "<dl>\n<dt>Updating</dt><dd>A source control update is in progress.</dd>\n" +
"<dt>Never Updated</dt><dd>This project has not yet been updated from source control.</dd>\n" + "<dt>Never Updated</dt><dd>This project has not yet been updated from source control.</dd>\n" +
@@ -75,46 +77,46 @@ angular.module('ProjectsListDefinition', [])
mode: 'all', mode: 'all',
awToolTip: 'Click for help', awToolTip: 'Click for help',
awTipPlacement: 'top' awTipPlacement: 'top'
}, },
refresh: { refresh: {
mode: 'all', mode: 'all',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()" ngClick: "refresh()"
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editProject(\{\{ project.id \}\})", ngClick: "editProject(project.id)",
awToolTip: 'Edit project properties', awToolTip: 'Edit project properties',
dataPlacement: 'top' dataPlacement: 'top'
}, },
scm_update: { scm_update: {
label: 'Update', label: 'Update',
ngClick: 'SCMUpdate(\{\{ project.id \}\})', ngClick: 'SCMUpdate(project.id)',
awToolTip: "\{\{ project.scm_update_tooltip \}\}", awToolTip: "{{ project.scm_update_tooltip }}",
ngClass: "project.scm_type_class", ngClass: "project.scm_type_class",
dataPlacement: 'top' dataPlacement: 'top'
}, },
cancel: { cancel: {
label: 'Stop', label: 'Stop',
ngClick: "cancelUpdate(\{\{ project.id \}\}, '\{\{ project.name \}\}')", ngClick: "cancelUpdate(project.id, project.name)",
awToolTip: 'Cancel a running SCM update process', awToolTip: 'Cancel a running SCM update process',
ngShow: "project.status == 'updating'", ngShow: "project.status == 'updating'",
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteProject(\{\{ project.id \}\},'\{\{ project.name \}\}')", ngClick: "deleteProject(project.id, project.name)",
awToolTip: 'Permanently remove project from the database', awToolTip: 'Permanently remove project from the database',
ngShow: "project.status !== 'updating'", ngShow: "project.status !== 'updating'",
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,24 +1,26 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Streams.js * Streams.js
* List view object for activity stream data model. * List view object for activity stream data model.
* *
* *
*/ */
'use strict';
angular.module('StreamListDefinition', []) angular.module('StreamListDefinition', [])
.value( .value('StreamList', {
'StreamList', {
name: 'activities', name: 'activities',
iterator: 'activity', iterator: 'activity',
editTitle: 'Activity Stream', editTitle: 'Activity Stream',
selectInstructions: '', selectInstructions: '',
index: false, index: false,
hover: true, hover: true,
"class": "table-condensed", "class": "table-condensed",
searchWidgets: 3, searchWidgets: 3,
fields: { fields: {
timestamp: { timestamp: {
label: 'Event Time', label: 'Event Time',
@@ -26,7 +28,7 @@ angular.module('StreamListDefinition', [])
desc: true, desc: true,
noLink: true, noLink: true,
searchable: false searchable: false
}, },
user: { user: {
label: 'Initiated by', label: 'Initiated by',
ngBindHtml: 'activity.user', ngBindHtml: 'activity.user',
@@ -36,22 +38,22 @@ angular.module('StreamListDefinition', [])
//dataPlacement: 'top', //dataPlacement: 'top',
searchPlaceholder: 'Username', searchPlaceholder: 'Username',
searchWidget: 1 searchWidget: 1
}, },
description: { description: {
label: 'Action', label: 'Action',
ngBindHtml: 'activity.description', ngBindHtml: 'activity.description',
nosort: true, nosort: true,
searchable: false, searchable: false,
columnClass: 'col-lg-7' columnClass: 'col-lg-7'
}, },
system_event: { system_event: {
label: 'System event', label: 'System event',
searchOnly: true, searchOnly: true,
searchType: 'isnull', searchType: 'isnull',
sourceModel: 'actor', sourceModel: 'actor',
sourceField: 'username', sourceField: 'username',
searchWidget: 1 searchWidget: 1
}, },
// The following fields exist to force loading each type of object into the search // The following fields exist to force loading each type of object into the search
// dropdown // dropdown
@@ -61,7 +63,7 @@ angular.module('StreamListDefinition', [])
searchObject: 'all', searchObject: 'all',
searchPlaceholder: 'All resources', searchPlaceholder: 'All resources',
searchWidget: 2 searchWidget: 2
}, },
credential_search: { credential_search: {
label: 'Credential', label: 'Credential',
searchOnly: true, searchOnly: true,
@@ -69,7 +71,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Credential name', searchPlaceholder: 'Credential name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
group_search: { group_search: {
label: 'Group', label: 'Group',
searchOnly: true, searchOnly: true,
@@ -77,7 +79,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Group name', searchPlaceholder: 'Group name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
host_search: { host_search: {
label: 'Host', label: 'Host',
searchOnly: true, searchOnly: true,
@@ -85,7 +87,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Host name', searchPlaceholder: 'Host name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
inventory_search: { inventory_search: {
label: 'Inventory', label: 'Inventory',
searchOnly: true, searchOnly: true,
@@ -93,7 +95,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Inventory name', searchPlaceholder: 'Inventory name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
job_template_search: { job_template_search: {
label: 'Job Template', label: 'Job Template',
searchOnly: true, searchOnly: true,
@@ -101,7 +103,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Job template name', searchPlaceholder: 'Job template name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
job_search: { job_search: {
label: 'Job', label: 'Job',
searchOnly: true, searchOnly: true,
@@ -110,7 +112,7 @@ angular.module('StreamListDefinition', [])
searchOnID: true, searchOnID: true,
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
organization_search: { organization_search: {
label: 'Organization', label: 'Organization',
searchOnly: true, searchOnly: true,
@@ -118,7 +120,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Organization name', searchPlaceholder: 'Organization name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
project_search: { project_search: {
label: 'Project', label: 'Project',
searchOnly: true, searchOnly: true,
@@ -126,7 +128,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Project name', searchPlaceholder: 'Project name',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
user_search: { user_search: {
label: 'User', label: 'User',
searchOnly: true, searchOnly: true,
@@ -134,18 +136,18 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Primary username', searchPlaceholder: 'Primary username',
searchWidget: 2, searchWidget: 2,
searchField: 'object1' searchField: 'object1'
}, },
// The following fields exist to force loading each type of object into the search // The following fields exist to force loading each type of object into the search
// dropdown // dropdown
all_objects3: { all_objects3: {
label: 'All', label: 'All',
searchOnly: true, searchOnly: true,
searchObject: 'all', searchObject: 'all',
searchPlaceholder: 'All related resources', searchPlaceholder: 'All related resources',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
credential_search3: { credential_search3: {
label: 'Credential', label: 'Credential',
searchOnly: true, searchOnly: true,
@@ -153,7 +155,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related credential name', searchPlaceholder: 'Related credential name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
group_search3: { group_search3: {
label: 'Group', label: 'Group',
searchOnly: true, searchOnly: true,
@@ -161,7 +163,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related group name', searchPlaceholder: 'Related group name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
host_search3: { host_search3: {
label: 'Host', label: 'Host',
searchOnly: true, searchOnly: true,
@@ -169,7 +171,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related host name', searchPlaceholder: 'Related host name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
inventory_search3: { inventory_search3: {
label: 'Inventory', label: 'Inventory',
searchOnly: true, searchOnly: true,
@@ -177,7 +179,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related inventory name', searchPlaceholder: 'Related inventory name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
job_search3: { job_search3: {
label: 'Job', label: 'Job',
searchOnly: true, searchOnly: true,
@@ -186,7 +188,7 @@ angular.module('StreamListDefinition', [])
searchOnID: true, searchOnID: true,
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
job_template_search3: { job_template_search3: {
label: 'Job Template', label: 'Job Template',
searchOnly: true, searchOnly: true,
@@ -194,7 +196,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related job template name', searchPlaceholder: 'Related job template name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
organization_search3: { organization_search3: {
label: 'Organization', label: 'Organization',
searchOnly: true, searchOnly: true,
@@ -202,7 +204,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related organization name', searchPlaceholder: 'Related organization name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
project_search3: { project_search3: {
label: 'Project', label: 'Project',
searchOnly: true, searchOnly: true,
@@ -210,7 +212,7 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related project name', searchPlaceholder: 'Related project name',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
}, },
user_search3: { user_search3: {
label: 'User', label: 'User',
searchOnly: true, searchOnly: true,
@@ -218,32 +220,32 @@ angular.module('StreamListDefinition', [])
searchPlaceholder: 'Related username', searchPlaceholder: 'Related username',
searchWidget: 3, searchWidget: 3,
searchField: 'object2' searchField: 'object2'
} }
}, },
actions: { actions: {
refresh: { refresh: {
mode: 'all', mode: 'all',
'class': 'btn-xs', 'class': 'btn-xs',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refreshStream()" ngClick: "refreshStream()"
}, },
close: { close: {
mode: 'all', mode: 'all',
awToolTip: "Close Activity Stream view", awToolTip: "Close Activity Stream view",
ngClick: "closeStream()" ngClick: "closeStream()"
} }
}, },
fieldActions: { fieldActions: {
view: { view: {
label: 'View', label: 'View',
ngClick: "showDetail(\{\{ activity.id \}\})", ngClick: "showDetail(activity.id)",
icon: 'fa-zoom-in', icon: 'fa-zoom-in',
"class": 'btn-default btn-xs', "class": 'btn-default btn-xs',
awToolTip: 'View event details', awToolTip: 'View event details',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}
});
});

View File

@@ -1,69 +1,71 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Teams.js * Teams.js
* List view object for Team data model. * List view object for Team data model.
* *
*
*/ */
'use strict';
angular.module('TeamsListDefinition', []) angular.module('TeamsListDefinition', [])
.value( .value('TeamList', {
'TeamList', {
name: 'teams', name: 'teams',
iterator: 'team', iterator: 'team',
selectTitle: 'Add Team', selectTitle: 'Add Team',
editTitle: 'Teams', editTitle: 'Teams',
selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> button to create a new row.', selectInstructions: "Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> " +
"button to create a new row.",
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
name: { name: {
key: true, key: true,
label: 'Name' label: 'Name'
}, },
description: { description: {
label: 'Description' label: 'Description'
}, },
organization: { organization: {
label: 'Organization', label: 'Organization',
ngBind: 'team.organization_name', ngBind: 'team.organization_name',
sourceModel: 'organization', sourceModel: 'organization',
sourceField: 'name' sourceField: 'name'
} }
}, },
actions: { actions: {
add: { add: {
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addTeam()', ngClick: 'addTeam()',
awToolTip: 'Create a new team' awToolTip: 'Create a new team'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editTeam(\{\{ team.id \}\})", ngClick: "editTeam(team.id)",
icon: 'icon-edit', icon: 'icon-edit',
"class": 'btn-xs btn-default', "class": 'btn-xs btn-default',
awToolTip: 'Edit team', awToolTip: 'Edit team',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteTeam(\{\{ team.id \}\},'\{\{ team.name \}\}')", ngClick: "deleteTeam(team.id, team.name)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-xs btn-danger', "class": 'btn-xs btn-danger',
awToolTip: 'Delete team', awToolTip: 'Delete team',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,71 +1,72 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Users.js * Users.js
* List view object for Users data model. * List view object for Users data model.
* *
*
*/ */
'use strict';
angular.module('UserListDefinition', []) angular.module('UserListDefinition', [])
.value( .value('UserList', {
'UserList', {
name: 'users', name: 'users',
iterator: 'user', iterator: 'user',
selectTitle: 'Add Users', selectTitle: 'Add Users',
editTitle: 'Users', editTitle: 'Users',
selectInstructions: '<p>Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' + selectInstructions: '<p>Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' +
'<em>Select</em> button, located bottom right.</p> <p>When available, a brand new user can be created by clicking the green ' + '<em>Select</em> button, located bottom right.</p> <p>When available, a brand new user can be created by clicking the green ' +
'<em>Create New</em> button.</p>', '<em>Create New</em> button.</p>',
index: true, index: true,
hover: true, hover: true,
fields: { fields: {
username: { username: {
key: true, key: true,
label: 'Username' label: 'Username'
}, },
first_name: { first_name: {
label: 'First Name' label: 'First Name'
}, },
last_name: { last_name: {
label: 'Last Name' label: 'Last Name'
} }
}, },
actions: { actions: {
add: { add: {
label: 'Create New', label: 'Create New',
mode: 'all', // One of: edit, select, all mode: 'all', // One of: edit, select, all
ngClick: 'addUser()', ngClick: 'addUser()',
basePaths: ['organizations','users'], // base path must be in list, or action not available basePaths: ['organizations', 'users'], // base path must be in list, or action not available
"class": 'btn-xs', "class": 'btn-xs',
awToolTip: 'Create a new user' awToolTip: 'Create a new user'
}, },
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",
mode: 'all' mode: 'all'
} }
}, },
fieldActions: { fieldActions: {
edit: { edit: {
label: 'Edit', label: 'Edit',
ngClick: "editUser(\{\{ user.id \}\})", ngClick: "editUser(user.id)",
icon: 'icon-edit', icon: 'icon-edit',
"class": 'btn-xs btn-default', "class": 'btn-xs btn-default',
awToolTip: 'Edit user', awToolTip: 'Edit user',
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: 'Delete', label: 'Delete',
ngClick: "deleteUser(\{\{ user.id \}\},'\{\{ user.username \}\}')", ngClick: "deleteUser(user.id, user.username)",
icon: 'icon-trash', icon: 'icon-trash',
"class": 'btn-xs btn-danger', "class": 'btn-xs btn-danger',
awToolTip: 'Delete user', awToolTip: 'Delete user',
dataPlacement: 'top' dataPlacement: 'top'
}
} }
}); }
});

View File

@@ -1,7 +1,7 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* InventorySyncStatus.js * InventorySyncStatus.js
* *
* Dashboard widget showing object counts and license availability. * Dashboard widget showing object counts and license availability.
* *
@@ -10,94 +10,98 @@
'use strict'; 'use strict';
angular.module('InventorySyncStatusWidget', ['RestServices', 'Utilities']) angular.module('InventorySyncStatusWidget', ['RestServices', 'Utilities'])
.factory('InventorySyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'GetChoices', .factory('InventorySyncStatus', ['$rootScope', '$compile', function ($rootScope, $compile) {
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait, GetChoices) { return function (params) {
return function(params) {
var scope = params.scope,
var scope = params.scope; target = params.target,
var target = params.target; dashboard = params.dashboard,
var dashboard = params.dashboard; html, group_total, group_fail, element, src;
var html = "<div class=\"panel panel-default\">\n"; html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Inventory Sync Status</div>\n"; html += "<div class=\"panel-heading\">Inventory Sync Status</div>\n";
html += "<div class=\"panel-body\">\n"; html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n"; html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n"; html += "<thead>\n";
html += "<tr>\n"; html += "<tr>\n";
html += "<th class=\"col-md-4 col-lg-3\"></th>\n"; html += "<th class=\"col-md-4 col-lg-3\"></th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n"; html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n"; html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n"; html += "</tr>\n";
html += "</thead>\n"; html += "</thead>\n";
html += "<tbody>\n"; html += "<tbody>\n";
function makeRow(params) { function makeRow(params) {
var label = params.label; var label = params.label,
var count = params.count; count = params.count,
var fail = params.fail; fail = params.fail,
var link = params.link; link = params.link,
var fail_link = params.fail_link; fail_link = params.fail_link,
var html = "<tr>\n"; html = "<tr>\n";
html += "<td><a href=\"" + link + "\""; html += "<td><a href=\"" + link + "\"";
html += (label == 'Hosts' || label == 'Groups') ? " class=\"pad-left-sm\" " : ""; html += (label === 'Hosts' || label === 'Groups') ? " class=\"pad-left-sm\" " : "";
html += ">" + label + "</a></td>\n"; html += ">" + label + "</a></td>\n";
html += "<td class=\""; html += "<td class=\"";
html += (fail > 0) ? 'failed-column' : 'zero-column'; html += (fail > 0) ? 'failed-column' : 'zero-column';
html += " text-right\">"; html += " text-right\">";
html += "<a href=\"" + fail_link + "\">" + fail + "</a>"; html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
html += "</td>\n"; html += "</td>\n";
html += "<td class=\"text-right\">"; html += "<td class=\"text-right\">";
html += "<a href=\"" + link + "\">" + count + "</a>"; html += "<a href=\"" + link + "\">" + count + "</a>";
html += "</td></tr>\n"; html += "</td></tr>\n";
return html; return html;
} }
html += makeRow({ label: 'Inventories', html += makeRow({
count: (dashboard.inventories && dashboard.inventories.total_with_inventory_source) ? label: 'Inventories',
dashboard.inventories.total_with_inventory_source : 0, count: (dashboard.inventories && dashboard.inventories.total_with_inventory_source) ?
fail: (dashboard.inventories && dashboard.inventories.inventory_failed) ? dashboard.inventories.inventory_failed : 0, dashboard.inventories.total_with_inventory_source : 0,
link: '/#/inventories/?has_inventory_sources=true', fail: (dashboard.inventories && dashboard.inventories.inventory_failed) ? dashboard.inventories.inventory_failed : 0,
fail_link: '/#/inventories/?inventory_sources_with_failures=true' link: '/#/inventories/?has_inventory_sources=true',
}); fail_link: '/#/inventories/?inventory_sources_with_failures=true'
var group_total = 0;
var group_fail = 0;
if (dashboard.inventory_sources) {
for (var src in dashboard.inventory_sources) {
group_total += (dashboard.inventory_sources[src].total) ? dashboard.inventory_sources[src].total : 0;
group_fail += (dashboard.inventory_sources[src].failed) ? dashboard.inventory_sources[src].failed : 0;
}
}
html += makeRow({ label: 'Groups',
count: group_total,
fail: group_fail,
link: '/#/home/groups/?has_external_source=true',
fail_link: '/#/home/groups/?status=failed'
}); });
// Each inventory source group_total = 0;
for (var src in dashboard.inventory_sources) { group_fail = 0;
if (dashboard.inventory_sources[src].total) { if (dashboard.inventory_sources) {
html += makeRow({ label: dashboard.inventory_sources[src].label, for (src in dashboard.inventory_sources) {
count: (dashboard.inventory_sources[src].total) ? dashboard.inventory_sources[src].total : 0, group_total += (dashboard.inventory_sources[src].total) ? dashboard.inventory_sources[src].total : 0;
fail: (dashboard.inventory_sources[src].failed) ? dashboard.inventory_sources[src].failed : 0, group_fail += (dashboard.inventory_sources[src].failed) ? dashboard.inventory_sources[src].failed : 0;
link: '/#/home/groups/?source=' + src, }
fail_link: '/#/home/groups/?status=failed&source=' + src }
html += makeRow({
label: 'Groups',
count: group_total,
fail: group_fail,
link: '/#/home/groups/?has_external_source=true',
fail_link: '/#/home/groups/?status=failed'
});
// Each inventory source
for (src in dashboard.inventory_sources) {
if (dashboard.inventory_sources[src].total) {
html += makeRow({
label: dashboard.inventory_sources[src].label,
count: (dashboard.inventory_sources[src].total) ? dashboard.inventory_sources[src].total : 0,
fail: (dashboard.inventory_sources[src].failed) ? dashboard.inventory_sources[src].failed : 0,
link: '/#/home/groups/?source=' + src,
fail_link: '/#/home/groups/?status=failed&source=' + src
}); });
}
} }
}
html += "</tbody>\n"; html += "</tbody>\n";
html += "</table>\n"; html += "</table>\n";
html += "</div>\n"; html += "</div>\n";
html += "</div>\n"; html += "</div>\n";
html += "</div>\n"; html += "</div>\n";
var element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
} element = angular.element(document.getElementById(target));
}]); element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
};
}
]);

View File

@@ -1,7 +1,7 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* JobStatus.js * JobStatus.js
* *
* Dashboard widget showing object counts and license availability. * Dashboard widget showing object counts and license availability.
* *
@@ -11,88 +11,89 @@
angular.module('JobStatusWidget', ['RestServices', 'Utilities']) angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
.factory('JobStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', .factory('JobStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait) { function ($rootScope, $compile) {
return function(params) { return function (params) {
var scope = params.scope;
var target = params.target;
var dashboard = params.dashboard;
var html = '';
var html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Job Status</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-4 col-lg-3\"></th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
function makeRow(params) {
var html = '';
var label = params.label;
var link = params.link;
var fail_link = params.fail_link;
var count = params.count;
var fail = params.fail;
html += "<tr>\n";
html += "<td><a href=\"" + link + "\"";
html += (label == 'Hosts' || label == 'Groups') ? " class=\"pad-left-sm\" " : "";
html += ">" + label + "</a></td>\n";
html += "<td class=\"";
html += (fail > 0) ? 'failed-column' : 'zero-column';
html += " text-right\">";
html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
html += "</td>\n";
html += "<td class=\"text-right\">"
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
html += makeRow({
label: 'Jobs',
link: '/#/jobs',
count: (dashboard.jobs && dashboard.jobs.total) ? dashboard.jobs.total : 0,
fail: (dashboard.jobs && dashboard.jobs.failed) ? dashboard.jobs.failed : 0,
fail_link: '/#/jobs/?status=failed'
});
html += makeRow({
label: 'Inventories',
link: '/#/inventories',
count: (dashboard.inventories && dashboard.inventories.total) ? dashboard.inventories.total : 0,
fail: (dashboard.inventories && dashboard.inventories.job_failed) ? dashboard.inventories.job_failed : 0,
fail_link: '/#/inventories/?has_active_failures=true'
});
html += makeRow({
label: 'Groups',
link: '/#/home/groups',
count: (dashboard.groups && dashboard.groups.total) ? dashboard.groups.total : 0,
fail: (dashboard.groups && dashboard.groups.job_failed) ? dashboard.groups.job_failed : 0,
fail_link: '/#/home/groups/?has_active_failures=true'
});
html += makeRow({
label: 'Hosts',
link: '/#/home/hosts',
count: (dashboard.hosts && dashboard.hosts.total) ? dashboard.hosts.total : 0,
fail: (dashboard.hosts && dashboard.hosts.failed) ? dashboard.hosts.failed : 0,
fail_link: '/#/home/hosts/?has_active_failures=true'
});
html += "</tbody>\n"; var scope = params.scope,
html += "</table>\n"; target = params.target,
html += "</div>\n"; dashboard = params.dashboard,
html += "</div>\n"; html = '', element;
html += "</div>\n";
html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Job Status</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-4 col-lg-3\"></th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
var element = angular.element(document.getElementById(target)); function makeRow(params) {
element.html(html); var html = '',
$compile(element)(scope); label = params.label,
scope.$emit('WidgetLoaded'); link = params.link,
fail_link = params.fail_link,
count = params.count,
fail = params.fail;
html += "<tr>\n";
html += "<td><a href=\"" + link + "\"";
html += (label === 'Hosts' || label === 'Groups') ? " class=\"pad-left-sm\" " : "";
html += ">" + label + "</a></td>\n";
html += "<td class=\"";
html += (fail > 0) ? 'failed-column' : 'zero-column';
html += " text-right\">";
html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
html += "</td>\n";
html += "<td class=\"text-right\">";
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
html += makeRow({
label: 'Jobs',
link: '/#/jobs',
count: (dashboard.jobs && dashboard.jobs.total) ? dashboard.jobs.total : 0,
fail: (dashboard.jobs && dashboard.jobs.failed) ? dashboard.jobs.failed : 0,
fail_link: '/#/jobs/?status=failed'
});
html += makeRow({
label: 'Inventories',
link: '/#/inventories',
count: (dashboard.inventories && dashboard.inventories.total) ? dashboard.inventories.total : 0,
fail: (dashboard.inventories && dashboard.inventories.job_failed) ? dashboard.inventories.job_failed : 0,
fail_link: '/#/inventories/?has_active_failures=true'
});
html += makeRow({
label: 'Groups',
link: '/#/home/groups',
count: (dashboard.groups && dashboard.groups.total) ? dashboard.groups.total : 0,
fail: (dashboard.groups && dashboard.groups.job_failed) ? dashboard.groups.job_failed : 0,
fail_link: '/#/home/groups/?has_active_failures=true'
});
html += makeRow({
label: 'Hosts',
link: '/#/home/hosts',
count: (dashboard.hosts && dashboard.hosts.total) ? dashboard.hosts.total : 0,
fail: (dashboard.hosts && dashboard.hosts.failed) ? dashboard.hosts.failed : 0,
fail_link: '/#/home/hosts/?has_active_failures=true'
});
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n";
html += "</div>\n";
element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
};
} }
}]); ]);

View File

@@ -1,7 +1,7 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* ObjectCount.js * ObjectCount.js
* *
* Dashboard widget showing object counts and license availability. * Dashboard widget showing object counts and license availability.
* *
@@ -11,60 +11,60 @@
angular.module('ObjectCountWidget', ['RestServices', 'Utilities']) angular.module('ObjectCountWidget', ['RestServices', 'Utilities'])
.factory('ObjectCount', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', .factory('ObjectCount', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait) { function ($rootScope, $compile) {
return function(params) { return function (params) {
var scope = params.scope;
var target = params.target;
var dashboard = params.dashboard;
var keys=[ 'organizations', 'users', 'teams', 'credentials', 'projects', 'inventories', 'groups', 'hosts',
'job_templates', 'jobs' ];
var html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">System Summary</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-5 col-lg-4\"></th>\n";
html += "<th class=\"col-md-1 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
function makeRow(params) {
var html = '';
var label = params.label;
var link = params.link;
var count = params.count;
html += "<tr>\n";
html += "<td class=\"capitalize\"><a href=\"" + link + "\"";
html += (label == 'hosts' || label == 'groups') ? " class=\"pad-left-sm\" " : "";
html += ">" + label.replace(/\_/g,' ') + "</a></td>\n";
html += "<td class=\"text-right\">"
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
for (var i=0; i < keys.length; i++) {
html += makeRow({
label: keys[i],
link: '/#/' + ( (keys[i] == 'hosts' || keys[i] == 'groups') ? 'home/' + keys[i] : keys[i] ),
count: (dashboard[keys[i]] && dashboard[keys[i]].total) ? dashboard[keys[i]].total : 0
});
}
html += "</tbody>\n"; var scope = params.scope,
html += "</table>\n"; target = params.target,
html += "</div>\n"; dashboard = params.dashboard,
html += "</div>\n" keys = ['organizations', 'users', 'teams', 'credentials', 'projects', 'inventories', 'groups', 'hosts',
var element = angular.element(document.getElementById(target)); 'job_templates', 'jobs'
element.html(html); ],
$compile(element)(scope); i, html, element;
scope.$emit('WidgetLoaded');
html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">System Summary</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-5 col-lg-4\"></th>\n";
html += "<th class=\"col-md-1 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
function makeRow(params) {
var html = '',
label = params.label,
link = params.link,
count = params.count;
html += "<tr>\n";
html += "<td class=\"capitalize\"><a href=\"" + link + "\"";
html += (label === 'hosts' || label === 'groups') ? " class=\"pad-left-sm\" " : "";
html += ">" + label.replace(/\_/g, ' ') + "</a></td>\n";
html += "<td class=\"text-right\">";
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
for (i = 0; i < keys.length; i++) {
html += makeRow({
label: keys[i],
link: '/#/' + ((keys[i] === 'hosts' || keys[i] === 'groups') ? 'home/' + keys[i] : keys[i]),
count: (dashboard[keys[i]] && dashboard[keys[i]].total) ? dashboard[keys[i]].total : 0
});
}
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n";
element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
};
} }
}]); ]);

View File

@@ -1,99 +1,100 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* SCMSyncStatus.js * SCMSyncStatus.js
* *
* Dashboard widget showing object counts and license availability. * Dashboard widget showing object counts and license availability.
* *
*/ */
'use strict'; 'use strict';
angular.module('SCMSyncStatusWidget', ['RestServices', 'Utilities']) angular.module('SCMSyncStatusWidget', ['RestServices', 'Utilities'])
.factory('SCMSyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'GetChoices', .factory('SCMSyncStatus', ['$rootScope', '$compile',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait, GetChoices) { function ($rootScope, $compile) {
return function(params) { return function (params) {
var scope = params.scope;
var target = params.target;
var dashboard = params.dashboard;
var html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Project SCM Status</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-4 col-lg-3\"></th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
function makeRow(params) {
var html = '';
var label = params.label;
var link = params.link;
var fail_link = params.fail_link;
var count = params.count;
var fail = params.fail;
html += "<tr>\n";
html += "<td><a href=\"" + link + "\">" + label + "</a></td>\n";
html += "<td class=\"";
html += (fail > 0) ? 'failed-column' : 'zero-column';
html += " text-right\">";
html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
html += "</td>\n";
html += "<td class=\"text-right\">"
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
var total_count = 0; var scope = params.scope,
if (dashboard.scm_types) { target = params.target,
for (var type in dashboard.scm_types) { dashboard = params.dashboard,
total_count += (dashboard.scm_types[type].total) ? dashboard.scm_types[type].total : 0; i, html, total_count, element, type, labelList;
}
} html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Project SCM Status</div>\n";
html += "<div class=\"panel-body\">\n";
html += "<table class=\"table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th class=\"col-md-4 col-lg-3\"></th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Failed</th>\n";
html += "<th class=\"col-md-2 col-lg-1 text-right\">Total</th>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
function makeRow(params) {
var html = '',
label = params.label,
link = params.link,
fail_link = params.fail_link,
count = params.count,
fail = params.fail;
html += "<tr>\n";
html += "<td><a href=\"" + link + "\">" + label + "</a></td>\n";
html += "<td class=\"";
html += (fail > 0) ? 'failed-column' : 'zero-column';
html += " text-right\">";
html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
html += "</td>\n";
html += "<td class=\"text-right\">";
html += "<a href=\"" + link + "\" >" + count + "</a>";
html += "</td></tr>\n";
return html;
}
total_count = 0;
if (dashboard.scm_types) {
for (type in dashboard.scm_types) {
total_count += (dashboard.scm_types[type].total) ? dashboard.scm_types[type].total : 0;
}
}
html += makeRow({ label: 'Projects',
link: '/#/projects',
count: total_count,
fail: (dashboard.projects && dashboard.projects.failed) ? dashboard.projects.failed : 0,
fail_link: '/#/projects/?status=failed'
});
var labelList = [];
for (var type in dashboard.scm_types) {
labelList.push(type);
}
labelList.sort();
var type;
for (var i=0; i < labelList.length; i++) {
type = labelList[i];
if (dashboard.scm_types[type].total) {
html += makeRow({ html += makeRow({
label: dashboard.scm_types[type].label, label: 'Projects',
link: '/#/projects/?scm_type=' + type, link: '/#/projects',
count: (dashboard.scm_types[type].total) ? dashboard.scm_types[type].total : 0, count: total_count,
fail: (dashboard.scm_types[type].failed) ? dashboard.scm_types[type].failed : 0, fail: (dashboard.projects && dashboard.projects.failed) ? dashboard.projects.failed : 0,
fail_link: '/#/projects/?scm_type=' + type + '&status=failed' fail_link: '/#/projects/?status=failed'
}); });
}
labelList = [];
for (type in dashboard.scm_types) {
labelList.push(type);
}
labelList.sort();
for (i = 0; i < labelList.length; i++) {
type = labelList[i];
if (dashboard.scm_types[type].total) {
html += makeRow({
label: dashboard.scm_types[type].label,
link: '/#/projects/?scm_type=' + type,
count: (dashboard.scm_types[type].total) ? dashboard.scm_types[type].total : 0,
fail: (dashboard.scm_types[type].failed) ? dashboard.scm_types[type].failed : 0,
fail_link: '/#/projects/?scm_type=' + type + '&status=failed'
});
}
}
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n";
html += "</div>\n";
element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
};
} }
]);
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n";
html += "</div>\n";
var element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
}
}]);

View File

@@ -1,7 +1,7 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Stream.js * Stream.js
* *
* Activity stream widget that can be called from anywhere * Activity stream widget that can be called from anywhere
* *
@@ -10,457 +10,470 @@
'use strict'; 'use strict';
angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginationHelpers', angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginationHelpers',
'RefreshHelper', 'ListGenerator', 'StreamWidget', 'AuthService']) 'RefreshHelper', 'ListGenerator', 'StreamWidget', 'AuthService'
])
.factory('setStreamHeight', [ function() {
return function() {
// Try not to overlap footer. Because stream is positioned absolute, the parent
// doesn't resize correctly when stream is loaded.
var sheight = $('#stream-content').height();
var theight = parseInt($('#tab-content-container').css('min-height').replace(/px/,''));
var height = (theight < sheight) ? sheight : theight;
$('#tab-content-container').css({ "min-height": height });
}
}])
.factory('ShowStream', [ 'setStreamHeight', 'Authorization', function(setStreamHeight, Authorization) { .factory('setStreamHeight', [
return function() { function () {
// Slide in the Stream widget return function () {
// Try not to overlap footer. Because stream is positioned absolute, the parent
// Make some style/position adjustments adjustments // doesn't resize correctly when stream is loaded.
var stream = $('#stream-container'); var sheight = $('#stream-content').height(),
stream.css({ theight = parseInt($('#tab-content-container').css('min-height').replace(/px/, '')),
position: 'absolute', height = (theight < sheight) ? sheight : theight;
top: 0, $('#tab-content-container').css({
left: 0, "min-height": height
width: '100%', });
'min-height': '100%', };
'background-color': '#FFF' }
])
.factory('ShowStream', ['setStreamHeight', 'Authorization',
function (setStreamHeight) {
return function () {
// Slide in the Stream widget
// Make some style/position adjustments adjustments
var stream = $('#stream-container');
stream.css({
position: 'absolute',
top: 0,
left: 0,
width: '100%',
'min-height': '100%',
'background-color': '#FFF'
}); });
setStreamHeight(); setStreamHeight();
// Slide in stream // Slide in stream
stream.show('slide', {'direction': 'left'}, {'duration': 500, 'queue': false }); stream.show('slide', {
'direction': 'left'
} }, {
}]) 'duration': 500,
'queue': false
});
.factory('HideStream', [ 'LoadBreadCrumbs', function(LoadBreadCrumbs) { };
return function() { }
// Remove the stream widget ])
var stream = $('#stream-container');
stream.hide('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
// Completely destroy the container so we don't experience random flashes of it later.
// There was some sort of weirdness with the tab 'show' causing the stream to slide in when
// a tab was clicked, after the stream had been hidden. Seemed like timing- wait long enough
// before clicking a tab, and it would not happen.
setTimeout( function() {
stream.detach();
stream.empty();
stream.unbind();
$('#tab-content-container').css({ 'min-height': 0 }); //let the parent height go back to normal
}, 500);
LoadBreadCrumbs();
}
}])
.factory('StreamBreadCrumbs', ['$rootScope', '$location', function($rootScope, $location) { .factory('HideStream', ['LoadBreadCrumbs',
return function() { function (LoadBreadCrumbs) {
// Load the breadcrumbs array. We have to do things a bit different than Utilities.LoadBreadcrumbs. return function () {
// Rather than botch that all up, we'll do our own thing here. // Remove the stream widget
$rootScope.breadcrumbs = [];
var paths = $location.path().split('/'); var stream = $('#stream-container');
paths.splice(0,1); stream.hide('slide', {
var path, title, i, j; 'direction': 'left'
for (i=0; i < paths.length; i++) { }, {
if (/^\d+/.test(paths[i])) { 'duration': 500,
path=''; 'queue': false
title=''; });
for (j=0; j <= i; j++) {
path += '/' + paths[j]; // Completely destroy the container so we don't experience random flashes of it later.
} // There was some sort of weirdness with the tab 'show' causing the stream to slide in when
for (j=0; j < $rootScope.crumbCache.length; j++) { // a tab was clicked, after the stream had been hidden. Seemed like timing- wait long enough
if ($rootScope.crumbCache[j].path == path) { // before clicking a tab, and it would not happen.
title = $rootScope.crumbCache[j].title; setTimeout(function () {
break; stream.detach();
} stream.empty();
} stream.unbind();
if (!title) { $('#tab-content-container').css({
title = paths[i - 1].substr(0,paths[i - 1].length - 1); 'min-height': 0
title = title.charAt(0).toUpperCase() + title.slice(1); }); //let the parent height go back to normal
title = (title == 'Inventorie') ? 'Inventory' : title; }, 500);
}
} LoadBreadCrumbs();
else { };
path=''; }
title=''; ])
if (i > 0) {
for (j=0; j <= i; j++) { .factory('StreamBreadCrumbs', ['$rootScope', '$location',
function ($rootScope, $location) {
return function () {
// Load the breadcrumbs array. We have to do things a bit different than Utilities.LoadBreadcrumbs.
// Rather than botch that all up, we'll do our own thing here.
$rootScope.breadcrumbs = [];
var path, title, i, j, paths = $location.path().split('/');
paths.splice(0, 1);
for (i = 0; i < paths.length; i++) {
if (/^\d+/.test(paths[i])) {
path = '';
title = '';
for (j = 0; j <= i; j++) {
path += '/' + paths[j]; path += '/' + paths[j];
} }
for (j = 0; j < $rootScope.crumbCache.length; j++) {
if ($rootScope.crumbCache[j].path === path) {
title = $rootScope.crumbCache[j].title;
break;
}
}
if (!title) {
title = paths[i - 1].substr(0, paths[i - 1].length - 1);
title = title.charAt(0).toUpperCase() + title.slice(1);
title = (title === 'Inventorie') ? 'Inventory' : title;
}
} else {
path = '';
title = '';
if (i > 0) {
for (j = 0; j <= i; j++) {
path += '/' + paths[j];
}
} else {
path = '/' + paths[i];
}
title = paths[i];
title = title.charAt(0).toUpperCase() + title.slice(1);
} }
else { $rootScope.breadcrumbs.push({
path = '/' + paths[i]; path: path,
} title: title,
title = paths[i]; ngClick: "closeStream('" + path + "')"
title = title.charAt(0).toUpperCase() + title.slice(1); });
} }
$rootScope.breadcrumbs.push({ path: path, title: title, ngClick: "closeStream('" + path + "')" }); };
} }
} ])
}])
.factory('FixUrl', [ function() { .factory('FixUrl', [
return function(u) { function () {
return u.replace(/\/api\/v1\//,'/#/'); return function (u) {
} return u.replace(/\/api\/v1\//, '/#/');
}]) };
}
])
.factory('BuildUrl', [ function() { .factory('BuildUrl', [
return function(obj) { function () {
var url = '/#/'; return function (obj) {
switch(obj.base) { var url = '/#/';
case 'group': switch (obj.base) {
case 'host': case 'group':
url += 'home/' + obj.base + 's/?id=' + obj.id; case 'host':
break; url += 'home/' + obj.base + 's/?id=' + obj.id;
case 'inventory': break;
url += 'inventories/' + obj.id + '/'; case 'inventory':
break; url += 'inventories/' + obj.id + '/';
default: break;
url += obj.base + 's/' + obj.id + '/'; default:
} url += obj.base + 's/' + obj.id + '/';
return url;
}
}])
.factory('BuildDescription', ['FixUrl', 'BuildUrl', function(FixUrl, BuildUrl) {
return function(activity) {
function stripDeleted(s) {
return s.replace(/^_deleted_\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+\+\d+:\d+_/,'');
} }
return url;
var descr = ''; };
var descr_nolink; }
descr += activity.operation; ])
descr += (/e$/.test(activity.operation)) ? 'd ' : 'ed ';
descr_nolink = descr;
// labels .factory('BuildDescription', ['FixUrl', 'BuildUrl',
var obj1 = activity.object1; function (FixUrl, BuildUrl) {
var obj2 = activity.object2; return function (activity) {
// objects function stripDeleted(s) {
var obj1_obj = (activity.summary_fields[obj1]) ? activity.summary_fields[obj1][0] : null; return s.replace(/^_deleted_\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+\+\d+:\d+_/, '');
if (obj1 == obj2) {
var obj2_obj = activity.summary_fields[obj1][1];
}
else if (activity.summary_fields[obj2]) {
var obj2_obj = activity.summary_fields[obj2][0];
}
else {
var obj2_obj = null;
}
if (obj1 == 'user' || obj2 == 'user') {
activity.summary_fields['user'][0].name = activity.summary_fields['user'][0].username;
}
var name;
if (obj2_obj && obj2_obj.name && !/^_delete/.test(obj2_obj.name)) {
obj2_obj['base'] = obj2;
descr += obj2 + ' <a href=\"' + BuildUrl(obj2_obj) + '\">'
+ obj2_obj.name + '</a>' + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' );
descr_nolink += obj2 + ' ' + obj2_obj.name + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' );
}
else if (obj2) {
name = '';
if (obj2_obj && obj2_obj.name) {
name = ' ' + stripDeleted(obj2_obj.name);
} }
descr += obj2 + name + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' );
descr_nolink += obj2 + name + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' ); var descr, descr_nolink, obj1, obj2, obj1_obj, obj2_obj, name, name_nolink;
}
if (obj1_obj && obj1_obj.name && !/^\_delete/.test(obj1_obj.name)) { descr = activity.operation;
obj1_obj['base'] = obj1; descr += (/e$/.test(activity.operation)) ? 'd ' : 'ed ';
descr += obj1 + ' <a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.name + '</a>'; descr_nolink = descr;
descr_nolink += obj1 + ' ' + obj1_obj.name;
} // labels
else if (obj1) { obj1 = activity.object1;
name = ''; obj2 = activity.object2;
var name_nolink = '';
// find the name in changes, if needed // objects
if ( !(obj1_obj && obj1_obj.name) || obj1_obj && obj1_obj.name && /^_delete/.test(obj1_obj.name) ) { obj1_obj = (activity.summary_fields[obj1]) ? activity.summary_fields[obj1][0] : null;
if (activity.changes && activity.changes.name) { if (obj1 === obj2) {
if (typeof activity.changes.name == 'string') { obj2_obj = activity.summary_fields[obj1][1];
name = ' ' + activity.changes.name; } else if (activity.summary_fields[obj2]) {
name_nolink = name; obj2_obj = activity.summary_fields[obj2][0];
} } else {
else if (typeof activity.changes.name == 'object' && Array.isArray(activity.changes.name)) { obj2_obj = null;
name = ' ' + activity.changes.name[0];
name_nolink = name;
}
}
else if (obj1 == 'job' && obj1_obj && activity.changes && activity.changes.job_template) {
// Hack for job activity where the template name is known
if (activity.operation != 'delete') {
obj1_obj['base'] = obj1;
name = ' ' + '<a href=\"' + BuildUrl(obj1_obj) + '\">'+ obj1_obj.id + ' ' + activity.changes.job_template + '</a>';
name_nolink = ' ' + obj1_obj.id + ' ' + activity.changes.job_template;
}
else {
name = ' ' + obj1_obj.id + ' ' + activity.changes.job_template;
name_nolink = name;
}
}
else if (obj1 == 'job' && obj1_obj) {
// Hack for job activity where template name not known
if (activity.operation != 'delete') {
obj1_obj['base'] = obj1;
name = ' ' + '<a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.id + '</a>';
name_nolink = ' ' + obj1_obj.id;
}
else {
name = ' ' + obj1_obj.id;
name_nolink = name;
}
}
} }
else if (obj1_obj && obj1_obj.name) {
name = ' ' + stripDeleted(obj1_obj.name); if (obj1 === 'user' || obj2 === 'user') {
name_nolink = name; activity.summary_fields.user[0].name = activity.summary_fields.user[0].username;
} }
descr += obj1 + name;
descr_nolink += obj1 + name_nolink;
}
activity['description'] = descr;
activity['description_nolink'] = descr_nolink;
}
}])
.factory('ShowDetail', ['$rootScope', 'Rest', 'Alert', 'GenerateForm', 'ProcessErrors', 'GetBasePath', 'FormatDate', if (obj2_obj && obj2_obj.name && !/^_delete/.test(obj2_obj.name)) {
'ActivityDetailForm', 'Empty', 'Find', obj2_obj.base = obj2;
function($rootScope, Rest, Alert, GenerateForm, ProcessErrors, GetBasePath, FormatDate, ActivityDetailForm, Empty, Find) { descr += obj2 + ' <a href=\"' + BuildUrl(obj2_obj) + '\">' + obj2_obj.name + '</a>' + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
return function(params) { descr_nolink += obj2 + ' ' + obj2_obj.name + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
} else if (obj2) {
var activity_id = params.activity_id; name = '';
var parent_scope = params.scope; if (obj2_obj && obj2_obj.name) {
name = ' ' + stripDeleted(obj2_obj.name);
var generator = GenerateForm;
var form = ActivityDetailForm;
var activity = Find({list: parent_scope.activities, key: 'id', val: activity_id });
if (activity) {
// Setup changes field
activity['changes_stringified'] = JSON.stringify(activity['changes'], null, '\t');
var n = activity['changes_stringified'].match(/\n/g);
var rows = (n) ? n.length : 1;
rows = (rows < 1) ? 3 : 10;
form.fields['changes'].rows = 10;
// Load the form
var scope = generator.inject(form, { mode: 'edit', modal: true, related: false });
scope['changes'] = activity['changes_stringified'];
scope['user'] = ( (activity.summary_fields.actor) ? activity.summary_fields.actor.username : 'system' ) +
' on ' + FormatDate(new Date(activity['timestamp']));
scope['operation'] = activity['description_nolink'];
scope.formModalAction = function() {
$('#form-modal').modal("hide");
} }
descr += obj2 + name + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
$('#form-modal').on('show.bs.modal', function (e) { descr_nolink += obj2 + name + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
$('#form-modal-body').css({ }
width:'auto', //probably not needed if (obj1_obj && obj1_obj.name && !/^\_delete/.test(obj1_obj.name)) {
height:'auto', //probably not needed obj1_obj.base = obj1;
'max-height':'100%' descr += obj1 + ' <a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.name + '</a>';
descr_nolink += obj1 + ' ' + obj1_obj.name;
} else if (obj1) {
name = '';
name_nolink = '';
// find the name in changes, if needed
if (!(obj1_obj && obj1_obj.name) || obj1_obj && obj1_obj.name && /^_delete/.test(obj1_obj.name)) {
if (activity.changes && activity.changes.name) {
if (typeof activity.changes.name === 'string') {
name = ' ' + activity.changes.name;
name_nolink = name;
} else if (typeof activity.changes.name === 'object' && Array.isArray(activity.changes.name)) {
name = ' ' + activity.changes.name[0];
name_nolink = name;
}
} else if (obj1 === 'job' && obj1_obj && activity.changes && activity.changes.job_template) {
// Hack for job activity where the template name is known
if (activity.operation !== 'delete') {
obj1_obj.base = obj1;
name = ' ' + '<a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.id + ' ' + activity.changes.job_template + '</a>';
name_nolink = ' ' + obj1_obj.id + ' ' + activity.changes.job_template;
} else {
name = ' ' + obj1_obj.id + ' ' + activity.changes.job_template;
name_nolink = name;
}
} else if (obj1 === 'job' && obj1_obj) {
// Hack for job activity where template name not known
if (activity.operation !== 'delete') {
obj1_obj.base = obj1;
name = ' ' + '<a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.id + '</a>';
name_nolink = ' ' + obj1_obj.id;
} else {
name = ' ' + obj1_obj.id;
name_nolink = name;
}
}
} else if (obj1_obj && obj1_obj.name) {
name = ' ' + stripDeleted(obj1_obj.name);
name_nolink = name;
}
descr += obj1 + name;
descr_nolink += obj1 + name_nolink;
}
activity.description = descr;
activity.description_nolink = descr_nolink;
};
}
])
.factory('ShowDetail', ['$rootScope', 'Rest', 'Alert', 'GenerateForm', 'ProcessErrors', 'GetBasePath', 'FormatDate',
'ActivityDetailForm', 'Empty', 'Find',
function ($rootScope, Rest, Alert, GenerateForm, ProcessErrors, GetBasePath, FormatDate, ActivityDetailForm, Empty, Find) {
return function (params) {
var activity_id = params.activity_id,
parent_scope = params.scope,
generator = GenerateForm,
form = ActivityDetailForm,
activity = Find({ list: parent_scope.activities, key: 'id', val: activity_id }),
n, rows, scope;
if (activity) {
// Setup changes field
activity.changes_stringified = JSON.stringify(activity.changes, null, '\t');
n = activity.changes_stringified.match(/\n/g);
rows = (n) ? n.length : 1;
rows = (rows < 1) ? 3 : 10;
form.fields.changes.rows = 10;
// Load the form
scope = generator.inject(form, { mode: 'edit', modal: true, related: false });
scope.changes = activity.changes_stringified;
scope.user = ((activity.summary_fields.actor) ? activity.summary_fields.actor.username : 'system') +
' on ' + FormatDate(new Date(activity.timestamps));
scope.operation = activity.description_nolink;
scope.formModalAction = function () {
$('#form-modal').modal("hide");
};
$('#form-modal').on('show.bs.modal', function () {
$('#form-modal-body').css({
width: 'auto', //probably not needed
height: 'auto', //probably not needed
'max-height': '100%'
}); });
}); });
scope.formModalActionLabel = 'OK'; scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false; scope.formModalCancelShow = false;
scope.formModalInfo = false; scope.formModalInfo = false;
scope.formModalHeader = "Event " + activity.id; scope.formModalHeader = "Event " + activity.id;
if (!scope.$$phase) { if (!scope.$$phase) {
scope.$digest(); scope.$digest();
}
} }
} };
}
])
} .factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit',
}]) 'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'BuildDescription', 'FixUrl', 'BuildUrl',
'ShowDetail', 'StreamBreadCrumbs', 'setStreamHeight', 'Find', 'Store',
.factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit', function ($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList,
'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'BuildDescription', 'FixUrl', 'BuildUrl',
'ShowDetail', 'StreamBreadCrumbs', 'setStreamHeight', 'Find', 'Store',
function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList,
FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail, StreamBreadCrumbs, setStreamHeight, FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail, StreamBreadCrumbs, setStreamHeight,
Find, Store) { Find, Store) {
return function(params) { return function (params) {
var list = StreamList;
var defaultUrl = GetBasePath('activity_stream');
var view = GenerateList;
var base = $location.path().replace(/^\//,'').split('/')[0];
var parent_scope = params.scope;
// Hang onto current search params var list = StreamList,
var PreviousSearchParams = Store('CurrentSearchParams'); defaultUrl = GetBasePath('activity_stream'),
view = GenerateList,
base = $location.path().replace(/^\//, '').split('/')[0],
parent_scope = params.scope,
PreviousSearchParams = Store('CurrentSearchParams'),
inventory_name = (params && params.inventory_name) ? params.inventory_name : null,
url = (params && params.url) ? params.url : null,
type, paths, itm, scope;
// pass in an inventory name to fix breadcrumb display $rootScope.flashMessage = null;
var inventory_name = (params && params.inventory_name) ? params.inventory_name : null;
// url will override the attempt to compute an activity_stream query if (url) {
var url = (params && params.url) ? params.url : null; defaultUrl = url;
} else {
$rootScope.flashMessage = null; if ($location.path() !== '/home') {
// Restrict what we're looking at based on the path
if (url) { type = (base === 'inventories') ? 'inventory' : base.replace(/s$/, '');
defaultUrl = url; paths = $location.path().split('/');
} paths.splice(0, 1);
else { if (paths.length > 1 && /^\d+/.test(paths[paths.length - 1])) {
if ($location.path() !== '/home') { type = paths[paths.length - 2];
// Restrict what we're looking at based on the path type = (type === 'inventories') ? 'inventory' : type.replace(/s$/, '');
var type = (base == 'inventories') ? 'inventory' : base.replace(/s$/,''); //defaultUrl += '?object1=' + type + '&object1__id=' +
var paths = $location.path().split('/'); defaultUrl += '?' + type + '__id=' + paths[paths.length - 1];
paths.splice(0,1); } else if (paths.length > 1) {
if (paths.length > 1 && /^\d+/.test(paths[paths.length - 1])) { type = paths[paths.length - 1];
type = paths[paths.length - 2]; type = (type === 'inventories') ? 'inventory' : type.replace(/s$/, '');
type = (type == 'inventories') ? 'inventory' : type.replace(/s$/,''); defaultUrl += '?or__object1=' + type + '&or__object2=' + type;
//defaultUrl += '?object1=' + type + '&object1__id=' + } else {
defaultUrl += '?' + type + '__id=' + paths[paths.length - 1]; defaultUrl += '?or__object1=' + type + '&or__object2=' + type;
} }
else if (paths.length > 1) {
type = paths[paths.length - 1];
type = (type == 'inventories') ? 'inventory' : type.replace(/s$/,'');
defaultUrl += '?or__object1=' + type + '&or__object2=' + type;
}
else {
defaultUrl += '?or__object1=' + type + '&or__object2=' + type;
} }
} }
}
// Add a container for the stream widget // Add a container for the stream widget
$('#tab-content-container').append("<div id=\"stream-container\"><div id=\"stream-content\"></div></div><!-- Stream widget -->"); $('#tab-content-container').append("<div id=\"stream-container\"><div id=\"stream-content\"></div></div><!-- Stream widget -->");
StreamBreadCrumbs();
// Fix inventory name. The way we're doing breadcrumbs doesn't support bind variables. StreamBreadCrumbs();
if (inventory_name) {
var itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory_name }}' }); // Fix inventory name. The way we're doing breadcrumbs doesn't support bind variables.
if (itm) { if (inventory_name) {
itm.title = inventory_name; itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory_name }}' });
if (itm) {
itm.title = inventory_name;
}
} }
}
ShowStream(); ShowStream();
// Generate the list // Generate the list
var scope = view.inject(list, { scope = view.inject(list, { mode: 'edit', id: 'stream-content', searchSize: 'col-lg-3', secondWidget: true, activityStream: true });
mode: 'edit',
id: 'stream-content', // descriptive title describing what AS is showing
searchSize: 'col-lg-3', scope.streamTitle = (params && params.title) ? params.title : null;
secondWidget: true,
activityStream: true scope.closeStream = function (inUrl) {
HideStream();
if (scope.searchCleanup) {
scope.searchCleanup();
}
// Restore prior search state
if (PreviousSearchParams) {
SearchInit({
scope: parent_scope,
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false
});
}
if (inUrl) {
$location.path(inUrl);
}
};
scope.refreshStream = function () {
scope.search(list.iterator);
};
scope.showDetail = function (id) {
ShowDetail({
scope: scope,
activity_id: id
});
};
if (scope.removeStreamPostRefresh) {
scope.removeStreamPostRefresh();
}
scope.removeStreamPostRefresh = scope.$on('PostRefresh', function () {
var i, cDate, href, deleted, obj1, obj2;
for (i = 0; i < scope.activities.length; i++) {
// Convert event_time date to local time zone
cDate = new Date(scope.activities[i].timestamp);
scope.activities[i].timestamp = FormatDate(cDate);
if (scope.activities[i].summary_fields.actor) {
scope.activities[i].user = "<a href=\"/#/users/" + scope.activities[i].summary_fields.actor.id + "\">" +
scope.activities[i].summary_fields.actor.username + "</a>";
} else {
scope.activities[i].user = 'system';
}
// Objects
deleted = /^\_delete/;
obj1 = scope.activities[i].object1;
obj2 = scope.activities[i].object2;
if (obj1 && scope.activities[i].summary_fields[obj1] && scope.activities[i].summary_fields[obj1].name) {
if (!deleted.test(scope.activities[i].summary_fields[obj1].name)) {
href = BuildUrl(scope.activities[i].summary_fields.object1);
scope.activities[i].objects = "<a href=\"" + href + "\">" + scope.activities[i].summary_fields[obj1].name + "</a>";
} else {
scope.activities[i].objects = scope.activities[i].summary_fields[obj1].name;
}
} else if (scope.activities[i].object1) {
scope.activities[i].objects = scope.activities[i].object1;
}
if (obj2 && scope.activities[i].summary_fields[obj2] && scope.activities[i].summary_fields[obj2].name) {
if (!deleted.test(scope.activities[i].summary_fields.object2.name)) {
href = BuildUrl(scope.activities[i].summary_fields.object2);
scope.activities[i].objects += ", <a href=\"" + href + "\">" + scope.activities[i].summary_fields[obj2].name + "</a>";
} else {
scope.activities[i].objects += "," + scope.activities[i].summary_fields[obj2].name;
}
} else if (scope.activities[i].object2) {
scope.activities[i].objects += ", " + scope.activities[i].object2;
}
BuildDescription(scope.activities[i]);
}
// Give ng-repeate a chance to show the data before adjusting the page size.
setTimeout(function () {
setStreamHeight();
}, 500);
}); });
// descriptive title describing what AS is showing // Initialize search and paginate pieces and load data
scope.streamTitle = (params && params.title) ? params.title : null; SearchInit({
scope: scope,
scope.closeStream = function(inUrl) { set: list.name,
HideStream(); list: list,
if (scope.searchCleanup) { url: defaultUrl
scope.searchCleanup(); });
} PaginateInit({
// Restore prior search state scope: scope,
if (PreviousSearchParams) { list: list,
SearchInit({ url: defaultUrl
scope: parent_scope, });
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false });
}
if (inUrl) {
$location.path(inUrl);
}
}
scope.refreshStream = function() {
scope.search(list.iterator); scope.search(list.iterator);
} };
}
scope.showDetail = function(id) { ]);
ShowDetail({ scope: scope, activity_id: id });
}
if (scope.removeStreamPostRefresh) {
scope.removeStreamPostRefresh();
}
scope.removeStreamPostRefresh = scope.$on('PostRefresh', function() {
var cDate, href, deleted, obj1, obj2;
for (var i=0; i < scope['activities'].length; i++) {
// Convert event_time date to local time zone
cDate = new Date(scope['activities'][i].timestamp);
scope['activities'][i].timestamp = FormatDate(cDate);
if (scope['activities'][i]['summary_fields']['actor']) {
scope['activities'][i]['user'] = "<a href=\"/#/users/" + scope['activities'][i]['summary_fields']['actor']['id'] + "\">" +
scope['activities'][i]['summary_fields']['actor']['username'] + "</a>";
}
else {
scope['activities'][i]['user'] = 'system';
}
// Objects
deleted = /^\_delete/;
obj1 = scope['activities'][i].object1;
obj2 = scope['activities'][i].object2;
if ( obj1 && scope['activities'][i].summary_fields[obj1] && scope['activities'][i].summary_fields[obj1].name) {
if ( !deleted.test(scope['activities'][i].summary_fields[obj1].name) ) {
href = BuildUrl(scope['activities'][i].summary_fields.object1);
scope['activities'][i].objects = "<a href=\"" + href + "\">" + scope['activities'][i].summary_fields[obj1].name + "</a>";
}
else {
scope['activities'][i].objects = scope['activities'][i].summary_fields[obj1].name;
}
}
else if (scope['activities'][i].object1) {
scope['activities'][i].objects = scope['activities'][i].object1;
}
if (obj2 && scope['activities'][i].summary_fields[obj2] && scope['activities'][i].summary_fields[obj2].name) {
if ( !deleted.test(scope['activities'][i].summary_fields.object2.name) ) {
href = BuildUrl(scope['activities'][i].summary_fields.object2);
scope['activities'][i].objects += ", <a href=\"" + href + "\">" + scope['activities'][i].summary_fields[obj2].name + "</a>";
}
else {
scope['activities'][i].objects += "," + scope['activities'][i].summary_fields[obj2].name;
}
}
else if (scope['activities'][i].object2) {
scope['activities'][i].objects += ", " + scope['activities'][i].object2;
}
BuildDescription(scope['activities'][i]);
}
// Give ng-repeate a chance to show the data before adjusting the page size.
setTimeout(function() { setStreamHeight(); }, 500);
});
// Initialize search and paginate pieces and load data
SearchInit({ scope: scope, set: list.name, list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
scope.search(list.iterator);
}
}]);

View File

@@ -4,129 +4,138 @@
* AuthService.js * AuthService.js
* *
* User authentication functions * User authentication functions
*
*/ */
'use strict'; 'use strict';
angular.module('AuthService', ['ngCookies', 'Utilities']) angular.module('AuthService', ['ngCookies', 'Utilities'])
.factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath', .factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath',
function($http, $rootScope, $location, $cookieStore, GetBasePath) { function ($http, $rootScope, $location, $cookieStore, GetBasePath) {
return { return {
setToken: function(token, expires) { setToken: function (token, expires) {
// set the session cookie // set the session cookie
$cookieStore.remove('token'); $cookieStore.remove('token');
$cookieStore.remove('token_expires'); $cookieStore.remove('token_expires');
$cookieStore.remove('userLoggedIn'); $cookieStore.remove('userLoggedIn');
$cookieStore.put('token', token); $cookieStore.put('token', token);
$cookieStore.put('token_expires', expires); $cookieStore.put('token_expires', expires);
$cookieStore.put('userLoggedIn', true); $cookieStore.put('userLoggedIn', true);
$cookieStore.put('sessionExpired', false); $cookieStore.put('sessionExpired', false);
$rootScope.token = token; $rootScope.token = token;
$rootScope.userLoggedIn = true; $rootScope.userLoggedIn = true;
$rootScope.token_expires = expires; $rootScope.token_expires = expires;
$rootScope.sessionExpired = false; $rootScope.sessionExpired = false;
}, },
isUserLoggedIn: function() { isUserLoggedIn: function () {
if ($rootScope.userLoggedIn === undefined) { if ($rootScope.userLoggedIn === undefined) {
// Browser refresh may have occurred // Browser refresh may have occurred
$rootScope.userLoggedIn = $cookieStore.get('userLoggedIn'); $rootScope.userLoggedIn = $cookieStore.get('userLoggedIn');
$rootScope.sessionExpired = $cookieStore.get('sessionExpired'); $rootScope.sessionExpired = $cookieStore.get('sessionExpired');
}
return $rootScope.userLoggedIn;
},
getToken: function() {
return ($rootScope.token) ? $rootScope.token : $cookieStore.get('token');
},
retrieveToken: function(username, password) {
return $http({ method: 'POST', url: GetBasePath('authtoken'),
data: {"username": username, "password": password} });
},
logout: function() {
// the following puts our primary scope up for garbage collection, which
// should prevent content flash from the prior user.
var scope = angular.element(document.getElementById('main-view')).scope();
scope.$destroy();
$rootScope.$destroy();
$cookieStore.remove('accordions');
$cookieStore.remove('token');
$cookieStore.remove('token_expires');
$cookieStore.remove('current_user');
$cookieStore.remove('lastPath');
$cookieStore.put('userLoggedIn', false);
$cookieStore.put('sessionExpired', false);
$cookieStore.remove('lastPath', '/home');
$rootScope.current_user = {};
$rootScope.license_tested = undefined;
$rootScope.userLoggedIn = false;
$rootScope.sessionExpired = false;
$rootScope.token = null;
$rootScope.token_expires = null;
$rootScope.lastPath = '/home';
},
getLicense: function() {
return $http({
method: 'GET',
url: GetBasePath('config'),
headers: { 'Authorization': 'Token ' + this.getToken() }
});
},
setLicense: function(license) {
license.tested = false;
$cookieStore.put('license', license);
},
licenseTested: function() {
var license, result;
if ($rootScope.license_tested !== undefined) {
result = $rootScope.license_tested;
}
else {
license = $cookieStore.get('license');
if (license && license.tested !== undefined) {
result = license.tested;
} }
else { return $rootScope.userLoggedIn;
result = false; },
getToken: function () {
return ($rootScope.token) ? $rootScope.token : $cookieStore.get('token');
},
retrieveToken: function (username, password) {
return $http({
method: 'POST',
url: GetBasePath('authtoken'),
data: {
"username": username,
"password": password
}
});
},
logout: function () {
// the following puts our primary scope up for garbage collection, which
// should prevent content flash from the prior user.
var scope = angular.element(document.getElementById('main-view')).scope();
scope.$destroy();
$rootScope.$destroy();
$cookieStore.remove('accordions');
$cookieStore.remove('token');
$cookieStore.remove('token_expires');
$cookieStore.remove('current_user');
$cookieStore.remove('lastPath');
$cookieStore.put('userLoggedIn', false);
$cookieStore.put('sessionExpired', false);
$cookieStore.remove('lastPath', '/home');
$rootScope.current_user = {};
$rootScope.license_tested = undefined;
$rootScope.userLoggedIn = false;
$rootScope.sessionExpired = false;
$rootScope.token = null;
$rootScope.token_expires = null;
$rootScope.lastPath = '/home';
},
getLicense: function () {
return $http({
method: 'GET',
url: GetBasePath('config'),
headers: {
'Authorization': 'Token ' + this.getToken()
}
});
},
setLicense: function (license) {
license.tested = false;
$cookieStore.put('license', license);
},
licenseTested: function () {
var license, result;
if ($rootScope.license_tested !== undefined) {
result = $rootScope.license_tested;
} else {
license = $cookieStore.get('license');
if (license && license.tested !== undefined) {
result = license.tested;
} else {
result = false;
}
} }
return result;
},
getUser: function () {
return $http({
method: 'GET',
url: '/api/v1/me/',
headers: {
'Authorization': 'Token ' + this.getToken()
}
});
},
setUserInfo: function (response) {
// store the response values in $rootScope so we can get to them later
$rootScope.current_user = response.results[0];
$cookieStore.put('current_user', response.results[0]); //keep in session cookie in the event of browser refresh
},
restoreUserInfo: function () {
$rootScope.current_user = $cookieStore.get('current_user');
},
getUserInfo: function (key) {
// Access values returned from the Me API call
var cu;
if ($rootScope.current_user) {
return $rootScope.current_user[key];
}
this.restoreUserInfo();
cu = $cookieStore.get('current_user');
return cu[key];
} }
return result; };
}, }
]);
getUser: function() {
return $http({
method: 'GET',
url: '/api/v1/me/',
headers: { 'Authorization': 'Token ' + this.getToken() }
});
},
setUserInfo: function(response) {
// store the response values in $rootScope so we can get to them later
$rootScope.current_user = response.results[0];
$cookieStore.put('current_user', response.results[0]); //keep in session cookie in the event of browser refresh
},
restoreUserInfo: function() {
$rootScope.current_user = $cookieStore.get('current_user');
},
getUserInfo: function(key) {
// Access values returned from the Me API call
var cu;
if ($rootScope.current_user ) {
return $rootScope.current_user[key];
}
this.restoreUserInfo();
cu = $cookieStore.get('current_user');
return cu[key];
}
};
}]);

View File

@@ -1,4 +1,3 @@
/************************************ /************************************
* *
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
@@ -8,86 +7,104 @@
* Build data for the tree selector table used on inventory detail page. * Build data for the tree selector table used on inventory detail page.
* *
*/ */
'use strict'; 'use strict';
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog']) angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
.factory('SortNodes', [ function() { .factory('SortNodes', [
return function(data) { function () {
return function (data) {
//Sort nodes by name //Sort nodes by name
var names = []; var i, j, names = [], newData = [];
var newData = []; for (i = 0; i < data.length; i++) {
for (var i=0; i < data.length; i++) {
names.push(data[i].name); names.push(data[i].name);
} }
names.sort(); names.sort();
for (var j=0; j < names.length; j++) { for (j = 0; j < names.length; j++) {
for (i=0; i < data.length; i++) { for (i = 0; i < data.length; i++) {
if (data[i].name == names[j]) { if (data[i].name === names[j]) {
newData.push(data[i]); newData.push(data[i]);
} }
} }
} }
return newData; return newData;
} };
}]) }
])
.factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
function (Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg) {
return function (params) {
var inventory_id = params.inventory_id,
scope = params.scope,
refresh = params.refresh,
emit = params.emit,
new_group_id = params.new_group_id,
groups = [],
id = 1;
.factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
function(Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg) {
return function(params) {
var inventory_id = params.inventory_id;
var scope = params.scope;
var refresh = params.refresh;
var emit = params.emit;
var new_group_id = params.new_group_id;
var groups = [];
var id = 1;
function buildAllHosts(tree_data) { function buildAllHosts(tree_data) {
// Start our tree object with All Hosts // Start our tree object with All Hosts
var children = []; var children = [],
var sorted = SortNodes(tree_data); sorted = SortNodes(tree_data),
for (var j=0; j < sorted.length; j++) { j, all_hosts;
children.push(sorted[j].id);
} for (j = 0; j < sorted.length; j++) {
var all_hosts = { children.push(sorted[j].id);
name: 'All Hosts', id: 1, group_id: null, parent: 0, description: '', show: true, ngicon: null,
has_children: false, related: {}, selected_class: '', show_failures: false, isDraggable: false,
isDroppable: true, children: children };
groups.push(all_hosts);
} }
all_hosts = {
name: 'All Hosts',
id: 1,
group_id: null,
parent: 0,
description: '',
show: true,
ngicon: null,
has_children: false,
related: {},
selected_class: '',
show_failures: false,
isDraggable: false,
isDroppable: true,
children: children
};
groups.push(all_hosts);
}
function buildGroups(tree_data, parent, level) { function buildGroups(tree_data, parent, level) {
var sorted = SortNodes(tree_data);
for (var i=0; i < sorted.length; i++) { var i, j, children, stat, hosts_status, group,
sorted = SortNodes(tree_data);
for (i = 0; i < sorted.length; i++) {
id++; id++;
stat = GetSyncStatusMsg({
var stat = GetSyncStatusMsg({
status: sorted[i].summary_fields.inventory_source.status status: sorted[i].summary_fields.inventory_source.status
}); // from helpers/Groups.js }); // from helpers/Groups.js
var hosts_status = GetHostsStatusMsg({ hosts_status = GetHostsStatusMsg({
active_failures: sorted[i].hosts_with_active_failures, active_failures: sorted[i].hosts_with_active_failures,
total_hosts: sorted[i].total_hosts, total_hosts: sorted[i].total_hosts,
inventory_id: inventory_id, inventory_id: inventory_id,
group_id: sorted[i].id group_id: sorted[i].id
}); // from helpers/Groups.js }); // from helpers/Groups.js
var children = []; children = [];
for (var j=0; j < sorted[i].children.length; j++) { for (j = 0; j < sorted[i].children.length; j++) {
children.push(sorted[i].children[j].id); children.push(sorted[i].children[j].id);
} }
var group = { group = {
name: sorted[i].name, name: sorted[i].name,
has_active_failures: sorted[i].has_active_failures, has_active_failures: sorted[i].has_active_failures,
total_hosts: sorted[i].total_hosts, total_hosts: sorted[i].total_hosts,
hosts_with_active_failures: sorted[i].hosts_with_active_failures, hosts_with_active_failures: sorted[i].hosts_with_active_failures,
total_groups: sorted[i].total_groups, total_groups: sorted[i].total_groups,
groups_with_active_failures: sorted[i].groups_with_active_failures, groups_with_active_failures: sorted[i].groups_with_active_failures,
parent: parent, parent: parent,
has_children: (sorted[i].children.length > 0) ? true : false, has_children: (sorted[i].children.length > 0) ? true : false,
has_inventory_sources: sorted[i].has_inventory_sources, has_inventory_sources: sorted[i].has_inventory_sources,
id: id, id: id,
@@ -97,25 +114,25 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
children: children, children: children,
ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-square-o node-no-toggle', ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-square-o node-no-toggle',
ngclick: 'toggle(' + id + ')', ngclick: 'toggle(' + id + ')',
related: { related: {
children: (sorted[i].children.length > 0) ? sorted[i].related.children : '', children: (sorted[i].children.length > 0) ? sorted[i].related.children : '',
inventory_source: sorted[i].related.inventory_source inventory_source: sorted[i].related.inventory_source
}, },
status: sorted[i].summary_fields.inventory_source.status, status: sorted[i].summary_fields.inventory_source.status,
status_class: stat['class'], status_class: stat['class'],
status_tooltip: stat['tooltip'], status_tooltip: stat.tooltip,
launch_tooltip: stat['launch_tip'], launch_tooltip: stat.launch_tip,
launch_class: stat['launch_class'], launch_class: stat.launch_class,
hosts_status_tip: hosts_status['tooltip'], hosts_status_tip: hosts_status.tooltip,
show_failures: hosts_status['failures'], show_failures: hosts_status.failures,
hosts_status_class: hosts_status['class'], hosts_status_class: hosts_status['class'],
selected_class: '', selected_class: '',
show: true, show: true,
isDraggable: true, isDraggable: true,
isDroppable: true isDroppable: true
} };
groups.push(group); groups.push(group);
if (new_group_id && group.group_id == new_group_id) { if (new_group_id && group.group_id === new_group_id) {
// For new group // For new group
scope.selected_tree_id = id; scope.selected_tree_id = id;
scope.selected_group_id = group.group_id; scope.selected_group_id = group.group_id;
@@ -130,62 +147,66 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
if (scope.buildAllGroupsRemove) { if (scope.buildAllGroupsRemove) {
scope.buildAllGroupsRemove(); scope.buildAllGroupsRemove();
} }
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree) { scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function (e, inventory_name, inventory_tree) {
Rest.setUrl(inventory_tree); Rest.setUrl(inventory_tree);
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success(function (data) {
buildAllHosts(data); buildAllHosts(data);
buildGroups(data, 0, 0); buildGroups(data, 0, 0);
scope.autoShowGroupHelp = (data.length == 0) ? true : false; scope.autoShowGroupHelp = (data.length === 0) ? true : false;
if (refresh) { if (refresh) {
scope.groups = groups; scope.groups = groups;
scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit); scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit);
} } else {
else {
scope.$emit('GroupTreeLoaded', inventory_name, groups, emit); scope.$emit('GroupTreeLoaded', inventory_name, groups, emit);
} }
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, {
{ hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status }); hdr: 'Error!',
msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status
}); });
}); });
});
function loadTreeData() { function loadTreeData() {
// Load the inventory root node // Load the inventory root node
Wait('start'); Wait('start');
Rest.setUrl (GetBasePath('inventory') + inventory_id + '/'); Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success(function (data) {
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups); scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, {
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); hdr: 'Error!',
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
}); });
} });
}
loadTreeData(); loadTreeData();
} };
}]) }
])
// Update a group with a set of properties
.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty', // Update a group with a set of properties
function(ApplyEllipsis, GetSyncStatusMsg, Empty) { .factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty',
return function(params) { function (ApplyEllipsis, GetSyncStatusMsg, Empty) {
return function (params) {
var scope = params.scope;
var group_id = params.group_id; var scope = params.scope,
var properties = params.properties; // object of key:value pairs to update group_id = params.group_id,
var old_name, stat; properties = params.properties,
for (var i=0; i < scope.groups.length; i++) { i, p, grp, old_name, stat;
for (i = 0; i < scope.groups.length; i++) {
if (scope.groups[i].group_id === group_id) { if (scope.groups[i].group_id === group_id) {
var grp = scope.groups[i]; grp = scope.groups[i];
for (var p in properties) { for (p in properties) {
if (p === 'name') { if (p === 'name') {
old_name = scope.groups[i].name; old_name = scope.groups[i].name;
} }
@@ -195,13 +216,14 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
if (!Empty(properties[p]) && (scope.groups[i].status === 'none' || Empty(scope.groups[i].status))) { if (!Empty(properties[p]) && (scope.groups[i].status === 'none' || Empty(scope.groups[i].status))) {
// We have a source but no status, seed the status with 'never' to enable sync button // We have a source but no status, seed the status with 'never' to enable sync button
scope.groups[i].status = 'never updated'; scope.groups[i].status = 'never updated';
} } else if (!properties[p]) {
else if (!properties[p]) {
// User removed source // User removed source
scope.groups[i].status = 'none'; scope.groups[i].status = 'none';
} }
// Update date sync status links/icons // Update date sync status links/icons
stat = GetSyncStatusMsg({ status: scope.groups[i].status }); stat = GetSyncStatusMsg({
status: scope.groups[i].status
});
scope.groups[i].status_class = stat['class']; scope.groups[i].status_class = stat['class'];
scope.groups[i].status_tooltip = stat.tooltip; scope.groups[i].status_tooltip = stat.tooltip;
scope.groups[i].launch_tooltip = stat.launch_tip; scope.groups[i].launch_tooltip = stat.launch_tip;
@@ -211,7 +233,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
scope.groups[i][p] = properties[p]; scope.groups[i][p] = properties[p];
} }
} }
if (scope.groups[i].id == scope.selected_tree_id) { if (scope.groups[i].id === scope.selected_tree_id) {
//Make sure potential group name change gets reflected throughout the page //Make sure potential group name change gets reflected throughout the page
scope.selected_group_name = scope.groups[i].name; scope.selected_group_name = scope.groups[i].name;
scope.search_place_holder = 'Search ' + scope.groups[i].name; scope.search_place_holder = 'Search ' + scope.groups[i].name;
@@ -221,77 +243,76 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
// Update any titles attributes created by ApplyEllipsis // Update any titles attributes created by ApplyEllipsis
if (old_name) { if (old_name) {
setTimeout(function() { setTimeout(function () {
$('#groups_table .group-name a[title="' + old_name + '"]').attr('title',properties.name); $('#groups_table .group-name a[title="' + old_name + '"]').attr('title', properties.name);
ApplyEllipsis('#groups_table .group-name a'); ApplyEllipsis('#groups_table .group-name a');
}, 2500); }, 2500);
} }
} };
}]) }
])
// Set node name and description after an update to Group properties. // Set node name and description after an update to Group properties.
.factory('SetNodeName', [ function() { .factory('SetNodeName', [
return function(params) { function () {
var scope = params.scope; return function (params) {
var name = params.name; var name = params.name,
var descr = params.description; descr = params.description,
var group_id = (params.group_id !== undefined) ? params.group_id : null; group_id = (params.group_id !== undefined) ? params.group_id : null,
var inventory_id = (params.inventory_id != undefined) ? params.inventory_id : null; inventory_id = (params.inventory_id !== undefined) ? params.inventory_id : null;
if (group_id !== null) { if (group_id !== null) {
$('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function(idx) { $('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function () {
$(this).attr('data-name',name); $(this).attr('data-name', name);
$(this).attr('data-description',descr); $(this).attr('data-description', descr);
$(this).find('.activate').first().text(name); $(this).find('.activate').first().text(name);
}); });
} }
if (inventory_id !== null) { if (inventory_id !== null) {
$('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name); $('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name);
} }
} };
}]) }
])
// Copy or Move a group on the tree after drag-n-drop
.factory('CopyMoveGroup', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
function($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
return function(params) {
var scope = params.scope; // Copy or Move a group on the tree after drag-n-drop
var target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }); .factory('CopyMoveGroup', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
var inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id }); function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
return function (params) {
var scope = params.scope,
target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }),
inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id }),
e, html = '';
// Build the html for our prompt dialog // Build the html for our prompt dialog
var html = '';
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n"; html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n"; html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\n"; html += "<div class=\"modal-content\">\n";
html += "<div class=\"modal-header\">\n"; html += "<div class=\"modal-header\">\n";
html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " + html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " +
"data-dismiss=\"modal\" aria-hidden=\"true\">&times;</button>\n"; "data-dismiss=\"modal\" aria-hidden=\"true\">&times;</button>\n";
if (target.id == 1 || inbound.parent == 0) { if (target.id === 1 || inbound.parent === 0) {
// We're moving the group to the top level, or we're moving a top level group down // We're moving the group to the top level, or we're moving a top level group down
html += "<h3>Move Group</h3>\n"; html += "<h3>Move Group</h3>\n";
} } else {
else { html += "<h3>Copy or Move?</h3>\n";
html += "<h3>Copy or Move?</h3>\n";
} }
html += "</div>\n"; html += "</div>\n";
html += "<div class=\"modal-body\">\n"; html += "<div class=\"modal-body\">\n";
if (target.id == 1) { if (target.id === 1) {
html += "<p>Are you sure you want to move group " + inbound.name + " to the top level?</p>"; html += "<p>Are you sure you want to move group " + inbound.name + " to the top level?</p>";
} } else if (inbound.parent === 0) {
else if (inbound.parent == 0) {
html += "<p>Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " + html += "<p>Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " +
target.name + "?</p>"; target.name + "?</p>";
} } else {
else {
html += "<div class=\"text-center\">\n"; html += "<div class=\"text-center\">\n";
html += "<p>Would you like to copy or move group <em>" + inbound.name + "</em> to group <em>" + target.name + "</em>?</p>\n"; html += "<p>Would you like to copy or move group <em>" + inbound.name + "</em> to group <em>" + target.name + "</em>?</p>\n";
html += "<div style=\"margin-top: 30px;\">\n"; html += "<div style=\"margin-top: 30px;\">\n";
@@ -300,12 +321,12 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "</div>\n"; html += "</div>\n";
html += "</div>\n"; html += "</div>\n";
} }
html += "</div>\n"; html += "</div>\n";
html += "<div class=\"modal-footer\">\n"; html += "<div class=\"modal-footer\">\n";
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">Cancel</a>\n"; html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">Cancel</a>\n";
if (target.id == 1 || inbound.parent == 0) { if (target.id === 1 || inbound.parent === 0) {
// We're moving the group to the top level, or we're moving a top level group down // We're moving the group to the top level, or we're moving a top level group down
html += "<a href=\"\" data-target=\"#prompt-modal\" ng-click=\"moveGroup()\" class=\"btn btn-primary\">Yes</a>\n"; html += "<a href=\"\" data-target=\"#prompt-modal\" ng-click=\"moveGroup()\" class=\"btn btn-primary\">Yes</a>\n";
} }
@@ -314,134 +335,135 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "</div><!-- modal-content -->\n"; html += "</div><!-- modal-content -->\n";
html += "</div><!-- modal-dialog -->\n"; html += "</div><!-- modal-dialog -->\n";
html += "</div><!-- modal -->\n"; html += "</div><!-- modal -->\n";
// Inject our custom dialog // Inject our custom dialog
var e = angular.element(document.getElementById('inventory-modal-container')); e= angular.element(document.getElementById('inventory-modal-container'));
e.empty().append(html); e.empty().append(html);
$compile(e)(scope); $compile(e)(scope);
// Display it // Display it
$('#copy-prompt-modal').modal({ $('#copy-prompt-modal').modal({
backdrop: 'static', backdrop: 'static',
keyboard: true, keyboard: true,
show: true show: true
}); });
// Respond to move // Respond to move
scope.moveGroup = function() { scope.moveGroup = function () {
var url, group, parent;
$('#copy-prompt-modal').modal('hide'); $('#copy-prompt-modal').modal('hide');
Wait('start'); Wait('start');
// disassociate the group from the original parent // disassociate the group from the original parent
if (scope.removeGroupRemove) { if (scope.removeGroupRemove) {
scope.removeGroupRemove(); scope.removeGroupRemove();
} }
scope.removeGroupRemove = scope.$on('removeGroup', function() { scope.removeGroupRemove = scope.$on('removeGroup', function () {
if (inbound.parent > 0) { if (inbound.parent > 0) {
// Only remove a group from a parent when the parent is a group and not the inventory root // Only remove a group from a parent when the parent is a group and not the inventory root
var parent = Find({ list: scope.groups, key: 'id', val: inbound.parent }) parent = Find({ list: scope.groups, key: 'id', val: inbound.parent });
var url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.post({ id: inbound.group_id, disassociate: 1 }) Rest.post({ id: inbound.group_id, disassociate: 1 })
.success( function(data, status, headers, config) { .success(function () {
//Triggers refresh of group list in inventory controller //Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted'); scope.$emit('GroupDeleteCompleted');
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, {
{ hdr: 'Error!', msg: 'Failed to remove ' + inbound.name + hdr: 'Error!',
' from ' + parent.name + '. POST returned status: ' + status }); msg: 'Failed to remove ' + inbound.name +
' from ' + parent.name + '. POST returned status: ' + status
}); });
} });
else { } else {
//Triggers refresh of group list in inventory controller //Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted'); scope.$emit('GroupDeleteCompleted');
} }
}); });
// add the new group to the target parent // add the new group to the target parent
var url = (!Empty(target.group_id)) ? url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' : GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/'; GetBasePath('inventory') + scope.inventory_id + '/groups/';
var group = { group = {
id: inbound.group_id, id: inbound.group_id,
name: inbound.name, name: inbound.name,
description: inbound.description, description: inbound.description,
inventory: scope.inventory_id inventory: scope.inventory_id
} };
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(group) Rest.post(group)
.success( function(data, status, headers, config) { .success(function () {
scope.$emit('removeGroup'); scope.$emit('removeGroup');
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' + msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status });
target_name + '. POST returned status: ' + status }); });
}); };
}
scope.copyGroup = function() {
scope.copyGroup = function () {
$('#copy-prompt-modal').modal('hide'); $('#copy-prompt-modal').modal('hide');
Wait('start'); Wait('start');
// add the new group to the target parent // add the new group to the target parent
var url = (!Empty(target.group_id)) ? var url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' : GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/'; GetBasePath('inventory') + scope.inventory_id + '/groups/',
var group = { group = {
id: inbound.group_id, id: inbound.group_id,
name: inbound.name, name: inbound.name,
description: inbound.description, description: inbound.description,
inventory: scope.inventory_id inventory: scope.inventory_id
} };
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(group) Rest.post(group)
.success( function(data, status, headers, config) { .success(function () {
//Triggers refresh of group list in inventory controller //Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted'); scope.$emit('GroupDeleteCompleted');
}) })
.error( function(data, status, headers, config) { .error(function (data, status) {
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Failed to add ' + inbound.name + ' to ' + msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status
target_name + '. POST returned status: ' + status }); });
}); });
} };
} };
}]) }
])
// Copy a host after drag-n-drop
.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath', // Copy a host after drag-n-drop
function($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) { .factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
return function(params) { function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
return function (params) {
var scope = params.scope, var scope = params.scope,
target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }), target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }),
host = Find({ list: scope.hosts, key: 'id', val: params.host_id }), host = Find({ list: scope.hosts, key: 'id', val: params.host_id }),
found = false, i; found = false, e, i, html = '';
if (host.summary_fields.all_groups) { if (host.summary_fields.all_groups) {
for (i=0; i< host.summary_fields.all_groups.length; i++) { for (i = 0; i < host.summary_fields.all_groups.length; i++) {
if (host.summary_fields.all_groups[i].id == target.group_id) { if (host.summary_fields.all_groups[i].id === target.group_id) {
found = true; found = true;
break; break;
} }
} }
} }
if (found) { if (found) {
var html = '';
html += "<div id=\"copy-alert-modal\" class=\"modal fade\">\n"; html += "<div id=\"copy-alert-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n"; html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\n"; html += "<div class=\"modal-content\">\n";
html += "<div class=\"modal-header\">\n"; html += "<div class=\"modal-header\">\n";
html += "<button type=\"button\" class=\"close\" ng-hide=\"disableButtons\" data-target=\"#copy-alert-modal\"\n"; html += "<button type=\"button\" class=\"close\" ng-hide=\"disableButtons\" data-target=\"#copy-alert-modal\"\n";
html += "data-dismiss=\"modal\" class=\"modal\" aria-hidden=\"true\">&times;</button>\n"; html += "data-dismiss=\"modal\" class=\"modal\" aria-hidden=\"true\">&times;</button>\n";
html += "<h3>Already in Group</h3>\n"; html += "<h3>Already in Group</h3>\n";
html += "</div>\n"; html += "</div>\n";
@@ -456,31 +478,30 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "</div>\n"; html += "</div>\n";
// Inject our custom dialog // Inject our custom dialog
var e = angular.element(document.getElementById('inventory-modal-container')); e = angular.element(document.getElementById('inventory-modal-container'));
e.empty().append(html); e.empty().append(html);
$compile(e)(scope); $compile(e)(scope);
// Display it // Display it
$('#copy-alert-modal').modal({ $('#copy-alert-modal').modal({
backdrop: 'static', backdrop: 'static',
keyboard: true, keyboard: true,
show: true show: true
}); });
} } else {
else {
// Build the html for our prompt dialog // Build the html for our prompt dialog
var html = ''; html = '';
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n"; html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n"; html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\n"; html += "<div class=\"modal-content\">\n";
html += "<div class=\"modal-header\">\n"; html += "<div class=\"modal-header\">\n";
html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " + html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " +
"data-dismiss=\"modal\" aria-hidden=\"true\">&times;</button>\n"; "data-dismiss=\"modal\" aria-hidden=\"true\">&times;</button>\n";
html += "<h3>Copy Host</h3>\n"; html += "<h3>Copy Host</h3>\n";
html += "</div>\n"; html += "</div>\n";
html += "<div class=\"modal-body\">\n"; html += "<div class=\"modal-body\">\n";
html += "<p>Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?</p>'; html += "<p>Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?</p>';
html += "</div>\n"; html += "</div>\n";
html += "<div class=\"modal-footer\">\n"; html += "<div class=\"modal-footer\">\n";
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">No</a>\n"; html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">No</a>\n";
@@ -489,35 +510,35 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "</div><!-- modal-content -->\n"; html += "</div><!-- modal-content -->\n";
html += "</div><!-- modal-dialog -->\n"; html += "</div><!-- modal-dialog -->\n";
html += "</div><!-- modal -->\n"; html += "</div><!-- modal -->\n";
// Inject our custom dialog // Inject our custom dialog
var e = angular.element(document.getElementById('inventory-modal-container')); e = angular.element(document.getElementById('inventory-modal-container'));
e.empty().append(html); e.empty().append(html);
$compile(e)(scope); $compile(e)(scope);
// Display it // Display it
$('#copy-prompt-modal').modal({ $('#copy-prompt-modal').modal({
backdrop: 'static', backdrop: 'static',
keyboard: true, keyboard: true,
show: true show: true
}); });
scope.copyHost = function() { scope.copyHost = function () {
$('#copy-prompt-modal').modal('hide'); $('#copy-prompt-modal').modal('hide');
Wait('start'); Wait('start');
Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/'); Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/');
Rest.post(host) Rest.post(host)
.success(function(data, status, headers, config) { .success(function () {
// Signal the controller to refresh the hosts view // Signal the controller to refresh the hosts view
scope.$emit('GroupTreeRefreshed'); scope.$emit('GroupTreeRefreshed');
}) })
.error(function(data, status, headers, config) { .error(function (data, status) {
Wait('stop'); Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' +
{ hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned status: ' + status });
target.name + '. POST returned status: ' + status }); });
}); };
}
} }
} };
}]); }
]);

View File

@@ -4,154 +4,194 @@
* Generic accessor for Ansible Commander services * Generic accessor for Ansible Commander services
* *
*/ */
'use strict';
'use strict';
angular.module('RestServices',['ngCookies','AuthService']) angular.module('RestServices', ['ngCookies', 'AuthService'])
.factory('Rest', ['$http','$rootScope','$cookieStore', '$q', 'Authorization', .factory('Rest', ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization',
function($http, $rootScope, $cookieStore, $q, Authorization) { function ($http, $rootScope, $cookieStore, $q, Authorization) {
return { return {
headers: {},
setUrl: function (url) { headers: {},
this.url = url;
}, setUrl: function (url) {
checkExpired: function() { this.url = url;
return $rootScope.sessionTimer.isExpired(); },
}, checkExpired: function () {
pReplace: function() { return $rootScope.sessionTimer.isExpired();
//in our url, replace :xx params with a value, assuming },
//we can find it in user supplied params. pReplace: function () {
var key,rgx; //in our url, replace :xx params with a value, assuming
for (key in this.params) { //we can find it in user supplied params.
rgx = new RegExp("\\:" + key,'gm'); var key, rgx;
if (rgx.test(this.url)) { for (key in this.params) {
this.url = this.url.replace(rgx,this.params[key]); rgx = new RegExp("\\:" + key, 'gm');
delete this.params[key]; if (rgx.test(this.url)) {
} this.url = this.url.replace(rgx, this.params[key]);
} delete this.params[key];
}, }
createResponse: function(data, status) { }
// Simulate an http response when a token error occurs },
// http://stackoverflow.com/questions/18243286/angularjs-promises-simulate-http-promises createResponse: function (data, status) {
// Simulate an http response when a token error occurs
var promise = $q.reject({ data: data, status: status }); // http://stackoverflow.com/questions/18243286/angularjs-promises-simulate-http-promises
promise.success = function(fn){
promise.then(function(response){ fn(response.data, response.status) }, null); var promise = $q.reject({
return promise data: data,
status: status
});
promise.success = function (fn) {
promise.then(function (response) {
fn(response.data, response.status);
}, null);
return promise;
};
promise.error = function (fn) {
promise.then(null, function (response) {
fn(response.data, response.status);
});
return promise;
};
return promise;
},
setHeader: function (hdr) {
// Pass in { key: value } pairs to be added to the header
for (var h in hdr) {
this.headers[h] = hdr[h];
}
},
get: function (args) {
args = (args) ? args : {};
this.params = (args.params) ? args.params : null;
this.pReplace();
var expired = this.checkExpired(),
token = Authorization.getToken();
if (expired) {
return this.createResponse({
detail: 'Token is expired'
}, 401);
} else if (token) {
this.setHeader({
Authorization: 'Token ' + token
});
this.setHeader({
"X-Auth-Token": 'Token ' + token
});
return $http({
method: 'GET',
url: this.url,
headers: this.headers,
params: this.params
});
} else {
return this.createResponse({
detail: 'Invalid token'
}, 401);
}
},
post: function (data) {
var token = Authorization.getToken(),
expired = this.checkExpired();
if (expired) {
return this.createResponse({
detail: 'Token is expired'
}, 401);
} else if (token) {
this.setHeader({
Authorization: 'Token ' + token
});
this.setHeader({
"X-Auth-Token": 'Token ' + token
});
return $http({
method: 'POST',
url: this.url,
headers: this.headers,
data: data
});
} else {
return this.createResponse({
detail: 'Invalid token'
}, 401);
}
},
put: function (data) {
var token = Authorization.getToken(),
expired = this.checkExpired();
if (expired) {
return this.createResponse({
detail: 'Token is expired'
}, 401);
} else if (token) {
this.setHeader({
Authorization: 'Token ' + token
});
this.setHeader({
"X-Auth-Token": 'Token ' + token
});
return $http({
method: 'PUT',
url: this.url,
headers: this.headers,
data: data
});
} else {
return this.createResponse({
detail: 'Invalid token'
}, 401);
}
},
destroy: function (data) {
var token = Authorization.getToken(),
expired = this.checkExpired();
if (expired) {
return this.createResponse({
detail: 'Token is expired'
}, 401);
} else if (token) {
this.setHeader({
Authorization: 'Token ' + token
});
this.setHeader({
"X-Auth-Token": 'Token ' + token
});
return $http({
method: 'DELETE',
url: this.url,
headers: this.headers,
data: data
});
} else {
return this.createResponse({
detail: 'Invalid token'
}, 401);
}
},
options: function () {
var token = Authorization.getToken(),
expired = this.checkExpired();
if (expired) {
return this.createResponse({
detail: 'Token is expired'
}, 401);
} else if (token) {
this.setHeader({
Authorization: 'Token ' + token
});
this.setHeader({
"X-Auth-Token": 'Token ' + token
});
return $http({
method: 'OPTIONS',
url: this.url,
headers: this.headers
});
} else {
return this.createResponse({
detail: 'Invalid token'
}, 401);
}
}
}; };
promise.error = function(fn){
promise.then(null, function(response){ fn(response.data, response.status) });
return promise;
};
return promise;
},
setHeader: function(hdr) {
// Pass in { key: value } pairs to be added to the header
for (var h in hdr) {
this.headers[h] = hdr[h];
} }
}, ]);
get: function(args) {
args = (args) ? args : {};
this.params = (args.params) ? args.params : null;
this.pReplace();
var expired = this.checkExpired();
var token = Authorization.getToken();
if (expired) {
return this.createResponse({ detail: 'Token is expired' }, 401);
}
else if (token) {
this.setHeader({ Authorization: 'Token ' + token });
this.setHeader({ "X-Auth-Token": 'Token ' + token });
return $http({method: 'GET',
url: this.url,
headers: this.headers,
params: this.params
});
}
else {
return this.createResponse({ detail: 'Invalid token' }, 401);
}
},
post: function(data) {
var token = Authorization.getToken();
var expired = this.checkExpired();
if (expired) {
return this.createResponse({ detail: 'Token is expired' }, 401);
}
else if (token) {
this.setHeader({ Authorization: 'Token ' + token });
this.setHeader({ "X-Auth-Token": 'Token ' + token });
return $http({
method: 'POST',
url: this.url,
headers: this.headers,
data: data });
}
else {
return this.createResponse({ detail: 'Invalid token' }, 401);
}
},
put: function(data) {
var token = Authorization.getToken();
var expired = this.checkExpired();
if (expired) {
return this.createResponse({ detail: 'Token is expired' }, 401);
}
else if (token) {
this.setHeader({ Authorization: 'Token ' + token });
this.setHeader({ "X-Auth-Token": 'Token ' + token });
return $http({
method: 'PUT',
url: this.url,
headers: this.headers,
data: data });
}
else {
return this.createResponse({ detail: 'Invalid token' }, 401);
}
},
destroy: function(data) {
var token = Authorization.getToken();
var expired = this.checkExpired();
if (expired) {
return this.createResponse({ detail: 'Token is expired' }, 401);
}
else if (token) {
this.setHeader({ Authorization: 'Token ' + token });
this.setHeader({ "X-Auth-Token": 'Token ' + token });
return $http({
method: 'DELETE',
url: this.url,
headers: this.headers,
data: data });
}
else {
return this.createResponse({ detail: 'Invalid token' }, 401);
}
},
options: function() {
var token = Authorization.getToken();
var expired = this.checkExpired();
if (expired) {
return this.createResponse({ detail: 'Token is expired' }, 401);
}
else if (token) {
this.setHeader({ Authorization: 'Token ' + token });
this.setHeader({ "X-Auth-Token": 'Token ' + token });
return $http({
method: 'OPTIONS',
url: this.url,
headers: this.headers
});
}
else {
return this.createResponse({ detail: 'Invalid token' }, 401);
}
}
}
}]);

View File

@@ -7,52 +7,51 @@
* duration set in config.js * duration set in config.js
* *
*/ */
'use strict'; 'use strict';
angular.module('TimerService', ['ngCookies', 'Utilities']) angular.module('TimerService', ['ngCookies', 'Utilities'])
.factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty', .factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty',
function($rootScope, $cookieStore, $location, GetBasePath, Empty) { function ($rootScope, $cookieStore) {
return { return {
sessionTime: null, sessionTime: null,
timeout: null, timeout: null,
getSessionTime: function() {
return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime');
},
isExpired: function() { getSessionTime: function () {
var stime = this.getSessionTime(); return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime');
var now = new Date().getTime(); },
if ((stime - now) <= 0) {
//expired
return true;
}
else {
// not expired. move timer forward.
this.moveForward();
return false;
}
},
expireSession: function() {
this.sessionTime = 0;
$rootScope.sessionExpired = true;
$cookieStore.put('sessionExpired', true);
},
moveForward: function() { isExpired: function () {
var t = new Date().getTime() + ($AnsibleConfig.session_timeout * 1000); var stime = this.getSessionTime(),
this.sessionTime = t; now = new Date().getTime();
$cookieStore.put('sessionTime', t); if ((stime - now) <= 0) {
$rootScope.sessionExpired = false; //expired
$cookieStore.put('sessionExpired', false); return true;
}, } else {
// not expired. move timer forward.
this.moveForward();
return false;
}
},
init: function() { expireSession: function () {
this.moveForward(); this.sessionTime = 0;
return this; $rootScope.sessionExpired = true;
} $cookieStore.put('sessionExpired', true);
} },
}]);
moveForward: function () {
var t = new Date().getTime() + ($AnsibleConfig.session_timeout * 1000);
this.sessionTime = t;
$cookieStore.put('sessionTime', t);
$rootScope.sessionExpired = false;
$cookieStore.put('sessionExpired', false);
},
init: function () {
this.moveForward();
return this;
}
};
}
]);

View File

@@ -1,693 +0,0 @@
/************************************
*
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* TreeSelector.js
*
*/
angular.module('TreeSelector', ['Utilities', 'RestServices', 'TreeSelector', 'GroupsHelper'])
.factory('SortNodes', [ function() {
return function(data) {
//Sort nodes by name
var names = [];
var newData = [];
for (var i=0; i < data.length; i++) {
names.push(data[i].name);
}
names.sort();
for (var j=0; j < names.length; j++) {
for (i=0; i < data.length; i++) {
if (data[i].name == names[j]) {
newData.push(data[i]);
}
}
}
return newData;
}
}])
// Figure out the group level tool tip
.factory('GetToolTip', [ 'FormatDate', function(FormatDate) {
return function(params) {
var node = params.node;
var tip = '';
var link = '';
var html_class = '';
var active_failures = node.hosts_with_active_failures;
var total_hosts = node.total_hosts;
var source = node.summary_fields.inventory_source.source;
var status = node.summary_fields.inventory_source.status;
// Return values for the status indicator
var status_date = node.summary_fields.inventory_source.last_updated
var last_update = ( status_date == "" || status_date == null ) ? null : FormatDate(new Date(status_date));
switch (status) {
case 'never updated':
html_class = 'na';
tip = '<p>Inventory update has not been performed.</p>';
link = '';
break;
case 'failed':
tip = '<p>Inventory update failed! Click to view process output.</p>';
link = '/#/inventories/' + node.inventory + '/groups?name=' + node.name;
html_class = true;
break;
case 'successful':
tip = '<p>Inventory update completed on ' + last_update + '.</p>';
html_class = false;
link = '';
break;
case 'updating':
tip = '<p>Inventory update process running now. Click to view status.</p>';
link = '/#/inventories/' + node.inventory + '/groups?name=' + node.name;
html_class = false;
break;
}
if (status !== 'failed' && status !== 'updating') {
// update status will not override job status
if (active_failures > 0) {
tip += "<p>Contains " + active_failures +
[ (active_failures == 1) ? ' host' : ' hosts' ] + ' with failed jobs. Click to view the offending ' +
[ (active_failures == 1) ? ' host' : ' hosts' ] + '.</p>';
link = '/#/inventories/' + node.inventory + '/hosts?has_active_failures=true';
html_class = 'true';
}
else {
if (total_hosts == 0) {
// no hosts
tip += "<p>There are no hosts in this group. It's a sad empty shell.</p>";
html_class = (html_class == '') ? 'na' : html_class;
}
else if (total_hosts == 1) {
// on host with 0 failures
tip += "<p>The 1 host in this group is happy! It does not have a job failure.</p>";
html_class = 'false';
}
else {
// many hosts with 0 failures
tip += "<p>All " + total_hosts + " hosts in this group are happy! None of them have " +
" job failures.</p>";
html_class = 'false';
}
}
}
return { tooltip: tip, url: link, 'class': html_class };
}
}])
.factory('GetInventoryToolTip', [ 'FormatDate', function(FormatDate) {
return function(params) {
var node = params.node;
var tip = '';
var link = '';
var html_class = '';
var active_failures = node.hosts_with_active_failures;
var total_hosts = node.total_hosts;
var group_failures = node.groups_with_active_failures;
var total_groups = node.total_groups;
var inventory_sources = node.total_inventory_sources;
if (group_failures > 0) {
tip += "Has " + group_failures +
[ (group_failures == 1) ? ' group' : ' groups' ] + ' with failed inventory updates. ' +
'Click to view the offending ' +
[ (group_failures == 1) ? ' group.' : ' groups.' ];
link = '/#/inventories/' + node.id + '/groups?status=failed';
html_class = 'true';
}
else if (inventory_sources == 1) {
// on host with 0 failures
tip += "<p>1 group with an inventory source is happy! No updates have failed.</p>";
link = '';
html_class = 'false';
}
else if (inventory_sources > 0) {
tip += "<p>" + inventory_sources + " groups with an inventory source are happy! No updates have failed.</p>";
link = 0;
html_class = 'false';
}
if (html_class !== 'true') {
// Add job status
if (active_failures > 0) {
tip += "<p>Contains " + scope.inventories[i].hosts_with_active_failures +
[ (active_failures == 1) ? ' host' : ' hosts' ] + ' with job failures. Click to view the offending ' +
[ (active_failures == 1) ? ' host' : ' hosts' ] + '.</p>';
link = '/#/inventories/' + node.id + '/hosts?has_active_failures=true';
html_class = 'true';
}
else if (total_hosts == 0) {
tip += "<p>There are no hosts in this inventory. It's a sad empty shell.</p>";
link = "";
html_class = (html_class == '') ? 'na' : html_class;
}
else if (total_hosts == 1) {
tip += "<p>The 1 host found in this inventory is happy! There are no job failures.</p>";
link = "";
html_class = "false";
}
else if (total_hosts > 0) {
tip += "<p>All " + total_hosts + " hosts are happy! There are no job failures.";
link = "";
html_class = "false";
}
}
return { tooltip: tip, url: link, 'class': html_class };
}
}])
.factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile', '$rootScope', 'Wait', 'SortNodes', 'GetToolTip',
'GetInventoryToolTip',
function(Rest, GetBasePath, ProcessErrors, $compile, $rootScope, Wait, SortNodes, GetToolTip, GetInventoryToolTip) {
return function(params) {
var scope = params.scope;
var inventory_id = params.inventory_id;
var emit_on_select = params.emit_on_select;
var target_id = params.target_id;
var refresh_tree = (params.refresh == undefined || params.refresh == false) ? false : true;
var moveable = (params.moveable == undefined || params.moveable == false) ? false : true;
var group_id = params.group_id;
var id = params.id;
var html = '';
var toolTip = 'Hosts have failed jobs?';
var idx = 0;
function refresh(parent) {
var group, title;
var id = parent.attr('id');
if (parent.attr('data-group-id')) {
group = parent.attr('data-group-id');
title = parent.attr('data-name');
}
else {
group = null;
title = 'All Hosts'
}
// The following will trigger the host list to load. See Inventory.js controller.
scope.$emit(emit_on_select, id, group, title);
}
function activate(e) {
/* Set the clicked node as active */
var elm = angular.element(e.target); //<a>
var parent = angular.element(e.target.parentNode.parentNode); //<li>
$('.search-tree .active').removeClass('active');
elm.parent().addClass('active'); // add active class to <div>
refresh(parent);
}
function toggle(e) {
var id, parent, elm, icon;
if (e.target.tagName == 'I') {
id = e.target.parentNode.parentNode.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode.parentNode.parentNode); //<li>
elm = angular.element(e.target.parentNode); // <a>
}
else {
id = e.target.parentNode.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode.parentNode);
elm = angular.element(e.target);
}
var sibling = angular.element(parent.children()[2]); // <a>
var state = parent.attr('data-state');
var icon = angular.element(elm.children()[0]);
if (state == 'closed') {
// expand the elment
var childlists = parent.find('ul');
if (childlists && childlists.length > 0) {
// has childen
for (var i=0; i < childlists.length; i++) {
var listChild = angular.element(childlists[i]);
var listParent = angular.element(listChild.parent());
if (listParent.attr('id') == id) {
angular.element(childlists[i]).removeClass('hidden');
}
}
}
parent.attr('data-state','open');
icon.removeClass('icon-caret-right').addClass('icon-caret-down');
}
else {
// close the element
parent.attr('data-state','closed');
icon.removeClass('icon-caret-down').addClass('icon-caret-right');
var childlists = parent.find('ul');
var sublist, subicon;
if (childlists && childlists.length > 0) {
// has childen
childlists.each(function(idx) {
$(this).addClass('hidden');
subicon = $(this).find('li').first().find('.expand-container i');
subicon.removeClass('icon-caret-down').addClass('icon-caret-right');
});
}
/* When the active node's parent is closed, activate the parent */
if ($(parent).find('.active').length > 0) {
$(parent).find('.active').removeClass('active');
sibling.addClass('active');
refresh(parent);
}
}
}
if (scope.moveNodeRemove) {
scope.moveNodeRemove();
}
scope.moveNodeRemove = scope.$on('MoveNode', function(e, node, parent, target) {
var inv_id = scope['inventory_id'];
var variables;
function cleanUp(state) {
/*
if (state !== 'fail') {
// Visually move the element. Elment will be appended to the
// end of target element list
var elm = $('#' + node.attr('id')).detach();
if (target.find('ul').length > 0) {
// parent has children
target.find('ul').first().append(elm);
}
else {
target.append('<ul></ul>');
target.find('ul').first().append(elm);
}
// Remove any styling that might be left on the target
// and put the expander icon back the way it should be
target.find('div').each(function(idx) {
if (idx > 0 && idx < 3) {
$(this).css({ 'border-bottom': '2px solid #f5f5f5' });
}
});
// Make sure the parent and target have the correct expander class/icon.
function setExpander(n) {
var c = n.find('.expand-container');
var icon;
c.first().empty();
if (n.attr('id') == 'inventory-root-node') {
c.first().html('<i class=\"icon-sitemap\"></i>');
}
else if (c.length > 1) {
// not root and has children, put expander icon back
icon = (n.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right';
c.first().html('<a class="expand"><i class="' + icon + '"></i></a>');
c.first().find('a').first().bind('click', toggle);
}
}
setExpander(target);
setExpander(parent);
}
Wait('stop');
*/
// Reload the tree
html = '';
idx = 0;
loadTreeData();
}
// disassociate the group from the original parent
if (scope.removeGroupRemove) {
scope.removeGroupRemove();
}
scope.removeGroupRemove = scope.$on('removeGroup', function() {
if (parent.attr('data-group-id')) {
// Only remove a group from a parent when the parent is a group and not the inventory root
var url = GetBasePath('base') + 'groups/' + parent.attr('data-group-id') + '/children/';
Rest.setUrl(url);
Rest.post({ id: node.attr('data-group-id'), disassociate: 1 })
.success( function(data, status, headers, config) {
cleanUp('success');
})
.error( function(data, status, headers, config) {
cleanUp('fail');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to remove ' + node.attr('name') + ' from ' +
parent.attr('name') + '. POST returned status: ' + status });
});
}
else {
cleanUp('success');
}
});
if (scope['addToTargetRemove']) {
scope.addToTargetRemove();
}
scope.addToTargetRemove = scope.$on('addToTarget', function() {
// add the new group to the target parent
var url = (target.attr('data-group-id')) ? GetBasePath('base') + 'groups/' + target.attr('data-group-id') + '/children/' :
GetBasePath('inventory') + inv_id + '/groups/';
var group = {
id: node.attr('data-group-id'),
name: node.attr('data-name'),
description: node.attr('data-description'),
inventory: inv_id
}
Rest.setUrl(url);
Rest.post(group)
.success( function(data, status, headers, config) {
scope.$emit('removeGroup');
})
.error( function(data, status, headers, config) {
cleanUp('fail');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' +
target.attr('name') + '. POST returned status: ' + status });
});
});
Wait('start');
// Lookup the inventory. We already have what we need except for variables.
var url = GetBasePath('base') + 'groups/' + node.attr('data-group-id') + '/';
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
variables = (data.variables) ? JSON.parse(data.variables) : "";
scope.$emit('addToTarget');
})
.error( function(data, status, headers, config) {
cleanUp('fail');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to lookup group ' + node.attr('name') +
'. GET returned status: ' + status });
});
});
// The HTML is ready. Insert it into the view.
if (scope.searchTreeReadyRemove) {
scope.searchTreeReadyRemove();
}
scope.searchTreeReadyRemove = scope.$on('searchTreeReady', function(e, html) {
var container = angular.element(document.getElementById(target_id));
container.empty();
var compiled = $compile(html)(scope);
container.append(compiled);
function setTitleWidth(elm) {
// Fix for overflowing title text
var container = $('#search-tree-target');
var container_offset = container.offset();
var parent = elm.parent(); // <li>
var parent_offset = parent.offset();
var expander = parent.find('.expand-container').first();
var badge = parent.find('.badge-container').first();
var width = container.width() - parent_offset.left + container_offset.left -
badge.width() - expander.width() - 10;
elm.css('width', width + 'px');
}
// Fix overflowing title text now
$('#' + target_id).find('.title-container').each(function(idx) {
setTitleWidth($(this));
});
// Fix overflowing title text on screen resize
var timeout;
$(window).resize(function() {
clearTimeout(timeout); //remove prior timer so we don't resize a million times
timeout = setTimeout(function() {
$('#' + target_id).find('.title-container').each(function(idx) {
setTitleWidth($(this));
});
}, 500);
});
var links = container.find('a');
for (var i=0; i < links.length; i++) {
var link = angular.element(links[i]);
if (link.hasClass('expand')) {
link.unbind('click', toggle);
link.bind('click', toggle);
}
if (link.hasClass('activate')) {
link.unbind('click', activate);
link.bind('click', activate);
}
}
if (refresh_tree && group_id !== undefined) {
// pick a node by group_id
$('li[data-group-id="' + group_id + '"] .activate').first().click();
}
else if (refresh_tree && id !== undefined) {
// pick a node by id
$('#' + id + ' .activate').first().click();
}
else if (!refresh_tree) {
// default to the root node
$('#inventory-root-node .activate').first().click();
}
// Make the tree drag-n-droppable
if (moveable) {
$('#selector-tree .activate').draggable({
cursor: "pointer",
cursorAt: { top: -16, left: -10 },
revert: 'invalid',
helper: 'clone',
start: function (e, ui) {
var txt = '[ ' + ui.helper.text() + ' ]';
ui.helper.css({ 'display': 'inline-block', 'font-weight': 'normal', 'color': '#171717',
'background-color': '#f5f5f5', 'overflow': 'visible', 'white-space': 'normal',
'z-index': 5000 }).text(txt);
}
})
.droppable({
//hoverClass: 'droppable-hover',
tolerance: 'pointer',
over: function (e, ui) {
var p = $(this).parent().parent();
p.find('div').each(function(idx) {
if (idx > 0 && idx < 3) {
$(this).css({ 'border-bottom': '2px solid #171717' });
}
});
var c = p.find('.expand-container').first();
c.empty().html('<i class="icon-circle-arrow-right" style="color: #171717;"></i>');
},
out: function (e, ui) {
var p = $(this).parent().parent();
p.find('div').each(function(idx) {
if (idx > 0 && idx < 3) {
$(this).css({ 'border-bottom': '2px solid #f5f5f5' });
}
});
var c = p.find('.expand-container');
var icon;
c.first().empty();
if (c.length > 1) {
// has children, put expander icon back
icon = (p.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right';
c.first().html('<a class="expand"><i class="' + icon + '"></i></a>');
c.first().find('a').first().bind('click', toggle);
}
},
drop: function (e,ui) {
var variables;
var node = ui.draggable.parent().parent(); // node being moved
var parent = node.parent().parent(); // node from
var target = $(this).parent().parent(); // node to
scope.$emit('MoveNode', node, parent, target);
// Make sure angular picks up changes and jQuery doesn't
// leave us in limbo...
if (!scope.$$phase) {
scope.$digest();
}
e.preventDefault();
}
});
} // if moveable
Wait('stop');
});
function buildHTML(tree_data) {
var sorted = SortNodes(tree_data);
var toolTip;
html += (sorted.length > 0) ? "<ul>\n" : "";
for(var i=0; i < sorted.length; i++) {
html += "<li id=\"search-node-0" + idx + "\" data-state=\"opened\" data-hosts=\"" + sorted[i].related.hosts + "\" " +
"data-description=\"" + sorted[i].description + "\" " +
"data-failures=\"" + sorted[i].has_active_failures + "\" " +
"data-groups=\"" + sorted[i].related.groups + "\" " +
"data-name=\"" + sorted[i].name + "\" " +
"data-group-id=\"" + sorted[i].id + "\" " +
"><div class=\"expand-container\">";
if (sorted[i].children.length > 0) {
html += "<a href=\"\" class=\"expand\"><i class=\"icon-caret-down\"></i></a>";
}
else {
html += " ";
}
html += "</div>";
toolTip = GetToolTip({ node: sorted[i] });
html += "<div class=\"badge-container\">";
html += "<a aw-tool-tip=\"" + toolTip.tooltip + "\" data-placement=\"top\"";
html += (toolTip.url !== '') ? " href=\"" + toolTip.url + "\"": "";
html += ">";
html += "<i class=\"field-badge icon-failures-" + toolTip['class'] + "\" ></i>";
html += "</a>";
html += "</div> ";
html += "<div class=\"title-container\"><a class=\"activate\">" + sorted[i].name + "</a></div>";
idx++;
if (sorted[i].children.length > 0) {
buildHTML(sorted[i].children);
}
else {
//html += "<ul></ul>\n";
html += "</li>\n";
}
}
html += "</ul>\n";
}
// Build the HTML for our tree
if (scope.buildAllGroupsRemove) {
scope.buildAllGroupsRemove();
}
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree) {
Rest.setUrl(inventory_tree);
Rest.get()
.success( function(data, status, headers, config) {
buildHTML(data);
scope.$emit('searchTreeReady', html + "</li>\n</ul>\n</div>\n");
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status });
});
});
// Builds scope.inventory_groups, used by the group picker on Hosts view to build the list of potential groups
// that can be added to a host. <<<< Should probably be moved to /helpers/Hosts.js
if (scope.buildGroupListRemove) {
scope.buildGroupListRemove();
}
scope.buildGroupListRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree, groups_url) {
scope.inventory_groups = [];
Rest.setUrl(groups_url);
Rest.get()
.success( function(data, status, headers, config) {
var groups = [];
for (var i=0; i < data.results.length; i++) {
groups.push({
id: data.results[i].id,
description: data.results[i].description,
name: data.results[i].name });
}
scope.inventory_groups = SortNodes(groups);
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status });
});
});
function loadTreeData() {
// Load the inventory root node
Wait('start');
Rest.setUrl (GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
var tip = GetInventoryToolTip({ node: data });
html += "<div class=\"title\">Group Selector:</div>\n" +
"<div id=\"selector-tree\">\n" +
"<ul id=\"inventory-tree\" class=\"tree-root\">\n" +
"<li id=\"inventory-root-node\" data-state=\"opened\" data-hosts=\"" + data.related.hosts + "\" " +
"data-description=\"" + data.description + "\" " +
"data-failures=\"" + data.has_active_failures + "\" " +
"data-groups=\"" + data.related.groups + "\" " +
"data-name=\"" + data.name + "\" " +
"data-inventory=\"" + data.id + "\"" +
">" +
"<div class=\"expand-container\" id=\"root-expand-container\"><i class=\"icon-sitemap\"></i></div>" +
"<div class=\"badge-container\" id=\"root-badge-container\">\n";
html += "<a aw-tool-tip=\"" + tip['tooltip'] + "\" data-placement=\"top\"";
html += (tip.link) ? " href=\"" + tip['link'] + "\"" : "";
html += ">";
html += "<i class=\"field-badge icon-failures-" + tip['class'] + "\"></i></a>";
html += "</div>\n";
html += "<div class=\"title-container\" id=\"root-title-container\">" +
"<a class=\"activate\">" + data.name + "</a></div>";
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
});
}
loadTreeData();
}
}])
// Set node name and description after an update to Group properties.
.factory('SetNodeName', [ function() {
return function(params) {
var scope = params.scope;
var name = params.name;
var descr = params.description;
var group_id = (params.group_id !== undefined) ? params.group_id : null;
var inventory_id = (params.inventory_id != undefined) ? params.inventory_id : null;
if (group_id !== null) {
$('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function(idx) {
$(this).attr('data-name',name);
$(this).attr('data-description',descr);
$(this).find('.activate').first().text(name);
});
}
if (inventory_id !== null) {
$('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name);
}
}
}])
.factory('ClickNode', [ function() {
return function(params) {
var selector = params.selector; //jquery selector string to find the correct <li>
$(selector + ' .activate').first().click();
}
}])
.factory('DeleteNode', [ function() {
return function(params) {
var selector = params.selector; //jquery selector string to find the correct <li>
$(selector).first().detach();
}
}]);

View File

@@ -10,68 +10,72 @@
'use strict'; 'use strict';
angular.module('Utilities',['RestServices', 'Utilities']) angular.module('Utilities', ['RestServices', 'Utilities'])
.factory('ClearScope', [ function() { .factory('ClearScope', [
return function(id) { function () {
return function (id) {
var element = document.getElementById(id), scope;
var element = document.getElementById(id),
scope;
if (element) { if (element) {
scope = angular.element(element).scope(); scope = angular.element(element).scope();
scope.$destroy(); scope.$destroy();
} }
$('.tooltip').each( function() { $('.tooltip').each(function () {
// Remove any lingering tooltip and popover <div> elements // Remove any lingering tooltip and popover <div> elements
$(this).remove(); $(this).remove();
}); });
$('.popover').each(function() { $('.popover').each(function () {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1 // remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove(); $(this).remove();
}); });
try { try {
$('#help-modal').dialog('close'); $('#help-modal').dialog('close');
} } catch (e) {
catch(e) {
// ignore // ignore
} }
$(window).unbind('resize'); $(window).unbind('resize');
}; };
}]) }
])
/* Empty() /* Empty()
* *
* Test if a value is 'empty'. Returns true if val is null | '' | undefined. * Test if a value is 'empty'. Returns true if val is null | '' | undefined.
* Only works on non-Ojbect types. * Only works on non-Ojbect types.
* *
*/ */
.factory('Empty', [ function() { .factory('Empty', [
return function(val) { function () {
return function (val) {
return (val === null || val === undefined || val === '') ? true : false; return (val === null || val === undefined || val === '') ? true : false;
}; };
}]) }
])
.factory('ToggleClass', function() {
return function(selector, cssClass) {
// Toggles the existance of a css class on a given element
if ( $(selector) && $(selector).hasClass(cssClass) ) {
$(selector).removeClass(cssClass);
}
else if ($(selector)) {
$(selector).addClass(cssClass);
}
};
})
.factory('Alert', ['$rootScope', function($rootScope) { .factory('ToggleClass', function () {
return function(hdr, msg, cls, action, secondAlert, disableButtons) { return function (selector, cssClass) {
// Toggles the existance of a css class on a given element
if ($(selector) && $(selector).hasClass(cssClass)) {
$(selector).removeClass(cssClass);
} else if ($(selector)) {
$(selector).addClass(cssClass);
}
};
})
.factory('Alert', ['$rootScope',
function ($rootScope) {
return function (hdr, msg, cls, action, secondAlert, disableButtons) {
// Pass in the header and message you want displayed on TB modal dialog found in index.html. // Pass in the header and message you want displayed on TB modal dialog found in index.html.
// Assumes an #id of 'alert-modal'. Pass in an optional TB alert class (i.e. alert-danger, alert-success, // Assumes an #id of 'alert-modal'. Pass in an optional TB alert class (i.e. alert-danger, alert-success,
// alert-info...). Pass an optional function(){}, if you want a specific action to occur when user // alert-info...). Pass an optional function(){}, if you want a specific action to occur when user
@@ -79,15 +83,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
if (secondAlert) { if (secondAlert) {
$rootScope.alertHeader2 = hdr; $rootScope.alertHeader2 = hdr;
$rootScope.alertBody2 = msg; $rootScope.alertBody2 = msg;
$rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger $rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal2').modal({ show: true, keyboard: true , backdrop: 'static' }); $('#alert-modal2').modal({
show: true,
keyboard: true,
backdrop: 'static'
});
$rootScope.disableButtons2 = (disableButtons) ? true : false; $rootScope.disableButtons2 = (disableButtons) ? true : false;
if (action) { if (action) {
$('#alert-modal2').on('hidden', function() { $('#alert-modal2').on('hidden', function () {
action(); action();
}); });
} }
$(document).bind('keydown', function(e) { $(document).bind('keydown', function (e) {
if (e.keyCode === 27) { if (e.keyCode === 27) {
$('#alert-modal2').modal('hide'); $('#alert-modal2').modal('hide');
if (action) { if (action) {
@@ -95,14 +103,17 @@ angular.module('Utilities',['RestServices', 'Utilities'])
} }
} }
}); });
} } else {
else {
$rootScope.alertHeader = hdr; $rootScope.alertHeader = hdr;
$rootScope.alertBody = msg; $rootScope.alertBody = msg;
$rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger $rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal').modal({ show: true, keyboard: true , backdrop: 'static' }); $('#alert-modal').modal({
show: true,
$(document).bind('keydown', function(e) { keyboard: true,
backdrop: 'static'
});
$(document).bind('keydown', function (e) {
if (e.keyCode === 27) { if (e.keyCode === 27) {
$('#alert-modal').modal('hide'); $('#alert-modal').modal('hide');
if (action) { if (action) {
@@ -113,168 +124,171 @@ angular.module('Utilities',['RestServices', 'Utilities'])
$rootScope.disableButtons = (disableButtons) ? true : false; $rootScope.disableButtons = (disableButtons) ? true : false;
if (action) { if (action) {
$('#alert-modal').on('hidden', function() { $('#alert-modal').on('hidden', function () {
action(); action();
}); });
} }
} }
}; };
}]) }
])
.factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait', .factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait',
function($rootScope, $cookieStore, $log, $location, Alert, Wait) { function ($rootScope, $cookieStore, $log, $location, Alert, Wait) {
return function(scope, data, status, form, defaultMsg) { return function (scope, data, status, form, defaultMsg) {
var field, fieldErrors, msg; var field, fieldErrors, msg;
Wait('stop'); Wait('stop');
if ($AnsibleConfig.debug_mode && console) { if ($AnsibleConfig.debug_mode && console) {
console.log('Debug status: ' + status); console.log('Debug status: ' + status);
console.log('Debug data: '); console.log('Debug data: ');
console.log(data); console.log(data);
}
if (status === 403) {
msg = 'The API responded with a 403 Access Denied error. ';
if (data.detail) {
msg += 'Detail: ' + data.detail;
} else {
msg += 'Please contact your system administrator.';
} }
if (status === 403) { Alert(defaultMsg.hdr, msg);
msg = 'The API responded with a 403 Access Denied error. '; } else if ((status === 401 && data.detail && data.detail === 'Token is expired') ||
if (data.detail) { (status === 401 && data.detail && data.detail === 'Invalid token')) {
msg += 'Detail: ' + data.detail; $rootScope.sessionTimer.expireSession();
$location.url('/login');
} else if (data.non_field_errors) {
Alert('Error!', data.non_field_errors);
} else if (data.detail) {
Alert(defaultMsg.hdr, defaultMsg.msg + ' ' + data.detail);
} else if (data.__all__) {
Alert('Error!', data.__all__);
} else if (form) {
fieldErrors = false;
for (field in form.fields) {
if (data[field] && form.fields[field].tab) {
// If the form is part of a tab group, activate the tab
$('#' + form.name + "_tabs a[href=\"#" + form.fields[field].tab + '"]').tab('show');
} }
else { if (form.fields[field].realName) {
msg += 'Please contact your system administrator.'; if (data[form.fields[field].realName]) {
} scope[field + '_api_error'] = data[form.fields[field]][0];
Alert(defaultMsg.hdr, msg); //scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false);
} $('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid');
else if ( (status === 401 && data.detail && data.detail === 'Token is expired') || fieldErrors = true;
(status === 401 && data.detail && data.detail === 'Invalid token') ) {
$rootScope.sessionTimer.expireSession();
$location.url('/login');
}
else if (data.non_field_errors) {
Alert('Error!', data.non_field_errors);
}
else if (data.detail) {
Alert(defaultMsg.hdr, defaultMsg.msg + ' ' + data.detail);
}
else if (data.__all__) {
Alert('Error!', data.__all__);
}
else if (form) {
fieldErrors = false;
for (field in form.fields ) {
if (data[field] && form.fields[field].tab) {
// If the form is part of a tab group, activate the tab
$('#' + form.name + "_tabs a[href=\"#" + form.fields[field].tab + '"]').tab('show');
}
if (form.fields[field].realName) {
if (data[form.fields[field].realName]) {
scope[field + '_api_error'] = data[form.fields[field]][0];
//scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false);
$('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid');
fieldErrors = true;
}
}
if (form.fields[field].sourceModel) {
if (data[field]) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] =
data[field][0];
//scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false);
$('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid');
fieldErrors = true;
}
}
else {
if (data[field]) {
scope[field + '_api_error'] = data[field][0];
//scope[form.name + '_form'][field].$setValidity('apiError', false);
$('[name="' + field + '"]').addClass('ng-invalid');
fieldErrors = true;
}
} }
} }
if ((!fieldErrors) && defaultMsg) { if (form.fields[field].sourceModel) {
Alert(defaultMsg.hdr, defaultMsg.msg); if (data[field]) {
scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] =
data[field][0];
//scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false);
$('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid');
fieldErrors = true;
}
} else {
if (data[field]) {
scope[field + '_api_error'] = data[field][0];
//scope[form.name + '_form'][field].$setValidity('apiError', false);
$('[name="' + field + '"]').addClass('ng-invalid');
fieldErrors = true;
}
} }
} }
else { if ((!fieldErrors) && defaultMsg) {
Alert(defaultMsg.hdr, defaultMsg.msg); Alert(defaultMsg.hdr, defaultMsg.msg);
} }
}; } else {
}]) Alert(defaultMsg.hdr, defaultMsg.msg);
}
};
}
])
.factory('LoadBreadCrumbs', ['$rootScope', '$routeParams', '$location', 'Empty',
function($rootScope, $routeParams, $location, Empty) {
return function(crumb) {
var title, found, j, i, paths, ppath, parent, child;
function toUppercase(a) {
return a.toUpperCase();
}
function singular(a) { .factory('LoadBreadCrumbs', ['$rootScope', '$routeParams', '$location', 'Empty',
return (a === 'ies') ? 'y' : ''; function ($rootScope, $routeParams, $location, Empty) {
} return function (crumb) {
//Keep a list of path/title mappings. When we see /organizations/XX in the path, for example, var title, found, j, i, paths, ppath, parent, child;
//we'll know the actual organization name it maps to.
if (!Empty(crumb)) { function toUppercase(a) {
found = false; return a.toUpperCase();
//crumb.title = crumb.title.charAt(0).toUpperCase() + crumb.title.slice(1); }
for (i=0; i < $rootScope.crumbCache.length; i++) {
if ($rootScope.crumbCache[i].path === crumb.path) { function singular(a) {
found = true; return (a === 'ies') ? 'y' : '';
$rootScope.crumbCache[i] = crumb; }
break;
} //Keep a list of path/title mappings. When we see /organizations/XX in the path, for example,
} //we'll know the actual organization name it maps to.
if (!found) { if (!Empty(crumb)) {
$rootScope.crumbCache.push(crumb); found = false;
//crumb.title = crumb.title.charAt(0).toUpperCase() + crumb.title.slice(1);
for (i = 0; i < $rootScope.crumbCache.length; i++) {
if ($rootScope.crumbCache[i].path === crumb.path) {
found = true;
$rootScope.crumbCache[i] = crumb;
break;
} }
} }
if (!found) {
paths = $location.path().replace(/^\//,'').split('/'); $rootScope.crumbCache.push(crumb);
ppath = '';
$rootScope.breadcrumbs = [];
if (paths.length > 1) {
for (i=0; i < paths.length - 1; i++) {
if (i > 0 && paths[i].match(/\d+/)) {
parent = paths[i-1];
child = parent.replace(/(ies$|s$)/, singular);
child = child.charAt(0).toUpperCase() + child.slice(1);
// find the correct title
found = false;
for (j=0; j < $rootScope.crumbCache.length; j++) {
if ($rootScope.crumbCache[j].path === '/' + parent + '/' + paths[i]) {
child = $rootScope.crumbCache[j].title;
found = true;
break;
}
}
if (found && $rootScope.crumbCache[j].altPath !== undefined) {
// Use altPath to override default path construction
$rootScope.breadcrumbs.push({ title: child, path: $rootScope.crumbCache[j].altPath });
}
else {
$rootScope.breadcrumbs.push({ title: child, path: ppath + '/' + paths[i] });
}
}
else {
if (/_/.test(paths[i])) {
// replace '_' with space and uppercase each word
paths[i] = paths[i].replace(/(?:^|_)\S/g, toUppercase)
.replace(/_/g,' ');
}
title = paths[i].charAt(0).toUpperCase() + paths[i].slice(1);
$rootScope.breadcrumbs.push({ title: title, path: ppath + '/' + paths[i] });
}
ppath += '/' + paths[i];
}
} }
}; }
}])
paths = $location.path().replace(/^\//, '').split('/');
.factory('HelpDialog', ['$rootScope', '$location', 'Store', function($rootScope, $location, Store) { ppath = '';
return function(params) { $rootScope.breadcrumbs = [];
if (paths.length > 1) {
for (i = 0; i < paths.length - 1; i++) {
if (i > 0 && paths[i].match(/\d+/)) {
parent = paths[i - 1];
child = parent.replace(/(ies$|s$)/, singular);
child = child.charAt(0).toUpperCase() + child.slice(1);
// find the correct title
found = false;
for (j = 0; j < $rootScope.crumbCache.length; j++) {
if ($rootScope.crumbCache[j].path === '/' + parent + '/' + paths[i]) {
child = $rootScope.crumbCache[j].title;
found = true;
break;
}
}
if (found && $rootScope.crumbCache[j].altPath !== undefined) {
// Use altPath to override default path construction
$rootScope.breadcrumbs.push({
title: child,
path: $rootScope.crumbCache[j].altPath
});
} else {
$rootScope.breadcrumbs.push({
title: child,
path: ppath + '/' + paths[i]
});
}
} else {
if (/_/.test(paths[i])) {
// replace '_' with space and uppercase each word
paths[i] = paths[i].replace(/(?:^|_)\S/g, toUppercase)
.replace(/_/g, ' ');
}
title = paths[i].charAt(0).toUpperCase() + paths[i].slice(1);
$rootScope.breadcrumbs.push({
title: title,
path: ppath + '/' + paths[i]
});
}
ppath += '/' + paths[i];
}
}
};
}
])
.factory('HelpDialog', ['$rootScope', '$location', 'Store',
function ($rootScope, $location, Store) {
return function (params) {
// Display a help dialog // Display a help dialog
// //
// HelpDialog({ defn: <HelpDefinition> }) // HelpDialog({ defn: <HelpDefinition> })
@@ -285,10 +299,10 @@ angular.module('Utilities',['RestServices', 'Utilities'])
autoShow = params.autoShow || false; autoShow = params.autoShow || false;
function showHelp(step) { function showHelp(step) {
var btns, ww, width, height, isOpen=false; var btns, ww, width, height, isOpen = false;
current_step = step; current_step = step;
function buildHtml(step) { function buildHtml(step) {
var html = ''; var html = '';
//html += (step.intro) ? "<div class=\"help-intro\">" + step.intro + "</div>" : ""; //html += (step.intro) ? "<div class=\"help-intro\">" + step.intro + "</div>" : "";
@@ -301,7 +315,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
"name=\"auto-off-checkbox\" id=\"auto-off-checkbox\"> Do not show this message in the future</label></div>\n" : ""; "name=\"auto-off-checkbox\" id=\"auto-off-checkbox\"> Do not show this message in the future</label></div>\n" : "";
return html; return html;
} }
width = (defn.story.width) ? defn.story.width : 510; width = (defn.story.width) ? defn.story.width : 510;
height = (defn.story.height) ? defn.story.height : 600; height = (defn.story.height) ? defn.story.height : 600;
@@ -311,21 +325,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
try { try {
isOpen = $('#help-modal').dialog('isOpen'); isOpen = $('#help-modal').dialog('isOpen');
} } catch (e) {
catch(e) {
// ignore // ignore
} }
if (isOpen) { if (isOpen) {
$('#help-modal').html(buildHtml(defn.story.steps[current_step])); $('#help-modal').html(buildHtml(defn.story.steps[current_step]));
} } else {
else {
// Define buttons based on story length // Define buttons based on story length
btns = []; btns = [];
if (defn.story.steps.length > 1) { if (defn.story.steps.length > 1) {
btns.push({ btns.push({
text: "Prev", text: "Prev",
click: function(e) { click: function (e) {
if (current_step - 1 === 0) { if (current_step - 1 === 0) {
$(e.target).button('disable'); $(e.target).button('disable');
} }
@@ -338,7 +350,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
}); });
btns.push({ btns.push({
text: "Next", text: "Next",
click: function(e) { click: function (e) {
if (current_step + 1 > 0) { if (current_step + 1 > 0) {
$(e.target).prev().button('enable'); $(e.target).prev().button('enable');
} }
@@ -349,12 +361,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
} }
}); });
} }
btns.push({ text: "Close", btns.push({
click: function() { $('#help-modal').dialog('close'); } text: "Close",
click: function () {
$('#help-modal').dialog('close');
}
}); });
// Show the dialog // Show the dialog
$('#help-modal').html(buildHtml(defn.story.steps[current_step])).dialog({ $('#help-modal').html(buildHtml(defn.story.steps[current_step])).dialog({
position: { my: "center top", at: "center top+150", of: 'body' }, position: {
my: "center top",
at: "center top+150",
of: 'body'
},
title: defn.story.hdr, title: defn.story.hdr,
width: width, width: width,
height: height, height: height,
@@ -363,75 +382,91 @@ angular.module('Utilities',['RestServices', 'Utilities'])
show: 500, show: 500,
hide: 500, hide: 500,
resizable: false, resizable: false,
close: function() { $('#help-modal').empty(); } close: function () {
$('#help-modal').empty();
}
}); });
// Make the buttons look like TB and add FA icons // Make the buttons look like TB and add FA icons
$('.ui-dialog-buttonset button').each( function() { $('.ui-dialog-buttonset button').each(function () {
var c, h, l; var c, h, l;
l = $(this).text(); l = $(this).text();
if (l === 'Close') { if (l === 'Close') {
h = "fa-times"; h = "fa-times";
c = "btn btn-default"; c = "btn btn-default";
$(this).attr({ 'class': c }).html("<i class=\"fa " + h + "\"></i> Close"); $(this).attr({
} 'class': c
else if (l === 'Prev') { }).html("<i class=\"fa " + h + "\"></i> Close");
} else if (l === 'Prev') {
h = "fa-chevron-left"; h = "fa-chevron-left";
c = "btn btn-primary"; c = "btn btn-primary";
$(this).attr({ 'class': c }).html("<i class=\"fa " + h + "\"></i> Prev"); $(this).attr({
} 'class': c
else { }).html("<i class=\"fa " + h + "\"></i> Prev");
} else {
h = "fa-chevron-right"; h = "fa-chevron-right";
c = "btn btn-primary"; c = "btn btn-primary";
$(this).attr({ 'class': c }).html("Next <i class=\"fa " + h + "\"></i>").css({ 'margin-right': '20px'}); $(this).attr({
'class': c
}).html("Next <i class=\"fa " + h + "\"></i>").css({
'margin-right': '20px'
});
} }
}); });
$('.ui-dialog[aria-describedby="help-modal"]').find('.ui-dialog-titlebar button') $('.ui-dialog[aria-describedby="help-modal"]').find('.ui-dialog-titlebar button')
.empty().attr({ 'class': 'close' }).text('x'); .empty().attr({
'class': 'close'
}).text('x');
// If user clicks the checkbox, update local storage // If user clicks the checkbox, update local storage
$('#auto-off-checkbox').click(function() { $('#auto-off-checkbox').click(function () {
if ($('input[name="auto-off-checkbox"]:checked').length) { if ($('input[name="auto-off-checkbox"]:checked').length) {
Store('inventoryAutoHelp','off'); Store('inventoryAutoHelp', 'off');
} } else {
else { Store('inventoryAutoHelp', 'on');
Store('inventoryAutoHelp','on');
} }
}); });
} }
} }
showHelp(0); showHelp(0);
}; };
}]) }
])
.factory('ReturnToCaller', ['$location', 'Empty', function($location, Empty) { .factory('ReturnToCaller', ['$location', 'Empty',
return function(idx) { function ($location, Empty) {
return function (idx) {
// Split the current path by '/' and use the array elements from 0 up to and // Split the current path by '/' and use the array elements from 0 up to and
// including idx as the new path. If no idx value supplied, use 0 to length - 1. // including idx as the new path. If no idx value supplied, use 0 to length - 1.
var paths = $location.path().replace(/^\//,'').split('/'), var paths = $location.path().replace(/^\//, '').split('/'),
newpath = '', i; newpath = '',
i;
idx = (Empty(idx)) ? paths.length - 1 : idx + 1; idx = (Empty(idx)) ? paths.length - 1 : idx + 1;
for (i=0; i < idx; i++) { for (i = 0; i < idx; i++) {
newpath += '/' + paths[i]; newpath += '/' + paths[i];
} }
$location.path(newpath); $location.path(newpath);
}; };
}]) }
])
.factory('FormatDate', ['$filter', function($filter) { .factory('FormatDate', ['$filter',
return function(dt) { function ($filter) {
return function (dt) {
// Wrapper for data filter- an attempt to insure all dates display in // Wrapper for data filter- an attempt to insure all dates display in
// the same format. Pass in date object. // the same format. Pass in date object.
return $filter('date')(dt, 'MM/dd/yy HH:mm:ss'); return $filter('date')(dt, 'MM/dd/yy HH:mm:ss');
}; };
}]) }
])
.factory('Wait', [ '$rootScope', function($rootScope) { .factory('Wait', ['$rootScope',
return function(directive) { function ($rootScope) {
return function (directive) {
// Display a spinning icon in the center of the screen to freeze the // Display a spinning icon in the center of the screen to freeze the
// UI while waiting on async things to complete (i.e. API calls). // UI while waiting on async things to complete (i.e. API calls).
// Wait('start' | 'stop'); // Wait('start' | 'stop');
@@ -448,16 +483,22 @@ angular.module('Utilities',['RestServices', 'Utilities'])
width: $(document).width(), width: $(document).width(),
height: $(document).height() height: $(document).height()
}).fadeIn(); }).fadeIn();
$('.spinny').css({ top: y, left: x }).fadeIn(400); $('.spinny').css({
} top: y,
else if (directive === 'stop' && $rootScope.waiting){ left: x
$('.spinny, .overlay').fadeOut(400, function(){ $rootScope.waiting = false; }); }).fadeIn(400);
} else if (directive === 'stop' && $rootScope.waiting) {
$('.spinny, .overlay').fadeOut(400, function () {
$rootScope.waiting = false;
});
} }
}; };
}]) }
])
.factory('HideElement', [ function() {
return function(selector, action) { .factory('HideElement', [
function () {
return function (selector, action) {
// Fade-in a cloack or vail or a specific element // Fade-in a cloack or vail or a specific element
var target = $(selector), var target = $(selector),
width = target.css('width'), width = target.css('width'),
@@ -468,7 +509,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
backgroundColor = target.css('background-color'), backgroundColor = target.css('background-color'),
margin = target.css('margin'), margin = target.css('margin'),
padding = target.css('padding'); padding = target.css('padding');
parent.append("<div id=\"curtain-div\" style=\"" + parent.append("<div id=\"curtain-div\" style=\"" +
"position: absolute;" + "position: absolute;" +
"top: " + position.top + "px; " + "top: " + position.top + "px; " +
@@ -485,66 +526,78 @@ angular.module('Utilities',['RestServices', 'Utilities'])
"\"></div>"); "\"></div>");
$('#curtain-div').show(0, action); $('#curtain-div').show(0, action);
}; };
}]) }
])
.factory('ShowElement', [ function() { .factory('ShowElement', [
return function() { function () {
return function () {
// And Fade-out the cloack revealing the element // And Fade-out the cloack revealing the element
$('#curtain-div').fadeOut(500, function() { $(this).remove(); }); $('#curtain-div').fadeOut(500, function () {
$(this).remove();
});
}; };
}]) }
])
.factory('GetChoices', [ 'Rest', 'ProcessErrors', function(Rest, ProcessErrors) { .factory('GetChoices', ['Rest', 'ProcessErrors',
return function(params) { function (Rest, ProcessErrors) {
return function (params) {
// Get dropdown options // Get dropdown options
var scope = params.scope, var scope = params.scope,
url = params.url, url = params.url,
field = params.field, field = params.field,
variable = params.variable, variable = params.variable,
callback = params.callback, // Optional. Provide if you want scop.$emit on completion. callback = params.callback, // Optional. Provide if you want scop.$emit on completion.
choice_name = params.choice_name; // Optional. Used when data is in something other than 'choices' choice_name = params.choice_name; // Optional. Used when data is in something other than 'choices'
if (scope[variable]) { if (scope[variable]) {
scope[variable].length = 0; scope[variable].length = 0;
} } else {
else {
scope[variable] = []; scope[variable] = [];
} }
Rest.setUrl(url); Rest.setUrl(url);
Rest.options() Rest.options()
.success( function(data) { .success(function (data) {
var choices, i; var choices, i;
choices = (choice_name) ? data.actions.GET[field][choice_name] : data.actions.GET[field].choices; choices = (choice_name) ? data.actions.GET[field][choice_name] : data.actions.GET[field].choices;
// including 'name' property so list can be used by search // including 'name' property so list can be used by search
for (i=0; i < choices.length; i++) { for (i = 0; i < choices.length; i++) {
scope[variable].push({ label: choices[i][1], value: choices[i][0], name: choices[i][1]}); scope[variable].push({
label: choices[i][1],
value: choices[i][0],
name: choices[i][1]
});
} }
if (callback) { if (callback) {
scope.$emit(callback); scope.$emit(callback);
} }
}) })
.error( function(data, status) { .error(function (data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null, { hdr: 'Error!',
{ hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); msg: 'Failed to get ' + url + '. GET status: ' + status });
}); });
}; };
}]) }
])
/*
* Search an array of objects, returning the matchting object or null /*
* * Search an array of objects, returning the matchting object or null
* Find({ list: [], key: "key", val: <key value> }); *
*/ * Find({ list: [], key: "key", val: <key value> });
.factory('Find', [ function(){ */
return function(params) { .factory('Find', [
function () {
return function (params) {
var list = params.list, var list = params.list,
key = params.key, key = params.key,
val = params.val, val = params.val,
found = false, i; found = false,
i;
if (typeof list === 'object' && Array.isArray(list)) { if (typeof list === 'object' && Array.isArray(list)) {
for (i=0; i < params.list.length; i++) { for (i = 0; i < params.list.length; i++) {
if (list[i][key] === val) { if (list[i][key] === val) {
found = true; found = true;
break; break;
@@ -554,19 +607,22 @@ angular.module('Utilities',['RestServices', 'Utilities'])
} }
return null; return null;
}; };
}]) }
])
/* /*
* DeugForm({ form: <form object>, scope: <current scope object> }); * DeugForm({ form: <form object>, scope: <current scope object> });
* *
* Use to log the $pristine and $valid properties of each form element. Helpful when form * Use to log the $pristine and $valid properties of each form element. Helpful when form
* buttons fail to enable/disable properly. * buttons fail to enable/disable properly.
* *
*/ */
.factory('DebugForm', [ function() { .factory('DebugForm', [
return function(params) { function () {
return function (params) {
var form = params.form, var form = params.form,
scope = params.scope, fld; scope = params.scope,
fld;
for (fld in form.fields) { for (fld in form.fields) {
if (scope[form.name + '_form'][fld]) { if (scope[form.name + '_form'][fld]) {
console.log(fld + ' valid: ' + scope[form.name + '_form'][fld].$valid); console.log(fld + ' valid: ' + scope[form.name + '_form'][fld].$valid);
@@ -574,49 +630,52 @@ angular.module('Utilities',['RestServices', 'Utilities'])
if (form.fields[fld].sourceModel) { if (form.fields[fld].sourceModel) {
if (scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]) { if (scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]) {
console.log(form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField + ' valid: ' + console.log(form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField + ' valid: ' +
scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField].$valid); scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField].$valid);
} }
} }
} }
console.log('form pristine: ' + scope[form.name + '_form'].$pristine); console.log('form pristine: ' + scope[form.name + '_form'].$pristine);
console.log('form valid: ' + scope[form.name + '_form'].$valid); console.log('form valid: ' + scope[form.name + '_form'].$valid);
}; };
}]) }
])
/* Store /* Store
* *
* Wrapper for local storage. All local storage requests flow through here so that we can * Wrapper for local storage. All local storage requests flow through here so that we can
* stringify/unstringify objects and respond to future issues in one place. For example, * stringify/unstringify objects and respond to future issues in one place. For example,
* we may at some point want to only use session storage rather than local storage. We might * we may at some point want to only use session storage rather than local storage. We might
* want to add a test for whether or not local/session storage exists for the browser, etc. * want to add a test for whether or not local/session storage exists for the browser, etc.
* *
* store(key,value) will store the value using the key * store(key,value) will store the value using the key
* *
* store(key) retrieves the value of the key * store(key) retrieves the value of the key
* *
*/ */
.factory('Store', ['Empty', function(Empty) { .factory('Store', ['Empty',
return function(key, value) { function (Empty) {
return function (key, value) {
if (!Empty(value)) { if (!Empty(value)) {
// Store the value // Store the value
localStorage[key] = JSON.stringify(value); localStorage[key] = JSON.stringify(value);
} } else if (!Empty(key)) {
else if (!Empty(key)) {
// Return the value // Return the value
var val = localStorage[key]; var val = localStorage[key];
return (!Empty(val)) ? JSON.parse(val) : null; return (!Empty(val)) ? JSON.parse(val) : null;
} }
}; };
}]) }
])
/* /*
* *
* ApplyEllipsis() * ApplyEllipsis()
* *
*/ */
.factory('ApplyEllipsis', [ function() { .factory('ApplyEllipsis', [
return function(selector) { function () {
return function (selector) {
// Add a hidden element to the DOM. We'll use this to calc the px length of // Add a hidden element to the DOM. We'll use this to calc the px length of
// our target text. // our target text.
var tmp = $('#string-test'); var tmp = $('#string-test');
@@ -625,26 +684,26 @@ angular.module('Utilities',['RestServices', 'Utilities'])
tmp = $('#string-test'); tmp = $('#string-test');
} }
// Find and process the text. // Find and process the text.
$(selector).each(function() { $(selector).each(function () {
var setTitle = true, txt, w, pw, cw, df; var setTitle = true,
txt, w, pw, cw, df;
if ($(this).attr('title')) { if ($(this).attr('title')) {
txt = $(this).attr('title'); txt = $(this).attr('title');
setTitle = false; setTitle = false;
} } else {
else {
txt = $(this).text(); txt = $(this).text();
} }
tmp.text(txt); tmp.text(txt);
w = tmp.width(); //text width w = tmp.width(); //text width
pw = $(this).parent().width(); //parent width pw = $(this).parent().width(); //parent width
if (w > pw) { if (w > pw) {
// text is wider than parent width // text is wider than parent width
if (setTitle) { if (setTitle) {
// Save the original text in the title // Save the original text in the title
$(this).attr('title',txt); $(this).attr('title', txt);
} }
cw = w / txt.length; // px width per character cw = w / txt.length; // px width per character
df = w - pw; // difference in px df = w - pw; // difference in px
txt = txt.substr(0, txt.length - (Math.ceil(df / cw) + 3)); txt = txt.substr(0, txt.length - (Math.ceil(df / cw) + 3));
$(this).text(txt + '...'); $(this).text(txt + '...');
} }
@@ -654,16 +713,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
$(this).text(txt); $(this).text(txt);
} }
}); });
}; };
}]); }
]);

View File

@@ -1,45 +1,53 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* *
* Read /api and /api/X to discover all the base paths needed * Read /api and /api/X to discover all the base paths needed
* to access the primary model objects. * to access the primary model objects.
* *
*/ */
'use strict'; 'use strict';
angular.module('ApiLoader', ['Utilities']) angular.module('ApiLoader', ['Utilities'])
.factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors', .factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors',
function($http, $rootScope, Store, ProcessErrors) { function ($http, $rootScope, Store, ProcessErrors) {
return function() { return function () {
$http.get('/api/') $http.get('/api/')
.success( function(data) { .success(function (data) {
var base = data.current_version; var base = data.current_version;
$http.get(base) $http.get(base)
.success( function(data) { .success(function (data) {
data.base = base; data.base = base;
$rootScope.defaultUrls = data; $rootScope.defaultUrls = data;
Store('api', data); Store('api', data);
}) })
.error ( function(data, status) { .error(function (data, status) {
$rootScope.defaultUrls = { status: 'error' }; $rootScope.defaultUrls = {
ProcessErrors(null, data, status, null, status: 'error'
{ hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status }); };
ProcessErrors(null, data, status, null, {
hdr: 'Error',
msg: 'Failed to read ' + base + '. GET status: ' + status
});
}); });
}) })
.error( function(data, status) { .error(function (data, status) {
$rootScope.defaultUrls = { status: 'error' }; $rootScope.defaultUrls = {
ProcessErrors(null, data, status, null, status: 'error'
{ hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status }); };
ProcessErrors(null, data, status, null, {
hdr: 'Error',
msg: 'Failed to read /api. GET status: ' + status
});
}); });
}; };
}]) }
])
.factory('GetBasePath', ['$rootScope', 'Store', 'LoadBasePaths', 'Empty', .factory('GetBasePath', ['$rootScope', 'Store', 'LoadBasePaths', 'Empty',
function($rootScope, Store, LoadBasePaths, Empty) { function ($rootScope, Store, LoadBasePaths, Empty) {
return function(set) { return function (set) {
// use /api/v1/ results to construct API URLs. // use /api/v1/ results to construct API URLs.
if (Empty($rootScope.defaultUrls)) { if (Empty($rootScope.defaultUrls)) {
// browser refresh must have occurred. load from local storage // browser refresh must have occurred. load from local storage
@@ -47,11 +55,9 @@ angular.module('ApiLoader', ['Utilities'])
$rootScope.defaultUrls = Store('api'); $rootScope.defaultUrls = Store('api');
return $rootScope.defaultUrls[set]; return $rootScope.defaultUrls[set];
} }
return ''; //we should never get here return ''; //we should never get here
} }
return $rootScope.defaultUrls[set]; return $rootScope.defaultUrls[set];
}; };
}]); }
]);

View File

@@ -10,6 +10,7 @@
/* global chkPass:false */ /* global chkPass:false */
angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper']) angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper'])
// awpassmatch: Add to password_confirm field. Will test if value // awpassmatch: Add to password_confirm field. Will test if value
// matches that of 'input[name="password"]' // matches that of 'input[name="password"]'
.directive('awpassmatch', function() { .directive('awpassmatch', function() {

View File

@@ -1,26 +1,29 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* Custom filters * Custom filters
* *
*/ */
'use strict'; 'use strict';
angular.module('AWFilters', []) angular.module('AWFilters', [])
// //
// capitalize -capitalize the first letter of each word // capitalize -capitalize the first letter of each word
// //
.filter('capitalize', [ function() { .filter('capitalize', [
return function(input) { function () {
var values, result, i; return function (input) {
if (input) { var values, result, i;
values = input.replace(/\_/g,' ').split(" "); if (input) {
result = ""; values = input.replace(/\_/g, ' ').split(" ");
for (i = 0; i < values.length; i++){ result = "";
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' '; for (i = 0; i < values.length; i++) {
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' ';
}
return result.trim();
} }
return result.trim(); };
} }
}; ]);
}]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9,200 +9,129 @@
'use strict'; 'use strict';
angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog']) angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog', 'LicenseFormDefinition'])
.factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', .factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors',
'FormatDate', 'Prompt', 'Empty', 'FormatDate', 'Prompt', 'Empty', 'LicenseForm',
function($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty) { function ($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty,
return function() { LicenseForm) {
return function () {
var defaultUrl=GetBasePath('config');
var generator = GenerateForm;
var form = {
name: 'license',
well: false,
forceListeners: true,
fields: {
license_status: {
type: 'custom',
control: '<div class=\"license-status\" ng-class=\"status_color\"><i class="fa fa-circle"></i> \{\{ license_status \}\}</span></div>',
readonly: true,
section: 'License'
},
license_key: {
label: 'Key',
type: 'textarea',
'class': 'modal-input-xlarge',
readonly: true,
section: 'License'
},
license_date: {
label: 'Expires On',
type: 'text',
readonly: true,
section: 'License'
},
time_remaining: {
label: 'Time Left',
type: 'text',
readonly: true,
section: 'License'
},
available_instances: {
label: 'Available',
type: 'text',
readonly: true,
section: 'Managed Hosts'
},
current_instances: {
label: 'Used',
type: 'text',
readonly: true,
section: 'Managed Hosts'
},
free_instances: {
label: 'Remaining',
type: 'text',
readonly: true,
section: 'Managed Hosts',
controlNGClass: 'free_instances_class',
labelNGClass: 'free_instances_class'
},
company_name: {
label: 'Company',
type: 'text',
readonly: true,
section: 'Contact Info'
},
contact_name: {
label: 'Contact',
type: 'text',
readonly: true,
section: 'Contact Info'
},
contact_email: {
label: 'Contact Email',
type: 'text',
readonly: true,
section: 'Contact Info'
}
}
};
var base = $location.path().replace(/^\//,'').split('/')[0]; var defaultUrl = GetBasePath('config'),
generator = GenerateForm,
form = angular.copy(LicenseForm),
// Retrieve detail record and prepopulate the form scope;
Rest.setUrl(defaultUrl);
Rest.get()
.success( function(data, status, headers, config) {
for (var fld in form.fields) {
if (fld != 'time_remaining' && fld != 'license_status') {
if (Empty(data['license_info'][fld])) {
delete form.fields[fld];
}
}
}
if (data['license_info']['is_aws'] || Empty(data['license_info']['license_date'])) { // Retrieve detail record and prepopulate the form
delete form.fields['license_date']; Rest.setUrl(defaultUrl);
delete form.fields['time_remaining']; Rest.get()
} .success(function (data) {
var scope = generator.inject(form, { mode: 'edit', modal: true, related: false}); var fld, dt, days, remainder, hours, minutes, seconds, license;
generator.reset();
for (fld in form.fields) {
scope.formModalAction = function() { if (fld !== 'time_remaining' && fld !== 'license_status') {
$('#form-modal').modal("hide"); if (Empty(data.license_info[fld])) {
} delete form.fields[fld];
}
scope.formModalActionLabel = 'OK'; }
scope.formModalCancelShow = false; }
scope.formModalInfo = 'Purchase/Extend License';
scope.formModalHeader = 'Tower License';
//$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none'); if (data.license_info.is_aws || Empty(data.license_info.license_date)) {
//$('#form-modal').addClass('skinny-modal'); delete form.fields.license_date;
delete form.fields.time_remaining;
}
// Respond to license button
scope.formModalInfoAction = function() {
Prompt({
hdr: 'Tower Licensing',
body: "<p>Ansible Tower licenses can be purchased or extended by visiting <a id=\"license-link\" " +
"href=\"http://www.ansible.com/ansible-pricing\" target=\"_blank\">" +
"the Ansible online store</a>. Would you like to purchase or extend your license now?</p>",
'class': 'btn-primary',
action: function() {
var href = $('#license-link').attr('href');
window.open(href, 'storeWindow');
}
});
}
for (var fld in form.fields) { scope = generator.inject(form, { mode: 'edit', modal: true, related: false });
if (!Empty(data['license_info'][fld])) { generator.reset();
scope[fld] = data['license_info'][fld];
}
}
if (scope['license_date']) {
var dt = new Date(parseInt(scope['license_date']));
if (dt.getFullYear() == '1970') {
// date was passed in seconds rather than milliseconds
dt = new Date(parseInt(scope['license_date']) * 1000);
scope['time_remaining'] = scope['time_remaining'] + '000';
}
scope['license_date'] = FormatDate(dt);
var days = parseInt(scope['time_remaining'] / 86400000); scope.formModalAction = function () {
var remainder = scope['time_remaining'] - (days * 86400000); $('#form-modal').modal("hide");
var hours = parseInt(remainder / 3600000); };
remainder = remainder - (hours * 3600000);
var minutes = parseInt(remainder / 60000);
remainder = remainder - (minutes * 60000);
var seconds = parseInt(remainder / 1000);
scope['time_remaining'] = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' + ('0' + seconds).slice(-2);
}
if (parseInt(scope['free_instances']) <= 0) { scope.formModalActionLabel = 'OK';
scope['free_instances_class'] = 'field-failure'; scope.formModalCancelShow = false;
} scope.formModalInfo = 'Purchase/Extend License';
else { scope.formModalHeader = 'Tower License';
scope['free_instances_class'] = 'field-success';
}
var license = data['license_info']; //$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
if (license['valid_key'] !== undefined && license['valid_key'] == false) { //$('#form-modal').addClass('skinny-modal');
scope['license_status'] = 'Invalid';
scope['status_color'] = 'license-invalid';
}
else if (license['demo'] !== undefined && license['demo'] == true) {
scope['license_status'] = 'Demo';
scope['status_color'] = 'license-demo';
}
else if (license['date_expired'] !== undefined && license['date_expired'] == true) {
scope['license_status'] = 'Expired';
scope['status_color'] = 'license-expired';
}
else if (license['date_warning'] !== undefined && license['date_warning'] == true) {
scope['license_status'] = 'Expiration Warning';
scope['status_color'] = 'license-warning';
}
else if (license['free_instances'] !== undefined && parseInt(license['free_instances']) <= 0) {
scope['license_status'] = 'No available managed hosts';
scope['status_color'] = 'license-invalid';
}
else {
scope['license_status'] = 'Valid';
scope['status_color'] = 'license-valid';
}
})
.error( function(data, status, headers, config) { // Respond to license button
ProcessErrors($rootScope, data, status, form, scope.formModalInfoAction = function () {
{ hdr: 'Error!', msg: 'Failed to retrieve license. GET status: ' + status }); Prompt({
}); hdr: 'Tower Licensing',
} body: "<p>Ansible Tower licenses can be purchased or extended by visiting <a id=\"license-link\" " +
}]); "href=\"http://www.ansible.com/ansible-pricing\" target=\"_blank\">" +
"the Ansible online store</a>. Would you like to purchase or extend your license now?</p>",
'class': 'btn-primary',
action: function () {
var href = $('#license-link').attr('href');
window.open(href, 'storeWindow');
}
});
};
for (fld in form.fields) {
if (!Empty(data.license_info[fld])) {
scope[fld] = data.license_info[fld];
}
}
if (scope.license_date) {
dt = new Date(parseInt(scope.license_date));
if (dt.getFullYear() === '1970') {
// date was passed in seconds rather than milliseconds
dt = new Date(parseInt(scope.license_date,10) * 1000);
scope.time_remaining = scope.time_remaining + '000';
}
scope.license_date = FormatDate(dt);
days = parseInt(scope.time_remaining / 86400000, 10);
remainder = scope.time_remaining - (days * 86400000);
hours = parseInt(remainder / 3600000, 10);
remainder = remainder - (hours * 3600000);
minutes = parseInt(remainder / 60000, 10);
remainder = remainder - (minutes * 60000);
seconds = parseInt(remainder / 1000, 10);
scope.time_remaining = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' +
('0' + seconds).slice(-2);
}
if (parseInt(scope.free_instances) <= 0) {
scope.free_instances_class = 'field-failure';
} else {
scope.free_instances_class = 'field-success';
}
license = data.license_info;
if (license.valid_key !== undefined && !license.valid_key) {
scope.license_status = 'Invalid';
scope.status_color = 'license-invalid';
} else if (license.demo !== undefined && license.demo) {
scope.license_status = 'Demo';
scope.status_color = 'license-demo';
} else if (license.date_expired !== undefined && license.date_expired) {
scope.license_status = 'Expired';
scope.status_color = 'license-expired';
} else if (license.date_warning !== undefined && license.date_warning) {
scope.license_status = 'Expiration Warning';
scope.status_color = 'license-warning';
} else if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) {
scope.license_status = 'No available managed hosts';
scope.status_color = 'license-invalid';
} else {
scope.license_status = 'Valid';
scope.status_color = 'license-valid';
}
})
.error(function (data, status) {
ProcessErrors($rootScope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to retrieve license. GET status: ' + status
});
});
};
}
]);

View File

@@ -1,7 +1,7 @@
/********************************************* /*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* ListGenerator * ListGenerator
* Pass in a list definition from ListDefinitions and out pops an html template. * Pass in a list definition from ListDefinitions and out pops an html template.
* Use inject method to generate the html and inject into the current view. * Use inject method to generate the html and inject into the current view.
* *
@@ -10,412 +10,439 @@
'use strict'; 'use strict';
angular.module('ListGenerator', ['GeneratorHelpers']) angular.module('ListGenerator', ['GeneratorHelpers'])
.factory('GenerateList', [ '$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', .factory('GenerateList', ['$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon',
'Column', 'DropDown', 'NavigationLink', 'Button', 'SelectIcon', 'Breadcrumbs', 'Column', 'DropDown', 'NavigationLink', 'Button', 'SelectIcon', 'Breadcrumbs',
function($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon, function ($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon,
Breadcrumbs) { Breadcrumbs) {
return { return {
setList: function(list) {
this.list = list;
},
attr: Attr,
icon: Icon, setList: function (list) {
this.list = list;
},
has: function(key) { attr: Attr,
return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false;
},
hide: function() { icon: Icon,
$('#lookup-modal').modal('hide');
},
button: Button, has: function (key) {
return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false;
},
inject: function(list, options) { hide: function () {
// options.mode = one of edit, select or lookup $('#lookup-modal').modal('hide');
// },
// Modes edit and select will inject the list as html into element #htmlTemplate.
// 'lookup' mode injects the list html into #lookup-modal-body.
//
// For options.mode == 'lookup', include the following:
//
// hdr: <lookup dialog header>
//
// Inject into a custom element using options.id: <'.selector'>
// Control breadcrumb creation with options.breadCrumbs: <true | false>
//
var element;
if (options.mode === 'lookup') { button: Button,
element = angular.element(document.getElementById('lookup-modal-body'));
}
else if (options.id) {
element = angular.element(document.getElementById(options.id));
}
else {
element = angular.element(document.getElementById('htmlTemplate'));
}
this.setList(list);
element.html(this.build(options)); // Inject the html
if (options.prepend) { // Add any extra HTML passed in options
element.prepend(options.prepend);
}
if (options.append) {
element.append(options.prepend);
}
if (options.scope) {
this.scope = options.scope;
}
else {
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
} // From here use 'scope' to manipulate the form, as the form is not in '$scope'
$compile(element)(this.scope);
// Reset the scope to prevent displaying old data from our last visit to this list inject: function (list, options) {
this.scope[list.name] = null; // options.mode = one of edit, select or lookup
this.scope[list.iterator] = null; //
// Modes edit and select will inject the list as html into element #htmlTemplate.
// 'lookup' mode injects the list html into #lookup-modal-body.
//
// For options.mode == 'lookup', include the following:
//
// hdr: <lookup dialog header>
//
// Inject into a custom element using options.id: <'.selector'>
// Control breadcrumb creation with options.breadCrumbs: <true | false>
//
var element;
// Remove any lingering tooltip and popover <div> elements if (options.mode === 'lookup') {
$('.tooltip').each( function(index) { element = angular.element(document.getElementById('lookup-modal-body'));
$(this).remove(); } else if (options.id) {
}); element = angular.element(document.getElementById(options.id));
} else {
$('.popover').each(function(index) { element = angular.element(document.getElementById('htmlTemplate'));
// remove lingering popover <div>. Seems to be a bug in TB3 RC1 }
$(this).remove(); this.setList(list);
}); element.html(this.build(options)); // Inject the html
$(window).unbind('resize'); if (options.prepend) { // Add any extra HTML passed in options
element.prepend(options.prepend);
try { }
$('#help-modal').empty().dialog('destroy'); if (options.append) {
} element.append(options.prepend);
catch(e) {
//ignore any errors should the dialog not be initialized
}
if (options.mode === 'lookup') {
// options should include {hdr: <dialog header>, action: <function...> }
this.scope.formModalActionDisabled = false;
this.scope.lookupHeader = options.hdr;
$('#lookup-modal').modal({ backdrop: 'static', keyboard: true });
$('#lookup-modal').unbind('hidden.bs.modal');
$(document).bind('keydown', function(e) {
if (e.keyCode === 27) {
$('#lookup-modal').modal('hide');
} }
});
}
return this.scope;
},
build: function(options) { if (options.scope) {
// this.scope = options.scope;
// Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML } else {
// string to be injected into the current view. this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
// } // From here use 'scope' to manipulate the form, as the form is not in '$scope'
var html = '';
var list = this.list;
if (options.activityStream) { $compile(element)(this.scope);
// Breadcrumbs for activity stream widget
// Make the links clickable using ng-click function so we can first remove the stream widget
// before navigation
html += "<div class=\"nav-path\">\n";
html += "<ul class=\"breadcrumb\">\n";
html += "<li ng-repeat=\"crumb in breadcrumbs\"><a href=\"\" ng-click=\"\{\{ crumb.ngClick \}\}\">{{ crumb.title }}</a></li>\n";
html += "<li class=\"active\">";
html += list.editTitle;
html += "</li>\n</ul>\n</div>\n";
}
else if (options.mode != 'lookup' && (options.breadCrumbs == undefined || options.breadCrumbs == true)) {
//Breadcrumbs
html += Breadcrumbs({ list: list, mode: options.mode });
}
if (options.mode == 'edit' && list.editInstructions) {
html += "<div class=\"alert alert-info alert-block\">\n";
html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">&times;</button>\n";
html += "<strong>Hint: </strong>" + list.editInstructions + "\n";
html += "</div>\n";
}
if (options.mode != 'lookup' && (list.well == undefined || list.well == true)) {
html += "<div class=\"well\">\n";
}
if (options.activityStream) {
// Add a title row
html += "<div class=\"row\">\n";
html += "<div class=\"col-lg-12\">\n";
html += "<h5>{{ streamTitle }}</h5>\n";
html += "</div>\n";
html += "</div>\n";
}
html += "<div class=\"row\">\n"; // Reset the scope to prevent displaying old data from our last visit to this list
this.scope[list.name] = null;
if (list.name != 'groups') { this.scope[list.iterator] = null;
if (options.searchSize) {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize,
searchWidgets: list.searchWidgets });
}
else if (options.mode == 'summary') {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' });
}
else if (options.mode == 'lookup' || options.id != undefined) {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-8' });
}
else {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
}
}
if (options.mode != 'lookup') { // Remove any lingering tooltip and popover <div> elements
//actions $('.tooltip').each(function() {
var base = $location.path().replace(/^\//,'').split('/')[0]; $(this).remove();
html += "<div class=\""; });
if (list.name == 'groups') {
html += "col-lg-12";
}
else if (options.searchSize) {
// User supplied searchSize, calc the remaining
var size = parseInt(options.searchSize.replace(/([A-Z]|[a-z]|\-)/g,''));
size = (list.searchWidgets) ? list.searchWidgets * size : size;
html += 'col-lg-' + (12 - size);
}
else if (options.mode == 'summary') {
html += 'col-lg-6';
}
else if (options.id != undefined) {
html += "col-lg-4";
}
else {
html += "col-lg-8 col-md-6";
}
html += "\">\n";
html += "<div class=\"list-actions\">\n";
// Add toolbar buttons or 'actions'
for (var action in list.actions) {
if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
if ( (list.actions[action].basePaths == undefined) ||
(list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1) ) {
html += this.button({ btn: list.actions[action], action: action, toolbar: true });
}
}
}
//select instructions $('.popover').each(function() {
if (options.mode == 'select' && list.selectInstructions) { // remove lingering popover <div>. Seems to be a bug in TB3 RC1
var btn = { $(this).remove();
awPopOver: list.selectInstructions, });
dataPlacement: 'top', $(window).unbind('resize');
dataContainer: 'body',
'class': 'btn-xs btn-help',
awToolTip: 'Click for help',
dataTitle: 'Help',
iconSize: 'fa-lg'
};
//html += this.button(btn, 'select');
html += this.button({ btn: btn, action: 'help', toolbar: true });
}
html += "</div><!-- list-acitons -->\n"; try {
html += "</div><!-- list-actions-column -->\n"; $('#help-modal').empty().dialog('destroy');
} } catch (e) {
else { //ignore any errors should the dialog not be initialized
//lookup }
html += "<div class=\"col-lg-7\"></div>\n";
}
html += "</div><!-- row -->\n"; if (options.mode === 'lookup') {
// options should include {hdr: <dialog header>, action: <function...> }
// Add a title and optionally a close button (used on Inventory->Groups) this.scope.formModalActionDisabled = false;
if (options.mode !== 'lookup' && list.showTitle) { this.scope.lookupHeader = options.hdr;
html += "<div class=\"form-title\">"; $('#lookup-modal').modal({
html += (options.mode == 'edit' || options.mode == 'summary') ? list.editTitle : list.addTitle; backdrop: 'static',
html += "</div>\n"; keyboard: true
} });
$('#lookup-modal').unbind('hidden.bs.modal');
$(document).bind('keydown', function (e) {
if (e.keyCode === 27) {
$('#lookup-modal').modal('hide');
}
});
}
// table header row return this.scope;
html += "<table id=\"" + list.name + "_table\" "; },
html += "class=\"table"
html += (list['class']) ? " " + list['class'] : ""; build: function (options) {
html += (options.mode !== 'summary' && options.mode !== 'edit' && (options.mode == 'lookup' || options.id)) ? //
' table-hover-inverse' : ''; // Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML
html += (list.hover) ? ' table-hover' : ''; // string to be injected into the current view.
html += (options.mode == 'summary') ? ' table-summary' : ''; //
html += "\" "; var html = '',
html += ">\n"; list = this.list,
html += "<thead>\n"; base, size, action, btn, fld, cnt, field_action, fAction, itm;
html += "<tr>\n";
if (list.index) { if (options.activityStream) {
html += "<th class=\"col-lg-1 col-md-1 col-sm-1 hidden-xs\">#</th>\n"; // Breadcrumbs for activity stream widget
} // Make the links clickable using ng-click function so we can first remove the stream widget
for (var fld in list.fields) { // before navigation
if ( (list.fields[fld].searchOnly == undefined || list.fields[fld].searchOnly == false) && html += "<div class=\"nav-path\">\n";
!(options.mode == 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal == true) ) { html += "<ul class=\"breadcrumb\">\n";
html += "<th class=\"list-header"; html += "<li ng-repeat=\"crumb in breadcrumbs\"><a href=\"\" " +
html += (list.fields[fld].columnClass) ? " " + list.fields[fld].columnClass : ""; "ng-click=\"{{ crumb.ngClick }}\">{{ crumb.title }}</a></li>\n";
html += "\" id=\""; html += "<li class=\"active\">";
html += (list.fields[fld].id) ? list.fields[fld].id : fld + "-header"; html += list.editTitle;
html += "\""; html += "</li>\n</ul>\n</div>\n";
html += (list.fields[fld].columnShow) ? " ng-show=\"" + list.fields[fld].columnShow + "\" " : ""; } else if (options.mode !== 'lookup' && (options.breadCrumbs === undefined || options.breadCrumbs)) {
html += (list.fields[fld].nosort === undefined || list.fields[fld].nosort !== true) ? "ng-click=\"sort('" + fld + "')\"" : ""; //Breadcrumbs
html += ">"; html += Breadcrumbs({
html += list.fields[fld].label; list: list,
if (list.fields[fld].nosort === undefined || list.fields[fld].nosort !== true) { mode: options.mode
html += " <i class=\"fa "; });
if (list.fields[fld].key) { }
if (list.fields[fld].desc) {
html += "fa-sort-down"; if (options.mode === 'edit' && list.editInstructions) {
} html += "<div class=\"alert alert-info alert-block\">\n";
else { html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">&times;</button>\n";
html += "fa-sort-up"; html += "<strong>Hint: </strong>" + list.editInstructions + "\n";
html += "</div>\n";
}
if (options.mode !== 'lookup' && (list.well === undefined || list.well)) {
html += "<div class=\"well\">\n";
}
if (options.activityStream) {
// Add a title row
html += "<div class=\"row\">\n";
html += "<div class=\"col-lg-12\">\n";
html += "<h5>{{ streamTitle }}</h5>\n";
html += "</div>\n";
html += "</div>\n";
}
html += "<div class=\"row\">\n";
if (list.name !== 'groups') {
if (options.searchSize) {
html += SearchWidget({
iterator: list.iterator,
template: list,
mini: true,
size: options.searchSize,
searchWidgets: list.searchWidgets
});
} else if (options.mode === 'summary') {
html += SearchWidget({
iterator: list.iterator,
template: list,
mini: true,
size: 'col-lg-6'
});
} else if (options.mode === 'lookup' || options.id !== undefined) {
html += SearchWidget({
iterator: list.iterator,
template: list,
mini: true,
size: 'col-lg-8'
});
} else {
html += SearchWidget({
iterator: list.iterator,
template: list,
mini: true
});
} }
} }
else {
html += "fa-sort"; if (options.mode !== 'lookup') {
} //actions
html += "\"></i></a>"; base = $location.path().replace(/^\//, '').split('/')[0];
html += "<div class=\"";
if (list.name === 'groups') {
html += "col-lg-12";
} else if (options.searchSize) {
// User supplied searchSize, calc the remaining
size = parseInt(options.searchSize.replace(/([A-Z]|[a-z]|\-)/g, ''));
size = (list.searchWidgets) ? list.searchWidgets * size : size;
html += 'col-lg-' + (12 - size);
} else if (options.mode === 'summary') {
html += 'col-lg-6';
} else if (options.id !== undefined) {
html += "col-lg-4";
} else {
html += "col-lg-8 col-md-6";
}
html += "\">\n";
html += "<div class=\"list-actions\">\n";
// Add toolbar buttons or 'actions'
for (action in list.actions) {
if (list.actions[action].mode === 'all' || list.actions[action].mode === options.mode) {
if ((list.actions[action].basePaths === undefined) ||
(list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1)) {
html += this.button({
btn: list.actions[action],
action: action,
toolbar: true
});
}
}
}
//select instructions
if (options.mode === 'select' && list.selectInstructions) {
btn = {
awPopOver: list.selectInstructions,
dataPlacement: 'top',
dataContainer: 'body',
'class': 'btn-xs btn-help',
awToolTip: 'Click for help',
dataTitle: 'Help',
iconSize: 'fa-lg'
};
//html += this.button(btn, 'select');
html += this.button({
btn: btn,
action: 'help',
toolbar: true
});
}
html += "</div><!-- list-acitons -->\n";
html += "</div><!-- list-actions-column -->\n";
} else {
//lookup
html += "<div class=\"col-lg-7\"></div>\n";
}
html += "</div><!-- row -->\n";
// Add a title and optionally a close button (used on Inventory->Groups)
if (options.mode !== 'lookup' && list.showTitle) {
html += "<div class=\"form-title\">";
html += (options.mode === 'edit' || options.mode === 'summary') ? list.editTitle : list.addTitle;
html += "</div>\n";
}
// table header row
html += "<table id=\"" + list.name + "_table\" ";
html += "class=\"table";
html += (list['class']) ? " " + list['class'] : "";
html += (options.mode !== 'summary' && options.mode !== 'edit' && (options.mode === 'lookup' || options.id)) ?
' table-hover-inverse' : '';
html += (list.hover) ? ' table-hover' : '';
html += (options.mode === 'summary') ? ' table-summary' : '';
html += "\" ";
html += ">\n";
html += "<thead>\n";
html += "<tr>\n";
if (list.index) {
html += "<th class=\"col-lg-1 col-md-1 col-sm-1 hidden-xs\">#</th>\n";
}
for (fld in list.fields) {
if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) &&
!(options.mode === 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal === true)) {
html += "<th class=\"list-header";
html += (list.fields[fld].columnClass) ? " " + list.fields[fld].columnClass : "";
html += "\" id=\"";
html += (list.fields[fld].id) ? list.fields[fld].id : fld + "-header";
html += "\"";
html += (list.fields[fld].columnShow) ? " ng-show=\"" + list.fields[fld].columnShow + "\" " : "";
html += (list.fields[fld].nosort === undefined || list.fields[fld].nosort !== true) ? "ng-click=\"sort('" + fld + "')\"" : "";
html += ">";
html += list.fields[fld].label;
if (list.fields[fld].nosort === undefined || list.fields[fld].nosort !== true) {
html += " <i class=\"fa ";
if (list.fields[fld].key) {
if (list.fields[fld].desc) {
html += "fa-sort-down";
} else {
html += "fa-sort-up";
}
} else {
html += "fa-sort";
}
html += "\"></i></a>";
}
html += "</th>\n";
}
}
if (options.mode === 'select' || options.mode === 'lookup') {
html += "<th>Select</th>";
} else if (options.mode === 'edit' && list.fieldActions) {
html += "<th class=\"actions-column";
html += (list.fieldActions && list.fieldActions.columnClass) ? " " + list.fieldActions.columnClass : "";
html += "\">Actions</th>\n";
}
html += "</tr>\n";
html += "</thead>\n";
// table body
html += "<tbody>\n";
html += "<tr ng-class=\"" + list.iterator;
html += (options.mode === 'lookup' || options.mode === 'select') ? ".success_class" : ".active_class";
html += "\" ";
html += "class=\"" + list.iterator + "_class\" ng-repeat=\"" + list.iterator + " in " + list.name;
html += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
html += (list.filterBy) ? " | filter: " + list.filterBy : "";
html += "\"";
html += ">\n";
if (list.index) {
html += "<td class=\"index-column hidden-xs\">{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.</td>\n";
}
cnt = 2;
base = (list.base) ? list.base : list.name;
base = base.replace(/^\//, '');
for (fld in list.fields) {
cnt++;
if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) &&
!(options.mode === 'lookup' && list.fields[fld].excludeModal !== undefined &&
list.fields[fld].excludeModal === true)) {
html += Column({
list: list,
fld: fld,
options: options,
base: base
});
}
}
if (options.mode === 'select' || options.mode === 'lookup') {
html += "<td><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator + "({{ " + list.iterator + ".id }}, true)\" ng-true-value=\"1\" " +
"ng-false-value=\"0\" id=\"check_{{" + list.iterator + ".id}}\" /></td>";
} else if ((options.mode === 'edit' || options.mode === 'summary') && list.fieldActions) {
// Row level actions
html += "<td class=\"actions\">";
for (field_action in list.fieldActions) {
if (field_action !== 'columnClass') {
if (list.fieldActions[field_action].type && list.fieldActions[field_action].type === 'DropDown') {
html += DropDown({
list: list,
fld: field_action,
options: options,
base: base,
type: 'fieldActions',
td: false
});
} else {
fAction = list.fieldActions[field_action];
html += "<a ";
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
html += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
html += (field_action === 'cancel') ? "class=\"cancel red-txt\" " : "";
html += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
html += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
html += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
for (itm in fAction) {
if (itm !== 'ngHref' && itm !== 'href' && itm !== 'label' && itm !== 'icon' && itm !== 'class' &&
itm !== 'iconClass' && itm !== "dataPlacement" && itm !== "awPopOver" && itm !== "dataTitle") {
html += Attr(fAction, itm);
}
}
html += ">";
if (fAction.iconClass) {
html += "<i class=\"" + fAction.iconClass + "\"></i>";
} else {
html += SelectIcon({
action: field_action
});
}
html += (fAction.label) ? " " + list.fieldActions[field_action].label : "";
html += "</a>";
}
}
}
html += "</td>";
}
html += "</tr>\n";
// Message for when a collection is empty
html += "<tr class=\"info\" ng-show=\"" + list.iterator + "Loading == false && (" + list.name + " == null || " + list.name + ".length == 0)\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">No records matched your search.</div></td>\n";
html += "</tr>\n";
// Message for loading
html += "<tr class=\"info\" ng-show=\"" + list.iterator + "Loading == true\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">Loading...</div></td>\n";
html += "</tr>\n";
// End List
html += "</tbody>\n";
html += "</table>\n";
if (options.mode === 'select' && (options.selectButton === undefined || options.selectButton)) {
html += "<div class=\"navigation-buttons\">\n";
html += " <button class=\"btn btn-sm btn-primary pull-right\" aw-tool-tip=\"Complete your selection\" " +
"ng-click=\"finishSelection()\" ng-disabled=\"disableSelectBtn\"><i class=\"fa fa-check\"></i> Select</button>\n";
html += "</div>\n";
}
if (options.mode !== 'lookup' && (list.well === undefined || list.well === true)) {
html += "</div>\n"; //well
}
if (list.name !== 'groups') {
if (options.mode === 'lookup' || (options.id && options.id === "form-modal-body")) {
html += PaginateWidget({
set: list.name,
iterator: list.iterator
});
} else {
html += PaginateWidget({
set: list.name,
iterator: list.iterator
});
}
}
return html;
} }
html += "</th>\n"; };
} }
} ]);
if (options.mode == 'select' || options.mode == 'lookup') {
html += "<th>Select</th>";
}
else if (options.mode == 'edit' && list.fieldActions) {
html += "<th class=\"actions-column";
html += (list.fieldActions && list.fieldActions.columnClass) ? " " + list.fieldActions.columnClass : "";
html += "\">Actions</th>\n";
}
html += "</tr>\n";
html += "</thead>\n";
// table body
html += "<tbody>\n";
html += "<tr ng-class=\"" + list.iterator;
html += (options.mode == 'lookup' || options.mode == 'select') ? ".success_class" : ".active_class";
html += "\" ";
html += "class=\"" + list.iterator + "_class\" ng-repeat=\"" + list.iterator + " in " + list.name;
html += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
html += (list.filterBy) ? " | filter: " + list.filterBy : "";
html += "\"";
html += ">\n";
if (list.index) {
html += "<td class=\"index-column hidden-xs\">{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.</td>\n";
}
var cnt = 2;
var base = (list.base) ? list.base : list.name;
base = base.replace(/^\//,'');
for (fld in list.fields) {
cnt++;
if ( (list.fields[fld].searchOnly == undefined || list.fields[fld].searchOnly == false) &&
!(options.mode == 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal == true) ) {
html += Column({ list: list, fld: fld, options: options, base: base });
}
}
if (options.mode == 'select' || options.mode == 'lookup') {
html += "<td><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator +"({{ " + list.iterator + ".id }}, true)\" ng-true-value=\"1\" " +
"ng-false-value=\"0\" id=\"check_{{" + list.iterator + ".id}}\" /></td>";
}
else if ((options.mode == 'edit' || options.mode == 'summary') && list.fieldActions) {
// Row level actions
html += "<td class=\"actions\">";
var field_action, fAction, itm;
for (field_action in list.fieldActions) {
if (field_action !== 'columnClass') {
if (list.fieldActions[field_action].type && list.fieldActions[field_action].type == 'DropDown') {
html += DropDown({
list: list,
fld: field_action,
options: options,
base: base,
type: 'fieldActions',
td: false
});
}
else {
fAction = list.fieldActions[field_action];
html += "<a ";
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
html += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
html += (field_action == 'cancel') ? "class=\"cancel red-txt\" " : "";
html += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
html += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
html += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
for (itm in fAction) {
if (itm != 'ngHref' && itm != 'href' && itm != 'label' && itm != 'icon' && itm != 'class' &&
itm != 'iconClass' && itm != "dataPlacement" && itm != "awPopOver" && itm != "dataTitle") {
html += Attr(fAction, itm);
}
}
html += ">";
if (fAction.iconClass) {
html += "<i class=\"" + fAction.iconClass + "\"></i>";
}
else {
html += SelectIcon({ action: field_action });
}
html += (fAction.label) ? " " + list.fieldActions[field_action]['label'] : "";
html += "</a>";
}
}
}
html += "</td>";
}
html += "</tr>\n";
// Message for when a collection is empty
html += "<tr class=\"info\" ng-show=\"" + list.iterator + "Loading == false && (" + list.name + " == null || " + list.name + ".length == 0)\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">No records matched your search.</div></td>\n";
html += "</tr>\n";
// Message for loading
html += "<tr class=\"info\" ng-show=\"" + list.iterator + "Loading == true\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">Loading...</div></td>\n";
html += "</tr>\n";
// End List
html += "</tbody>\n";
html += "</table>\n";
if (options.mode == 'select' && (options.selectButton == undefined || options.selectButton == true)) {
html += "<div class=\"navigation-buttons\">\n";
html += " <button class=\"btn btn-sm btn-primary pull-right\" aw-tool-tip=\"Complete your selection\" " +
"ng-click=\"finishSelection()\" ng-disabled=\"disableSelectBtn\"><i class=\"fa fa-check\"></i> Select</button>\n";
html += "</div>\n";
}
if (options.mode != 'lookup' && (list.well == undefined || list.well == true)) {
html += "</div>\n"; //well
}
if (list.name !== 'groups') {
if ( options.mode == 'lookup' || (options.id && options.id == "form-modal-body") ) {
html += PaginateWidget({ set: list.name, iterator: list.iterator });
}
else {
html += PaginateWidget({ set: list.name, iterator: list.iterator });
}
}
return html;
}
}}]);

View File

@@ -2,12 +2,12 @@
* Copyright (c) 2014 AnsibleWorks, Inc. * Copyright (c) 2014 AnsibleWorks, Inc.
* *
* PromptDialog * PromptDialog
* Prompt the user with a Yes/No dialog to confirm an action such * Prompt the user with a Yes/No dialog to confirm an action such
* as Delete. Assumes a hidden dialog already exists in $scope. * as Delete. Assumes a hidden dialog already exists in $scope.
* See example at bottom. If user responds with Yes, execute action * See example at bottom. If user responds with Yes, execute action
* parameter. * parameter.
* *
* params: { hdr: 'header msg', * params: { hdr: 'header msg',
* body: 'body text/html', * body: 'body text/html',
* class: 'btn-class for Yes button', --defaults to btn-danger * class: 'btn-class for Yes button', --defaults to btn-danger
* action: function() {} --action to take, if use clicks Yes * action: function() {} --action to take, if use clicks Yes
@@ -17,25 +17,26 @@
'use strict'; 'use strict';
angular.module('PromptDialog', ['Utilities']) angular.module('PromptDialog', ['Utilities'])
.factory('Prompt', ['$rootScope', '$compile', 'Alert', function($rootScope, $compile, Alert) { .factory('Prompt', [
return function(params) { function () {
return function (params) {
var dialog = angular.element(document.getElementById('prompt-modal'));
var scope = dialog.scope(); var dialog = angular.element(document.getElementById('prompt-modal')),
scope = dialog.scope(), cls;
scope.promptHeader = params.hdr;
scope.promptBody = params.body; scope.promptHeader = params.hdr;
scope.promptAction = params.action; scope.promptBody = params.body;
scope.promptAction = params.action;
var cls = (params['class'] == null || params['class'] == undefined) ? 'btn-danger' : params['class'];
cls = (params['class'] === null || params['class'] === undefined) ? 'btn-danger' : params['class'];
$('#prompt_action_btn').removeClass(cls).addClass(cls);
$('#prompt_action_btn').removeClass(cls).addClass(cls);
$(dialog).modal({
backdrop: 'static', $(dialog).modal({
keyboard: true, backdrop: 'static',
show: true keyboard: true,
}); show: true
});
};
} }
}]); ]);

View File

@@ -22,205 +22,294 @@
** 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
** **
** **
** CLH 9/5/13 - Set required strength in config.js ** CLH 09/05/13 - Set required strength in config.js
** 02/10/14 - Applied jsHint
*/ */
String.prototype.strReverse = function() { /*jshint eqeqeq:false, unused:false */
var newstring = "";
for (var s=0; s < this.length; s++) { String.prototype.strReverse = function () {
newstring = this.charAt(s) + newstring; var newstring = "", s;
} for (s = 0; s < this.length; s++) {
return newstring; newstring = this.charAt(s) + newstring;
}
return newstring;
}; };
var nScore = 0; var nScore = 0;
function chkPass(pwd) { function chkPass(pwd) {
// Simultaneous variable declaration and value assignment aren't supported in IE apparently // Simultaneous variable declaration and value assignment aren't supported in IE apparently
// so I'm forced to assign the same value individually per var to support a crappy browser *sigh* // so I'm forced to assign the same value individually per var to support a crappy browser *sigh*
var nLength=0, nAlphaUC=0, nAlphaLC=0, nNumber=0, nSymbol=0, nMidChar=0, nRequirements=0, var nLength = 0,
nAlphasOnly=0, nNumbersOnly=0, nUnqChar=0, nRepChar=0, nRepInc=0, nConsecAlphaUC=0, nConsecAlphaLC=0, nAlphaUC = 0,
nConsecNumber=0, nConsecSymbol=0, nConsecCharType=0, nSeqAlpha=0, nSeqNumber=0, nSeqSymbol=0, nAlphaLC = 0,
nSeqChar=0, nReqChar=0, nMultConsecCharType=0; nNumber = 0,
var nMultRepChar=1, nMultConsecSymbol=1; nSymbol = 0,
var nMultMidChar=2, nMultRequirements=2, nMultConsecAlphaUC=2, nMultConsecAlphaLC=2, nMultConsecNumber=2; nMidChar = 0,
var nReqCharType=3, nMultAlphaUC=3, nMultAlphaLC=3, nMultSeqAlpha=3, nMultSeqNumber=3, nMultSeqSymbol=3; nRequirements = 0,
var nMultLength=4, nMultNumber=4; nAlphasOnly = 0,
var nMultSymbol=6; nNumbersOnly = 0,
var nTmpAlphaUC="", nTmpAlphaLC="", nTmpNumber="", nTmpSymbol=""; nUnqChar = 0,
var sAlphaUC="0", sAlphaLC="0", sNumber="0", sSymbol="0", sMidChar="0", sRequirements="0", nRepChar = 0,
sAlphasOnly="0", sNumbersOnly="0", sRepChar="0", sConsecAlphaUC="0", sConsecAlphaLC="0", nRepInc = 0,
sConsecNumber="0", sSeqAlpha="0", sSeqNumber="0", sSeqSymbol="0"; nConsecAlphaUC = 0,
var sAlphas = "abcdefghijklmnopqrstuvwxyz"; nConsecAlphaLC = 0,
var sNumerics = "01234567890"; nConsecNumber = 0,
var sSymbols = ")_!@#$%^&*()"; nConsecSymbol = 0,
var sComplexity = "Too Short"; nConsecCharType = 0,
var sStandards = "Below"; nSeqAlpha = 0,
var nMinPwdLen = 8; nSeqNumber = 0,
if (document.all) { var nd = 0; } else { var nd = 1; } nSeqSymbol = 0,
if (pwd) { nSeqChar = 0,
nScore = parseInt(pwd.length * nMultLength); nReqChar = 0,
nLength = pwd.length; nMultConsecCharType = 0,
var arrPwd = pwd.replace(/\s+/g,"").split(/\s*/); nMultRepChar = 1,
var arrPwdLen = arrPwd.length; nMultConsecSymbol = 1,
nMultMidChar = 2,
/* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */ nMultRequirements = 2,
for (var a=0; a < arrPwdLen; a++) { nMultConsecAlphaUC = 2,
if (arrPwd[a].match(/[A-Z]/g)) { nMultConsecAlphaLC = 2,
if (nTmpAlphaUC !== "") { if ((nTmpAlphaUC + 1) == a) { nConsecAlphaUC++; nConsecCharType++; } } nMultConsecNumber = 2,
nTmpAlphaUC = a; nReqCharType = 3,
nAlphaUC++; nMultAlphaUC = 3,
} nMultAlphaLC = 3,
else if (arrPwd[a].match(/[a-z]/g)) { nMultSeqAlpha = 3,
if (nTmpAlphaLC !== "") { if ((nTmpAlphaLC + 1) == a) { nConsecAlphaLC++; nConsecCharType++; } } nMultSeqNumber = 3,
nTmpAlphaLC = a; nMultSeqSymbol = 3,
nAlphaLC++; nMultLength = 4,
} nMultNumber = 4,
else if (arrPwd[a].match(/[0-9]/g)) { nMultSymbol = 6,
if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; } nTmpAlphaUC = "",
if (nTmpNumber !== "") { if ((nTmpNumber + 1) == a) { nConsecNumber++; nConsecCharType++; } } nTmpAlphaLC = "",
nTmpNumber = a; nTmpNumber = "",
nNumber++; nTmpSymbol = "",
} sAlphaUC = "0",
else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) { sAlphaLC = "0",
if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; } sNumber = "0",
if (nTmpSymbol !== "") { if ((nTmpSymbol + 1) == a) { nConsecSymbol++; nConsecCharType++; } } sSymbol = "0",
nTmpSymbol = a; sMidChar = "0",
nSymbol++; sRequirements = "0",
} sAlphasOnly = "0",
/* Internal loop through password to check for repeat characters */ sNumbersOnly = "0",
var bCharExists = false; sRepChar = "0",
for (var b=0; b < arrPwdLen; b++) { sConsecAlphaUC = "0",
if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */ sConsecAlphaLC = "0",
bCharExists = true; sConsecNumber = "0",
/* sSeqAlpha = "0",
sSeqNumber = "0",
sSeqSymbol = "0",
sAlphas = "abcdefghijklmnopqrstuvwxyz",
sNumerics = "01234567890",
sSymbols = ")_!@#$%^&*()",
sComplexity = "Too Short",
sStandards = "Below",
nMinPwdLen = 8,
a, nd, arrPwd, arrPwdLen,bCharExists,b,s,sFwd,sRev,progbar,required_strength,warning_level;
if (document.all) {
nd = 0;
} else {
nd = 1;
}
if (pwd) {
nScore = parseInt(pwd.length * nMultLength);
nLength = pwd.length;
arrPwd = pwd.replace(/\s+/g, "").split(/\s*/);
arrPwdLen = arrPwd.length;
/* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */
for (a = 0; a < arrPwdLen; a++) {
if (arrPwd[a].match(/[A-Z]/g)) {
if (nTmpAlphaUC !== "") {
if ((nTmpAlphaUC + 1) == a) {
nConsecAlphaUC++;
nConsecCharType++;
}
}
nTmpAlphaUC = a;
nAlphaUC++;
} else if (arrPwd[a].match(/[a-z]/g)) {
if (nTmpAlphaLC !== "") {
if ((nTmpAlphaLC + 1) == a) {
nConsecAlphaLC++;
nConsecCharType++;
}
}
nTmpAlphaLC = a;
nAlphaLC++;
} else if (arrPwd[a].match(/[0-9]/g)) {
if (a > 0 && a < (arrPwdLen - 1)) {
nMidChar++;
}
if (nTmpNumber !== "") {
if ((nTmpNumber + 1) == a) {
nConsecNumber++;
nConsecCharType++;
}
}
nTmpNumber = a;
nNumber++;
} else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) {
if (a > 0 && a < (arrPwdLen - 1)) {
nMidChar++;
}
if (nTmpSymbol !== "") {
if ((nTmpSymbol + 1) == a) {
nConsecSymbol++;
nConsecCharType++;
}
}
nTmpSymbol = a;
nSymbol++;
}
/* Internal loop through password to check for repeat characters */
bCharExists = false;
for (b = 0; b < arrPwdLen; b++) {
if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */
bCharExists = true;
/*
Calculate icrement deduction based on proximity to identical characters Calculate icrement deduction based on proximity to identical characters
Deduction is incremented each time a new match is discovered Deduction is incremented each time a new match is discovered
Deduction amount is based on total password length divided by the Deduction amount is based on total password length divided by the
difference of distance between currently selected match difference of distance between currently selected match
*/ */
nRepInc += Math.abs(arrPwdLen/(b-a)); nRepInc += Math.abs(arrPwdLen / (b - a));
}
} }
} if (bCharExists) {
if (bCharExists) { nRepChar++;
nRepChar++; nUnqChar = arrPwdLen - nRepChar;
nUnqChar = arrPwdLen-nRepChar; nRepInc = (nUnqChar) ? Math.ceil(nRepInc / nUnqChar) : Math.ceil(nRepInc);
nRepInc = (nUnqChar) ? Math.ceil(nRepInc/nUnqChar) : Math.ceil(nRepInc); }
} }
}
/* Check for sequential alpha string patterns (forward and reverse) */
for (var s=0; s < 23; s++) {
var sFwd = sAlphas.substring(s,parseInt(s+3));
var sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqAlpha++; nSeqChar++;}
}
/* Check for sequential numeric string patterns (forward and reverse) */
for (var s=0; s < 8; s++) {
var sFwd = sNumerics.substring(s,parseInt(s+3));
var sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqNumber++; nSeqChar++;}
}
/* Check for sequential symbol string patterns (forward and reverse) */
for (var s=0; s < 8; s++) {
var sFwd = sSymbols.substring(s,parseInt(s+3));
var sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqSymbol++; nSeqChar++;}
}
/* Modify overall score value based on usage vs requirements */
/* General point assignment */ /* Check for sequential alpha string patterns (forward and reverse) */
if (nAlphaUC > 0 && nAlphaUC < nLength) { for (s = 0; s < 23; s++) {
nScore = parseInt(nScore + ((nLength - nAlphaUC) * 2)); sFwd = sAlphas.substring(s, parseInt(s + 3));
sAlphaUC = "+ " + parseInt((nLength - nAlphaUC) * 2); sRev = sFwd.strReverse();
} if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
if (nAlphaLC > 0 && nAlphaLC < nLength) { nSeqAlpha++;
nScore = parseInt(nScore + ((nLength - nAlphaLC) * 2)); nSeqChar++;
sAlphaLC = "+ " + parseInt((nLength - nAlphaLC) * 2); }
} }
if (nNumber > 0 && nNumber < nLength) {
nScore = parseInt(nScore + (nNumber * nMultNumber));
sNumber = "+ " + parseInt(nNumber * nMultNumber);
}
if (nSymbol > 0) {
nScore = parseInt(nScore + (nSymbol * nMultSymbol));
sSymbol = "+ " + parseInt(nSymbol * nMultSymbol);
}
if (nMidChar > 0) {
nScore = parseInt(nScore + (nMidChar * nMultMidChar));
sMidChar = "+ " + parseInt(nMidChar * nMultMidChar);
}
/* Point deductions for poor practices */ /* Check for sequential numeric string patterns (forward and reverse) */
if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters for (s = 0; s < 8; s++) {
nScore = parseInt(nScore - nLength); sFwd = sNumerics.substring(s, parseInt(s + 3));
nAlphasOnly = nLength; sRev = sFwd.strReverse();
sAlphasOnly = "- " + nLength; if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
} nSeqNumber++;
if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers nSeqChar++;
nScore = parseInt(nScore - nLength); }
nNumbersOnly = nLength; }
sNumbersOnly = "- " + nLength;
}
if (nRepChar > 0) { // Same character exists more than once
nScore = parseInt(nScore - nRepInc);
sRepChar = "- " + nRepInc;
}
if (nConsecAlphaUC > 0) { // Consecutive Uppercase Letters exist
nScore = parseInt(nScore - (nConsecAlphaUC * nMultConsecAlphaUC));
sConsecAlphaUC = "- " + parseInt(nConsecAlphaUC * nMultConsecAlphaUC);
}
if (nConsecAlphaLC > 0) { // Consecutive Lowercase Letters exist
nScore = parseInt(nScore - (nConsecAlphaLC * nMultConsecAlphaLC));
sConsecAlphaLC = "- " + parseInt(nConsecAlphaLC * nMultConsecAlphaLC);
}
if (nConsecNumber > 0) { // Consecutive Numbers exist
nScore = parseInt(nScore - (nConsecNumber * nMultConsecNumber));
sConsecNumber = "- " + parseInt(nConsecNumber * nMultConsecNumber);
}
if (nSeqAlpha > 0) { // Sequential alpha strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqAlpha * nMultSeqAlpha));
sSeqAlpha = "- " + parseInt(nSeqAlpha * nMultSeqAlpha);
}
if (nSeqNumber > 0) { // Sequential numeric strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqNumber * nMultSeqNumber));
sSeqNumber = "- " + parseInt(nSeqNumber * nMultSeqNumber);
}
if (nSeqSymbol > 0) { // Sequential symbol strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqSymbol * nMultSeqSymbol));
sSeqSymbol = "- " + parseInt(nSeqSymbol * nMultSeqSymbol);
}
/* Determine complexity based on overall score */ /* Check for sequential symbol string patterns (forward and reverse) */
if (nScore > 100) { nScore = 100; } else if (nScore < 0) { nScore = 0; } for (s = 0; s < 8; s++) {
sFwd = sSymbols.substring(s, parseInt(s + 3));
sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
nSeqSymbol++;
nSeqChar++;
}
}
var progbar = $("#progbar"); /* Modify overall score value based on usage vs requirements */
var required_strength = $AnsibleConfig.password_strength;
var warning_level = ($AnsibleConfig.password_strength - 15 < 0) ? 0 : $AnsibleConfig.password_strength - 15;
progbar.css("width", nScore + '%'); /* General point assignment */
if (nAlphaUC > 0 && nAlphaUC < nLength) {
nScore = parseInt(nScore + ((nLength - nAlphaUC) * 2));
sAlphaUC = "+ " + parseInt((nLength - nAlphaUC) * 2);
}
if (nAlphaLC > 0 && nAlphaLC < nLength) {
nScore = parseInt(nScore + ((nLength - nAlphaLC) * 2));
sAlphaLC = "+ " + parseInt((nLength - nAlphaLC) * 2);
}
if (nNumber > 0 && nNumber < nLength) {
nScore = parseInt(nScore + (nNumber * nMultNumber));
sNumber = "+ " + parseInt(nNumber * nMultNumber);
}
if (nSymbol > 0) {
nScore = parseInt(nScore + (nSymbol * nMultSymbol));
sSymbol = "+ " + parseInt(nSymbol * nMultSymbol);
}
if (nMidChar > 0) {
nScore = parseInt(nScore + (nMidChar * nMultMidChar));
sMidChar = "+ " + parseInt(nMidChar * nMultMidChar);
}
if (nScore >= 0 && nScore <= warning_level) { /* Point deductions for poor practices */
sComplexity = 'Weak'; if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters
progbar.addClass('progress-bar-danger') nScore = parseInt(nScore - nLength);
progbar.removeClass('progress-bar-success progress-bar-warning') nAlphasOnly = nLength;
} else if (nScore > warning_level && nScore <= required_strength) { sAlphasOnly = "- " + nLength;
sComplexity = 'Good'; }
progbar.addClass('progress-bar-warning') if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers
progbar.removeClass('progress-bar-success progress-bar-danger') nScore = parseInt(nScore - nLength);
} else if (nScore > required_strength) { nNumbersOnly = nLength;
sComplexity = "Strong"; sNumbersOnly = "- " + nLength;
progbar.addClass('progress-bar-success') }
progbar.removeClass('progress-bar-warning progress-bar-danger') if (nRepChar > 0) { // Same character exists more than once
} nScore = parseInt(nScore - nRepInc);
} sRepChar = "- " + nRepInc;
else { }
/* no password, so reset the displays */ if (nConsecAlphaUC > 0) { // Consecutive Uppercase Letters exist
var progbar = $("#progbar"); nScore = parseInt(nScore - (nConsecAlphaUC * nMultConsecAlphaUC));
progbar.css("width", '0%'); sConsecAlphaUC = "- " + parseInt(nConsecAlphaUC * nMultConsecAlphaUC);
progbar.removeClass('progress-bar-success progress-bar-warning') }
} if (nConsecAlphaLC > 0) { // Consecutive Lowercase Letters exist
return nScore; nScore = parseInt(nScore - (nConsecAlphaLC * nMultConsecAlphaLC));
sConsecAlphaLC = "- " + parseInt(nConsecAlphaLC * nMultConsecAlphaLC);
}
if (nConsecNumber > 0) { // Consecutive Numbers exist
nScore = parseInt(nScore - (nConsecNumber * nMultConsecNumber));
sConsecNumber = "- " + parseInt(nConsecNumber * nMultConsecNumber);
}
if (nSeqAlpha > 0) { // Sequential alpha strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqAlpha * nMultSeqAlpha));
sSeqAlpha = "- " + parseInt(nSeqAlpha * nMultSeqAlpha);
}
if (nSeqNumber > 0) { // Sequential numeric strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqNumber * nMultSeqNumber));
sSeqNumber = "- " + parseInt(nSeqNumber * nMultSeqNumber);
}
if (nSeqSymbol > 0) { // Sequential symbol strings exist (3 characters or more)
nScore = parseInt(nScore - (nSeqSymbol * nMultSeqSymbol));
sSeqSymbol = "- " + parseInt(nSeqSymbol * nMultSeqSymbol);
}
/* Determine complexity based on overall score */
if (nScore > 100) {
nScore = 100;
} else if (nScore < 0) {
nScore = 0;
}
progbar = $("#progbar");
required_strength = $AnsibleConfig.password_strength;
warning_level = ($AnsibleConfig.password_strength - 15 < 0) ? 0 : $AnsibleConfig.password_strength - 15;
progbar.css("width", nScore + '%');
if (nScore >= 0 && nScore <= warning_level) {
sComplexity = 'Weak';
progbar.addClass('progress-bar-danger');
progbar.removeClass('progress-bar-success progress-bar-warning');
} else if (nScore > warning_level && nScore <= required_strength) {
sComplexity = 'Good';
progbar.addClass('progress-bar-warning');
progbar.removeClass('progress-bar-success progress-bar-danger');
} else if (nScore > required_strength) {
sComplexity = "Strong";
progbar.addClass('progress-bar-success');
progbar.removeClass('progress-bar-warning progress-bar-danger');
}
} else {
/* no password, so reset the displays */
progbar = $("#progbar");
progbar.css("width", '0%');
progbar.removeClass('progress-bar-success progress-bar-warning');
}
return nScore;
} }

View File

@@ -71,10 +71,11 @@
<script src="{{ STATIC_URL }}js/forms/ProjectStatus.js"></script> <script src="{{ STATIC_URL }}js/forms/ProjectStatus.js"></script>
<script src="{{ STATIC_URL }}js/forms/Permissions.js"></script> <script src="{{ STATIC_URL }}js/forms/Permissions.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobEventData.js"></script> <script src="{{ STATIC_URL }}js/forms/JobEventData.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobEvents.js"></script>
<script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script> <script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script>
<script src="{{ STATIC_URL }}js/forms/InventoryStatus.js"></script>
<script src="{{ STATIC_URL }}js/forms/ActivityDetail.js"></script> <script src="{{ STATIC_URL }}js/forms/ActivityDetail.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobSummary.js"></script> <script src="{{ STATIC_URL }}js/forms/JobSummary.js"></script>
<script src="{{ STATIC_URL }}js/forms/LicenseForm.js"></script>
<script src="{{ STATIC_URL }}js/lists/Users.js"></script> <script src="{{ STATIC_URL }}js/lists/Users.js"></script>
<script src="{{ STATIC_URL }}js/lists/Organizations.js"></script> <script src="{{ STATIC_URL }}js/lists/Organizations.js"></script>
<script src="{{ STATIC_URL }}js/lists/Admins.js"></script> <script src="{{ STATIC_URL }}js/lists/Admins.js"></script>
@@ -123,7 +124,6 @@
<script src="{{ STATIC_URL }}js/widgets/ObjectCount.js"></script> <script src="{{ STATIC_URL }}js/widgets/ObjectCount.js"></script>
<script src="{{ STATIC_URL }}js/widgets/Stream.js"></script> <script src="{{ STATIC_URL }}js/widgets/Stream.js"></script>
<script src="{{ STATIC_URL }}js/help/InventoryGroups.js"></script> <script src="{{ STATIC_URL }}js/help/InventoryGroups.js"></script>
<script src="{{ STATIC_URL }}js/help/InventoryHosts.js"></script>
<script src="{{ STATIC_URL }}lib/less/less-1.4.1.min.js"></script> <script src="{{ STATIC_URL }}lib/less/less-1.4.1.min.js"></script>
{% endif %} {% endif %}

9
package.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "ansible-commander",
"version": "1.4.5",
"devDependencies": {
"grunt": "latest",
"grunt-contrib-jshint": "~0.8.0",
"grunt-contrib-uglify": "~0.3.1"
}
}