diff --git a/ansibleworks/ui/static/css/ansible-ui.css b/ansibleworks/ui/static/css/ansible-ui.css index 097d5b3c61..49ef34b97f 100644 --- a/ansibleworks/ui/static/css/ansible-ui.css +++ b/ansibleworks/ui/static/css/ansible-ui.css @@ -279,8 +279,12 @@ color: #778899; } - .job-pending, .job-running, .job-success, - input[type="text"].job-success + .job-pending, + .job-running, + .job-success, + .job-successful, + input[type="text"].job-success, + input[type="text"].job-successful { color: #5bb75b; } diff --git a/ansibleworks/ui/static/js/app.js b/ansibleworks/ui/static/js/app.js index 1871b995d5..bd1aa103d4 100644 --- a/ansibleworks/ui/static/js/app.js +++ b/ansibleworks/ui/static/js/app.js @@ -45,6 +45,7 @@ angular.module('ansible', [ 'JobTemplateFormDefinition', 'JobTemplateHelper', 'ProjectsListDefinition', + 'ProjectFormDefinition', 'JobsListDefinition', 'JobFormDefinition', 'JobEventsListDefinition', @@ -76,6 +77,12 @@ angular.module('ansible', [ when('/projects', { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsList }). + when('/projects/add', + { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsAdd }). + + when('/projects/:id', + { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsEdit }). + when('/inventories', { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesList }). diff --git a/ansibleworks/ui/static/js/controllers/Projects.js b/ansibleworks/ui/static/js/controllers/Projects.js index bfd6a05189..3676aefc79 100644 --- a/ansibleworks/ui/static/js/controllers/Projects.js +++ b/ansibleworks/ui/static/js/controllers/Projects.js @@ -30,15 +30,15 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, LoadBreadCrumbs(); - scope.addCredential = function() { + scope.addProject = function() { $location.path($location.path() + '/add'); } - scope.editCredential = function(id) { + scope.editProject = function(id) { $location.path($location.path() + '/' + id); } - scope.deleteCredential = function(id, name) { + scope.deleteProject = function(id, name) { var action = function() { var url = defaultUrl + id + '/'; @@ -138,4 +138,176 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath' ]; \ No newline at end of file + 'GetBasePath' ]; + + +function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, + GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, + GetBasePath, ReturnToCaller) +{ + ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior + //scope. + + // Inject dynamic view + var form = ProjectsForm; + var generator = GenerateForm; + var defaultUrl = GetBasePath('projects'); + var scope = generator.inject(form, {mode: 'add', related: false}); + var id = $routeParams.id; + generator.reset(); + + LoadBreadCrumbs(); + + // Save + scope.formSave = function() { + + var data = {}; + for (var fld in form.fields) { + data[fld] = scope[fld]; + } + + Rest.setUrl(defaultUrl); + Rest.post(data) + .success( function(data, status, headers, config) { + ReturnToCaller(); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, ProjectsForm, + { hdr: 'Error!', msg: 'Failed to add new project. Post returned status: ' + status }); + }); + }; + + // Cancel + scope.formReset = function() { + $rootScope.flashMessage = null; + form.reset(); + }; +} + +ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm', + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', + 'ReturnToCaller' + ]; + + +function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, + GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, + RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller) +{ + ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior + //scope. + + // Inject dynamic view + var form = ProjectsForm; + var generator = GenerateForm; + var scope = generator.inject(form, {mode: 'edit', related: true}); + generator.reset(); + + var defaultUrl = GetBasePath('projects') + $routeParams.id + '/'; + var base = $location.path().replace(/^\//,'').split('/')[0]; + var master = {}; + var id = $routeParams.id; + var relatedSets = {}; + + // After the Organization is loaded, retrieve each related set + if (scope.projectLoadedRemove) { + scope.projectLoadedRemove(); + } + scope.projectLoadedRemove = scope.$on('projectLoaded', function() { + for (var set in relatedSets) { + scope.search(relatedSets[set].iterator); + } + }); + + // Retrieve detail record and prepopulate the form + Rest.setUrl(defaultUrl); + Rest.get({ params: {id: id} }) + .success( function(data, status, headers, config) { + LoadBreadCrumbs({ path: '/projects/' + id, title: data.name }); + for (var fld in form.fields) { + if (data[fld]) { + scope[fld] = data[fld]; + master[fld] = data[fld]; + } + } + var related = data.related; + for (var set in form.related) { + if (related[set]) { + relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; + } + } + // Initialize related search functions. Doing it here to make sure relatedSets object is populated. + RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets }); + RelatedPaginateInit({ scope: scope, relatedSets: relatedSets }); + scope.$emit('projectLoaded'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to retrieve project: ' + id + '. GET status: ' + status }); + }); + + + // Save changes to the parent + scope.formSave = function() { + var params = {}; + for (var fld in form.fields) { + params[fld] = scope[fld]; + } + Rest.setUrl(defaultUrl); + Rest.put(params) + .success( function(data, status, headers, config) { + ReturnToCaller(); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, OrganizationForm, + { hdr: 'Error!', msg: 'Failed to update project: ' + id + '. PUT status: ' + status }); + }); + }; + + // Reset the form + scope.formReset = function() { + form.reset(); + for (var fld in master) { + scope[fld] = master[fld]; + } + }; + + // Related set: Add button + scope.add = function(set) { + $location.path('/' + base + '/' + $routeParams.id + '/' + set); + }; + + // Related set: Edit button + scope.edit = function(set, id, name) { + $location.path('/' + base + '/' + $routeParams.id + '/' + set + '/' + id); + }; + + // Related set: Delete button + scope.delete = function(set, itm_id, name, title) { + var action = function() { + var url = GetBasePath('projects') + id + '/' + set + '/'; + Rest.setUrl(url); + Rest.post({ id: itm_id, disassociate: 1 }) + .success( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + scope.search(form.related[set].iterator); + }) + .error( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); + }); + }; + + Prompt({ hdr: 'Delete', + body: 'Are you sure you want to remove ' + name + ' from ' + scope.name + ' ' + title + '?', + action: action + }); + + } +} + +ProjectsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm', + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', + 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller' + ]; diff --git a/ansibleworks/ui/static/js/controllers/Teams.js b/ansibleworks/ui/static/js/controllers/Teams.js index 9494a09d54..b9ca3fdebe 100644 --- a/ansibleworks/ui/static/js/controllers/Teams.js +++ b/ansibleworks/ui/static/js/controllers/Teams.js @@ -241,7 +241,6 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, Rest.get({ params: {id: id} }) .success( function(data, status, headers, config) { LoadBreadCrumbs({ path: '/teams/' + id, title: data.name }); - console.log(data); for (var fld in form.fields) { if (data[fld]) { scope[fld] = data[fld]; diff --git a/ansibleworks/ui/static/js/forms/Projects.js b/ansibleworks/ui/static/js/forms/Projects.js new file mode 100644 index 0000000000..aa78a5bde3 --- /dev/null +++ b/ansibleworks/ui/static/js/forms/Projects.js @@ -0,0 +1,62 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * Projects.js + * + * 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/ + + fields: { + name: { + label: 'Name', + type: 'text', + addRequired: true, + editRequired: true, + capitalize: true + }, + description: { + label: 'Description', + type: 'text', + addRequired: false, + editRequired: false + }, + local_path: { + label: 'Local Path', + type: 'text', + addRequired: true, + editRequired: true + } + }, + + buttons: { //for now always generates