From 7d23160c1add4cbde6d58225a737a4145af220f3 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 23 Feb 2017 15:53:33 -0500 Subject: [PATCH] Modularized what was left in the controllers directory --- awx/ui/client/src/app.js | 187 +---- awx/ui/client/src/controllers/Credentials.js | 631 --------------- awx/ui/client/src/controllers/Home.js | 142 ---- awx/ui/client/src/controllers/JobEvents.js | 356 --------- awx/ui/client/src/controllers/JobHosts.js | 121 --- awx/ui/client/src/controllers/Projects.js | 749 ------------------ awx/ui/client/src/controllers/Schedules.js | 86 -- awx/ui/client/src/controllers/Sockets.js | 119 --- awx/ui/client/src/controllers/Teams.js | 247 ------ awx/ui/client/src/controllers/Users.js | 357 --------- .../add/credentials-add.controller.js | 177 +++++ .../edit/credentials-edit.controller.js | 346 ++++++++ .../list/credentials-list.controller.js | 106 +++ awx/ui/client/src/credentials/main.js | 40 +- .../counts/dashboard-counts.block.less | 2 +- .../counts/dashboard-counts.directive.js | 2 +- .../counts/dashboard-counts.partial.html | 0 .../src/{ => home}/dashboard/counts/main.js | 0 .../{ => home}/dashboard/dashboard.block.less | 2 +- .../dashboard/dashboard.directive.js | 2 +- .../dashboard/dashboard.partial.html | 0 .../graphs/dashboard-graphs.block.less | 2 +- .../graphs/dashboard-graphs.directive.js | 2 +- .../graphs/dashboard-graphs.partial.html | 0 .../adjust-graph-size.service.js | 0 .../graph-helpers/auto-size.directive.js | 0 .../dashboard/graphs/graph-helpers/main.js | 0 .../job-status/job-status-graph.directive.js | 2 +- .../job-status/job-status-graph.service.js | 0 .../job-status/job_status_graph.partial.html | 0 .../dashboard/graphs/job-status/main.js | 2 +- .../src/{ => home}/dashboard/graphs/main.js | 0 .../hosts/dashboard-hosts-edit.controller.js | 0 .../hosts/dashboard-hosts-list.controller.js | 0 .../dashboard/hosts/dashboard-hosts.form.js | 0 .../dashboard/hosts/dashboard-hosts.list.js | 0 .../hosts/dashboard-hosts.service.js | 0 .../src/{ => home}/dashboard/hosts/main.js | 2 +- .../dashboard/lists/dashboard-list.block.less | 2 +- .../job-templates-list.directive.js | 2 +- .../job-templates-list.partial.html | 0 .../dashboard/lists/job-templates/main.js | 4 +- .../lists/jobs/jobs-list.directive.js | 2 +- .../lists/jobs/jobs-list.partial.html | 0 .../{ => home}/dashboard/lists/jobs/main.js | 0 .../src/{ => home}/dashboard/lists/main.js | 0 .../client/src/{ => home}/dashboard/main.js | 0 awx/ui/client/src/home/home.controller.js | 125 +++ .../home.html => home/home.partial.html} | 1 - awx/ui/client/src/home/home.route.js | 46 ++ awx/ui/client/src/home/main.js | 12 + .../projects/add/projects-add.controller.js | 154 ++++ .../projects/edit/projects-edit.controller.js | 297 +++++++ .../projects/list/projects-list.controller.js | 299 +++++++ awx/ui/client/src/projects/main.js | 51 ++ .../src/teams/add/teams-add.controller.js | 68 ++ .../src/teams/edit/teams-edit.controller.js | 97 +++ .../src/teams/list/teams-list.controller.js | 81 ++ awx/ui/client/src/teams/main.js | 47 ++ .../src/users/add/users-add.controller.js | 119 +++ .../src/users/edit/users-edit.controller.js | 179 +++++ .../src/users/list/users-list.controller.js | 90 +++ awx/ui/client/src/users/main.js | 47 ++ 63 files changed, 2404 insertions(+), 3001 deletions(-) delete mode 100644 awx/ui/client/src/controllers/Credentials.js delete mode 100644 awx/ui/client/src/controllers/Home.js delete mode 100644 awx/ui/client/src/controllers/JobEvents.js delete mode 100644 awx/ui/client/src/controllers/JobHosts.js delete mode 100644 awx/ui/client/src/controllers/Projects.js delete mode 100644 awx/ui/client/src/controllers/Schedules.js delete mode 100644 awx/ui/client/src/controllers/Sockets.js delete mode 100644 awx/ui/client/src/controllers/Teams.js delete mode 100644 awx/ui/client/src/controllers/Users.js create mode 100644 awx/ui/client/src/credentials/add/credentials-add.controller.js create mode 100644 awx/ui/client/src/credentials/edit/credentials-edit.controller.js create mode 100644 awx/ui/client/src/credentials/list/credentials-list.controller.js rename awx/ui/client/src/{ => home}/dashboard/counts/dashboard-counts.block.less (97%) rename awx/ui/client/src/{ => home}/dashboard/counts/dashboard-counts.directive.js (97%) rename awx/ui/client/src/{ => home}/dashboard/counts/dashboard-counts.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/counts/main.js (100%) rename awx/ui/client/src/{ => home}/dashboard/dashboard.block.less (93%) rename awx/ui/client/src/{ => home}/dashboard/dashboard.directive.js (80%) rename awx/ui/client/src/{ => home}/dashboard/dashboard.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/dashboard-graphs.block.less (98%) rename awx/ui/client/src/{ => home}/dashboard/graphs/dashboard-graphs.directive.js (86%) rename awx/ui/client/src/{ => home}/dashboard/graphs/dashboard-graphs.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/graph-helpers/adjust-graph-size.service.js (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/graph-helpers/auto-size.directive.js (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/graph-helpers/main.js (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/job-status/job-status-graph.directive.js (98%) rename awx/ui/client/src/{ => home}/dashboard/graphs/job-status/job-status-graph.service.js (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/job-status/job_status_graph.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/graphs/job-status/main.js (86%) rename awx/ui/client/src/{ => home}/dashboard/graphs/main.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/dashboard-hosts-edit.controller.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/dashboard-hosts-list.controller.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/dashboard-hosts.form.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/dashboard-hosts.list.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/dashboard-hosts.service.js (100%) rename awx/ui/client/src/{ => home}/dashboard/hosts/main.js (98%) rename awx/ui/client/src/{ => home}/dashboard/lists/dashboard-list.block.less (98%) rename awx/ui/client/src/{ => home}/dashboard/lists/job-templates/job-templates-list.directive.js (97%) rename awx/ui/client/src/{ => home}/dashboard/lists/job-templates/job-templates-list.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/lists/job-templates/main.js (64%) rename awx/ui/client/src/{ => home}/dashboard/lists/jobs/jobs-list.directive.js (94%) rename awx/ui/client/src/{ => home}/dashboard/lists/jobs/jobs-list.partial.html (100%) rename awx/ui/client/src/{ => home}/dashboard/lists/jobs/main.js (100%) rename awx/ui/client/src/{ => home}/dashboard/lists/main.js (100%) rename awx/ui/client/src/{ => home}/dashboard/main.js (100%) create mode 100644 awx/ui/client/src/home/home.controller.js rename awx/ui/client/src/{partials/home.html => home/home.partial.html} (99%) create mode 100644 awx/ui/client/src/home/home.route.js create mode 100644 awx/ui/client/src/home/main.js create mode 100644 awx/ui/client/src/projects/add/projects-add.controller.js create mode 100644 awx/ui/client/src/projects/edit/projects-edit.controller.js create mode 100644 awx/ui/client/src/projects/list/projects-list.controller.js create mode 100644 awx/ui/client/src/projects/main.js create mode 100644 awx/ui/client/src/teams/add/teams-add.controller.js create mode 100644 awx/ui/client/src/teams/edit/teams-edit.controller.js create mode 100644 awx/ui/client/src/teams/list/teams-list.controller.js create mode 100644 awx/ui/client/src/teams/main.js create mode 100644 awx/ui/client/src/users/add/users-add.controller.js create mode 100644 awx/ui/client/src/users/edit/users-edit.controller.js create mode 100644 awx/ui/client/src/users/list/users-list.controller.js create mode 100644 awx/ui/client/src/users/main.js diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 70efcfa1c8..610dcc31c2 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -41,9 +41,6 @@ import './helpers'; import './lists'; import './widgets'; import './filters'; -import { Home } from './controllers/Home'; -import { SocketsController } from './controllers/Sockets'; -import { CredentialsAdd, CredentialsEdit, CredentialsList } from './controllers/Credentials'; import portalMode from './portal-mode/main'; import systemTracking from './system-tracking/main'; import inventories from './inventories/main'; @@ -62,7 +59,7 @@ import mainMenu from './main-menu/main'; import breadCrumb from './bread-crumb/main'; import browserData from './browser-data/main'; import configuration from './configuration/main'; -import dashboard from './dashboard/main'; +import home from './home/main'; import moment from './shared/moment/main'; import login from './login/main'; import activityStream from './activity-stream/main'; @@ -70,9 +67,9 @@ import standardOut from './standard-out/main'; import Templates from './templates/main'; import credentials from './credentials/main'; import jobs from './jobs/main'; -import { ProjectsList, ProjectsAdd, ProjectsEdit } from './controllers/Projects'; -import { UsersList, UsersAdd, UsersEdit } from './controllers/Users'; -import { TeamsList, TeamsAdd, TeamsEdit } from './controllers/Teams'; +import teams from './teams/main'; +import users from './users/main'; +import projects from './projects/main'; import RestServices from './rest/main'; import access from './access/main'; @@ -85,7 +82,6 @@ import config from './shared/config/main'; import './login/authenticationServices/pendo/ng-pendo'; import footer from './footer/main'; import scheduler from './scheduler/main'; -import { N_ } from './i18n'; var tower = angular.module('Tower', [ // how to add CommonJS / AMD third-party dependencies: @@ -119,7 +115,7 @@ var tower = angular.module('Tower', [ setupMenu.name, mainMenu.name, breadCrumb.name, - dashboard.name, + home.name, moment.name, login.name, activityStream.name, @@ -135,6 +131,9 @@ var tower = angular.module('Tower', [ config.name, credentials.name, jobs.name, + teams.name, + users.name, + projects.name, //'templates', 'Utilities', 'OrganizationFormDefinition', @@ -227,10 +226,9 @@ var tower = angular.module('Tower', [ }); }]) .config(['$urlRouterProvider', '$breadcrumbProvider', 'QuerySetProvider', - '$urlMatcherFactoryProvider', 'stateDefinitionsProvider', '$stateProvider', + '$urlMatcherFactoryProvider', function($urlRouterProvider, $breadcrumbProvider, QuerySet, - $urlMatcherFactoryProvider, stateDefinitionsProvider, $stateProvider) { - let stateDefinitions = stateDefinitionsProvider.$get(); + $urlMatcherFactoryProvider) { $urlMatcherFactoryProvider.strictMode(false); $breadcrumbProvider.setOptions({ templateUrl: urlPrefix + 'partials/breadcrumb.html' @@ -266,109 +264,6 @@ var tower = angular.module('Tower', [ // $stateProvider.stateRegistry.onStatesChanged((event, states) =>{ // console.log(event, states) // }) - - - // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry - // see: stateDefinition.factory for usage documentation - $stateProvider.state({ - name: 'projects', - url: '/projects', - lazyLoad: () => stateDefinitions.generateTree({ - parent: 'projects', // top-most node in the generated tree (will replace this state definition) - modes: ['add', 'edit'], - list: 'ProjectList', - form: 'ProjectsForm', - controllers: { - list: ProjectsList, // DI strings or objects - add: ProjectsAdd, - edit: ProjectsEdit - }, - data: { - activityStream: true, - activityStreamTarget: 'project', - socket: { - "groups": { - "jobs": ["status_changed"] - } - } - }, - ncyBreadcrumb: { - label: N_('PROJECTS') - } - }) - }); - - $stateProvider.state({ - name: 'credentials', - url: '/credentials', - lazyLoad: () => stateDefinitions.generateTree({ - parent: 'credentials', - modes: ['add', 'edit'], - list: 'CredentialList', - form: 'CredentialForm', - controllers: { - list: CredentialsList, - add: CredentialsAdd, - edit: CredentialsEdit - }, - data: { - activityStream: true, - activityStreamTarget: 'credential' - }, - ncyBreadcrumb: { - parent: 'setup', - label: N_('CREDENTIALS') - } - }) - }); - - $stateProvider.state({ - name: 'teams', - url: '/teams', - lazyLoad: () => stateDefinitions.generateTree({ - parent: 'teams', - modes: ['add', 'edit'], - list: 'TeamList', - form: 'TeamForm', - controllers: { - list: TeamsList, - add: TeamsAdd, - edit: TeamsEdit - }, - data: { - activityStream: true, - activityStreamTarget: 'team' - }, - ncyBreadcrumb: { - parent: 'setup', - label: N_('TEAMS') - } - }) - }); - - $stateProvider.state({ - name: 'users', - url: '/users', - lazyLoad: () => stateDefinitions.generateTree({ - parent: 'users', - modes: ['add', 'edit'], - list: 'UserList', - form: 'UserForm', - controllers: { - list: UsersList, - add: UsersAdd, - edit: UsersEdit - }, - data: { - activityStream: true, - activityStreamTarget: 'user' - }, - ncyBreadcrumb: { - parent: 'setup', - label: N_('USERS') - } - }) - }); } ]) .run(['$stateExtender', '$q', '$compile', '$cookieStore', '$rootScope', '$log', '$stateParams', @@ -392,68 +287,6 @@ var tower = angular.module('Tower', [ $log.debug(`$state.defaultErrorHandler: ${error}`); }); - $stateExtender.addState({ - name: 'dashboard', - url: '/home', - templateUrl: urlPrefix + 'partials/home.html', - controller: Home, - params: { licenseMissing: null }, - data: { - activityStream: true, - refreshButton: true, - socket: { - "groups": { - "jobs": ["status_changed"] - } - }, - }, - ncyBreadcrumb: { - label: N_("DASHBOARD") - }, - resolve: { - graphData: ['$q', 'jobStatusGraphData', '$rootScope', - function($q, jobStatusGraphData, $rootScope) { - return $rootScope.featuresConfigured.promise.then(function() { - return $q.all({ - jobStatus: jobStatusGraphData.get("month", "all"), - }); - }); - } - ] - } - }); - - $stateExtender.addState({ - name: 'userCredentials', - url: '/users/:user_id/credentials', - templateUrl: urlPrefix + 'partials/users.html', - controller: CredentialsList - }); - - $stateExtender.addState({ - name: 'userCredentialAdd', - url: '/users/:user_id/credentials/add', - templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsAdd - }); - - $stateExtender.addState({ - name: 'teamUserCredentialEdit', - url: '/teams/:user_id/credentials/:credential_id', - templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsEdit - }); - - $stateExtender.addState({ - name: 'sockets', - url: '/sockets', - templateUrl: urlPrefix + 'partials/sockets.html', - controller: SocketsController, - ncyBreadcrumb: { - label: N_('SOCKETS') - } - }); - function activateTab() { // Make the correct tab active var base = $location.path().replace(/^\//, '').split('/')[0]; diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js deleted file mode 100644 index 3c06b69182..0000000000 --- a/awx/ui/client/src/controllers/Credentials.js +++ /dev/null @@ -1,631 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Credentials - * @description This controller's for the credentials page - */ - - -export function CredentialsList($scope, $rootScope, $location, $log, - $stateParams, Rest, Alert, CredentialList, Prompt, ClearScope, - ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset, - i18n) { - - ClearScope(); - - var list = CredentialList, - defaultUrl = GetBasePath('credentials'); - - init(); - - function init() { - rbacUiControlService.canAdd('credentials') - .then(function(canAdd) { - $scope.canAdd = canAdd; - }); - - // search init - $scope.list = list; - $scope[`${list.iterator}_dataset`] = Dataset.data; - $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - - $scope.selected = []; - } - - $scope.$on(`${list.iterator}_options`, function(event, data){ - $scope.options = data.data.actions.GET; - optionsRequestDataProcessing(); - }); - - $scope.$watchCollection(`${$scope.list.name}`, function() { - optionsRequestDataProcessing(); - }); - - // iterate over the list and add fields like type label, after the - // OPTIONS request returns, or the list is sorted/paginated/searched - function optionsRequestDataProcessing(){ - if ($scope[list.name] !== undefined) { - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; - - // Set the item type label - if (list.fields.kind && $scope.options && - $scope.options.hasOwnProperty('kind')) { - $scope.options.kind.choices.every(function(choice) { - if (choice[0] === item.kind) { - itm.kind_label = choice[1]; - return false; - } - return true; - }); - } - }); - } - } - - $scope.addCredential = function() { - $state.go('credentials.add'); - }; - - $scope.editCredential = function(id) { - $state.go('credentials.edit', { credential_id: id }); - }; - - $scope.deleteCredential = function(id, name) { - var action = function() { - $('#prompt-modal').modal('hide'); - Wait('start'); - var url = defaultUrl + id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success(function() { - if (parseInt($state.params.credential_id) === id) { - $state.go("^", null, { reload: true }); - } else { - $state.go('.', null, {reload: true}); - } - Wait('stop'); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status - }); - }); - }; - - Prompt({ - hdr: i18n._('Delete'), - body: '
' + i18n._('Are you sure you want to delete the credential below?') + '
' + $filter('sanitize')(name) + '
', - action: action, - actionText: i18n._('DELETE') - }); - }; -} - -CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log', - '$stateParams', 'Rest', 'Alert', 'CredentialList', 'Prompt', 'ClearScope', - 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n' -]; - - -export function CredentialsAdd($scope, $rootScope, $compile, $location, $log, - $stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors, - ClearScope, GetBasePath, GetChoices, Empty, KindChange, BecomeMethodChange, - OwnerChange, FormSave, $state, CreateSelect2, i18n) { - ClearScope(); - - // Inject dynamic view - var form = CredentialForm, - defaultUrl = GetBasePath('credentials'), - url; - - init(); - - function init() { - // Load the list of options for Kind - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'kind', - variable: 'credential_kind_options' - }); - - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'become_method', - variable: 'become_options' - }); - - CreateSelect2({ - element: '#credential_become_method', - multiple: false - }); - - CreateSelect2({ - element: '#credential_kind', - multiple: false - }); - - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); - - $scope.keyEntered = false; - $scope.permissionsTooltip = i18n._('Please save before assigning permissions'); - - // determine if the currently logged-in user may share this credential - // previous commentary said: "$rootScope.current_user isn't available because a call to the config endpoint hasn't finished resolving yet" - // I'm 99% sure this state's will never resolve block will be rejected if setup surrounding config endpoint hasn't completed - if ($rootScope.current_user && $rootScope.current_user.is_superuser) { - $scope.canShareCredential = true; - } else { - Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`); - Rest.get() - .success(function(data) { - $scope.canShareCredential = (data.count) ? true : false; - }).error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status }); - }); - } - } - - if (!Empty($stateParams.user_id)) { - // Get the username based on incoming route - $scope.owner = 'user'; - $scope.user = $stateParams.user_id; - OwnerChange({ scope: $scope }); - url = GetBasePath('users') + $stateParams.user_id + '/'; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - $scope.user_username = data.username; - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status }); - }); - } else if (!Empty($stateParams.team_id)) { - // Get the username based on incoming route - $scope.owner = 'team'; - $scope.team = $stateParams.team_id; - OwnerChange({ scope: $scope }); - url = GetBasePath('teams') + $stateParams.team_id + '/'; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - $scope.team_name = data.name; - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status }); - }); - } else { - // default type of owner to a user - $scope.owner = 'user'; - OwnerChange({ scope: $scope }); - } - - $scope.$watch("ssh_key_data", function(val) { - if (val === "" || val === null || val === undefined) { - $scope.keyEntered = false; - $scope.ssh_key_unlock_ask = false; - $scope.ssh_key_unlock = ""; - } else { - $scope.keyEntered = true; - } - }); - - // Handle Kind change - $scope.kindChange = function() { - KindChange({ scope: $scope, form: form, reset: true }); - }; - - $scope.becomeMethodChange = function() { - BecomeMethodChange({ scope: $scope }); - }; - - // Save - $scope.formSave = function() { - if ($scope[form.name + '_form'].$valid) { - FormSave({ scope: $scope, mode: 'add' }); - } - }; - - $scope.formCancel = function() { - $state.go('credentials'); - }; - - // Password change - $scope.clearPWConfirm = function(fld) { - // If password value changes, make sure password_confirm must be re-entered - $scope[fld] = ''; - $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); - }; - - // Respond to 'Ask at runtime?' checkbox - $scope.ask = function(fld, associated) { - if ($scope[fld + '_ask']) { - $scope[fld] = 'ASK'; - $("#" + form.name + "_" + fld + "_input").attr("type", "text"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); - if (associated !== "undefined") { - $("#" + form.name + "_" + fld + "_input").attr("type", "password"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - } - } else { - $scope[fld] = ''; - $("#" + form.name + "_" + fld + "_input").attr("type", "password"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); - if (associated !== "undefined") { - $("#" + form.name + "_" + fld + "_input").attr("type", "text"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - } - } - }; - - // Click clear button - $scope.clear = function(fld, associated) { - $scope[fld] = ''; - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - $scope[form.name + '_form'].$setDirty(); - }; - -} - -CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', - '$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert', - 'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'BecomeMethodChange', - 'OwnerChange', 'FormSave', '$state', 'CreateSelect2', 'i18n' -]; - -export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, - $stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt, - GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, FormSave, Wait, - $state, CreateSelect2, Authorization, i18n) { - - ClearScope(); - - var defaultUrl = GetBasePath('credentials'), - form = CredentialForm, - base = $location.path().replace(/^\//, '').split('/')[0], - master = {}, - id = $stateParams.credential_id; - - init(); - - function init() { - $scope.id = id; - $scope.$watch('credential_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } - }); - - $scope.canShareCredential = false; - Wait('start'); - if (!$rootScope.current_user) { - Authorization.restoreUserInfo(); - } - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'kind', - variable: 'credential_kind_options', - callback: 'choicesReadyCredential' - }); - - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'become_method', - variable: 'become_options' - }); - - if ($rootScope.current_user && $rootScope.current_user.is_superuser) { - $scope.canShareCredential = true; - } else { - Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`); - Rest.get() - .success(function(data) { - $scope.canShareCredential = (data.count) ? true : false; - Wait('stop'); - }).error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status }); - }); - } - - $scope.$watch('organization', function(val) { - if (val === undefined) { - $scope.permissionsTooltip = i18n._('Credentials are only shared within an organization. Assign credentials to an organization to delegate credential permissions. The organization cannot be edited after credentials are assigned.'); - } else { - $scope.permissionsTooltip = ''; - } - }); - - setAskCheckboxes(); - OwnerChange({ scope: $scope }); - $scope.$watch("ssh_key_data", function(val) { - if (val === "" || val === null || val === undefined) { - $scope.keyEntered = false; - $scope.ssh_key_unlock_ask = false; - $scope.ssh_key_unlock = ""; - } else { - $scope.keyEntered = true; - } - }); - } - - function setAskCheckboxes() { - var fld, i; - for (fld in form.fields) { - if (form.fields[fld].type === 'sensitive' && $scope[fld] === 'ASK') { - // turn on 'ask' checkbox for password fields with value of 'ASK' - $("#" + form.name + "_" + fld + "_input").attr("type", "text"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); - $("#" + fld + "-clear-btn").attr("disabled", "disabled"); - $scope[fld + '_ask'] = true; - } else { - $scope[fld + '_ask'] = false; - $("#" + fld + "-clear-btn").removeAttr("disabled"); - } - master[fld + '_ask'] = $scope[fld + '_ask']; - } - - // Set kind field to the correct option - for (i = 0; i < $scope.credential_kind_options.length; i++) { - if ($scope.kind === $scope.credential_kind_options[i].value) { - $scope.kind = $scope.credential_kind_options[i]; - break; - } - } - } - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReadyCredential', function() { - // Retrieve detail record and prepopulate the form - Rest.setUrl(defaultUrl + ':id/'); - Rest.get({ params: { id: id } }) - .success(function(data) { - if (data && data.summary_fields && - data.summary_fields.organization && - data.summary_fields.organization.id) { - $scope.needsRoleList = true; - } else { - $scope.needsRoleList = false; - } - - $scope.credential_name = data.name; - - var i, fld; - - - for (fld in form.fields) { - if (data[fld] !== null && data[fld] !== undefined) { - $scope[fld] = data[fld]; - master[fld] = $scope[fld]; - } - if (form.fields[fld].type === 'lookup' && 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] = - $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]; - } - } - - if (!Empty($scope.user)) { - $scope.owner = 'user'; - } else { - $scope.owner = 'team'; - } - master.owner = $scope.owner; - - for (i = 0; i < $scope.become_options.length; i++) { - if ($scope.become_options[i].value === data.become_method) { - $scope.become_method = $scope.become_options[i]; - break; - } - } - - if ($scope.become_method && $scope.become_method.value === "") { - $scope.become_method = null; - } - master.become_method = $scope.become_method; - - $scope.$watch('become_method', function(val) { - if (val !== null) { - if (val.value === "") { - $scope.become_username = ""; - $scope.become_password = ""; - } - } - }); - - for (i = 0; i < $scope.credential_kind_options.length; i++) { - if ($scope.credential_kind_options[i].value === data.kind) { - $scope.kind = $scope.credential_kind_options[i]; - break; - } - } - - KindChange({ - scope: $scope, - form: form, - reset: false - }); - - master.kind = $scope.kind; - - CreateSelect2({ - element: '#credential_become_method', - multiple: false - }); - - CreateSelect2({ - element: '#credential_kind', - multiple: false - }); - - switch (data.kind) { - case 'aws': - $scope.access_key = data.username; - $scope.secret_key = data.password; - master.access_key = $scope.access_key; - master.secret_key = $scope.secret_key; - break; - case 'ssh': - $scope.ssh_password = data.password; - master.ssh_password = $scope.ssh_password; - break; - case 'rax': - $scope.api_key = data.password; - master.api_key = $scope.api_key; - break; - case 'gce': - $scope.email_address = data.username; - $scope.project = data.project; - break; - case 'azure': - $scope.subscription = data.username; - break; - } - $scope.credential_obj = data; - - $scope.$emit('credentialLoaded'); - Wait('stop'); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to retrieve Credential: ' + $stateParams.id + '. GET status: ' + status - }); - }); - }); - - // Save changes to the parent - $scope.formSave = function() { - if ($scope[form.name + '_form'].$valid) { - FormSave({ scope: $scope, mode: 'edit' }); - } - }; - - // Handle Owner change - $scope.ownerChange = function() { - OwnerChange({ scope: $scope }); - }; - - // Handle Kind change - $scope.kindChange = function() { - KindChange({ scope: $scope, form: form, reset: true }); - }; - - $scope.becomeMethodChange = function() { - BecomeMethodChange({ scope: $scope }); - }; - - $scope.formCancel = function() { - $state.transitionTo('credentials'); - }; - - // Related set: Add button - $scope.add = function(set) { - $rootScope.flashMessage = null; - $location.path('/' + base + '/' + $stateParams.id + '/' + set + '/add'); - }; - - // Related set: Edit button - $scope.edit = function(set, id) { - $rootScope.flashMessage = null; - $location.path('/' + base + '/' + $stateParams.id + '/' + set + '/' + id); - }; - - // Related set: Delete button - $scope['delete'] = function(set, itm_id, name, title) { - $rootScope.flashMessage = null; - - var action = function() { - var url = defaultUrl + id + '/' + set + '/'; - Rest.setUrl(url); - Rest.post({ - id: itm_id, - disassociate: 1 - }) - .success(function() { - $('#prompt-modal').modal('hide'); - // @issue: OLD SEARCH - // $scope.search(form.related[set].iterator); - }) - .error(function(data, status) { - $('#prompt-modal').modal('hide'); - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Call to ' + url + ' failed. POST returned status: ' + status - }); - }); - }; - - Prompt({ - hdr: i18n._('Delete'), - body: '
' + i18n.sprintf(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '
' + name + '
', - action: action, - actionText: i18n._('DELETE') - }); - - }; - - // Password change - $scope.clearPWConfirm = function(fld) { - // If password value changes, make sure password_confirm must be re-entered - $scope[fld] = ''; - $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); - }; - - // Respond to 'Ask at runtime?' checkbox - $scope.ask = function(fld, associated) { - if ($scope[fld + '_ask']) { - $scope[fld] = 'ASK'; - $("#" + form.name + "_" + fld + "_input").attr("type", "text"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); - if (associated !== "undefined") { - $("#" + form.name + "_" + fld + "_input").attr("type", "password"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - } - } else { - $scope[fld] = ''; - $("#" + form.name + "_" + fld + "_input").attr("type", "password"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); - if (associated !== "undefined") { - $("#" + form.name + "_" + fld + "_input").attr("type", "text"); - $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - } - } - }; - - $scope.clear = function(fld, associated) { - $scope[fld] = ''; - $scope[associated] = ''; - $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); - $scope[form.name + '_form'].$setDirty(); - }; - -} - -CredentialsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', - '$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert', - 'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices', - 'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange', - 'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n', -]; diff --git a/awx/ui/client/src/controllers/Home.js b/awx/ui/client/src/controllers/Home.js deleted file mode 100644 index 3c81e7a798..0000000000 --- a/awx/ui/client/src/controllers/Home.js +++ /dev/null @@ -1,142 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Home - * @description This controller's for the dashboard -*/ - - -/** - * @ngdoc method - * @name controllers.function:Home#Home - * @methodOf controllers.function:Home - * @description this function loads all the widgets on the dashboard. - * dashboardReady (emit) - this is called when the preliminary parts of the dashboard have been loaded, and loads each of the widgets. Note that the - * Host count graph should only be loaded if the user is a super user - * -*/ - -export function Home($scope, $compile, $stateParams, $rootScope, $location, $log, Wait, - ClearScope, Rest, GetBasePath, ProcessErrors, $window, graphData){ - - ClearScope('home'); - - var dataCount = 0; - - $scope.$on('ws-jobs', function () { - Rest.setUrl(GetBasePath('dashboard')); - Rest.get() - .success(function (data) { - $scope.dashboardData = data; - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard host graph data: ' + status }); - }); - - Rest.setUrl(GetBasePath("jobs") + "?order_by=-finished&page_size=5&finished__isnull=false"); - Rest.get() - .success(function (data) { - $scope.dashboardJobsListData = data.results; - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); - }); - - Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); - Rest.get() - .success(function (data) { - $scope.dashboardJobTemplatesListData = data.results; - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); - }); - - }); - - if ($scope.removeDashboardDataLoadComplete) { - $scope.removeDashboardDataLoadComplete(); - } - $scope.removeDashboardDataLoadComplete = $scope.$on('dashboardDataLoadComplete', function () { - dataCount++; - if (dataCount === 3) { - Wait("stop"); - dataCount = 0; - } - }); - - if ($scope.removeDashboardReady) { - $scope.removeDashboardReady(); - } - $scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) { - $scope.dashboardCountsData = data; - $scope.graphData = graphData; - $scope.$emit('dashboardDataLoadComplete'); - - var cleanupJobListener = - $rootScope.$on('DataReceived:JobStatusGraph', function(e, data) { - $scope.graphData.jobStatus = data; - }); - - $scope.$on('$destroy', function() { - cleanupJobListener(); - }); - }); - - if ($scope.removeDashboardJobsListReady) { - $scope.removeDashboardJobsListReady(); - } - $scope.removeDashboardJobsListReady = $scope.$on('dashboardJobsListReady', function (e, data) { - $scope.dashboardJobsListData = data; - $scope.$emit('dashboardDataLoadComplete'); - }); - - if ($scope.removeDashboardJobTemplatesListReady) { - $scope.removeDashboardJobTemplatesListReady(); - } - $scope.removeDashboardJobTemplatesListReady = $scope.$on('dashboardJobTemplatesListReady', function (e, data) { - $scope.dashboardJobTemplatesListData = data; - $scope.$emit('dashboardDataLoadComplete'); - }); - - $scope.refresh = function () { - Wait('start'); - Rest.setUrl(GetBasePath('dashboard')); - Rest.get() - .success(function (data) { - $scope.dashboardData = data; - $scope.$emit('dashboardReady', data); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard: ' + status }); - }); - Rest.setUrl(GetBasePath("jobs") + "?order_by=-finished&page_size=5&finished__isnull=false"); - Rest.get() - .success(function (data) { - data = data.results; - $scope.$emit('dashboardJobsListReady', data); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); - }); - Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); - Rest.get() - .success(function (data) { - data = data.results; - $scope.$emit('dashboardJobTemplatesListReady', data); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard job templates list: ' + status }); - }); - }; - - $scope.refresh(); -} - -Home.$inject = ['$scope', '$compile', '$stateParams', '$rootScope', '$location', '$log','Wait', - 'ClearScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$window', 'graphData' -]; diff --git a/awx/ui/client/src/controllers/JobEvents.js b/awx/ui/client/src/controllers/JobEvents.js deleted file mode 100644 index 0ea23a76ce..0000000000 --- a/awx/ui/client/src/controllers/JobEvents.js +++ /dev/null @@ -1,356 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:JobEvent - * @description This controller's for the job event page -*/ - - -export function JobEventsList($sce, $filter, $scope, $rootScope, $location, $log, $stateParams, Rest, Alert, JobEventList, GenerateList, - Prompt, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, LookUpInit, ToggleChildren, - FormatDate, EventView, Wait) { - - ClearScope(); - - var list = JobEventList, - generator = GenerateList; - - // @issue: OLD SEARCH - // var defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_events/', //?parent__isnull=1'; - // page; - - list.base = $location.path(); - $scope.job_id = $stateParams.id; - $rootScope.flashMessage = null; - $scope.selected = []; - $scope.expand = true; //on load, automatically expand all nodes - - $scope.parentNode = 'parent-event'; // used in ngClass to dynamically set row level class and control - $scope.childNode = 'child-event'; // link color and cursor - - if ($scope.removeSetHostLinks) { - $scope.removeSetHostLinks(); - } - $scope.removeSetHostLinks = $scope.$on('SetHostLinks', function (e, inventory_id) { - for (var i = 0; i < $scope.jobevents.length; i++) { - if ($scope.jobevents[i].summary_fields.host) { - $scope.jobevents[i].hostLink = "/#/inventories/" + inventory_id; - //encodeURI($scope.jobevents[i].summary_fields.host.name); - } - } - }); - - function formatJSON(eventData) { - //turn JSON event data into an html form - - var i, n, rows, fld, txt, - html = '', - found = false; - - if (eventData.res) { - if (typeof eventData.res === 'string') { - n = eventData.res.match(/\n/g); - rows = (n) ? n.length : 1; - rows = (rows > 10) ? 10 : rows; - found = true; - html += "
\n"; - html += "\n"; - html += "\n"; - html += "
\n"; - } else { - for (fld in eventData.res) { - if ((fld === 'msg' || fld === 'stdout' || fld === 'stderr') && - (eventData.res[fld] !== null && eventData.res[fld] !== '')) { - html += "
\n"; - html += "\n"; - n = eventData.res[fld].match(/\n/g); - rows = (n) ? n.length : 1; - rows = (rows > 10) ? 10 : rows; - html += "\n"; - //html += "
" + eventData.res[fld] + "
\n"; - html += "
\n"; - found = true; - } - if (fld === "results" && Array.isArray(eventData.res[fld]) && eventData.res[fld].length > 0) { - txt = ''; - for (i = 0; i < eventData.res[fld].length; i++) { - txt += eventData.res[fld][i]; - } - n = txt.match(/\n/g); - rows = (n) ? n.length : 1; - rows = (rows > 10) ? 10 : rows; - if (txt !== '') { - html += "
\n"; - html += "\n"; - html += "\n"; - //html += "
" + txt + "
\n"; - html += "
\n"; - found = true; - } - } - if (fld === "rc" && eventData.res[fld] !== '') { - - html += "
\n"; - html += "
" + eventData.res[fld] + "
\n"; - //html += "\n"; - html += "
\n"; - found = true; - } - } - } - html = (found) ? "
\n" + html + "
\n" : ''; - } - if (eventData.hosts) { - html = "" + eventData.host + "\n" + html; - } else { - html = (html === '') ? null : html; - } - return html; - } - - if ($scope.removePostRefresh) { - $scope.removePostRefresh(); - } - $scope.removePostRefresh = $scope.$on('PostRefresh', function () { - // Initialize the parent levels - - generator.inject(list, { mode: 'edit', scope: $scope }); - - var set = $scope[list.name], i; - for (i = 0; i < set.length; i++) { - set[i].event_display = set[i].event_display.replace(/^\u00a0*/g, ''); - if (set[i].event_level < 3) { - set[i].ngicon = 'fa fa-minus-square-o node-toggle'; - set[i]['class'] = 'parentNode'; - } else { - set[i].ngicon = 'fa fa-square-o node-no-toggle'; - set[i]['class'] = 'childNode'; - set[i].event_detail = $sce.trustAsHtml(formatJSON(set[i].event_data)); - } - set[i].show = true; - set[i].spaces = set[i].event_level * 24; - if ($scope.jobevents[i].failed) { - $scope.jobevents[i].status = 'error'; - if (i === set.length - 1) { - $scope.jobevents[i].statusBadgeToolTip = "A failure occurred durring one or more playbook tasks."; - } else if (set[i].event_level < 3) { - $scope.jobevents[i].statusBadgeToolTip = "A failure occurred within the children of this event."; - } else { - $scope.jobevents[i].statusBadgeToolTip = "A failure occurred. Click to view details"; - } - } else if ($scope.jobevents[i].changed) { - $scope.jobevents[i].status = 'changed'; - if (i === set.length - 1) { - $scope.jobevents[i].statusBadgeToolTip = "A change was completed durring one or more playbook tasks."; - } else if (set[i].event_level < 3) { - $scope.jobevents[i].statusBadgeToolTip = "A change was completed by one or more children of this event."; - } else { - $scope.jobevents[i].statusBadgeToolTip = "A change was completed. Click to view details"; - } - } else { - $scope.jobevents[i].status = 'success'; - if (i === set.length - 1) { - $scope.jobevents[i].statusBadgeToolTip = "All playbook tasks completed successfully."; - } else if (set[i].event_level < 3) { - $scope.jobevents[i].statusBadgeToolTip = "All the children of this event completed successfully."; - } else { - $scope.jobevents[i].statusBadgeToolTip = "No errors occurred. Click to view details"; - } - } - //cDate = new Date(set[i].created); - //set[i].created = FormatDate(cDate); - set[i].created = $filter('longDate')(set[i].created); - } - - // Need below lookup to get inventory_id, which is not on event record. Plus, good idea to get status and name - // from job in the event that there are no job event records - Rest.setUrl(GetBasePath('jobs') + $scope.job_id); - Rest.get() - .success(function (data) { - $scope.job_status = data.status; - $scope.job_name = data.summary_fields.job_template.name; - $scope.$emit('SetHostLinks', data.inventory); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to get job status for job: ' + $scope.job_id + '. GET status: ' + status - }); - }); - }); - - // @issue: OLD SEARCH - // SearchInit({ - // scope: $scope, - // set: 'jobevents', - // list: list, - // url: defaultUrl - // }); - // - // page = ($stateParams.page) ? parseInt($stateParams.page,10) - 1 : null; - // - // PaginateInit({ - // scope: $scope, - // list: list, - // url: defaultUrl, - // page: page - // }); - // - // // Called from Inventories tab, host failed events link: - // if ($stateParams.host) { - // $scope[list.iterator + 'SearchField'] = 'host'; - // $scope[list.iterator + 'SearchValue'] = $stateParams.host; - // $scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label; - // } - // - // $scope.search(list.iterator, $stateParams.page); - - $scope.toggle = function (id) { - ToggleChildren({ - scope: $scope, - list: list, - id: id - }); - }; - - $scope.viewJobEvent = function (id) { - EventView({ - event_id: id - }); - }; - - $scope.refresh = function () { - // @issue: OLD SEARCH - // $scope.jobSearchSpin = true; - $scope.jobLoading = true; - Wait('start'); - - // @issue: OLD SEARCH - // Refresh({ - // scope: $scope, - // set: 'jobevents', - // iterator: 'jobevent', - // url: $scope.current_url - // }); - }; -} - -JobEventsList.$inject = ['$sce', '$filter', '$scope', '$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'JobEventList', - 'generateList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Wait' -]; - -export function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $stateParams, JobEventsForm, GenerateForm, - Rest, Alert, ProcessErrors, ClearScope, GetBasePath, FormatDate, EventView, Wait) { - - ClearScope(); - - var form = JobEventsForm, - generator = GenerateForm, - defaultUrl = GetBasePath('base') + 'job_events/' + $stateParams.event_id + '/'; - - generator.inject(form, { mode: 'edit', related: true, scope: $scope}); - generator.reset(); - - // Retrieve detail record and prepopulate the form - Wait('start'); - Rest.setUrl(defaultUrl); - Rest.get() - .success(function (data) { - var cDate, fld, n, rows; - $scope.event_display = data.event_display.replace(/^\u00a0*/g, ''); - 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'; - } - break; - case 'created': - 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; - } - break; - case 'id': - case 'task': - case 'play': - $scope[fld] = data[fld]; - break; - case 'start': - case 'end': - if (data.event_data && data.event_data.res && data.event_data.res[fld] !== undefined) { - cDate = new Date(data.event_data.res[fld]); - $scope[fld] = FormatDate(cDate); - } - 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 (form.fields[fld].type === 'textarea') { - n = data.event_data.res[fld].match(/\n/g); - rows = (n) ? n.length : 1; - rows = (rows > 15) ? 5 : rows; - $('textarea[name="' + fld + '"]').attr('rows', rows); - } - } - 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; - } - break; - } - } - Wait('stop'); - }) - .error(function (data) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve host: ' + $stateParams.event_id + - '. GET status: ' + status }); - }); - - $scope.navigateBack = function () { - var url = '/jobs/' + $stateParams.job_id + '/job_events'; - if ($stateParams.page) { - url += '?page=' + $stateParams.page; - } - $location.url(url); - }; - - $scope.rawView = function () { - EventView({ - "event_id": $scope.id - }); - }; - -} - -JobEventsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$stateParams', 'JobEventsForm', 'GenerateForm', - 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'FormatDate', 'EventView', 'Wait' -]; diff --git a/awx/ui/client/src/controllers/JobHosts.js b/awx/ui/client/src/controllers/JobHosts.js deleted file mode 100644 index e078bc4df7..0000000000 --- a/awx/ui/client/src/controllers/JobHosts.js +++ /dev/null @@ -1,121 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:JobHosts - * @description This controller's for the job hosts page -*/ - - -export function JobHostSummaryList($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, JobHostList, GenerateList, - Prompt, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, - JobStatusToolTip) { - - ClearScope(); - - var list = JobHostList, - // @issue: OLD SEARCH - // defaultUrl = GetBasePath('jobs') + $stateParams.id + '/job_host_summaries/', - view = GenerateList, - inventory; - - $scope.job_id = $stateParams.id; - $scope.host_id = null; - - // After a refresh, populate any needed summary field values on each row - if ($scope.removePostRefresh) { - $scope.removePostRefresh(); - } - $scope.removePostRefresh = $scope.$on('PostRefresh', function () { - - // Set status, tooltips, badges icons, etc. - $scope.jobhosts.forEach(function(element, i) { - $scope.jobhosts[i].host_name = ($scope.jobhosts[i].summary_fields.host) ? $scope.jobhosts[i].summary_fields.host.name : ''; - $scope.jobhosts[i].status = ($scope.jobhosts[i].failed) ? 'failed' : 'success'; - $scope.jobhosts[i].statusBadgeToolTip = JobStatusToolTip($scope.jobhosts[i].status) + - " Click to view details."; - if ($scope.jobhosts[i].summary_fields.host) { - $scope.jobhosts[i].statusLinkTo = '/#/job_events/' + $scope.jobhosts[i].job + '/?host=' + - encodeURI($scope.jobhosts[i].summary_fields.host.name); - } - else { - $scope.jobhosts[i].statusLinkTo = '/#/job_events/' + $scope.jobhosts[i].job; - } - }); - - for (var i = 0; i < $scope.jobhosts.length; i++) { - $scope.jobhosts[i].hostLinkTo = '/#/inventories/' + inventory + '/?host_name=' + - encodeURI($scope.jobhosts[i].summary_fields.host.name); - } - }); - - if ($scope.removeJobReady) { - $scope.removeJobReady(); - } - $scope.removeJobReady = $scope.$on('JobReady', function() { - view.inject(list, { mode: 'edit', scope: $scope }); - - // @issue: OLD SEARCH - // SearchInit({ - // scope: $scope, - // set: 'jobhosts', - // list: list, - // url: defaultUrl - // }); - // - // PaginateInit({ - // scope: $scope, - // list: list, - // url: defaultUrl - // }); - // - // // Called from Inventories tab, host failed events link: - // if ($stateParams.host_name) { - // $scope[list.iterator + 'SearchField'] = 'host'; - // $scope[list.iterator + 'SearchValue'] = $stateParams.host_name; - // $scope[list.iterator + 'SearchFieldLabel'] = list.fields.host.label; - // } - // $scope.search(list.iterator); - }); - - Rest.setUrl(GetBasePath('jobs') + $scope.job_id); - Rest.get() - .success(function (data) { - inventory = data.inventory; - $scope.job_status = data.status; - $scope.$emit('JobReady'); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to get job status for job: ' + $scope.job_id + '. GET status: ' + status - }); - }); - - $scope.showEvents = function (host_name, last_job) { - // When click on !Failed Events link, redirect to latest job/job_events for the host - Rest.setUrl(last_job); - Rest.get() - .success(function (data) { - $location.url('/jobs_events/' + data.id + '/?host=' + encodeURI(host_name)); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to lookup last job: ' + last_job + - '. GET status: ' + status }); - }); - }; - - $scope.refresh = function () { - // @issue: OLD SEARCH - // $scope.search(list.iterator); - }; - -} - -JobHostSummaryList.$inject = ['$scope', '$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'JobHostList', - 'generateList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'JobStatusToolTip', 'Wait' -]; diff --git a/awx/ui/client/src/controllers/Projects.js b/awx/ui/client/src/controllers/Projects.js deleted file mode 100644 index 4b4ef05c19..0000000000 --- a/awx/ui/client/src/controllers/Projects.js +++ /dev/null @@ -1,749 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Projects - * @description This controller's for the projects page - */ - - -export function ProjectsList($scope, $rootScope, $location, $log, $stateParams, - Rest, Alert, ProjectList, Prompt, ReturnToCaller, ClearScope, ProcessErrors, - GetBasePath, ProjectUpdate, Wait, GetChoices, Empty, Find, GetProjectIcon, - GetProjectToolTip, $filter, $state, rbacUiControlService, Dataset, i18n, qs) { - - var list = ProjectList, - defaultUrl = GetBasePath('projects'); - - init(); - - function init() { - $scope.canAdd = false; - - rbacUiControlService.canAdd('projects') - .then(function(canAdd) { - $scope.canAdd = canAdd; - }); - - // search init - $scope.list = list; - $scope[`${list.iterator}_dataset`] = Dataset.data; - $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - - _.forEach($scope[list.name], buildTooltips); - $rootScope.flashMessage = null; - } - - $scope.$on(`${list.iterator}_options`, function(event, data){ - $scope.options = data.data.actions.GET; - optionsRequestDataProcessing(); - }); - - $scope.$watchCollection(`${$scope.list.name}`, function() { - optionsRequestDataProcessing(); - } - ); - - // iterate over the list and add fields like type label, after the - // OPTIONS request returns, or the list is sorted/paginated/searched - function optionsRequestDataProcessing(){ - if ($scope[list.name] !== undefined) { - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; - - // Set the item type label - if (list.fields.scm_type && $scope.options && - $scope.options.hasOwnProperty('scm_type')) { - $scope.options.scm_type.choices.every(function(choice) { - if (choice[0] === item.scm_type) { - itm.type_label = choice[1]; - return false; - } - return true; - }); - } - - buildTooltips(itm); - - }); - } - } - - function buildTooltips(project) { - project.statusIcon = GetProjectIcon(project.status); - project.statusTip = GetProjectToolTip(project.status); - project.scm_update_tooltip = i18n._("Start an SCM update"); - project.scm_schedule_tooltip = i18n._("Schedule future SCM updates"); - project.scm_type_class = ""; - - if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') { - project.statusTip = i18n._('Canceled. Click for details'); - } - - if (project.status === 'running' || project.status === 'updating') { - project.scm_update_tooltip = i18n._("SCM update currently running"); - project.scm_type_class = "btn-disabled"; - } - if (project.scm_type === 'manual') { - project.scm_update_tooltip = i18n._('Manual projects do not require an SCM update'); - project.scm_schedule_tooltip = i18n._('Manual projects do not require a schedule'); - project.scm_type_class = 'btn-disabled'; - project.statusTip = i18n._('Not configured for SCM'); - project.statusIcon = 'none'; - } - } - - $scope.reloadList = function(){ - let path = GetBasePath(list.basePath) || GetBasePath(list.name); - qs.search(path, $stateParams[`${list.iterator}_search`]) - .then(function(searchResponse) { - $scope[`${list.iterator}_dataset`] = searchResponse.data; - $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - }); - }; - - $scope.$on(`ws-jobs`, function(e, data) { - var project; - $log.debug(data); - if ($scope.projects) { - // Assuming we have a list of projects available - project = Find({ list: $scope.projects, key: 'id', val: data.project_id }); - if (project) { - // And we found the affected project - $log.debug('Received event for project: ' + project.name); - $log.debug('Status changed to: ' + data.status); - if (data.status === 'successful' || data.status === 'failed') { - $scope.reloadList(); - } else { - project.scm_update_tooltip = "SCM update currently running"; - project.scm_type_class = "btn-disabled"; - } - project.status = data.status; - project.statusIcon = GetProjectIcon(data.status); - project.statusTip = GetProjectToolTip(data.status); - } - } - }); - - $scope.addProject = function() { - $state.go('projects.add'); - }; - - $scope.editProject = function(id) { - $state.go('projects.edit', { project_id: id }); - }; - - if ($scope.removeGoToJobDetails) { - $scope.removeGoToJobDetails(); - } - $scope.removeGoToJobDetails = $scope.$on('GoToJobDetails', function(e, data) { - if (data.summary_fields.current_update || data.summary_fields.last_update) { - - Wait('start'); - - // Grab the id from summary_fields - var id = (data.summary_fields.current_update) ? data.summary_fields.current_update.id : data.summary_fields.last_update.id; - - $state.go('scmUpdateStdout', { id: id }); - - } else { - Alert(i18n._('No Updates Available'), i18n._('There is no SCM update information available for this project. An update has not yet been ' + - ' completed. If you have not already done so, start an update for this project.'), 'alert-info'); - } - }); - - $scope.showSCMStatus = function(id) { - // Refresh the project list - var project = Find({ list: $scope.projects, key: 'id', val: id }); - if (Empty(project.scm_type) || project.scm_type === 'Manual') { - Alert(i18n._('No SCM Configuration'), i18n._('The selected project is not configured for SCM. To configure for SCM, edit the project and provide SCM settings, ' + - 'and then run an update.'), 'alert-info'); - } else { - // Refresh what we have in memory to insure we're accessing the most recent status record - Rest.setUrl(project.url); - Rest.get() - .success(function(data) { - $scope.$emit('GoToJobDetails', data); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), - msg: i18n._('Project lookup failed. GET returned: ') + status }); - }); - } - }; - - $scope.deleteProject = function(id, name) { - var action = function() { - $('#prompt-modal').modal('hide'); - Wait('start'); - var url = defaultUrl + id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success(function() { - if (parseInt($state.params.project_id) === id) { - $state.go("^", null, { reload: true }); - } else { - $state.go('.', null, {reload: true}); - } - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), url) + status }); - }) - .finally(function() { - Wait('stop'); - }); - }; - - Prompt({ - hdr: i18n._('Delete'), - body: '
' + i18n._('Are you sure you want to delete the project below?') + '
' + '
' + $filter('sanitize')(name) + '
', - action: action, - actionText: 'DELETE' - }); - }; - - if ($scope.removeCancelUpdate) { - $scope.removeCancelUpdate(); - } - $scope.removeCancelUpdate = $scope.$on('Cancel_Update', function(e, url) { - // Cancel the project update process - Rest.setUrl(url); - Rest.post() - .success(function () { - Alert(i18n._('SCM Update Cancel'), i18n._('Your request to cancel the update was submitted to the task manager.'), 'alert-info'); - $scope.refresh(); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. POST status: '), url) + status }); - }); - }); - - if ($scope.removeCheckCancel) { - $scope.removeCheckCancel(); - } - $scope.removeCheckCancel = $scope.$on('Check_Cancel', function(e, data) { - // Check that we 'can' cancel the update - var url = data.related.cancel; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - if (data.can_cancel) { - $scope.$emit('Cancel_Update', url); - } else { - Alert(i18n._('Cancel Not Allowed'), '
' + i18n.sprintf(i18n._('Either you do not have access or the SCM update process completed. ' + - 'Click the %sRefresh%s button to view the latest status.'), '', '') + '
', 'alert-info', null, null, null, null, true); - } - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. GET status: '), url) + status }); - }); - }); - - $scope.cancelUpdate = function(id, name) { - Rest.setUrl(GetBasePath("projects") + id); - Rest.get() - .success(function(data) { - if (data.related.current_update) { - Rest.setUrl(data.related.current_update); - Rest.get() - .success(function(data) { - $scope.$emit('Check_Cancel', data); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Call to %s failed. GET status: '), data.related.current_update) + status }); - }); - } else { - Alert(i18n._('Update Not Found'), '
' + i18n.sprintf(i18n._('An SCM update does not appear to be running for project: %s. Click the %sRefresh%s ' + - 'button to view the latest status.'), $filter('sanitize')(name), '', '') + '
', 'alert-info',undefined,undefined,undefined,undefined,true); - } - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), - msg: i18n._('Call to get project failed. GET status: ') + status }); - }); - }; - - $scope.SCMUpdate = function(project_id, event) { - try { - $(event.target).tooltip('hide'); - } catch (e) { - // ignore - } - $scope.projects.every(function(project) { - if (project.id === project_id) { - if (project.scm_type === "Manual" || Empty(project.scm_type)) { - // Do not respond. Button appears greyed out as if it is disabled. Not disabled though, because we need mouse over event - // to work. So user can click, but we just won't do anything. - //Alert('Missing SCM Setup', 'Before running an SCM update, edit the project and provide the SCM access information.', 'alert-info'); - } else if (project.status === 'updating' || project.status === 'running' || project.status === 'pending') { - // Alert('Update in Progress', 'The SCM update process is running. Use the Refresh button to monitor the status.', 'alert-info'); - } else { - ProjectUpdate({ scope: $scope, project_id: project.id }); - } - return false; - } - return true; - }); - }; - - $scope.editSchedules = function(id) { - var project = Find({ list: $scope.projects, key: 'id', val: id }); - if (!(project.scm_type === "Manual" || Empty(project.scm_type)) && !(project.status === 'updating' || project.status === 'running' || project.status === 'pending')) { - $state.go('projectSchedules', { id: id }); - } - }; -} - -ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$stateParams', - 'Rest', 'Alert', 'ProjectList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'ProjectUpdate', 'Wait', 'GetChoices', 'Empty', 'Find', 'GetProjectIcon', - 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService', 'Dataset', 'i18n', 'QuerySet' -]; - -export function ProjectsAdd($scope, $rootScope, $compile, $location, $log, - $stateParams, GenerateForm, ProjectsForm, Rest, Alert, ProcessErrors, - GetBasePath, GetProjectPath, GetChoices, Wait, $state, CreateSelect2, i18n) { - - var form = ProjectsForm(), - base = $location.path().replace(/^\//, '').split('/')[0], - defaultUrl = GetBasePath('projects'), - master = {}; - - init(); - - function init() { - Rest.setUrl(GetBasePath('projects')); - Rest.options() - .success(function(data) { - if (!data.actions.POST) { - $state.go("^"); - Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a project.'), 'alert-info'); - } - }); - - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); - } - - GetProjectPath({ scope: $scope, master: master }); - - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - var i; - for (i = 0; i < $scope.scm_type_options.length; i++) { - if ($scope.scm_type_options[i].value === '') { - $scope.scm_type_options[i].value = "manual"; - //$scope.scm_type = $scope.scm_type_options[i]; - break; - } - } - - CreateSelect2({ - element: '#project_scm_type', - multiple: false - }); - - $scope.scmRequired = false; - master.scm_type = $scope.scm_type; - }); - - // Load the list of options for Kind - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'scm_type', - variable: 'scm_type_options', - callback: 'choicesReady' - }); - CreateSelect2({ - element: '#local-path-select', - multiple: false - }); - - // Save - $scope.formSave = function() { - var i, fld, url, data = {}; - data = {}; - for (fld in form.fields) { - if (form.fields[fld].type === 'checkbox_group') { - for (i = 0; i < form.fields[fld].fields.length; i++) { - data[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name]; - } - } else { - if (form.fields[fld].type !== 'alertblock') { - data[fld] = $scope[fld]; - } - } - } - - if ($scope.scm_type.value === "manual") { - data.scm_type = ""; - data.local_path = $scope.local_path.value; - } else { - data.scm_type = $scope.scm_type.value; - delete data.local_path; - } - - url = (base === 'teams') ? GetBasePath('teams') + $stateParams.team_id + '/projects/' : defaultUrl; - Wait('start'); - Rest.setUrl(url); - Rest.post(data) - .success(function(data) { - $scope.addedItem = data.id; - $state.go('projects.edit', { project_id: data.id }, { reload: true }); - }) - .error(function(data, status) { - Wait('stop'); - ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), - msg: i18n._('Failed to create new project. POST returned status: ') + status }); - }); - }; - - $scope.scmChange = function() { - // When an scm_type is set, path is not required - if ($scope.scm_type) { - $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; - $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; - $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; - } - - // Dynamically update popover values - if ($scope.scm_type.value) { - switch ($scope.scm_type.value) { - case 'git': - $scope.urlPopover = '

' + - i18n._('Example URLs for GIT SCM include:') + - '

' + - '

' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' + - 'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' + - 'SSH. GIT read only protocol (git://) does not use username or password information.'), '', ''); - break; - case 'svn': - $scope.urlPopover = '

' + i18n._('Example URLs for Subversion SCM include:') + '

' + - ''; - break; - case 'hg': - $scope.urlPopover = '

' + i18n._('Example URLs for Mercurial SCM include:') + '

' + - '' + - '

' + i18n.sprintf(i18n._('%sNote:%s Mercurial does not support password authentication for SSH. ' + - 'Do not put the username and key in the URL. ' + - 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); - break; - default: - $scope.urlPopover = '

' + i18n._('URL popover text'); - } - } - - }; - $scope.formCancel = function() { - $state.go('projects'); - }; -} - -ProjectsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', - '$stateParams', 'GenerateForm', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', - 'GetProjectPath', 'GetChoices', 'Wait', '$state', 'CreateSelect2', 'i18n']; - - -export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, - $stateParams, ProjectsForm, Rest, Alert, ProcessErrors, GenerateForm, - Prompt, ClearScope, GetBasePath, GetProjectPath, Authorization, - GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n) { - - ClearScope('htmlTemplate'); - - var form = ProjectsForm(), - defaultUrl = GetBasePath('projects') + $stateParams.project_id + '/', - master = {}, - id = $stateParams.project_id; - - init(); - - function init() { - $scope.project_local_paths = []; - $scope.base_dir = ''; - } - - $scope.$watch('project_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } - }); - - if ($scope.pathsReadyRemove) { - $scope.pathsReadyRemove(); - } - $scope.pathsReadyRemove = $scope.$on('pathsReady', function () { - CreateSelect2({ - element: '#local-path-select', - multiple: false - }); - }); - - // After the project is loaded, retrieve each related set - if ($scope.projectLoadedRemove) { - $scope.projectLoadedRemove(); - } - $scope.projectLoadedRemove = $scope.$on('projectLoaded', function() { - var opts = []; - - if (Authorization.getUserInfo('is_superuser') === true) { - GetProjectPath({ scope: $scope, master: master }); - } else { - opts.push({ - label: $scope.local_path, - value: $scope.local_path - }); - $scope.project_local_paths = opts; - $scope.local_path = $scope.project_local_paths[0]; - $scope.base_dir = i18n._('You do not have access to view this property'); - $scope.$emit('pathsReady'); - } - - $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; - $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; - $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; - Wait('stop'); - - $scope.scmChange(); - }); - - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - let i; - for (i = 0; i < $scope.scm_type_options.length; i++) { - if ($scope.scm_type_options[i].value === '') { - $scope.scm_type_options[i].value = "manual"; - break; - } - } - // Retrieve detail record and prepopulate the form - Rest.setUrl(defaultUrl); - Rest.get({ params: { id: id } }) - .success(function(data) { - var fld, i; - for (fld in form.fields) { - if (form.fields[fld].type === 'checkbox_group') { - for (i = 0; i < form.fields[fld].fields.length; i++) { - $scope[form.fields[fld].fields[i].name] = data[form.fields[fld].fields[i].name]; - master[form.fields[fld].fields[i].name] = data[form.fields[fld].fields[i].name]; - } - } else { - if (data[fld] !== undefined) { - $scope[fld] = data[fld]; - master[fld] = data[fld]; - } - } - if (form.fields[fld].sourceModel && data.summary_fields && - data.summary_fields[form.fields[fld].sourceModel]) { - $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - } - } - - data.scm_type = (Empty(data.scm_type)) ? 'manual' : data.scm_type; - for (i = 0; i < $scope.scm_type_options.length; i++) { - if ($scope.scm_type_options[i].value === data.scm_type) { - $scope.scm_type = $scope.scm_type_options[i]; - break; - } - } - - if ($scope.scm_type.value !== 'manual') { - $scope.pathRequired = false; - $scope.scmRequired = true; - } else { - $scope.pathRequired = true; - $scope.scmRequired = false; - } - - master.scm_type = $scope.scm_type; - CreateSelect2({ - element: '#project_scm_type', - multiple: false - }); - - $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; - $scope.scm_update_tooltip = i18n._("Start an SCM update"); - $scope.scm_type_class = ""; - if (data.status === 'running' || data.status === 'updating') { - $scope.scm_update_tooltip = i18n._("SCM update currently running"); - $scope.scm_type_class = "btn-disabled"; - } - if (Empty(data.scm_type)) { - $scope.scm_update_tooltip = i18n._('Manual projects do not require an SCM update'); - $scope.scm_type_class = "btn-disabled"; - } - - $scope.project_obj = data; - $scope.name = data.name; - $scope.$emit('projectLoaded'); - Wait('stop'); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Failed to retrieve project: %s. GET status: '), id) + status - }); - }); - }); - - // Load the list of options for Kind - Wait('start'); - GetChoices({ - url: GetBasePath('projects'), - scope: $scope, - field: 'scm_type', - variable: 'scm_type_options', - callback: 'choicesReady' - }); - - $scope.toggleNotification = function(event, id, column) { - var notifier = this.notification; - try { - $(event.target).tooltip('hide'); - } catch (e) { - // ignore - } - ToggleNotification({ - scope: $scope, - url: $scope.project_obj.url, - notifier: notifier, - column: column, - callback: 'NotificationRefresh' - }); - }; - - // Save changes to the parent - $scope.formSave = function() { - var fld, i, params; - GenerateForm.clearApiErrors($scope); - Wait('start'); - $rootScope.flashMessage = null; - params = {}; - for (fld in form.fields) { - if (form.fields[fld].type === 'checkbox_group') { - for (i = 0; i < form.fields[fld].fields.length; i++) { - params[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name]; - } - } else { - if (form.fields[fld].type !== 'alertblock') { - params[fld] = $scope[fld]; - } - } - } - - if ($scope.scm_type.value === "manual") { - params.scm_type = ""; - params.local_path = $scope.local_path.value; - } else { - params.scm_type = $scope.scm_type.value; - delete params.local_path; - } - - Rest.setUrl(defaultUrl); - Rest.put(params) - .success(function() { - Wait('stop'); - $state.go($state.current, {}, { reload: true }); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Failed to update project: %s. PUT status: '), id) + status }); - }); - }; - - // Related set: Delete button - $scope['delete'] = function(set, itm_id, name, title) { - var action = function() { - var url = GetBasePath('projects') + id + '/' + set + '/'; - $rootScope.flashMessage = null; - Rest.setUrl(url); - Rest.post({ id: itm_id, disassociate: 1 }) - .success(function() { - $('#prompt-modal').modal('hide'); - // @issue: OLD SEARCH - // $scope.search(form.related[set].iterator); - }) - .error(function(data, status) { - $('#prompt-modal').modal('hide'); - ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. POST returned status: '), url) + status }); - }); - }; - - Prompt({ - hdr: i18n._('Delete'), - body: '

' + i18n.sprintf(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '
' + '
' + name + '
', - action: action, - actionText: i18n._('DELETE') - }); - }; - - $scope.scmChange = function() { - if ($scope.scm_type) { - $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; - $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; - $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? i18n._('Revision #') : i18n._('SCM Branch'); - } - - // Dynamically update popover values - if ($scope.scm_type.value) { - switch ($scope.scm_type.value) { - case 'git': - $scope.urlPopover = '

' + i18n._('Example URLs for GIT SCM include:') + '

' + - '

' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' + - 'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' + - 'SSH. GIT read only protocol (git://) does not use username or password information.'), '', ''); - break; - case 'svn': - $scope.urlPopover = '

' + i18n._('Example URLs for Subversion SCM include:') + '

' + - ''; - break; - case 'hg': - $scope.urlPopover = '

' + i18n._('Example URLs for Mercurial SCM include:') + '

' + - '' + - '

' + i18n.sprintf(i18n._('%sNote:%s Mercurial does not support password authentication for SSH. ' + - 'Do not put the username and key in the URL. ' + - 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); - break; - default: - $scope.urlPopover = '

' + i18n._('URL popover text'); - } - } - }; - - $scope.SCMUpdate = function() { - if ($scope.project_obj.scm_type === "Manual" || Empty($scope.project_obj.scm_type)) { - // ignore - } else if ($scope.project_obj.status === 'updating' || $scope.project_obj.status === 'running' || $scope.project_obj.status === 'pending') { - Alert(i18n._('Update in Progress'), i18n._('The SCM update process is running.'), 'alert-info'); - } else { - ProjectUpdate({ scope: $scope, project_id: $scope.project_obj.id }); - } - }; - - $scope.formCancel = function() { - $state.transitionTo('projects'); - }; -} - -ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', - '$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GenerateForm', - 'Prompt', 'ClearScope', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty', - 'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n']; diff --git a/awx/ui/client/src/controllers/Schedules.js b/awx/ui/client/src/controllers/Schedules.js deleted file mode 100644 index edebf66a84..0000000000 --- a/awx/ui/client/src/controllers/Schedules.js +++ /dev/null @@ -1,86 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Schedules - * @description This controller's for schedules -*/ - - -export function ScheduleEditController($scope, $compile, $location, $stateParams, SchedulesList, Rest, ProcessErrors, ReturnToCaller, ClearScope, -GetBasePath, Wait, Find, LoadSchedulesScope, GetChoices) { - - ClearScope(); - - var base, id, url, parentObject; - - // base = $location.path().replace(/^\//, '').split('/')[0]; - - // if ($scope.removePostRefresh) { - // $scope.removePostRefresh(); - // } - // $scope.removePostRefresh = $scope.$on('PostRefresh', function() { - // var list = $scope.schedules; - // list.forEach(function(element, idx) { - // list[idx].play_tip = (element.enabled) ? 'Schedule is Active. Click to temporarily stop.' : 'Schedule is temporarily stopped. Click to activate.'; - // }); - // }); - - // if ($scope.removeParentLoaded) { - // $scope.removeParentLoaded(); - // } - // $scope.removeParentLoaded = $scope.$on('ParentLoaded', function() { - // url += "schedules/"; - // SchedulesList.well = true; - // LoadSchedulesScope({ - // parent_scope: $scope, - // scope: $scope, - // list: SchedulesList, - // id: 'schedule-list-target', - // url: url, - // pageSize: 20 - // }); - // }); - - - if ($scope.removeChoicesReady) { - $scope.removeChocesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - // Load the parent object - id = $stateParams.id; - url = GetBasePath(base) + id + '/'; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - parentObject = data; - $scope.$emit('ParentLoaded'); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + ' failed. GET returned: ' + status }); - }); - }); - - $scope.refreshJobs = function() { - // @issue: OLD SEARCH - // $scope.search(SchedulesList.iterator); - }; - - Wait('start'); - - GetChoices({ - scope: $scope, - url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json' - field: 'type', - variable: 'type_choices', - callback: 'choicesReady' - }); -} - -ScheduleEditController.$inject = [ '$scope', '$compile', '$location', '$stateParams', 'SchedulesList', 'Rest', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', - 'GetBasePath', 'Wait', 'Find', 'LoadSchedulesScope', 'GetChoices']; diff --git a/awx/ui/client/src/controllers/Sockets.js b/awx/ui/client/src/controllers/Sockets.js deleted file mode 100644 index 5930b57014..0000000000 --- a/awx/ui/client/src/controllers/Sockets.js +++ /dev/null @@ -1,119 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Sockets - * @description This controller's for controlling websockets - * discuss -*/ - - -export function SocketsController ($scope, $compile, ClearScope, Socket) { - - ClearScope(); - - var test_scope = $scope.$new(), - jobs_scope = $scope.$new(), - job_events_scope = $scope.$new(), - schedules_scope = $scope.$new(), - test_socket = Socket({ scope: test_scope, endpoint: "test" }), - jobs_socket = Socket({ scope: jobs_scope, endpoint: "jobs" }), - schedules_socket = Socket({ scope: schedules_scope, endpoint: "schedules" }), - job_events_socket = Socket({ scope: job_events_scope, endpoint: "job_events" }), - e, html; - - test_scope.messages = []; - jobs_scope.messages = []; - schedules_scope.messages = []; - job_events_scope.messages = []; - - html = "

Socket url: {{ socket_url }}  Status: {{ socket_status }} {{ socket_reason }}
\n" + - "
\n" + - "
Received Messages:
\n" + - "
\n" + - "
\n" + - "\n" + - "
\n"; - - e = angular.element(document.getElementById('test-container')); - e.append(html); - $compile(e)(test_scope); - e = angular.element(document.getElementById('schedules-container')); - e.append(html); - $compile(e)(schedules_scope); - e = angular.element(document.getElementById('jobs-container')); - e.append(html); - $compile(e)(jobs_scope); - - html = "
\n" + - "
\n" + - "
Socket url: {{ socket_url }}  Status: {{ socket_status }} {{ socket_reason }}
\n" + - "
\n" + - "
\n" + - "
\n" + - "
\n" + - "\n" + - "\n" + - "
\n" + - "\n" + - "
\n" + - "
\n" + - "
\n" + - "
" + - "

Subscribed to events for job: {{ jobs_list }}

\n" + - "
Received Messages:
\n" + - "
\n" + - "
\n" + - "\n" + - "
\n"; - - e = angular.element(document.getElementById('job-events-container')); - e.append(html); - $compile(e)(job_events_scope); - - schedules_scope.url = schedules_socket.getUrl(); - test_scope.url = test_socket.getUrl(); - jobs_scope.url = jobs_socket.getUrl(); - job_events_scope.url = job_events_socket.getUrl(); - - test_scope.messages.push('Message Displayed Before Connection'); - - test_socket.on('test', function(data) { - test_scope.messages.push(data); - }); - - schedules_socket.on("schedule_changed", function(data) { - schedules_scope.messages.push(data); - }); - - jobs_socket.on("status_changed", function(data) { - jobs_scope.messages.push(data); - }); - - jobs_socket.on("summary_complete", function(data) { - jobs_scope.messages.push(data); - }); - - job_events_scope.jobs_list = []; - - job_events_scope.subscribeToJobEvent = function() { - job_events_scope.jobs_list.push(job_events_scope.job_id); - job_events_socket.on("job_events-" + job_events_scope.job_id, function(data) { - job_events_scope.messages.push(data); - setTimeout(function() { - $(document).scrollTop($(document).prop("scrollHeight")); - $('#event-message-container').scrollTop($('#event-message-container').prop("scrollHeight")); - }, 300); - }); - }; -} - -SocketsController.$inject = [ '$scope', '$compile', 'ClearScope', 'Socket']; diff --git a/awx/ui/client/src/controllers/Teams.js b/awx/ui/client/src/controllers/Teams.js deleted file mode 100644 index 464a10ee10..0000000000 --- a/awx/ui/client/src/controllers/Teams.js +++ /dev/null @@ -1,247 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Teams - * @description This controller's for teams - */ - -export function TeamsList($scope, $rootScope, $log, $stateParams, - Rest, Alert, TeamList, Prompt, ClearScope, ProcessErrors, - GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset) { - - ClearScope(); - - var list = TeamList, - defaultUrl = GetBasePath('teams'); - - init(); - - function init() { - $scope.canAdd = false; - - rbacUiControlService.canAdd('teams') - .then(function(canAdd) { - $scope.canAdd = canAdd; - }); - // search init - $scope.list = list; - $scope[`${list.iterator}_dataset`] = Dataset.data; - $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - _.forEach($scope[list.name], (team) => { - team.organization_name = team.summary_fields.organization.name; - }); - - $scope.selected = []; - } - - $scope.addTeam = function() { - $state.go('teams.add'); - }; - - $scope.editTeam = function(id) { - $state.go('teams.edit', { team_id: id }); - }; - - $scope.deleteTeam = function(id, name) { - - var action = function() { - Wait('start'); - var url = defaultUrl + id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success(function() { - Wait('stop'); - $('#prompt-modal').modal('hide'); - if (parseInt($state.params.team_id) === id) { - $state.go('^', null, { reload: true }); - } else { - $state.go('.', null, { reload: true }); - } - }) - .error(function(data, status) { - Wait('stop'); - $('#prompt-modal').modal('hide'); - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status - }); - }); - }; - - Prompt({ - hdr: 'Delete', - body: '
Are you sure you want to delete the team below?
' + $filter('sanitize')(name) + '
', - action: action, - actionText: 'DELETE' - }); - }; -} - - -TeamsList.$inject = ['$scope', '$rootScope', '$log', - '$stateParams', 'Rest', 'Alert', 'TeamList', 'Prompt', 'ClearScope', - 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset' -]; - - -export function TeamsAdd($scope, $rootScope, $stateParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors, - ClearScope, GetBasePath, Wait, $state) { - ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior - //$scope. - - Rest.setUrl(GetBasePath('teams')); - Rest.options() - .success(function(data) { - if (!data.actions.POST) { - $state.go("^"); - Alert('Permission Error', 'You do not have permission to add a team.', 'alert-info'); - } - }); - - // Inject dynamic view - var defaultUrl = GetBasePath('teams'), - form = TeamForm; - - init(); - - function init() { - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); - - $rootScope.flashMessage = null; - } - - // Save - $scope.formSave = function() { - var fld, data; - GenerateForm.clearApiErrors($scope); - Wait('start'); - Rest.setUrl(defaultUrl); - data = {}; - for (fld in form.fields) { - data[fld] = $scope[fld]; - } - Rest.post(data) - .success(function(data) { - Wait('stop'); - $rootScope.flashMessage = "New team successfully created!"; - $rootScope.$broadcast("EditIndicatorChange", "users", data.id); - $state.go('teams.edit', { team_id: data.id }, { reload: true }); - }) - .error(function(data, status) { - Wait('stop'); - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to add new team. Post returned status: ' + - status - }); - }); - }; - - $scope.formCancel = function() { - $state.go('teams'); - }; -} - -TeamsAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'GenerateForm', - 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state' -]; - - -export function TeamsEdit($scope, $rootScope, $stateParams, - TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state) { - - ClearScope(); - - var form = TeamForm, - id = $stateParams.team_id, - defaultUrl = GetBasePath('teams') + id; - - init(); - - function init() { - $scope.team_id = id; - Rest.setUrl(defaultUrl); - Wait('start'); - Rest.get(defaultUrl).success(function(data) { - setScopeFields(data); - $scope.organization_name = data.summary_fields.organization.name; - - $scope.team_obj = data; - Wait('stop'); - }); - - $scope.$watch('team_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } - }); - - - } - - // @issue I think all this really want to do is _.forEach(form.fields, (field) =>{ $scope[field] = data[field]}) - function setScopeFields(data) { - _(data) - .pick(function(value, key) { - return form.fields.hasOwnProperty(key) === true; - }) - .forEach(function(value, key) { - $scope[key] = value; - }) - .value(); - return; - } - - // prepares a data payload for a PUT request to the API - function processNewData(fields) { - var data = {}; - _.forEach(fields, function(value, key) { - if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) { - data[key] = $scope[key]; - } - }); - return data; - } - - $scope.formCancel = function() { - $state.go('teams', null, { reload: true }); - }; - - $scope.formSave = function() { - $rootScope.flashMessage = null; - if ($scope[form.name + '_form'].$valid) { - var data = processNewData(form.fields); - Rest.setUrl(defaultUrl); - Rest.put(data).success(function() { - $state.go($state.current, null, { reload: true }); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to retrieve user: ' + - $stateParams.id + '. GET status: ' + status - }); - }); - } - }; - - init(); - - $scope.convertApiUrl = function(str) { - if (str) { - return str.replace("api/v1", "#"); - } else { - return null; - } - }; -} - -TeamsEdit.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', - 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state' -]; diff --git a/awx/ui/client/src/controllers/Users.js b/awx/ui/client/src/controllers/Users.js deleted file mode 100644 index a305a3e133..0000000000 --- a/awx/ui/client/src/controllers/Users.js +++ /dev/null @@ -1,357 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Users - * @description This controller's the Users page - */ - -import { N_ } from "../i18n"; - -const user_type_options = [ - { type: 'normal', label: N_('Normal User') }, - { type: 'system_auditor', label: N_('System Auditor') }, - { type: 'system_administrator', label: N_('System Administrator') }, -]; - -function user_type_sync($scope) { - return (type_option) => { - $scope.is_superuser = false; - $scope.is_system_auditor = false; - switch (type_option.type) { - case 'system_administrator': - $scope.is_superuser = true; - break; - case 'system_auditor': - $scope.is_system_auditor = true; - break; - } - }; -} - -export function UsersList($scope, $rootScope, $stateParams, - Rest, Alert, UserList, Prompt, ClearScope, ProcessErrors, GetBasePath, - Wait, $state, $filter, rbacUiControlService, Dataset, i18n) { - - for (var i = 0; i < user_type_options.length; i++) { - user_type_options[i].label = i18n._(user_type_options[i].label); - } - - ClearScope(); - - var list = UserList, - defaultUrl = GetBasePath('users'); - - init(); - - function init() { - $scope.canAdd = false; - - rbacUiControlService.canAdd('users') - .then(function(canAdd) { - $scope.canAdd = canAdd; - }); - - // search init - $scope.list = list; - $scope[`${list.iterator}_dataset`] = Dataset.data; - $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - - - $rootScope.flashMessage = null; - $scope.selected = []; - } - - $scope.addUser = function() { - $state.go('users.add'); - }; - - $scope.editUser = function(id) { - $state.go('users.edit', { user_id: id }); - }; - - $scope.deleteUser = function(id, name) { - - var action = function() { - $('#prompt-modal').modal('hide'); - Wait('start'); - var url = defaultUrl + id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success(function() { - if (parseInt($state.params.user_id) === id) { - $state.go('^', null, { reload: true }); - } else { - $state.go('.', null, { reload: true }); - } - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { - hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), url) + status - }); - }); - }; - - Prompt({ - hdr: i18n._('Delete'), - body: '
' + i18n._('Are you sure you want to delete the user below?') + '
' + $filter('sanitize')(name) + '
', - action: action, - actionText: i18n._('DELETE') - }); - }; -} - -UsersList.$inject = ['$scope', '$rootScope', '$stateParams', - 'Rest', 'Alert', 'UserList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', - 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n' -]; - - -export function UsersAdd($scope, $rootScope, $stateParams, UserForm, - GenerateForm, Rest, Alert, ProcessErrors, ReturnToCaller, ClearScope, - GetBasePath, ResetForm, Wait, CreateSelect2, $state, $location, i18n) { - - ClearScope(); - - var defaultUrl = GetBasePath('organizations'), - form = UserForm; - - init(); - - function init() { - // apply form definition's default field values - GenerateForm.applyDefaults(form, $scope); - - $scope.ldap_user = false; - $scope.not_ldap_user = !$scope.ldap_user; - $scope.ldap_dn = null; - $scope.socialAuthUser = false; - $scope.external_account = null; - - Rest.setUrl(GetBasePath('users')); - Rest.options() - .success(function(data) { - if (!data.actions.POST) { - $state.go("^"); - Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a user.'), 'alert-info'); - } - }); - - $scope.user_type_options = user_type_options; - $scope.user_type = user_type_options[0]; - $scope.$watch('user_type', user_type_sync($scope)); - CreateSelect2({ - element: '#user_user_type', - multiple: false - }); - } - - // Save - $scope.formSave = function() { - var fld, data = {}; - if ($scope[form.name + '_form'].$valid) { - if ($scope.organization !== undefined && $scope.organization !== null && $scope.organization !== '') { - Rest.setUrl(defaultUrl + $scope.organization + '/users/'); - for (fld in form.fields) { - if (form.fields[fld].realName) { - data[form.fields[fld].realName] = $scope[fld]; - } else { - data[fld] = $scope[fld]; - } - } - data.is_superuser = $scope.is_superuser; - data.is_system_auditor = $scope.is_system_auditor; - Wait('start'); - Rest.post(data) - .success(function(data) { - var base = $location.path().replace(/^\//, '').split('/')[0]; - if (base === 'users') { - $rootScope.flashMessage = i18n._('New user successfully created!'); - $rootScope.$broadcast("EditIndicatorChange", "users", data.id); - $state.go('users.edit', { user_id: data.id }, { reload: true }); - } else { - ReturnToCaller(1); - } - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), msg: i18n._('Failed to add new user. POST returned status: ') + status }); - }); - } else { - $scope.organization_name_api_error = i18n._('A value is required'); - } - } - }; - - $scope.formCancel = function() { - $state.go('users'); - }; - - // Password change - $scope.clearPWConfirm = function(fld) { - // If password value changes, make sure password_confirm must be re-entered - $scope[fld] = ''; - $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); - }; -} - -UsersAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'UserForm', 'GenerateForm', - 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath', - 'ResetForm', 'Wait', 'CreateSelect2', '$state', '$location', 'i18n' -]; - -export function UsersEdit($scope, $rootScope, $location, - $stateParams, UserForm, Rest, ProcessErrors, - ClearScope, GetBasePath, ResetForm, Wait, CreateSelect2, $state, i18n) { - - for (var i = 0; i < user_type_options.length; i++) { - user_type_options[i].label = i18n._(user_type_options[i].label); - } - ClearScope(); - - var form = UserForm, - master = {}, - id = $stateParams.user_id, - defaultUrl = GetBasePath('users') + id; - - init(); - - function init() { - $scope.hidePagination = false; - $scope.hideSmartSearch = false; - $scope.user_type_options = user_type_options; - $scope.user_type = user_type_options[0]; - $scope.$watch('user_type', user_type_sync($scope)); - $scope.$watch('is_superuser', hidePermissionsTabSmartSearchAndPaginationIfSuperUser($scope)); - Rest.setUrl(defaultUrl); - Wait('start'); - Rest.get(defaultUrl).success(function(data) { - $scope.user_id = id; - $scope.ldap_user = (data.ldap_dn !== null && data.ldap_dn !== undefined && data.ldap_dn !== '') ? true : false; - $scope.not_ldap_user = !$scope.ldap_user; - master.ldap_user = $scope.ldap_user; - $scope.socialAuthUser = (data.auth.length > 0) ? true : false; - $scope.external_account = data.external_account; - - $scope.user_type = $scope.user_type_options[0]; - $scope.is_system_auditor = false; - $scope.is_superuser = false; - if (data.is_system_auditor) { - $scope.user_type = $scope.user_type_options[1]; - $scope.is_system_auditor = true; - } - if (data.is_superuser) { - $scope.user_type = $scope.user_type_options[2]; - $scope.is_superuser = true; - } - - $scope.user_obj = data; - $scope.name = data.username; - - CreateSelect2({ - element: '#user_user_type', - multiple: false - }); - - $scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } - }); - - setScopeFields(data); - Wait('stop'); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { - hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Failed to retrieve user: %s. GET status: '), $stateParams.id) + status - }); - }); - } - - // Organizations and Teams tab pagination is hidden through other mechanism - function hidePermissionsTabSmartSearchAndPaginationIfSuperUser(scope) { - return function(isSuperuserNewValue) { - let shouldHide = isSuperuserNewValue; - if (shouldHide === true) { - scope.hidePagination = true; - scope.hideSmartSearch = true; - } else if (shouldHide === false) { - scope.hidePagination = false; - scope.hideSmartSearch = false; - } - }; - } - - - function setScopeFields(data) { - _(data) - .pick(function(value, key) { - return form.fields.hasOwnProperty(key) === true; - }) - .forEach(function(value, key) { - $scope[key] = value; - }) - .value(); - return; - } - - $scope.convertApiUrl = function(str) { - if (str) { - return str.replace("api/v1", "#"); - } else { - return null; - } - }; - - // prepares a data payload for a PUT request to the API - var processNewData = function(fields) { - var data = {}; - _.forEach(fields, function(value, key) { - if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) { - data[key] = $scope[key]; - } - }); - data.is_superuser = $scope.is_superuser; - data.is_system_auditor = $scope.is_system_auditor; - return data; - }; - - $scope.formCancel = function() { - $state.go('users', null, { reload: true }); - }; - - $scope.formSave = function() { - $rootScope.flashMessage = null; - if ($scope[form.name + '_form'].$valid) { - Rest.setUrl(defaultUrl + '/'); - var data = processNewData(form.fields); - Rest.put(data).success(function() { - $state.go($state.current, null, { reload: true }); - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, null, { - hdr: i18n._('Error!'), - msg: i18n.sprintf(i18n._('Failed to retrieve user: %s. GET status: '), $stateParams.id) + status - }); - }); - } - }; - - $scope.clearPWConfirm = function(fld) { - // If password value changes, make sure password_confirm must be re-entered - $scope[fld] = ''; - $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); - $rootScope.flashMessage = null; - }; -} - -UsersEdit.$inject = ['$scope', '$rootScope', '$location', - '$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath', - 'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n' -]; diff --git a/awx/ui/client/src/credentials/add/credentials-add.controller.js b/awx/ui/client/src/credentials/add/credentials-add.controller.js new file mode 100644 index 0000000000..fa6c436e66 --- /dev/null +++ b/awx/ui/client/src/credentials/add/credentials-add.controller.js @@ -0,0 +1,177 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$compile', '$location', + '$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert', + 'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'BecomeMethodChange', + 'OwnerChange', 'FormSave', '$state', 'CreateSelect2', 'i18n', + function($scope, $rootScope, $compile, $location, $log, + $stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors, + ClearScope, GetBasePath, GetChoices, Empty, KindChange, BecomeMethodChange, + OwnerChange, FormSave, $state, CreateSelect2, i18n) { + + ClearScope(); + + // Inject dynamic view + var form = CredentialForm, + defaultUrl = GetBasePath('credentials'), + url; + + init(); + + function init() { + // Load the list of options for Kind + GetChoices({ + scope: $scope, + url: defaultUrl, + field: 'kind', + variable: 'credential_kind_options' + }); + + GetChoices({ + scope: $scope, + url: defaultUrl, + field: 'become_method', + variable: 'become_options' + }); + + CreateSelect2({ + element: '#credential_become_method', + multiple: false + }); + + CreateSelect2({ + element: '#credential_kind', + multiple: false + }); + + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); + + $scope.keyEntered = false; + $scope.permissionsTooltip = i18n._('Please save before assigning permissions'); + + // determine if the currently logged-in user may share this credential + // previous commentary said: "$rootScope.current_user isn't available because a call to the config endpoint hasn't finished resolving yet" + // I'm 99% sure this state's will never resolve block will be rejected if setup surrounding config endpoint hasn't completed + if ($rootScope.current_user && $rootScope.current_user.is_superuser) { + $scope.canShareCredential = true; + } else { + Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`); + Rest.get() + .success(function(data) { + $scope.canShareCredential = (data.count) ? true : false; + }).error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status }); + }); + } + } + + if (!Empty($stateParams.user_id)) { + // Get the username based on incoming route + $scope.owner = 'user'; + $scope.user = $stateParams.user_id; + OwnerChange({ scope: $scope }); + url = GetBasePath('users') + $stateParams.user_id + '/'; + Rest.setUrl(url); + Rest.get() + .success(function(data) { + $scope.user_username = data.username; + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status }); + }); + } else if (!Empty($stateParams.team_id)) { + // Get the username based on incoming route + $scope.owner = 'team'; + $scope.team = $stateParams.team_id; + OwnerChange({ scope: $scope }); + url = GetBasePath('teams') + $stateParams.team_id + '/'; + Rest.setUrl(url); + Rest.get() + .success(function(data) { + $scope.team_name = data.name; + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status }); + }); + } else { + // default type of owner to a user + $scope.owner = 'user'; + OwnerChange({ scope: $scope }); + } + + $scope.$watch("ssh_key_data", function(val) { + if (val === "" || val === null || val === undefined) { + $scope.keyEntered = false; + $scope.ssh_key_unlock_ask = false; + $scope.ssh_key_unlock = ""; + } else { + $scope.keyEntered = true; + } + }); + + // Handle Kind change + $scope.kindChange = function() { + KindChange({ scope: $scope, form: form, reset: true }); + }; + + $scope.becomeMethodChange = function() { + BecomeMethodChange({ scope: $scope }); + }; + + // Save + $scope.formSave = function() { + if ($scope[form.name + '_form'].$valid) { + FormSave({ scope: $scope, mode: 'add' }); + } + }; + + $scope.formCancel = function() { + $state.go('credentials'); + }; + + // Password change + $scope.clearPWConfirm = function(fld) { + // If password value changes, make sure password_confirm must be re-entered + $scope[fld] = ''; + $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); + }; + + // Respond to 'Ask at runtime?' checkbox + $scope.ask = function(fld, associated) { + if ($scope[fld + '_ask']) { + $scope[fld] = 'ASK'; + $("#" + form.name + "_" + fld + "_input").attr("type", "text"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); + if (associated !== "undefined") { + $("#" + form.name + "_" + fld + "_input").attr("type", "password"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + } + } else { + $scope[fld] = ''; + $("#" + form.name + "_" + fld + "_input").attr("type", "password"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); + if (associated !== "undefined") { + $("#" + form.name + "_" + fld + "_input").attr("type", "text"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + } + } + }; + + // Click clear button + $scope.clear = function(fld, associated) { + $scope[fld] = ''; + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + $scope[form.name + '_form'].$setDirty(); + }; + } +]; diff --git a/awx/ui/client/src/credentials/edit/credentials-edit.controller.js b/awx/ui/client/src/credentials/edit/credentials-edit.controller.js new file mode 100644 index 0000000000..1926e811ca --- /dev/null +++ b/awx/ui/client/src/credentials/edit/credentials-edit.controller.js @@ -0,0 +1,346 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$compile', '$location', + '$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert', + 'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices', + 'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange', + 'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n', + function($scope, $rootScope, $compile, $location, $log, + $stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt, + GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, FormSave, Wait, + $state, CreateSelect2, Authorization, i18n) { + + ClearScope(); + + var defaultUrl = GetBasePath('credentials'), + form = CredentialForm, + base = $location.path().replace(/^\//, '').split('/')[0], + master = {}, + id = $stateParams.credential_id; + + init(); + + function init() { + $scope.id = id; + $scope.$watch('credential_obj.summary_fields.user_capabilities.edit', function(val) { + if (val === false) { + $scope.canAdd = false; + } + }); + + $scope.canShareCredential = false; + Wait('start'); + if (!$rootScope.current_user) { + Authorization.restoreUserInfo(); + } + GetChoices({ + scope: $scope, + url: defaultUrl, + field: 'kind', + variable: 'credential_kind_options', + callback: 'choicesReadyCredential' + }); + + GetChoices({ + scope: $scope, + url: defaultUrl, + field: 'become_method', + variable: 'become_options' + }); + + if ($rootScope.current_user && $rootScope.current_user.is_superuser) { + $scope.canShareCredential = true; + } else { + Rest.setUrl(`/api/v1/users/${$rootScope.current_user.id}/admin_of_organizations`); + Rest.get() + .success(function(data) { + $scope.canShareCredential = (data.count) ? true : false; + Wait('stop'); + }).error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status }); + }); + } + + $scope.$watch('organization', function(val) { + if (val === undefined) { + $scope.permissionsTooltip = i18n._('Credentials are only shared within an organization. Assign credentials to an organization to delegate credential permissions. The organization cannot be edited after credentials are assigned.'); + } else { + $scope.permissionsTooltip = ''; + } + }); + + setAskCheckboxes(); + OwnerChange({ scope: $scope }); + $scope.$watch("ssh_key_data", function(val) { + if (val === "" || val === null || val === undefined) { + $scope.keyEntered = false; + $scope.ssh_key_unlock_ask = false; + $scope.ssh_key_unlock = ""; + } else { + $scope.keyEntered = true; + } + }); + } + + function setAskCheckboxes() { + var fld, i; + for (fld in form.fields) { + if (form.fields[fld].type === 'sensitive' && $scope[fld] === 'ASK') { + // turn on 'ask' checkbox for password fields with value of 'ASK' + $("#" + form.name + "_" + fld + "_input").attr("type", "text"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); + $("#" + fld + "-clear-btn").attr("disabled", "disabled"); + $scope[fld + '_ask'] = true; + } else { + $scope[fld + '_ask'] = false; + $("#" + fld + "-clear-btn").removeAttr("disabled"); + } + master[fld + '_ask'] = $scope[fld + '_ask']; + } + + // Set kind field to the correct option + for (i = 0; i < $scope.credential_kind_options.length; i++) { + if ($scope.kind === $scope.credential_kind_options[i].value) { + $scope.kind = $scope.credential_kind_options[i]; + break; + } + } + } + if ($scope.removeChoicesReady) { + $scope.removeChoicesReady(); + } + $scope.removeChoicesReady = $scope.$on('choicesReadyCredential', function() { + // Retrieve detail record and prepopulate the form + Rest.setUrl(defaultUrl + ':id/'); + Rest.get({ params: { id: id } }) + .success(function(data) { + if (data && data.summary_fields && + data.summary_fields.organization && + data.summary_fields.organization.id) { + $scope.needsRoleList = true; + } else { + $scope.needsRoleList = false; + } + + $scope.credential_name = data.name; + + var i, fld; + + + for (fld in form.fields) { + if (data[fld] !== null && data[fld] !== undefined) { + $scope[fld] = data[fld]; + master[fld] = $scope[fld]; + } + if (form.fields[fld].type === 'lookup' && 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] = + $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]; + } + } + + if (!Empty($scope.user)) { + $scope.owner = 'user'; + } else { + $scope.owner = 'team'; + } + master.owner = $scope.owner; + + for (i = 0; i < $scope.become_options.length; i++) { + if ($scope.become_options[i].value === data.become_method) { + $scope.become_method = $scope.become_options[i]; + break; + } + } + + if ($scope.become_method && $scope.become_method.value === "") { + $scope.become_method = null; + } + master.become_method = $scope.become_method; + + $scope.$watch('become_method', function(val) { + if (val !== null) { + if (val.value === "") { + $scope.become_username = ""; + $scope.become_password = ""; + } + } + }); + + for (i = 0; i < $scope.credential_kind_options.length; i++) { + if ($scope.credential_kind_options[i].value === data.kind) { + $scope.kind = $scope.credential_kind_options[i]; + break; + } + } + + KindChange({ + scope: $scope, + form: form, + reset: false + }); + + master.kind = $scope.kind; + + CreateSelect2({ + element: '#credential_become_method', + multiple: false + }); + + CreateSelect2({ + element: '#credential_kind', + multiple: false + }); + + switch (data.kind) { + case 'aws': + $scope.access_key = data.username; + $scope.secret_key = data.password; + master.access_key = $scope.access_key; + master.secret_key = $scope.secret_key; + break; + case 'ssh': + $scope.ssh_password = data.password; + master.ssh_password = $scope.ssh_password; + break; + case 'rax': + $scope.api_key = data.password; + master.api_key = $scope.api_key; + break; + case 'gce': + $scope.email_address = data.username; + $scope.project = data.project; + break; + case 'azure': + $scope.subscription = data.username; + break; + } + $scope.credential_obj = data; + + $scope.$emit('credentialLoaded'); + Wait('stop'); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to retrieve Credential: ' + $stateParams.id + '. GET status: ' + status + }); + }); + }); + + // Save changes to the parent + $scope.formSave = function() { + if ($scope[form.name + '_form'].$valid) { + FormSave({ scope: $scope, mode: 'edit' }); + } + }; + + // Handle Owner change + $scope.ownerChange = function() { + OwnerChange({ scope: $scope }); + }; + + // Handle Kind change + $scope.kindChange = function() { + KindChange({ scope: $scope, form: form, reset: true }); + }; + + $scope.becomeMethodChange = function() { + BecomeMethodChange({ scope: $scope }); + }; + + $scope.formCancel = function() { + $state.transitionTo('credentials'); + }; + + // Related set: Add button + $scope.add = function(set) { + $rootScope.flashMessage = null; + $location.path('/' + base + '/' + $stateParams.id + '/' + set + '/add'); + }; + + // Related set: Edit button + $scope.edit = function(set, id) { + $rootScope.flashMessage = null; + $location.path('/' + base + '/' + $stateParams.id + '/' + set + '/' + id); + }; + + // Related set: Delete button + $scope['delete'] = function(set, itm_id, name, title) { + $rootScope.flashMessage = null; + + var action = function() { + var url = defaultUrl + id + '/' + set + '/'; + Rest.setUrl(url); + Rest.post({ + id: itm_id, + disassociate: 1 + }) + .success(function() { + $('#prompt-modal').modal('hide'); + // @issue: OLD SEARCH + // $scope.search(form.related[set].iterator); + }) + .error(function(data, status) { + $('#prompt-modal').modal('hide'); + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: 'Call to ' + url + ' failed. POST returned status: ' + status + }); + }); + }; + + Prompt({ + hdr: i18n._('Delete'), + body: '
' + i18n.sprintf(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '
' + name + '
', + action: action, + actionText: i18n._('DELETE') + }); + + }; + + // Password change + $scope.clearPWConfirm = function(fld) { + // If password value changes, make sure password_confirm must be re-entered + $scope[fld] = ''; + $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); + }; + + // Respond to 'Ask at runtime?' checkbox + $scope.ask = function(fld, associated) { + if ($scope[fld + '_ask']) { + $scope[fld] = 'ASK'; + $("#" + form.name + "_" + fld + "_input").attr("type", "text"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); + if (associated !== "undefined") { + $("#" + form.name + "_" + fld + "_input").attr("type", "password"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + } + } else { + $scope[fld] = ''; + $("#" + form.name + "_" + fld + "_input").attr("type", "password"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Show"); + if (associated !== "undefined") { + $("#" + form.name + "_" + fld + "_input").attr("type", "text"); + $("#" + form.name + "_" + fld + "_show_input_button").html("Hide"); + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + } + } + }; + + $scope.clear = function(fld, associated) { + $scope[fld] = ''; + $scope[associated] = ''; + $scope[form.name + '_form'][associated].$setValidity('awpassmatch', true); + $scope[form.name + '_form'].$setDirty(); + }; + } +]; diff --git a/awx/ui/client/src/credentials/list/credentials-list.controller.js b/awx/ui/client/src/credentials/list/credentials-list.controller.js new file mode 100644 index 0000000000..859e0ea998 --- /dev/null +++ b/awx/ui/client/src/credentials/list/credentials-list.controller.js @@ -0,0 +1,106 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$location', '$log', + '$stateParams', 'Rest', 'Alert', 'CredentialList', 'Prompt', 'ClearScope', + 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n', + function($scope, $rootScope, $location, $log, + $stateParams, Rest, Alert, CredentialList, Prompt, ClearScope, + ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset, + i18n) { + + ClearScope(); + + var list = CredentialList, + defaultUrl = GetBasePath('credentials'); + + init(); + + function init() { + rbacUiControlService.canAdd('credentials') + .then(function(canAdd) { + $scope.canAdd = canAdd; + }); + + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + + $scope.selected = []; + } + + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + }); + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + if ($scope[list.name] !== undefined) { + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + if (list.fields.kind && $scope.options && + $scope.options.hasOwnProperty('kind')) { + $scope.options.kind.choices.every(function(choice) { + if (choice[0] === item.kind) { + itm.kind_label = choice[1]; + return false; + } + return true; + }); + } + }); + } + } + + $scope.addCredential = function() { + $state.go('credentials.add'); + }; + + $scope.editCredential = function(id) { + $state.go('credentials.edit', { credential_id: id }); + }; + + $scope.deleteCredential = function(id, name) { + var action = function() { + $('#prompt-modal').modal('hide'); + Wait('start'); + var url = defaultUrl + id + '/'; + Rest.setUrl(url); + Rest.destroy() + .success(function() { + if (parseInt($state.params.credential_id) === id) { + $state.go("^", null, { reload: true }); + } else { + $state.go('.', null, {reload: true}); + } + Wait('stop'); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status + }); + }); + }; + + Prompt({ + hdr: i18n._('Delete'), + body: '
' + i18n._('Are you sure you want to delete the credential below?') + '
' + $filter('sanitize')(name) + '
', + action: action, + actionText: i18n._('DELETE') + }); + }; + } +]; diff --git a/awx/ui/client/src/credentials/main.js b/awx/ui/client/src/credentials/main.js index faee3f5093..16cc13aad0 100644 --- a/awx/ui/client/src/credentials/main.js +++ b/awx/ui/client/src/credentials/main.js @@ -5,7 +5,45 @@ *************************************************/ import ownerList from './ownerList.directive'; +import CredentialsList from './list/credentials-list.controller'; +import CredentialsAdd from './add/credentials-add.controller'; +import CredentialsEdit from './edit/credentials-edit.controller'; +import { N_ } from '../i18n'; export default angular.module('credentials', []) - .directive('ownerList', ownerList); + .directive('ownerList', ownerList) + .controller('CredentialsList', CredentialsList) + .controller('CredentialsAdd', CredentialsAdd) + .controller('CredentialsEdit', CredentialsEdit) + .config(['$stateProvider', 'stateDefinitionsProvider', + function($stateProvider, stateDefinitionsProvider) { + let stateDefinitions = stateDefinitionsProvider.$get(); + + // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry + // see: stateDefinition.factory for usage documentation + $stateProvider.state({ + name: 'credentials', + url: '/credentials', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'credentials', + modes: ['add', 'edit'], + list: 'CredentialList', + form: 'CredentialForm', + controllers: { + list: CredentialsList, + add: CredentialsAdd, + edit: CredentialsEdit + }, + data: { + activityStream: true, + activityStreamTarget: 'credential' + }, + ncyBreadcrumb: { + parent: 'setup', + label: N_('CREDENTIALS') + } + }) + }); + } + ]); diff --git a/awx/ui/client/src/dashboard/counts/dashboard-counts.block.less b/awx/ui/client/src/home/dashboard/counts/dashboard-counts.block.less similarity index 97% rename from awx/ui/client/src/dashboard/counts/dashboard-counts.block.less rename to awx/ui/client/src/home/dashboard/counts/dashboard-counts.block.less index d9a85ed444..8d85f93bf5 100644 --- a/awx/ui/client/src/dashboard/counts/dashboard-counts.block.less +++ b/awx/ui/client/src/home/dashboard/counts/dashboard-counts.block.less @@ -1,4 +1,4 @@ -@import "../../shared/branding/colors.default.less"; +@import "../../../shared/branding/colors.default.less"; /** @define DashboardCounts */ diff --git a/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js b/awx/ui/client/src/home/dashboard/counts/dashboard-counts.directive.js similarity index 97% rename from awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js rename to awx/ui/client/src/home/dashboard/counts/dashboard-counts.directive.js index 8d26b9d1c6..bc91c9c8ba 100644 --- a/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js +++ b/awx/ui/client/src/home/dashboard/counts/dashboard-counts.directive.js @@ -9,7 +9,7 @@ export default data: '=' }, replace: false, - templateUrl: templateUrl('dashboard/counts/dashboard-counts'), + templateUrl: templateUrl('home/dashboard/counts/dashboard-counts'), link: function(scope, element, attrs) { scope.$watch("data", function(data) { if (data && data.hosts) { diff --git a/awx/ui/client/src/dashboard/counts/dashboard-counts.partial.html b/awx/ui/client/src/home/dashboard/counts/dashboard-counts.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/counts/dashboard-counts.partial.html rename to awx/ui/client/src/home/dashboard/counts/dashboard-counts.partial.html diff --git a/awx/ui/client/src/dashboard/counts/main.js b/awx/ui/client/src/home/dashboard/counts/main.js similarity index 100% rename from awx/ui/client/src/dashboard/counts/main.js rename to awx/ui/client/src/home/dashboard/counts/main.js diff --git a/awx/ui/client/src/dashboard/dashboard.block.less b/awx/ui/client/src/home/dashboard/dashboard.block.less similarity index 93% rename from awx/ui/client/src/dashboard/dashboard.block.less rename to awx/ui/client/src/home/dashboard/dashboard.block.less index 40bcd84002..f1b6446fa6 100644 --- a/awx/ui/client/src/dashboard/dashboard.block.less +++ b/awx/ui/client/src/home/dashboard/dashboard.block.less @@ -1,5 +1,5 @@ /** @define Dashboard */ -@import "../shared/branding/colors.default.less"; +@import "../../shared/branding/colors.default.less"; .Dashboard { display: flex; diff --git a/awx/ui/client/src/dashboard/dashboard.directive.js b/awx/ui/client/src/home/dashboard/dashboard.directive.js similarity index 80% rename from awx/ui/client/src/dashboard/dashboard.directive.js rename to awx/ui/client/src/home/dashboard/dashboard.directive.js index 243a423a67..b7bf23d019 100644 --- a/awx/ui/client/src/dashboard/dashboard.directive.js +++ b/awx/ui/client/src/home/dashboard/dashboard.directive.js @@ -5,7 +5,7 @@ export default return { restrict: 'E', scope: true, - templateUrl: templateUrl('dashboard/dashboard'), + templateUrl: templateUrl('home/dashboard/dashboard'), link: function(scope, element, attrs) { } }; diff --git a/awx/ui/client/src/dashboard/dashboard.partial.html b/awx/ui/client/src/home/dashboard/dashboard.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/dashboard.partial.html rename to awx/ui/client/src/home/dashboard/dashboard.partial.html diff --git a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.block.less b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.block.less similarity index 98% rename from awx/ui/client/src/dashboard/graphs/dashboard-graphs.block.less rename to awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.block.less index 09cdbf7b72..5de5162761 100644 --- a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.block.less +++ b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.block.less @@ -1,6 +1,6 @@ /** @define DashboardGraphs */ -@import "../../shared/branding/colors.default.less"; +@import "../../../shared/branding/colors.default.less"; .DashboardGraphs { margin-top: 20px; diff --git a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.directive.js b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.directive.js similarity index 86% rename from awx/ui/client/src/dashboard/graphs/dashboard-graphs.directive.js rename to awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.directive.js index 64fb391738..80a0d4e181 100644 --- a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.directive.js +++ b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.directive.js @@ -4,7 +4,7 @@ export default ['templateUrl', return { restrict: 'E', scope: true, - templateUrl: templateUrl('dashboard/graphs/dashboard-graphs'), + templateUrl: templateUrl('home/dashboard/graphs/dashboard-graphs'), link: function(scope, element, attrs) { function clearStatus() { diff --git a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html b/awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html rename to awx/ui/client/src/home/dashboard/graphs/dashboard-graphs.partial.html diff --git a/awx/ui/client/src/dashboard/graphs/graph-helpers/adjust-graph-size.service.js b/awx/ui/client/src/home/dashboard/graphs/graph-helpers/adjust-graph-size.service.js similarity index 100% rename from awx/ui/client/src/dashboard/graphs/graph-helpers/adjust-graph-size.service.js rename to awx/ui/client/src/home/dashboard/graphs/graph-helpers/adjust-graph-size.service.js diff --git a/awx/ui/client/src/dashboard/graphs/graph-helpers/auto-size.directive.js b/awx/ui/client/src/home/dashboard/graphs/graph-helpers/auto-size.directive.js similarity index 100% rename from awx/ui/client/src/dashboard/graphs/graph-helpers/auto-size.directive.js rename to awx/ui/client/src/home/dashboard/graphs/graph-helpers/auto-size.directive.js diff --git a/awx/ui/client/src/dashboard/graphs/graph-helpers/main.js b/awx/ui/client/src/home/dashboard/graphs/graph-helpers/main.js similarity index 100% rename from awx/ui/client/src/dashboard/graphs/graph-helpers/main.js rename to awx/ui/client/src/home/dashboard/graphs/graph-helpers/main.js diff --git a/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js similarity index 98% rename from awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js rename to awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js index 8202fbc21d..d6a289b953 100644 --- a/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js +++ b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.directive.js @@ -23,7 +23,7 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG scope: { data: '=' }, - templateUrl: templateUrl('dashboard/graphs/job-status/job_status_graph'), + templateUrl: templateUrl('home/dashboard/graphs/job-status/job_status_graph'), link: link }; diff --git a/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.service.js b/awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js similarity index 100% rename from awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.service.js rename to awx/ui/client/src/home/dashboard/graphs/job-status/job-status-graph.service.js diff --git a/awx/ui/client/src/dashboard/graphs/job-status/job_status_graph.partial.html b/awx/ui/client/src/home/dashboard/graphs/job-status/job_status_graph.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/graphs/job-status/job_status_graph.partial.html rename to awx/ui/client/src/home/dashboard/graphs/job-status/job_status_graph.partial.html diff --git a/awx/ui/client/src/dashboard/graphs/job-status/main.js b/awx/ui/client/src/home/dashboard/graphs/job-status/main.js similarity index 86% rename from awx/ui/client/src/dashboard/graphs/job-status/main.js rename to awx/ui/client/src/home/dashboard/graphs/job-status/main.js index 66a6086723..927bc475df 100644 --- a/awx/ui/client/src/dashboard/graphs/job-status/main.js +++ b/awx/ui/client/src/home/dashboard/graphs/job-status/main.js @@ -1,7 +1,7 @@ import JobStatusGraphDirective from './job-status-graph.directive'; import JobStatusGraphService from './job-status-graph.service'; import DashboardGraphHelpers from '../graph-helpers/main'; -import templateUrl from '../../../shared/template-url/main'; +import templateUrl from '../../../../shared/template-url/main'; export default angular.module('JobStatusGraph', [DashboardGraphHelpers.name, templateUrl.name]) .directive('jobStatusGraph', JobStatusGraphDirective) diff --git a/awx/ui/client/src/dashboard/graphs/main.js b/awx/ui/client/src/home/dashboard/graphs/main.js similarity index 100% rename from awx/ui/client/src/dashboard/graphs/main.js rename to awx/ui/client/src/home/dashboard/graphs/main.js diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts-edit.controller.js b/awx/ui/client/src/home/dashboard/hosts/dashboard-hosts-edit.controller.js similarity index 100% rename from awx/ui/client/src/dashboard/hosts/dashboard-hosts-edit.controller.js rename to awx/ui/client/src/home/dashboard/hosts/dashboard-hosts-edit.controller.js diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts-list.controller.js b/awx/ui/client/src/home/dashboard/hosts/dashboard-hosts-list.controller.js similarity index 100% rename from awx/ui/client/src/dashboard/hosts/dashboard-hosts-list.controller.js rename to awx/ui/client/src/home/dashboard/hosts/dashboard-hosts-list.controller.js diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.form.js b/awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.form.js similarity index 100% rename from awx/ui/client/src/dashboard/hosts/dashboard-hosts.form.js rename to awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.form.js diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js b/awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.list.js similarity index 100% rename from awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js rename to awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.list.js diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.service.js b/awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.service.js similarity index 100% rename from awx/ui/client/src/dashboard/hosts/dashboard-hosts.service.js rename to awx/ui/client/src/home/dashboard/hosts/dashboard-hosts.service.js diff --git a/awx/ui/client/src/dashboard/hosts/main.js b/awx/ui/client/src/home/dashboard/hosts/main.js similarity index 98% rename from awx/ui/client/src/dashboard/hosts/main.js rename to awx/ui/client/src/home/dashboard/hosts/main.js index 84d04fa8ad..e68efc7300 100644 --- a/awx/ui/client/src/dashboard/hosts/main.js +++ b/awx/ui/client/src/home/dashboard/hosts/main.js @@ -9,7 +9,7 @@ import form from './dashboard-hosts.form'; import listController from './dashboard-hosts-list.controller'; import editController from './dashboard-hosts-edit.controller'; import service from './dashboard-hosts.service'; -import { N_ } from '../../i18n'; +import { N_ } from '../../../i18n'; export default angular.module('dashboardHosts', []) diff --git a/awx/ui/client/src/dashboard/lists/dashboard-list.block.less b/awx/ui/client/src/home/dashboard/lists/dashboard-list.block.less similarity index 98% rename from awx/ui/client/src/dashboard/lists/dashboard-list.block.less rename to awx/ui/client/src/home/dashboard/lists/dashboard-list.block.less index 0903b01c74..7937e0906c 100644 --- a/awx/ui/client/src/dashboard/lists/dashboard-list.block.less +++ b/awx/ui/client/src/home/dashboard/lists/dashboard-list.block.less @@ -1,6 +1,6 @@ /** @define DashboardList */ -@import "../../shared/branding/colors.default.less"; +@import "../../../shared/branding/colors.default.less"; .DashboardList { flex: 1; diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js similarity index 97% rename from awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js rename to awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js index 5d4a8a11fd..cfed3878f7 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js +++ b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.directive.js @@ -11,7 +11,7 @@ export default scope: { data: '=' }, - templateUrl: templateUrl('dashboard/lists/job-templates/job-templates-list') + templateUrl: templateUrl('home/dashboard/lists/job-templates/job-templates-list') }; function link(scope, element, attr) { diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html b/awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html rename to awx/ui/client/src/home/dashboard/lists/job-templates/job-templates-list.partial.html diff --git a/awx/ui/client/src/dashboard/lists/job-templates/main.js b/awx/ui/client/src/home/dashboard/lists/job-templates/main.js similarity index 64% rename from awx/ui/client/src/dashboard/lists/job-templates/main.js rename to awx/ui/client/src/home/dashboard/lists/job-templates/main.js index 90c7f1deb5..1a47bb600b 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/main.js +++ b/awx/ui/client/src/home/dashboard/lists/job-templates/main.js @@ -1,6 +1,6 @@ import JobTemplatesListDirective from './job-templates-list.directive'; -import systemStatus from '../../../smart-status/main'; -import jobSubmissionHelper from '../../../helpers/JobSubmission'; +import systemStatus from '../../../../smart-status/main'; +import jobSubmissionHelper from '../../../../helpers/JobSubmission'; export default angular.module('JobTemplatesList', [systemStatus.name, jobSubmissionHelper.name]) .directive('jobTemplatesList', JobTemplatesListDirective); diff --git a/awx/ui/client/src/dashboard/lists/jobs/jobs-list.directive.js b/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js similarity index 94% rename from awx/ui/client/src/dashboard/lists/jobs/jobs-list.directive.js rename to awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js index 4658b45574..fd59f4496e 100644 --- a/awx/ui/client/src/dashboard/lists/jobs/jobs-list.directive.js +++ b/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.directive.js @@ -10,7 +10,7 @@ export default scope: { data: '=' }, - templateUrl: templateUrl('dashboard/lists/jobs/jobs-list') + templateUrl: templateUrl('home/dashboard/lists/jobs/jobs-list') }; function link(scope, element, attr) { diff --git a/awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html b/awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.partial.html similarity index 100% rename from awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html rename to awx/ui/client/src/home/dashboard/lists/jobs/jobs-list.partial.html diff --git a/awx/ui/client/src/dashboard/lists/jobs/main.js b/awx/ui/client/src/home/dashboard/lists/jobs/main.js similarity index 100% rename from awx/ui/client/src/dashboard/lists/jobs/main.js rename to awx/ui/client/src/home/dashboard/lists/jobs/main.js diff --git a/awx/ui/client/src/dashboard/lists/main.js b/awx/ui/client/src/home/dashboard/lists/main.js similarity index 100% rename from awx/ui/client/src/dashboard/lists/main.js rename to awx/ui/client/src/home/dashboard/lists/main.js diff --git a/awx/ui/client/src/dashboard/main.js b/awx/ui/client/src/home/dashboard/main.js similarity index 100% rename from awx/ui/client/src/dashboard/main.js rename to awx/ui/client/src/home/dashboard/main.js diff --git a/awx/ui/client/src/home/home.controller.js b/awx/ui/client/src/home/home.controller.js new file mode 100644 index 0000000000..b5bf73e5de --- /dev/null +++ b/awx/ui/client/src/home/home.controller.js @@ -0,0 +1,125 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$compile', '$stateParams', '$rootScope', '$location', '$log','Wait', + 'ClearScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$window', 'graphData', + function($scope, $compile, $stateParams, $rootScope, $location, $log, Wait, + ClearScope, Rest, GetBasePath, ProcessErrors, $window, graphData) { + + ClearScope('home'); + + var dataCount = 0; + + $scope.$on('ws-jobs', function () { + Rest.setUrl(GetBasePath('dashboard')); + Rest.get() + .success(function (data) { + $scope.dashboardData = data; + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard host graph data: ' + status }); + }); + + Rest.setUrl(GetBasePath("jobs") + "?order_by=-finished&page_size=5&finished__isnull=false"); + Rest.get() + .success(function (data) { + $scope.dashboardJobsListData = data.results; + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); + }); + + Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); + Rest.get() + .success(function (data) { + $scope.dashboardJobTemplatesListData = data.results; + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); + }); + + }); + + if ($scope.removeDashboardDataLoadComplete) { + $scope.removeDashboardDataLoadComplete(); + } + $scope.removeDashboardDataLoadComplete = $scope.$on('dashboardDataLoadComplete', function () { + dataCount++; + if (dataCount === 3) { + Wait("stop"); + dataCount = 0; + } + }); + + if ($scope.removeDashboardReady) { + $scope.removeDashboardReady(); + } + $scope.removeDashboardReady = $scope.$on('dashboardReady', function (e, data) { + $scope.dashboardCountsData = data; + $scope.graphData = graphData; + $scope.$emit('dashboardDataLoadComplete'); + + var cleanupJobListener = + $rootScope.$on('DataReceived:JobStatusGraph', function(e, data) { + $scope.graphData.jobStatus = data; + }); + + $scope.$on('$destroy', function() { + cleanupJobListener(); + }); + }); + + if ($scope.removeDashboardJobsListReady) { + $scope.removeDashboardJobsListReady(); + } + $scope.removeDashboardJobsListReady = $scope.$on('dashboardJobsListReady', function (e, data) { + $scope.dashboardJobsListData = data; + $scope.$emit('dashboardDataLoadComplete'); + }); + + if ($scope.removeDashboardJobTemplatesListReady) { + $scope.removeDashboardJobTemplatesListReady(); + } + $scope.removeDashboardJobTemplatesListReady = $scope.$on('dashboardJobTemplatesListReady', function (e, data) { + $scope.dashboardJobTemplatesListData = data; + $scope.$emit('dashboardDataLoadComplete'); + }); + + $scope.refresh = function () { + Wait('start'); + Rest.setUrl(GetBasePath('dashboard')); + Rest.get() + .success(function (data) { + $scope.dashboardData = data; + $scope.$emit('dashboardReady', data); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard: ' + status }); + }); + Rest.setUrl(GetBasePath("jobs") + "?order_by=-finished&page_size=5&finished__isnull=false"); + Rest.get() + .success(function (data) { + data = data.results; + $scope.$emit('dashboardJobsListReady', data); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); + }); + Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); + Rest.get() + .success(function (data) { + data = data.results; + $scope.$emit('dashboardJobTemplatesListReady', data); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard job templates list: ' + status }); + }); + }; + + $scope.refresh(); + + } +]; diff --git a/awx/ui/client/src/partials/home.html b/awx/ui/client/src/home/home.partial.html similarity index 99% rename from awx/ui/client/src/partials/home.html rename to awx/ui/client/src/home/home.partial.html index 4069ec9c7b..6a45b63334 100644 --- a/awx/ui/client/src/partials/home.html +++ b/awx/ui/client/src/home/home.partial.html @@ -1,4 +1,3 @@ -
diff --git a/awx/ui/client/src/home/home.route.js b/awx/ui/client/src/home/home.route.js new file mode 100644 index 0000000000..6c06e510e1 --- /dev/null +++ b/awx/ui/client/src/home/home.route.js @@ -0,0 +1,46 @@ +import {templateUrl} from '../shared/template-url/template-url.factory'; +import controller from './home.controller'; +import { N_ } from '../i18n'; + +export default { + name: 'dashboard', + url: '/home', + templateUrl: templateUrl('home/home'), + controller: controller, + params: { licenseMissing: null }, + data: { + activityStream: true, + refreshButton: true, + socket: { + "groups": { + "jobs": ["status_changed"] + } + }, + }, + ncyBreadcrumb: { + label: N_("DASHBOARD") + }, + resolve: { + graphData: ['$q', 'jobStatusGraphData', '$rootScope', + function($q, jobStatusGraphData, $rootScope) { + return $rootScope.featuresConfigured.promise.then(function() { + return $q.all({ + jobStatus: jobStatusGraphData.get("month", "all"), + }); + }); + } + ] + } + // name: 'setup.about', + // route: '/about', + // controller: controller, + // ncyBreadcrumb: { + // label: N_("ABOUT") + // }, + // onExit: function(){ + // // hacky way to handle user browsing away via URL bar + // $('.modal-backdrop').remove(); + // $('body').removeClass('modal-open'); + // }, + // templateUrl: templateUrl('about/about') +}; diff --git a/awx/ui/client/src/home/main.js b/awx/ui/client/src/home/main.js new file mode 100644 index 0000000000..cc4c6ebb19 --- /dev/null +++ b/awx/ui/client/src/home/main.js @@ -0,0 +1,12 @@ +import dashboard from './dashboard/main'; +import HomeController from './home.controller'; +import route from './home.route'; + +export default + angular.module('home', [ + dashboard.name + ]) + .controller('HomeController', HomeController) + .run(['$stateExtender', function($stateExtender){ + $stateExtender.addState(route); + }]); diff --git a/awx/ui/client/src/projects/add/projects-add.controller.js b/awx/ui/client/src/projects/add/projects-add.controller.js new file mode 100644 index 0000000000..4e513acb13 --- /dev/null +++ b/awx/ui/client/src/projects/add/projects-add.controller.js @@ -0,0 +1,154 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$compile', '$location', '$log', + '$stateParams', 'GenerateForm', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', + 'GetProjectPath', 'GetChoices', 'Wait', '$state', 'CreateSelect2', 'i18n', + function($scope, $rootScope, $compile, $location, $log, + $stateParams, GenerateForm, ProjectsForm, Rest, Alert, ProcessErrors, + GetBasePath, GetProjectPath, GetChoices, Wait, $state, CreateSelect2, i18n) { + + var form = ProjectsForm(), + base = $location.path().replace(/^\//, '').split('/')[0], + defaultUrl = GetBasePath('projects'), + master = {}; + + init(); + + function init() { + Rest.setUrl(GetBasePath('projects')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a project.'), 'alert-info'); + } + }); + + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); + } + + GetProjectPath({ scope: $scope, master: master }); + + if ($scope.removeChoicesReady) { + $scope.removeChoicesReady(); + } + $scope.removeChoicesReady = $scope.$on('choicesReady', function() { + var i; + for (i = 0; i < $scope.scm_type_options.length; i++) { + if ($scope.scm_type_options[i].value === '') { + $scope.scm_type_options[i].value = "manual"; + //$scope.scm_type = $scope.scm_type_options[i]; + break; + } + } + + CreateSelect2({ + element: '#project_scm_type', + multiple: false + }); + + $scope.scmRequired = false; + master.scm_type = $scope.scm_type; + }); + + // Load the list of options for Kind + GetChoices({ + scope: $scope, + url: defaultUrl, + field: 'scm_type', + variable: 'scm_type_options', + callback: 'choicesReady' + }); + CreateSelect2({ + element: '#local-path-select', + multiple: false + }); + + // Save + $scope.formSave = function() { + var i, fld, url, data = {}; + data = {}; + for (fld in form.fields) { + if (form.fields[fld].type === 'checkbox_group') { + for (i = 0; i < form.fields[fld].fields.length; i++) { + data[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name]; + } + } else { + if (form.fields[fld].type !== 'alertblock') { + data[fld] = $scope[fld]; + } + } + } + + if ($scope.scm_type.value === "manual") { + data.scm_type = ""; + data.local_path = $scope.local_path.value; + } else { + data.scm_type = $scope.scm_type.value; + delete data.local_path; + } + + url = (base === 'teams') ? GetBasePath('teams') + $stateParams.team_id + '/projects/' : defaultUrl; + Wait('start'); + Rest.setUrl(url); + Rest.post(data) + .success(function(data) { + $scope.addedItem = data.id; + $state.go('projects.edit', { project_id: data.id }, { reload: true }); + }) + .error(function(data, status) { + Wait('stop'); + ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), + msg: i18n._('Failed to create new project. POST returned status: ') + status }); + }); + }; + + $scope.scmChange = function() { + // When an scm_type is set, path is not required + if ($scope.scm_type) { + $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; + $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; + $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; + } + + // Dynamically update popover values + if ($scope.scm_type.value) { + switch ($scope.scm_type.value) { + case 'git': + $scope.urlPopover = '

' + + i18n._('Example URLs for GIT SCM include:') + + '

  • https://github.com/ansible/ansible.git
  • ' + + '
  • git@github.com:ansible/ansible.git
  • git://servername.example.com/ansible.git
' + + '

' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' + + 'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' + + 'SSH. GIT read only protocol (git://) does not use username or password information.'), '', ''); + break; + case 'svn': + $scope.urlPopover = '

' + i18n._('Example URLs for Subversion SCM include:') + '

' + + '
  • https://github.com/ansible/ansible
  • svn://servername.example.com/path
  • ' + + '
  • svn+ssh://servername.example.com/path
'; + break; + case 'hg': + $scope.urlPopover = '

' + i18n._('Example URLs for Mercurial SCM include:') + '

' + + '
  • https://bitbucket.org/username/project
  • ssh://hg@bitbucket.org/username/project
  • ' + + '
  • ssh://server.example.com/path
' + + '

' + i18n.sprintf(i18n._('%sNote:%s Mercurial does not support password authentication for SSH. ' + + 'Do not put the username and key in the URL. ' + + 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); + break; + default: + $scope.urlPopover = '

' + i18n._('URL popover text'); + } + } + + }; + $scope.formCancel = function() { + $state.go('projects'); + }; + } +]; diff --git a/awx/ui/client/src/projects/edit/projects-edit.controller.js b/awx/ui/client/src/projects/edit/projects-edit.controller.js new file mode 100644 index 0000000000..db4a647dd5 --- /dev/null +++ b/awx/ui/client/src/projects/edit/projects-edit.controller.js @@ -0,0 +1,297 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$compile', '$location', '$log', + '$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GenerateForm', + 'Prompt', 'ClearScope', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty', + 'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n', + function($scope, $rootScope, $compile, $location, $log, + $stateParams, ProjectsForm, Rest, Alert, ProcessErrors, GenerateForm, + Prompt, ClearScope, GetBasePath, GetProjectPath, Authorization, + GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n) { + + ClearScope('htmlTemplate'); + + var form = ProjectsForm(), + defaultUrl = GetBasePath('projects') + $stateParams.project_id + '/', + master = {}, + id = $stateParams.project_id; + + init(); + + function init() { + $scope.project_local_paths = []; + $scope.base_dir = ''; + } + + $scope.$watch('project_obj.summary_fields.user_capabilities.edit', function(val) { + if (val === false) { + $scope.canAdd = false; + } + }); + + if ($scope.pathsReadyRemove) { + $scope.pathsReadyRemove(); + } + $scope.pathsReadyRemove = $scope.$on('pathsReady', function () { + CreateSelect2({ + element: '#local-path-select', + multiple: false + }); + }); + + // After the project is loaded, retrieve each related set + if ($scope.projectLoadedRemove) { + $scope.projectLoadedRemove(); + } + $scope.projectLoadedRemove = $scope.$on('projectLoaded', function() { + var opts = []; + + if (Authorization.getUserInfo('is_superuser') === true) { + GetProjectPath({ scope: $scope, master: master }); + } else { + opts.push({ + label: $scope.local_path, + value: $scope.local_path + }); + $scope.project_local_paths = opts; + $scope.local_path = $scope.project_local_paths[0]; + $scope.base_dir = i18n._('You do not have access to view this property'); + $scope.$emit('pathsReady'); + } + + $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; + $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; + $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; + Wait('stop'); + + $scope.scmChange(); + }); + + if ($scope.removeChoicesReady) { + $scope.removeChoicesReady(); + } + $scope.removeChoicesReady = $scope.$on('choicesReady', function() { + let i; + for (i = 0; i < $scope.scm_type_options.length; i++) { + if ($scope.scm_type_options[i].value === '') { + $scope.scm_type_options[i].value = "manual"; + break; + } + } + // Retrieve detail record and prepopulate the form + Rest.setUrl(defaultUrl); + Rest.get({ params: { id: id } }) + .success(function(data) { + var fld, i; + for (fld in form.fields) { + if (form.fields[fld].type === 'checkbox_group') { + for (i = 0; i < form.fields[fld].fields.length; i++) { + $scope[form.fields[fld].fields[i].name] = data[form.fields[fld].fields[i].name]; + master[form.fields[fld].fields[i].name] = data[form.fields[fld].fields[i].name]; + } + } else { + if (data[fld] !== undefined) { + $scope[fld] = data[fld]; + master[fld] = data[fld]; + } + } + if (form.fields[fld].sourceModel && data.summary_fields && + data.summary_fields[form.fields[fld].sourceModel]) { + $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; + master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; + } + } + + data.scm_type = (Empty(data.scm_type)) ? 'manual' : data.scm_type; + for (i = 0; i < $scope.scm_type_options.length; i++) { + if ($scope.scm_type_options[i].value === data.scm_type) { + $scope.scm_type = $scope.scm_type_options[i]; + break; + } + } + + if ($scope.scm_type.value !== 'manual') { + $scope.pathRequired = false; + $scope.scmRequired = true; + } else { + $scope.pathRequired = true; + $scope.scmRequired = false; + } + + master.scm_type = $scope.scm_type; + CreateSelect2({ + element: '#project_scm_type', + multiple: false + }); + + $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch'; + $scope.scm_update_tooltip = i18n._("Start an SCM update"); + $scope.scm_type_class = ""; + if (data.status === 'running' || data.status === 'updating') { + $scope.scm_update_tooltip = i18n._("SCM update currently running"); + $scope.scm_type_class = "btn-disabled"; + } + if (Empty(data.scm_type)) { + $scope.scm_update_tooltip = i18n._('Manual projects do not require an SCM update'); + $scope.scm_type_class = "btn-disabled"; + } + + $scope.project_obj = data; + $scope.name = data.name; + $scope.$emit('projectLoaded'); + Wait('stop'); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed to retrieve project: %s. GET status: '), id) + status + }); + }); + }); + + // Load the list of options for Kind + Wait('start'); + GetChoices({ + url: GetBasePath('projects'), + scope: $scope, + field: 'scm_type', + variable: 'scm_type_options', + callback: 'choicesReady' + }); + + $scope.toggleNotification = function(event, id, column) { + var notifier = this.notification; + try { + $(event.target).tooltip('hide'); + } catch (e) { + // ignore + } + ToggleNotification({ + scope: $scope, + url: $scope.project_obj.url, + notifier: notifier, + column: column, + callback: 'NotificationRefresh' + }); + }; + + // Save changes to the parent + $scope.formSave = function() { + var fld, i, params; + GenerateForm.clearApiErrors($scope); + Wait('start'); + $rootScope.flashMessage = null; + params = {}; + for (fld in form.fields) { + if (form.fields[fld].type === 'checkbox_group') { + for (i = 0; i < form.fields[fld].fields.length; i++) { + params[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name]; + } + } else { + if (form.fields[fld].type !== 'alertblock') { + params[fld] = $scope[fld]; + } + } + } + + if ($scope.scm_type.value === "manual") { + params.scm_type = ""; + params.local_path = $scope.local_path.value; + } else { + params.scm_type = $scope.scm_type.value; + delete params.local_path; + } + + Rest.setUrl(defaultUrl); + Rest.put(params) + .success(function() { + Wait('stop'); + $state.go($state.current, {}, { reload: true }); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Failed to update project: %s. PUT status: '), id) + status }); + }); + }; + + // Related set: Delete button + $scope['delete'] = function(set, itm_id, name, title) { + var action = function() { + var url = GetBasePath('projects') + id + '/' + set + '/'; + $rootScope.flashMessage = null; + Rest.setUrl(url); + Rest.post({ id: itm_id, disassociate: 1 }) + .success(function() { + $('#prompt-modal').modal('hide'); + // @issue: OLD SEARCH + // $scope.search(form.related[set].iterator); + }) + .error(function(data, status) { + $('#prompt-modal').modal('hide'); + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. POST returned status: '), url) + status }); + }); + }; + + Prompt({ + hdr: i18n._('Delete'), + body: '

' + i18n.sprintf(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '
' + '
' + name + '
', + action: action, + actionText: i18n._('DELETE') + }); + }; + + $scope.scmChange = function() { + if ($scope.scm_type) { + $scope.pathRequired = ($scope.scm_type.value === 'manual') ? true : false; + $scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false; + $scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? i18n._('Revision #') : i18n._('SCM Branch'); + } + + // Dynamically update popover values + if ($scope.scm_type.value) { + switch ($scope.scm_type.value) { + case 'git': + $scope.urlPopover = '

' + i18n._('Example URLs for GIT SCM include:') + '

  • https://github.com/ansible/ansible.git
  • ' + + '
  • git@github.com:ansible/ansible.git
  • git://servername.example.com/ansible.git
' + + '

' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' + + 'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' + + 'SSH. GIT read only protocol (git://) does not use username or password information.'), '', ''); + break; + case 'svn': + $scope.urlPopover = '

' + i18n._('Example URLs for Subversion SCM include:') + '

' + + '
  • https://github.com/ansible/ansible
  • svn://servername.example.com/path
  • ' + + '
  • svn+ssh://servername.example.com/path
'; + break; + case 'hg': + $scope.urlPopover = '

' + i18n._('Example URLs for Mercurial SCM include:') + '

' + + '
  • https://bitbucket.org/username/project
  • ssh://hg@bitbucket.org/username/project
  • ' + + '
  • ssh://server.example.com/path
' + + '

' + i18n.sprintf(i18n._('%sNote:%s Mercurial does not support password authentication for SSH. ' + + 'Do not put the username and key in the URL. ' + + 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); + break; + default: + $scope.urlPopover = '

' + i18n._('URL popover text'); + } + } + }; + + $scope.SCMUpdate = function() { + if ($scope.project_obj.scm_type === "Manual" || Empty($scope.project_obj.scm_type)) { + // ignore + } else if ($scope.project_obj.status === 'updating' || $scope.project_obj.status === 'running' || $scope.project_obj.status === 'pending') { + Alert(i18n._('Update in Progress'), i18n._('The SCM update process is running.'), 'alert-info'); + } else { + ProjectUpdate({ scope: $scope, project_id: $scope.project_obj.id }); + } + }; + + $scope.formCancel = function() { + $state.transitionTo('projects'); + }; + } +]; diff --git a/awx/ui/client/src/projects/list/projects-list.controller.js b/awx/ui/client/src/projects/list/projects-list.controller.js new file mode 100644 index 0000000000..6ecb5eb7fa --- /dev/null +++ b/awx/ui/client/src/projects/list/projects-list.controller.js @@ -0,0 +1,299 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$location', '$log', '$stateParams', + 'Rest', 'Alert', 'ProjectList', 'Prompt', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', + 'GetBasePath', 'ProjectUpdate', 'Wait', 'GetChoices', 'Empty', 'Find', 'GetProjectIcon', + 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService', 'Dataset', 'i18n', 'QuerySet', + function($scope, $rootScope, $location, $log, $stateParams, + Rest, Alert, ProjectList, Prompt, ReturnToCaller, ClearScope, ProcessErrors, + GetBasePath, ProjectUpdate, Wait, GetChoices, Empty, Find, GetProjectIcon, + GetProjectToolTip, $filter, $state, rbacUiControlService, Dataset, i18n, qs) { + + var list = ProjectList, + defaultUrl = GetBasePath('projects'); + + init(); + + function init() { + $scope.canAdd = false; + + rbacUiControlService.canAdd('projects') + .then(function(canAdd) { + $scope.canAdd = canAdd; + }); + + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + + _.forEach($scope[list.name], buildTooltips); + $rootScope.flashMessage = null; + } + + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + } + ); + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + if ($scope[list.name] !== undefined) { + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + if (list.fields.scm_type && $scope.options && + $scope.options.hasOwnProperty('scm_type')) { + $scope.options.scm_type.choices.every(function(choice) { + if (choice[0] === item.scm_type) { + itm.type_label = choice[1]; + return false; + } + return true; + }); + } + + buildTooltips(itm); + + }); + } + } + + function buildTooltips(project) { + project.statusIcon = GetProjectIcon(project.status); + project.statusTip = GetProjectToolTip(project.status); + project.scm_update_tooltip = i18n._("Start an SCM update"); + project.scm_schedule_tooltip = i18n._("Schedule future SCM updates"); + project.scm_type_class = ""; + + if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') { + project.statusTip = i18n._('Canceled. Click for details'); + } + + if (project.status === 'running' || project.status === 'updating') { + project.scm_update_tooltip = i18n._("SCM update currently running"); + project.scm_type_class = "btn-disabled"; + } + if (project.scm_type === 'manual') { + project.scm_update_tooltip = i18n._('Manual projects do not require an SCM update'); + project.scm_schedule_tooltip = i18n._('Manual projects do not require a schedule'); + project.scm_type_class = 'btn-disabled'; + project.statusTip = i18n._('Not configured for SCM'); + project.statusIcon = 'none'; + } + } + + $scope.reloadList = function(){ + let path = GetBasePath(list.basePath) || GetBasePath(list.name); + qs.search(path, $stateParams[`${list.iterator}_search`]) + .then(function(searchResponse) { + $scope[`${list.iterator}_dataset`] = searchResponse.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + }); + }; + + $scope.$on(`ws-jobs`, function(e, data) { + var project; + $log.debug(data); + if ($scope.projects) { + // Assuming we have a list of projects available + project = Find({ list: $scope.projects, key: 'id', val: data.project_id }); + if (project) { + // And we found the affected project + $log.debug('Received event for project: ' + project.name); + $log.debug('Status changed to: ' + data.status); + if (data.status === 'successful' || data.status === 'failed') { + $scope.reloadList(); + } else { + project.scm_update_tooltip = "SCM update currently running"; + project.scm_type_class = "btn-disabled"; + } + project.status = data.status; + project.statusIcon = GetProjectIcon(data.status); + project.statusTip = GetProjectToolTip(data.status); + } + } + }); + + $scope.addProject = function() { + $state.go('projects.add'); + }; + + $scope.editProject = function(id) { + $state.go('projects.edit', { project_id: id }); + }; + + if ($scope.removeGoToJobDetails) { + $scope.removeGoToJobDetails(); + } + $scope.removeGoToJobDetails = $scope.$on('GoToJobDetails', function(e, data) { + if (data.summary_fields.current_update || data.summary_fields.last_update) { + + Wait('start'); + + // Grab the id from summary_fields + var id = (data.summary_fields.current_update) ? data.summary_fields.current_update.id : data.summary_fields.last_update.id; + + $state.go('scmUpdateStdout', { id: id }); + + } else { + Alert(i18n._('No Updates Available'), i18n._('There is no SCM update information available for this project. An update has not yet been ' + + ' completed. If you have not already done so, start an update for this project.'), 'alert-info'); + } + }); + + $scope.showSCMStatus = function(id) { + // Refresh the project list + var project = Find({ list: $scope.projects, key: 'id', val: id }); + if (Empty(project.scm_type) || project.scm_type === 'Manual') { + Alert(i18n._('No SCM Configuration'), i18n._('The selected project is not configured for SCM. To configure for SCM, edit the project and provide SCM settings, ' + + 'and then run an update.'), 'alert-info'); + } else { + // Refresh what we have in memory to insure we're accessing the most recent status record + Rest.setUrl(project.url); + Rest.get() + .success(function(data) { + $scope.$emit('GoToJobDetails', data); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n._('Project lookup failed. GET returned: ') + status }); + }); + } + }; + + $scope.deleteProject = function(id, name) { + var action = function() { + $('#prompt-modal').modal('hide'); + Wait('start'); + var url = defaultUrl + id + '/'; + Rest.setUrl(url); + Rest.destroy() + .success(function() { + if (parseInt($state.params.project_id) === id) { + $state.go("^", null, { reload: true }); + } else { + $state.go('.', null, {reload: true}); + } + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), url) + status }); + }) + .finally(function() { + Wait('stop'); + }); + }; + + Prompt({ + hdr: i18n._('Delete'), + body: '

' + i18n._('Are you sure you want to delete the project below?') + '
' + '
' + $filter('sanitize')(name) + '
', + action: action, + actionText: 'DELETE' + }); + }; + + if ($scope.removeCancelUpdate) { + $scope.removeCancelUpdate(); + } + $scope.removeCancelUpdate = $scope.$on('Cancel_Update', function(e, url) { + // Cancel the project update process + Rest.setUrl(url); + Rest.post() + .success(function () { + Alert(i18n._('SCM Update Cancel'), i18n._('Your request to cancel the update was submitted to the task manager.'), 'alert-info'); + $scope.refresh(); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. POST status: '), url) + status }); + }); + }); + + if ($scope.removeCheckCancel) { + $scope.removeCheckCancel(); + } + $scope.removeCheckCancel = $scope.$on('Check_Cancel', function(e, data) { + // Check that we 'can' cancel the update + var url = data.related.cancel; + Rest.setUrl(url); + Rest.get() + .success(function(data) { + if (data.can_cancel) { + $scope.$emit('Cancel_Update', url); + } else { + Alert(i18n._('Cancel Not Allowed'), '
' + i18n.sprintf(i18n._('Either you do not have access or the SCM update process completed. ' + + 'Click the %sRefresh%s button to view the latest status.'), '', '') + '
', 'alert-info', null, null, null, null, true); + } + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.sprintf(i18n._('Call to %s failed. GET status: '), url) + status }); + }); + }); + + $scope.cancelUpdate = function(id, name) { + Rest.setUrl(GetBasePath("projects") + id); + Rest.get() + .success(function(data) { + if (data.related.current_update) { + Rest.setUrl(data.related.current_update); + Rest.get() + .success(function(data) { + $scope.$emit('Check_Cancel', data); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Call to %s failed. GET status: '), data.related.current_update) + status }); + }); + } else { + Alert(i18n._('Update Not Found'), '
' + i18n.sprintf(i18n._('An SCM update does not appear to be running for project: %s. Click the %sRefresh%s ' + + 'button to view the latest status.'), $filter('sanitize')(name), '', '') + '
', 'alert-info',undefined,undefined,undefined,undefined,true); + } + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n._('Call to get project failed. GET status: ') + status }); + }); + }; + + $scope.SCMUpdate = function(project_id, event) { + try { + $(event.target).tooltip('hide'); + } catch (e) { + // ignore + } + $scope.projects.every(function(project) { + if (project.id === project_id) { + if (project.scm_type === "Manual" || Empty(project.scm_type)) { + // Do not respond. Button appears greyed out as if it is disabled. Not disabled though, because we need mouse over event + // to work. So user can click, but we just won't do anything. + //Alert('Missing SCM Setup', 'Before running an SCM update, edit the project and provide the SCM access information.', 'alert-info'); + } else if (project.status === 'updating' || project.status === 'running' || project.status === 'pending') { + // Alert('Update in Progress', 'The SCM update process is running. Use the Refresh button to monitor the status.', 'alert-info'); + } else { + ProjectUpdate({ scope: $scope, project_id: project.id }); + } + return false; + } + return true; + }); + }; + + $scope.editSchedules = function(id) { + var project = Find({ list: $scope.projects, key: 'id', val: id }); + if (!(project.scm_type === "Manual" || Empty(project.scm_type)) && !(project.status === 'updating' || project.status === 'running' || project.status === 'pending')) { + $state.go('projectSchedules', { id: id }); + } + }; + } +]; diff --git a/awx/ui/client/src/projects/main.js b/awx/ui/client/src/projects/main.js new file mode 100644 index 0000000000..05addaf1fe --- /dev/null +++ b/awx/ui/client/src/projects/main.js @@ -0,0 +1,51 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import ProjectsList from './list/projects-list.controller'; +import ProjectsAdd from './add/projects-add.controller'; +import ProjectsEdit from './edit/projects-edit.controller'; +import { N_ } from '../i18n'; + +export default +angular.module('Projects', []) + .controller('ProjectsList', ProjectsList) + .controller('ProjectsAdd', ProjectsAdd) + .controller('ProjectsEdit', ProjectsEdit) + .config(['$stateProvider', 'stateDefinitionsProvider', + function($stateProvider, stateDefinitionsProvider) { + let stateDefinitions = stateDefinitionsProvider.$get(); + + // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry + // see: stateDefinition.factory for usage documentation + $stateProvider.state({ + name: 'projects', + url: '/projects', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'projects', // top-most node in the generated tree (will replace this state definition) + modes: ['add', 'edit'], + list: 'ProjectList', + form: 'ProjectsForm', + controllers: { + list: ProjectsList, // DI strings or objects + add: ProjectsAdd, + edit: ProjectsEdit + }, + data: { + activityStream: true, + activityStreamTarget: 'project', + socket: { + "groups": { + "jobs": ["status_changed"] + } + } + }, + ncyBreadcrumb: { + label: N_('PROJECTS') + } + }) + }); + } + ]); diff --git a/awx/ui/client/src/teams/add/teams-add.controller.js b/awx/ui/client/src/teams/add/teams-add.controller.js new file mode 100644 index 0000000000..7f09f166a7 --- /dev/null +++ b/awx/ui/client/src/teams/add/teams-add.controller.js @@ -0,0 +1,68 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'GenerateForm', + 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state', + function($scope, $rootScope, $stateParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors, + ClearScope, GetBasePath, Wait, $state) { + + ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior + //$scope. + + Rest.setUrl(GetBasePath('teams')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add a team.', 'alert-info'); + } + }); + + // Inject dynamic view + var defaultUrl = GetBasePath('teams'), + form = TeamForm; + + init(); + + function init() { + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); + + $rootScope.flashMessage = null; + } + + // Save + $scope.formSave = function() { + var fld, data; + GenerateForm.clearApiErrors($scope); + Wait('start'); + Rest.setUrl(defaultUrl); + data = {}; + for (fld in form.fields) { + data[fld] = $scope[fld]; + } + Rest.post(data) + .success(function(data) { + Wait('stop'); + $rootScope.flashMessage = "New team successfully created!"; + $rootScope.$broadcast("EditIndicatorChange", "users", data.id); + $state.go('teams.edit', { team_id: data.id }, { reload: true }); + }) + .error(function(data, status) { + Wait('stop'); + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to add new team. Post returned status: ' + + status + }); + }); + }; + + $scope.formCancel = function() { + $state.go('teams'); + }; + } +]; diff --git a/awx/ui/client/src/teams/edit/teams-edit.controller.js b/awx/ui/client/src/teams/edit/teams-edit.controller.js new file mode 100644 index 0000000000..c4b1d1fa4c --- /dev/null +++ b/awx/ui/client/src/teams/edit/teams-edit.controller.js @@ -0,0 +1,97 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', + 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state', + function($scope, $rootScope, $stateParams, + TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state) { + + ClearScope(); + + var form = TeamForm, + id = $stateParams.team_id, + defaultUrl = GetBasePath('teams') + id; + + init(); + + function init() { + $scope.team_id = id; + Rest.setUrl(defaultUrl); + Wait('start'); + Rest.get(defaultUrl).success(function(data) { + setScopeFields(data); + $scope.organization_name = data.summary_fields.organization.name; + + $scope.team_obj = data; + Wait('stop'); + }); + + $scope.$watch('team_obj.summary_fields.user_capabilities.edit', function(val) { + if (val === false) { + $scope.canAdd = false; + } + }); + + + } + + // @issue I think all this really want to do is _.forEach(form.fields, (field) =>{ $scope[field] = data[field]}) + function setScopeFields(data) { + _(data) + .pick(function(value, key) { + return form.fields.hasOwnProperty(key) === true; + }) + .forEach(function(value, key) { + $scope[key] = value; + }) + .value(); + return; + } + + // prepares a data payload for a PUT request to the API + function processNewData(fields) { + var data = {}; + _.forEach(fields, function(value, key) { + if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) { + data[key] = $scope[key]; + } + }); + return data; + } + + $scope.formCancel = function() { + $state.go('teams', null, { reload: true }); + }; + + $scope.formSave = function() { + $rootScope.flashMessage = null; + if ($scope[form.name + '_form'].$valid) { + var data = processNewData(form.fields); + Rest.setUrl(defaultUrl); + Rest.put(data).success(function() { + $state.go($state.current, null, { reload: true }); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: 'Failed to retrieve user: ' + + $stateParams.id + '. GET status: ' + status + }); + }); + } + }; + + init(); + + $scope.convertApiUrl = function(str) { + if (str) { + return str.replace("api/v1", "#"); + } else { + return null; + } + }; + } +]; diff --git a/awx/ui/client/src/teams/list/teams-list.controller.js b/awx/ui/client/src/teams/list/teams-list.controller.js new file mode 100644 index 0000000000..a4793de0ec --- /dev/null +++ b/awx/ui/client/src/teams/list/teams-list.controller.js @@ -0,0 +1,81 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['$scope', '$rootScope', '$log', + '$stateParams', 'Rest', 'Alert', 'TeamList', 'Prompt', 'ClearScope', + 'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', + function($scope, $rootScope, $log, $stateParams, + Rest, Alert, TeamList, Prompt, ClearScope, ProcessErrors, + GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset) { + + ClearScope(); + + var list = TeamList, + defaultUrl = GetBasePath('teams'); + + init(); + + function init() { + $scope.canAdd = false; + + rbacUiControlService.canAdd('teams') + .then(function(canAdd) { + $scope.canAdd = canAdd; + }); + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + _.forEach($scope[list.name], (team) => { + team.organization_name = team.summary_fields.organization.name; + }); + + $scope.selected = []; + } + + $scope.addTeam = function() { + $state.go('teams.add'); + }; + + $scope.editTeam = function(id) { + $state.go('teams.edit', { team_id: id }); + }; + + $scope.deleteTeam = function(id, name) { + + var action = function() { + Wait('start'); + var url = defaultUrl + id + '/'; + Rest.setUrl(url); + Rest.destroy() + .success(function() { + Wait('stop'); + $('#prompt-modal').modal('hide'); + if (parseInt($state.params.team_id) === id) { + $state.go('^', null, { reload: true }); + } else { + $state.go('.', null, { reload: true }); + } + }) + .error(function(data, status) { + Wait('stop'); + $('#prompt-modal').modal('hide'); + ProcessErrors($scope, data, status, null, { + hdr: 'Error!', + msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status + }); + }); + }; + + Prompt({ + hdr: 'Delete', + body: '
Are you sure you want to delete the team below?
' + $filter('sanitize')(name) + '
', + action: action, + actionText: 'DELETE' + }); + }; + } +]; diff --git a/awx/ui/client/src/teams/main.js b/awx/ui/client/src/teams/main.js new file mode 100644 index 0000000000..cd800ddb11 --- /dev/null +++ b/awx/ui/client/src/teams/main.js @@ -0,0 +1,47 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import TeamsList from './list/teams-list.controller'; +import TeamsAdd from './add/teams-add.controller'; +import TeamsEdit from './edit/teams-edit.controller'; +import { N_ } from '../i18n'; + +export default +angular.module('Teams', []) + .controller('TeamsList', TeamsList) + .controller('TeamsAdd', TeamsAdd) + .controller('TeamsEdit', TeamsEdit) + .config(['$stateProvider', 'stateDefinitionsProvider', + function($stateProvider, stateDefinitionsProvider) { + let stateDefinitions = stateDefinitionsProvider.$get(); + + // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry + // see: stateDefinition.factory for usage documentation + $stateProvider.state({ + name: 'teams', + url: '/teams', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'teams', + modes: ['add', 'edit'], + list: 'TeamList', + form: 'TeamForm', + controllers: { + list: TeamsList, + add: TeamsAdd, + edit: TeamsEdit + }, + data: { + activityStream: true, + activityStreamTarget: 'team' + }, + ncyBreadcrumb: { + parent: 'setup', + label: N_('TEAMS') + } + }) + }); + } + ]); diff --git a/awx/ui/client/src/users/add/users-add.controller.js b/awx/ui/client/src/users/add/users-add.controller.js new file mode 100644 index 0000000000..df174a0da6 --- /dev/null +++ b/awx/ui/client/src/users/add/users-add.controller.js @@ -0,0 +1,119 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import { N_ } from "../../i18n"; + +const user_type_options = [ + { type: 'normal', label: N_('Normal User') }, + { type: 'system_auditor', label: N_('System Auditor') }, + { type: 'system_administrator', label: N_('System Administrator') }, +]; + +export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'GenerateForm', + 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath', + 'ResetForm', 'Wait', 'CreateSelect2', '$state', '$location', 'i18n', + function($scope, $rootScope, $stateParams, UserForm, + GenerateForm, Rest, Alert, ProcessErrors, ReturnToCaller, ClearScope, + GetBasePath, ResetForm, Wait, CreateSelect2, $state, $location, i18n) { + + ClearScope(); + + var defaultUrl = GetBasePath('organizations'), + form = UserForm; + + init(); + + function init() { + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); + + $scope.ldap_user = false; + $scope.not_ldap_user = !$scope.ldap_user; + $scope.ldap_dn = null; + $scope.socialAuthUser = false; + $scope.external_account = null; + + Rest.setUrl(GetBasePath('users')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a user.'), 'alert-info'); + } + }); + + $scope.user_type_options = user_type_options; + $scope.user_type = user_type_options[0]; + $scope.$watch('user_type', user_type_sync($scope)); + CreateSelect2({ + element: '#user_user_type', + multiple: false + }); + } + + function user_type_sync($scope) { + return (type_option) => { + $scope.is_superuser = false; + $scope.is_system_auditor = false; + switch (type_option.type) { + case 'system_administrator': + $scope.is_superuser = true; + break; + case 'system_auditor': + $scope.is_system_auditor = true; + break; + } + }; + } + + // Save + $scope.formSave = function() { + var fld, data = {}; + if ($scope[form.name + '_form'].$valid) { + if ($scope.organization !== undefined && $scope.organization !== null && $scope.organization !== '') { + Rest.setUrl(defaultUrl + $scope.organization + '/users/'); + for (fld in form.fields) { + if (form.fields[fld].realName) { + data[form.fields[fld].realName] = $scope[fld]; + } else { + data[fld] = $scope[fld]; + } + } + data.is_superuser = $scope.is_superuser; + data.is_system_auditor = $scope.is_system_auditor; + Wait('start'); + Rest.post(data) + .success(function(data) { + var base = $location.path().replace(/^\//, '').split('/')[0]; + if (base === 'users') { + $rootScope.flashMessage = i18n._('New user successfully created!'); + $rootScope.$broadcast("EditIndicatorChange", "users", data.id); + $state.go('users.edit', { user_id: data.id }, { reload: true }); + } else { + ReturnToCaller(1); + } + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'), msg: i18n._('Failed to add new user. POST returned status: ') + status }); + }); + } else { + $scope.organization_name_api_error = i18n._('A value is required'); + } + } + }; + + $scope.formCancel = function() { + $state.go('users'); + }; + + // Password change + $scope.clearPWConfirm = function(fld) { + // If password value changes, make sure password_confirm must be re-entered + $scope[fld] = ''; + $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); + }; + } +]; diff --git a/awx/ui/client/src/users/edit/users-edit.controller.js b/awx/ui/client/src/users/edit/users-edit.controller.js new file mode 100644 index 0000000000..545670117e --- /dev/null +++ b/awx/ui/client/src/users/edit/users-edit.controller.js @@ -0,0 +1,179 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import { N_ } from "../../i18n"; + +const user_type_options = [ + { type: 'normal', label: N_('Normal User') }, + { type: 'system_auditor', label: N_('System Auditor') }, + { type: 'system_administrator', label: N_('System Administrator') }, +]; + +export default ['$scope', '$rootScope', '$location', + '$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath', + 'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n', + function($scope, $rootScope, $location, + $stateParams, UserForm, Rest, ProcessErrors, + ClearScope, GetBasePath, ResetForm, Wait, CreateSelect2, $state, i18n) { + + for (var i = 0; i < user_type_options.length; i++) { + user_type_options[i].label = i18n._(user_type_options[i].label); + } + ClearScope(); + + var form = UserForm, + master = {}, + id = $stateParams.user_id, + defaultUrl = GetBasePath('users') + id; + + init(); + + function init() { + $scope.hidePagination = false; + $scope.hideSmartSearch = false; + $scope.user_type_options = user_type_options; + $scope.user_type = user_type_options[0]; + $scope.$watch('user_type', user_type_sync($scope)); + $scope.$watch('is_superuser', hidePermissionsTabSmartSearchAndPaginationIfSuperUser($scope)); + Rest.setUrl(defaultUrl); + Wait('start'); + Rest.get(defaultUrl).success(function(data) { + $scope.user_id = id; + $scope.ldap_user = (data.ldap_dn !== null && data.ldap_dn !== undefined && data.ldap_dn !== '') ? true : false; + $scope.not_ldap_user = !$scope.ldap_user; + master.ldap_user = $scope.ldap_user; + $scope.socialAuthUser = (data.auth.length > 0) ? true : false; + $scope.external_account = data.external_account; + + $scope.user_type = $scope.user_type_options[0]; + $scope.is_system_auditor = false; + $scope.is_superuser = false; + if (data.is_system_auditor) { + $scope.user_type = $scope.user_type_options[1]; + $scope.is_system_auditor = true; + } + if (data.is_superuser) { + $scope.user_type = $scope.user_type_options[2]; + $scope.is_superuser = true; + } + + $scope.user_obj = data; + $scope.name = data.username; + + CreateSelect2({ + element: '#user_user_type', + multiple: false + }); + + $scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) { + if (val === false) { + $scope.canAdd = false; + } + }); + + setScopeFields(data); + Wait('stop'); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed to retrieve user: %s. GET status: '), $stateParams.id) + status + }); + }); + } + + function user_type_sync($scope) { + return (type_option) => { + $scope.is_superuser = false; + $scope.is_system_auditor = false; + switch (type_option.type) { + case 'system_administrator': + $scope.is_superuser = true; + break; + case 'system_auditor': + $scope.is_system_auditor = true; + break; + } + }; + } + + // Organizations and Teams tab pagination is hidden through other mechanism + function hidePermissionsTabSmartSearchAndPaginationIfSuperUser(scope) { + return function(isSuperuserNewValue) { + let shouldHide = isSuperuserNewValue; + if (shouldHide === true) { + scope.hidePagination = true; + scope.hideSmartSearch = true; + } else if (shouldHide === false) { + scope.hidePagination = false; + scope.hideSmartSearch = false; + } + }; + } + + + function setScopeFields(data) { + _(data) + .pick(function(value, key) { + return form.fields.hasOwnProperty(key) === true; + }) + .forEach(function(value, key) { + $scope[key] = value; + }) + .value(); + return; + } + + $scope.convertApiUrl = function(str) { + if (str) { + return str.replace("api/v1", "#"); + } else { + return null; + } + }; + + // prepares a data payload for a PUT request to the API + var processNewData = function(fields) { + var data = {}; + _.forEach(fields, function(value, key) { + if ($scope[key] !== '' && $scope[key] !== null && $scope[key] !== undefined) { + data[key] = $scope[key]; + } + }); + data.is_superuser = $scope.is_superuser; + data.is_system_auditor = $scope.is_system_auditor; + return data; + }; + + $scope.formCancel = function() { + $state.go('users', null, { reload: true }); + }; + + $scope.formSave = function() { + $rootScope.flashMessage = null; + if ($scope[form.name + '_form'].$valid) { + Rest.setUrl(defaultUrl + '/'); + var data = processNewData(form.fields); + Rest.put(data).success(function() { + $state.go($state.current, null, { reload: true }); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed to retrieve user: %s. GET status: '), $stateParams.id) + status + }); + }); + } + }; + + $scope.clearPWConfirm = function(fld) { + // If password value changes, make sure password_confirm must be re-entered + $scope[fld] = ''; + $scope[form.name + '_form'][fld].$setValidity('awpassmatch', false); + $rootScope.flashMessage = null; + }; + } +]; diff --git a/awx/ui/client/src/users/list/users-list.controller.js b/awx/ui/client/src/users/list/users-list.controller.js new file mode 100644 index 0000000000..437690da83 --- /dev/null +++ b/awx/ui/client/src/users/list/users-list.controller.js @@ -0,0 +1,90 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import { N_ } from "../../i18n"; + +const user_type_options = [ + { type: 'normal', label: N_('Normal User') }, + { type: 'system_auditor', label: N_('System Auditor') }, + { type: 'system_administrator', label: N_('System Administrator') }, +]; + +export default ['$scope', '$rootScope', '$stateParams', + 'Rest', 'Alert', 'UserList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', + 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n', + function($scope, $rootScope, $stateParams, + Rest, Alert, UserList, Prompt, ClearScope, ProcessErrors, GetBasePath, + Wait, $state, $filter, rbacUiControlService, Dataset, i18n) { + + for (var i = 0; i < user_type_options.length; i++) { + user_type_options[i].label = i18n._(user_type_options[i].label); + } + + ClearScope(); + + var list = UserList, + defaultUrl = GetBasePath('users'); + + init(); + + function init() { + $scope.canAdd = false; + + rbacUiControlService.canAdd('users') + .then(function(canAdd) { + $scope.canAdd = canAdd; + }); + + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + + + $rootScope.flashMessage = null; + $scope.selected = []; + } + + $scope.addUser = function() { + $state.go('users.add'); + }; + + $scope.editUser = function(id) { + $state.go('users.edit', { user_id: id }); + }; + + $scope.deleteUser = function(id, name) { + + var action = function() { + $('#prompt-modal').modal('hide'); + Wait('start'); + var url = defaultUrl + id + '/'; + Rest.setUrl(url); + Rest.destroy() + .success(function() { + if (parseInt($state.params.user_id) === id) { + $state.go('^', null, { reload: true }); + } else { + $state.go('.', null, { reload: true }); + } + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), url) + status + }); + }); + }; + + Prompt({ + hdr: i18n._('Delete'), + body: '
' + i18n._('Are you sure you want to delete the user below?') + '
' + $filter('sanitize')(name) + '
', + action: action, + actionText: i18n._('DELETE') + }); + }; + } +]; diff --git a/awx/ui/client/src/users/main.js b/awx/ui/client/src/users/main.js new file mode 100644 index 0000000000..1d978a091b --- /dev/null +++ b/awx/ui/client/src/users/main.js @@ -0,0 +1,47 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import UsersList from './list/users-list.controller'; +import UsersAdd from './add/users-add.controller'; +import UsersEdit from './edit/users-edit.controller'; +import { N_ } from '../i18n'; + +export default +angular.module('Users', []) + .controller('UsersList', UsersList) + .controller('UsersAdd', UsersAdd) + .controller('UsersEdit', UsersEdit) + .config(['$stateProvider', 'stateDefinitionsProvider', + function($stateProvider, stateDefinitionsProvider) { + let stateDefinitions = stateDefinitionsProvider.$get(); + + // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry + // see: stateDefinition.factory for usage documentation + $stateProvider.state({ + name: 'users', + url: '/users', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'users', + modes: ['add', 'edit'], + list: 'UserList', + form: 'UserForm', + controllers: { + list: UsersList, + add: UsersAdd, + edit: UsersEdit + }, + data: { + activityStream: true, + activityStreamTarget: 'user' + }, + ncyBreadcrumb: { + parent: 'setup', + label: N_('USERS') + } + }) + }); + } + ]);