diff --git a/awx/ui_next/src/api/models/WorkflowJobTemplates.js b/awx/ui_next/src/api/models/WorkflowJobTemplates.js index 0725a1a3e4..e369d71cc9 100644 --- a/awx/ui_next/src/api/models/WorkflowJobTemplates.js +++ b/awx/ui_next/src/api/models/WorkflowJobTemplates.js @@ -1,7 +1,8 @@ import Base from '../Base'; import SchedulesMixin from '../mixins/Schedules.mixin'; +import NotificationsMixin from '../mixins/Notifications.mixin'; -class WorkflowJobTemplates extends SchedulesMixin(Base) { +class WorkflowJobTemplates extends SchedulesMixin(NotificationsMixin(Base)) { constructor(http) { super(http); this.baseUrl = '/api/v2/workflow_job_templates/'; @@ -46,6 +47,10 @@ class WorkflowJobTemplates extends SchedulesMixin(Base) { params, }); } + + readAccessList(id, params) { + return this.http.get(`${this.baseUrl}${id}/access_list/`, { params }); + } } export default WorkflowJobTemplates; diff --git a/awx/ui_next/src/screens/Project/Project.jsx b/awx/ui_next/src/screens/Project/Project.jsx index 0016acdc39..12f18407d8 100644 --- a/awx/ui_next/src/screens/Project/Project.jsx +++ b/awx/ui_next/src/screens/Project/Project.jsx @@ -127,12 +127,15 @@ class Project extends Component { isAuditorOfThisOrg, isAdminOfThisOrg, } = this.state; - const canSeeNotificationsTab = - me.is_system_auditor || isNotifAdmin || isAuditorOfThisOrg; + me.is_superuser || + me.is_system_auditor || + isNotifAdmin || + isAuditorOfThisOrg; const canToggleNotifications = - isNotifAdmin && - (me.is_system_auditor || isAuditorOfThisOrg || isAdminOfThisOrg); + me.is_superuser || + (isNotifAdmin && + (me.is_system_auditor || isAuditorOfThisOrg || isAdminOfThisOrg)); const tabsArray = [ { name: i18n._(t`Details`), link: `${match.url}/details` }, diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx index a1a29752b0..0020df5e05 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx @@ -12,7 +12,13 @@ import JobList from '@components/JobList'; import RoutedTabs from '@components/RoutedTabs'; import { Schedules } from '@components/Schedule'; import ContentLoading from '@components/ContentLoading'; -import { WorkflowJobTemplatesAPI, CredentialsAPI } from '@api'; +import { ResourceAccessList } from '@components/ResourceAccessList'; +import NotificationList from '@components/NotificationList'; +import { + WorkflowJobTemplatesAPI, + CredentialsAPI, + OrganizationsAPI, +} from '@api'; import WorkflowJobTemplateDetail from './WorkflowJobTemplateDetail'; import WorkflowJobTemplateEdit from './WorkflowJobTemplateEdit'; import { Visualizer } from './WorkflowJobTemplateVisualizer'; @@ -26,6 +32,9 @@ class WorkflowJobTemplate extends Component { hasContentLoading: true, template: null, webhook_key: null, + isNotifAdmin: false, + isAuditorOfThisOrg: false, + isAdminOfThisOrg: false, }; this.loadTemplate = this.loadTemplate.bind(this); this.loadSchedules = this.loadSchedules.bind(this); @@ -68,8 +77,28 @@ class WorkflowJobTemplate extends Component { ); data.summary_fields.webhook_credential.kind = name; } + const [notifAdminRes, auditorRes, adminRes] = await Promise.all([ + OrganizationsAPI.read({ + page_size: 1, + role_level: 'notification_admin_role', + }), + OrganizationsAPI.read({ + id: data.organization, + role_level: 'auditor_role', + }), + OrganizationsAPI.read({ + id: data.organization, + role_level: 'admin_role', + }), + ]); this.setState({ template: data }); setBreadcrumb(data); + this.setState({ + template: data, + isNotifAdmin: notifAdminRes.data.results.length > 0, + isAuditorOfThisOrg: auditorRes.data.results.length > 0, + isAdminOfThisOrg: adminRes.data.results.length > 0, + }); } catch (err) { this.setState({ contentError: err }); } finally { @@ -88,20 +117,35 @@ class WorkflowJobTemplate extends Component { } render() { - const { i18n, location, match, setBreadcrumb } = this.props; + const { i18n, me, location, match, setBreadcrumb } = this.props; const { contentError, hasContentLoading, template, webhook_key, + isNotifAdmin, + isAuditorOfThisOrg, + isAdminOfThisOrg, } = this.state; + const canSeeNotificationsTab = + me.is_system_auditor || isNotifAdmin || isAuditorOfThisOrg; + const canToggleNotifications = + isNotifAdmin && + (me.is_system_auditor || isAuditorOfThisOrg || isAdminOfThisOrg); + const tabsArray = [ { name: i18n._(t`Details`), link: `${match.url}/details` }, - { name: i18n._(t`Visualizer`), link: `${match.url}/visualizer` }, - { name: i18n._(t`Completed Jobs`), link: `${match.url}/completed_jobs` }, + { name: i18n._(t`Access`), link: `${match.url}/access` }, ]; + if (canSeeNotificationsTab) { + tabsArray.push({ + name: i18n._(t`Notifications`), + link: `${match.url}/notifications`, + }); + } + if (template) { tabsArray.push({ name: i18n._(t`Schedules`), @@ -109,6 +153,15 @@ class WorkflowJobTemplate extends Component { }); } + tabsArray.push({ + name: i18n._(t`Visualizer`), + link: `${match.url}/visualizer`, + }); + tabsArray.push({ + name: i18n._(t`Completed Jobs`), + link: `${match.url}/completed_jobs`, + }); + tabsArray.forEach((tab, n) => { tab.id = n; }); @@ -174,6 +227,29 @@ class WorkflowJobTemplate extends Component { )} /> )} + {template && ( + ( + + )} + /> + )} + {canSeeNotificationsTab && ( + ( + + )} + /> + )} {template && (