diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 80377297a0..c3f37e1528 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -33,7 +33,6 @@ import {PortalController} from 'tower/controllers/Portal'; import dataServices from 'tower/services/_data-services'; import dashboardGraphs from 'tower/directives/_dashboard-graphs'; - import {JobDetailController} from 'tower/controllers/JobDetail'; import {JobStdoutController} from 'tower/controllers/JobStdout'; import {JobTemplatesList, JobTemplatesAdd, JobTemplatesEdit} from 'tower/controllers/JobTemplates'; @@ -58,6 +57,7 @@ import 'tower/shared/Timer'; import 'tower/shared/Socket'; import 'tower/job-templates/main'; +import 'tower/shared/features/main'; /*#if DEBUG#*/ import {__deferLoadIfEnabled} from 'tower/debug'; @@ -171,7 +171,8 @@ var tower = angular.module('Tower', [ 'ConfigureTowerJobsListDefinition', 'CreateCustomInventoryHelper', 'CustomInventoryListDefinition', - 'AdhocHelper' + 'AdhocHelper', + 'features' ]) .constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/') @@ -184,277 +185,552 @@ var tower = angular.module('Tower', [ when('/jobs', { templateUrl: urlPrefix + 'partials/jobs.html', - controller: JobsListController + controller: JobsListController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/portal', { templateUrl: urlPrefix + 'partials/portal.html', - controller: PortalController + controller: PortalController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/jobs/:id', { templateUrl: urlPrefix + 'partials/job_detail.html', - controller: JobDetailController + controller: JobDetailController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/jobs/:id/stdout', { templateUrl: urlPrefix + 'partials/job_stdout.html', - controller: JobStdoutController + controller: JobStdoutController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/ad_hoc_commands/:id', { templateUrl: urlPrefix + 'partials/job_stdout_adhoc.html', - controller: JobStdoutController + controller: JobStdoutController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/job_templates', { templateUrl: urlPrefix + 'partials/job_templates.html', - controller: JobTemplatesList + controller: JobTemplatesList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/job_templates/add', { templateUrl: urlPrefix + 'partials/job_templates.html', - controller: JobTemplatesAdd + controller: JobTemplatesAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/job_templates/:template_id', { templateUrl: urlPrefix + 'partials/job_templates.html', - controller: JobTemplatesEdit + controller: JobTemplatesEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/job_templates/:id/schedules', { templateUrl: urlPrefix + 'partials/schedule_detail.html', - controller: ScheduleEditController + controller: ScheduleEditController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects', { templateUrl: urlPrefix + 'partials/projects.html', - controller: ProjectsList + controller: ProjectsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects/add', { templateUrl: urlPrefix + 'partials/projects.html', - controller: ProjectsAdd + controller: ProjectsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects/:id', { templateUrl: urlPrefix + 'partials/projects.html', - controller: ProjectsEdit + controller: ProjectsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects/:id/schedules', { templateUrl: urlPrefix + 'partials/schedule_detail.html', - controller: ScheduleEditController + controller: ScheduleEditController, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects/:project_id/organizations', { templateUrl: urlPrefix + 'partials/projects.html', - controller: OrganizationsList + controller: OrganizationsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/projects/:project_id/organizations/add', { templateUrl: urlPrefix + 'partials/projects.html', - controller: OrganizationsAdd + controller: OrganizationsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories', { templateUrl: urlPrefix + 'partials/inventories.html', - controller: InventoriesList + controller: InventoriesList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/add', { templateUrl: urlPrefix + 'partials/inventories.html', - controller: InventoriesAdd + controller: InventoriesAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/:inventory_id', { templateUrl: urlPrefix + 'partials/inventories.html', - controller: InventoriesEdit + controller: InventoriesEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/:inventory_id/job_templates/add', { templateUrl: urlPrefix + 'partials/job_templates.html', - controller: JobTemplatesAdd + controller: JobTemplatesAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/:inventory_id/job_templates/:template_id', { templateUrl: urlPrefix + 'partials/job_templates.html', - controller: JobTemplatesEdit + controller: JobTemplatesEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/:inventory_id/manage', { templateUrl: urlPrefix + 'partials/inventory-manage.html', - controller: InventoriesManage + controller: InventoriesManage, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/inventories/:inventory_id/adhoc', { templateUrl: urlPrefix + 'partials/adhoc.html', - controller: AdhocCtrl + controller: AdhocCtrl, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsList + controller: OrganizationsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/add', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsAdd + controller: OrganizationsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/:organization_id', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsEdit + controller: OrganizationsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/:organization_id/admins', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: AdminsList + controller: AdminsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/:organization_id/users', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersList + controller: UsersList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/:organization_id/users/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersAdd + controller: UsersAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/organizations/:organization_id/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersEdit + controller: UsersEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsList + controller: TeamsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsAdd + controller: TeamsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsEdit + controller: TeamsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/permissions/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsAdd + controller: PermissionsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/permissions', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsList + controller: PermissionsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsEdit + controller: PermissionsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/users', { templateUrl: urlPrefix + 'partials/teams.html', - controller: UsersList + controller: UsersList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/users/:user_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: UsersEdit + controller: UsersEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/projects', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsList + controller: ProjectsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/projects/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsAdd + controller: ProjectsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/projects/:project_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsEdit + controller: ProjectsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/credentials', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsList + controller: CredentialsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsAdd + controller: CredentialsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:team_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsEdit + controller: CredentialsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/credentials', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsList + controller: CredentialsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/credentials/add', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsAdd + controller: CredentialsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsEdit + controller: CredentialsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersList + controller: UsersList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersAdd + controller: UsersAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersEdit + controller: UsersEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id/credentials', { templateUrl: urlPrefix + 'partials/users.html', - controller: CredentialsList + controller: CredentialsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id/permissions/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsAdd + controller: PermissionsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id/permissions', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsList + controller: PermissionsList, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsEdit + controller: PermissionsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/users/:user_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsAdd + controller: CredentialsAdd, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/teams/:user_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsEdit + controller: CredentialsEdit, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/login', { @@ -464,17 +740,23 @@ var tower = angular.module('Tower', [ when('/logout', { templateUrl: urlPrefix + 'partials/blank.html', - controller: Authenticate + controller: Authenticate, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/home', { templateUrl: urlPrefix + 'partials/home.html', controller: Home, resolve: { - graphData: ['$q', 'jobStatusGraphData', 'hostCountGraphData', function($q, jobStatusGraphData, hostCountGraphData) { + graphData: ['$q', 'jobStatusGraphData', 'hostCountGraphData', 'FeaturesService', function($q, jobStatusGraphData, hostCountGraphData, FeaturesService) { return $q.all({ jobStatus: jobStatusGraphData.get("month", "all"), - hostCounts: hostCountGraphData.get() + hostCounts: hostCountGraphData.get(), + features: FeaturesService.get() }); }] } @@ -482,12 +764,22 @@ var tower = angular.module('Tower', [ when('/home/groups', { templateUrl: urlPrefix + 'partials/subhome.html', - controller: HomeGroups + controller: HomeGroups, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/home/hosts', { templateUrl: urlPrefix + 'partials/subhome.html', - controller: HomeHosts + controller: HomeHosts, + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } }). when('/sockets', { @@ -684,7 +976,6 @@ var tower = angular.module('Tower', [ if($location.$$url !== '/login'){ $rootScope.$emit('OpenSocket'); } - } activateTab(); diff --git a/awx/ui/static/js/forms/Credentials.js b/awx/ui/static/js/forms/Credentials.js index 607c785bc3..fe2e7ada65 100644 --- a/awx/ui/static/js/forms/Credentials.js +++ b/awx/ui/static/js/forms/Credentials.js @@ -25,7 +25,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index 3b628da008..bc0e957df9 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -30,6 +30,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', diff --git a/awx/ui/static/js/forms/JobTemplates.js b/awx/ui/static/js/forms/JobTemplates.js index 49f3b70f9f..62b72f7617 100644 --- a/awx/ui/static/js/forms/JobTemplates.js +++ b/awx/ui/static/js/forms/JobTemplates.js @@ -40,6 +40,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', @@ -258,27 +259,12 @@ export default dataTitle: 'Prompt for Extra Variables', dataContainer: "body" }, - // survey_enabled: { - // type: 'custom', - // column: 2, - // control: '
'+ - // '
'+ - // ''+ - // '
'+ - // ''+ - // '
'+ - // '
' - // }, survey_enabled: { label: 'Enable Survey', type: 'checkbox', addRequired: false, editRequird: false, - // trueValue: true, - // falseValue: false, + awFeature: 'surveys', ngChange: "surveyEnabled()", column: 2, awPopOver: "

If checked, user will be prompted at job launch with a series of questions related to the job.

", diff --git a/awx/ui/static/js/forms/Organizations.js b/awx/ui/static/js/forms/Organizations.js index b3b6eaa102..a25ee330fa 100644 --- a/awx/ui/static/js/forms/Organizations.js +++ b/awx/ui/static/js/forms/Organizations.js @@ -30,6 +30,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', diff --git a/awx/ui/static/js/forms/Permissions.js b/awx/ui/static/js/forms/Permissions.js index 274ab2285a..a09c1ec1f8 100644 --- a/awx/ui/static/js/forms/Permissions.js +++ b/awx/ui/static/js/forms/Permissions.js @@ -27,6 +27,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', diff --git a/awx/ui/static/js/forms/Projects.js b/awx/ui/static/js/forms/Projects.js index 14d6595b13..e56095bcf3 100644 --- a/awx/ui/static/js/forms/Projects.js +++ b/awx/ui/static/js/forms/Projects.js @@ -41,6 +41,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition']) 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', diff --git a/awx/ui/static/js/forms/Teams.js b/awx/ui/static/js/forms/Teams.js index fa12e26385..8783401631 100644 --- a/awx/ui/static/js/forms/Teams.js +++ b/awx/ui/static/js/forms/Teams.js @@ -30,6 +30,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', diff --git a/awx/ui/static/js/forms/Users.js b/awx/ui/static/js/forms/Users.js index 3d882ee784..86793fd90e 100644 --- a/awx/ui/static/js/forms/Users.js +++ b/awx/ui/static/js/forms/Users.js @@ -31,6 +31,7 @@ export default 'class': "btn-primary btn-xs activity-btn", ngClick: "showActivity()", awToolTip: "View Activity Stream", + awFeature: 'activity_streams', dataPlacement: "top", icon: "icon-comments-alt", mode: 'edit', @@ -114,7 +115,8 @@ export default ldap_user: { label: 'Created by LDAP', type: 'checkbox', - readonly: true + readonly: true, + awFeature: 'ldap' } }, diff --git a/awx/ui/static/js/lists/Credentials.js b/awx/ui/static/js/lists/Credentials.js index bc2ac87090..47d79677df 100644 --- a/awx/ui/static/js/lists/Credentials.js +++ b/awx/ui/static/js/lists/Credentials.js @@ -53,7 +53,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/HomeGroups.js b/awx/ui/static/js/lists/HomeGroups.js index 0d1d294cc2..d732f2eb18 100644 --- a/awx/ui/static/js/lists/HomeGroups.js +++ b/awx/ui/static/js/lists/HomeGroups.js @@ -169,7 +169,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'all' + mode: 'all', + awFeature: 'activity_streams' } } diff --git a/awx/ui/static/js/lists/HomeHosts.js b/awx/ui/static/js/lists/HomeHosts.js index 992a46a7b8..3e7a15e3fa 100644 --- a/awx/ui/static/js/lists/HomeHosts.js +++ b/awx/ui/static/js/lists/HomeHosts.js @@ -102,7 +102,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'all' + mode: 'all', + awFeature: 'activity_streams' } } diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index 6fc95a4b50..5091c344a3 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -92,7 +92,8 @@ export default ngClick: "showActivity()", awToolTip: "View Activity Stream", icon: "icon-comments-alt", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js index 8d283cfa61..d405f6caa2 100644 --- a/awx/ui/static/js/lists/InventoryGroups.js +++ b/awx/ui/static/js/lists/InventoryGroups.js @@ -112,7 +112,8 @@ export default stream: { ngClick: "showGroupActivity()", awToolTip: "View Activity Stream", - mode: 'all' + mode: 'all', + awFeature: 'activity_streams' }, help: { mode: 'all', diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js index 6bd7d69b09..bbc3ba4c31 100644 --- a/awx/ui/static/js/lists/InventoryHosts.js +++ b/awx/ui/static/js/lists/InventoryHosts.js @@ -101,7 +101,8 @@ export default stream: { ngClick: "showHostActivity()", awToolTip: "View Activity Stream", - mode: 'all' + mode: 'all', + awFeature: 'activity_streams' } } diff --git a/awx/ui/static/js/lists/JobTemplates.js b/awx/ui/static/js/lists/JobTemplates.js index 536768a692..6c2822c04e 100644 --- a/awx/ui/static/js/lists/JobTemplates.js +++ b/awx/ui/static/js/lists/JobTemplates.js @@ -54,6 +54,7 @@ export default ngClick: "showActivity()", awToolTip: "View Activity Stream", icon: "icon-comments-alt", + awFeature: 'activity_streams', mode: 'edit', ngHide: 'portalMode===true' } diff --git a/awx/ui/static/js/lists/Organizations.js b/awx/ui/static/js/lists/Organizations.js index e4f577cf55..7acea076dc 100644 --- a/awx/ui/static/js/lists/Organizations.js +++ b/awx/ui/static/js/lists/Organizations.js @@ -40,12 +40,14 @@ export default add: { mode: 'all', // One of: edit, select, all ngClick: 'addOrganization()', - awToolTip: 'Create a new organization' + awToolTip: 'Create a new organization', + awFeature: 'multiple_organizations' }, stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/Permissions.js b/awx/ui/static/js/lists/Permissions.js index 0188366ee2..da2d362ffd 100644 --- a/awx/ui/static/js/lists/Permissions.js +++ b/awx/ui/static/js/lists/Permissions.js @@ -56,7 +56,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/Projects.js b/awx/ui/static/js/lists/Projects.js index 1b8cd0c352..84adf22b17 100644 --- a/awx/ui/static/js/lists/Projects.js +++ b/awx/ui/static/js/lists/Projects.js @@ -75,7 +75,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/ScanJobs.js b/awx/ui/static/js/lists/ScanJobs.js index 33b1d6b0f8..319a0873b6 100644 --- a/awx/ui/static/js/lists/ScanJobs.js +++ b/awx/ui/static/js/lists/ScanJobs.js @@ -48,7 +48,8 @@ export default awToolTip: "View Activity Stream", icon: "icon-comments-alt", mode: 'edit', - ngHide: 'portalMode===true' + ngHide: 'portalMode===true', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/Schedules.js b/awx/ui/static/js/lists/Schedules.js index 0b4e156bfd..52e94d6187 100644 --- a/awx/ui/static/js/lists/Schedules.js +++ b/awx/ui/static/js/lists/Schedules.js @@ -67,7 +67,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/Teams.js b/awx/ui/static/js/lists/Teams.js index 8540d175f5..c159786ab4 100644 --- a/awx/ui/static/js/lists/Teams.js +++ b/awx/ui/static/js/lists/Teams.js @@ -52,7 +52,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/lists/Users.js b/awx/ui/static/js/lists/Users.js index de539a622f..0e5d4b078f 100644 --- a/awx/ui/static/js/lists/Users.js +++ b/awx/ui/static/js/lists/Users.js @@ -50,7 +50,8 @@ export default stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", - mode: 'edit' + mode: 'edit', + awFeature: 'activity_streams' } }, diff --git a/awx/ui/static/js/shared/AuthService.js b/awx/ui/static/js/shared/AuthService.js index aab7f41aea..d1672404e4 100644 --- a/awx/ui/static/js/shared/AuthService.js +++ b/awx/ui/static/js/shared/AuthService.js @@ -95,6 +95,7 @@ angular.module('AuthService', ['ngCookies', Utilities.name]) }, getLicense: function () { + //check in here first to see if license is already obtained, if we do have it, then rootScope.license return $http({ method: 'GET', url: GetBasePath('config'), @@ -109,6 +110,7 @@ angular.module('AuthService', ['ngCookies', Utilities.name]) license.version = data.version; license.tested = false; Store('license', license); + $rootScope.features = Store('license').features; }, licenseTested: function () { diff --git a/awx/ui/static/js/shared/features/features.controller.js b/awx/ui/static/js/shared/features/features.controller.js new file mode 100644 index 0000000000..ad15be4771 --- /dev/null +++ b/awx/ui/static/js/shared/features/features.controller.js @@ -0,0 +1,10 @@ +export default ['$rootScope', function ($rootScope) { + + this.isFeatureEnabled = function(feature){ + if($rootScope.features[feature] === false){ + return false; + } else { + return true; + } + }; +}]; diff --git a/awx/ui/static/js/shared/features/features.directive.js b/awx/ui/static/js/shared/features/features.directive.js new file mode 100644 index 0000000000..a5fa6f5873 --- /dev/null +++ b/awx/ui/static/js/shared/features/features.directive.js @@ -0,0 +1,15 @@ +import featureController from 'tower/shared/features/features.controller'; +export default [ function() { + return { + restrict: 'A', + controller: featureController, + link: function (scope, element, attrs, controller){ + if(attrs.awFeature.length > 0){ + if(!controller.isFeatureEnabled(attrs.awFeature)){ + element.remove(); + } + } + } + + }; +}]; diff --git a/awx/ui/static/js/shared/features/features.service.js b/awx/ui/static/js/shared/features/features.service.js new file mode 100644 index 0000000000..2251e561b6 --- /dev/null +++ b/awx/ui/static/js/shared/features/features.service.js @@ -0,0 +1,30 @@ +export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$http', '$q', +function ($rootScope, Rest, GetBasePath, ProcessErrors, $http, $q) { + return { + getFeatures: function(){ + var promise; + Rest.setUrl(GetBasePath('config')); + promise = Rest.get(); + return promise.then(function (data) { + $rootScope.features = data.data.license_info.features; + return $rootScope.features; + }).catch(function (response) { + ProcessErrors($rootScope, response.data, response.status, null, { + hdr: 'Error!', + msg: 'Failed to get license info. GET returned status: ' + + response.status + }); + }); + + }, + get: function(){ + if(_.isEmpty($rootScope.features)){ + return this.getFeatures(); + } else { + // $q.when will ensure that the result is returned + // as a resovled promise. + return $q.when($rootScope.features); + } + } + }; +}]; diff --git a/awx/ui/static/js/shared/features/main.js b/awx/ui/static/js/shared/features/main.js new file mode 100644 index 0000000000..ad7cb50cdf --- /dev/null +++ b/awx/ui/static/js/shared/features/main.js @@ -0,0 +1,6 @@ +import awFeatureDirective from 'tower/shared/features/features.directive'; +import FeaturesService from 'tower/shared/features/features.service'; +export default + angular.module('features', []) + .directive('awFeature', awFeatureDirective) + .service('FeaturesService', FeaturesService); diff --git a/awx/ui/static/js/shared/form-generator.js b/awx/ui/static/js/shared/form-generator.js index 50d24d561e..e8f89f3e56 100644 --- a/awx/ui/static/js/shared/form-generator.js +++ b/awx/ui/static/js/shared/form-generator.js @@ -752,6 +752,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "'"; html += (field.ngShow) ? this.attr(field, 'ngShow') : ""; html += (field.ngHide) ? this.attr(field, 'ngHide') : ""; + html += (field.awFeature) ? "aw-feature=\"" + field.awFeature + "\" " : ""; html += ">\n"; //text fields diff --git a/awx/ui/static/js/shared/list-generator/list-actions.partial.html b/awx/ui/static/js/shared/list-generator/list-actions.partial.html index 01749fdd01..e59af015cc 100644 --- a/awx/ui/static/js/shared/list-generator/list-actions.partial.html +++ b/awx/ui/static/js/shared/list-generator/list-actions.partial.html @@ -16,8 +16,10 @@ ng-hide="isHiddenByOptions(options) || hiddenOnCurrentPage(options.basePaths) || hiddenInCurrentMode(options.mode)" - toolbar="true"> + toolbar="true" + aw-feature="{{options.awFeature}}"> + diff --git a/awx/ui/tests/unit/features/features.controller-test.js b/awx/ui/tests/unit/features/features.controller-test.js new file mode 100644 index 0000000000..ac131334a1 --- /dev/null +++ b/awx/ui/tests/unit/features/features.controller-test.js @@ -0,0 +1,25 @@ +import featuresController from 'tower/shared/features/features.controller'; + +describe('featuresController', function() { + + it('checks if a feature is enabled', inject(['$rootScope', function($rootScope) { + var actual; + + $rootScope.features = { + activity_streams: true, + ldap: false + }; + + // TODO: extract into test controller in describeModule + var Controller = featuresController[1]; + var controller = new Controller($rootScope); + + actual = controller.isFeatureEnabled('activity_streams'); + expect(actual).to.be.true; + + actual = controller.isFeatureEnabled('ldap'); + expect(actual).to.be.false; + + + }])); +}) diff --git a/awx/ui/tests/unit/features/features.service-test.js b/awx/ui/tests/unit/features/features.service-test.js new file mode 100644 index 0000000000..26804f3254 --- /dev/null +++ b/awx/ui/tests/unit/features/features.service-test.js @@ -0,0 +1,55 @@ +import features from 'tower/shared/features/main'; +import {describeModule} from '../describe-module'; + +//test that it returns features, as well as test that it is returned in rootScope + +describeModule(features.name) + .testService('FeaturesService', function(test, restStub) { + + var service; + + test.withService(function(_service) { + service = _service; + }); + + it('returns list of features', function() { + var features = {}, + result = { + data: { + license_info: { + features: features + } + } + }; + + var actual = service.get(); + + restStub.succeed(result); + restStub.flush(); + + return expect(actual).to.eventually.equal(features); + + }); + + it('caches in rootScope', inject(['$rootScope', + function($rootScope){ + var features = {}, + result = { + data: { + license_info: { + features: features + } + } + }; + + var actual = service.get(); + + restStub.succeed(result); + restStub.flush(); + + return actual.then(function(){ + expect($rootScope.features).to.equal(features); + }); + }])); + + }); diff --git a/nodemon.json b/nodemon.json index 6ceb91b153..0ab79edb96 100644 --- a/nodemon.json +++ b/nodemon.json @@ -6,7 +6,8 @@ "awx/ui/static/docs/**/*" ], "watch": [ - "awx/ui/static" + "awx/ui/static", + "awx/ui/tests" ], "ext": "js json less html" }