diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index ee6ab56ed..1c103c695 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -63,6 +63,8 @@ action.advanced=Advanced action.add-new-attribute=Add new attribute action.add-attribute=Add Attribute action.custom-entity-attributes=Custom Entity Attributes +action.enable=Enable +action.disable=Disable value.enabled=Enabled value.disabled=Disabled diff --git a/ui/src/app/admin/container/MetadataActions.js b/ui/src/app/admin/container/MetadataActions.js new file mode 100644 index 000000000..80f43d56a --- /dev/null +++ b/ui/src/app/admin/container/MetadataActions.js @@ -0,0 +1,45 @@ +import React from 'react'; +import { DeleteConfirmation } from '../../metadata/component/DeleteConfirmation'; +import { useMetadataEntity } from '../../metadata/hooks/api'; + +import { NotificationContext, createNotificationAction } from '../../notifications/hoc/Notifications'; + +export function MetadataActions ({type, children}) { + + const { dispatch } = React.useContext(NotificationContext); + + const { del, put, response } = useMetadataEntity(type, { + cachePolicy: 'no-cache' + }); + + async function enableEntity(entity, enabled, cb = () => {}) { + await put(`/${type === 'source' ? entity.id : entity.resourceId}`, { + ...entity, + [type === 'source' ? 'serviceEnabled' : 'enabled']: enabled + }); + if (response.ok) { + dispatch(createNotificationAction( + `Metadata ${type} has been ${enabled ? 'enabled' : 'disabled'}.` + )); + cb(); + } + } + + async function deleteEntity(id, cb = () => {}) { + await del(`/${id}`); + if (response.ok) { + dispatch(createNotificationAction( + `Metadata ${type} has been deleted.` + )); + cb(); + } + } + + return ( + + {(block) => + <>{children(enableEntity, (id, cb) => block(() => deleteEntity(id, cb)))} + } + + ); +} \ No newline at end of file diff --git a/ui/src/app/admin/container/SourcesActions.js b/ui/src/app/admin/container/SourcesActions.js deleted file mode 100644 index 1d7316735..000000000 --- a/ui/src/app/admin/container/SourcesActions.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import SourceList from '../../metadata/domain/source/component/SourceList'; -import { useMetadataEntity } from '../../metadata/hooks/api'; - -import { NotificationContext, createNotificationAction } from '../../notifications/hoc/Notifications'; - -export function SourcesActions ({sources, reloadSources}) { - - const { dispatch } = React.useContext(NotificationContext); - - const { put, response } = useMetadataEntity('source', { - cachePolicy: 'no-cache' - }); - - async function enableSource(source) { - await put(`/${source.id}`, { - ...source, - serviceEnabled: true - }); - if (response.ok) { - dispatch(createNotificationAction( - `Metadata Source has been enabled.` - )); - reloadSources(); - } - } - - return ( - - ); -} \ No newline at end of file diff --git a/ui/src/app/dashboard/view/ActionsTab.js b/ui/src/app/dashboard/view/ActionsTab.js index 76d1ff592..30bc9e371 100644 --- a/ui/src/app/dashboard/view/ActionsTab.js +++ b/ui/src/app/dashboard/view/ActionsTab.js @@ -1,8 +1,9 @@ import React from 'react'; -import { SourcesActions } from '../../admin/container/SourcesActions'; +import { MetadataActions } from '../../admin/container/MetadataActions'; import UserActions from '../../admin/container/UserActions'; import Translate from '../../i18n/components/translate'; +import SourceList from '../../metadata/domain/source/component/SourceList'; export function ActionsTab({ sources, users, reloadSources, reloadUsers }) { @@ -18,7 +19,11 @@ export function ActionsTab({ sources, users, reloadSources, reloadUsers }) {
- + + {(enable) => + enable(s, e, reloadSources)} /> + } +
diff --git a/ui/src/app/dashboard/view/ProvidersTab.js b/ui/src/app/dashboard/view/ProvidersTab.js index 123e54f41..023a7f78a 100644 --- a/ui/src/app/dashboard/view/ProvidersTab.js +++ b/ui/src/app/dashboard/view/ProvidersTab.js @@ -7,7 +7,7 @@ import {Search} from '../component/Search'; import { Ordered } from '../component/Ordered'; import { useIsAdmin } from '../../core/user/UserContext'; import Alert from 'react-bootstrap/Alert'; - +import { MetadataActions } from '../../admin/container/MetadataActions'; const searchProps = ['name', '@type', 'createdBy']; export function ProvidersTab () { @@ -44,13 +44,20 @@ export function ProvidersTab () { {(ordered, first, last, onOrderUp, onOrderDown) => - {(searched) => } + {(searched) => + + {(enable) => + enable(p, e, loadProviders)} + onOrderUp={onOrderUp} + onOrderDown={onOrderDown}> + } + + } } diff --git a/ui/src/app/dashboard/view/SourcesTab.js b/ui/src/app/dashboard/view/SourcesTab.js index 852ec221a..b40a94aa9 100644 --- a/ui/src/app/dashboard/view/SourcesTab.js +++ b/ui/src/app/dashboard/view/SourcesTab.js @@ -1,4 +1,5 @@ import React from 'react'; +import { MetadataActions } from '../../admin/container/MetadataActions'; import Translate from '../../i18n/components/translate'; import SourceList from '../../metadata/domain/source/component/SourceList'; @@ -22,8 +23,6 @@ export function SourcesTab () { } } - const updateSources = () => loadSources(); - /*eslint-disable react-hooks/exhaustive-deps*/ React.useEffect(() => { loadSources() }, []); @@ -37,7 +36,16 @@ export function SourcesTab () {
- {(searched) => } + {(searched) => + + {(enable, remove) => + remove(id, loadSources)} + onEnable={(s, e) => enable(s, e, loadSources) } /> + } + + }
diff --git a/ui/src/app/metadata/Metadata.js b/ui/src/app/metadata/Metadata.js index 0d1e38b07..9fb70ad44 100644 --- a/ui/src/app/metadata/Metadata.js +++ b/ui/src/app/metadata/Metadata.js @@ -20,13 +20,13 @@ export function Metadata () { return ( <> - {(entity) => + {(entity, reload) => - + } /> diff --git a/ui/src/app/metadata/component/MetadataHeader.js b/ui/src/app/metadata/component/MetadataHeader.js index d8a2baa85..e9cd62aea 100644 --- a/ui/src/app/metadata/component/MetadataHeader.js +++ b/ui/src/app/metadata/component/MetadataHeader.js @@ -8,7 +8,7 @@ export function MetadataHeader ({ model, current = true, enabled = true, childre
-
+
Saved diff --git a/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js b/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js index aab3adb4d..3260f79a1 100644 --- a/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js @@ -100,7 +100,6 @@ export const EntityAttributesFilterEditor= { 'name', '@type', 'resourceId', - 'filterEnabled', 'entityAttributesFilterTarget' ] }, diff --git a/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js b/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js index e4abd17eb..aa575cefd 100644 --- a/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js @@ -66,7 +66,6 @@ export const NameIDFilterEditor = { index: 1, fields: [ 'name', - 'filterEnabled', '@type', 'resourceId', 'nameIdFormatFilterTarget' diff --git a/ui/src/app/metadata/domain/provider/component/ProviderList.js b/ui/src/app/metadata/domain/provider/component/ProviderList.js index 86e0d83ea..4cd26c2b9 100644 --- a/ui/src/app/metadata/domain/provider/component/ProviderList.js +++ b/ui/src/app/metadata/domain/provider/component/ProviderList.js @@ -2,14 +2,21 @@ import React from 'react'; import { Link } from 'react-router-dom'; import Badge from 'react-bootstrap/Badge'; import Button from 'react-bootstrap/Button'; +import Form from 'react-bootstrap/Form'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronCircleDown, faChevronCircleUp } from '@fortawesome/free-solid-svg-icons'; import FormattedDate from '../../../../core/components/FormattedDate'; import Translate from '../../../../i18n/components/translate'; import { Scroller } from '../../../../dashboard/component/Scroller'; +import { useIsAdmin } from '../../../../core/user/UserContext'; +import { useTranslator } from '../../../../i18n/hooks'; + +export function ProviderList({ entities, reorder = true, first, last, onEnable, onOrderUp, onOrderDown }) { + + const isAdmin = useIsAdmin(); + const translator = useTranslator(); -export function ProviderList({ entities, reorder = true, first, last, onOrderUp, onOrderDown }) { return ( {(limited) =>
@@ -61,10 +68,24 @@ export function ProviderList({ entities, reorder = true, first, last, onOrderUp, { provider['@type'] } { provider.createdBy } - - - - + + + {onEnable && isAdmin ? + onEnable(provider, checked)} + checked={provider.enabled} + > + + : + + + + } + )} diff --git a/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js index 87ed3bb31..58bb0002b 100644 --- a/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js @@ -61,9 +61,7 @@ export const DynamicHttpMetadataProviderWizard = { label: 'label.finished', index: 5, initialValues: [], - fields: [ - 'enabled' - ] + fields: [] } ], uiSchema: defaultsDeep({ @@ -191,7 +189,6 @@ export const DynamicHttpMetadataProviderEditor = { '@type', 'xmlId', 'metadataRequestURLConstructionScheme', - 'enabled', 'requireValidMetadata', 'failFastInitialization' ] diff --git a/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js index 5d2d8e009..4d7146c98 100644 --- a/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js @@ -52,9 +52,7 @@ export const FileBackedHttpMetadataProviderWizard = { label: 'label.finished', index: 5, initialValues: [], - fields: [ - 'enabled' - ] + fields: [] } ], uiSchema: defaultsDeep({ @@ -68,12 +66,6 @@ export const FileBackedHttpMetadataProviderWizard = { '@type' ] }, - { - size: 8, - fields: [ - 'enabled' - ] - }, { size: 8, fields: [ @@ -181,7 +173,6 @@ export const FileBackedHttpMetadataProviderEditor = { fields: [ 'name', '@type', - 'enabled', 'xmlId', 'metadataURL', 'initializeFromBackupFile', diff --git a/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js index 8738ffad2..57d3447af 100644 --- a/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js @@ -35,9 +35,7 @@ export const FileSystemMetadataProviderWizard = { label: 'label.finished', index: 4, initialValues: [], - fields: [ - 'enabled' - ] + fields: [] } ], uiSchema: defaultsDeep({ @@ -51,12 +49,6 @@ export const FileSystemMetadataProviderWizard = { '@type' ] }, - { - size: 8, - fields: [ - 'enabled' - ] - }, { size: 8, fields: [ @@ -115,7 +107,6 @@ export const FileSystemMetadataProviderEditor = { 'xmlId', '@type', 'metadataFile', - 'enabled', 'doInitialization' ], override: { @@ -140,7 +131,6 @@ export const FileSystemMetadataProviderEditor = { type: 'group-lg', class: ['col-12'], fields: [ - 'enabled', 'xmlId', 'metadataFile', 'doInitialization' diff --git a/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js index 339c0c606..eea5d3541 100644 --- a/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js @@ -35,9 +35,7 @@ export const LocalDynamicMetadataProviderWizard = { label: 'label.finished', index: 4, initialValues: [], - fields: [ - 'enabled' - ] + fields: [] } ], uiSchema: defaultsDeep({ @@ -51,12 +49,6 @@ export const LocalDynamicMetadataProviderWizard = { '@type' ] }, - { - size: 8, - fields: [ - 'enabled' - ] - }, { size: 8, fields: [ @@ -125,7 +117,6 @@ export const LocalDynamicMetadataProviderEditor = { fields: [ 'name', '@type', - 'enabled', 'xmlId', 'sourceDirectory', ], @@ -150,7 +141,6 @@ export const LocalDynamicMetadataProviderEditor = { type: 'group-lg', class: ['col-12'], fields: [ - 'enabled', 'xmlId', 'sourceDirectory', ] diff --git a/ui/src/app/metadata/domain/source/component/SourceList.js b/ui/src/app/metadata/domain/source/component/SourceList.js index 41e2c7adb..04ea8f983 100644 --- a/ui/src/app/metadata/domain/source/component/SourceList.js +++ b/ui/src/app/metadata/domain/source/component/SourceList.js @@ -3,24 +3,27 @@ import { Link } from 'react-router-dom'; import Badge from 'react-bootstrap/Badge'; import Popover from 'react-bootstrap/Popover'; import Button from 'react-bootstrap/Button'; +import Form from 'react-bootstrap/Form'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faTrash, faCheck } from '@fortawesome/free-solid-svg-icons'; +import { faTrash } from '@fortawesome/free-solid-svg-icons'; import FormattedDate from '../../../../core/components/FormattedDate'; import Translate from '../../../../i18n/components/translate'; import { Scroller } from '../../../../dashboard/component/Scroller'; -import { DeleteSourceConfirmation } from './DeleteSourceConfirmation'; +import { useTranslator } from '../../../../i18n/hooks'; +import { useIsAdmin } from '../../../../core/user/UserContext'; export default function SourceList({ entities, onDelete, onEnable }) { + + const translator = useTranslator(); + const isAdmin = useIsAdmin(); + return ( - - {(onDeleteSource) => - + {(limited) =>
- @@ -29,7 +32,7 @@ export default function SourceList({ entities, onDelete, onEnable }) { - {onDeleteSource && } + {onDelete && } @@ -46,39 +49,40 @@ export default function SourceList({ entities, onDelete, onEnable }) { - - - {onDeleteSource && } @@ -87,8 +91,6 @@ export default function SourceList({ entities, onDelete, onEnable }) {
Author Created Date Enabled
- {onEnable ? - - : - - - - } + + {onEnable && isAdmin ? + onEnable(source, checked)} + checked={source.serviceEnabled} + > + + : + + + + } + + {onDelete && A metadata source must be disabled before it can be deleted. }> - - - + + +
} -
- } -
+ ); } diff --git a/ui/src/app/metadata/domain/source/definition/SourceDefinition.js b/ui/src/app/metadata/domain/source/definition/SourceDefinition.js index bc11108ee..6f7276255 100644 --- a/ui/src/app/metadata/domain/source/definition/SourceDefinition.js +++ b/ui/src/app/metadata/domain/source/definition/SourceDefinition.js @@ -89,7 +89,6 @@ export const SourceBase = { fields: [ 'serviceProviderName', 'entityId', - 'serviceEnabled', 'organization' ] }, @@ -291,7 +290,6 @@ export const SourceEditor = { fields: [ 'serviceProviderName', 'entityId', - 'serviceEnabled', 'organization', 'contacts' ] @@ -424,9 +422,7 @@ export const SourceWizard = { }, { size: 6, - fields: [ - 'serviceEnabled' - ] + fields: [] } ] } @@ -510,9 +506,7 @@ export const SourceWizard = { index: 10, id: 'summary', label: 'label.finished', - fields: [ - 'serviceEnabled' - ] + fields: [] } ] } \ No newline at end of file diff --git a/ui/src/app/metadata/hoc/MetadataSelector.js b/ui/src/app/metadata/hoc/MetadataSelector.js index 7d7155066..fd37ed2c3 100644 --- a/ui/src/app/metadata/hoc/MetadataSelector.js +++ b/ui/src/app/metadata/hoc/MetadataSelector.js @@ -31,6 +31,9 @@ export function MetadataSelector({ children, ...props }) { setMetadata(source); } } + + const reload = () => loadMetadata(id); + React.useEffect(() => { loadMetadata(id) }, [id]); return ( @@ -38,7 +41,7 @@ export function MetadataSelector({ children, ...props }) { {type && {metadata && metadata.version && - {children(metadata)} + {children(metadata, reload)} } } diff --git a/ui/src/app/metadata/view/MetadataOptions.js b/ui/src/app/metadata/view/MetadataOptions.js index 93011229a..04242016d 100644 --- a/ui/src/app/metadata/view/MetadataOptions.js +++ b/ui/src/app/metadata/view/MetadataOptions.js @@ -1,5 +1,5 @@ import React from 'react'; -import { faArrowDown, faArrowUp, faHistory, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faHistory, faPlus, faToggleOff, faToggleOn, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Link, useHistory, useParams } from 'react-router-dom'; import Button from 'react-bootstrap/Button'; @@ -14,14 +14,14 @@ import { MetadataDefinitionContext, MetadataSchemaContext } from '../hoc/Metadat import { useMetadataConfiguration } from '../hooks/configuration'; import { MetadataViewToggle } from '../component/MetadataViewToggle'; -import { DeleteSourceConfirmation } from '../domain/source/component/DeleteSourceConfirmation'; +import { MetadataActions } from '../../admin/container/MetadataActions'; import { MetadataFilters } from '../domain/filter/component/MetadataFilters'; import { MetadataFilterConfigurationList } from '../domain/filter/component/MetadataFilterConfigurationList'; import { MetadataFilterTypes } from '../domain/filter'; import { useMetadataSchema } from '../hooks/schema'; import { FilterableProviders } from '../domain/provider'; -export function MetadataOptions () { +export function MetadataOptions ({reload}) { const metadata = React.useContext(MetadataObjectContext); const definition = React.useContext(MetadataDefinitionContext); @@ -49,9 +49,11 @@ export function MetadataOptions () { const canFilter = FilterableProviders.indexOf(definition.type) > -1; + const enabled = type === 'source' ? metadata.serviceEnabled : metadata.enabled; + return ( - - {(onDeleteSource) => + + {(enable, remove) => <>
diff --git a/ui/src/app/metadata/wizard/MetadataSourceWizard.js b/ui/src/app/metadata/wizard/MetadataSourceWizard.js index 0d23b34af..57369b0dc 100644 --- a/ui/src/app/metadata/wizard/MetadataSourceWizard.js +++ b/ui/src/app/metadata/wizard/MetadataSourceWizard.js @@ -12,18 +12,13 @@ import { useMetadataDefinitionContext, useMetadataSchemaContext } from '../hoc/M import { useMetadataFormDispatcher, setFormDataAction, setFormErrorAction, useMetadataFormData, useMetadataFormErrors } from '../hoc/MetadataFormContext'; import { MetadataConfiguration } from '../component/MetadataConfiguration'; import { Configuration } from '../hoc/Configuration'; -import { useMetadataEntity, useMetadataSources } from '../hooks/api'; -import { Prompt, useHistory } from 'react-router'; -import { removeNull } from '../../core/utility/remove_null'; +import { useMetadataSources } from '../hooks/api'; import Translate from '../../i18n/components/translate'; import { checkChanges } from '../hooks/utility'; -export function MetadataSourceWizard ({ onShowNav }) { - - const { post, loading, response } = useMetadataEntity('source'); - const history = useHistory(); +export function MetadataSourceWizard ({ onShowNav, onSave, block, loading }) { const { data } = useMetadataSources({ cachePolicy: 'no-cache' @@ -50,39 +45,20 @@ export function MetadataSourceWizard ({ onShowNav }) { const onChange = (changes) => { formDispatch(setFormDataAction(changes.formData)); formDispatch(setFormErrorAction(changes.errors)); - setBlocking(checkChanges(metadata, changes.formData)); + block(checkChanges(metadata, changes.formData)); }; const onEditFromSummary = (idx) => { wizardDispatch(setWizardIndexAction(idx)); }; - const onBlur = (form) => { - // console.log(form); - } - - async function save () { - const body = removeNull(metadata, true); - await post('', body); - if (response.ok) { - setBlocking(false); - history.push('/'); - } - } - - const [blocking, setBlocking] = React.useState(false); + const save = () => onSave(definition.parser(metadata)); const validator = definition.validator(data); const warnings = definition.warnings && definition.warnings(metadata); return ( <> - - `message.unsaved-editor` - } - />
0 || loading } saving={loading} /> @@ -109,7 +85,6 @@ export function MetadataSourceWizard ({ onShowNav }) { schema={schema || {}} current={current} onChange={onChange} - onBlur={onBlur} validator={validator} />
diff --git a/ui/src/app/metadata/wizard/MetadataWizardForm.js b/ui/src/app/metadata/wizard/MetadataWizardForm.js index c508bd267..58000c605 100644 --- a/ui/src/app/metadata/wizard/MetadataWizardForm.js +++ b/ui/src/app/metadata/wizard/MetadataWizardForm.js @@ -12,7 +12,7 @@ function ErrorListTemplate () { return (<>); } -export function MetadataWizardForm ({ metadata, definition, schema, current, onChange, onBlur = false, validator }) { +export function MetadataWizardForm ({ metadata, definition, schema, current, onChange, onBlur = () => {}, validator }) { const {uiSchema} = useUiSchema(definition, schema, current);