Skip to content

Commit

Permalink
Merge pull request #1100 from dennis531/callback_url
Browse files Browse the repository at this point in the history
Add callback url for integration (#1084)
  • Loading branch information
Arnei authored Sep 21, 2023
2 parents 9d1662e + b88c774 commit 54cb714
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 19 deletions.
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,21 @@ If an option can be specified both ways, the URL parameter will always take prec

Options which are usually specified in the configuration file are documented in there as well. Metadata configuration options are only documented in the configuration file.

| Option | URL | File | Description
| --------------------|-----|------|------------
| id | ✓ | ✓ | Id of the event that the editor should open by default.
| mediaPackageId | ✓ | ✓ | Deprecated. Use `id` instead.
| opencast.url | ✗ | ✓ | URL of the opencast server to connect to.
| opencast.name | ✗ | ✓ | Opencast user to use. For demo purposes only.
| opencast.password | ✗ | ✓ | Password to use for authentication. For demo purposes only.
| metadata.show | ✓ | ✓ | Show metadata tab.
| trackSelection.show | ✓ | ✓ | Show track selection tab.
| thumbnail.show | ✓ | ✓ | Show thumbnail tab. Demo only.
| debug | ✓ | ✗ | Enable internationalization debugging.
| lng | ✓ | ✗ | Select a specific language. Use language codes like `de` or `en-US`.

| Option | URL | File | Description |
|-------------------------|-----|------|----------------------------------------------------------------------|
| id ||| Id of the event that the editor should open by default. |
| mediaPackageId ||| Deprecated. Use `id` instead. |
| allowedCallbackPrefixes ||| Allowed callback prefixes in callback url. |
| callbackUrl ||| Callback url to go back after finish. |
| callbackSystem ||| Callback system name to go back to. |
| opencast.url ||| URL of the opencast server to connect to. |
| opencast.name ||| Opencast user to use. For demo purposes only. |
| opencast.password ||| Password to use for authentication. For demo purposes only. |
| metadata.show ||| Show metadata tab. |
| trackSelection.show ||| Show track selection tab. |
| thumbnail.show ||| Show thumbnail tab. Demo only. |
| debug ||| Enable internationalization debugging. |
| lng ||| Select a specific language. Use language codes like `de` or `en-US`. |

How to cut a release for Opencast
---------------------------------
Expand Down
23 changes: 23 additions & 0 deletions editor-settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@
# ⚠️ When deployed, this file is publicly accessibly!


####
# General Settings
##

# Allowed prefixes in callback urls to prevent malicious urls
# If empty, no callback url is allowed
# Type: string[]
# Default: []
#allowedCallbackPrefixes = []

# Url to go back after finishing editing
# If undefined, no return link will be shown on the end pages
# Type: string | undefined
# Default: undefined
#callbackUrl =

# Name of system to go back to
# If undefined, a generic system name is used instead of a speficic name
# Type: string | undefined
# Default: undefined
#callbackSystem =


####
# Metadata
##
Expand Down
5 changes: 5 additions & 0 deletions public/editor-settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
# Pick a default event identifier which should be on develop.opencast.org
id = 'ID-dual-stream-demo'

# Callback to develop.opencast.org
allowedCallbackPrefixes = ["https://develop.opencast.org"]
callbackUrl = "https://develop.opencast.org"
callbackSystem = "OPENCAST"

[opencast]
# Connect to develop.opencast.org and use the default demo user
url = 'https://develop.opencast.org'
Expand Down
26 changes: 25 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export interface subtitleTags {
*/
interface iSettings {
id: string | undefined,
allowedCallbackPrefixes: string[],
callbackUrl: string | undefined,
callbackSystem: string | undefined,
opencast: {
url: string,
name: string | undefined,
Expand Down Expand Up @@ -76,6 +79,9 @@ interface iSettings {
*/
const defaultSettings: iSettings = {
id: undefined,
allowedCallbackPrefixes: [],
callbackUrl: undefined,
callbackSystem: undefined,
opencast: {
url: window.location.origin,
name: undefined,
Expand Down Expand Up @@ -136,7 +142,7 @@ export const init = async () => {
// Create empty objects for full path (if the key contains '.') and set
// the value at the end.
let obj : {[k: string]: any} = rawUrlSettings;
if (key.startsWith('opencast.')) {
if (key.startsWith('opencast.') || key === 'allowedCallbackPrefixes') {
return;
}

Expand All @@ -163,6 +169,11 @@ export const init = async () => {
// Prepare local setting to avoid complicated checks later
settings.opencast.local = settings.opencast.local && settings.opencast.url === window.location.origin;

// Prevent malicious callback urls
settings.callbackUrl = settings.allowedCallbackPrefixes.some(
p => settings.callbackUrl?.startsWith(p)
) ? settings.callbackUrl : undefined;

// Configure hotkeys
configure({
ignoreTags: [], // Do not ignore hotkeys when focused on a textarea, input, select
Expand Down Expand Up @@ -321,6 +332,16 @@ const types = {
throw new Error("is not a boolean");
}
},
'array': (v: any, _allowParse: any) => {
if (!Array.isArray(v)) {
throw new Error("is not an array, but should be");
}
for (const entry in v) {
if (typeof entry !== 'string') {
throw new Error("is not a string, but should be");
}
}
},
'map': (v: any, _allowParse: any) => {
for (const key in v) {
if (typeof key !== 'string') {
Expand Down Expand Up @@ -368,6 +389,9 @@ const types = {
// above for some examples.
const SCHEMA = {
id: types.string,
allowedCallbackPrefixes: types.array,
callbackUrl: types.string,
callbackSystem: types.string,
opencast: {
url: types.string,
name: types.string,
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@
"various": {
"error-details-text": "Details: {{errorMessage}}\n",
"error-text": "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal.",
"goBack-button": "Nein, zurück"
"goBack-button": "Nein, zurück",
"callback-button-system": "Zurück zu {{system}}",
"callback-button-generic": "Zurück zum vorigen System"
},
"trackSelection": {
"title": "Spur(en) für die Verarbeitung auswählen",
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@
"various": {
"error-details-text": "Details: {{errorMessage}}\n",
"error-text": "An error has occurred. Please wait a bit and try again.",
"goBack-button": "No, take me back"
"goBack-button": "No, take me back",
"callback-button-system": "Back to {{system}}",
"callback-button-generic": "Back to previous system"
},

"trackSelection": {
Expand Down
36 changes: 35 additions & 1 deletion src/main/Finish.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import Discard from "./Discard"
import WorkflowSelection from "./WorkflowSelection";
import WorkflowConfiguration from "./WorkflowConfiguration";

import { LuDoorOpen} from "react-icons/lu";

import { css } from '@emotion/react'
import { basicButtonStyle } from '../cssStyles'
import { basicButtonStyle, navigationButtonStyle } from '../cssStyles'

import { IconType } from "react-icons";

import { useDispatch, useSelector } from 'react-redux';
import { selectPageNumber, setPageNumber } from '../redux/finishSlice';
import { useTheme } from "../themes";
import { settings } from "../config";
import { useTranslation } from "react-i18next";

/**
* Displays a menu for selecting what should be done with the current changes
Expand Down Expand Up @@ -86,5 +90,35 @@ export const PageButton : React.FC<{pageNumber: number, label: string, Icon: Ico
);
}

/**
* Takes you back to the callback url resource
*/
export const CallbackButton : React.FC = () => {

const { t } = useTranslation();

const theme = useTheme();

const openCallbackUrl = () => {
window.open(settings.callbackUrl, "_self");
}

return (
<>
{settings.callbackUrl !== undefined &&
<div css={[basicButtonStyle(theme), navigationButtonStyle(theme)]}
role="button" tabIndex={0}
onClick={openCallbackUrl}
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => { if (event.key === " " || event.key === "Enter") {
openCallbackUrl()
} }}>
<LuDoorOpen />
<span>{settings.callbackSystem ? t("various.callback-button-system", {system: settings.callbackSystem}) : t("various.callback-button-generic")}</span>
</div>
}
</>
);
}


export default Finish;
3 changes: 2 additions & 1 deletion src/main/Save.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { selectFinishState } from '../redux/finishSlice'
import { selectHasChanges, selectSegments, selectTracks, setHasChanges as videoSetHasChanges } from '../redux/videoSlice'
import { postVideoInformation, selectStatus, selectError } from '../redux/workflowPostSlice'

import { PageButton } from './Finish'
import { CallbackButton, PageButton } from './Finish'

import { useTranslation } from 'react-i18next';
import { AppDispatch } from "../redux/store";
Expand Down Expand Up @@ -58,6 +58,7 @@ const Save : React.FC = () => {
<>
<LuCheckCircle css={{fontSize: 80}}/>
<div>{t("save.success-text")}</div>
<CallbackButton />
</>
)
// Pre save
Expand Down
12 changes: 11 additions & 1 deletion src/main/TheEnd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { basicButtonStyle, flexGapReplacementStyle, navigationButtonStyle } from
import { useTranslation } from 'react-i18next';
import { useTheme } from "../themes";
import { ThemedTooltip } from "./Tooltip";
import { CallbackButton } from "./Finish";

/**
* This page is to be displayed when the user is "done" with the editor
Expand Down Expand Up @@ -41,11 +42,20 @@ const TheEnd : React.FC = () => {
...(flexGapReplacementStyle(20, false)),
})

const restartOrBackStyle = css({
display: "flex",
flexDirection: 'row',
...(flexGapReplacementStyle(20, false)),
})

return (
<div css={theEndStyle}>
{endState === 'discarded' ? <LuXCircle css={{fontSize: 80}}/> : <LuCheckCircle css={{fontSize: 80}}/> }
<div>{text()}</div>
{(endState === 'discarded') && <StartOverButton />}
<div css={restartOrBackStyle}>
<CallbackButton />
{(endState === 'discarded') && <StartOverButton />}
</div>
</div>
);
}
Expand Down

0 comments on commit 54cb714

Please sign in to comment.