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/css/awx.min.css
env/*
node_modules/**
build
deb-build
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.
*
*/
var urlPrefix = $basePath;
angular.module('ansible', [
'RestServices',
'AuthService',
'Utilities',
'OrganizationFormDefinition',
'OrganizationFormDefinition',
'UserFormDefinition',
'FormGenerator',
'OrganizationListDefinition',
'UserListDefinition',
'UserListDefinition',
'UserHelper',
'ListGenerator',
'ListGenerator',
'PromptDialog',
'ApiLoader',
'RelatedSearchHelper',
'ApiLoader',
'RelatedSearchHelper',
'SearchHelper',
'PaginationHelpers',
'RefreshHelper',
@ -58,6 +57,7 @@ angular.module('ansible', [
'JobFormDefinition',
'JobEventsListDefinition',
'JobEventDataDefinition',
'JobEventsFormDefinition',
'JobHostDefinition',
'JobSummaryDefinition',
'ParseHelper',
@ -75,9 +75,7 @@ angular.module('ansible', [
'ObjectCountWidget',
'StreamWidget',
'JobsHelper',
'InventoryStatusDefinition',
'InventoryGroupsHelpDefinition',
'InventoryHostsHelpDefinition',
'InventoryTree',
'CredentialsHelper',
'TimerService',
@ -85,253 +83,368 @@ angular.module('ansible', [
'HomeGroupListDefinition',
'HomeHostListDefinition',
'ActivityDetailDefinition'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/jobs',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsListCtrl }).
])
.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/jobs', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobsListCtrl'
}).
when('/jobs/:id',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsEdit }).
when('/jobs/:id', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobsEdit'
}).
when('/jobs/:id/job_events',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsList }).
when('/jobs/:id/job_events', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobEventsList'
}).
when('/jobs/:id/job_host_summaries',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }).
when('/jobs/:job_id/job_events/:event_id',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsEdit }).
when('/jobs/:id/job_host_summaries', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobHostSummaryList'
}).
when('/job_templates',
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesList }).
when('/job_templates/add',
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesAdd }).
when('/jobs/:job_id/job_events/:event_id', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobEventsEdit'
}).
when('/job_templates/:id',
{ templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesEdit }).
when('/job_templates', {
templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesList'
}).
when('/projects',
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsList }).
when('/job_templates/add', {
templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesAdd'
}).
when('/projects/add',
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsAdd }).
when('/job_templates/:id', {
templateUrl: urlPrefix + 'partials/job_templates.html',
controller: 'JobTemplatesEdit'
}).
when('/projects/:id',
{ templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsEdit }).
when('/projects', {
templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsList'
}).
when('/projects/:project_id/organizations',
{ templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsList }).
when('/projects/add', {
templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsAdd'
}).
when('/projects/:project_id/organizations/add',
{ templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsAdd }).
when('/projects/:id', {
templateUrl: urlPrefix + 'partials/projects.html',
controller: 'ProjectsEdit'
}).
when('/hosts/:id/job_host_summaries',
{ templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }).
when('/projects/:project_id/organizations', {
templateUrl: urlPrefix + 'partials/projects.html',
controller: 'OrganizationsList'
}).
when('/inventories',
{ templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesList }).
when('/projects/:project_id/organizations/add', {
templateUrl: urlPrefix + 'partials/projects.html',
controller: 'OrganizationsAdd'
}).
when('/inventories/add',
{ templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesAdd }).
when('/hosts/:id/job_host_summaries', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobHostSummaryList'
}).
when('/inventories/:inventory_id',
{ templateUrl: urlPrefix + 'partials/inventory-edit.html', controller: InventoriesEdit }).
when('/inventories', {
templateUrl: urlPrefix + 'partials/inventories.html',
controller: 'InventoriesList'
}).
when('/organizations', { templateUrl: urlPrefix + 'partials/organizations.html',
controller: OrganizationsList }).
when('/inventories/add', {
templateUrl: urlPrefix + 'partials/inventories.html',
controller: 'InventoriesAdd'
}).
when('/organizations/add', { templateUrl: urlPrefix + 'partials/organizations.html',
controller: OrganizationsAdd }).
when('/inventories/:inventory_id', {
templateUrl: urlPrefix + 'partials/inventory-edit.html',
controller: 'InventoriesEdit'
}).
when('/organizations/:organization_id', { templateUrl: urlPrefix + 'partials/organizations.html',
controller: OrganizationsEdit }).
when('/organizations', {
templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsList'
}).
when('/organizations/:organization_id/admins', { templateUrl: urlPrefix + 'partials/organizations.html',
controller: AdminsList }).
when('/organizations/add', {
templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsAdd'
}).
when('/organizations/:organization_id/users', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersList }).
when('/organizations/:organization_id', {
templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'OrganizationsEdit'
}).
when('/organizations/:organization_id/users/add', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersAdd }).
when('/organizations/:organization_id/admins', {
templateUrl: urlPrefix + 'partials/organizations.html',
controller: 'AdminsList'
}).
when('/organizations/:organization_id/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersEdit }).
when('/organizations/:organization_id/users', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersList'
}).
when('/teams', { templateUrl: urlPrefix + 'partials/teams.html',
controller: TeamsList }).
when('/organizations/:organization_id/users/add', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersAdd'
}).
when('/teams/add', { templateUrl: urlPrefix + 'partials/teams.html',
controller: TeamsAdd }).
when('/organizations/:organization_id/users/:user_id', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersEdit'
}).
when('/teams/:team_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: TeamsEdit }).
when('/teams/:team_id/permissions/add', { templateUrl: urlPrefix + 'partials/teams.html',
controller: PermissionsAdd }).
when('/teams', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsList'
}).
when('/teams/:team_id/permissions', { templateUrl: urlPrefix + 'partials/teams.html',
controller: PermissionsList }).
when('/teams/add', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsAdd'
}).
when('/teams/:team_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: PermissionsEdit }).
when('/teams/:team_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'TeamsEdit'
}).
when('/teams/:team_id/users', { templateUrl: urlPrefix + 'partials/teams.html',
controller: UsersList }).
when('/teams/:team_id/permissions/add', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsAdd'
}).
when('/teams/:team_id/users/:user_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: UsersEdit }).
when('/teams/:team_id/permissions', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsList'
}).
when('/teams/:team_id/projects', { templateUrl: urlPrefix + 'partials/teams.html',
controller: ProjectsList }).
when('/teams/:team_id/permissions/:permission_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'PermissionsEdit'
}).
when('/teams/:team_id/projects/add', { templateUrl: urlPrefix + 'partials/teams.html',
controller: ProjectsAdd }).
when('/teams/:team_id/users', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'UsersList'
}).
when('/teams/:team_id/projects/:project_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: ProjectsEdit }).
when('/teams/:team_id/credentials', { templateUrl: urlPrefix + 'partials/teams.html',
controller: CredentialsList }).
when('/teams/:team_id/users/:user_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'UsersEdit'
}).
when('/teams/:team_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html',
controller: CredentialsAdd }).
when('/teams/:team_id/projects', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsList'
}).
when('/teams/:team_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: CredentialsEdit }).
when('/teams/:team_id/projects/add', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsAdd'
}).
when('/credentials', { templateUrl: urlPrefix + 'partials/credentials.html',
controller: CredentialsList }).
when('/teams/:team_id/projects/:project_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'ProjectsEdit'
}).
when('/credentials/add', { templateUrl: urlPrefix + 'partials/credentials.html',
controller: CredentialsAdd }).
when('/teams/:team_id/credentials', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsList'
}).
when('/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/credentials.html',
controller: CredentialsEdit }).
when('/users', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersList }).
when('/teams/:team_id/credentials/add', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsAdd'
}).
when('/users/add', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersAdd }).
when('/teams/:team_id/credentials/:credential_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsEdit'
}).
when('/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html',
controller: UsersEdit }).
when('/credentials', {
templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsList'
}).
when('/users/:user_id/credentials', { templateUrl: urlPrefix + 'partials/users.html',
controller: CredentialsList }).
when('/credentials/add', {
templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsAdd'
}).
when('/users/:user_id/permissions/add', { templateUrl: urlPrefix + 'partials/users.html',
controller: PermissionsAdd }).
when('/credentials/:credential_id', {
templateUrl: urlPrefix + 'partials/credentials.html',
controller: 'CredentialsEdit'
}).
when('/users/:user_id/permissions', { templateUrl: urlPrefix + 'partials/users.html',
controller: PermissionsList }).
when('/users', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersList'
}).
when('/users/:user_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/users.html',
controller: PermissionsEdit }).
when('/users/add', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersAdd'
}).
when('/users/:user_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html',
controller: CredentialsAdd }).
when('/users/:user_id', {
templateUrl: urlPrefix + 'partials/users.html',
controller: 'UsersEdit'
}).
when('/teams/:user_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html',
controller: CredentialsEdit }).
when('/users/:user_id/credentials', {
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 }).
otherwise({redirectTo: '/home'});
}])
.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();
when('/teams/:user_id/credentials/:credential_id', {
templateUrl: urlPrefix + 'partials/teams.html',
controller: 'CredentialsEdit'
}).
$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
if ($('#stream-container').is(':visible')) {
HideStream();
}
when('/logout', {
templateUrl: urlPrefix + 'partials/home.html',
controller: 'Authenticate'
}).
// 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());
}
when('/home', {
templateUrl: urlPrefix + 'partials/home.html',
controller: 'Home'
}).
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');
when('/home/groups', {
templateUrl: urlPrefix + 'partials/subhome.html',
controller: 'HomeGroups'
}).
when('/home/hosts', {
templateUrl: urlPrefix + 'partials/subhome.html',
controller: 'HomeHosts'
}).
otherwise({
redirectTo: '/home'
});
}
])
.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 = [];
$rootScope.crumbCache = [];
$rootScope.sessionTimer = Timer.init();
$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()) {
// When the app first loads, redirect to login page
$rootScope.sessionExpired = false;
$cookieStore.put('sessionExpired', false);
$location.path('/login');
}
else {
// If browser refresh, set the user_is_superuser value
$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);
if (!Authorization.getToken()) {
// When the app first loads, redirect to login page
$rootScope.sessionExpired = false;
$cookieStore.put('sessionExpired', false);
$location.path('/login');
} else {
// If browser refresh, set the user_is_superuser value
$rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser');
}
$rootScope.viewLicense = function() {
//$location.path('/license');
ViewLicense();
}
}]);
// 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 () {
//$location.path('/license');
ViewLicense();
};
}
]);

View File

@ -1,4 +1,4 @@
/************************************
/**********************************************************************
*
* Copyright (c) 2014 AnsibleWorks, Inc.
*
@ -8,19 +8,21 @@
*
*/
var $AnsibleConfig =
{
tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
/*jshint unused:false */
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.
var $AnsibleConfig = {
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.
}
tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips
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.
*
*
* Credentials.js
*
* Controller functions for the Credential model.
*
*/
'use strict';
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 + '/';
Rest.setUrl(url);
Rest.destroy()
.success(function (data, status, headers, config) {
.success(function () {
scope.search(list.iterator);
})
.error(function (data, status, headers, config) {
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
@ -143,7 +143,7 @@ CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$routeP
function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath,
GetChoices, Empty, KindChange, OwnerChange, FormSave, DebugForm) {
GetChoices, Empty, KindChange, OwnerChange, FormSave) {
ClearScope('tree-form');
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@ -152,7 +152,6 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
var form = CredentialForm,
generator = GenerateForm,
scope = generator.inject(form, { mode: 'add', related: false }),
base = $location.path().replace(/^\//, '').split('/')[0],
defaultUrl = GetBasePath('credentials'),
url;
@ -193,10 +192,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
url = GetBasePath('users') + $routeParams.user_id + '/';
Rest.setUrl(url);
Rest.get()
.success(function (data, status, headers, config) {
.success(function (data) {
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
});
});
@ -210,10 +209,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
url = GetBasePath('teams') + $routeParams.team_id + '/';
Rest.setUrl(url);
Rest.get()
.success(function (data, status, headers, config) {
.success(function (data) {
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
});
});
@ -289,7 +288,7 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar
CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'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'
];
function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, GenerateForm,
Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView,
Wait) {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
// Inject dynamic view
var form = JobEventForm,
function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventsForm, GenerateForm,
Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView, Wait) {
ClearScope('htmlTemplate');
var form = JobEventsForm,
generator = GenerateForm,
scope = GenerateForm.inject(form, { mode: 'edit', related: true }),
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',
'EventView', 'Wait'
];

View File

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

View File

@ -6,23 +6,22 @@
*
*/
angular.module('CredentialFormDefinition', [])
.value(
'CredentialForm', {
addTitle: 'Create Credential', //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
.value('CredentialForm', {
addTitle: 'Create Credential', //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'credential',
well: true,
forceListeners: true,
actions: {
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
mode: 'edit'
}
},
}
},
fields: {
name: {
label: 'Name',
@ -30,27 +29,31 @@ angular.module('CredentialFormDefinition', [])
addRequired: true,
editRequired: true,
autocomplete: false
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
},
},
owner: {
label: "Does this credential belong to a team or user?",
type: 'radio_group',
ngChange: "ownerChange()",
options: [
{ label: 'User', value: 'user', selected: true },
{ 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 " +
options: [{
label: 'User',
value: 'user',
selected: true
}, {
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>",
dataTitle: 'Owner',
dataPlacement: 'right',
dataContainer: "body"
},
},
user: {
label: 'User that owns this credential',
type: 'lookup',
@ -58,8 +61,11 @@ angular.module('CredentialFormDefinition', [])
sourceField: 'username',
ngClick: 'lookUpUser()',
ngShow: "owner == 'user'",
awRequiredWhen: { variable: "user_required", init: "false" }
},
awRequiredWhen: {
variable: "user_required",
init: "false"
}
},
team: {
label: 'Team that owns this credential',
type: 'lookup',
@ -67,69 +73,84 @@ angular.module('CredentialFormDefinition', [])
sourceField: 'name',
ngClick: 'lookUpTeam()',
ngShow: "owner == 'team'",
awRequiredWhen: { variable: "team_required", init: "false" }
},
awRequiredWhen: {
variable: "team_required",
init: "false"
}
},
kind: {
label: 'Type',
excludeModal: true,
type: 'select',
ngOptions: 'kind.label for kind in credential_kind_options',
ngChange: 'kindChange()',
addRequired: true,
addRequired: true,
editRequired: true,
helpCollapse: [
{ hdr: 'Select a Credential Type',
content: '<dl>\n' +
'<dt>AWS</dt>\n' +
'<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' +
'<dt>Machine</dt>\n' +
'<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 ' +
'remote hosts.</dd>' +
'<dt>Rackspace</dt>\n' +
'<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' +
'<dt>SCM</dt>\n' +
'<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 ' +
'used on the Projects tab.</dd>\n' +
'</dl>\n'
}]
},
helpCollapse: [{
hdr: 'Select a Credential Type',
content: '<dl>\n' +
'<dt>AWS</dt>\n' +
'<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' +
'<dt>Machine</dt>\n' +
'<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 ' +
'remote hosts.</dd>' +
'<dt>Rackspace</dt>\n' +
'<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' +
'<dt>SCM</dt>\n' +
'<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 ' +
'used on the Projects tab.</dd>\n' +
'</dl>\n'
}]
},
access_key: {
label: 'Access Key',
type: 'text',
ngShow: "kind.value == 'aws'",
awRequiredWhen: { variable: "aws_required", init: false },
awRequiredWhen: {
variable: "aws_required",
init: false
},
autocomplete: false,
apiField: 'username'
},
},
secret_key: {
label: 'Secret Key',
type: 'password',
ngShow: "kind.value == 'aws'",
awRequiredWhen: { variable: "aws_required", init: false },
awRequiredWhen: {
variable: "aws_required",
init: false
},
autocomplete: false,
ask: false,
clear: false,
apiField: 'passwowrd'
},
},
"username": {
labelBind: 'usernameLabel',
type: 'text',
ngShow: "kind.value && kind.value !== 'aws'",
awRequiredWhen: {variable: 'rackspace_required', init: false },
autocomplete: false
awRequiredWhen: {
variable: 'rackspace_required',
init: false
},
autocomplete: false
},
"api_key": {
label: 'API Key',
type: 'password',
ngShow: "kind.value == 'rax'",
awRequiredWhen: { variable: "rackspace_required", init: false },
awRequiredWhen: {
variable: "rackspace_required",
init: false
},
autocomplete: false,
ask: false,
clear: false,
apiField: 'passwowrd'
},
},
"password": {
label: 'Password',
type: 'password',
@ -141,7 +162,7 @@ angular.module('CredentialFormDefinition', [])
clear: false,
associated: 'password_confirm',
autocomplete: false
},
},
"password_confirm": {
label: 'Confirm Password',
type: 'password',
@ -151,7 +172,7 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true,
associated: 'password',
autocomplete: false
},
},
"ssh_password": {
label: 'SSH Password',
type: 'password',
@ -163,7 +184,7 @@ angular.module('CredentialFormDefinition', [])
clear: true,
associated: 'ssh_password_confirm',
autocomplete: false
},
},
"ssh_password_confirm": {
label: 'Confirm SSH Password',
type: 'password',
@ -173,7 +194,7 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true,
associated: 'ssh_password',
autocomplete: false
},
},
"ssh_key_data": {
labelBind: 'sshKeyDataLabel',
type: 'textarea',
@ -182,7 +203,7 @@ angular.module('CredentialFormDefinition', [])
editRequired: false,
'class': 'ssh-key-field',
rows: 10
},
},
"ssh_key_unlock": {
label: 'Key Password',
type: 'password',
@ -192,9 +213,9 @@ angular.module('CredentialFormDefinition', [])
ngChange: "clearPWConfirm('ssh_key_unlock_confirm')",
associated: 'ssh_key_unlock_confirm',
ask: true,
askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials
askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials
clear: true
},
},
"ssh_key_unlock_confirm": {
label: 'Confirm Key Password',
type: 'password',
@ -203,7 +224,7 @@ angular.module('CredentialFormDefinition', [])
editRequired: false,
awPassMatch: true,
associated: 'ssh_key_unlock'
},
},
"sudo_username": {
label: 'Sudo Username',
type: 'text',
@ -211,7 +232,7 @@ angular.module('CredentialFormDefinition', [])
addRequired: false,
editRequired: false,
autocomplete: false
},
},
"sudo_password": {
label: 'Sudo Password',
type: 'password',
@ -223,7 +244,7 @@ angular.module('CredentialFormDefinition', [])
clear: true,
associated: 'sudo_password_confirm',
autocomplete: false
},
},
"sudo_password_confirm": {
label: 'Confirm Sudo Password',
type: 'password',
@ -233,24 +254,21 @@ angular.module('CredentialFormDefinition', [])
awPassMatch: true,
associated: 'sudo_password',
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
* Form definition for Group model
*
*
*
*/
angular.module('GroupFormDefinition', [])
.value(
'GroupForm', {
.value('GroupForm', {
addTitle: 'Create Group',
editTitle: 'Edit Group',
showTitle: true,
@ -18,12 +17,15 @@ angular.module('GroupFormDefinition', [])
well: true,
formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9',
tabs: [
{ name: 'properties', label: 'Properties'},
{ name: 'source', label: 'Source' }
],
tabs: [{
name: 'properties',
label: 'Properties'
}, {
name: 'source',
label: 'Source'
}],
fields: {
name: {
label: 'Name',
@ -31,19 +33,19 @@ angular.module('GroupFormDefinition', [])
addRequired: true,
editRequired: true,
tab: 'properties'
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false,
tab: 'properties'
},
},
variables: {
label: 'Variables',
type: 'textarea',
addRequired: false,
editRequird: false,
editRequird: false,
rows: 6,
'default': '---',
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>',
dataContainer: 'body',
tab: 'properties'
},
},
source: {
label: 'Source',
type: 'select',
ngOptions: 'source.label for source in source_type_options',
ngChange: 'sourceChange()',
addRequired: false,
addRequired: false,
editRequired: false,
//'default': { label: 'Manual', value: '' },
tab: 'source'
},
},
source_path: {
label: 'Script Path',
ngShow: "source && source.value == 'file'",
type: 'text',
awRequiredWhen: {variable: "sourcePathRequired", init: "false" },
tab: 'source'
awRequiredWhen: {
variable: "sourcePathRequired",
init: "false"
},
tab: 'source'
},
credential: {
label: 'Cloud Credential',
type: 'lookup',
@ -84,10 +89,10 @@ angular.module('GroupFormDefinition', [])
sourceModel: 'credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
addRequired: false,
addRequired: false,
editRequired: false,
tab: 'source'
},
},
source_regions: {
label: 'Regions',
type: 'text',
@ -102,13 +107,13 @@ angular.module('GroupFormDefinition', [])
"</p>",
dataContainer: 'body',
tab: 'source'
},
},
source_vars: {
label: 'Source Variables',
ngShow: "source && (source.value == 'file' || source.value == 'ec2')",
type: 'textarea',
addRequired: false,
editRequird: false,
editRequird: false,
rows: 6,
'default': '---',
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>',
dataContainer: 'body',
tab: 'source'
},
},
checkbox_group: {
label: 'Update Options',
type: 'checkbox_group',
ngShow: "source && (source.value !== '' && source.value !== null)",
tab: 'source',
fields: [
{
name: 'overwrite',
label: 'Overwrite',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
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 ' +
'remain untouched by the inventory update process.</p>',
dataTitle: 'Overwrite',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
},
{
name: 'overwrite_vars',
label: 'Overwrite Variables',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
awPopOver: '<p>If checked, all variables for child groups and hosts will be removed and replaced by those ' +
'found on the external source.</p><p>When not checked a merge will be performed, combining local variables with ' +
'those found on the external source.</p>',
dataTitle: 'Overwrite Variables',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
},
{
name: 'update_on_launch',
label: 'Update on Launch',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
awPopOver: '<p>Each time a job runs using this inventory, refresh the inventory from the selected source before ' +
'executing job tasks.</p>',
dataTitle: 'Update on Launch',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}
]
}
},
fields: [{
name: 'overwrite',
label: 'Overwrite',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
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 ' +
'remain untouched by the inventory update process.</p>',
dataTitle: 'Overwrite',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}, {
name: 'overwrite_vars',
label: 'Overwrite Variables',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
awPopOver: '<p>If checked, all variables for child groups and hosts will be removed and replaced by those ' +
'found on the external source.</p><p>When not checked a merge will be performed, combining local variables with ' +
'those found on the external source.</p>',
dataTitle: 'Overwrite Variables',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}, {
name: 'update_on_launch',
label: 'Update on Launch',
type: 'checkbox',
ngShow: "source.value !== '' && source.value !== null",
addRequired: false,
editRequired: false,
awPopOver: '<p>Each time a job runs using this inventory, refresh the inventory from the selected source before ' +
'executing job tasks.</p>',
dataTitle: 'Update on Launch',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}]
}
},
buttons: {
buttons: { //for now always generates <button> tags
labelClass: 'col-lg-3',
controlClass: 'col-lg-5',
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
}
save: {
ngClick: 'formSave()',
ngDisabled: true
},
related: { //related colletions (and maybe items?)
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
},
});
related: { }
});

View File

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

View File

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

View File

@ -4,12 +4,11 @@
* Inventories.js
* Form definition for User model
*
*
*
*/
angular.module('InventoryFormDefinition', [])
.value(
'InventoryForm', {
.value('InventoryForm', {
addTitle: 'Create Inventory',
editTitle: '{{ inventory_name | capitalize }}',
name: 'inventory',
@ -24,8 +23,8 @@ angular.module('InventoryFormDefinition', [])
icon: "icon-comments-alt",
mode: 'edit',
iconSize: 'large'
}
},
}
},
fields: {
inventory_name: {
@ -35,29 +34,32 @@ angular.module('InventoryFormDefinition', [])
addRequired: true,
editRequired: true,
capitalize: false
},
inventory_description: {
},
inventory_description: {
realName: 'description',
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
},
},
organization: {
label: 'Organization',
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
ngClick: 'lookUpOrganization()',
awRequiredWhen: {variable: "organizationrequired", init: "true" }
},
awRequiredWhen: {
variable: "organizationrequired",
init: "true"
}
},
inventory_variables: {
realName: 'variables',
label: 'Variables',
type: 'textarea',
'class': 'span12',
addRequired: false,
editRequird: false,
editRequird: false,
parseTypeName: 'inventoryParseType',
rows: 6,
"default": "---",
@ -71,23 +73,22 @@ angular.module('InventoryFormDefinition', [])
dataTitle: 'Inventory Variables',
dataPlacement: 'right',
dataContainer: 'body'
}
},
}
},
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
buttons: {
save: {
ngClick: 'formSave()',
ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true
}
},
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', [])
.value(
'JobEventDataForm', {
.value('JobEventDataForm', {
editTitle: '{{ id }} - {{ event_display }}', //Legend in edit mode
editTitle: '{{ id }} - {{ event_display }}',
name: 'job_events',
well: false,
'class': 'horizontal-narrow',
@ -22,7 +21,7 @@ angular.module('JobEventDataDefinition', [])
readonly: true,
rows: 18,
'class': 'modal-input-xxlarge'
}
}
}
}); //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', [])
.value(
'JobSummary', {
.value('JobSummary', {
editTitle: '{{ id }} - {{ name }}',
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> ' +
'<i class=\"fa icon-job-{{ status }}\"></i> {{ status }}</div>',
readonly: true
},
},
created: {
label: 'Created On',
type: 'text',
readonly: true
},
},
result_stdout: {
label: 'Standard Out',
type: 'textarea',
@ -35,7 +34,7 @@ angular.module('JobSummaryDefinition', [])
rows: '{{ stdout_rows }}',
'class': 'nowrap mono-space resizable',
ngShow: 'result_stdout != ""'
},
},
result_traceback: {
label: 'Traceback',
type: 'textarea',
@ -44,7 +43,6 @@ angular.module('JobSummaryDefinition', [])
rows: '{{ traceback_rows }}',
'class': 'nowrap mono-space resizable',
ngShow: 'result_traceback != ""'
}
}
}
});

View File

@ -7,11 +7,10 @@
*
*/
angular.module('JobTemplateFormDefinition', [])
.value(
'JobTemplateForm', {
.value('JobTemplateForm', {
addTitle: 'Create Job Templates', //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
addTitle: 'Create Job Templates',
editTitle: '{{ name }}',
name: 'job_templates',
twoColumns: true,
well: true,
@ -25,8 +24,8 @@ angular.module('JobTemplateFormDefinition', [])
icon: "icon-comments-alt",
mode: 'edit',
iconSize: 'large'
}
},
}
},
fields: {
name: {
@ -35,20 +34,20 @@ angular.module('JobTemplateFormDefinition', [])
addRequired: true,
editRequired: true,
column: 1
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false,
column: 1
},
},
job_type: {
label: 'Job Type',
type: 'select',
ngOptions: 'type.label for type in job_type_options',
"default": 0,
addRequired: true,
addRequired: true,
editRequired: true,
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 " +
@ -57,7 +56,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Job Type',
dataPlacement: 'right',
dataContainer: "body"
},
},
inventory: {
label: 'Inventory',
type: 'lookup',
@ -70,7 +69,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Inventory',
dataPlacement: 'right',
dataContainer: "body"
},
},
project: {
label: 'Project',
type: 'lookup',
@ -83,7 +82,7 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Project',
dataPlacement: 'right',
dataContainer: "body"
},
},
playbook: {
label: 'Playbook',
type:'select',
@ -95,29 +94,29 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Playbook',
dataPlacement: 'right',
dataContainer: "body"
},
},
credential: {
label: 'Machine Credential',
type: 'lookup',
sourceModel: 'credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
addRequired: false,
addRequired: false,
editRequired: false,
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>",
dataTitle: 'Credential',
dataPlacement: 'right',
dataContainer: "body"
},
},
cloud_credential: {
label: 'Cloud Credential',
type: 'lookup',
sourceModel: 'cloud_credential',
sourceField: 'name',
ngClick: 'lookUpCloudcredential()',
addRequired: false,
addRequired: false,
editRequired: false,
column: 1,
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',
dataPlacement: 'right',
dataContainer: "body"
},
},
forks: {
label: 'Forks',
id: 'forks-number',
type: 'number',
type: 'number',
integer: true,
min: 0,
spinner: true,
spinner: true,
"default": '0',
addRequired: false,
addRequired: false,
editRequired: false,
'class': "input-small",
column: 1,
@ -144,11 +143,11 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Forks',
dataPlacement: 'right',
dataContainer: "body"
},
},
limit: {
label: 'Limit',
type: 'text',
addRequired: false,
type: 'text',
addRequired: false,
editRequired: false,
column: 1,
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',
dataPlacement: 'right',
dataContainer: "body"
},
},
verbosity: {
label: 'Verbosity',
type: 'select',
ngOptions: 'v.label for v in verbosity_options',
"default": 0,
addRequired: true,
addRequired: true,
editRequired: true,
column: 1,
awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>",
dataTitle: 'Verbosity',
dataPlacement: 'right',
dataContainer: "body"
},
},
variables: {
label: 'Extra Variables',
type: 'textarea',
rows: 6,
"class": 'span12',
addRequired: false,
addRequired: false,
editRequired: false,
"default": "---",
column: 2,
@ -189,13 +188,13 @@ angular.module('JobTemplateFormDefinition', [])
dataTitle: 'Extra Variables',
dataPlacement: 'right',
dataContainer: "body"
},
},
job_tags: {
label: 'Job Tags',
type: 'textarea',
rows: 1,
addRequired: false,
editRequired: false,
type: 'textarea',
rows: 1,
addRequired: false,
editRequired: false,
'class': 'span12',
column: 2,
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. " +
"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 " +
"in the Job Tags field:<\p>\n" +
"<blockquote>configuration,packages</blockquote>\n",
"in the Job Tags field:</p>\n<blockquote>configuration,packages</blockquote>\n",
dataTitle: "Job Tags",
dataPlacement: "right",
dataContainer: "body"
},
},
allow_callbacks: {
label: 'Allow Callbacks',
type: 'checkbox',
addRequired: false,
addRequired: false,
editRequird: false,
trueValue: 'true',
falseValue: 'false',
ngChange: "toggleCallback('host_config_key')",
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" +
"<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 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" +
"<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>" +
@ -230,11 +228,11 @@ angular.module('JobTemplateFormDefinition', [])
dataPlacement: 'right',
dataTitle: 'Callback URL',
dataContainer: "body"
},
},
callback_url: {
label: 'Callback URL',
label: 'Callback URL',
type: 'text',
addRequired: false,
addRequired: false,
editRequired: false,
readonly: true,
column: 2,
@ -242,7 +240,7 @@ angular.module('JobTemplateFormDefinition', [])
'class': 'span12',
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" +
"<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" +
"<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>" +
@ -250,7 +248,7 @@ angular.module('JobTemplateFormDefinition', [])
dataPlacement: 'right',
dataTitle: 'Callback URL',
dataContainer: "body"
},
},
host_config_key: {
label: 'Host Config Key',
type: 'text',
@ -259,25 +257,25 @@ angular.module('JobTemplateFormDefinition', [])
column: 2,
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" +
"<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",
dataPlacement: 'right',
dataContainer: "body"
}
},
}
},
buttons: { //for now always generates <button> tags
save: {
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
},
}
},
related: { //related colletions (and maybe items?)
related: {
jobs: {
type: 'collection',
@ -295,54 +293,55 @@ angular.module('JobTemplateFormDefinition', [])
awToolTip: "Reset the search filter",
ngClick: "resetSearch('job')",
iconSize: 'large'
}
},
}
},
fields: {
id: {
label: 'Job ID',
key: true,
desc: true,
searchType: 'int'
},
searchType: 'int'
},
created: {
label: 'Date',
link: false,
searchable: false
},
},
status: {
label: 'Status',
"class": 'job-\{\{ job.status \}\}',
"class": 'job-{{ job.status }}',
searchType: 'select',
linkTo: "\{\{ job.statusLinkTo \}\}",
linkTo: "{{}} job.statusLinkTo }}",
searchOptions: [
{ name: "new", value: "new" },
{ name: "waiting", value: "waiting" },
{ name: "pending", value: "pending" },
{ name: "running", value: "running" },
{ name: "running", value: "running" },
{ name: "successful", value: "successful" },
{ name: "error", value: "error" },
{ name: "failed", value: "failed" },
{ name: "canceled", value: "canceled" } ],
badgeIcon: 'fa icon-job-\{\{ job.status \}\}',
{ name: "canceled", value: "canceled" }
],
badgeIcon: 'fa icon-job-{{ job.status }}',
badgePlacement: 'left',
badgeToolTip: "\{\{ job.statusBadgeToolTip \}\}",
badgeToolTip: "{{ job.statusBadgeToolTip }}",
badgeTipPlacement: 'top',
badgeNgHref: "\{\{ job.statusLinkTo \}\}",
awToolTip: "\{\{ job.statusBadgeToolTip \}\}",
badgeNgHref: "{{ job.statusLinkTo }}",
awToolTip: "{{ job.statusBadgeToolTip }}",
dataPlacement: 'top'
}
},
}
},
fieldActions: {
edit: {
label: 'View',
ngClick: "edit('jobs', \{\{ job.id \}\}, '\{\{ job.name \}\}')",
ngClick: "edit('jobs', job.id, job.name)",
icon: 'icon-zoom-in'
}
}
}
}
}
}); //InventoryForm

View File

@ -7,9 +7,8 @@
* @dict
*/
angular.module('JobFormDefinition', [])
.value(
'JobForm', {
.value('JobForm', {
addTitle: 'Create Job',
editTitle: '{{ id }} - {{ name }}',
name: 'jobs',
@ -26,19 +25,19 @@ angular.module('JobFormDefinition', [])
icon: 'icon-zoom-in',
active: true,
ngShow: "job_id !== null"
},
},
events: {
href: "/#/jobs/{{ job_id }}/job_events",
label: 'Events',
icon: 'icon-list-ul'
},
hosts: {
},
hosts: {
href: "/#/jobs/{{ job_id }}/job_host_summaries",
label: 'Host Summary',
icon: 'icon-laptop'
}
},
}
},
fields: {
name: {
label: 'Job Template',
@ -47,20 +46,20 @@ angular.module('JobFormDefinition', [])
editRequired: false,
readonly: true,
column: 1
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false,
column: 1
},
},
job_type: {
label: 'Job Type',
type: 'select',
ngOptions: 'type.label for type in job_type_options',
"default": 'run',
addRequired: true,
addRequired: 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 " +
" 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',
dataContainer: 'body',
column: 1
},
},
inventory: {
label: 'Inventory',
type: 'lookup',
@ -83,7 +82,7 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Inventory',
dataPlacement: 'right',
dataContainer: "body"
},
},
project: {
label: 'Project',
type: 'lookup',
@ -97,42 +96,42 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Project',
dataPlacement: 'right',
dataContainer: "body"
},
},
playbook: {
label: 'Playbook',
type:'select',
type: 'select',
ngOptions: 'book for book in playbook_options',
id: 'playbook-select',
addRequired: true,
addRequired: true,
editRequired: true,
column: 1,
awPopOver: "<p>Select the playbook to be executed by this job.</p>",
dataTitle: 'Playbook',
dataPlacement: 'right',
dataContainer: "body"
},
},
credential: { // FIXME: Lookup only credentials with kind=ssh
label: 'Credential',
type: 'lookup',
sourceModel: 'credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
addRequired: false,
addRequired: false,
editRequired: false,
column: 1,
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>",
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>",
dataTitle: 'Credential',
dataPlacement: 'right',
dataContainer: "body"
},
},
cloud_credential: { // FIXME: Lookup only credentials with kind=aws/rax
label: 'Cloud Credential',
type: 'lookup',
sourceModel: 'cloud_credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
addRequired: false,
addRequired: false,
editRequired: false,
column: 1,
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',
dataPlacement: 'right',
dataContainer: "body"
},
},
forks: {
label: 'Forks',
id: 'forks-number',
type: 'number',
type: 'number',
integer: true,
min: 0,
spinner: true,
spinner: true,
"class": 'input-small',
"default": '0',
addRequired: false,
addRequired: false,
editRequired: false,
column: 1,
disabled: true,
@ -158,43 +157,43 @@ angular.module('JobFormDefinition', [])
dataContainer: 'body',
dataTitle: 'Forks',
dataPlacement: 'right'
},
},
limit: {
label: 'Limit',
type: 'text',
addRequired: false,
type: 'text',
addRequired: false,
editRequired: false,
column: 1,
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 " +
"<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>",
dataContainer: 'body',
dataTitle: 'Limit',
dataPlacement: 'right'
},
},
verbosity: {
label: 'Verbosity',
type: 'select',
ngOptions: 'v.label for v in verbosity_options',
"default": 0,
addRequired: true,
addRequired: true,
editRequired: true,
column: 1,
awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>",
dataTitle: 'Verbosity',
dataPlacement: 'right',
dataContainer: 'body'
},
},
variables: {
label: 'Extra Variables',
type: 'textarea',
rows: 6,
"class": 'span12',
addRequired: false,
addRequired: false,
editRequired: false,
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>" +
"JSON:<br />\n" +
"<blockquote>{<br />\"somevar\": \"somevalue\",<br />\"password\": \"magic\"<br /> }</blockquote>\n" +
@ -203,13 +202,13 @@ angular.module('JobFormDefinition', [])
dataTitle: 'Extra Variables',
dataContainer: 'body',
dataPlacement: 'right'
},
},
job_tags: {
label: 'Job Tags',
type: 'textarea',
rows: 1,
addRequired: false,
editRequired: false,
type: 'textarea',
rows: 1,
addRequired: false,
editRequired: false,
'class': 'span12',
column: 2,
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. " +
"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 " +
"in the Job Tags field:<\p>\n" +
"in the Job Tags field:</p>\n" +
"<blockquote>configuration,packages</blockquote>\n",
dataTitle: "Job Tags",
dataContainer: 'body',
dataPlacement: "right"
},
},
allow_callbacks: {
label: 'Allow Callbacks',
type: 'checkbox',
addRequired: false,
addRequired: false,
editRequird: false,
trueValue: 'true',
falseValue: 'false',
ngChange: "toggleCallback('host_config_key')",
"class": "span12",
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" +
"<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 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" +
"<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>" +
@ -245,11 +244,11 @@ angular.module('JobFormDefinition', [])
dataPlacement: 'right',
dataContainer: 'body',
dataTitle: 'Callback URL'
},
},
callback_url: {
label: 'Callback URL',
label: 'Callback URL',
type: 'text',
addRequired: false,
addRequired: false,
editRequired: false,
readonly: true,
column: 2,
@ -257,7 +256,7 @@ angular.module('JobFormDefinition', [])
'class': 'span12',
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" +
"<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" +
"<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>" +
@ -265,7 +264,7 @@ angular.module('JobFormDefinition', [])
dataPlacement: 'right',
dataContainer: 'body',
dataTitle: 'Callback URL'
},
},
host_config_key: {
label: 'Host Config Key',
type: 'text',
@ -274,64 +273,65 @@ angular.module('JobFormDefinition', [])
column: 2,
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" +
"<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",
dataPlacement: 'right',
dataContainer: 'body'
}
},
}
},
buttons: { //for now always generates <button> tags
save: {
label: 'Save',
buttons: {
save: {
label: 'Save',
icon: 'icon-ok',
"class": 'btn-success',
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formSave()',
ngDisabled: true
},
reset: {
ngClick: 'formReset()',
label: 'Reset',
icon: 'icon-undo',
'class': 'btn btn-default',
ngDisabled: true //Disabled when $pristine
}
},
ngDisabled: true
}
},
statusFields: {
status: {
//label: 'Job Status',
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
},
},
created: {
label: 'Created On',
type: 'text',
readonly: true
},
},
result_stdout: {
label: 'Standard Out',
label: 'Standard Out',
type: 'textarea',
readonly: true,
xtraWide: true,
rows: "\{\{ stdout_rows \}\}",
rows: "{{ stdout_rows }}",
"class": 'nowrap mono-space',
ngShow: "result_stdout != ''"
},
},
result_traceback: {
label: 'Traceback',
type: 'textarea',
xtraWide: true,
readonly: true,
rows: "\{\{ traceback_rows \}\}",
rows: "{{ traceback_rows }}",
"class": 'nowrap mono-space',
ngShow: "result_traceback != ''"
}
},
}
},
statusActions: {
refresh: {
refresh: {
dataPlacement: 'top',
icon: "icon-refresh",
iconSize: 'large',
@ -340,8 +340,7 @@ angular.module('JobFormDefinition', [])
'class': 'btn-xs btn-primary',
awToolTip: "Refresh the page",
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
* Form definition for Organization model
*
*
*
*/
angular.module('OrganizationFormDefinition', [])
.value(
'OrganizationForm', {
addTitle: 'Create Organization', //Title in add mode
editTitle: '{{ name }}', //Title in edit mode
name: 'organization', //entity or model name in singular form
well: true, //Wrap the form with TB well/
.value('OrganizationForm', {
addTitle: 'Create Organization', //Title in add mode
editTitle: '{{ name }}', //Title in edit mode
name: 'organization', //entity or model name in singular form
well: true,
actions: {
stream: {
@ -24,8 +23,8 @@ angular.module('OrganizationFormDefinition', [])
icon: "icon-comments-alt",
mode: 'edit',
iconSize: 'large'
}
},
}
},
fields: {
name: {
@ -34,120 +33,118 @@ angular.module('OrganizationFormDefinition', [])
addRequired: true,
editRequired: true,
capitalize: false
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
}
},
}
},
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
},
ngDisabled: true //Disabled when $pristine
}
},
related: { //related colletions (and maybe items?)
related: {
users: {
users: {
type: 'collection',
title: 'Users',
iterator: 'user',
open: false,
actions: {
actions: {
add: {
ngClick: "add('users')",
label: 'Add',
icon: 'icon-plus',
awToolTip: 'Add a new user'
}
},
}
},
fields: {
username: {
key: true,
label: 'Username'
},
},
first_name: {
label: 'First Name'
},
},
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',
title: 'Administrators',
iterator: 'admin', // Singular form of name (e.g. thing)
open: false, // Open accordion on load?
iterator: 'admin', // Singular form of name (e.g. thing)
open: false, // Open accordion on load?
base: '/users',
actions: { // Actions displayed top right of list
actions: { // Actions displayed top right of list
add: {
ngClick: "add('admins')",
icon: 'icon-plus',
label: 'Add',
awToolTip: 'Add new administrator'
}
},
}
},
fields: {
username: {
key: true,
label: 'Username'
},
},
first_name: {
label: 'First Name'
},
},
last_name: {
label: 'Last Name'
}
},
fieldActions: { // Actions available on each row
}
},
fieldActions: { // Actions available on each row
edit: {
label: 'Edit',
ngClick: "edit('users', \{\{ admin.id \}\}, '\{\{ admin.username \}\}')",
ngClick: "edit('users', admin.id, admin.username)",
icon: 'icon-edit',
awToolTip: 'Edit administrator',
'class': 'btn-default'
},
},
"delete": {
label: 'Delete',
ngClick: "delete('admins', \{\{ admin.id \}\}, '\{\{ admin.username \}\}', 'administrators')",
ngClick: "delete('admins', admin.id, admin.username, 'administrators')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Remove administrator'
}
}
}
}
}); //OrganizationForm
}
}); //OrganizationForm

View File

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

View File

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

View File

@ -5,16 +5,15 @@
*
* Form definition for Projects model
*
*
*
*/
angular.module('ProjectFormDefinition', [])
.value(
'ProjectsForm', {
addTitle: 'Create Project', // Title in add mode
editTitle: '{{ name }}', // Title in edit mode
name: 'project', // entity or model name in singular form
well: true, // Wrap the form with TB well
.value('ProjectsForm', {
addTitle: 'Create Project', // Title in add mode
editTitle: '{{ name }}', // Title in edit mode
name: 'project', // entity or model name in singular form
well: true, // Wrap the form with TB well
forceListeners: true,
actions: {
@ -26,8 +25,8 @@ angular.module('ProjectFormDefinition', [])
icon: "icon-comments-alt",
mode: 'edit',
iconSize: 'large'
}
},
}
},
fields: {
name: {
@ -36,13 +35,13 @@ angular.module('ProjectFormDefinition', [])
addRequired: true,
editRequired: true,
capitalize: false
},
description: {
},
description: {
label: 'Description',
type: 'text',
addRequired: false,
editRequired: false
},
},
organization: {
label: 'Organization',
type: 'lookup',
@ -52,35 +51,38 @@ angular.module('ProjectFormDefinition', [])
editRequired: false,
excludeMode: 'edit',
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 ' +
'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 ' +
'access to modify the project.',
'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 ' +
'access to modify the project.',
dataTitle: 'Organization',
dataContainer: 'body',
dataPlacement: 'right'
},
},
scm_type: {
label: 'SCM Type',
type: 'select',
ngOptions: 'type.label for type in scm_type_options',
ngChange: 'scmChange()',
addRequired: true,
addRequired: true,
editRequired: true
},
},
missing_path_alert: {
type: 'alertblock',
"class": 'alert-info',
type: 'alertblock',
"class": 'alert-info',
ngShow: "showMissingPlaybooksAlert && scm_type.value == ''",
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 ' +
'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 ' +
'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>',
closeable: false
},
},
base_dir: {
label: 'Project Base Path',
type: 'textarea',
@ -88,61 +90,69 @@ angular.module('ProjectFormDefinition', [])
showonly: true,
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. ' +
'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>',
'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>',
dataTitle: 'Project Base Path',
dataContainer: 'body',
dataPlacement: 'right'
},
},
local_path: {
label: 'Playbook Directory',
type: 'select',
id: 'local-path-select',
ngOptions: 'path.label for path in project_local_paths',
awRequiredWhen: { variable: "pathRequired", init: false },
awRequiredWhen: {
variable: "pathRequired",
init: false
},
ngShow: "scm_type.value == '' && !showMissingPlaybooksAlert",
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>' +
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</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>',
dataTitle: 'Project Path',
dataContainer: 'body',
dataPlacement: 'right'
},
},
scm_url: {
label: 'SCM URL',
type: 'text',
ngShow: "scm_type && scm_type.value !== ''",
awRequiredWhen: { variable: "scmRequired", init: false },
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'" }
]
awRequiredWhen: {
variable: "scmRequired",
init: false
},
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: {
labelBind: "scmBranchLabel",
type: 'text',
ngShow: "scm_type && scm_type.value !== ''",
addRequired: false,
editRequired: false
},
},
credential: {
label: 'SCM Credential',
type: 'lookup',
@ -150,111 +160,105 @@ angular.module('ProjectFormDefinition', [])
sourceModel: 'credential',
sourceField: 'name',
ngClick: 'lookUpCredential()',
addRequired: false,
addRequired: false,
editRequired: false
},
},
checkbox_group: {
label: 'SCM Update Options',
type: 'checkbox_group',
ngShow: "scm_type && scm_type.value !== ''",
fields: [
{
name: 'scm_clean',
label: 'Clean',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Remove any local modifications prior to performing an update.</p>',
dataTitle: 'SCM Clean',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
},
{
name: 'scm_delete_on_update',
label: 'Delete on Update',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
'repository this may significantly increase the amount of time required to complete an update.</p>',
dataTitle: 'SCM Delete',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
},
{
name: 'scm_update_on_launch',
label: 'Update on Launch',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>',
dataTitle: 'SCM Update',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}
]
}
},
fields: [{
name: 'scm_clean',
label: 'Clean',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Remove any local modifications prior to performing an update.</p>',
dataTitle: 'SCM Clean',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}, {
name: 'scm_delete_on_update',
label: 'Delete on Update',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
'repository this may significantly increase the amount of time required to complete an update.</p>',
dataTitle: 'SCM Delete',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}, {
name: 'scm_update_on_launch',
label: 'Update on Launch',
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>',
dataTitle: 'SCM Update',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options'
}]
}
},
buttons: { //for now always generates <button> tags
save: {
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
buttons: {
save: {
ngClick: 'formSave()',
ngDisabled: true
},
reset: {
ngClick: 'formReset()',
ngDisabled: true //Disabled when $pristine
}
},
ngDisabled: true
}
},
related: { //related colletions (and maybe items?)
related: {
organizations: {
type: 'collection',
title: 'Organizations',
iterator: 'organization',
open: false,
actions: {
actions: {
add: {
ngClick: "add('organizations')",
icon: 'icon-plus',
label: 'Add',
awToolTip: 'Add an organization'
}
},
}
},
fields: {
name: {
key: true,
label: 'Name'
},
},
description: {
label: 'Description'
}
},
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "edit('organizations', \{\{ organization.id \}\}, '\{\{ organization.name \}\}')",
ngClick: "edit('organizations', organization.id, organization.name)",
icon: 'icon-edit',
awToolTip: 'Edit the organization',
'class': 'btn btn-default'
},
},
"delete": {
label: 'Delete',
ngClick: "delete('organizations', \{\{ organization.id \}\}, '\{\{ organization.name \}\}', 'organizations')",
ngClick: "delete('organizations', organization.id, organization.name, 'organizations')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Delete the organization'
}
}
}
}
}
}); // Form
}); // Form

View File

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

View File

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

View File

@ -1,65 +1,86 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventoryGroups.js
* InventoryGroups.js
*
* Help object for inventory groups/hosts page.
*
* @dict
*/
'use strict';
angular.module('InventoryGroupsHelpDefinition', [])
.value(
'InventoryGroupsHelp', {
.value('InventoryGroupsHelp', {
story: {
hdr: 'Building your inventory',
width: 510,
height: 560,
steps: [
{
steps: [{
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.",
autoOffNotice: true
},
{
}, {
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. ' +
'For more on inventory variables, see <a href=\"http://docs.ansible.com/intro_inventory.html\" target="_blank"> ' +
'docs.ansible.com/intro_inventory.html</a>'
},
{
'For more on inventory variables, see <a href=\"http://docs.ansible.com/intro_inventory.html\" target="_blank"> ' +
'docs.ansible.com/intro_inventory.html</a>'
}, {
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 " +
"credentials for the provider, you will need to do that first on the <a href=\"/#/credentials\" " +
"target=\"_blank\">Credentials</a> tab."
},
{
"credentials for the provider, you will need to do that first on the <a href=\"/#/credentials\" " +
"target=\"_blank\">Credentials</a> tab."
}, {
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>."
},
{
}, {
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 " +
"will be added to the selected group.</div>"
},
{
"will be added to the selected group.</div>"
}, {
intro: 'Copy or move groups:',
img: { src: 'groups006.png', 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>"
img: {
src: 'groups006.png',
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:',
img: { src: 'groups007.png', maxWidth: 466, 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>"
}
]
}
});
img: {
src: 'groups007.png',
maxWidth: 466,
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
*
* Routines for checking user access and license state
*
*
*/
angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
.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;
'use strict';
if (me.is_superuser) {
scope.PermissionAddAllowed = true;
}
else {
if (me.related.admin_of_organizations) {
Rest.setUrl(me.related.admin_of_organizations);
Rest.get()
.success( function(data, status, headers, config) {
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>';
angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
.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,
scope = params.scope;
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);
}
if (me.is_superuser) {
scope.PermissionAddAllowed = true;
} else {
if (me.related.admin_of_organizations) {
Rest.setUrl(me.related.admin_of_organizations);
Rest.get()
.success(function (data) {
if (data.results.length > 0) {
scope.PermissionAddAllowed = true;
} else {
scope.PermissionAddAllowed = false;
}
})
.error(function (data, status) {
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',
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
*
* 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.
* 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) {
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);
}
}
}
}
'use strict';
// Scan the array list and find the clicked element
var clicked;
var found = false;
for (var i = 0; i < set.length && found == false; i++){
if (set[i].id == id) {
clicked = i;
found = true;
angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
.factory('ToggleChildren', [ function () {
return function (params) {
var scope = params.scope,
list = params.list,
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
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);
}
function collapse(node) {
var i;
set[node].ngicon = 'fa fa-plus-square-o node-toggle';
for (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
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'])
.factory('KindChange', [ 'Empty', function(Empty) {
return function(params) {
var scope = params.scope;
var form = params.form;
var reset = params.reset;
'use strict';
// 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) {
angular.module('CredentialsHelper', ['Utilities'])
.factory('KindChange', ['Empty',
function (Empty) {
return function (params) {
var scope = params.scope,
reset = params.reset,
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':
scope['aws_required'] = true;
break;
case 'rax':
scope['rackspace_required'] = true;
scope.aws_required = true;
break;
case 'rax':
scope.rackspace_required = true;
break;
case 'ssh':
scope['usernameLabel'] = 'SSH Username';
break;
case 'scm':
scope['sshKeyDataLabel'] = 'SCM Private Key';
scope.usernameLabel = 'SSH Username';
break;
case 'scm':
scope.sshKeyDataLabel = 'SCM Private Key';
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']) {
case 'ssh':
data['password'] = scope['ssh_password'];
break;
// Collapse or open help widget based on whether scm value is selected
collapse = $('#credential_kind').parent().find('.panel-collapse').first();
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,
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':
data['username'] = scope['access_key'];
data['password'] = scope['secret_key'];
data.username = scope.access_key;
data.password = scope.secret_key;
break;
case 'rax':
data['password'] = scope['api_key'];
data.password = scope.api_key;
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
*
*/
angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefinition'])
.factory('EventView', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'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 !== ''"
}
}
};
'use strict';
var event_id = params.event_id;
var generator = GenerateForm;
var scope;
var defaultUrl = GetBasePath('base') + 'job_events/' + event_id + '/';
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
Rest.get()
.success( function(data, status, headers, config) {
// If event_data is not available, remove fields that depend on it
if ($.isEmptyObject(data['event_data']) || !data['event_data']['res'] || typeof data['event_data']['res'] == 'string') {
for (var fld in form.fields) {
switch(fld) {
case 'start':
case 'end':
case 'delta':
case 'msg':
case 'stdout':
case 'stderr':
case 'msg':
case 'results':
case 'module_name':
case 'module_args':
case 'rc':
delete form.fields[fld];
break;
}
}
}
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 (var fld in form.fields) {
switch (fld) {
angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefinition', 'JobEventsFormDefinition'])
.factory('EventView', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'JobEventDataForm', 'Empty', 'JobEventsForm',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
FormatDate, JobEventDataForm, Empty, JobEventsForm) {
return function (params) {
var event_id = params.event_id,
generator = GenerateForm,
form = angular.copy(JobEventsForm),
scope,
defaultUrl = GetBasePath('base') + 'job_events/' + event_id + '/';
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
Rest.get()
.success(function (data) {
var i, n, fld, rows, txt, cDate;
// If event_data is not available, remove fields that depend on it
if ($.isEmptyObject(data.event_data) || !data.event_data.res || typeof data.event_data.res === 'string') {
for (fld in form.fields) {
switch (fld) {
case 'start':
case 'end':
case 'delta':
case 'msg':
case 'stdout':
case 'stderr':
case 'msg':
case 'results':
case 'module_name':
case 'module_args':
case 'rc':
delete form.fields[fld];
break;
}
}
}
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 'end':
case 'delta':
@ -202,114 +65,118 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefini
case 'stderr':
case 'msg':
case 'rc':
if (data['event_data'] && data['event_data']['res'] && Empty(data['event_data']['res'][fld])) {
delete form.fields[fld];
}
else {
if (form.fields[fld].type == 'textarea') {
var n = data['event_data']['res'][fld].match(/\n/g);
var rows = (n) ? n.length : 1;
rows = (rows > 10) ? 10 : rows;
rows = (rows < 3) ? 3 : rows;
form.fields[fld].rows = rows;
}
if (data.event_data && data.event_data.res && Empty(data.event_data.res[fld])) {
delete form.fields[fld];
} else {
if (form.fields[fld].type === 'textarea') {
n = data.event_data.res[fld].match(/\n/g);
rows = (n) ? n.length : 1;
rows = (rows > 10) ? 10 : rows;
rows = (rows < 3) ? 3 : rows;
form.fields[fld].rows = rows;
}
}
break;
case 'results':
if ( data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] == undefined) {
// not defined
delete form.fields[fld];
}
else if (!Array.isArray(data['event_data']['res'][fld]) || data['event_data']['res'][fld].length == 0) {
// defined, but empty
delete form.fields[fld];
}
else {
// defined and not empty, so attempt to size the textarea field
var txt = '';
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
delete form.fields[fld];
}
else {
var n = txt.match(/\n/g);
var rows = (n) ? n.length : 1;
rows = (rows > 10) ? 10 : rows;
rows = (rows < 3) ? 3 : rows;
form.fields[fld].rows = rows;
}
if (data.event_data && data.event_data.res && data.event_data.res[fld] === undefined) {
// not defined
delete form.fields[fld];
} else if (!Array.isArray(data.event_data.res[fld]) || data.event_data.res[fld].length === 0) {
// defined, but empty
delete form.fields[fld];
} else {
// defined and not empty, so attempt to size the textarea field
txt = '';
for (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
delete form.fields[fld];
} else {
n = txt.match(/\n/g);
rows = (n) ? n.length : 1;
rows = (rows > 10) ? 10 : rows;
rows = (rows < 3) ? 3 : rows;
form.fields[fld].rows = rows;
}
}
break;
case 'module_name':
case 'module_args':
if (data['event_data'] && data['event_data']['res']) {
if (data['event_data']['res']['invocation'] === undefined ||
data['event_data']['res']['invocation'][fld] === undefined) {
if (data.event_data && data.event_data.res) {
if (data.event_data.res.invocation === undefined ||
data.event_data.res.invocation[fld] === undefined) {
delete form.fields[fld];
}
}
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') {
scope['traceback'] = data['event_data']['res'];
}
for (var fld in form.fields) {
switch(fld) {
// 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,
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':
if (data['failed']) {
scope['status'] = 'error';
}
else if (data['changed']) {
scope['status'] = 'changed';
}
else {
scope['status'] = 'success';
if (data.failed) {
scope.status = 'error';
} else if (data.changed) {
scope.status = 'changed';
} else {
scope.status = 'success';
}
break;
case 'created':
var cDate = new Date(data['created']);
scope['created'] = FormatDate(cDate);
cDate = new Date(data.created);
scope.created = FormatDate(cDate);
break;
case 'host':
if (data['summary_fields'] && data['summary_fields']['host']) {
scope['host'] = data['summary_fields']['host']['name'];
if (data.summary_fields && data.summary_fields.host) {
scope.host = data.summary_fields.host.name;
}
break;
case 'id':
@ -319,50 +186,50 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefini
break;
case 'start':
case 'end':
if (data['event_data'] && data['event_data']['res'] && !Empty(data['event_data']['res'][fld])) {
scope[fld] = 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];
}
break;
case 'results':
if (Array.isArray(data['event_data']['res'][fld]) && data['event_data']['res'][fld].length > 0 ) {
var txt = '';
for (var i=0; i < data['event_data']['res'][fld].length; i++) {
txt += data['event_data']['res'][fld][i];
}
if (txt !== '') {
scope[fld] = txt;
}
case 'results':
if (Array.isArray(data.event_data.res[fld]) && data.event_data.res[fld].length > 0) {
txt = '';
for (i = 0; i < data.event_data.res[fld].length; i++) {
txt += data.event_data.res[fld][i];
}
if (txt !== '') {
scope[fld] = txt;
}
}
break;
break;
case 'msg':
case 'stdout':
case 'stderr':
case 'delta':
case 'rc':
if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) {
scope[fld] = data['event_data']['res'][fld];
if (data.event_data && data.event_data.res && data.event_data.res[fld] !== undefined) {
scope[fld] = data.event_data.res[fld];
}
break;
case 'module_name':
case 'module_args':
if (data['event_data']['res'] && data['event_data']['res']['invocation']) {
scope[fld] = data['event_data']['res']['invocation'][fld];
if (data.event_data.res && data.event_data.res.invocation) {
scope[fld] = data.event_data.res.invocation[fld];
}
break;
}
}
if (!scope.$$phase) {
scope.$digest();
}
}
if (!scope.$$phase) {
scope.$digest();
}
})
.error( function(data, status, headers, config) {
$('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve event: ' + event_id + '. GET status: ' + status });
.error(function (data, status) {
$('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
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 */
'use strict';
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',

View File

@ -2,479 +2,463 @@
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* JobSubmission.js
*
*
*/
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper' ])
.factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors',
'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;
'use strict';
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);
}
}
angular.module('JobSubmissionHelper', ['RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper'
])
function cancel() {
// Delete a job
var url = GetBasePath('jobs') + scope.job_id +'/'
Rest.setUrl(url);
Rest.destroy()
.success ( function(data, status, headers, config) {
if (form.name == 'credential') {
navigate(true);
.factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors',
'GetBasePath', 'Alert', 'Empty', 'Wait',
function (CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, Wait) {
return function (params) {
var scope = params.scope,
passwords = params.passwords,
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) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
}
scope.cancelJob = function() {
// User clicked cancel button
$('#password-modal').modal('hide');
if (form.name == 'credential') {
cancel();
}
else {
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;
scope.cancelJob = function () {
// User clicked cancel button
$('#password-modal').modal('hide');
if (form.name === 'credential') {
cancel();
} else {
scope.$emit('UpdateSubmitted', 'canceled');
}
});
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) {
// Create the job record
if (scope.credentialWatchRemove) {
scope.credentialWatchRemove();
scope.startJob = function () {
var pswd = {}, value_supplied = false;
$('#password-modal').modal('hide');
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);
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, status, headers, config) {
};
}
])
.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,
id = params.id,
template_name = (params.template) ? params.template : null,
base = $location.path().replace(/^\//, '').split('/')[0],
url = GetBasePath(base) + id + '/';
function postJob(data) {
var dt, url, name;
// Create the job record
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;
if (data.passwords_needed_to_start.length > 0) {
// Passwords needed. Prompt for passwords, then start job.
PromptPasswords({
scope: scope,
passwords: data.passwords_needed_to_start,
start_url: data.related.start,
form: CredentialForm
});
// Passwords needed. Prompt for passwords, then start job.
PromptPasswords({
scope: scope,
passwords: data.passwords_needed_to_start,
start_url: data.related.start,
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 {
// 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) {
}).error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to create job. POST returned status: ' + status });
});
};
// Get the job or job_template record
Wait('start');
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
// Create a job record
scope.credential = '';
if (data.credential == '' || data.credential == null) {
// Template does not have credential, prompt for one
Wait('stop');
if (scope.credentialWatchRemove) {
scope.credentialWatchRemove();
}
scope.credentialWatchRemove = scope.$watch('credential', function(newVal, oldVal) {
if (newVal !== oldVal) {
// After user selects a credential from the modal,
// submit the job
if (scope.credential != '' && scope.credential !== null && scope.credential !== undefined) {
data.credential = scope.credential;
postJob(data);
}
}
});
LookUpInit({
scope: scope,
form: JobTemplateForm,
current_item: null,
list: CredentialList,
field: 'credential',
hdr: 'Credential Required'
});
scope.lookUpCredential();
}
else {
// We have what we need, submit the job
postJob(data);
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get job template details. GET returned status: ' + status });
});
};
}])
// Sumbit SCM Update request
.factory('ProjectUpdate',['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'ProjectsForm', 'Wait',
function(PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, ProjectsForm, Wait) {
return function(params) {
var scope = params.scope;
var project_id = params.project_id;
var url = GetBasePath('projects') + project_id + '/update/';
if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted();
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
// 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) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to create job. POST returned status: ' + status });
});
}
// Get the job or job_template record
Wait('start');
Rest.setUrl(url);
Rest.get()
.success(function (data) {
// Create a job record
scope.credential = '';
if (data.credential === '' || data.credential === null) {
// Template does not have credential, prompt for one
Wait('stop');
if (scope.credentialWatchRemove) {
scope.credentialWatchRemove();
}
scope.credentialWatchRemove = scope.$watch('credential', function (newVal, oldVal) {
if (newVal !== oldVal) {
// After user selects a credential from the modal,
// submit the job
if (scope.credential !== '' && scope.credential !== null && scope.credential !== undefined) {
data.credential = scope.credential;
postJob(data);
}
}
});
LookUpInit({
scope: scope,
form: JobTemplateForm,
current_item: null,
list: CredentialList,
field: 'credential',
hdr: 'Credential Required'
});
scope.lookUpCredential();
} else {
// We have what we need, submit the job
postJob(data);
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get job template details. GET returned status: ' + status });
});
};
}
])
// Sumbit SCM Update request
.factory('ProjectUpdate', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'ProjectsForm', 'Wait',
function (PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, ProjectsForm, Wait) {
return function (params) {
var scope = params.scope,
project_id = params.project_id,
url = GetBasePath('projects') + project_id + '/update/';
if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted();
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function () {
// Refresh the project list after update request submitted
Wait('stop');
if (data.can_update) {
var extra_html = '';
for (var 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, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get project update details: ' + url + ' GET status: ' + status });
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) {
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
.factory('InventoryUpdate',['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'GroupForm', 'BuildTree', 'Wait',
function(PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, GroupForm, BuildTree, Wait) {
return function(params) {
var scope = params.scope;
var inventory_id = params.inventory_id;
var url = params.url;
var group_name = params.group_name;
var group_source = params.group_source;
var group_id = params.group_id;
var tree_id = params.tree_id;
// Sumbit Inventory Update request
.factory('InventoryUpdate', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert',
'GroupForm', 'BuildTree', 'Wait',
function (PromptPasswords, $compile, Rest, $location, GetBasePath, ProcessErrors, Alert, GroupForm, BuildTree, Wait) {
return function (params) {
var scope = params.scope,
url = params.url,
group_id = params.group_id,
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) {
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, 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) {
scope.removeHostReloadComplete = scope.$on('HostReloadComplete', function () {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory_source details. ' + url + 'GET status: ' + status });
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) {
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
*
* Routines shared by job related controllers
*
*
*/
'use strict';
angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper'])
.factory('JobStatusToolTip', [ function() {
return function(status) {
var toolTip;
switch (status) {
.factory('JobStatusToolTip', [
function () {
return function (status) {
var toolTip;
switch (status) {
case 'successful':
case 'success':
toolTip = 'There were no failed tasks.';
@ -35,118 +38,124 @@ angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinitio
case 'running':
toolTip = 'Playbook tasks executing.';
break;
}
return toolTip;
}
return toolTip;
};
}])
}
])
.factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary',
.factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary',
'WatchInventoryWindowResize',
function(Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary, WatchInventoryWindowResize) {
return function(params) {
// Display status info in a modal dialog- called from inventory edit page
var job_id = params.job_id;
function (Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary, WatchInventoryWindowResize) {
return function (params) {
// Display status info in a modal dialog- called from inventory edit page
var generator = GenerateForm;
var form = JobSummary;
// Using jquery dialog for its expandable property
var html = '<div id=\"status-modal-dialog\" title=\"Job ' + job_id + '\">' +
'<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n';
$('#inventory-modal-container').empty().append(html);
var scope = generator.inject(form, { mode: 'edit', id: 'form-container', breadCrumbs: false, related: false });
// Set modal dimensions based on viewport width
var ww = $(document).width();
var wh = $('body').height();
var x, y, maxrows;
if (ww > 1199) {
// desktop
x = 675;
y = (750 > wh) ? wh - 20 : 750;
maxrows = 20;
}
else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (620 > wh) ? wh - 15 : 620;
maxrows = 15;
}
else {
x = (ww - 20);
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;
var job_id = params.job_id,
generator = GenerateForm,
form = JobSummary,
scope, ww, wh, x, y, maxrows, url, html;
html = '<div id=\"status-modal-dialog\" title=\"Job ' + job_id + '\">' +
'<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n';
$('#inventory-modal-container').empty().append(html);
scope = generator.inject(form, { mode: 'edit', id: 'form-container', breadCrumbs: false, related: false });
// Set modal dimensions based on viewport width
ww = $(document).width();
wh = $('body').height();
if (ww > 1199) {
// desktop
x = 675;
y = (750 > wh) ? wh - 20 : 750;
maxrows = 20;
} else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (620 > wh) ? wh - 15 : 620;
maxrows = 15;
} else {
x = (ww - 20);
y = (500 > wh) ? wh : 500;
maxrows = 10;
}
Wait('start');
var url = GetBasePath('jobs') + job_id + '/';
Rest.setUrl(url);
Rest.get()
.success( function(data) {
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);
var cDate = new Date(data.created);
scope.created = FormatDate(cDate);
$('#status-modal-dialog').dialog('open');
// 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"]'),
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) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Attempt to load job failed. GET returned status: ' + status });
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Attempt to load job failed. GET returned status: ' + status });
});
};
}]);
}
]);

View File

@ -3,7 +3,7 @@
*
* LookupHelper
* Build a lookup dialog
*
*
* LookUpInit( {
* scope: <form scope>,
* 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',
function(Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) {
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
function (Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) {
return function (params) {
var defaultUrl;
if (params.url) {
// pass in a url value to override the default
defaultUrl = params.url;
}
else {
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 scope = params.scope,
form = params.form,
list = params.list,
field = params.field,
postAction = params.postAction,
defaultUrl, name, hdr, watchUrl;
var watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&';
watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value';
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source',field);
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();
}
}
if (params.url) {
// pass in a url value to override the default
defaultUrl = params.url;
} else {
defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name);
}
listScope['toggle_' + list.iterator] = function(id) {
for (var 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() {
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;
// Show pop-up
name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);
hdr = (params.hdr) ? params.hdr : 'Select ' + name;
watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&';
watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value';
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);
scope['lookUp' + name] = function () {
var listGenerator = GenerateList,
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, 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();
}
}
if (!Empty(scope[field])) {
listScope['toggle_' + list.iterator](scope[field]);
}
listScope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () {
var fld, i;
for (fld in list.fields) {
if (list.fields[fld].type && list.fields[fld].type === 'date') {
//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) {
return function(params) {
var scope = params.scope;
var count = params.count;
var next = params.next;
var previous = params.previous;
var iterator = params.iterator;
angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelatedHelper'])
scope[iterator + '_page'] = 1;
scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size']));
scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages'];
scope[iterator + '_total_rows'] = count;
.factory('PageRangeSetup', ['Empty',
function (Empty) {
return function (params) {
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;
}
else if ( next && previous ) {
// we're in between next and previous
scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/,'')) + 1;
}
scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size']));
scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages'];
scope[iterator + '_total_rows'] = count;
// Calc the range of up to 10 pages to show
scope[iterator + '_page_range'] = new Array();
var first = (scope[iterator + '_page'] > 5) ? scope[iterator + '_page'] - 5 : 1;
if (scope[iterator + '_page'] < 6) {
var last = (10 <= scope[iterator + '_num_pages']) ? 10 : scope[iterator + '_num_pages'];
}
else {
var last = (scope[iterator + '_page'] + 4 < scope[iterator + '_num_pages']) ?
scope[iterator + '_page'] + 4 : scope[iterator + '_num_pages'];
}
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 });
// 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;
} else if (next && previous) {
// we're in between next and previous
scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/, '')) + 1;
}
scope.pageIsActive = function(page, iterator) {
return (page == scope[iterator + '_page']) ? 'active' : '';
// Calc the range of up to 10 pages to show
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;
var url = scope[iterator + '_url'];
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');
Refresh({ scope: scope, set: set, iterator: iterator, url: new_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 });
}
scope.pageIsActive = function (page, iterator) {
return (page === scope[iterator + '_page']) ? 'active' : '';
};
}
}])
.factory('PaginateInit', [ 'Refresh', '$cookieStore', 'Wait',
function(Refresh, $cookieStore, Wait) {
return function(params) {
var scope = params.scope;
var list = params.list;
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 });
}
}
}]);
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;
var new_url = scope[iterator + '_url'].replace(/\?page_size\=\d+/, ''),
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
*
* Routines for parsing variable data and toggling
* Routines for parsing variable data and toggling
* between JSON and YAML.
*
*/
angular.module('ParseHelper', [])
.factory('ParseTypeChange', [function() {
return function(scope, varName, parseTypeName) {
'use strict';
// Toggle displayed variable string between JSON and YAML
angular.module('ParseHelper', [])
.factory('ParseTypeChange', [
function () {
return function (scope, varName, parseTypeName) {
var fld = (varName) ? varName : 'variables';
var pfld = (parseTypeName) ? parseTypeName : 'parseType';
scope.blockParseTypeWatch = false;
scope.blockVariableDataWatch = false;
// Toggle displayed variable string between JSON and YAML
if (scope['remove' + fld + 'Watch']) {
scope['remove' + fld + 'Watch']();
var fld = (varName) ? varName : 'variables',
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
*
*/
angular.module('PermissionsHelper', [])
// Handle category change event
.factory('PermissionCategoryChange', [ function() {
return function(params) {
var scope = params.scope;
var reset = params.reset;
angular.module('PermissionsHelper', [])
if (scope.category == 'Inventory') {
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";
}
// Handle category change event
.factory('PermissionCategoryChange', [
function () {
return function (params) {
var scope = params.scope,
reset = params.reset;
if (reset) {
scope.permission_type = (scope.category == 'Inventory') ? 'read' : 'run'; //default to the first option
}
}
}]);
if (scope.category === 'Inventory') {
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
*
* 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
* 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'])
.factory('GetProjectPath', ['Alert', 'Rest', 'GetBasePath','ProcessErrors',
function(Alert, Rest, GetBasePath, ProcessErrors) {
return function(params) {
var scope = params.scope;
var master = params.master;
function arraySort(data) {
//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]);
var scope = params.scope,
master = params.master;
function arraySort(data) {
//Sort nodes by name
var i, j, names = [],
newData = [];
for (i = 0; i < data.length; i++) {
names.push(data[i].value);
}
}
}
return newData;
}
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;
}
names.sort();
for (j = 0; j < names.length; j++) {
for (i = 0; i < data.length; i++) {
if (data[i].value === names[j]) {
newData.push(data[i]);
}
}
}
return newData;
}
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, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to access API config. GET status: ' + status });
});
scope.showMissingPlaybooksAlert = false;
Rest.setUrl(GetBasePath('config'));
Rest.get()
.success(function (data) {
var opts = [], i;
for (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 (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
*
* 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
* scope.base_dir (readonly field).
* scope.base_dir (readonly field).
*
*/
'use strict';
angular.module('ProjectsHelper', ['RestServices', 'Utilities', 'ProjectStatusDefinition', 'ProjectFormDefinition'])
.factory('ProjectStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ProjectStatusForm', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
FormatDate, ProjectStatusForm, Wait) {
return function(params) {
.factory('ProjectStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ProjectStatusForm', 'Wait',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
FormatDate, ProjectStatusForm, Wait) {
return function (params) {
var project_id = params.project_id;
var last_update = params.last_update;
var generator = GenerateForm;
var form = ProjectStatusForm;
Wait('start');
var project_id = params.project_id,
last_update = params.last_update,
generator = GenerateForm,
form = ProjectStatusForm,
html, scope, ww, wh, x, y, maxrows;
// Using jquery dialog for its expandable property
var html = "<div id=\"status-modal-dialog\"><div id=\"form-container\" style=\"width: 100%;\"></div></div>\n";
$('#projects-modal-container').empty().append(html);
Wait('start');
var scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false, breadCrumbs: false });
generator.reset();
// Using jquery dialog for its expandable property
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
var ww = $(document).width();
var wh = $('body').height();
var x, y, maxrows;
if (ww > 1199) {
// desktop
x = 675;
y = (750 > wh) ? wh - 20 : 750;
maxrows = 20;
}
else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (620 > wh) ? wh - 15 : 620;
maxrows = 15;
}
else {
x = (ww - 20);
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 (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' });
scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false, breadCrumbs: false });
generator.reset();
// Set modal dimensions based on viewport width
ww = $(document).width();
wh = $('body').height();
if (ww > 1199) {
// desktop
x = 675;
y = (750 > wh) ? wh - 20 : 750;
maxrows = 20;
} else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (620 > wh) ? wh - 15 : 620;
maxrows = 15;
} else {
x = (ww - 20);
y = (500 > wh) ? wh : 500;
maxrows = 10;
}
// Create the modal
$('#status-modal-dialog').dialog({
buttons: {
"OK": function () {
$(this).dialog("close");
}
},
resizeStop: function(e, ui) {
// 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 );
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'
});
},
close: function(e, ui) {
// Destroy on close
// Destroy on close
$('.tooltip').each( function(index) {
// Remove any lingering tooltip <div> elements
$(this).remove();
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
// Destroy on close
$('.tooltip').each(function () {
// Remove any lingering tooltip <div> elements
$(this).remove();
});
$('.popover').each(function(index) {
// remove lingering popover <div> elements
$(this).remove();
$('.popover').each(function () {
// remove lingering popover <div> elements
$(this).remove();
});
$('#status-modal-dialog').dialog('destroy');
$('#projects-modal-container').empty();
$('#status-modal-dialog').dialog('destroy');
$('#projects-modal-container').empty();
},
open: function(e, ui) {
Wait('stop');
open: function () {
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
* Used in list controllers where the list might also be used as a selection list.
*
*
* SelectionInit( {
* scope: <list scope>,
* list: <list object>
* })
*/
'use strict';
angular.module('SelectionHelper', ['Utilities', 'RestServices'])
.factory('SelectionInit', [ 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait',
function(Rest, Alert, ProcessErrors, ReturnToCaller, Wait) {
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;
.factory('SelectionInit', ['Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait',
function (Rest, Alert, ProcessErrors, ReturnToCaller, Wait) {
return function (params) {
if (params.selected !== undefined) {
var selected = params.selected;
}
else {
var selected = []; //array of selected row IDs
}
var scope = params.scope,
list = params.list,
target_url = params.url,
returnToCaller = params.returnToCaller,
selected;
scope.formModalActionDisabled = true;
scope.disableSelectBtn = true;
if (params.selected !== undefined) {
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.disableSelectBtn = true;
Wait('start');
function finished() {
selected = [];
if (returnToCaller !== undefined) {
ReturnToCaller(returnToCaller);
// toggle row selection
scope['toggle_' + list.iterator] = function (id, ischeckbox) {
var i, j, found;
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 {
$('#form-modal').modal('hide');
scope.$emit('modalClosed');
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 = [], 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 ) {
for (var j=0; j < selected.length; j++) {
Rest.post(selected[j])
.success( function(data, status, headers, config) {
queue.push({ result: 'success', data: data, status: status });
scope.$emit('callFinished');
})
.error( function(data, status, headers, config) {
queue.push({ result: 'error', data: data, status: status, headers: headers });
scope.$emit('callFinished');
});
}
}
else {
finished();
}
}
function postIt(data) {
Rest.post(data)
.success(function (data, status) {
queue.push({ result: 'success', data: data, status: status });
scope.$emit('callFinished');
})
.error(function (data, status, headers) {
queue.push({ result: 'error', data: data, status: status, headers: headers });
scope.$emit('callFinished');
});
}
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 (scope.SelectPostRefreshRemove) {
scope.SelectPostRefreshRemove();
}
scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function() {
if (scope[list.name]) {
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) {
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'] = '';
}
}
if (selected.length > 0) {
for (j = 0; j < selected.length; j++) {
postIt(selected[j]);
}
} else {
finished();
}
};
scope.formModalAction = scope.finishSelection;
// Initialize our data set after a refresh (page change or search)
if (scope.SelectPostRefreshRemove) {
scope.SelectPostRefreshRemove();
}
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.
*
* 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;
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;
}
}]);
'use strict';
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;
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
*
*
*
*/
angular.module('APIDefaults', ['RestServices', 'Utilities'])
.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;
'use strict';
function lookup(key) {
var result = {};
for (id in $rootScope.apiDefaults) {
if (id == key || id.iterator == key) {
result[id] = defaults[id];
break;
}
angular.module('APIDefaults', ['RestServices', 'Utilities'])
.factory('GetAPIDefaults', ['Alert', 'Rest', '$rootScope',
function (Alert, Rest, $rootScope) {
return function (key) {
//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
* for the menu piece. The routine for building the menu is in InventoriesEdit controller
* (controllers/Inventories.js)
*
*
*/
angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService',
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper'
])
.factory('WatchInventoryWindowResize', ['ApplyEllipsis', function(ApplyEllipsis) {
return function() {
// Call to set or restore window resize
var timeOut;
$(window).resize(function() {
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();
var level_width = $(this).find('.level').width();
var level_padding = parseInt($(this).find('.level').css('padding-left').replace(/px/,''));
var level = level_width + level_padding;
var pct = ( 100 - Math.ceil((level / td_width)*100) ) + '%';
$(this).find('.group-name').css({ width: pct });
/* globals console:false */
'use strict';
angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService',
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper'
])
.factory('WatchInventoryWindowResize', ['ApplyEllipsis',
function (ApplyEllipsis) {
return function () {
// Call to set or restore window resize
var timeOut;
$(window).resize(function () {
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('#hosts_table .host-name a');
ApplyEllipsis('#groups_table .group-name a');
ApplyEllipsis('#hosts_table .host-name a');
}, 100);
});
}
}])
});
};
}
])
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'Wait',
function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait) {
return function(params) {
// Save inventory property modifications
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'Wait',
function (InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, Wait) {
return function (params) {
var scope = params.scope;
var form = InventoryForm;
var defaultUrl=GetBasePath('inventory');
// Save inventory property modifications
Wait('start');
try {
// Make sure we have valid variable 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
}
var scope = params.scope,
form = InventoryForm,
defaultUrl = GetBasePath('inventory'),
fld, json_data, data;
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
Wait('start');
var data = {}
for (var fld in form.fields) {
if (fld != 'inventory_variables') {
if (form.fields[fld].realName) {
data[form.fields[fld].realName] = scope[fld];
}
else {
data[fld] = scope[fld];
}
}
}
try {
// Make sure we have valid variable data
if (scope.inventoryParseType === 'json') {
json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses
} else {
json_data = jsyaml.load(scope.inventory_variables); //parse yaml
}
Rest.setUrl(defaultUrl + scope['inventory_id'] + '/');
Rest.put(data)
.success( function(data, status, headers, config) {
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);
}
}
}])
// Make sure our JSON is actually an object
if (typeof json_data !== 'object') {
throw "failed to return an object!";
}
.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
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 = "---";
data = {};
for (fld in form.fields) {
if (fld !== 'inventory_variables') {
if (form.fields[fld].realName) {
data[form.fields[fld].realName] = scope[fld];
} else {
data[fld] = scope[fld];
}
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({
scope: scope,
form: form,
current_item: scope.organization,
list: OrganizationList,
field: 'organization'
});
Rest.setUrl(defaultUrl + scope.inventory_id + '/');
Rest.put(data)
.success(function (data) {
if (scope.inventory_variables) {
Rest.setUrl(data.related.variable_data);
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');
$('#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) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
.error(function (data, status) {
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
});
});
if (scope.removeInventorySaved) {
scope.removeInventorySaved();
}
scope.removeInventorySaved = scope.$on('InventorySaved', function() {
$('#form-modal').modal('hide');
// Restore prior search state
if (scope.searchCleanp) {
scope.searchCleanup();
if (scope.removeInventorySaved) {
scope.removeInventorySaved();
}
SearchInit({
scope: parent_scope,
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false
scope.removeInventorySaved = scope.$on('InventorySaved', function () {
$('#form-modal').modal('hide');
// Restore prior search state
if (scope.searchCleanp) {
scope.searchCleanup();
}
SearchInit({
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() {
// Restore prior search state
if (scope.searchCleanp) {
scope.searchCleanup();
}
SearchInit({
scope: parent_scope,
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false
scope.cancelModal = function () {
// Restore prior search state
if (scope.searchCleanp) {
scope.searchCleanup();
}
SearchInit({
scope: parent_scope,
set: PreviousSearchParams.set,
list: PreviousSearchParams.list,
url: PreviousSearchParams.defaultUrl,
iterator: PreviousSearchParams.iterator,
sort_order: PreviousSearchParams.sort_order,
setWidgets: false
});
}
};
scope.formModalAction = function() {
parent_scope.inventory_id = inventory_id;
parent_scope.inventory_name = scope.inventory_name;
SaveInventory({ scope: scope });
}
}
}]);
scope.formModalAction = function () {
parent_scope.inventory_id = inventory_id;
parent_scope.inventory_name = scope.inventory_name;
SaveInventory({
scope: scope
});
};
};
}
]);

View File

@ -8,33 +8,33 @@
*
*/
angular.module('md5Helper', ['RestServices', 'Utilities'])
.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;
'use strict';
scope.genMD5 = function(fld) {
var now = new Date();
scope[fld] = $.md5('AnsibleWorks' + now.getTime());
}
angular.module('md5Helper', ['RestServices', 'Utilities'])
.factory('md5Setup', [ function () {
return function (params) {
var scope = params.scope,
master = params.master,
check_field = params.check_field,
default_val = params.default_val;
scope.toggleCallback = function(fld) {
if (scope.allow_callbacks == 'false') {
scope[fld] = '';
}
}
scope[check_field] = default_val;
master[check_field] = default_val;
scope.selectAll = function(fld) {
$('input[name="' + fld +'"]').focus().select();
}
}
}]);
scope.genMD5 = function (fld) {
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>,
* set: <model>,
* 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'])
.factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup',
function(ProcessErrors, Rest, Wait, PageRangeSetup) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
var url = params.url;
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator });
scope[set] = data['results'];
scope[iterator + 'Loading'] = false;
scope[iterator + 'HoldInput'] = false;
Wait('stop');
scope.$emit('related' + set);
})
.error ( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
});
'use strict';
angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities', 'PaginationHelpers'])
.factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup',
function (ProcessErrors, Rest, Wait, PageRangeSetup) {
return function (params) {
var scope = params.scope,
set = params.set,
iterator = params.iterator,
url = params.url;
Rest.setUrl(url);
Rest.get()
.success(function (data) {
PageRangeSetup({
scope: scope,
count: data.count,
next: data.next,
previous: data.previous,
iterator: iterator
});
scope[set] = data.results;
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>,
* set: <model>,
* 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;
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator });
scope[iterator + 'Loading'] = false;
for (var i=1; i <= 3; i++) {
var 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, headers, config) {
scope[iterator + 'HoldInput'] = false;
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
});
'use strict';
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,
set = params.set,
iterator = params.iterator,
url = params.url;
scope.current_url = url;
Rest.setUrl(url);
Rest.get()
.success(function (data) {
var i, modifier;
PageRangeSetup({
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
*
* All the parts for controlling the search widget on
* All the parts for controlling the search widget on
* related collections.
*
* SearchInit({
* scope: <scope>,
* relatedSets: <array of related collections {model_name, url, iterator}>,
* 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',
function($timeout, Alert, Rest, RefreshRelated, Wait) {
return function(params) {
var scope = params.scope;
var relatedSets = params.relatedSets;
var form = params.form;
// Set default values
function setDefaults(inIterator) {
var iterator, f;
for (var set in form.related) {
if (form.related[set].type != 'tree' && (inIterator === undefined || inIterator == form.related[set].iterator)) {
iterator = form.related[set].iterator;
for (var fld in form.related[set].fields) {
if (form.related[set].fields[fld].key) {
scope[iterator + 'SearchField'] = fld
scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
break;
function ($timeout, Alert, Rest, RefreshRelated, Wait) {
return function (params) {
var scope = params.scope,
relatedSets = params.relatedSets,
form = params.form, f;
// Set default values
function setDefaults(inIterator) {
var iterator, f, fld, set;
for (set in form.related) {
if (form.related[set].type !== 'tree' && (inIterator === undefined || inIterator === form.related[set].iterator)) {
iterator = form.related[set].iterator;
for (fld in form.related[set].fields) {
if (form.related[set].fields[fld].key) {
scope[iterator + 'SearchField'] = fld;
scope[iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
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';
scope[iterator + 'SearchValue'] = null;
}
setDefaults();
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 + 'HideSearchType'] = false;
scope[iterator + 'InputHide'] = 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'] = list.fields[f].searchOptions;
if (f.searchType !== undefined && f.searchType === 'gtzero') {
scope[iterator + "InputHide"] = true;
scope[iterator + 'ShowStartBtn'] = false;
}
if (form.related[set].fields[f].searchType && form.related[set].fields[f].searchType == 'gtzero') {
scope[iterator + "InputHide"] = true;
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[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[iterator + "InputHide"] = true;
scope[iterator + 'ShowStartBtn'] = false;
}
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.setSearchType = function (model, type, label) {
scope[model + 'SearchTypeLabel'] = label;
scope[model + 'SearchType'] = type;
scope.search(model);
};
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) {
scope[model + 'SearchTypeLabel'] = label;
scope[model + 'SearchType'] = type;
scope.search(model);
}
if (scope[iterator + 'SearchValue']) {
// User typed a value in input field
scope[iterator + 'ShowStartBtn'] = false;
}
scope.startSearch = function(e,iterator) {
// If use clicks enter while on input field, start the search
if (e.keyCode == 13) {
scope.search(iterator);
}
}
if (iterator === 'host') {
if (scope.hostSearchField === 'has_active_failures') {
if (scope.hostSearchSelectValue && scope.hostSearchSelectValue.value === 1) {
scope.hostFailureFilter = true;
} else {
scope.hostFailureFilter = false;
}
}
}
scope.search = function(iterator) {
//scope[iterator + 'SearchSpin'] = true;
Wait('start');
scope[iterator + 'Loading'] = true;
scope[iterator + 'HoldInput'] = true;
if (scope[iterator + 'SearchValue']) {
// User typed a value in input field
scope[iterator + 'ShowStartBtn'] = false;
}
var fld, key, set, url, sort_order;
for (key in relatedSets) {
if (relatedSets[key].iterator === iterator) {
set = key;
url = relatedSets[key].url;
for (fld in form.related[key].fields) {
if (form.related[key].fields[fld].key) {
if (form.related[key].fields[fld].desc) {
sort_order = '-' + fld;
} else {
sort_order = fld;
}
}
}
break;
}
}
if (iterator == 'host') {
if (scope['hostSearchField'] == 'has_active_failures') {
if (scope['hostSearchSelectValue'] && scope['hostSearchSelectValue'].value == 1) {
scope['hostFailureFilter'] = true;
}
else {
scope['hostFailureFilter'] = false;
}
}
}
sort_order = (scope[iterator + 'SortOrder'] === null) ? sort_order : scope[iterator + 'SortOrder'];
var set, url, iterator, sort_order;
for (var key in relatedSets) {
if (relatedSets[key].iterator == iterator) {
set = key;
url = relatedSets[key].url;
for (var fld in form.related[key].fields) {
if (form.related[key].fields[fld].key) {
if (form.related[key].fields[fld].desc) {
sort_order = '-' + fld;
}
else {
sort_order = fld;
}
}
}
break;
}
}
sort_order = (scope[iterator + 'SortOrder'] == null) ? sort_order : scope[iterator + 'SortOrder'];
f = form.related[set].fields[scope[iterator + 'SearchField']];
if ((scope[iterator + 'SelectShow'] === false && scope[iterator + 'SearchValue'] !== '' &&
scope[iterator + 'SearchValue'] !== undefined) || (scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) ||
(f.searchType && f.searchType === 'gtzero')) {
if (f.sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] = f.sourceModel + '__' + f.sourceField + '__';
} else if (f.searchField) {
scope[iterator + 'SearchParams'] = f.searchField + '__';
} else {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__';
}
var f = form.related[set].fields[scope[iterator + 'SearchField']];
if ( (scope[iterator + 'SelectShow'] == false && scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) ||
(scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) || (f.searchType && f.searchType == 'gtzero') ) {
if (f.sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] = f.sourceModel + '__' + f.sourceField + '__';
}
else if (f.searchField) {
scope[iterator + 'SearchParams'] = f.searchField + '__';
}
else {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__';
}
if ( f.searchType && (f.searchType == 'int' || f.searchType == 'boolean' ) ) {
scope[iterator + 'SearchParams'] += 'int=';
}
else if ( f.searchType && f.searchType == 'gtzero' ) {
scope[iterator + 'SearchParams'] += 'gt=0';
}
else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '=';
}
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 });
}
if (f.searchType && (f.searchType === 'int' || f.searchType === 'boolean')) {
scope[iterator + 'SearchParams'] += 'int=';
} else if (f.searchType && f.searchType === 'gtzero') {
scope[iterator + 'SearchParams'] += 'gt=0';
} else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '=';
}
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'] += encodeURI(scope[iterator + 'SearchValue']);
}
scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + encodeURI(sort_order) : '';
} else {
scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + encodeURI(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) {
var sort_order;
scope.sort = function (iterator, fld) {
var sort_order, icon, direction, set;
// reset sort icons back to 'icon-sort' on all columns
// except the one clicked
$('.' + iterator + ' .list-header').each(function(index) {
if ($(this).attr('id') != iterator + '-' + fld + '-header') {
var icon = $(this).find('i');
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');
}
// reset sort icons back to 'icon-sort' on all columns
// except the one clicked
$('.' + iterator + ' .list-header').each(function () {
if ($(this).attr('id') !== iterator + '-' + fld + '-header') {
var icon = $(this).find('i');
icon.attr('class', 'icon-sort');
}
});
// Set the sorder order value and call the API to refresh the list with the new order
for (var 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);
}
// Toggle the icon for the clicked column
// and set the sort direction
icon = $('#' + iterator + '-' + fld + '-header i');
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
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
*
* All the parts for controlling the search widget on
* All the parts for controlling the search widget on
* related collections.
*
* SearchInit({
@ -11,26 +11,122 @@
* set: <model name (i.e. organizations) used in ng-repeat>
* url: <default api url used to load data>
* list: <list object used by ListGenerator>
* });
* });
*
*/
'use strict';
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store',
function(Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) {
return function(params) {
var scope = params.scope,
set = params.set,
defaultUrl = params.url,
list = params.list,
iterator = (params.iterator) ? params.iterator : list.iterator,
setWidgets = (params.setWidgets === false) ? false : true,
sort_order = params.sort_order || '',
widgets, i, modifier, current_params;
/*
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store',
function (Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) {
return function (params) {
var scope = params.scope,
set = params.set,
defaultUrl = params.url,
list = params.list,
iterator = (params.iterator) ? params.iterator : list.iterator,
setWidgets = (params.setWidgets === false) ? false : true,
sort_order = params.sort_order || '',
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 = {
set: set,
defaultUrl: defaultUrl,
@ -38,471 +134,351 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
iterator: iterator,
sort_order: sort_order
};
Store('CurrentSearchParams', current_params); // Save in case Activity Stream widget needs to restore */
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;
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 (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;
}
if (list.fields[fld].searchType && list.fields[fld].searchType === 'gtzero') {
scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'ShowStartBtn' + modifier] = false;
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 + '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 + '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[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;
}
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) ) {
scope.search(iterator);
};
scope.resetSearch = function (iterator) {
// Respdond to click of reset button
var i,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
// Clear each search widget
setDefaults(i);
}
}
}
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;
// Force removal of search keys from the URL
window.location = '/#' + $location.path();
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) {
// Respdond to click of reset button
var i,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i=1; i <= widgets; i++) {
// Clear each search widget
setDefaults(i);
if (scope.removeDoSearch) {
scope.removeDoSearch();
}
// Force removal of search keys from the URL
window.location = '/#' + $location.path();
scope.search(iterator);
};
if (scope.removeDoSearch) {
scope.removeDoSearch();
}
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;
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
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
if (scope[iterator + 'SearchParams']) {
if (/\/$/.test(url)) {
url += '?' + scope[iterator + 'SearchParams'];
//finalize and execute the query
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
if (scope[iterator + 'SearchParams']) {
if (/\/$/.test(url)) {
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)) ? '?' : '&';
url += connect + 'page=' + page;
}
if (scope[iterator + 'ExtraParms']) {
connect = (/\/$/.test(url)) ? '?' : '&';
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;
}
}
url += (scope[iterator + '_page_size']) ? connect + 'page_size=' + scope[iterator + '_page_size'] : "";
if (page) {
connect = (/\/$/.test(url)) ? '?' : '&';
url += connect + 'page=' + page;
}
}
scope.$emit('prepareSearch2', iterator, page, load, spin);
});
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');
if (scope[iterator + 'ExtraParms']) {
connect = (/\/$/.test(url)) ? '?' : '&';
url += connect + scope[iterator + 'ExtraParms'];
}
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) {
sort_order = direction + list.fields[fld].searchField;
if (scope.removePrepareSearch) {
scope.removePrepareSearch();
}
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.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;
}
}
}
}
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
scope.searchCleanup = function() {
scope.removeDoSearch();
scope.removePrepareSearch();
scope.removePrepareSearch2();
};
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');
}
});
// 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.
*
* 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) {
if ( lookup_results.length == results.length ) {
key = 'organization';
property = 'organization_name';
for (var i=0; i < results.length; i++) {
for (var j=0; j < lookup_results.length; j++) {
if (results[i][key] == lookup_results[j].id) {
results[i][property] = lookup_results[j].value;
}
}
}
scope[iterator + 'SearchSpin'] = false;
scope[set] = results;
}
});
angular.module('TeamHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'SearchHelper',
'PaginationHelpers', 'ListGenerator'
])
.factory('SetTeamListeners', ['Alert', 'Rest',
function (Alert, Rest) {
return function (params) {
scope.$on('TeamRefreshFinished', function(e, results) {
// Loop through the result set (sent to us by the search helper) and
// lookup the id and name of each organization. After each lookup
// 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;
var scope = params.scope,
set = params.set,
iterator = params.iterator;
// Show pop-up to select organization
scope.lookUpOrganization = function() {
var list = OrganizationList;
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Organization' });
var defaultUrl = '/api/v1/organizations/';
// Listeners to perform lookups after main inventory list loads
listScope.selectAction = function() {
var found = false;
for (var 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();
scope.$on('TeamResultFound', function (e, results, lookup_results) {
var i, j, key, property;
if (lookup_results.length === results.length) {
key = 'organization';
property = 'organization_name';
for (i = 0; i < results.length; i++) {
for (j = 0; j < lookup_results.length; j++) {
if (results[i][key] === lookup_results[j].id) {
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) {
// 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";
}
}
scope.$on('TeamRefreshFinished', function (e, results) {
// Loop through the result set (sent to us by the search helper) and
// lookup the id and name of each organization. After each lookup
// completes, call resultFound.
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);
}
var i, lookup_results = [], url;
function getOrganization(url) {
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.
*
* Admins.js
* Admins.js
* List view object for Admins data model.
*
* @dict
*/
'use strict';
angular.module('AdminListDefinition', [])
.value(
'AdminList', {
.value('AdminList', {
name: 'admins',
iterator: 'admin',
selectTitle: 'Add Administrators',
editTitle: 'Admins',
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',
index: true,
hover: true,
hover: true,
fields: {
username: {
key: true,
label: 'Username'
},
first_name: {
},
first_name: {
label: 'First Name'
},
last_name: {
},
last_name: {
label: 'Last Name'
}
},
actions: {
},
fieldActions: {
}
});
},
actions: {},
fieldActions: {}
});

View File

@ -1,15 +1,17 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* CloudCredentials.js
* CloudCredentials.js
* List view object for Credential data model.
*
* @dict
*/
'use strict';
angular.module('CloudCredentialsListDefinition', [])
.value(
'CloudCredentialList', {
.value('CloudCredentialList', {
name: 'cloudcredentials',
iterator: 'cloudcredential',
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>',
index: true,
hover: true,
fields: {
name: {
key: true,
label: 'Name'
},
},
description: {
label: 'Description',
excludeModal: false
},
},
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: {
add: {
mode: 'all', // One of: edit, select, all
mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()',
"class": 'btn-sm',
awToolTip: 'Create a new credential'
}
},
}
},
fieldActions: {
edit: {
ngClick: "editCredential(\{\{ credential.id \}\})",
ngClick: "editCredential(credential.id)",
icon: 'fa-edit',
label: 'Edit',
"class": 'btn-sm',
awToolTip: 'Edit credential',
dataPlacement: 'top'
},
},
"delete": {
ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')",
ngClick: "deleteCredential(credential.id, credential.name)",
icon: 'fa-trash-o',
label: 'Delete',
"class": 'btn-sm',
awToolTip: 'Delete credential',
dataPlacement: 'top'
}
}
});
}
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,20 +1,23 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* JobHosts.js
* JobHosts.js
* List view object for Job Host Summary data model.
*
*
*
*/
'use strict';
angular.module('JobHostDefinition', [])
.value('JobHostList', {
name: 'jobhosts',
iterator: 'jobhost',
editTitle: 'All summaries',
index: true,
hover: true,
navigationLinks: {
ngHide: 'host_id !== null',
details: {
@ -64,7 +67,13 @@ angular.module('JobHostDefinition', [])
dataPlacement: 'top',
searchField: 'failed',
searchType: 'boolean',
searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }]
searchOptions: [{
name: "success",
value: 0
}, {
name: "error",
value: 1
}]
},
failed: {
label: 'Job failed?',
@ -99,7 +108,7 @@ angular.module('JobHostDefinition', [])
searchable: false
}
},
actions: {
help: {
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',
awToolTip: "Refresh the page",
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: {}
});
});

View File

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

View File

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

View File

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

View File

@ -1,78 +1,81 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* Permissions.js
* Permissions.js
* List view object for Permissions data model.
*
*
*
*/
'use strict';
angular.module('PermissionListDefinition', [])
.value(
'PermissionList', {
.value('PermissionList', {
name: 'permissions',
iterator: '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',
index: true,
well: true,
fields: {
name: {
key: true,
label: 'Name',
ngClick: 'editPermission(\{\{ permission.id \}\})'
},
ngClick: 'editPermission(permission.id)'
},
inventory: {
label: 'Inventory',
sourceModel: 'inventory',
sourceField: 'name',
ngBind: 'permission.summary_fields.inventory.name'
},
},
project: {
label: 'Project',
sourceModel: 'project',
sourceField: 'name',
ngBind: 'permission.summary_fields.project.name'
},
},
permission_type: {
label: 'Permission'
}
},
}
},
actions: {
add: {
mode: 'all', // One of: edit, select, all
mode: 'all', // One of: edit, select, all
ngClick: 'addPermission()',
awToolTip: 'Add a new permission',
ngShow: 'PermissionAddAllowed'
},
},
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
mode: 'all'
}
},
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "editPermission(\{\{ permission.id \}\})",
ngClick: "editPermission(permission.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
awToolTip: 'Edit permission',
dataPlacement: 'top'
},
},
"delete": {
label: 'Delete',
ngClick: "deletePermission(\{\{ permission.id \}\},'\{\{ permission.name \}\}')",
ngClick: "deletePermission(permission.id, permission.name)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete permission',
ngShow: 'PermissionAddAllowed',
dataPlacement: 'top'
}
}
});
}
});

View File

@ -1,66 +1,68 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* Projects.js
* Projects.js
* List view object for Project data model.
*
*
*/
'use strict';
angular.module('ProjectsListDefinition', [])
.value(
'ProjectList', {
.value('ProjectList', {
name: 'projects',
iterator: 'project',
selectTitle: 'Add Project',
editTitle: 'Projects',
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,
hover: true,
hover: true,
fields: {
name: {
key: true,
label: 'Name'
},
},
description: {
label: 'Description',
columnClass: 'hidden-sm hidden-xs',
excludeModal: true
},
},
scm_type: {
label: 'Type',
searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource
searchOptions: [], // will be set by Options call to projects resource
excludeModal: true,
nosort: true
},
},
status: {
label: 'Status',
ngClick: 'showSCMStatus(\{\{ project.id \}\})',
ngClick: 'showSCMStatus(project.id)',
awToolTip: 'View details of last SCM Update',
dataPlacement: 'top',
badgeIcon: "\{\{ 'fa icon-failures-' + project.badge \}\}",
badgeIcon: "{{ 'fa icon-failures-' + project.badge }}",
badgePlacement: 'left',
searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource
searchOptions: [], // will be set by Options call to projects resource
excludeModal: true
},
},
last_updated: {
label: 'Last Updated',
type: 'date',
excludeModal: true,
searchable: false
}
},
}
},
actions: {
add: {
mode: 'all', // One of: edit, select, all
mode: 'all', // One of: edit, select, all
ngClick: 'addProject()',
awToolTip: 'Create a new project'
},
},
help: {
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" +
@ -75,46 +77,46 @@ angular.module('ProjectsListDefinition', [])
mode: 'all',
awToolTip: 'Click for help',
awTipPlacement: 'top'
},
},
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
},
},
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
mode: 'all'
}
},
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "editProject(\{\{ project.id \}\})",
ngClick: "editProject(project.id)",
awToolTip: 'Edit project properties',
dataPlacement: 'top'
},
},
scm_update: {
label: 'Update',
ngClick: 'SCMUpdate(\{\{ project.id \}\})',
awToolTip: "\{\{ project.scm_update_tooltip \}\}",
ngClick: 'SCMUpdate(project.id)',
awToolTip: "{{ project.scm_update_tooltip }}",
ngClass: "project.scm_type_class",
dataPlacement: 'top'
},
},
cancel: {
label: 'Stop',
ngClick: "cancelUpdate(\{\{ project.id \}\}, '\{\{ project.name \}\}')",
ngClick: "cancelUpdate(project.id, project.name)",
awToolTip: 'Cancel a running SCM update process',
ngShow: "project.status == 'updating'",
dataPlacement: 'top'
},
},
"delete": {
label: 'Delete',
ngClick: "deleteProject(\{\{ project.id \}\},'\{\{ project.name \}\}')",
ngClick: "deleteProject(project.id, project.name)",
awToolTip: 'Permanently remove project from the database',
ngShow: "project.status !== 'updating'",
dataPlacement: 'top'
}
}
});
}
});

View File

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

View File

@ -1,69 +1,71 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* Teams.js
* Teams.js
* List view object for Team data model.
*
*
*/
'use strict';
angular.module('TeamsListDefinition', [])
.value(
'TeamList', {
.value('TeamList', {
name: 'teams',
iterator: 'team',
selectTitle: 'Add Team',
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,
hover: true,
hover: true,
fields: {
name: {
key: true,
label: 'Name'
},
},
description: {
label: 'Description'
},
},
organization: {
label: 'Organization',
ngBind: 'team.organization_name',
sourceModel: 'organization',
sourceField: 'name'
}
},
}
},
actions: {
add: {
mode: 'all', // One of: edit, select, all
mode: 'all', // One of: edit, select, all
ngClick: 'addTeam()',
awToolTip: 'Create a new team'
},
},
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
mode: 'all'
}
},
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "editTeam(\{\{ team.id \}\})",
ngClick: "editTeam(team.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
awToolTip: 'Edit team',
dataPlacement: 'top'
},
},
"delete": {
label: 'Delete',
ngClick: "deleteTeam(\{\{ team.id \}\},'\{\{ team.name \}\}')",
ngClick: "deleteTeam(team.id, team.name)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete team',
dataPlacement: 'top'
}
}
});
}
});

View File

@ -1,71 +1,72 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* Users.js
* Users.js
* List view object for Users data model.
*
*
*/
'use strict';
angular.module('UserListDefinition', [])
.value(
'UserList', {
.value('UserList', {
name: 'users',
iterator: 'user',
selectTitle: 'Add Users',
editTitle: 'Users',
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>Create New</em> button.</p>',
'<em>Create New</em> button.</p>',
index: true,
hover: true,
hover: true,
fields: {
username: {
key: true,
label: 'Username'
},
},
first_name: {
label: 'First Name'
},
},
last_name: {
label: 'Last Name'
}
},
}
},
actions: {
add: {
label: 'Create New',
mode: 'all', // One of: edit, select, all
mode: 'all', // One of: edit, select, all
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',
awToolTip: 'Create a new user'
},
},
stream: {
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
mode: 'all'
}
},
}
},
fieldActions: {
edit: {
label: 'Edit',
ngClick: "editUser(\{\{ user.id \}\})",
ngClick: "editUser(user.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
awToolTip: 'Edit user',
dataPlacement: 'top'
},
},
"delete": {
label: 'Delete',
ngClick: "deleteUser(\{\{ user.id \}\},'\{\{ user.username \}\}')",
ngClick: "deleteUser(user.id, user.username)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete user',
dataPlacement: 'top'
}
}
});
}
});

View File

@ -1,7 +1,7 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* InventorySyncStatus.js
* InventorySyncStatus.js
*
* Dashboard widget showing object counts and license availability.
*
@ -10,94 +10,98 @@
'use strict';
angular.module('InventorySyncStatusWidget', ['RestServices', 'Utilities'])
.factory('InventorySyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'GetChoices',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait, GetChoices) {
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\">Inventory Sync 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 label = params.label;
var count = params.count;
var fail = params.fail;
var link = params.link;
var fail_link = params.fail_link;
var 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;
.factory('InventorySyncStatus', ['$rootScope', '$compile', function ($rootScope, $compile) {
return function (params) {
var scope = params.scope,
target = params.target,
dashboard = params.dashboard,
html, group_total, group_fail, element, src;
html = "<div class=\"panel panel-default\">\n";
html += "<div class=\"panel-heading\">Inventory Sync 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 label = params.label,
count = params.count,
fail = params.fail,
link = params.link,
fail_link = params.fail_link,
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: 'Inventories',
count: (dashboard.inventories && dashboard.inventories.total_with_inventory_source) ?
dashboard.inventories.total_with_inventory_source : 0,
fail: (dashboard.inventories && dashboard.inventories.inventory_failed) ? dashboard.inventories.inventory_failed : 0,
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'
html += makeRow({
label: 'Inventories',
count: (dashboard.inventories && dashboard.inventories.total_with_inventory_source) ?
dashboard.inventories.total_with_inventory_source : 0,
fail: (dashboard.inventories && dashboard.inventories.inventory_failed) ? dashboard.inventories.inventory_failed : 0,
link: '/#/inventories/?has_inventory_sources=true',
fail_link: '/#/inventories/?inventory_sources_with_failures=true'
});
// Each inventory source
for (var 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
group_total = 0;
group_fail = 0;
if (dashboard.inventory_sources) {
for (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
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 += "</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');
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.
*
* JobStatus.js
* JobStatus.js
*
* Dashboard widget showing object counts and license availability.
*
@ -11,88 +11,89 @@
angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
.factory('JobStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait) {
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'
});
function ($rootScope, $compile) {
return function (params) {
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n";
html += "</div>\n";
var scope = params.scope,
target = params.target,
dashboard = params.dashboard,
html = '', element;
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));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
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 + "\"";
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.
*
* ObjectCount.js
* ObjectCount.js
*
* Dashboard widget showing object counts and license availability.
*
@ -11,60 +11,60 @@
angular.module('ObjectCountWidget', ['RestServices', 'Utilities'])
.factory('ObjectCount', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait) {
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
});
}
function ($rootScope, $compile) {
return function (params) {
html += "</tbody>\n";
html += "</table>\n";
html += "</div>\n";
html += "</div>\n"
var element = angular.element(document.getElementById(target));
element.html(html);
$compile(element)(scope);
scope.$emit('WidgetLoaded');
var scope = params.scope,
target = params.target,
dashboard = params.dashboard,
keys = ['organizations', 'users', 'teams', 'credentials', 'projects', 'inventories', 'groups', 'hosts',
'job_templates', 'jobs'
],
i, html, element;
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.
*
* SCMSyncStatus.js
* SCMSyncStatus.js
*
* Dashboard widget showing object counts and license availability.
*
*/
'use strict';
angular.module('SCMSyncStatusWidget', ['RestServices', 'Utilities'])
.factory('SCMSyncStatus', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'GetChoices',
function($rootScope, $compile, Rest, GetBasePath, ProcessErrors, Wait, GetChoices) {
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;
}
.factory('SCMSyncStatus', ['$rootScope', '$compile',
function ($rootScope, $compile) {
return function (params) {
var total_count = 0;
if (dashboard.scm_types) {
for (var type in dashboard.scm_types) {
total_count += (dashboard.scm_types[type].total) ? dashboard.scm_types[type].total : 0;
}
}
var scope = params.scope,
target = params.target,
dashboard = params.dashboard,
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({
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'
});
}
label: 'Projects',
link: '/#/projects',
count: total_count,
fail: (dashboard.projects && dashboard.projects.failed) ? dashboard.projects.failed : 0,
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.
*
* Stream.js
* Stream.js
*
* Activity stream widget that can be called from anywhere
*
@ -10,457 +10,470 @@
'use strict';
angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginationHelpers',
'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 });
}
}])
'RefreshHelper', 'ListGenerator', 'StreamWidget', 'AuthService'
])
.factory('ShowStream', [ 'setStreamHeight', 'Authorization', function(setStreamHeight, Authorization) {
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'
.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(),
theight = parseInt($('#tab-content-container').css('min-height').replace(/px/, '')),
height = (theight < sheight) ? sheight : theight;
$('#tab-content-container').css({
"min-height": height
});
};
}
])
.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
stream.show('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
}
}])
// Slide in stream
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) {
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 paths = $location.path().split('/');
paths.splice(0,1);
var path, title, i, j;
for (i=0; i < paths.length; i++) {
if (/^\d+/.test(paths[i])) {
path='';
title='';
for (j=0; j <= i; 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++) {
.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) {
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];
}
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 {
path = '/' + paths[i];
}
title = paths[i];
title = title.charAt(0).toUpperCase() + title.slice(1);
$rootScope.breadcrumbs.push({
path: path,
title: title,
ngClick: "closeStream('" + path + "')"
});
}
$rootScope.breadcrumbs.push({ path: path, title: title, ngClick: "closeStream('" + path + "')" });
}
}
}])
};
}
])
.factory('FixUrl', [ function() {
return function(u) {
return u.replace(/\/api\/v1\//,'/#/');
}
}])
.factory('FixUrl', [
function () {
return function (u) {
return u.replace(/\/api\/v1\//, '/#/');
};
}
])
.factory('BuildUrl', [ function() {
return function(obj) {
var url = '/#/';
switch(obj.base) {
case 'group':
case 'host':
url += 'home/' + obj.base + 's/?id=' + obj.id;
break;
case 'inventory':
url += 'inventories/' + obj.id + '/';
break;
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+_/,'');
.factory('BuildUrl', [
function () {
return function (obj) {
var url = '/#/';
switch (obj.base) {
case 'group':
case 'host':
url += 'home/' + obj.base + 's/?id=' + obj.id;
break;
case 'inventory':
url += 'inventories/' + obj.id + '/';
break;
default:
url += obj.base + 's/' + obj.id + '/';
}
var descr = '';
var descr_nolink;
descr += activity.operation;
descr += (/e$/.test(activity.operation)) ? 'd ' : 'ed ';
descr_nolink = descr;
return url;
};
}
])
// labels
var obj1 = activity.object1;
var obj2 = activity.object2;
// objects
var obj1_obj = (activity.summary_fields[obj1]) ? activity.summary_fields[obj1][0] : null;
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);
.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+_/, '');
}
descr += obj2 + name + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' );
descr_nolink += obj2 + name + ( (activity.operation == 'disassociate') ? ' from ' : ' to ' );
}
if (obj1_obj && obj1_obj.name && !/^\_delete/.test(obj1_obj.name)) {
obj1_obj['base'] = obj1;
descr += obj1 + ' <a href=\"' + BuildUrl(obj1_obj) + '\">' + obj1_obj.name + '</a>';
descr_nolink += obj1 + ' ' + obj1_obj.name;
}
else if (obj1) {
name = '';
var 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;
}
}
var descr, descr_nolink, obj1, obj2, obj1_obj, obj2_obj, name, name_nolink;
descr = activity.operation;
descr += (/e$/.test(activity.operation)) ? 'd ' : 'ed ';
descr_nolink = descr;
// labels
obj1 = activity.object1;
obj2 = activity.object2;
// objects
obj1_obj = (activity.summary_fields[obj1]) ? activity.summary_fields[obj1][0] : null;
if (obj1 === obj2) {
obj2_obj = activity.summary_fields[obj1][1];
} else if (activity.summary_fields[obj2]) {
obj2_obj = activity.summary_fields[obj2][0];
} else {
obj2_obj = null;
}
else if (obj1_obj && obj1_obj.name) {
name = ' ' + stripDeleted(obj1_obj.name);
name_nolink = name;
if (obj1 === 'user' || obj2 === 'user') {
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',
'ActivityDetailForm', 'Empty', 'Find',
function($rootScope, Rest, Alert, GenerateForm, ProcessErrors, GetBasePath, FormatDate, ActivityDetailForm, Empty, Find) {
return function(params) {
var activity_id = params.activity_id;
var parent_scope = params.scope;
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");
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);
}
$('#form-modal').on('show.bs.modal', function (e) {
$('#form-modal-body').css({
width:'auto', //probably not needed
height:'auto', //probably not needed
'max-height':'100%'
descr += obj2 + name + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
descr_nolink += obj2 + name + ((activity.operation === 'disassociate') ? ' from ' : ' to ');
}
if (obj1_obj && obj1_obj.name && !/^\_delete/.test(obj1_obj.name)) {
obj1_obj.base = obj1;
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.formModalCancelShow = false;
scope.formModalInfo = false;
scope.formModalHeader = "Event " + activity.id;
if (!scope.$$phase) {
scope.$digest();
scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false;
scope.formModalInfo = false;
scope.formModalHeader = "Event " + activity.id;
if (!scope.$$phase) {
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',
function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList,
.factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit',
'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,
Find, Store) {
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;
return function (params) {
// Hang onto current search params
var PreviousSearchParams = Store('CurrentSearchParams');
var list = StreamList,
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
var inventory_name = (params && params.inventory_name) ? params.inventory_name : null;
$rootScope.flashMessage = null;
// url will override the attempt to compute an activity_stream query
var url = (params && params.url) ? params.url : null;
$rootScope.flashMessage = null;
if (url) {
defaultUrl = url;
}
else {
if ($location.path() !== '/home') {
// Restrict what we're looking at based on the path
var type = (base == 'inventories') ? 'inventory' : base.replace(/s$/,'');
var paths = $location.path().split('/');
paths.splice(0,1);
if (paths.length > 1 && /^\d+/.test(paths[paths.length - 1])) {
type = paths[paths.length - 2];
type = (type == 'inventories') ? 'inventory' : type.replace(/s$/,'');
//defaultUrl += '?object1=' + type + '&object1__id=' +
defaultUrl += '?' + type + '__id=' + paths[paths.length - 1];
}
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;
if (url) {
defaultUrl = url;
} else {
if ($location.path() !== '/home') {
// Restrict what we're looking at based on the path
type = (base === 'inventories') ? 'inventory' : base.replace(/s$/, '');
paths = $location.path().split('/');
paths.splice(0, 1);
if (paths.length > 1 && /^\d+/.test(paths[paths.length - 1])) {
type = paths[paths.length - 2];
type = (type === 'inventories') ? 'inventory' : type.replace(/s$/, '');
//defaultUrl += '?object1=' + type + '&object1__id=' +
defaultUrl += '?' + type + '__id=' + paths[paths.length - 1];
} 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
$('#tab-content-container').append("<div id=\"stream-container\"><div id=\"stream-content\"></div></div><!-- Stream widget -->");
StreamBreadCrumbs();
// Add a container for the stream widget
$('#tab-content-container').append("<div id=\"stream-container\"><div id=\"stream-content\"></div></div><!-- Stream widget -->");
// Fix inventory name. The way we're doing breadcrumbs doesn't support bind variables.
if (inventory_name) {
var itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory_name }}' });
if (itm) {
itm.title = inventory_name;
StreamBreadCrumbs();
// Fix inventory name. The way we're doing breadcrumbs doesn't support bind variables.
if (inventory_name) {
itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory_name }}' });
if (itm) {
itm.title = inventory_name;
}
}
}
ShowStream();
// Generate the list
var scope = view.inject(list, {
mode: 'edit',
id: 'stream-content',
searchSize: 'col-lg-3',
secondWidget: true,
activityStream: true
ShowStream();
// Generate the list
scope = view.inject(list, { mode: 'edit', id: 'stream-content', searchSize: 'col-lg-3', secondWidget: true, activityStream: true });
// descriptive title describing what AS is showing
scope.streamTitle = (params && params.title) ? params.title : null;
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
scope.streamTitle = (params && params.title) ? params.title : null;
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() {
// 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);
}
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
*
* User authentication functions
*
*/
'use strict';
angular.module('AuthService', ['ngCookies', 'Utilities'])
.factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath',
function($http, $rootScope, $location, $cookieStore, GetBasePath) {
return {
setToken: function(token, expires) {
// set the session cookie
$cookieStore.remove('token');
$cookieStore.remove('token_expires');
$cookieStore.remove('userLoggedIn');
$cookieStore.put('token', token);
$cookieStore.put('token_expires', expires);
$cookieStore.put('userLoggedIn', true);
$cookieStore.put('sessionExpired', false);
$rootScope.token = token;
$rootScope.userLoggedIn = true;
$rootScope.token_expires = expires;
$rootScope.sessionExpired = false;
},
function ($http, $rootScope, $location, $cookieStore, GetBasePath) {
return {
setToken: function (token, expires) {
// set the session cookie
$cookieStore.remove('token');
$cookieStore.remove('token_expires');
$cookieStore.remove('userLoggedIn');
$cookieStore.put('token', token);
$cookieStore.put('token_expires', expires);
$cookieStore.put('userLoggedIn', true);
$cookieStore.put('sessionExpired', false);
$rootScope.token = token;
$rootScope.userLoggedIn = true;
$rootScope.token_expires = expires;
$rootScope.sessionExpired = false;
},
isUserLoggedIn: function() {
if ($rootScope.userLoggedIn === undefined) {
// Browser refresh may have occurred
$rootScope.userLoggedIn = $cookieStore.get('userLoggedIn');
$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;
isUserLoggedIn: function () {
if ($rootScope.userLoggedIn === undefined) {
// Browser refresh may have occurred
$rootScope.userLoggedIn = $cookieStore.get('userLoggedIn');
$rootScope.sessionExpired = $cookieStore.get('sessionExpired');
}
else {
result = false;
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 {
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.
@ -8,86 +7,104 @@
* Build data for the tree selector table used on inventory detail page.
*
*/
'use strict';
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
.factory('SortNodes', [ function() {
return function(data) {
.factory('SortNodes', [
function () {
return function (data) {
//Sort nodes by name
var names = [];
var newData = [];
for (var i=0; i < data.length; i++) {
var i, j, names = [], newData = [];
for (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]);
for (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;
}
}])
};
}
])
.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) {
// Start our tree object with All Hosts
var children = [];
var sorted = SortNodes(tree_data);
for (var j=0; j < sorted.length; j++) {
children.push(sorted[j].id);
}
var 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);
var children = [],
sorted = SortNodes(tree_data),
j, all_hosts;
for (j = 0; j < sorted.length; j++) {
children.push(sorted[j].id);
}
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) {
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++;
var stat = GetSyncStatusMsg({
stat = GetSyncStatusMsg({
status: sorted[i].summary_fields.inventory_source.status
}); // from helpers/Groups.js
var hosts_status = GetHostsStatusMsg({
}); // from helpers/Groups.js
hosts_status = GetHostsStatusMsg({
active_failures: sorted[i].hosts_with_active_failures,
total_hosts: sorted[i].total_hosts,
inventory_id: inventory_id,
inventory_id: inventory_id,
group_id: sorted[i].id
}); // from helpers/Groups.js
var children = [];
for (var j=0; j < sorted[i].children.length; j++) {
}); // from helpers/Groups.js
children = [];
for (j = 0; j < sorted[i].children.length; j++) {
children.push(sorted[i].children[j].id);
}
var group = {
}
group = {
name: sorted[i].name,
has_active_failures: sorted[i].has_active_failures,
total_hosts: sorted[i].total_hosts,
hosts_with_active_failures: sorted[i].hosts_with_active_failures,
total_groups: sorted[i].total_groups,
groups_with_active_failures: sorted[i].groups_with_active_failures,
parent: parent,
parent: parent,
has_children: (sorted[i].children.length > 0) ? true : false,
has_inventory_sources: sorted[i].has_inventory_sources,
id: id,
@ -97,25 +114,25 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
children: children,
ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-square-o node-no-toggle',
ngclick: 'toggle(' + id + ')',
related: {
children: (sorted[i].children.length > 0) ? sorted[i].related.children : '',
related: {
children: (sorted[i].children.length > 0) ? sorted[i].related.children : '',
inventory_source: sorted[i].related.inventory_source
},
},
status: sorted[i].summary_fields.inventory_source.status,
status_class: stat['class'],
status_tooltip: stat['tooltip'],
launch_tooltip: stat['launch_tip'],
launch_class: stat['launch_class'],
hosts_status_tip: hosts_status['tooltip'],
show_failures: hosts_status['failures'],
status_tooltip: stat.tooltip,
launch_tooltip: stat.launch_tip,
launch_class: stat.launch_class,
hosts_status_tip: hosts_status.tooltip,
show_failures: hosts_status.failures,
hosts_status_class: hosts_status['class'],
selected_class: '',
show: true,
isDraggable: true,
isDraggable: true,
isDroppable: true
}
};
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
scope.selected_tree_id = id;
scope.selected_group_id = group.group_id;
@ -130,62 +147,66 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
if (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.get()
.success( function(data, status, headers, config) {
.success(function (data) {
buildAllHosts(data);
buildGroups(data, 0, 0);
scope.autoShowGroupHelp = (data.length == 0) ? true : false;
scope.autoShowGroupHelp = (data.length === 0) ? true : false;
if (refresh) {
scope.groups = groups;
scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit);
}
else {
} else {
scope.$emit('GroupTreeLoaded', inventory_name, groups, emit);
}
})
.error( function(data, status, headers, config) {
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status });
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status
});
});
});
});
function loadTreeData() {
// Load the inventory root node
Wait('start');
Rest.setUrl (GetBasePath('inventory') + inventory_id + '/');
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
.success(function (data) {
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
})
.error( function(data, status, headers, config) {
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
});
}
});
}
loadTreeData();
}
}])
};
}
])
// Update a group with a set of properties
.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty',
function(ApplyEllipsis, GetSyncStatusMsg, Empty) {
return function(params) {
var scope = params.scope;
var group_id = params.group_id;
var properties = params.properties; // object of key:value pairs to update
var old_name, stat;
for (var i=0; i < scope.groups.length; i++) {
// Update a group with a set of properties
.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty',
function (ApplyEllipsis, GetSyncStatusMsg, Empty) {
return function (params) {
var scope = params.scope,
group_id = params.group_id,
properties = params.properties,
i, p, grp, old_name, stat;
for (i = 0; i < scope.groups.length; i++) {
if (scope.groups[i].group_id === group_id) {
var grp = scope.groups[i];
for (var p in properties) {
grp = scope.groups[i];
for (p in properties) {
if (p === '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))) {
// We have a source but no status, seed the status with 'never' to enable sync button
scope.groups[i].status = 'never updated';
}
else if (!properties[p]) {
} else if (!properties[p]) {
// User removed source
scope.groups[i].status = 'none';
}
// 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_tooltip = stat.tooltip;
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];
}
}
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
scope.selected_group_name = 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
if (old_name) {
setTimeout(function() {
$('#groups_table .group-name a[title="' + old_name + '"]').attr('title',properties.name);
setTimeout(function () {
$('#groups_table .group-name a[title="' + old_name + '"]').attr('title', properties.name);
ApplyEllipsis('#groups_table .group-name a');
}, 2500);
}, 2500);
}
}
}])
};
}
])
// 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;
// Set node name and description after an update to Group properties.
.factory('SetNodeName', [
function () {
return function (params) {
var name = params.name,
descr = params.description,
group_id = (params.group_id !== undefined) ? params.group_id : null,
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);
});
$('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function () {
$(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);
}
}
}])
};
}
])
// 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;
var target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id });
var inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id });
// 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,
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
var html = '';
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\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";
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
html += "<h3>Move Group</h3>\n";
}
else {
html += "<h3>Copy or Move?</h3>\n";
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
html += "<h3>Move Group</h3>\n";
} else {
html += "<h3>Copy or Move?</h3>\n";
}
html += "</div>\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>";
}
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 " +
target.name + "?</p>";
}
else {
} else {
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 += "<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 class=\"modal-footer\">\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
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-dialog -->\n";
html += "</div><!-- modal -->\n";
// 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);
$compile(e)(scope);
// Display it
$('#copy-prompt-modal').modal({
backdrop: 'static',
keyboard: true,
show: true
});
});
// Respond to move
scope.moveGroup = function() {
scope.moveGroup = function () {
var url, group, parent;
$('#copy-prompt-modal').modal('hide');
Wait('start');
// disassociate the group from the original parent
if (scope.removeGroupRemove) {
scope.removeGroupRemove();
scope.removeGroupRemove();
}
scope.removeGroupRemove = scope.$on('removeGroup', function() {
scope.removeGroupRemove = scope.$on('removeGroup', function () {
if (inbound.parent > 0) {
// 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 })
var url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/';
parent = Find({ list: scope.groups, key: 'id', val: inbound.parent });
url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/';
Rest.setUrl(url);
Rest.post({ id: inbound.group_id, disassociate: 1 })
.success( function(data, status, headers, config) {
.success(function () {
//Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted');
})
.error( function(data, status, headers, config) {
scope.$emit('GroupDeleteCompleted');
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to remove ' + inbound.name +
' from ' + parent.name + '. POST returned status: ' + status });
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to remove ' + inbound.name +
' from ' + parent.name + '. POST returned status: ' + status
});
}
else {
});
} else {
//Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted');
scope.$emit('GroupDeleteCompleted');
}
});
});
// add the new group to the target parent
var url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/';
var group = {
url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/';
group = {
id: inbound.group_id,
name: inbound.name,
description: inbound.description,
inventory: scope.inventory_id
}
};
Rest.setUrl(url);
Rest.post(group)
.success( function(data, status, headers, config) {
.success(function () {
scope.$emit('removeGroup');
})
.error( function(data, status, headers, config) {
})
.error(function (data, status) {
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' +
target_name + '. POST returned status: ' + status });
});
}
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status });
});
};
scope.copyGroup = function() {
scope.copyGroup = function () {
$('#copy-prompt-modal').modal('hide');
Wait('start');
// add the new group to the target parent
var url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/';
var group = {
id: inbound.group_id,
name: inbound.name,
description: inbound.description,
inventory: scope.inventory_id
}
var url = (!Empty(target.group_id)) ?
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
GetBasePath('inventory') + scope.inventory_id + '/groups/',
group = {
id: inbound.group_id,
name: inbound.name,
description: inbound.description,
inventory: scope.inventory_id
};
Rest.setUrl(url);
Rest.post(group)
.success( function(data, status, headers, config) {
//Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted');
})
.error( function(data, status, headers, config) {
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to add ' + inbound.name + ' to ' +
target_name + '. POST returned status: ' + status });
});
}
.success(function () {
//Triggers refresh of group list in inventory controller
scope.$emit('GroupDeleteCompleted');
})
.error(function (data, status) {
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
Wait('stop');
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status
});
});
};
}
}])
// Copy a host after drag-n-drop
.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
function($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
return function(params) {
};
}
])
// Copy a host after drag-n-drop
.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
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 }),
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) {
for (i=0; i< host.summary_fields.all_groups.length; i++) {
if (host.summary_fields.all_groups[i].id == target.group_id) {
found = true;
break;
for (i = 0; i < host.summary_fields.all_groups.length; i++) {
if (host.summary_fields.all_groups[i].id === target.group_id) {
found = true;
break;
}
}
}
if (found) {
var html = '';
html += "<div id=\"copy-alert-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\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 += "<h3>Already in Group</h3>\n";
html += "</div>\n";
@ -456,31 +478,30 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "</div>\n";
// 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);
$compile(e)(scope);
// Display it
$('#copy-alert-modal').modal({
backdrop: 'static',
keyboard: true,
show: true
});
}
else {
});
} else {
// Build the html for our prompt dialog
var html = '';
html = '';
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
html += "<div class=\"modal-dialog\">\n";
html += "<div class=\"modal-content\">\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";
html += "<h3>Copy Host</h3>\n";
html += "</div>\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 class=\"modal-footer\">\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-dialog -->\n";
html += "</div><!-- modal -->\n";
// 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);
$compile(e)(scope);
// Display it
$('#copy-prompt-modal').modal({
backdrop: 'static',
keyboard: true,
show: true
});
});
scope.copyHost = function() {
scope.copyHost = function () {
$('#copy-prompt-modal').modal('hide');
Wait('start');
Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/');
Rest.post(host)
.success(function(data, status, headers, config) {
.success(function () {
// Signal the controller to refresh the hosts view
scope.$emit('GroupTreeRefreshed');
})
.error(function(data, status, headers, config) {
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' +
target.name + '. POST returned status: ' + status });
});
}
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' +
target.name + '. POST returned status: ' + status });
});
};
}
}
}]);
};
}
]);

View File

@ -4,154 +4,194 @@
* Generic accessor for Ansible Commander services
*
*/
'use strict';
'use strict';
angular.module('RestServices',['ngCookies','AuthService'])
.factory('Rest', ['$http','$rootScope','$cookieStore', '$q', 'Authorization',
function($http, $rootScope, $cookieStore, $q, Authorization) {
return {
headers: {},
angular.module('RestServices', ['ngCookies', 'AuthService'])
.factory('Rest', ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization',
function ($http, $rootScope, $cookieStore, $q, Authorization) {
return {
setUrl: function (url) {
this.url = url;
},
checkExpired: function() {
return $rootScope.sessionTimer.isExpired();
},
pReplace: function() {
//in our url, replace :xx params with a value, assuming
//we can find it in user supplied params.
var key,rgx;
for (key in this.params) {
rgx = new RegExp("\\:" + key,'gm');
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
var promise = $q.reject({ data: data, status: status });
promise.success = function(fn){
promise.then(function(response){ fn(response.data, response.status) }, null);
return promise
headers: {},
setUrl: function (url) {
this.url = url;
},
checkExpired: function () {
return $rootScope.sessionTimer.isExpired();
},
pReplace: function () {
//in our url, replace :xx params with a value, assuming
//we can find it in user supplied params.
var key, rgx;
for (key in this.params) {
rgx = new RegExp("\\:" + key, 'gm');
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
var promise = $q.reject({
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
*
*/
'use strict';
angular.module('TimerService', ['ngCookies', 'Utilities'])
.factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty',
function($rootScope, $cookieStore, $location, GetBasePath, Empty) {
return {
.factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty',
function ($rootScope, $cookieStore) {
return {
sessionTime: null,
timeout: null,
getSessionTime: function() {
return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime');
},
sessionTime: null,
timeout: null,
isExpired: function() {
var stime = this.getSessionTime();
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);
},
getSessionTime: function () {
return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime');
},
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);
},
isExpired: function () {
var stime = this.getSessionTime(),
now = new Date().getTime();
if ((stime - now) <= 0) {
//expired
return true;
} else {
// not expired. move timer forward.
this.moveForward();
return false;
}
},
init: function() {
this.moveForward();
return this;
}
}
}]);
expireSession: function () {
this.sessionTime = 0;
$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';
angular.module('Utilities',['RestServices', 'Utilities'])
.factory('ClearScope', [ function() {
return function(id) {
var element = document.getElementById(id), scope;
angular.module('Utilities', ['RestServices', 'Utilities'])
.factory('ClearScope', [
function () {
return function (id) {
var element = document.getElementById(id),
scope;
if (element) {
scope = angular.element(element).scope();
scope.$destroy();
}
$('.tooltip').each( function() {
$('.tooltip').each(function () {
// Remove any lingering tooltip and popover <div> elements
$(this).remove();
});
$('.popover').each(function() {
$('.popover').each(function () {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove();
});
try {
$('#help-modal').dialog('close');
}
catch(e) {
} catch (e) {
// ignore
}
$(window).unbind('resize');
};
}])
}
])
/* Empty()
*
* Test if a value is 'empty'. Returns true if val is null | '' | undefined.
* Only works on non-Ojbect types.
*
*/
.factory('Empty', [ function() {
return function(val) {
/* Empty()
*
* Test if a value is 'empty'. Returns true if val is null | '' | undefined.
* Only works on non-Ojbect types.
*
*/
.factory('Empty', [
function () {
return function (val) {
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) {
return function(hdr, msg, cls, action, secondAlert, disableButtons) {
.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) {
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.
// 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
@ -79,15 +83,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
if (secondAlert) {
$rootScope.alertHeader2 = hdr;
$rootScope.alertBody2 = msg;
$rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal2').modal({ show: true, keyboard: true , backdrop: 'static' });
$rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal2').modal({
show: true,
keyboard: true,
backdrop: 'static'
});
$rootScope.disableButtons2 = (disableButtons) ? true : false;
if (action) {
$('#alert-modal2').on('hidden', function() {
$('#alert-modal2').on('hidden', function () {
action();
});
}
$(document).bind('keydown', function(e) {
$(document).bind('keydown', function (e) {
if (e.keyCode === 27) {
$('#alert-modal2').modal('hide');
if (action) {
@ -95,14 +103,17 @@ angular.module('Utilities',['RestServices', 'Utilities'])
}
}
});
}
else {
} else {
$rootScope.alertHeader = hdr;
$rootScope.alertBody = msg;
$rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal').modal({ show: true, keyboard: true , backdrop: 'static' });
$(document).bind('keydown', function(e) {
$rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
$('#alert-modal').modal({
show: true,
keyboard: true,
backdrop: 'static'
});
$(document).bind('keydown', function (e) {
if (e.keyCode === 27) {
$('#alert-modal').modal('hide');
if (action) {
@ -113,168 +124,171 @@ angular.module('Utilities',['RestServices', 'Utilities'])
$rootScope.disableButtons = (disableButtons) ? true : false;
if (action) {
$('#alert-modal').on('hidden', function() {
$('#alert-modal').on('hidden', function () {
action();
});
}
}
};
}])
}
])
.factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait',
function($rootScope, $cookieStore, $log, $location, Alert, Wait) {
return function(scope, data, status, form, defaultMsg) {
var field, fieldErrors, msg;
Wait('stop');
if ($AnsibleConfig.debug_mode && console) {
console.log('Debug status: ' + status);
console.log('Debug data: ');
console.log(data);
.factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait',
function ($rootScope, $cookieStore, $log, $location, Alert, Wait) {
return function (scope, data, status, form, defaultMsg) {
var field, fieldErrors, msg;
Wait('stop');
if ($AnsibleConfig.debug_mode && console) {
console.log('Debug status: ' + status);
console.log('Debug 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) {
msg = 'The API responded with a 403 Access Denied error. ';
if (data.detail) {
msg += 'Detail: ' + data.detail;
Alert(defaultMsg.hdr, msg);
} else if ((status === 401 && data.detail && data.detail === 'Token is expired') ||
(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');
}
else {
msg += 'Please contact your system administrator.';
}
Alert(defaultMsg.hdr, msg);
}
else if ( (status === 401 && data.detail && data.detail === 'Token is expired') ||
(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 (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 ((!fieldErrors) && defaultMsg) {
Alert(defaultMsg.hdr, defaultMsg.msg);
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;
}
}
}
else {
if ((!fieldErrors) && defaultMsg) {
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) {
return (a === 'ies') ? 'y' : '';
}
//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 (!Empty(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) {
$rootScope.crumbCache.push(crumb);
.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) {
return (a === 'ies') ? 'y' : '';
}
//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 (!Empty(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;
}
}
paths = $location.path().replace(/^\//,'').split('/');
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];
}
if (!found) {
$rootScope.crumbCache.push(crumb);
}
};
}])
.factory('HelpDialog', ['$rootScope', '$location', 'Store', function($rootScope, $location, Store) {
return function(params) {
}
paths = $location.path().replace(/^\//, '').split('/');
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];
}
}
};
}
])
.factory('HelpDialog', ['$rootScope', '$location', 'Store',
function ($rootScope, $location, Store) {
return function (params) {
// Display a help dialog
//
// HelpDialog({ defn: <HelpDefinition> })
@ -285,10 +299,10 @@ angular.module('Utilities',['RestServices', 'Utilities'])
autoShow = params.autoShow || false;
function showHelp(step) {
var btns, ww, width, height, isOpen=false;
var btns, ww, width, height, isOpen = false;
current_step = step;
function buildHtml(step) {
var html = '';
//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" : "";
return html;
}
width = (defn.story.width) ? defn.story.width : 510;
height = (defn.story.height) ? defn.story.height : 600;
@ -311,21 +325,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
try {
isOpen = $('#help-modal').dialog('isOpen');
}
catch(e) {
} catch (e) {
// ignore
}
if (isOpen) {
$('#help-modal').html(buildHtml(defn.story.steps[current_step]));
}
else {
} else {
// Define buttons based on story length
btns = [];
if (defn.story.steps.length > 1) {
btns.push({
text: "Prev",
click: function(e) {
click: function (e) {
if (current_step - 1 === 0) {
$(e.target).button('disable');
}
@ -338,7 +350,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
});
btns.push({
text: "Next",
click: function(e) {
click: function (e) {
if (current_step + 1 > 0) {
$(e.target).prev().button('enable');
}
@ -349,12 +361,19 @@ angular.module('Utilities',['RestServices', 'Utilities'])
}
});
}
btns.push({ text: "Close",
click: function() { $('#help-modal').dialog('close'); }
btns.push({
text: "Close",
click: function () {
$('#help-modal').dialog('close');
}
});
// Show the 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,
width: width,
height: height,
@ -363,75 +382,91 @@ angular.module('Utilities',['RestServices', 'Utilities'])
show: 500,
hide: 500,
resizable: false,
close: function() { $('#help-modal').empty(); }
close: function () {
$('#help-modal').empty();
}
});
// 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;
l = $(this).text();
if (l === 'Close') {
h = "fa-times";
c = "btn btn-default";
$(this).attr({ 'class': c }).html("<i class=\"fa " + h + "\"></i> Close");
}
else if (l === 'Prev') {
$(this).attr({
'class': c
}).html("<i class=\"fa " + h + "\"></i> Close");
} else if (l === 'Prev') {
h = "fa-chevron-left";
c = "btn btn-primary";
$(this).attr({ 'class': c }).html("<i class=\"fa " + h + "\"></i> Prev");
}
else {
$(this).attr({
'class': c
}).html("<i class=\"fa " + h + "\"></i> Prev");
} else {
h = "fa-chevron-right";
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')
.empty().attr({ 'class': 'close' }).text('x');
.empty().attr({
'class': 'close'
}).text('x');
// 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) {
Store('inventoryAutoHelp','off');
}
else {
Store('inventoryAutoHelp','on');
Store('inventoryAutoHelp', 'off');
} else {
Store('inventoryAutoHelp', 'on');
}
});
}
}
showHelp(0);
};
}])
}
])
.factory('ReturnToCaller', ['$location', 'Empty', function($location, Empty) {
return function(idx) {
.factory('ReturnToCaller', ['$location', 'Empty',
function ($location, Empty) {
return function (idx) {
// 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.
var paths = $location.path().replace(/^\//,'').split('/'),
newpath = '', i;
var paths = $location.path().replace(/^\//, '').split('/'),
newpath = '',
i;
idx = (Empty(idx)) ? paths.length - 1 : idx + 1;
for (i=0; i < idx; i++) {
for (i = 0; i < idx; i++) {
newpath += '/' + paths[i];
}
$location.path(newpath);
};
}])
}
])
.factory('FormatDate', ['$filter', function($filter) {
return function(dt) {
.factory('FormatDate', ['$filter',
function ($filter) {
return function (dt) {
// Wrapper for data filter- an attempt to insure all dates display in
// the same format. Pass in date object.
return $filter('date')(dt, 'MM/dd/yy HH:mm:ss');
};
}])
}
])
.factory('Wait', [ '$rootScope', function($rootScope) {
return function(directive) {
.factory('Wait', ['$rootScope',
function ($rootScope) {
return function (directive) {
// 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).
// Wait('start' | 'stop');
@ -448,16 +483,22 @@ angular.module('Utilities',['RestServices', 'Utilities'])
width: $(document).width(),
height: $(document).height()
}).fadeIn();
$('.spinny').css({ top: y, left: x }).fadeIn(400);
}
else if (directive === 'stop' && $rootScope.waiting){
$('.spinny, .overlay').fadeOut(400, function(){ $rootScope.waiting = false; });
$('.spinny').css({
top: y,
left: x
}).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
var target = $(selector),
width = target.css('width'),
@ -468,7 +509,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
backgroundColor = target.css('background-color'),
margin = target.css('margin'),
padding = target.css('padding');
parent.append("<div id=\"curtain-div\" style=\"" +
"position: absolute;" +
"top: " + position.top + "px; " +
@ -485,66 +526,78 @@ angular.module('Utilities',['RestServices', 'Utilities'])
"\"></div>");
$('#curtain-div').show(0, action);
};
}])
}
])
.factory('ShowElement', [ function() {
return function() {
.factory('ShowElement', [
function () {
return function () {
// 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) {
return function(params) {
.factory('GetChoices', ['Rest', 'ProcessErrors',
function (Rest, ProcessErrors) {
return function (params) {
// Get dropdown options
var scope = params.scope,
url = params.url,
field = params.field,
variable = params.variable,
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'
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'
if (scope[variable]) {
scope[variable].length = 0;
}
else {
} else {
scope[variable] = [];
}
Rest.setUrl(url);
Rest.options()
.success( function(data) {
.success(function (data) {
var choices, i;
choices = (choice_name) ? data.actions.GET[field][choice_name] : data.actions.GET[field].choices;
// including 'name' property so list can be used by search
for (i=0; i < choices.length; i++) {
scope[variable].push({ label: choices[i][1], value: choices[i][0], name: choices[i][1]});
for (i = 0; i < choices.length; i++) {
scope[variable].push({
label: choices[i][1],
value: choices[i][0],
name: choices[i][1]
});
}
if (callback) {
scope.$emit(callback);
}
})
.error( function(data, status) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status });
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get ' + url + '. GET status: ' + status });
});
};
}])
/*
* Search an array of objects, returning the matchting object or null
*
* Find({ list: [], key: "key", val: <key value> });
*/
.factory('Find', [ function(){
return function(params) {
}
])
/*
* Search an array of objects, returning the matchting object or null
*
* Find({ list: [], key: "key", val: <key value> });
*/
.factory('Find', [
function () {
return function (params) {
var list = params.list,
key = params.key,
val = params.val,
found = false, i;
found = false,
i;
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) {
found = true;
break;
@ -554,19 +607,22 @@ angular.module('Utilities',['RestServices', 'Utilities'])
}
return null;
};
}])
}
])
/*
* DeugForm({ form: <form object>, scope: <current scope object> });
*
* Use to log the $pristine and $valid properties of each form element. Helpful when form
* buttons fail to enable/disable properly.
*
*/
.factory('DebugForm', [ function() {
return function(params) {
/*
* DeugForm({ form: <form object>, scope: <current scope object> });
*
* Use to log the $pristine and $valid properties of each form element. Helpful when form
* buttons fail to enable/disable properly.
*
*/
.factory('DebugForm', [
function () {
return function (params) {
var form = params.form,
scope = params.scope, fld;
scope = params.scope,
fld;
for (fld in form.fields) {
if (scope[form.name + '_form'][fld]) {
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 (scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]) {
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 valid: ' + scope[form.name + '_form'].$valid);
};
}])
}
])
/* Store
*
* 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,
* 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.
*
* store(key,value) will store the value using the key
*
* store(key) retrieves the value of the key
*
*/
.factory('Store', ['Empty', function(Empty) {
return function(key, value) {
/* Store
*
* 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,
* 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.
*
* store(key,value) will store the value using the key
*
* store(key) retrieves the value of the key
*
*/
.factory('Store', ['Empty',
function (Empty) {
return function (key, value) {
if (!Empty(value)) {
// Store the value
localStorage[key] = JSON.stringify(value);
}
else if (!Empty(key)) {
} else if (!Empty(key)) {
// Return the value
var val = localStorage[key];
return (!Empty(val)) ? JSON.parse(val) : null;
}
};
}])
}
])
/*
*
* ApplyEllipsis()
*
*/
.factory('ApplyEllipsis', [ function() {
return function(selector) {
/*
*
* ApplyEllipsis()
*
*/
.factory('ApplyEllipsis', [
function () {
return function (selector) {
// Add a hidden element to the DOM. We'll use this to calc the px length of
// our target text.
var tmp = $('#string-test');
@ -625,26 +684,26 @@ angular.module('Utilities',['RestServices', 'Utilities'])
tmp = $('#string-test');
}
// Find and process the text.
$(selector).each(function() {
var setTitle = true, txt, w, pw, cw, df;
$(selector).each(function () {
var setTitle = true,
txt, w, pw, cw, df;
if ($(this).attr('title')) {
txt = $(this).attr('title');
setTitle = false;
}
else {
} else {
txt = $(this).text();
}
tmp.text(txt);
w = tmp.width(); //text width
pw = $(this).parent().width(); //parent width
w = tmp.width(); //text width
pw = $(this).parent().width(); //parent width
if (w > pw) {
// text is wider than parent width
if (setTitle) {
// Save the original text in the title
$(this).attr('title',txt);
$(this).attr('title', txt);
}
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));
$(this).text(txt + '...');
}
@ -654,16 +713,7 @@ angular.module('Utilities',['RestServices', 'Utilities'])
$(this).text(txt);
}
});
};
}]);
}
]);

View File

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

View File

@ -10,6 +10,7 @@
/* global chkPass:false */
angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper'])
// awpassmatch: Add to password_confirm field. Will test if value
// matches that of 'input[name="password"]'
.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';
angular.module('AWFilters', [])
//
// capitalize -capitalize the first letter of each word
//
.filter('capitalize', [ function() {
return function(input) {
var values, result, i;
if (input) {
values = input.replace(/\_/g,' ').split(" ");
result = "";
for (i = 0; i < values.length; i++){
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' ';
.filter('capitalize', [
function () {
return function (input) {
var values, result, i;
if (input) {
values = input.replace(/\_/g, ' ').split(" ");
result = "";
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';
angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog'])
.factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors',
'FormatDate', 'Prompt', 'Empty',
function($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty) {
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'
}
}
};
angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog', 'LicenseFormDefinition'])
.factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors',
'FormatDate', 'Prompt', 'Empty', 'LicenseForm',
function ($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty,
LicenseForm) {
return function () {
var base = $location.path().replace(/^\//,'').split('/')[0];
// Retrieve detail record and prepopulate the form
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];
}
}
}
var defaultUrl = GetBasePath('config'),
generator = GenerateForm,
form = angular.copy(LicenseForm),
scope;
if (data['license_info']['is_aws'] || Empty(data['license_info']['license_date'])) {
delete form.fields['license_date'];
delete form.fields['time_remaining'];
}
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
Rest.get()
.success(function (data) {
var 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 = 'Purchase/Extend License';
scope.formModalHeader = 'Tower License';
var fld, dt, days, remainder, hours, minutes, seconds, license;
for (fld in form.fields) {
if (fld !== 'time_remaining' && fld !== 'license_status') {
if (Empty(data.license_info[fld])) {
delete form.fields[fld];
}
}
}
//$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
//$('#form-modal').addClass('skinny-modal');
// 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');
}
});
}
if (data.license_info.is_aws || Empty(data.license_info.license_date)) {
delete form.fields.license_date;
delete form.fields.time_remaining;
}
for (var fld in form.fields) {
if (!Empty(data['license_info'][fld])) {
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);
scope = generator.inject(form, { mode: 'edit', modal: true, related: false });
generator.reset();
var days = parseInt(scope['time_remaining'] / 86400000);
var remainder = scope['time_remaining'] - (days * 86400000);
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);
}
scope.formModalAction = function () {
$('#form-modal').modal("hide");
};
if (parseInt(scope['free_instances']) <= 0) {
scope['free_instances_class'] = 'field-failure';
}
else {
scope['free_instances_class'] = 'field-success';
}
scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false;
scope.formModalInfo = 'Purchase/Extend License';
scope.formModalHeader = 'Tower License';
var license = data['license_info'];
if (license['valid_key'] !== undefined && license['valid_key'] == false) {
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';
}
//$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
//$('#form-modal').addClass('skinny-modal');
})
.error( function(data, status, headers, config) {
ProcessErrors($rootScope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve license. GET status: ' + status });
});
}
}]);
// 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 (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.
*
* ListGenerator
* ListGenerator
* 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.
*
@ -10,412 +10,439 @@
'use strict';
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',
function($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon,
Breadcrumbs) {
return {
setList: function(list) {
this.list = list;
},
attr: Attr,
function ($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon,
Breadcrumbs) {
return {
icon: Icon,
setList: function (list) {
this.list = list;
},
has: function(key) {
return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false;
},
attr: Attr,
hide: function() {
$('#lookup-modal').modal('hide');
},
icon: Icon,
button: Button,
has: function (key) {
return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false;
},
inject: function(list, options) {
// options.mode = one of edit, select or lookup
//
// 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;
hide: function () {
$('#lookup-modal').modal('hide');
},
if (options.mode === 'lookup') {
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);
button: Button,
// Reset the scope to prevent displaying old data from our last visit to this list
this.scope[list.name] = null;
this.scope[list.iterator] = null;
inject: function (list, options) {
// options.mode = one of edit, select or lookup
//
// 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
$('.tooltip').each( function(index) {
$(this).remove();
});
$('.popover').each(function(index) {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove();
});
$(window).unbind('resize');
try {
$('#help-modal').empty().dialog('destroy');
}
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');
if (options.mode === 'lookup') {
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);
}
});
}
return this.scope;
},
build: function(options) {
//
// Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML
// string to be injected into the current view.
//
var html = '';
var list = this.list;
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'
if (options.activityStream) {
// 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";
}
$compile(element)(this.scope);
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 });
}
}
// Reset the scope to prevent displaying old data from our last visit to this list
this.scope[list.name] = null;
this.scope[list.iterator] = null;
if (options.mode != 'lookup') {
//actions
var 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
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 });
}
}
}
// Remove any lingering tooltip and popover <div> elements
$('.tooltip').each(function() {
$(this).remove();
});
//select instructions
if (options.mode == 'select' && list.selectInstructions) {
var 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 });
}
$('.popover').each(function() {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove();
});
$(window).unbind('resize');
html += "</div><!-- list-acitons -->\n";
html += "</div><!-- list-actions-column -->\n";
}
else {
//lookup
html += "<div class=\"col-lg-7\"></div>\n";
}
try {
$('#help-modal').empty().dialog('destroy');
} catch (e) {
//ignore any errors should the dialog not be initialized
}
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";
}
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');
}
});
}
// 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 (var 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";
return this.scope;
},
build: function (options) {
//
// Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML
// string to be injected into the current view.
//
var html = '',
list = this.list,
base, size, action, btn, fld, cnt, field_action, fAction, itm;
if (options.activityStream) {
// 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)) {
//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)) {
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";
}
html += "\"></i></a>";
if (options.mode !== 'lookup') {
//actions
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.
*
* 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.
* See example at bottom. If user responds with Yes, execute action
* See example at bottom. If user responds with Yes, execute action
* parameter.
*
* params: { hdr: 'header msg',
* params: { hdr: 'header msg',
* body: 'body text/html',
* class: 'btn-class for Yes button', --defaults to btn-danger
* action: function() {} --action to take, if use clicks Yes
@ -17,25 +17,26 @@
'use strict';
angular.module('PromptDialog', ['Utilities'])
.factory('Prompt', ['$rootScope', '$compile', 'Alert', function($rootScope, $compile, Alert) {
return function(params) {
var dialog = angular.element(document.getElementById('prompt-modal'));
var scope = dialog.scope();
scope.promptHeader = params.hdr;
scope.promptBody = params.body;
scope.promptAction = params.action;
var cls = (params['class'] == null || params['class'] == undefined) ? 'btn-danger' : params['class'];
$('#prompt_action_btn').removeClass(cls).addClass(cls);
$(dialog).modal({
backdrop: 'static',
keyboard: true,
show: true
});
.factory('Prompt', [
function () {
return function (params) {
var dialog = angular.element(document.getElementById('prompt-modal')),
scope = dialog.scope(), cls;
scope.promptHeader = params.hdr;
scope.promptBody = params.body;
scope.promptAction = params.action;
cls = (params['class'] === null || params['class'] === undefined) ? 'btn-danger' : params['class'];
$('#prompt_action_btn').removeClass(cls).addClass(cls);
$(dialog).modal({
backdrop: 'static',
keyboard: true,
show: true
});
};
}
}]);
]);

View File

@ -22,205 +22,294 @@
** 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() {
var newstring = "";
for (var s=0; s < this.length; s++) {
newstring = this.charAt(s) + newstring;
}
return newstring;
/*jshint eqeqeq:false, unused:false */
String.prototype.strReverse = function () {
var newstring = "", s;
for (s = 0; s < this.length; s++) {
newstring = this.charAt(s) + newstring;
}
return newstring;
};
var nScore = 0;
function chkPass(pwd) {
// 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*
var nLength=0, nAlphaUC=0, nAlphaLC=0, nNumber=0, nSymbol=0, nMidChar=0, nRequirements=0,
nAlphasOnly=0, nNumbersOnly=0, nUnqChar=0, nRepChar=0, nRepInc=0, nConsecAlphaUC=0, nConsecAlphaLC=0,
nConsecNumber=0, nConsecSymbol=0, nConsecCharType=0, nSeqAlpha=0, nSeqNumber=0, nSeqSymbol=0,
nSeqChar=0, nReqChar=0, nMultConsecCharType=0;
var nMultRepChar=1, nMultConsecSymbol=1;
var nMultMidChar=2, nMultRequirements=2, nMultConsecAlphaUC=2, nMultConsecAlphaLC=2, nMultConsecNumber=2;
var nReqCharType=3, nMultAlphaUC=3, nMultAlphaLC=3, nMultSeqAlpha=3, nMultSeqNumber=3, nMultSeqSymbol=3;
var nMultLength=4, nMultNumber=4;
var nMultSymbol=6;
var nTmpAlphaUC="", nTmpAlphaLC="", nTmpNumber="", nTmpSymbol="";
var sAlphaUC="0", sAlphaLC="0", sNumber="0", sSymbol="0", sMidChar="0", sRequirements="0",
sAlphasOnly="0", sNumbersOnly="0", sRepChar="0", sConsecAlphaUC="0", sConsecAlphaLC="0",
sConsecNumber="0", sSeqAlpha="0", sSeqNumber="0", sSeqSymbol="0";
var sAlphas = "abcdefghijklmnopqrstuvwxyz";
var sNumerics = "01234567890";
var sSymbols = ")_!@#$%^&*()";
var sComplexity = "Too Short";
var sStandards = "Below";
var nMinPwdLen = 8;
if (document.all) { var nd = 0; } else { var nd = 1; }
if (pwd) {
nScore = parseInt(pwd.length * nMultLength);
nLength = pwd.length;
var arrPwd = pwd.replace(/\s+/g,"").split(/\s*/);
var arrPwdLen = arrPwd.length;
/* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */
for (var 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 */
var bCharExists = false;
for (var b=0; b < arrPwdLen; b++) {
if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */
bCharExists = true;
/*
// 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*
var nLength = 0,
nAlphaUC = 0,
nAlphaLC = 0,
nNumber = 0,
nSymbol = 0,
nMidChar = 0,
nRequirements = 0,
nAlphasOnly = 0,
nNumbersOnly = 0,
nUnqChar = 0,
nRepChar = 0,
nRepInc = 0,
nConsecAlphaUC = 0,
nConsecAlphaLC = 0,
nConsecNumber = 0,
nConsecSymbol = 0,
nConsecCharType = 0,
nSeqAlpha = 0,
nSeqNumber = 0,
nSeqSymbol = 0,
nSeqChar = 0,
nReqChar = 0,
nMultConsecCharType = 0,
nMultRepChar = 1,
nMultConsecSymbol = 1,
nMultMidChar = 2,
nMultRequirements = 2,
nMultConsecAlphaUC = 2,
nMultConsecAlphaLC = 2,
nMultConsecNumber = 2,
nReqCharType = 3,
nMultAlphaUC = 3,
nMultAlphaLC = 3,
nMultSeqAlpha = 3,
nMultSeqNumber = 3,
nMultSeqSymbol = 3,
nMultLength = 4,
nMultNumber = 4,
nMultSymbol = 6,
nTmpAlphaUC = "",
nTmpAlphaLC = "",
nTmpNumber = "",
nTmpSymbol = "",
sAlphaUC = "0",
sAlphaLC = "0",
sNumber = "0",
sSymbol = "0",
sMidChar = "0",
sRequirements = "0",
sAlphasOnly = "0",
sNumbersOnly = "0",
sRepChar = "0",
sConsecAlphaUC = "0",
sConsecAlphaLC = "0",
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
Deduction is incremented each time a new match is discovered
Deduction amount is based on total password length divided by the
difference of distance between currently selected match
*/
nRepInc += Math.abs(arrPwdLen/(b-a));
nRepInc += Math.abs(arrPwdLen / (b - a));
}
}
}
if (bCharExists) {
nRepChar++;
nUnqChar = arrPwdLen-nRepChar;
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 */
if (bCharExists) {
nRepChar++;
nUnqChar = arrPwdLen - nRepChar;
nRepInc = (nUnqChar) ? Math.ceil(nRepInc / nUnqChar) : Math.ceil(nRepInc);
}
}
/* 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);
}
/* Check for sequential alpha string patterns (forward and reverse) */
for (s = 0; s < 23; s++) {
sFwd = sAlphas.substring(s, parseInt(s + 3));
sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
nSeqAlpha++;
nSeqChar++;
}
}
/* Point deductions for poor practices */
if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters
nScore = parseInt(nScore - nLength);
nAlphasOnly = nLength;
sAlphasOnly = "- " + nLength;
}
if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers
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);
}
/* Check for sequential numeric string patterns (forward and reverse) */
for (s = 0; s < 8; s++) {
sFwd = sNumerics.substring(s, parseInt(s + 3));
sRev = sFwd.strReverse();
if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) {
nSeqNumber++;
nSeqChar++;
}
}
/* Determine complexity based on overall score */
if (nScore > 100) { nScore = 100; } else if (nScore < 0) { nScore = 0; }
/* Check for sequential symbol string patterns (forward and reverse) */
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");
var required_strength = $AnsibleConfig.password_strength;
var warning_level = ($AnsibleConfig.password_strength - 15 < 0) ? 0 : $AnsibleConfig.password_strength - 15;
/* Modify overall score value based on usage vs requirements */
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) {
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 */
var progbar = $("#progbar");
progbar.css("width", '0%');
progbar.removeClass('progress-bar-success progress-bar-warning')
}
return nScore;
/* Point deductions for poor practices */
if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters
nScore = parseInt(nScore - nLength);
nAlphasOnly = nLength;
sAlphasOnly = "- " + nLength;
}
if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers
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 */
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/Permissions.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/InventoryStatus.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/LicenseForm.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/Admins.js"></script>
@ -123,7 +124,6 @@
<script src="{{ STATIC_URL }}js/widgets/ObjectCount.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/InventoryHosts.js"></script>
<script src="{{ STATIC_URL }}lib/less/less-1.4.1.min.js"></script>
{% 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"
}
}