mirror of
https://github.com/ansible/awx.git
synced 2026-01-21 06:28:01 -03:30
Disables template and workflow template fields for users that do not have permissions for those fields.
This commit is contained in:
parent
9c90804300
commit
fc4060778b
@ -19,7 +19,13 @@ const QS_CONFIG = getQSConfig('inventory', {
|
||||
|
||||
function InventoryLookup({ value, onChange, onBlur, required, i18n, history }) {
|
||||
const {
|
||||
result: { inventories, count, relatedSearchableKeys, searchableKeys },
|
||||
result: {
|
||||
inventories,
|
||||
count,
|
||||
relatedSearchableKeys,
|
||||
searchableKeys,
|
||||
canEdit,
|
||||
},
|
||||
request: fetchInventories,
|
||||
error,
|
||||
isLoading,
|
||||
@ -39,9 +45,16 @@ function InventoryLookup({ value, onChange, onBlur, required, i18n, history }) {
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
canEdit: Boolean(actionsResponse.data.actions.POST),
|
||||
};
|
||||
}, [history.location]),
|
||||
{ inventories: [], count: 0, relatedSearchableKeys: [], searchableKeys: [] }
|
||||
{
|
||||
inventories: [],
|
||||
count: 0,
|
||||
relatedSearchableKeys: [],
|
||||
searchableKeys: [],
|
||||
canEdit: false,
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -58,6 +71,7 @@ function InventoryLookup({ value, onChange, onBlur, required, i18n, history }) {
|
||||
onBlur={onBlur}
|
||||
required={required}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!canEdit}
|
||||
qsConfig={QS_CONFIG}
|
||||
renderOptionsList={({ state, dispatch, canDelete }) => (
|
||||
<OptionsList
|
||||
|
||||
@ -27,6 +27,8 @@ import { QSConfig } from '../../types';
|
||||
|
||||
const ChipHolder = styled.div`
|
||||
--pf-c-form-control--Height: auto;
|
||||
background-color: ${props =>
|
||||
props.isDisabled ? 'var(--pf-global--disabled-color--300)' : null};
|
||||
`;
|
||||
function Lookup(props) {
|
||||
const {
|
||||
@ -43,6 +45,7 @@ function Lookup(props) {
|
||||
renderOptionsList,
|
||||
history,
|
||||
i18n,
|
||||
isDisabled,
|
||||
} = props;
|
||||
|
||||
const [state, dispatch] = useReducer(
|
||||
@ -103,11 +106,15 @@ function Lookup(props) {
|
||||
id={id}
|
||||
onClick={() => dispatch({ type: 'TOGGLE_MODAL' })}
|
||||
variant={ButtonVariant.control}
|
||||
isDisabled={isLoading}
|
||||
isDisabled={isLoading || isDisabled}
|
||||
>
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
<ChipHolder className="pf-c-form-control">
|
||||
<ChipHolder
|
||||
isDisabled={isDisabled}
|
||||
// css="background-color: #d2d2d2"
|
||||
className="pf-c-form-control"
|
||||
>
|
||||
<ChipGroup numChips={5} totalChips={items.length}>
|
||||
{items.map(item =>
|
||||
renderItemChip({
|
||||
|
||||
@ -32,7 +32,7 @@ function ProjectLookup({
|
||||
history,
|
||||
}) {
|
||||
const {
|
||||
result: { projects, count, relatedSearchableKeys, searchableKeys },
|
||||
result: { projects, count, relatedSearchableKeys, searchableKeys, canEdit },
|
||||
request: fetchProjects,
|
||||
error,
|
||||
isLoading,
|
||||
@ -55,6 +55,7 @@ function ProjectLookup({
|
||||
searchableKeys: Object.keys(
|
||||
actionsResponse.data.actions?.GET || {}
|
||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||
canEdit: Boolean(actionsResponse.data.actions.POST),
|
||||
};
|
||||
}, [history.location.search, autocomplete]),
|
||||
{
|
||||
@ -62,6 +63,7 @@ function ProjectLookup({
|
||||
projects: [],
|
||||
relatedSearchableKeys: [],
|
||||
searchableKeys: [],
|
||||
canEdit: false,
|
||||
}
|
||||
);
|
||||
|
||||
@ -87,6 +89,7 @@ function ProjectLookup({
|
||||
onChange={onChange}
|
||||
required={required}
|
||||
isLoading={isLoading}
|
||||
isDisabled={!canEdit}
|
||||
qsConfig={QS_CONFIG}
|
||||
renderOptionsList={({ state, dispatch, canDelete }) => (
|
||||
<OptionsList
|
||||
|
||||
@ -11,12 +11,22 @@ export default function useSyncedSelectValue(value, onChange) {
|
||||
const [selections, setSelections] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const newOptions = [];
|
||||
if (value !== selections && options.length) {
|
||||
const syncedValue = value.map(item =>
|
||||
options.find(i => i.id === item.id)
|
||||
);
|
||||
const syncedValue = value.map(item => {
|
||||
const match = options.find(i => {
|
||||
return i.id === item.id;
|
||||
});
|
||||
if (!match) {
|
||||
newOptions.push(item);
|
||||
}
|
||||
return match || item;
|
||||
});
|
||||
setSelections(syncedValue);
|
||||
}
|
||||
if (newOptions.length > 0) {
|
||||
setOptions(options.concat(newOptions));
|
||||
}
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [value, options]);
|
||||
|
||||
@ -27,7 +37,6 @@ export default function useSyncedSelectValue(value, onChange) {
|
||||
onChange(selections.concat(item));
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
selections: options.length ? addToStringToObjects(selections) : [],
|
||||
onSelect,
|
||||
|
||||
@ -4,13 +4,11 @@ import { withRouter, Redirect } from 'react-router-dom';
|
||||
import { CardBody } from '../../../components/Card';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
import ContentLoading from '../../../components/ContentLoading';
|
||||
import { JobTemplatesAPI, ProjectsAPI } from '../../../api';
|
||||
import { JobTemplatesAPI } from '../../../api';
|
||||
import { JobTemplate } from '../../../types';
|
||||
import { getAddedAndRemoved } from '../../../util/lists';
|
||||
import JobTemplateForm from '../shared/JobTemplateForm';
|
||||
|
||||
const loadRelatedProjectPlaybooks = async project =>
|
||||
ProjectsAPI.readPlaybooks(project);
|
||||
class JobTemplateEdit extends Component {
|
||||
static propTypes = {
|
||||
template: JobTemplate.isRequired,
|
||||
@ -43,17 +41,8 @@ class JobTemplateEdit extends Component {
|
||||
}
|
||||
|
||||
async loadRelated() {
|
||||
const {
|
||||
template: { project },
|
||||
} = this.props;
|
||||
this.setState({ contentError: null, hasContentLoading: true });
|
||||
try {
|
||||
if (project) {
|
||||
const { data: playbook = [] } = await loadRelatedProjectPlaybooks(
|
||||
project
|
||||
);
|
||||
this.setState({ relatedProjectPlaybooks: playbook });
|
||||
}
|
||||
const [relatedCredentials] = await this.loadRelatedCredentials();
|
||||
this.setState({
|
||||
relatedCredentials,
|
||||
|
||||
@ -39,7 +39,7 @@ import {
|
||||
ProjectLookup,
|
||||
MultiCredentialsLookup,
|
||||
} from '../../../components/Lookup';
|
||||
import { JobTemplatesAPI, ProjectsAPI } from '../../../api';
|
||||
import { JobTemplatesAPI } from '../../../api';
|
||||
import LabelSelect from './LabelSelect';
|
||||
import PlaybookSelect from './PlaybookSelect';
|
||||
import WebhookSubForm from './WebhookSubForm';
|
||||
@ -100,18 +100,6 @@ function JobTemplateForm({
|
||||
'webhook_credential'
|
||||
);
|
||||
|
||||
const {
|
||||
request: fetchProject,
|
||||
error: projectContentError,
|
||||
contentLoading: hasProjectLoading,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
if (template?.project) {
|
||||
await ProjectsAPI.readDetail(template?.project);
|
||||
}
|
||||
}, [template])
|
||||
);
|
||||
|
||||
const {
|
||||
request: loadRelatedInstanceGroups,
|
||||
error: instanceGroupError,
|
||||
@ -127,10 +115,6 @@ function JobTemplateForm({
|
||||
}, [setFieldValue, template])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchProject();
|
||||
}, [fetchProject]);
|
||||
|
||||
useEffect(() => {
|
||||
loadRelatedInstanceGroups();
|
||||
}, [loadRelatedInstanceGroups]);
|
||||
@ -204,16 +188,12 @@ function JobTemplateForm({
|
||||
callbackUrl = `${origin}${path}`;
|
||||
}
|
||||
|
||||
if (instanceGroupLoading || hasProjectLoading) {
|
||||
if (instanceGroupLoading) {
|
||||
return <ContentLoading />;
|
||||
}
|
||||
|
||||
if (contentError || instanceGroupError || projectContentError) {
|
||||
return (
|
||||
<ContentError
|
||||
error={contentError || instanceGroupError || projectContentError}
|
||||
/>
|
||||
);
|
||||
if (contentError || instanceGroupError) {
|
||||
return <ContentError error={contentError || instanceGroupError} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { number, string, oneOfType } from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -7,6 +7,7 @@ import { ProjectsAPI } from '../../../api';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
|
||||
function PlaybookSelect({ projectId, isValid, field, onBlur, onError, i18n }) {
|
||||
const [isDisabled, setIsDisabled] = useState(false);
|
||||
const {
|
||||
result: options,
|
||||
request: fetchOptions,
|
||||
@ -18,6 +19,7 @@ function PlaybookSelect({ projectId, isValid, field, onBlur, onError, i18n }) {
|
||||
return [];
|
||||
}
|
||||
const { data } = await ProjectsAPI.readPlaybooks(projectId);
|
||||
|
||||
const opts = (data || []).map(playbook => ({
|
||||
value: playbook,
|
||||
key: playbook,
|
||||
@ -33,7 +35,7 @@ function PlaybookSelect({ projectId, isValid, field, onBlur, onError, i18n }) {
|
||||
});
|
||||
return opts;
|
||||
}, [projectId, i18n]),
|
||||
[]
|
||||
[field.value]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -42,18 +44,30 @@ function PlaybookSelect({ projectId, isValid, field, onBlur, onError, i18n }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
onError(error);
|
||||
if (error.response.status === 403) {
|
||||
setIsDisabled(true);
|
||||
} else {
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
}, [error, onError]);
|
||||
|
||||
const isDisabledData = [
|
||||
{
|
||||
value: field.value || '',
|
||||
label: field.value || '',
|
||||
key: 1,
|
||||
isDisabled: true,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<AnsibleSelect
|
||||
id="template-playbook"
|
||||
data={options}
|
||||
data={isDisabled ? isDisabledData : options}
|
||||
isValid={isValid}
|
||||
{...field}
|
||||
onBlur={onBlur}
|
||||
isDisabled={isLoading}
|
||||
isDisabled={isLoading || isDisabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user