Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions public/editor-settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,31 @@ password = "opencast"
[metadata]
# If false, the metadata button is not displayed in the main menu
show = true
## Configure metadata display
## Here you can set which metadata fields should be displayed in the editor
## for each catalog.
## If the catalog is not specified, all fields of that catalog will be
## Here you can override various settings for how the metadata will be
## displayed in the editor, for each catalog.
## Setting #1: show (boolean)
## Whether the field should be visible at all
## Setting #2: readonly (boolean)
## Whether the field should be editable by the user
## If you don't configure a setting for a field, a default will be assumed.
## The default settings are based on the metadata display in the admin ui
## in Opencast, which can currently be configured in:
## org.opencastproject.ui.metadata.CatalogUIAdapterFactory-episode-common.cfg
## Furthermore:
## If a catalog is not specified, all fields of that catalog will be
## displayed.
## If the catalog is specified but empty, it will not be displayed at all.
## If the catalog is specified with at least one field, only the fields
## specified for the catalog will be displayed.
[metadata.showFields]
## If a catalog is specified but empty, it will not be displayed at all.
## Example definition
# # This is the default catalog
# "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE" = [
# "title",
# "language",
# "duration"
# ]
# "NameOfAnExtendedMetadataCatalog" = []
# [metadata.configureFields."EVENTS.EVENTS.DETAILS.CATALOG.EPISODE"]
# title = {show = true, readonly = false}
# subject = {show = false}
# description = {readonly = true}

# # This catalog is specified but empty, and as such will not be displayed
# [metadata.configureFields."NameOfAnExtendedMetadataCatalog"]



### Settings of the thumbnail tab
[thumbnail]
Expand Down
38 changes: 27 additions & 11 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ const CONTEXT_SETTINGS_FILE = 'editor-settings.toml';
const SRC_SERVER = 'src-server';
const SRC_URL = 'src-url';

/**
* Possible configuration values for a metadata catalog field
*/
export interface configureFieldsAttributes {
show: boolean,
readonly: boolean,
}

/**
* Settings interface
*/
Expand All @@ -29,7 +37,7 @@ interface iSettings {
},
metadata: {
show: boolean,
showFields: { [key: string]: string[]; } | undefined,
configureFields: { [key: string]: { [key: string]: configureFieldsAttributes } } | undefined,
},
thumbnail: {
show: boolean,
Expand All @@ -52,7 +60,7 @@ var defaultSettings: iSettings = {
},
metadata: {
show: true,
showFields: undefined,
configureFields: undefined,
},
thumbnail: {
show: true,
Expand Down Expand Up @@ -237,18 +245,26 @@ const types = {
throw new Error("is not a boolean");
}
},
'objectWithStringArrays': (v: any, allowParse: any) => {
for (let key in v) {
if (typeof key !== 'string') {
'objectsWithinObjects': (v: any, allowParse: any) => {
for (let catalogName in v) {
if (typeof catalogName !== 'string') {
throw new Error("is not a string, but should be");
}
if (!Array.isArray(v[key])) {
throw new Error("is not an array, but should be");
}
for (let item in v[key]) {
if (typeof item !== 'string') {
for (let fieldName in v[catalogName]) {
if (typeof fieldName !== 'string') {
throw new Error("is not a string, but should be");
}
for (let attributeName in v[catalogName][fieldName]) {
if (typeof attributeName !== 'string') {
throw new Error("is not a string, but should be");
}
if (attributeName === 'show' && typeof v[catalogName][fieldName][attributeName] !== 'boolean') {
throw new Error("is not a boolean");
}
if (attributeName === 'readonly' && typeof v[catalogName][fieldName][attributeName] !== 'boolean') {
throw new Error("is not a boolean");
}
}
}
}
}
Expand All @@ -273,7 +289,7 @@ const SCHEMA = {
},
metadata: {
show : types.boolean,
showFields: types.objectWithStringArrays,
configureFields: types.objectsWithinObjects,
},
thumbnail: {
show : types.boolean,
Expand Down
64 changes: 45 additions & 19 deletions src/main/Metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { errorBoxStyle } from '../cssStyles'
import { useSelector, useDispatch } from 'react-redux';
import {
fetchMetadata, postMetadata, selectCatalogs,
Catalog, MetadataField, setFieldValue, selectGetError, selectGetStatus, selectPostError, selectPostStatus
Catalog, MetadataField, setFieldValue, selectGetError, selectGetStatus, selectPostError, selectPostStatus, setFieldReadonly
} from '../redux/metadataSlice'

import { Form, Field, FieldInputProps } from 'react-final-form'
Expand All @@ -26,7 +26,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/free-solid-svg-icons";
import { DateTime as LuxonDateTime} from "luxon";

import { settings } from '../config'
import { configureFieldsAttributes, settings } from '../config'


/**
Expand Down Expand Up @@ -57,6 +57,36 @@ const Metadata: React.FC<{}> = () => {
}
}, [getStatus, dispatch])

// Overwrite readonly property of fields based on config settings
useEffect(() => {
if (getStatus === 'success') {
for(let catalogIndex = 0; catalogIndex < catalogs.length; catalogIndex++) {
if (settings.metadata.configureFields) {
let configureFields = settings.metadata.configureFields
let catalog = catalogs[catalogIndex]

if (catalog.title in configureFields) {
if (Object.keys(configureFields[catalog.title]).length > 0) {
let configureFieldsCatalog = configureFields[catalog.title]

for (let fieldIndex = 0; fieldIndex < catalog.fields.length; fieldIndex++) {
if (catalog.fields[fieldIndex].id in configureFieldsCatalog) {
if ("readonly" in configureFieldsCatalog[catalog.fields[fieldIndex].id]) {
dispatch(setFieldReadonly({catalogIndex: catalogIndex, fieldIndex: fieldIndex,
value: configureFieldsCatalog[catalog.fields[fieldIndex].id].readonly
}))
}
}
}
} else {
return undefined
}
}
}
}
}
}, [getStatus, catalogs, dispatch])

/**
* CSS
*/
Expand Down Expand Up @@ -585,13 +615,11 @@ const Metadata: React.FC<{}> = () => {
);
}

/**
* Renders a single catalog (e.g. dublincore/episode) in the form
* @param catalog
* @param catalogIndex
* @param showFields array of which fields should be displayed. If empty, display all
*/
const renderCatalog = (catalog: Catalog, catalogIndex: number, showFields: string[]) => {
const renderCatalog = (
catalog: Catalog,
catalogIndex: number,
configureFields: { [key: string]: configureFieldsAttributes }
) => {
return (
<div key={catalogIndex}>
<h2>
Expand All @@ -602,9 +630,8 @@ const Metadata: React.FC<{}> = () => {

{catalog.fields.map((field, i) => {
// Render fields based on given array (usually parsed from config settings)
if (showFields.length !== 0) {
// Lowercase include
if (showFields.filter((str) => str.toLowerCase().includes(field.id.toLowerCase())).length > 0) {
if (field.id in configureFields && "show" in configureFields[field.id]) {
if (configureFields[field.id].show) {
return renderField(field, catalogIndex, i)
} else {
return undefined
Expand Down Expand Up @@ -639,19 +666,18 @@ const Metadata: React.FC<{}> = () => {
</div>

{catalogs.map((catalog, i) => {
// Render catalog and its fields based on config settings
if (settings.metadata.showFields) {
if (catalog.title in settings.metadata.showFields) {
// If there are no fields for a given catalog, do not render it
if (settings.metadata.showFields[catalog.title].length > 0) {
return renderCatalog(catalog, i, settings.metadata.showFields[catalog.title])
if (settings.metadata.configureFields) {
if (catalog.title in settings.metadata.configureFields) {
// If there are no fields for a given catalog, do not render
if (Object.keys(settings.metadata.configureFields[catalog.title]).length > 0) {
return renderCatalog(catalog, i, settings.metadata.configureFields[catalog.title])
} else {
return undefined
}
}
}
// If there are no settings for a given catalog, just render it completely
return renderCatalog(catalog, i, [])
return renderCatalog(catalog, i, {})
})}

{/*
Expand Down
5 changes: 4 additions & 1 deletion src/redux/metadataSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ const metadataSlice = createSlice({
state.catalogs[action.payload.catalogIndex].fields[action.payload.fieldIndex].value = action.payload.value
state.hasChanges = true
},
setFieldReadonly: (state, action: any) => {
state.catalogs[action.payload.catalogIndex].fields[action.payload.fieldIndex].readOnly = action.payload.value
},
setHasChanges: (state, action: PayloadAction<metadata["hasChanges"]>) => {
state.hasChanges = action.payload
}
Expand Down Expand Up @@ -136,7 +139,7 @@ const metadataSlice = createSlice({
}
})

export const { setFieldValue, setHasChanges } = metadataSlice.actions
export const { setFieldValue, setHasChanges, setFieldReadonly } = metadataSlice.actions

export const selectCatalogs = (state: { metadataState: { catalogs: metadata["catalogs"] } }) =>
state.metadataState.catalogs
Expand Down