diff --git a/awx/ui/client/src/inventories/hosts/list/host-list.controller.js b/awx/ui/client/src/inventories/hosts/list/host-list.controller.js index 86d186ed74..eb3df91631 100644 --- a/awx/ui/client/src/inventories/hosts/list/host-list.controller.js +++ b/awx/ui/client/src/inventories/hosts/list/host-list.controller.js @@ -78,7 +78,7 @@ function HostsList($scope, HostsList, $rootScope, GetBasePath, }); $rootScope.promptActionBtnClass = 'Modal-errorButton'; }; - + $scope.toggleHost = function(event, host) { try { $(event.target).tooltip('hide'); @@ -93,6 +93,10 @@ function HostsList($scope, HostsList, $rootScope, GetBasePath, }); }; + $scope.smartInventory = function() { + $state.go('hosts.addSmartInventory'); + }; + } export default ['$scope', 'HostsList', '$rootScope', 'GetBasePath', diff --git a/awx/ui/client/src/inventories/hosts/main.js b/awx/ui/client/src/inventories/hosts/main.js index d6fca96d15..fe21c6a43e 100644 --- a/awx/ui/client/src/inventories/hosts/main.js +++ b/awx/ui/client/src/inventories/hosts/main.js @@ -12,12 +12,14 @@ import HostManageService from './hosts.service'; import SetStatus from './set-status.factory'; import SetEnabledMsg from './set-enabled-msg.factory'; + import SmartInventory from './smart-inventory/main'; export default angular.module('host', [ hostAdd.name, hostEdit.name, - hostList.name + hostList.name, + SmartInventory.name, ]) .factory('HostsForm', HostsForm) .factory('HostsList', HostsList) diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/add/main.js b/awx/ui/client/src/inventories/hosts/smart-inventory/add/main.js new file mode 100644 index 0000000000..911492557f --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/add/main.js @@ -0,0 +1,11 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import controller from './smart-inventory-add.controller'; + +export default +angular.module('smartInventoryAdd', []) + .controller('SmartInventoryAddController', controller); diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/add/smart-inventory-add.controller.js b/awx/ui/client/src/inventories/hosts/smart-inventory/add/smart-inventory-add.controller.js new file mode 100644 index 0000000000..a97ea886d0 --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/add/smart-inventory-add.controller.js @@ -0,0 +1,104 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +/** + * @ngdoc function + * @name controllers.function:Inventories + * @description This controller's for the Inventory page + */ + +function SmartInventoryAdd($scope, $location, + GenerateForm, SmartInventoryForm, rbacUiControlService, Rest, Alert, ProcessErrors, + ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON, + $state) { + + $scope.canAdd = false; + rbacUiControlService.canAdd(GetBasePath('inventory')) + .then(function(canAdd) { + $scope.canAdd = canAdd; + }); + + Rest.setUrl(GetBasePath('inventory')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add an inventory.', 'alert-info'); + } + }); + + ClearScope(); + + // Inject dynamic view + var defaultUrl = GetBasePath('inventory'), + form = SmartInventoryForm; + + init(); + + function init() { + $scope.canEditOrg = true; + form.formLabelSize = null; + form.formFieldSize = null; + + // apply form definition's default field values + GenerateForm.applyDefaults(form, $scope); + + $scope.parseType = 'yaml'; + ParseTypeChange({ + scope: $scope, + variable: 'variables', + parse_variable: 'parseType', + field_id: 'inventory_variables' + }); + } + + // Save + $scope.formSave = function() { + Wait('start'); + try { + var fld, json_data, data; + + json_data = ToJSON($scope.parseType, $scope.variables, true); + + data = {}; + for (fld in form.fields) { + if (form.fields[fld].realName) { + data[form.fields[fld].realName] = $scope[fld]; + } else { + data[fld] = $scope[fld]; + } + } + + Rest.setUrl(defaultUrl); + Rest.post(data) + .success(function(data) { + var inventory_id = data.id; + Wait('stop'); + $location.path('/inventories/' + inventory_id); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to add new inventory. Post returned status: ' + status + }); + }); + } catch (err) { + Wait('stop'); + Alert("Error", "Error parsing inventory variables. Parser returned: " + err); + } + + }; + + $scope.formCancel = function() { + $state.go('hosts'); + }; +} + +export default ['$scope', '$location', + 'GenerateForm', 'SmartInventoryForm', 'rbacUiControlService', 'Rest', 'Alert', + 'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', + 'Wait', 'ToJSON', '$state', SmartInventoryAdd +]; diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/edit/main.js b/awx/ui/client/src/inventories/hosts/smart-inventory/edit/main.js new file mode 100644 index 0000000000..b5c59c9e6d --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/edit/main.js @@ -0,0 +1,11 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import controller from './smart-inventory-edit.controller'; + +export default +angular.module('smartInventoryEdit', []) + .controller('SmartInventoryEditController', controller); diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/edit/smart-inventory-edit.controller.js b/awx/ui/client/src/inventories/hosts/smart-inventory/edit/smart-inventory-edit.controller.js new file mode 100644 index 0000000000..5cf8bf1612 --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/edit/smart-inventory-edit.controller.js @@ -0,0 +1,14 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +function SmartInventoryEdit($scope) { + +console.log('inside smart inventory add'); + +} + +export default ['$scope', SmartInventoryEdit +]; diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/main.js b/awx/ui/client/src/inventories/hosts/smart-inventory/main.js new file mode 100644 index 0000000000..7fa368df70 --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/main.js @@ -0,0 +1,16 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + import smartInventoryAdd from './add/main'; + import smartInventoryEdit from './edit/main'; + import SmartInventoryForm from './smart-inventory.form'; + +export default +angular.module('smartInventory', [ + smartInventoryAdd.name, + smartInventoryEdit.name + ]) + .factory('SmartInventoryForm', SmartInventoryForm); diff --git a/awx/ui/client/src/inventories/hosts/smart-inventory/smart-inventory.form.js b/awx/ui/client/src/inventories/hosts/smart-inventory/smart-inventory.form.js new file mode 100644 index 0000000000..d467217078 --- /dev/null +++ b/awx/ui/client/src/inventories/hosts/smart-inventory/smart-inventory.form.js @@ -0,0 +1,194 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default ['i18n', function(i18n) { + return { + + addTitle: i18n._('NEW SMART INVENTORY'), + editTitle: '{{ inventory_name }}', + name: 'inventory', + basePath: 'inventory', + // the top-most node of this generated state tree + stateTree: 'hosts', + tabs: true, + + fields: { + inventory_name: { + realName: 'name', + label: i18n._('Name'), + type: 'text', + required: true, + capitalize: false, + ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + }, + inventory_description: { + realName: 'description', + label: i18n._('Description'), + type: 'text', + ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + }, + organization: { + label: i18n._('Organization'), + type: 'lookup', + basePath: 'organizations', + list: 'OrganizationList', + sourceModel: 'organization', + sourceField: 'name', + awRequiredWhen: { + reqExpression: "organizationrequired", + init: "true" + }, + ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', + awLookupWhen: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' + }, + variables: { + label: i18n._('Variables'), + type: 'textarea', + class: 'Form-formGroup--fullWidth', + rows: 6, + "default": "---", + awPopOver: "
" + i18n._("Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.") + "
" + + "JSON:{\n" + + "YAML:
\"somevar\": \"somevalue\",
\"password\": \"magic\"
}
---\n" + + '
somevar: somevalue
password: magic
' + i18n.sprintf(i18n._('View JSON examples at %s'), 'www.json.org') + '
' + + '' + i18n.sprintf(i18n._('View YAML examples at %s'), 'docs.ansible.com') + '
', + dataTitle: i18n._('Inventory Variables'), + dataPlacement: 'right', + dataContainer: 'body', + ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working + } + }, + + buttons: { + cancel: { + ngClick: 'formCancel()', + ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + }, + close: { + ngClick: 'formCancel()', + ngShow: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + }, + save: { + ngClick: 'formSave()', + ngDisabled: true, + ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + } + }, + related: { + permissions: { + name: 'permissions', + awToolTip: i18n._('Please save before assigning permissions'), + dataPlacement: 'top', + basePath: 'api/v1/inventories/{{$stateParams.inventory_id}}/access_list/', + type: 'collection', + title: i18n._('Permissions'), + iterator: 'permission', + index: false, + open: false, + search: { + order_by: 'username' + }, + actions: { + add: { + label: i18n._('Add'), + ngClick: "$state.go('.add')", + awToolTip: i18n._('Add a permission'), + actionClass: 'btn List-buttonSubmit', + buttonContent: '+ ADD', + ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + + } + }, + fields: { + username: { + label: i18n._('User'), + linkBase: 'users', + class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4' + }, + role: { + label: i18n._('Role'), + type: 'role', + nosort: true, + class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4', + }, + team_roles: { + label: i18n._('Team Roles'), + type: 'team_roles', + nosort: true, + class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4', + } + } + }, + hosts: { + name: 'hosts', + // awToolTip: i18n._('Please save before assigning permissions'), + // dataPlacement: 'top', + basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/hosts/', + type: 'collection', + title: i18n._('Hosts'), + iterator: 'host', + index: false, + open: false, + // search: { + // order_by: 'username' + // }, + actions: { + add: { + label: i18n._('Add'), + ngClick: "$state.go('.add')", + awToolTip: i18n._('Add a permission'), + actionClass: 'btn List-buttonSubmit', + buttonContent: '+ ADD', + // ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + + } + }, + fields: { + name: { + label: i18n._('Name'), + // linkBase: 'users', + class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4' + } + } + }, + //this is a placeholder for when we're ready for completed jobs + completed_jobs: { + name: 'completed_jobs', + // awToolTip: i18n._('Please save before assigning permissions'), + // dataPlacement: 'top', + basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/completed_jobs/', + type: 'collection', + title: i18n._('Completed Jobs'), + iterator: 'completed_job', + index: false, + open: false, + // search: { + // order_by: 'username' + // }, + actions: { + add: { + label: i18n._('Add'), + ngClick: "$state.go('.add')", + awToolTip: i18n._('Add a permission'), + actionClass: 'btn List-buttonSubmit', + buttonContent: '+ ADD', + // ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + + } + }, + fields: { + name: { + label: i18n._('Name'), + // linkBase: 'users', + class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4' + } + } + } + } + + };}]; diff --git a/awx/ui/client/src/inventories/main.js b/awx/ui/client/src/inventories/main.js index 5ff397b7fd..3f71dee845 100644 --- a/awx/ui/client/src/inventories/main.js +++ b/awx/ui/client/src/inventories/main.js @@ -21,53 +21,36 @@ angular.module('inventory', [ ]) .factory('InventoryForm', InventoryForm) .factory('InventoryList', InventoryList) - .config(['$stateProvider', 'stateDefinitionsProvider', - function($stateProvider, stateDefinitionsProvider) { + .config(['$stateProvider', '$stateExtenderProvider', 'stateDefinitionsProvider', + function($stateProvider, $stateExtenderProvider, stateDefinitionsProvider) { // When stateDefinition.lazyLoad() resolves, states matching name.** or /url** will be de-registered and replaced with resolved states // This means inventoryManage states will not be registered correctly on page refresh, unless they're registered at the same time as the inventories state tree - let stateDefinitions = stateDefinitionsProvider.$get(); + let stateDefinitions = stateDefinitionsProvider.$get(), + stateExtender = $stateExtenderProvider.$get(); - $stateProvider.state({ - name: 'inventories', - url: '/inventories', - lazyLoad: () => stateDefinitions.generateTree({ - parent: 'inventories', // top-most node in the generated tree (will replace this state definition) - modes: ['add', 'edit'], - list: 'InventoryList', - form: 'InventoryForm', - controllers: { - list: 'InventoryListController', - add: 'InventoryAddController', - edit: 'InventoryEditController' - }, - urls: { - list: '/inventories' - }, + function foobar() { + + let smartInventoryAdd = { + name: 'hosts.addSmartInventory', + url: '/smartinventory', + form: 'SmartInventoryForm', ncyBreadcrumb: { - label: N_('INVENTORIES') + label: "CREATE SMART INVENTORY" }, views: { - '@': { - templateUrl: templateUrl('inventories/inventories') - }, - 'list@inventories': { - templateProvider: function(InventoryList, generateList) { - let html = generateList.build({ - list: InventoryList, - mode: 'edit' + 'form@hosts': { + templateProvider: function(SmartInventoryForm, GenerateForm) { + return GenerateForm.buildHTML(SmartInventoryForm, { + mode: 'add', + related: false }); - return html; }, - controller: 'InventoryListController' + controller: 'SmartInventoryAddController' } } - }) - }); + }; - $stateProvider.state({ - name: 'hosts', - url: '/hosts', - lazyLoad: () => stateDefinitions.generateTree({ + let hosts = stateDefinitions.generateTree({ parent: 'hosts', // top-most node in the generated tree (will replace this state definition) modes: ['add', 'edit'], list: 'HostsList', @@ -109,7 +92,63 @@ angular.module('inventory', [ controller: 'HostListController' } } + }); + + return Promise.all([ + hosts + ]).then((generated) => { + return { + states: _.reduce(generated, (result, definition) => { + return result.concat(definition.states); + }, [ + stateExtender.buildDefinition(smartInventoryAdd) + ]) + }; + }); + + } + + $stateProvider.state({ + name: 'inventories', + url: '/inventories', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'inventories', // top-most node in the generated tree (will replace this state definition) + modes: ['add', 'edit'], + list: 'InventoryList', + form: 'InventoryForm', + controllers: { + list: 'InventoryListController', + add: 'InventoryAddController', + edit: 'InventoryEditController' + }, + urls: { + list: '/inventories' + }, + ncyBreadcrumb: { + label: N_('INVENTORIES') + }, + views: { + '@': { + templateUrl: templateUrl('inventories/inventories') + }, + 'list@inventories': { + templateProvider: function(InventoryList, generateList) { + let html = generateList.build({ + list: InventoryList, + mode: 'edit' + }); + return html; + }, + controller: 'InventoryListController' + } + } }) }); + + $stateProvider.state({ + name: 'hosts', + url: '/hosts', + lazyLoad: () => foobar() + }); } ]);