diff --git a/.gitignore b/.gitignore
index 573966ad8..21674ccc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
-/repos
+# Ignore custom templates folder.
+/assets/shared/custom-templates/*
-/public/admin
-/public/client
+# Ignore release json file.
/public/release.json
###> symfony/framework-bundle ###
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e9902fb2..31a70fb95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
* Cleaned up Github Actions workflows.
* Updated PHP dependencies.
* Added Playwright github action.
+* Changed how templates are imported.
### NB! Prior to 3.x the project was split into separate repositories
diff --git a/assets/admin/app.jsx b/assets/admin/app.jsx
index 5b8392248..bca4ee6a5 100644
--- a/assets/admin/app.jsx
+++ b/assets/admin/app.jsx
@@ -7,7 +7,7 @@ import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import localStorageKeys from "./components/util/local-storage-keys";
-import RestrictedRoute from "./restricted-route";
+import RestrictedRoute from "./components/restricted-route";
import Topbar from "./components/navigation/topbar/top-bar";
import SideBar from "./components/navigation/sidebar/sidebar";
import ScreenList from "./components/screen/screen-list";
@@ -32,14 +32,14 @@ import UserContext from "./context/user-context";
import ListContext from "./context/list-context";
import SharedPlaylists from "./components/playlist/shared-playlists";
import Logout from "./components/user/logout";
-import AuthHandler from "./auth-handler";
+import AuthHandler from "./components/auth-handler";
import LoadingComponent from "./components/util/loading-component/loading-component";
import ModalProvider from "./context/modal-context/modal-provider";
import UsersList from "./components/users/users-list";
import ActivationCodeList from "./components/activation-code/activation-code-list";
import ActivationCodeCreate from "./components/activation-code/activation-code-create";
import ActivationCodeActivate from "./components/activation-code/activation-code-activate";
-import AdminConfigLoader from "./admin-config-loader.js";
+import AdminConfigLoader from "./components/util/admin-config-loader.js";
import "react-toastify/dist/ReactToastify.css";
import "./app.scss";
import FeedSourcesList from "./components/feed-sources/feed-sources-list";
diff --git a/assets/admin/auth-handler.jsx b/assets/admin/components/auth-handler.jsx
similarity index 86%
rename from assets/admin/auth-handler.jsx
rename to assets/admin/components/auth-handler.jsx
index 510942713..85ac60271 100644
--- a/assets/admin/auth-handler.jsx
+++ b/assets/admin/components/auth-handler.jsx
@@ -1,7 +1,7 @@
import { React, useContext } from "react";
import PropTypes from "prop-types";
-import Login from "./components/user/login";
-import UserContext from "./context/user-context";
+import Login from "./user/login";
+import UserContext from "../context/user-context";
/**
* The auth handler wrapper.
diff --git a/assets/admin/error-boundary.jsx b/assets/admin/components/error-boundary.jsx
similarity index 100%
rename from assets/admin/error-boundary.jsx
rename to assets/admin/components/error-boundary.jsx
diff --git a/assets/admin/error-boundary.scss b/assets/admin/components/error-boundary.scss
similarity index 100%
rename from assets/admin/error-boundary.scss
rename to assets/admin/components/error-boundary.scss
diff --git a/assets/admin/restricted-route.jsx b/assets/admin/components/restricted-route.jsx
similarity index 89%
rename from assets/admin/restricted-route.jsx
rename to assets/admin/components/restricted-route.jsx
index 4ac470e0e..c6b19ec30 100644
--- a/assets/admin/restricted-route.jsx
+++ b/assets/admin/components/restricted-route.jsx
@@ -1,7 +1,7 @@
import { React, useContext } from "react";
import PropTypes from "prop-types";
-import UserContext from "./context/user-context";
-import NoAccess from "./components/no-access/no-access";
+import UserContext from "../context/user-context";
+import NoAccess from "./no-access/no-access";
/**
* The restricted route wrapper.
diff --git a/assets/admin/components/screen/screen-list.jsx b/assets/admin/components/screen/screen-list.jsx
index eaaace95c..ab26b6ded 100644
--- a/assets/admin/components/screen/screen-list.jsx
+++ b/assets/admin/components/screen/screen-list.jsx
@@ -18,7 +18,7 @@ import {
displayError,
} from "../util/list/toast-component/display-toast";
import "./screen-list.scss";
-import AdminConfigLoader from "../../admin-config-loader.js";
+import AdminConfigLoader from "../util/admin-config-loader.js";
/**
* The screen list component.
diff --git a/assets/admin/components/screen/screen-status.jsx b/assets/admin/components/screen/screen-status.jsx
index 1c10835be..6530968a7 100644
--- a/assets/admin/components/screen/screen-status.jsx
+++ b/assets/admin/components/screen/screen-status.jsx
@@ -18,7 +18,7 @@ import idFromUrl from "../util/helpers/id-from-url";
import { api } from "../../redux/api/api.generated.ts";
import { displayError } from "../util/list/toast-component/display-toast";
import FormInput from "../util/forms/form-input";
-import AdminConfigLoader from "../../admin-config-loader.js";
+import AdminConfigLoader from "../util/admin-config-loader.js";
/**
* Displays screen status.
diff --git a/assets/admin/components/slide/preview/slide-preview.jsx b/assets/admin/components/slide/preview/slide-preview.jsx
index 855eb5a64..afd65adc9 100644
--- a/assets/admin/components/slide/preview/slide-preview.jsx
+++ b/assets/admin/components/slide/preview/slide-preview.jsx
@@ -2,16 +2,15 @@ import { React, useEffect, useState } from "react";
import { Button } from "react-bootstrap";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";
-import ErrorBoundary from "../../../error-boundary";
+import ErrorBoundary from "../../error-boundary";
import "./slide-preview.scss";
-import renderSlide from "../../../../shared/template/slide.jsx";
+import { renderSlide } from "../../../../shared/slide-utils/templates";
/**
- * A remote component wrapper
+ * Slide live preview.
*
* @param {object} props Props.
* @param {object} props.slide The slide.
- * @param {boolean} props.url The url for the remote component.
* @param {object} props.mediaData Object of loaded media.
* @param {object} props.themeData Object of theme data.
* @param {string} props.orientation Display orientation or horizontal.
@@ -23,7 +22,7 @@ import renderSlide from "../../../../shared/template/slide.jsx";
*/
function SlidePreview({
slide,
- templateData,
+ templateData,
showPreview,
orientation = "",
closeButton = false,
@@ -147,7 +146,6 @@ function SlidePreview({
SlidePreview.propTypes = {
slide: PropTypes.shape({ content: PropTypes.shape({}).isRequired })
.isRequired,
- url: PropTypes.string.isRequired,
mediaData: PropTypes.shape({
"@id": PropTypes.string,
}),
diff --git a/assets/admin/components/slide/slide-form.jsx b/assets/admin/components/slide/slide-form.jsx
index 602a5a007..e226b706a 100644
--- a/assets/admin/components/slide/slide-form.jsx
+++ b/assets/admin/components/slide/slide-form.jsx
@@ -16,7 +16,7 @@ import idFromUrl from "../util/helpers/id-from-url";
import FormInput from "../util/forms/form-input";
import ContentForm from "./content/content-form";
import LoadingComponent from "../util/loading-component/loading-component";
-import SlidePreview from "./preview/slide-preview.jsx";
+import SlidePreview from "./preview/slide-preview";
import FeedSelector from "./content/feed-selector";
import SelectPlaylistsTable from "../util/multi-and-table/select-playlists-table";
import localStorageKeys from "../util/local-storage-keys";
@@ -26,6 +26,7 @@ import "./slide-form.scss";
import Preview from "../preview/preview";
import StickyFooter from "../util/sticky-footer";
import Select from "../util/forms/select";
+import { getConfig } from "../../../shared/slide-utils/templates";
/**
* The slide form component.
@@ -92,6 +93,7 @@ function SlideForm({
const [themesOptions, setThemesOptions] = useState();
const [displayPreview, setDisplayPreview] = useState(null);
const [templateError, setTemplateError] = useState(false);
+ const [disableLivePreview, setDisableLivePreview] = useState(false);
// Load templates.
const { data: templates, isLoading: loadingTemplates } =
@@ -157,19 +159,12 @@ function SlideForm({
const newSelectedTemplates = [];
if (selectedTemplate) {
- // Get content form from template resources.
- const contentFormUrl = selectedTemplate?.resources?.admin;
- fetch(contentFormUrl)
- .then((response) => response.json())
- .then((data) => {
- setContentFormElements(data);
- })
- .catch((er) => {
- displayError(t("template-error"), er);
- });
-
+ const slideConfig = getConfig(selectedTemplate['id']);
+ setContentFormElements(slideConfig.adminForm ?? []);
+ setDisableLivePreview(slideConfig?.options?.disableLivePreview ?? false);
newSelectedTemplates.push(selectedTemplate);
}
+
setSelectedTemplates(newSelectedTemplates);
}, [selectedTemplate]);
@@ -401,13 +396,12 @@ function SlideForm({
- {selectedTemplate?.resources?.options?.disableLivePreview && (
+ {disableLivePreview && (
{t("slide-preview-disabled-preview")}
)}
- {!selectedTemplate?.resources?.options?.disableLivePreview &&
- selectedTemplate?.resources?.component && (
+ {!disableLivePreview && (
<>
{previewOrientation === "horizontal" && (
@@ -581,10 +575,6 @@ SlideForm.propTypes = {
selectTemplate: PropTypes.func.isRequired,
selectedTemplate: PropTypes.shape({
"@id": PropTypes.string,
- resources: PropTypes.shape({
- admin: PropTypes.string.isRequired,
- component: PropTypes.string.isRequired,
- }).isRequired,
}),
isLoading: PropTypes.bool,
loadingMessage: PropTypes.string,
diff --git a/assets/admin/components/user/login.jsx b/assets/admin/components/user/login.jsx
index bcfb922eb..f56677a11 100644
--- a/assets/admin/components/user/login.jsx
+++ b/assets/admin/components/user/login.jsx
@@ -9,7 +9,7 @@ import {MultiSelect} from "react-multi-select-component";
import UserContext from "../../context/user-context";
import FormInput from "../util/forms/form-input";
import {api} from "../../redux/api/api.generated.ts";
-import AdminConfigLoader from "../../admin-config-loader.js";
+import AdminConfigLoader from "../util/admin-config-loader.js";
import {displayError} from "../util/list/toast-component/display-toast";
import localStorageKeys from "../util/local-storage-keys";
import LoginSidebar from "../navigation/login-sidebar/login-sidebar";
diff --git a/assets/admin/admin-config-loader.js b/assets/admin/components/util/admin-config-loader.js
similarity index 100%
rename from assets/admin/admin-config-loader.js
rename to assets/admin/components/util/admin-config-loader.js
diff --git a/assets/client/app.jsx b/assets/client/app.jsx
index 477055dd4..c99cc02b0 100644
--- a/assets/client/app.jsx
+++ b/assets/client/app.jsx
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import Screen from "./components/screen.jsx";
import ContentService from "./service/content-service";
-import ClientConfigLoader from "./client-config-loader.js";
+import ClientConfigLoader from "./util/client-config-loader.js";
import logger from "./logger/logger";
import "./app.scss";
import fallback from "./assets/fallback.png";
diff --git a/assets/client/components/region.jsx b/assets/client/components/region.jsx
index 116237c95..a06d2b7ba 100644
--- a/assets/client/components/region.jsx
+++ b/assets/client/components/region.jsx
@@ -3,10 +3,10 @@ import PropTypes from "prop-types";
import "./region.scss";
import { createGridArea } from "../../shared/grid-generator/grid-generator";
import { TransitionGroup, CSSTransition } from "react-transition-group";
-import Slide from "./slide.jsx";
import ErrorBoundary from "./error-boundary.jsx";
import idFromPath from "../util/id-from-path";
import logger from "../logger/logger";
+import Slide from "./slide.jsx";
/**
* Region component.
diff --git a/assets/client/components/screen.jsx b/assets/client/components/screen.jsx
index 73d9d3789..5c384a6d9 100644
--- a/assets/client/components/screen.jsx
+++ b/assets/client/components/screen.jsx
@@ -6,7 +6,7 @@ import Region from "./region.jsx";
import "./screen.scss";
import logger from "../logger/logger";
import TouchRegion from "./touch-region.jsx";
-import ClientConfigLoader from "../client-config-loader.js";
+import ClientConfigLoader from "../util/client-config-loader.js";
/**
* Screen component.
diff --git a/assets/client/components/slide.jsx b/assets/client/components/slide.jsx
index ac3407fa9..7ca5dba8e 100644
--- a/assets/client/components/slide.jsx
+++ b/assets/client/components/slide.jsx
@@ -2,7 +2,7 @@ import {React} from "react";
import "./slide.scss";
import ErrorBoundary from "./error-boundary.jsx";
import logger from "../logger/logger";
-import renderSlide from "../../shared/template/slide.jsx";
+import { renderSlide } from "../../shared/slide-utils/templates.js";
/**
* Slide component.
diff --git a/assets/client/components/touch-region.jsx b/assets/client/components/touch-region.jsx
index 787a01fa4..05d3b5562 100644
--- a/assets/client/components/touch-region.jsx
+++ b/assets/client/components/touch-region.jsx
@@ -2,11 +2,11 @@ import { React, useEffect, useState, createRef } from "react";
import PropTypes from "prop-types";
import "./touch-region.scss";
import { createGridArea } from "../../shared/grid-generator/grid-generator";
-import Slide from "./slide.jsx";
import ErrorBoundary from "./error-boundary.jsx";
import idFromPath from "../util/id-from-path";
import IconClose from "../assets/icon-close.svg";
import IconPointer from "../assets/icon-pointer.svg";
+import Slide from "./slide.jsx";
/**
* Region component.
diff --git a/assets/client/service/content-service.js b/assets/client/service/content-service.js
index c1ae16656..512f46e6a 100644
--- a/assets/client/service/content-service.js
+++ b/assets/client/service/content-service.js
@@ -8,7 +8,7 @@ import {
import logger from "../logger/logger";
import DataSync from "../data-sync/data-sync";
import ScheduleService from "./schedule-service";
-import ClientConfigLoader from "../client-config-loader.js";
+import ClientConfigLoader from "../util/client-config-loader.js";
/**
* ContentService.
diff --git a/assets/client/service/release-service.js b/assets/client/service/release-service.js
index 731a7d145..ba2f7a175 100644
--- a/assets/client/service/release-service.js
+++ b/assets/client/service/release-service.js
@@ -1,5 +1,5 @@
import ReleaseLoader from "../util/release-loader";
-import ClientConfigLoader from "../client-config-loader.js";
+import ClientConfigLoader from "../util/client-config-loader.js";
import defaults from "../util/defaults";
import idFromPath from "../util/id-from-path";
import appStorage from "../util/app-storage";
diff --git a/assets/client/service/schedule-service.js b/assets/client/service/schedule-service.js
index fddbcff2e..a69c8cb4b 100644
--- a/assets/client/service/schedule-service.js
+++ b/assets/client/service/schedule-service.js
@@ -4,7 +4,7 @@ import Md5 from "crypto-js/md5";
import Base64 from "crypto-js/enc-base64";
import isPublished from "../util/isPublished";
import logger from "../logger/logger";
-import ClientConfigLoader from "../client-config-loader.js";
+import ClientConfigLoader from "../util/client-config-loader.js";
import ScheduleUtils from "../util/schedule";
/**
@@ -138,7 +138,7 @@ class ScheduleService {
// Update region.
this.regions[regionId].hash = hash;
- this.regions[regionId].slides = slides;
+ this.regions[regionId].slide = slides;
if (newContent) {
// Send slides to region.
diff --git a/assets/client/service/token-service.js b/assets/client/service/token-service.js
index 5d80706f3..478758cdb 100644
--- a/assets/client/service/token-service.js
+++ b/assets/client/service/token-service.js
@@ -1,6 +1,6 @@
import logger from "../logger/logger";
import appStorage from "../util/app-storage";
-import ClientConfigLoader from "../client-config-loader.js";
+import ClientConfigLoader from "../util/client-config-loader.js";
import defaults from "../util/defaults";
import statusService from "./status-service";
import constants from "../util/constants";
diff --git a/assets/client/client-config-loader.js b/assets/client/util/client-config-loader.js
similarity index 97%
rename from assets/client/client-config-loader.js
rename to assets/client/util/client-config-loader.js
index b02c0e180..f940bb779 100644
--- a/assets/client/client-config-loader.js
+++ b/assets/client/util/client-config-loader.js
@@ -1,5 +1,5 @@
// Only fetch new config if more than 15 minutes have passed.
-import appStorage from "./util/app-storage";
+import appStorage from "./app-storage.js";
const configFetchIntervalDefault = 15 * 60 * 1000;
diff --git a/assets/shared/custom-templates-example/custom-template-example.json b/assets/shared/custom-templates-example/custom-template-example.json
new file mode 100644
index 000000000..547022df1
--- /dev/null
+++ b/assets/shared/custom-templates-example/custom-template-example.json
@@ -0,0 +1,22 @@
+{
+ "title": "Test",
+ "id": "01K2PREY1Q0XCTR9EK1YT5R4XK",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "test-form-1",
+ "input": "header",
+ "text": "Skabelon: Test",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "test-form-title",
+ "input": "input",
+ "name": "title",
+ "type": "text",
+ "label": "Overskrift",
+ "helpText": "Her kan du skrive overskrift.",
+ "formGroupClasses": "mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/custom-templates-example/custom-template-example.jsx b/assets/shared/custom-templates-example/custom-template-example.jsx
new file mode 100644
index 000000000..ed53a6467
--- /dev/null
+++ b/assets/shared/custom-templates-example/custom-template-example.jsx
@@ -0,0 +1,58 @@
+import React, { useEffect } from "react";
+import templateConfig from "./custom-template-example.json";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { ThemeStyles } from "../slide-utils/slide-util.jsx";
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
;
+}
+
+/**
+ * @param {object} props Props.
+ * @param {object} props.slide The slide.
+ * @param {object} props.content The slide content.
+ * @param {boolean} props.run Whether or not the slide should start running.
+ * @param {Function} props.slideDone Function to invoke when the slide is done playing.
+ * @param {string} props.executionId Unique id for the instance.
+ * @returns {JSX.Element} The component.
+ */
+function CustomTemplateExample({ slide, content, run, slideDone, executionId }) {
+ const { duration = 15000 } = content;
+ const { title = "Default title" } = content;
+
+ const slideExecution = new BaseSlideExecution(slide, slideDone);
+
+ useEffect(() => {
+ if (run) {
+ slideExecution.start(duration);
+ }
+
+ return function cleanup() {
+ slideExecution.stop();
+ };
+ }, [run]);
+
+ return (<>
+
+
{title}
+
+
+
+ >);
+}
+
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/GlobalStyles.js b/assets/shared/slide-utils/GlobalStyles.js
similarity index 100%
rename from assets/shared/template/GlobalStyles.js
rename to assets/shared/slide-utils/GlobalStyles.js
diff --git a/assets/shared/template/base-slide-execution.js b/assets/shared/slide-utils/base-slide-execution.js
similarity index 100%
rename from assets/shared/template/base-slide-execution.js
rename to assets/shared/slide-utils/base-slide-execution.js
diff --git a/assets/shared/template/global-styles.css b/assets/shared/slide-utils/global-styles.css
similarity index 100%
rename from assets/shared/template/global-styles.css
rename to assets/shared/slide-utils/global-styles.css
diff --git a/assets/shared/template/slide-util.jsx b/assets/shared/slide-utils/slide-util.jsx
similarity index 100%
rename from assets/shared/template/slide-util.jsx
rename to assets/shared/slide-utils/slide-util.jsx
diff --git a/assets/shared/slide-utils/templates.js b/assets/shared/slide-utils/templates.js
new file mode 100644
index 000000000..793168ea3
--- /dev/null
+++ b/assets/shared/slide-utils/templates.js
@@ -0,0 +1,82 @@
+// Load templates.
+// @see https://vite.dev/guide/features.html#glob-import
+// @see docs/custom-templates.md
+// Eager loading because no other code piece imports the templates otherwise.
+const templateModules = import.meta.glob("../templates/*.jsx", { eager: true });
+const customTemplatesModules = import.meta.glob("../custom-templates/*.jsx", {
+ eager: true,
+});
+
+function duckTypingTemplateModule(module) {
+ return (
+ typeof module.id === "function" &&
+ typeof module.config === "function" &&
+ typeof module.renderSlide === "function"
+ );
+}
+
+function findModule(modules, templateUlid) {
+ for (const key of Object.keys(modules)) {
+ const module = modules[key].default;
+
+ if (duckTypingTemplateModule(module)) {
+ if (module.id() === templateUlid) {
+ return module;
+ }
+ } else {
+ throw new Error(
+ "Template should implement functions: id(), config(), renderSlide(slide, run, slideDone)",
+ );
+ }
+ }
+
+ return null;
+}
+
+function getTemplateModule(templateUlid) {
+ if (!templateUlid) {
+ return null;
+ }
+
+ const module =
+ findModule(templateModules, templateUlid) ??
+ findModule(customTemplatesModules, templateUlid) ??
+ null;
+
+ if (module === null) {
+ throw new Error(`Cannot find module '${templateUlid}'`);
+ }
+
+ return module;
+}
+
+/**
+ * Get the config of the template.
+ *
+ * @param templateUlid The ULID of the template.
+ * @return object
+ */
+function getConfig(templateUlid) {
+ return getTemplateModule(templateUlid).config();
+}
+
+/**
+ * Render slide.
+ *
+ * @param {object} slide The slide object.
+ * @param {string} run The run id.
+ * @param {Function} slideDone The function to invoke when the slide is done.
+ * @return {JSXElement|string}
+ */
+function renderSlide(slide, run, slideDone) {
+ const templateUlid = slide?.templateData?.id;
+ const module = getTemplateModule(templateUlid);
+
+ if (!module) {
+ return "";
+ }
+
+ return module.renderSlide(slide, run, slideDone);
+}
+
+export { getConfig, renderSlide };
diff --git a/assets/shared/template/book-review/book-review-admin.json b/assets/shared/template/book-review/book-review-admin.json
deleted file mode 100644
index b1fbe94ca..000000000
--- a/assets/shared/template/book-review/book-review-admin.json
+++ /dev/null
@@ -1,73 +0,0 @@
-[
- {
- "key": "book-review-form-1",
- "input": "header",
- "text": "Skabelon: Anmeldelse",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "book-review-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "book-review-form-3",
- "input": "rich-text-input",
- "name": "bookText",
- "label": "Teksten til anmeldelsen",
- "helpText": "Her kan du skrive teksten til anmeldelsen"
- },
- {
- "key": "book-review-form-4",
- "multipleImages": false,
- "input": "image",
- "name": "bookImage",
- "label": "Billede",
- "helpText": "Billede af det som bliver anmeldt"
- },
- {
- "key": "book-review-form-5",
- "input": "header-h3",
- "text": "Forfatter",
- "name": "header3",
- "formGroupClasses": "h5 mt-3 mb-3"
- },
- {
- "key": "book-review-form-6",
- "input": "input",
- "name": "authorText",
- "type": "text",
- "label": "Forfattertekst",
- "helpText": "Her kan du skrive tekst til forfatteren",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "book-review-form-7",
- "multipleImages": false,
- "input": "image",
- "name": "authorImage",
- "label": "Billede",
- "helpText": "Billede til forfatteren"
- },
- {
- "key": "book-review-form-8",
- "input": "header-h3",
- "text": "Opsætning",
- "name": "header3",
- "formGroupClasses": "h5 mt-3 mb-3"
- },
- {
- "key": "book-review-form-9",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/book-review/book-review-config.json b/assets/shared/template/book-review/book-review-config.json
deleted file mode 100644
index 6dfae808a..000000000
--- a/assets/shared/template/book-review/book-review-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Anmeldelse",
- "icon": "",
- "id": "01FP2SME0ENTXWF362XHM6Z1B4",
- "description": "Skabelon til anmeldelser.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/book-review.js",
- "admin": "https://display-templates.local.itkdev.dk/build/book-review-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/book-review-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/book-review/book-review-content-example.json b/assets/shared/template/book-review/book-review-content-example.json
deleted file mode 100644
index 0275ec789..000000000
--- a/assets/shared/template/book-review/book-review-content-example.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "bookText": "
Lorem Ipsum
...Is simply
dummy text of the printing and typesetting industry...",
- "authorText": "There are many variations of passages of Lorem Ipsum available.",
- "authorImage": "/v1/media/00000000000000000000000001",
- "bookImage": "/v1/media/00000000000000000000000002"
-}
diff --git a/assets/shared/template/book-review/book-review-schema.json b/assets/shared/template/book-review/book-review-schema.json
deleted file mode 100644
index 4f12a78a7..000000000
--- a/assets/shared/template/book-review/book-review-schema.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://github.com/os2display/display-templates/blob/develop/build/book-review/book-review-schema.json",
- "title": "book-review schema",
- "description": "Content schema for book-review",
- "type": "object",
- "properties": {
- "bookImage": {
- "description": "@id for the media for the review",
- "type": "string"
- },
- "authorImage": {
- "description": "@id for the media for the author",
- "type": "string"
- },
- "bookText": {
- "description": "Review rich text",
- "type": "string"
- },
- "authorText": {
- "description": "Author text",
- "type": "string"
- }
- }
-}
diff --git a/assets/shared/template/calendar/calendar-admin.json b/assets/shared/template/calendar/calendar-admin.json
deleted file mode 100644
index 253d0fca7..000000000
--- a/assets/shared/template/calendar/calendar-admin.json
+++ /dev/null
@@ -1,214 +0,0 @@
-[
- {
- "key": "calendar-form-1",
- "input": "header",
- "text": "Skabelon: Kalender",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "calendar-form-2",
- "input": "header-h3",
- "text": "Opsæt feed",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "calendar-form-3",
- "input": "feed",
- "text": "Opsæt feed",
- "name": "feed",
- "supportedFeedOutputType": "calendar",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "calendar-form-4",
- "input": "header-h3",
- "text": "Konfigurér slide",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "calendar-form-duration",
- "input": "duration",
- "name": "duration",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "calendar-form-layout",
- "input": "select",
- "name": "layout",
- "label": "Vælg layout",
- "required": true,
- "helpText": "Hvis du vælge \"Enkelt lokale med booking\" virker bookingdelen IKKE i preview.",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "layout1",
- "title": "Enkelt lokale",
- "value": "single"
- },
- {
- "key": "layout2",
- "title": "Flere resurser",
- "value": "multiple"
- },
- {
- "key": "layout3",
- "title": "Flere resurser, flere dage",
- "value": "multipleDays"
- },
- {
- "key": "layout4",
- "title": "Enkelt lokale med booking",
- "value": "singleBooking"
- }
- ]
- },
- {
- "key": "calendar-form-form-14",
- "input": "select",
- "required": true,
- "label": "Tekststørrelse",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "fontsize1",
- "title": "xs",
- "value": "font-size-xs"
- },
- {
- "key": "fontsize2",
- "title": "s",
- "value": "font-size-s"
- },
- {
- "key": "fontsize3",
- "title": "m",
- "value": "font-size-m"
- },
- {
- "key": "fontsize4",
- "title": "l",
- "value": "font-size-lg"
- },
- {
- "key": "fontsize5",
- "title": "xl",
- "value": "font-size-xl"
- }
- ],
- "name": "fontSize"
- },
- {
- "key": "calendar-form-title",
- "input": "input",
- "name": "title",
- "type": "text",
- "label": "Overskrift",
- "helpText": "Her kan du skrive overskrift.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "calendar-form-subtitle",
- "input": "input",
- "name": "subTitle",
- "type": "text",
- "label": "Underoverskrift",
- "helpText": "Her kan du skrive underoverskrift.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "calendar-form-resource-available-text",
- "input": "input",
- "name": "resourceAvailableText",
- "type": "text",
- "label": "Tekst når resursen er ledig",
- "helpText": "Her kan du skrive tekst, som vises når resursen er ledig.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "calendar-form-resource-unavailable-text",
- "input": "input",
- "name": "resourceUnavailableText",
- "type": "text",
- "label": "Begivenhedstitel fallback",
- "helpText": "Her kan du skrive tekst på begivenheder, hvor der ikke er sat en titel.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "calendar-form-footer-text",
- "input": "input",
- "name": "footerText",
- "type": "text",
- "label": "Footer tekst",
- "helpText": "Her kan du skrive tekst til footeren.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "calendar-form-hide-grid",
- "input": "checkbox",
- "label": "Skjul grid",
- "name": "hideGrid",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "calendar-form-has-date-and-time",
- "input": "checkbox",
- "label": "Vis dato og tidspunkt",
- "name": "hasDateAndTime",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "calendar-form-date-as-box",
- "input": "checkbox",
- "label": "Dato og tidspunkt som boks",
- "name": "dateAsBox",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "calendar-form-image",
- "multipleImages": false,
- "input": "image",
- "name": "image",
- "label": "Baggrundsbillede"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- },
- {
- "key": "calendar-form-display-headers",
- "input": "checkbox",
- "label": "Vis tabeloverskrifter",
- "name": "displayHeaders",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "calendar-form-header-orders",
- "input": "select",
- "label": "Hvilken rækkefølge skal overskrifterne i tabellen være i",
- "options": [
- {
- "key": "whenwhatwhere",
- "value": "whenwhatwhere",
- "title": "Hvornår, hvad, hvor"
- },
- {
- "key": "whatwherewhen",
- "value": "whatwherewhen",
- "title": "Hvad, hvor, hvornår"
- }
- ],
- "name": "headerOrder",
- "formGroupClasses": "col-md-6 mb-3",
- "helpText": "Dette er kun relevant hvis \"Flere resurser\" er valgt under \"layout\". Standard er \"Hvornår, hvad, hvor.\""
- }
-]
diff --git a/assets/shared/template/calendar/calendar-config.json b/assets/shared/template/calendar/calendar-config.json
deleted file mode 100644
index 9b2f3524f..000000000
--- a/assets/shared/template/calendar/calendar-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Kalender",
- "icon": "",
- "id": "01FRJPF4XATRN8PBZ35XN84PS6",
- "description": "Mulighed for at vise et kalenderfeed.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/calendar.js",
- "admin": "https://display-templates.local.itkdev.dk/build/calendar-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/calendar-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/contacts/contacts-admin.json b/assets/shared/template/contacts/contacts-admin.json
deleted file mode 100644
index ce6733023..000000000
--- a/assets/shared/template/contacts/contacts-admin.json
+++ /dev/null
@@ -1,33 +0,0 @@
-[
- {
- "key": "contacts-form-1",
- "input": "header",
- "text": "Skabelon: Kontakter",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "contacts-form-2",
- "input": "checkbox",
- "label": "Animeret streg under overskrift",
- "name": "separator",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "contacts-form-3",
- "input": "contacts",
- "name": "contacts",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "contacts-form-4",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/contacts/contacts-config.json b/assets/shared/template/contacts/contacts-config.json
deleted file mode 100644
index 7f923cd9d..000000000
--- a/assets/shared/template/contacts/contacts-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Kontakter",
- "icon": "",
- "id": "01FPZ19YEHX7MQ5Q6ZS0WK0VEA",
- "description": "Skabelon til kontakter.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/contacts.js",
- "admin": "https://display-templates.local.itkdev.dk/build/contacts-admin.json",
- "schema": "",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/iframe/iframe-admin.json b/assets/shared/template/iframe/iframe-admin.json
deleted file mode 100644
index fd579402b..000000000
--- a/assets/shared/template/iframe/iframe-admin.json
+++ /dev/null
@@ -1,27 +0,0 @@
-[
- {
- "key": "iframe-form-1",
- "input": "header",
- "text": "Skabelon: IFrame",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "iframe-form-2",
- "input": "input",
- "name": "source",
- "label": "URL til iframe",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "iframe-form-3",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/iframe/iframe-config.json b/assets/shared/template/iframe/iframe-config.json
deleted file mode 100644
index 6feac5b51..000000000
--- a/assets/shared/template/iframe/iframe-config.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "title": "IFrame",
- "icon": "",
- "id": "01FQBJQ2M3544ZKAADPWBXHY71",
- "description": "Skabelon til iFrame.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/iframe.js",
- "admin": "https://display-templates.local.itkdev.dk/build/iframe-admin.json",
- "schema": "",
- "assets": [],
- "options": {
- "disableLivePreview": true
- },
- "content": {}
- }
-}
diff --git a/assets/shared/template/image-text/image-text-admin.json b/assets/shared/template/image-text/image-text-admin.json
deleted file mode 100644
index a6f6dabd8..000000000
--- a/assets/shared/template/image-text/image-text-admin.json
+++ /dev/null
@@ -1,243 +0,0 @@
-[
- {
- "key": "image-text-form-1",
- "input": "header",
- "text": "Skabelon: Tekst og billede",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "image-text-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "image-text-form-3",
- "input": "textarea",
- "name": "title",
- "label": "Overskrift på slide",
- "helpText": "Her kan du skrive overskriften til slidet",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "image-text-form-4",
- "input": "rich-text-input",
- "name": "text",
- "label": "Tekst på slide",
- "helpText": "Her kan du skrive teksten til slidet",
- "formGroupClasses": "col-md"
- },
- {
- "key": "image-text-form-81",
- "input": "select",
- "required": true,
- "label": "Tekststørrelse",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "fontsize1",
- "title": "xs",
- "value": "font-size-xs"
- },
- {
- "key": "fontsize2",
- "title": "s",
- "value": "font-size-s"
- },
- {
- "key": "fontsize3",
- "title": "m",
- "value": "font-size-m"
- },
- {
- "key": "fontsize4",
- "title": "l",
- "value": "font-size-lg"
- },
- {
- "key": "fontsize5",
- "title": "xl",
- "value": "font-size-xl"
- }
- ],
- "name": "fontSize"
- },
-
- {
- "key": "image-text-form-5",
- "multipleImages": true,
- "input": "image",
- "name": "image",
- "label": "Billeder",
- "helpText": "Hvis du tilføjer mere end et billede bliver de vist i (varighed / antal_billeder) sekunder hver."
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- },
- {
- "key": "image-text-form-6",
- "input": "header-h3",
- "text": "Opsætning",
- "name": "header3",
- "formGroupClasses": "h5 mt-3 mb-3"
- },
- {
- "key": "image-text-form-7",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-8",
- "input": "select",
- "required": true,
- "label": "Hvor skal tekstboksen være placeret",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "placement1",
- "title": "Toppen",
- "value": "top"
- },
- {
- "key": "placement2",
- "title": "Højre",
- "value": "right"
- },
- {
- "key": "placement3",
- "title": "Bunden",
- "value": "bottom"
- },
- {
- "key": "placement4",
- "title": "Venstre",
- "value": "left"
- }
- ],
- "name": "boxAlign"
- },
- {
- "key": "image-text-form-9",
- "input": "checkbox",
- "label": "Margin rundt om boks",
- "name": "boxMargin",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-10",
- "input": "checkbox",
- "label": "Animeret streg under overskrift",
- "name": "separator",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-11",
- "input": "checkbox",
- "label": "Gør tekstboksen mindre",
- "name": "halfSize",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-12",
- "input": "checkbox",
- "label": "Alternativt layout uden tekstboks",
- "helpText": "Denne kan ikke kombineres med den animerede tekst under overskriften",
- "name": "reversed",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-13",
- "input": "checkbox",
- "label": "Skyggeeffekt på tekstboksen",
- "name": "shadow",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-14",
- "input": "checkbox",
- "label": "Vis logo fra tema",
- "name": "showLogo",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-15",
- "input": "select",
- "required": false,
- "label": "Logostørrelse",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "logosize1",
- "title": "s",
- "value": "logo-size-s"
- },
- {
- "key": "logosize2",
- "title": "m",
- "value": "logo-size-m"
- },
- {
- "key": "logosize3",
- "title": "l",
- "value": "logo-size-l"
- }
- ],
- "name": "logoSize"
- },
- {
- "key": "image-text-form-16",
- "input": "select",
- "required": false,
- "label": "Logoposition",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "logoposition1",
- "title": "Top/venstre",
- "value": "logo-position-top-left"
- },
- {
- "key": "logoposition2",
- "title": "Top/højre",
- "value": "logo-position-top-right"
- },
- {
- "key": "logoposition3",
- "title": "Bund/venstre",
- "value": "logo-position-bottom-left"
- },
- {
- "key": "logoposition4",
- "title": "Bund/højre",
- "value": "logo-position-bottom-right"
- }
- ],
- "name": "logoPosition"
- },
- {
- "key": "image-text-form-17",
- "input": "checkbox",
- "label": "Margin om logo",
- "name": "logoMargin",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "image-text-form-18",
- "input": "checkbox",
- "label": "Deaktiver fade ved flere billeder",
- "name": "disableImageFade",
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/image-text/image-text-config.json b/assets/shared/template/image-text/image-text-config.json
deleted file mode 100644
index 5b66a4ce8..000000000
--- a/assets/shared/template/image-text/image-text-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Billede og tekst",
- "icon": "",
- "id": "01FP2SNGFN0BZQH03KCBXHKYHG",
- "description": "Mulighed for at sætte billede og tekst, med forskellige visninger.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/image-text.js",
- "admin": "https://display-templates.local.itkdev.dk/build/image-text-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/image-text-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/image-text/image-text-content-example.json b/assets/shared/template/image-text/image-text-content-example.json
deleted file mode 100644
index 10d44fd1c..000000000
--- a/assets/shared/template/image-text/image-text-content-example.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "title": "Slide title",
- "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt...",
- "image": "/v1/media/00000000000000000000000001",
- "styling": {
- "boxAlign": "top",
- "boxMargin": false,
- "shadow": true,
- "separator": false,
- "halfSize": false,
- "reversed": false
- }
-}
diff --git a/assets/shared/template/image-text/image-text-schema.json b/assets/shared/template/image-text/image-text-schema.json
deleted file mode 100644
index c7eb041cc..000000000
--- a/assets/shared/template/image-text/image-text-schema.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://github.com/os2display/display-templates/blob/develop/build/image-text/image-text-schema.json",
- "title": "image-text schema",
- "description": "Content schema for image-text",
- "type": "object",
- "properties": {
- "image": {
- "description": "@id for the image",
- "type": "string"
- },
- "styling": {
- "type": "object",
- "properties": {
- "halfSize": {
- "description": "TODO: Describe",
- "type": "boolean"
- },
- "shadow": {
- "description": "TODO: Describe",
- "type": "boolean"
- },
- "boxAlign": {
- "description": "TODO: Describe",
- "type": "string"
- },
- "boxMargin": {
- "description": "TODO: Describe",
- "type": "boolean"
- },
- "separator": {
- "description": "TODO: Describe",
- "type": "boolean"
- },
- "reversed": {
- "description": "TODO: Describe",
- "type": "boolean"
- }
- }
- },
- "text": {
- "description": "Text for the slide",
- "type": "string"
- },
- "title": {
- "description": "Title for the slide",
- "type": "string"
- }
- }
-}
diff --git a/assets/shared/template/index.html b/assets/shared/template/index.html
deleted file mode 100644
index 8ab21a7aa..000000000
--- a/assets/shared/template/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
OS2Display templates
-
-
-
-
-
-
-
-
diff --git a/assets/shared/template/instagram-feed/instagram-feed-admin.json b/assets/shared/template/instagram-feed/instagram-feed-admin.json
deleted file mode 100644
index 9e19671d3..000000000
--- a/assets/shared/template/instagram-feed/instagram-feed-admin.json
+++ /dev/null
@@ -1,92 +0,0 @@
-[
- {
- "key": "instagram-feed-form-1",
- "input": "header",
- "text": "Skabelon: Instagram feed",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "instagram-feed-form-2",
- "input": "header-h3",
- "text": "Opsæt feed",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "instagram-feed-form-3",
- "input": "feed",
- "text": "Opsæt feed",
- "name": "feeds",
- "supportedFeedOutputType": "instagram",
- "formGroupClasses": "mb-3",
- "singleSelect": true
- },
- {
- "key": "instagram-feed-form-entry-duration",
- "input": "input",
- "name": "entryDuration",
- "type": "number",
- "label": "Varighed pr. billede/video (i sekunder)",
- "helpText": "Her skal du skrive varigheden pr. billede/video.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "instagram-feed-hashtag-text",
- "input": "input",
- "name": "hashtagText",
- "type": "text",
- "label": "Hashtag-tekst",
- "helpText": "Her kan du skrive hashtag-teksten.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "instagram-feed-orientation",
- "input": "select",
- "name": "orientation",
- "label": "Vælg skærmorientering",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "orientation1",
- "title": "Horisontalt",
- "value": "landscape"
- },
- {
- "key": "orientation2",
- "title": "Vertikal",
- "value": "vertical"
- }
- ]
- },
- {
- "key": "instagram-feed-image-width",
- "input": "input",
- "name": "imageWidth",
- "type": "number",
- "min": 0,
- "max": 100.0,
- "label": "Billedebredde %",
- "helpText": "Her kan du skrive billedebredden i procent.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "instagram-feed-max-entries",
- "input": "input",
- "name": "maxEntries",
- "type": "number",
- "min": 1,
- "label": "Maksimalt antal billeder/videoer fra feedet",
- "helpText": "Her kan du skrive maksimalt antal billeder/videoer fra feedet.",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- }
-]
diff --git a/assets/shared/template/instagram-feed/instagram-feed-config.json b/assets/shared/template/instagram-feed/instagram-feed-config.json
deleted file mode 100644
index 5c735cea3..000000000
--- a/assets/shared/template/instagram-feed/instagram-feed-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Instagram feed",
- "icon": "",
- "id": "01FTZC0RKJYHG4JVZG5K709G46",
- "description": "Mulighed for at vise instagram indhold.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/instagram-feed.js",
- "admin": "https://display-templates.local.itkdev.dk/build/instagram-feed-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/instagram-feed-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/news-feed/news-feed-admin.json b/assets/shared/template/news-feed/news-feed-admin.json
deleted file mode 100644
index dc76e419d..000000000
--- a/assets/shared/template/news-feed/news-feed-admin.json
+++ /dev/null
@@ -1,59 +0,0 @@
-[
- {
- "key": "news-feed-form-1",
- "input": "header",
- "text": "Skabelon: Nyheder",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "news-feed-form-2",
- "input": "header-h3",
- "text": "Opsæt feed",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "news-feed-form-3",
- "input": "feed",
- "text": "Opsæt feed",
- "name": "feeds",
- "supportedFeedOutputType": "rss",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "news-feed-form-entry-duration",
- "input": "input",
- "name": "entryDuration",
- "type": "number",
- "label": "Varighed pr. nyhed (i sekunder)",
- "helpText": "Her skal du skrive varigheden pr. nyhed.",
- "formGroupClasses": "mb-3",
- "required": true
- },
- {
- "key": "news-feed-form-readmore",
- "input": "input",
- "label": "Læs mere tekst",
- "helpText": "Hvis den ikke overskrives, står der \"Læs hele nyheden\"",
- "name": "readMore",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "rss-form-4",
- "multipleImages": false,
- "input": "image",
- "name": "fallbackImage",
- "label": "Fallback-billede",
- "helpText": "Dette billede kan vises, hvis nyheden mangler et billede.",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring",
- "formGroupClasses": "mb-3"
- }
-]
diff --git a/assets/shared/template/news-feed/news-feed-config.json b/assets/shared/template/news-feed/news-feed-config.json
deleted file mode 100644
index 6648c273f..000000000
--- a/assets/shared/template/news-feed/news-feed-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Nyheder",
- "icon": "",
- "id": "01JEWPAFF93YSF418TH72W1SBA",
- "description": "Mulighed for at vise nyhedsindhold.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/news-feed.js",
- "admin": "https://display-templates.local.itkdev.dk/build/news-feed-admin.json",
- "schema": "",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/poster/poster-admin.json b/assets/shared/template/poster/poster-admin.json
deleted file mode 100644
index fd3ff8138..000000000
--- a/assets/shared/template/poster/poster-admin.json
+++ /dev/null
@@ -1,49 +0,0 @@
-[
- {
- "key": "poster-form-1",
- "input": "header",
- "text": "Skabelon: Plakat fra feed",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "poster-form-2",
- "input": "header-h3",
- "text": "Opsæt feed",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "poster-form-3",
- "input": "feed",
- "text": "Opsæt feed",
- "name": "feeds",
- "supportedFeedOutputType": "poster",
- "formGroupClasses": "mb-3",
- "singleSelect": true
- },
- {
- "key": "poster-form-entry-duration",
- "input": "duration",
- "name": "duration",
- "type": "number",
- "label": "Varighed pr. begivenhed (i sekunder)",
- "helpText": "Her skal du skrive varigheden pr. begivenhed.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "poster-form-4",
- "input": "checkbox",
- "label": "Vis logo fra tema",
- "name": "showLogo",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- }
-]
diff --git a/assets/shared/template/poster/poster-config.json b/assets/shared/template/poster/poster-config.json
deleted file mode 100644
index 5d2480081..000000000
--- a/assets/shared/template/poster/poster-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Plakat",
- "icon": "",
- "id": "01FWJZQ25A1868V63CWYYHQFKQ",
- "description": "Mulighed for at vise plakat indhold.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/poster.js",
- "admin": "https://display-templates.local.itkdev.dk/build/poster-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/poster-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/rss/rss-admin.json b/assets/shared/template/rss/rss-admin.json
deleted file mode 100644
index fc7485ef9..000000000
--- a/assets/shared/template/rss/rss-admin.json
+++ /dev/null
@@ -1,74 +0,0 @@
-[
- {
- "key": "rss-form-1",
- "input": "header",
- "text": "Skabelon: RSS",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "rss-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "rss-form-3",
- "input": "feed",
- "text": "Opsæt feed",
- "name": "feed",
- "supportedFeedOutputType": "rss",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "rss-form-4",
- "multipleImages": false,
- "input": "image",
- "name": "image",
- "label": "Baggrundsbillede",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- },
- {
- "key": "rss-form-5",
- "input": "select",
- "required": false,
- "label": "Tekststørrelse",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "fontsize1",
- "title": "xs",
- "value": "font-size-xs"
- },
- {
- "key": "fontsize2",
- "title": "s",
- "value": "font-size-s"
- },
- {
- "key": "fontsize3",
- "title": "m",
- "value": "font-size-m"
- },
- {
- "key": "fontsize4",
- "title": "l",
- "value": "font-size-lg"
- },
- {
- "key": "fontsize5",
- "title": "xl",
- "value": "font-size-xl"
- }
- ],
- "name": "fontSize"
- }
-]
diff --git a/assets/shared/template/rss/rss-config.json b/assets/shared/template/rss/rss-config.json
deleted file mode 100644
index 2fe491c36..000000000
--- a/assets/shared/template/rss/rss-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "RSS",
- "icon": "",
- "id": "01FQC300GGWCA7A8H0SXY6P9FG",
- "description": "Mulighed for at vise et rss feed.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/rss.js",
- "admin": "https://display-templates.local.itkdev.dk/build/rss-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/rss-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/screens.js b/assets/shared/template/screens.js
deleted file mode 100644
index b34a71d23..000000000
--- a/assets/shared/template/screens.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import twoBoxes from "./screen-layouts/two-boxes.json";
-import threeBoxes from "./screen-layouts/three-boxes.json";
-import threeBoxesHorizontal from "./screen-layouts/three-boxes-horizontal.json";
-import twoBoxesVertical from "./screen-layouts/two-boxes-vertical.json";
-import touchTemplate from "./screen-layouts/touch-template.json";
-import sixAreas from "./screen-layouts/six-areas.json";
-import fullScreen from "./screen-layouts/full-screen.json";
-import fourAreas from "./screen-layouts/four-areas.json";
-import twoBoxesVerticalReversed from "./screen-layouts/two-boxes-vertical-reversed.json";
-
-const screens = [
- {
- id: "two-split",
- screenLayout: twoBoxes,
- },
- {
- id: "two-split-vertical",
- screenLayout: twoBoxesVertical,
- },
- {
- id: "two-split-vertical-reversed",
- screenLayout: twoBoxesVerticalReversed,
- },
- {
- id: "touch-template",
- screenLayout: touchTemplate,
- },
- {
- id: "three-split",
- screenLayout: threeBoxes,
- },
- {
- id: "three-split-horizontal",
- screenLayout: threeBoxesHorizontal,
- },
- {
- id: "six-areas",
- screenLayout: sixAreas,
- },
- {
- id: "full-screen",
- screenLayout: fullScreen,
- },
- {
- id: "four-areas",
- screenLayout: fourAreas,
- },
-];
-
-export default screens;
diff --git a/assets/shared/template/slide.jsx b/assets/shared/template/slide.jsx
deleted file mode 100644
index cc8898731..000000000
--- a/assets/shared/template/slide.jsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import React from "react";
-import BookReview from "./book-review/book-review.jsx";
-import Calendar from "./calendar/calendar.jsx";
-import Contacts from "./contacts/contacts.jsx";
-import ImageText from "./image-text/image-text.jsx";
-import IFrame from "./iframe/iframe.jsx";
-import Poster from "./poster/poster.jsx";
-import RSS from "./rss/rss.jsx";
-import Slideshow from "./slideshow/slideshow.jsx";
-import InstagramFeed from "./instagram-feed/instagram-feed.jsx";
-import NewsFeed from "./news-feed/news-feed.jsx";
-import Table from "./table/table.jsx";
-import Video from "./video/video.jsx";
-import Travel from "./travel/travel.jsx";
-import VimeoPlayer from "./vimeo-player/vimeo-player.jsx";
-
-const renderSlide = (slide, run, slideDone) => {
- switch (slide?.templateData?.id) {
- case "01FP2SME0ENTXWF362XHM6Z1B4":
- return (
-
- );
- case "01FRJPF4XATRN8PBZ35XN84PS6":
- return (
-
- );
- case "01FPZ19YEHX7MQ5Q6ZS0WK0VEA":
- return (
-
- );
- case "01FQBJQ2M3544ZKAADPWBXHY71":
- return (
-
- );
- case "01FP2SNGFN0BZQH03KCBXHKYHG":
- return (
-
- );
- case "01FWJZQ25A1868V63CWYYHQFKQ":
- return (
-
- );
- case "01FQC300GGWCA7A8H0SXY6P9FG":
- return (
-
- );
- case "01FP2SNSC9VXD10ZKXQR819NS9":
- return (
-
- );
- case "01FTZC0RKJYHG4JVZG5K709G46":
- return (
-
- );
- case "01JEWPAFF93YSF418TH72W1SBA":
- return (
-
- );
- case "01FQBJFKM0YFX1VW5K94VBSNCP":
- return (
-
- );
- case "01FQBJFKM0YFX1VW5K94VBSNCC":
- return (
-
- );
- case "01FZD7K807VAKZ99BGSSCHRJM6":
- return (
-
- );
- case "01FQBJQ2M3544ZKAADPWBXHY17":
- return (
-
- );
- default:
- return (
Slide type not found!
);
- }
-};
-
-export default renderSlide;
diff --git a/assets/shared/template/slideshow/slideshow-admin.json b/assets/shared/template/slideshow/slideshow-admin.json
deleted file mode 100644
index 86de406f5..000000000
--- a/assets/shared/template/slideshow/slideshow-admin.json
+++ /dev/null
@@ -1,174 +0,0 @@
-[
- {
- "key": "slideshow-form-1",
- "input": "header",
- "text": "Skabelon: Slideshow",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "slideshow-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "slideshow-form-3",
- "multipleImages": true,
- "input": "image",
- "name": "images",
- "label": "Billeder"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- },
- {
- "key": "slideshow-form-4",
- "input": "header-h3",
- "text": "Opsætning",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "slideshow-form-5",
- "input": "input",
- "name": "imageDuration",
- "type": "number",
- "label": "Varighed pr. billede (i sekunder)",
- "helpText": "Her skal du skrive varigheden pr. billede.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "name": "animation",
- "key": "slideshow-form-6",
- "input": "select",
- "label": "Vælg animation",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "animation0",
- "title": "Ingen animation",
- "value": null
- },
- {
- "key": "animation1",
- "title": "Tilfældig",
- "value": "random"
- },
- {
- "key": "animation2",
- "title": "Zoom mod midten",
- "value": "zoom-in-middle"
- },
- {
- "key": "animation3",
- "title": "Zoom ud fra midten",
- "value": "zoom-out-middle"
- },
- {
- "key": "animation4",
- "title": "Zoom mod tilfældigt punkt",
- "value": "zoom-in-random"
- },
- {
- "key": "animation5",
- "title": "Zoom ud fra tilfældigt punkt",
- "value": "zoom-out-random"
- }
- ]
- },
- {
- "name": "transition",
- "key": "slideshow-form-7",
- "input": "select",
- "label": "Vælg overgang",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "transition1",
- "title": "Ingen overgang",
- "value": null
- },
- {
- "key": "transition2",
- "title": "Cross fade",
- "value": "fade"
- }
- ]
- },
- {
- "key": "slideshow-form-8",
- "input": "checkbox",
- "label": "Vis logo fra tema",
- "name": "showLogo",
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "slideshow-form-9",
- "input": "select",
- "required": false,
- "label": "Logostørrelse",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "logosize1",
- "title": "s",
- "value": "logo-size-s"
- },
- {
- "key": "logosize2",
- "title": "m",
- "value": "logo-size-m"
- },
- {
- "key": "logosize3",
- "title": "l",
- "value": "logo-size-l"
- }
- ],
- "name": "logoSize"
- },
- {
- "key": "slideshow-form-10",
- "input": "select",
- "required": false,
- "label": "Logoposition",
- "formGroupClasses": "col-md-6 mb-3",
- "options": [
- {
- "key": "logoposition1",
- "title": "Top/venstre",
- "value": "logo-position-top-left"
- },
- {
- "key": "logoposition2",
- "title": "Top/højre",
- "value": "logo-position-top-right"
- },
- {
- "key": "logoposition3",
- "title": "Bund/venstre",
- "value": "logo-position-bottom-left"
- },
- {
- "key": "logoposition4",
- "title": "Bund/højre",
- "value": "logo-position-bottom-right"
- }
- ],
- "name": "logoPosition"
- },
- {
- "key": "slideshow-form-11",
- "input": "checkbox",
- "label": "Margin om logo",
- "name": "logoMargin",
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/slideshow/slideshow-config.json b/assets/shared/template/slideshow/slideshow-config.json
deleted file mode 100644
index 4b4abae09..000000000
--- a/assets/shared/template/slideshow/slideshow-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Slideshow",
- "icon": "",
- "id": "01FP2SNSC9VXD10ZKXQR819NS9",
- "description": "Skabelon til slideshow af billeder med forskellige effekter",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/slideshow.js",
- "admin": "https://display-templates.local.itkdev.dk/build/slideshow-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/slideshow-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/slideshow/slideshow-content-example.json b/assets/shared/template/slideshow/slideshow-content-example.json
deleted file mode 100644
index 726df6d2a..000000000
--- a/assets/shared/template/slideshow/slideshow-content-example.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "images": [
- "/v1/media/00000000000000000000000001",
- "/v1/media/00000000000000000000000002",
- "/v1/media/00000000000000000000000003",
- "/v1/media/00000000000000000000000004"
- ],
- "logoImage": "/v1/media/00000000000000000000000001",
- "logoPosition": "bottom right",
- "logoSize": "xl",
- "transitions": "fade",
- "animations": "random",
- "imageDuration": 5000
-}
diff --git a/assets/shared/template/slideshow/slideshow-schema.json b/assets/shared/template/slideshow/slideshow-schema.json
deleted file mode 100644
index 5638bfccf..000000000
--- a/assets/shared/template/slideshow/slideshow-schema.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://github.com/os2display/display-templates/blob/develop/build/slideshow/slideshow-schema.json",
- "title": "slideshow schema",
- "description": "Content schema for slideshow",
- "type": "object",
- "properties": {
- "images": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "animations": {
- "type": "string"
- },
- "logoPosition": {
- "type": "string"
- },
- "logoSize": {
- "type": "string"
- },
- "logoEnabled": {
- "type": "boolean"
- },
- "transitions": {
- "type": "string"
- },
- "imageDuration": {
- "type": "number"
- }
- }
-}
diff --git a/assets/shared/template/table/table-admin.json b/assets/shared/template/table/table-admin.json
deleted file mode 100644
index fdf86fedf..000000000
--- a/assets/shared/template/table/table-admin.json
+++ /dev/null
@@ -1,124 +0,0 @@
-[
- {
- "key": "table-form-1",
- "input": "header",
- "text": "Skabelon: Tabel",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "table-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "table-form-3",
- "input": "input",
- "name": "title",
- "label": "Overskrift på tabelslide",
- "helpText": "Her kan du skrive overskriften til tabelslide"
- },
- {
- "key": "table-form-4",
- "input": "input",
- "name": "text",
- "label": "Et tekstfelt med ekstra tekst",
- "helpText": "Kan placeres ovenover eller nedenunder tabellen"
- },
- {
- "key": "table-form-5",
- "input": "select",
- "required": true,
- "label": "Hvor skal teksten være placeret",
- "formGroupClasses": " mb-3",
- "options": [
- {
- "key": "placement1",
- "title": "Over tabellen",
- "value": "top"
- },
- {
- "key": "placement2",
- "title": "Under tabellen",
- "value": "bottom"
- }
- ],
- "name": "fontPlacement"
- },
- {
- "key": "table-form-6",
- "input": "select",
- "required": true,
- "label": "Tekststørrelse",
- "formGroupClasses": " mb-3",
- "options": [
- {
- "key": "fontsize1",
- "title": "xs",
- "value": "font-size-xs"
- },
- {
- "key": "fontsize2",
- "title": "s",
- "value": "font-size-s"
- },
- {
- "key": "fontsize3",
- "title": "m",
- "value": "font-size-m"
- },
- {
- "key": "fontsize4",
- "title": "l",
- "value": "font-size-lg"
- },
- {
- "key": "fontsize5",
- "title": "xl",
- "value": "font-size-xl"
- }
- ],
- "name": "fontSize"
- },
- {
- "key": "table-form-7",
- "multipleImages": false,
- "input": "image",
- "name": "image",
- "label": "Baggrundsbillede"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- },
- {
- "key": "table-form-8",
- "input": "header-h3",
- "text": "Tabellen",
- "name": "header3",
- "formGroupClasses": "h5 mt-3 mb-3"
- },
- {
- "key": "table-form-9",
- "input": "table",
- "name": "table",
- "label": "Tabellen",
- "helpText": "Her kan du angive tabeldata"
- },
- {
- "key": "table-form-10",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- }
-]
diff --git a/assets/shared/template/table/table-config.json b/assets/shared/template/table/table-config.json
deleted file mode 100644
index f3a57f555..000000000
--- a/assets/shared/template/table/table-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Tabel",
- "icon": "",
- "id": "01FQBJFKM0YFX1VW5K94VBSNCP",
- "description": "Skabelon til tabel.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/table.js",
- "admin": "https://display-templates.local.itkdev.dk/build/table-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/table-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/table/table-content-example.json b/assets/shared/template/table/table-content-example.json
deleted file mode 100644
index 74e8d2f95..000000000
--- a/assets/shared/template/table/table-content-example.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "backgroundImage": [
- "/v1/media/00000000000000000000000001"
- ],
- "title": "Overskrift",
- "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
- "styling": {
- "fontPlacement": "bottom",
- "fontSize": "s"
- },
- "table": [
- {
- "type": "header",
- "columns": [
- {
- "title": "Column 1",
- "field": "column1"
- },
- {
- "title": "Column 2",
- "field": "column2"
- }
- ]
- },
- {
- "column1": "Data 1",
- "column2": "Data 2"
- },
- {
- "column1": "Data 3",
- "column2": "Data 4"
- }
- ]
-}
diff --git a/assets/shared/template/table/table-schema.json b/assets/shared/template/table/table-schema.json
deleted file mode 100644
index b45a3d533..000000000
--- a/assets/shared/template/table/table-schema.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "title": "table-content-example",
- "description": "Auto generated JSON schema based on the 'table-content-example' file",
- "type": "object",
- "properties": {
- "styling": {
- "type": "object",
- "properties": {
- "fontPlacement": {
- "type": "string"
- },
- "fontSize": {
- "type": "string"
- }
- }
- },
- "backgroundImage": {
- "type": "array",
- "items": {}
- },
- "text": {
- "type": "string"
- },
- "title": {
- "type": "string"
- },
- "table": {
- "type": "array",
- "items": {
- "properties": {
- "columns": {
- "type": "array",
- "items": {
- "properties": {
- "field": {
- "type": "string"
- },
- "title": {
- "type": "string"
- }
- }
- }
- },
- "type": {
- "type": "string"
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/assets/shared/template/travel/travel-admin.json b/assets/shared/template/travel/travel-admin.json
deleted file mode 100644
index c81652ab1..000000000
--- a/assets/shared/template/travel/travel-admin.json
+++ /dev/null
@@ -1,138 +0,0 @@
-[
- {
- "key": "station-template-name",
- "input": "header",
- "text": "Skabelon: Rejseplanen",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "station-template-content",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "station-template-headline",
- "input": "input",
- "name": "title",
- "label": "Overskrift på slide",
- "helpText": "Her kan du skrive overskriften til slidet",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-text",
- "input": "rich-text-input",
- "name": "text",
- "label": "Tekst på slide",
- "helpText": "Her kan du skrive teksten til slidet",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-distance",
- "input": "input",
- "name": "distance",
- "label": "Afstand",
- "helpText": "Her kan du skrive teksten til afstandsfeltet",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-time-moderate",
- "input": "input",
- "name": "timeModerate",
- "label": "Tid (Moderat)",
- "helpText": "Her kan du skrive teksten til tidsfeltet (moderat)",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-time-fast",
- "input": "input",
- "name": "timeFast",
- "label": "Tid (Hurtigt)",
- "helpText": "Her kan du skrive teksten til tidsfeltet (hurtigt)",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-image",
- "multipleImages": false,
- "input": "image",
- "name": "image",
- "label": "Billede",
- "helpText": "Her uploades dit billede med indtegnet rute",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-number-of-journeys",
- "input": "input",
- "name": "numberOfJourneys",
- "label": "Antal afgange der skal vises",
- "helpText": "Hvis du for eksempel skriver 3, vises de næste 3 afgange fra stoppestedet",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-iframe-title",
- "input": "input",
- "name": "iframeTitle",
- "label": "Tekst i rejseplan",
- "helpText": "Her kan du skrive hvorfra bussen kører",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-stations",
- "input": "travel-plan",
- "label": "Vælg stoppested",
- "helpText": "Hvis du er i tvivl om hvilke stoppesteder, henviser vi til din lokale busservice",
- "formGroupClasses": "mb-3",
- "name": "station"
- },
- {
- "key": "station-template-bus-or-tram",
- "input": "radio",
- "label": "Er det valgte stoppested bus eller letbane",
- "options": [
- {
- "id": "bus",
- "label": "Bus"
- },
- {
- "id": "tram",
- "label": "Letbane"
- }
- ],
- "name": "busOrTram",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-darkmode",
- "input": "radio",
- "label": "Darkmode",
- "options": [
- {
- "id": "",
- "label": "Deaktiver"
- },
- {
- "id": "auto",
- "label": "Auto"
- },
- {
- "id": "night",
- "label": "Night"
- }
- ],
- "name": "monitorLayout",
- "formGroupClasses": "mb-3"
- },
- {
- "key": "station-template-duration",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "mb-3"
- }
-]
diff --git a/assets/shared/template/travel/travel-config.json b/assets/shared/template/travel/travel-config.json
deleted file mode 100644
index e65901f79..000000000
--- a/assets/shared/template/travel/travel-config.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "title": "Rejseplanen",
- "icon": "",
- "id": "01FZD7K807VAKZ99BGSSCHRJM6",
- "description": "Skabelon til rejseplanen.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/travel.js",
- "admin": "https://display-templates.local.itkdev.dk/build/travel-admin.json",
- "schema": "",
- "assets": [],
- "options": {
- "disableLivePreview": true
- },
- "content": {}
- }
-}
diff --git a/assets/shared/template/video/video-admin.json b/assets/shared/template/video/video-admin.json
deleted file mode 100644
index 774e8058a..000000000
--- a/assets/shared/template/video/video-admin.json
+++ /dev/null
@@ -1,40 +0,0 @@
-[
- {
- "key": "video-form-1",
- "input": "header",
- "text": "Skabelon: Video",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "video-form-2",
- "input": "header-h3",
- "text": "Indhold",
- "name": "header2",
- "formGroupClasses": "h5 mb-3"
- },
- {
- "key": "video-form-video",
- "input": "file",
- "text": "Video",
- "name": "video",
- "acceptedMimetypes": ["video/webm", "video/mp4"],
- "formGroupClasses": "h5 mb-3",
- "helpText": "Følgende formatter accepteres: .webm, mp4"
- },
- {
- "key": "video-form-sound",
- "input": "checkbox",
- "label": "Afspil lyd",
- "name": "sound",
- "formGroupClasses": "col-md-9 mt-3",
- "helpText": "Hvis videoen har lyd, skal enheden konfigureres til at automatisk afspilning er muligt."
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- }
-]
diff --git a/assets/shared/template/video/video-config.json b/assets/shared/template/video/video-config.json
deleted file mode 100644
index 98dd2ce45..000000000
--- a/assets/shared/template/video/video-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Video",
- "icon": "",
- "id": "01FQBJFKM0YFX1VW5K94VBSNCC",
- "description": "Skabelon til video.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/video.js",
- "admin": "https://display-templates.local.itkdev.dk/build/video-admin.json",
- "schema": "https://display-templates.local.itkdev.dk/build/video-schema.json",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/template/vimeo-player/vimeo-player-admin.json b/assets/shared/template/vimeo-player/vimeo-player-admin.json
deleted file mode 100644
index 090548788..000000000
--- a/assets/shared/template/vimeo-player/vimeo-player-admin.json
+++ /dev/null
@@ -1,34 +0,0 @@
-[
- {
- "key": "vimeo-form-1",
- "input": "header",
- "text": "Skabelon: Vimeo Video",
- "name": "header1",
- "formGroupClasses": "h4 mb-3"
- },
- {
- "key": "vimeo-form-2",
- "input": "input",
- "name": "vimeoid",
- "label": "Vimeo Id",
- "formGroupClasses": "col-md-6"
- },
- {
- "key": "vimeo-form-3",
- "input": "duration",
- "name": "duration",
- "min": "1",
- "type": "number",
- "label": "Varighed (i sekunder)",
- "helpText": "Her skal du skrive varigheden på slidet.",
- "required": true,
- "formGroupClasses": "col-md-6 mb-3"
- },
- {
- "key": "form-media-contain",
- "input": "checkbox",
- "name": "mediaContain",
- "label": "Deaktivér billedbeskæring",
- "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
- }
-]
diff --git a/assets/shared/template/vimeo-player/vimeo-player-config.json b/assets/shared/template/vimeo-player/vimeo-player-config.json
deleted file mode 100644
index 9785ac86f..000000000
--- a/assets/shared/template/vimeo-player/vimeo-player-config.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "title": "Vimeo Player",
- "icon": "",
- "id": "01FQBJQ2M3544ZKAADPWBXHY17",
- "description": "Skabelon til Vimeo Video.",
- "resources": {
- "component": "https://display-templates.local.itkdev.dk/build/vimeo-player.js",
- "admin": "https://display-templates.local.itkdev.dk/build/vimeo-player-admin.json",
- "schema": "",
- "assets": [],
- "options": {},
- "content": {}
- }
-}
diff --git a/assets/shared/templates/book-review.json b/assets/shared/templates/book-review.json
new file mode 100644
index 000000000..c0d29a5b4
--- /dev/null
+++ b/assets/shared/templates/book-review.json
@@ -0,0 +1,78 @@
+{
+ "title": "Anmeldelse",
+ "id": "01FP2SME0ENTXWF362XHM6Z1B4",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "book-review-form-1",
+ "input": "header",
+ "text": "Skabelon: Anmeldelse",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "book-review-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "book-review-form-3",
+ "input": "rich-text-input",
+ "name": "bookText",
+ "label": "Teksten til anmeldelsen",
+ "helpText": "Her kan du skrive teksten til anmeldelsen"
+ },
+ {
+ "key": "book-review-form-4",
+ "multipleImages": false,
+ "input": "image",
+ "name": "bookImage",
+ "label": "Billede",
+ "helpText": "Billede af det som bliver anmeldt"
+ },
+ {
+ "key": "book-review-form-5",
+ "input": "header-h3",
+ "text": "Forfatter",
+ "name": "header3",
+ "formGroupClasses": "h5 mt-3 mb-3"
+ },
+ {
+ "key": "book-review-form-6",
+ "input": "input",
+ "name": "authorText",
+ "type": "text",
+ "label": "Forfattertekst",
+ "helpText": "Her kan du skrive tekst til forfatteren",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "book-review-form-7",
+ "multipleImages": false,
+ "input": "image",
+ "name": "authorImage",
+ "label": "Billede",
+ "helpText": "Billede til forfatteren"
+ },
+ {
+ "key": "book-review-form-8",
+ "input": "header-h3",
+ "text": "Opsætning",
+ "name": "header3",
+ "formGroupClasses": "h5 mt-3 mb-3"
+ },
+ {
+ "key": "book-review-form-9",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/book-review/book-review.jsx b/assets/shared/templates/book-review.jsx
similarity index 72%
rename from assets/shared/template/book-review/book-review.jsx
rename to assets/shared/templates/book-review.jsx
index ceabe6497..5ce9f0e6f 100644
--- a/assets/shared/template/book-review/book-review.jsx
+++ b/assets/shared/templates/book-review.jsx
@@ -1,15 +1,31 @@
import React, { useEffect } from "react";
-import PropTypes from "prop-types";
import parse from "html-react-parser";
import DOMPurify from "dompurify";
-import BaseSlideExecution from "../base-slide-execution";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./book-review.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./book-review/book-review.scss";
+import templateConfig from './book-review.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
- * Book review component.
- *
* @param {object} props Props.
* @param {object} props.slide The slide.
* @param {object} props.content The slide content.
@@ -78,26 +94,5 @@ function BookReview({ slide, content, run, slideDone, executionId }) {
>
);
}
-BookReview.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number,
- authorText: PropTypes.string,
- bookText: PropTypes.string,
- authorImage: PropTypes.arrayOf(PropTypes.string),
- bookImage: PropTypes.arrayOf(PropTypes.string),
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-export default BookReview;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/book-review/book-review.scss b/assets/shared/templates/book-review/book-review.scss
similarity index 100%
rename from assets/shared/template/book-review/book-review.scss
rename to assets/shared/templates/book-review/book-review.scss
diff --git a/assets/shared/templates/calendar.json b/assets/shared/templates/calendar.json
new file mode 100644
index 000000000..4e3878f9e
--- /dev/null
+++ b/assets/shared/templates/calendar.json
@@ -0,0 +1,219 @@
+{
+ "title": "Kalender",
+ "id": "01FRJPF4XATRN8PBZ35XN84PS6",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "calendar-form-1",
+ "input": "header",
+ "text": "Skabelon: Kalender",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "calendar-form-2",
+ "input": "header-h3",
+ "text": "Opsæt feed",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "calendar-form-3",
+ "input": "feed",
+ "text": "Opsæt feed",
+ "name": "feed",
+ "supportedFeedOutputType": "calendar",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "calendar-form-4",
+ "input": "header-h3",
+ "text": "Konfigurér slide",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "calendar-form-duration",
+ "input": "duration",
+ "name": "duration",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "calendar-form-layout",
+ "input": "select",
+ "name": "layout",
+ "label": "Vælg layout",
+ "required": true,
+ "helpText": "Hvis du vælge \"Enkelt lokale med booking\" virker bookingdelen IKKE i preview.",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "layout1",
+ "title": "Enkelt lokale",
+ "value": "single"
+ },
+ {
+ "key": "layout2",
+ "title": "Flere resurser",
+ "value": "multiple"
+ },
+ {
+ "key": "layout3",
+ "title": "Flere resurser, flere dage",
+ "value": "multipleDays"
+ },
+ {
+ "key": "layout4",
+ "title": "Enkelt lokale med booking",
+ "value": "singleBooking"
+ }
+ ]
+ },
+ {
+ "key": "calendar-form-form-14",
+ "input": "select",
+ "required": true,
+ "label": "Tekststørrelse",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "fontsize1",
+ "title": "xs",
+ "value": "font-size-xs"
+ },
+ {
+ "key": "fontsize2",
+ "title": "s",
+ "value": "font-size-s"
+ },
+ {
+ "key": "fontsize3",
+ "title": "m",
+ "value": "font-size-m"
+ },
+ {
+ "key": "fontsize4",
+ "title": "l",
+ "value": "font-size-lg"
+ },
+ {
+ "key": "fontsize5",
+ "title": "xl",
+ "value": "font-size-xl"
+ }
+ ],
+ "name": "fontSize"
+ },
+ {
+ "key": "calendar-form-title",
+ "input": "input",
+ "name": "title",
+ "type": "text",
+ "label": "Overskrift",
+ "helpText": "Her kan du skrive overskrift.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "calendar-form-subtitle",
+ "input": "input",
+ "name": "subTitle",
+ "type": "text",
+ "label": "Underoverskrift",
+ "helpText": "Her kan du skrive underoverskrift.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "calendar-form-resource-available-text",
+ "input": "input",
+ "name": "resourceAvailableText",
+ "type": "text",
+ "label": "Tekst når resursen er ledig",
+ "helpText": "Her kan du skrive tekst, som vises når resursen er ledig.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "calendar-form-resource-unavailable-text",
+ "input": "input",
+ "name": "resourceUnavailableText",
+ "type": "text",
+ "label": "Begivenhedstitel fallback",
+ "helpText": "Her kan du skrive tekst på begivenheder, hvor der ikke er sat en titel.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "calendar-form-footer-text",
+ "input": "input",
+ "name": "footerText",
+ "type": "text",
+ "label": "Footer tekst",
+ "helpText": "Her kan du skrive tekst til footeren.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "calendar-form-hide-grid",
+ "input": "checkbox",
+ "label": "Skjul grid",
+ "name": "hideGrid",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "calendar-form-has-date-and-time",
+ "input": "checkbox",
+ "label": "Vis dato og tidspunkt",
+ "name": "hasDateAndTime",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "calendar-form-date-as-box",
+ "input": "checkbox",
+ "label": "Dato og tidspunkt som boks",
+ "name": "dateAsBox",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "calendar-form-image",
+ "multipleImages": false,
+ "input": "image",
+ "name": "image",
+ "label": "Baggrundsbillede"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ },
+ {
+ "key": "calendar-form-display-headers",
+ "input": "checkbox",
+ "label": "Vis tabeloverskrifter",
+ "name": "displayHeaders",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "calendar-form-header-orders",
+ "input": "select",
+ "label": "Hvilken rækkefølge skal overskrifterne i tabellen være i",
+ "options": [
+ {
+ "key": "whenwhatwhere",
+ "value": "whenwhatwhere",
+ "title": "Hvornår, hvad, hvor"
+ },
+ {
+ "key": "whatwherewhen",
+ "value": "whatwherewhen",
+ "title": "Hvad, hvor, hvornår"
+ }
+ ],
+ "name": "headerOrder",
+ "formGroupClasses": "col-md-6 mb-3",
+ "helpText": "Dette er kun relevant hvis \"Flere resurser\" er valgt under \"layout\". Standard er \"Hvornår, hvad, hvor.\""
+ }
+ ]
+}
diff --git a/assets/shared/template/calendar/calendar.jsx b/assets/shared/templates/calendar.jsx
similarity index 69%
rename from assets/shared/template/calendar/calendar.jsx
rename to assets/shared/templates/calendar.jsx
index d56ac436a..6ffee5fd9 100644
--- a/assets/shared/template/calendar/calendar.jsx
+++ b/assets/shared/templates/calendar.jsx
@@ -1,17 +1,35 @@
import React, { useEffect, Fragment, useState } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { FormattedMessage, IntlProvider } from "react-intl";
-import BaseSlideExecution from "../base-slide-execution";
-import da from "./lang/da.json";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import CalendarSingle from "./calendar-single.jsx";
-import CalendarSingleBooking from "./calendar-single-booking.jsx";
-import CalendarMultipleDays from "./calendar-multiple-days.jsx";
-import CalendarMultiple from "./calendar-multiple.jsx";
-import GlobalStyles from "../GlobalStyles";
-import "./calendar.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import da from "./calendar/lang/da.json";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import CalendarSingle from "./calendar/calendar-single.jsx";
+import CalendarSingleBooking from "./calendar/calendar-single-booking.jsx";
+import CalendarMultipleDays from "./calendar/calendar-multiple-days.jsx";
+import CalendarMultiple from "./calendar/calendar-multiple.jsx";
+import GlobalStyles from "../slide-utils/GlobalStyles.js";
+import "./calendar/calendar.scss";
+import templateConfig from './calendar.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Calendar component.
@@ -124,36 +142,4 @@ function Calendar({ slide, content, run, slideDone, executionId }) {
);
}
-Calendar.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- feedData: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- startTime: PropTypes.number.isRequired,
- endTime: PropTypes.number,
- resourceTitle: PropTypes.string,
- resourceId: PropTypes.string,
- })
- ),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number.isRequired,
- layout: PropTypes.string,
- image: PropTypes.arrayOf(PropTypes.string),
- fontSize: PropTypes.string,
- resourceUnavailableText: PropTypes.string,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Calendar;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/calendar/calendar-multiple-days.jsx b/assets/shared/templates/calendar/calendar-multiple-days.jsx
similarity index 90%
rename from assets/shared/template/calendar/calendar-multiple-days.jsx
rename to assets/shared/templates/calendar/calendar-multiple-days.jsx
index 3eaf96244..ba9c7cff9 100644
--- a/assets/shared/template/calendar/calendar-multiple-days.jsx
+++ b/assets/shared/templates/calendar/calendar-multiple-days.jsx
@@ -1,10 +1,9 @@
import React, { Fragment, useEffect } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
import styled from "styled-components";
-import { renderTimeOfDayFromUnixTimestamp } from "./helper";
+import { renderTimeOfDayFromUnixTimestamp } from "./helper.js";
/**
* Multiple days resource calendar.
@@ -265,26 +264,4 @@ const Footer = styled.div`
}
`;
-CalendarMultipleDays.propTypes = {
- templateClasses: PropTypes.arrayOf(PropTypes.string),
- templateRootStyle: PropTypes.shape({}),
- calendarEvents: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- startTime: PropTypes.number.isRequired,
- endTime: PropTypes.number,
- resourceTitle: PropTypes.string,
- resourceId: PropTypes.string,
- })
- ).isRequired,
- content: PropTypes.shape({
- title: PropTypes.string,
- displayHeaders: PropTypes.bool,
- footerText: PropTypes.string,
- mediaContain: PropTypes.bool,
- }).isRequired,
- getTitle: PropTypes.func.isRequired,
-};
-
export default CalendarMultipleDays;
diff --git a/assets/shared/template/calendar/calendar-multiple.jsx b/assets/shared/templates/calendar/calendar-multiple.jsx
similarity index 91%
rename from assets/shared/template/calendar/calendar-multiple.jsx
rename to assets/shared/templates/calendar/calendar-multiple.jsx
index 01e2c6d42..915a2e251 100644
--- a/assets/shared/template/calendar/calendar-multiple.jsx
+++ b/assets/shared/templates/calendar/calendar-multiple.jsx
@@ -1,6 +1,5 @@
import React, { Fragment, useEffect, useState } from "react";
import { FormattedMessage } from "react-intl";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
@@ -289,30 +288,4 @@ const ContentHeaderItem = styled.div`
}
`;
-CalendarMultiple.propTypes = {
- templateClasses: PropTypes.arrayOf(PropTypes.string),
- templateRootStyle: PropTypes.shape({}),
- calendarEvents: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- startTime: PropTypes.number.isRequired,
- endTime: PropTypes.number,
- resourceTitle: PropTypes.string,
- resourceId: PropTypes.string,
- })
- ).isRequired,
- content: PropTypes.shape({
- headerOrder: PropTypes.string,
- title: PropTypes.string,
- hasDateAndTime: PropTypes.bool,
- displayHeaders: PropTypes.bool,
- dateAsBox: PropTypes.bool,
- resourceUnavailableText: PropTypes.string,
- hideGrid: PropTypes.bool,
- mediaContain: PropTypes.bool,
- }).isRequired,
- getTitle: PropTypes.func.isRequired,
-};
-
export default CalendarMultiple;
diff --git a/assets/shared/template/calendar/calendar-single-booking-helper.jsx b/assets/shared/templates/calendar/calendar-single-booking-helper.jsx
similarity index 98%
rename from assets/shared/template/calendar/calendar-single-booking-helper.jsx
rename to assets/shared/templates/calendar/calendar-single-booking-helper.jsx
index e53412212..97ac4e806 100644
--- a/assets/shared/template/calendar/calendar-single-booking-helper.jsx
+++ b/assets/shared/templates/calendar/calendar-single-booking-helper.jsx
@@ -2,7 +2,7 @@ import styled from "styled-components";
import dayjs from "dayjs";
import React from "react";
import IconCalendarPlus from "./icon-calendar-plus.svg";
-import { renderTimeOfDayFromUnixTimestamp } from "./helper";
+import { renderTimeOfDayFromUnixTimestamp } from "./helper.js";
const Wrapper = styled.div`
/* Wrapper styling */
diff --git a/assets/shared/template/calendar/calendar-single-booking.jsx b/assets/shared/templates/calendar/calendar-single-booking.jsx
similarity index 91%
rename from assets/shared/template/calendar/calendar-single-booking.jsx
rename to assets/shared/templates/calendar/calendar-single-booking.jsx
index 7f227fdff..049b3fe2e 100644
--- a/assets/shared/template/calendar/calendar-single-booking.jsx
+++ b/assets/shared/templates/calendar/calendar-single-booking.jsx
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
@@ -9,7 +8,7 @@ import IconExclamation from "./icon-exclamation.svg";
import {
renderTimeOfDayFromUnixTimestamp,
timeCountdownString,
-} from "./helper";
+} from "./helper.js";
import {
Button,
Content,
@@ -382,36 +381,4 @@ function CalendarSingleBooking({
);
}
-CalendarSingleBooking.propTypes = {
- slide: PropTypes.shape({
- "@id": PropTypes.string.isRequired,
- feed: PropTypes.shape({
- configuration: PropTypes.shape({
- resources: PropTypes.arrayOf(PropTypes.string),
- }),
- }),
- }).isRequired,
- run: PropTypes.string.isRequired,
- templateClasses: PropTypes.arrayOf(PropTypes.string),
- templateRootStyle: PropTypes.shape({}),
- calendarEvents: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- startTime: PropTypes.number.isRequired,
- endTime: PropTypes.number,
- resourceTitle: PropTypes.string,
- resourceId: PropTypes.string,
- })
- ).isRequired,
- content: PropTypes.shape({
- title: PropTypes.string,
- subTitle: PropTypes.string,
- resourceAvailableText: PropTypes.string,
- resourceUnavailableText: PropTypes.string,
- mediaContain: PropTypes.bool,
- }).isRequired,
- getTitle: PropTypes.func.isRequired,
-};
-
export default CalendarSingleBooking;
diff --git a/assets/shared/template/calendar/calendar-single.jsx b/assets/shared/templates/calendar/calendar-single.jsx
similarity index 82%
rename from assets/shared/template/calendar/calendar-single.jsx
rename to assets/shared/templates/calendar/calendar-single.jsx
index 99cbbafc4..ec2ec8d8f 100644
--- a/assets/shared/template/calendar/calendar-single.jsx
+++ b/assets/shared/templates/calendar/calendar-single.jsx
@@ -1,9 +1,8 @@
import React, { useEffect } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import styled from "styled-components";
-import { renderTimeOfDayFromUnixTimestamp } from "./helper";
+import { renderTimeOfDayFromUnixTimestamp } from "./helper.js";
import "./calendar.scss";
/**
@@ -141,27 +140,4 @@ const Meta = styled.div`
font-size: smaller;
`;
-CalendarSingle.propTypes = {
- templateClasses: PropTypes.arrayOf(PropTypes.string),
- templateRootStyle: PropTypes.shape({}),
- calendarEvents: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- startTime: PropTypes.number.isRequired,
- endTime: PropTypes.number,
- resourceTitle: PropTypes.string,
- resourceId: PropTypes.string,
- })
- ).isRequired,
- content: PropTypes.shape({
- title: PropTypes.string,
- subTitle: PropTypes.string,
- resourceAvailableText: PropTypes.string,
- resourceUnavailableText: PropTypes.string,
- mediaContain: PropTypes.bool,
- }).isRequired,
- getTitle: PropTypes.func.isRequired,
-};
-
export default CalendarSingle;
diff --git a/assets/shared/template/calendar/calendar.scss b/assets/shared/templates/calendar/calendar.scss
similarity index 100%
rename from assets/shared/template/calendar/calendar.scss
rename to assets/shared/templates/calendar/calendar.scss
diff --git a/assets/shared/template/calendar/helper.js b/assets/shared/templates/calendar/helper.js
similarity index 100%
rename from assets/shared/template/calendar/helper.js
rename to assets/shared/templates/calendar/helper.js
diff --git a/assets/shared/template/calendar/icon-calendar-plus.svg b/assets/shared/templates/calendar/icon-calendar-plus.svg
similarity index 100%
rename from assets/shared/template/calendar/icon-calendar-plus.svg
rename to assets/shared/templates/calendar/icon-calendar-plus.svg
diff --git a/assets/shared/template/calendar/icon-check.svg b/assets/shared/templates/calendar/icon-check.svg
similarity index 100%
rename from assets/shared/template/calendar/icon-check.svg
rename to assets/shared/templates/calendar/icon-check.svg
diff --git a/assets/shared/template/calendar/icon-exclamation.svg b/assets/shared/templates/calendar/icon-exclamation.svg
similarity index 100%
rename from assets/shared/template/calendar/icon-exclamation.svg
rename to assets/shared/templates/calendar/icon-exclamation.svg
diff --git a/assets/shared/template/calendar/lang/da.json b/assets/shared/templates/calendar/lang/da.json
similarity index 100%
rename from assets/shared/template/calendar/lang/da.json
rename to assets/shared/templates/calendar/lang/da.json
diff --git a/assets/shared/templates/contacts.json b/assets/shared/templates/contacts.json
new file mode 100644
index 000000000..aa4771ae2
--- /dev/null
+++ b/assets/shared/templates/contacts.json
@@ -0,0 +1,38 @@
+{
+ "title": "Kontakter",
+ "id": "01FPZ19YEHX7MQ5Q6ZS0WK0VEA",
+ "adminForm": [
+ {
+ "key": "contacts-form-1",
+ "input": "header",
+ "text": "Skabelon: Kontakter",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "contacts-form-2",
+ "input": "checkbox",
+ "label": "Animeret streg under overskrift",
+ "name": "separator",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "contacts-form-3",
+ "input": "contacts",
+ "name": "contacts",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "contacts-form-4",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ],
+ "options": {}
+}
diff --git a/assets/shared/template/contacts/contacts.jsx b/assets/shared/templates/contacts.jsx
similarity index 81%
rename from assets/shared/template/contacts/contacts.jsx
rename to assets/shared/templates/contacts.jsx
index 99be8c289..71eca6f2b 100644
--- a/assets/shared/template/contacts/contacts.jsx
+++ b/assets/shared/templates/contacts.jsx
@@ -1,13 +1,31 @@
import React, { useState, useEffect } from "react";
-import PropTypes from "prop-types";
import { IntlProvider, FormattedMessage } from "react-intl";
import styled from "styled-components";
-import BaseSlideExecution from "../base-slide-execution";
-import da from "./lang/da.json";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import PersonSvg from "./person.svg";
-import GlobalStyles from "../GlobalStyles";
-import "./contacts.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import da from "./contacts/lang/da.json";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import PersonSvg from "./contacts/person.svg";
+import GlobalStyles from "../slide-utils/GlobalStyles.js";
+import "./contacts/contacts.scss";
+import templateConfig from './contacts.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Contacts component.
@@ -183,33 +201,4 @@ const FallbackImage = styled(PersonSvg)`
opacity: 0.5;
`;
-Contacts.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number.isRequired,
- separator: PropTypes.bool,
- contacts: PropTypes.arrayOf(
- PropTypes.shape({
- name: PropTypes.string.isRequired,
- phone: PropTypes.string.isRequired,
- email: PropTypes.string.isRequired,
- id: PropTypes.string.isRequired,
- image: PropTypes.arrayOf(PropTypes.string.isRequired),
- })
- ),
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Contacts;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/contacts/contacts.scss b/assets/shared/templates/contacts/contacts.scss
similarity index 100%
rename from assets/shared/template/contacts/contacts.scss
rename to assets/shared/templates/contacts/contacts.scss
diff --git a/assets/shared/template/contacts/lang/da.json b/assets/shared/templates/contacts/lang/da.json
similarity index 100%
rename from assets/shared/template/contacts/lang/da.json
rename to assets/shared/templates/contacts/lang/da.json
diff --git a/assets/shared/template/contacts/person.svg b/assets/shared/templates/contacts/person.svg
similarity index 100%
rename from assets/shared/template/contacts/person.svg
rename to assets/shared/templates/contacts/person.svg
diff --git a/assets/shared/templates/iframe.json b/assets/shared/templates/iframe.json
new file mode 100644
index 000000000..46427afcc
--- /dev/null
+++ b/assets/shared/templates/iframe.json
@@ -0,0 +1,34 @@
+{
+ "title": "IFrame",
+ "id": "01FQBJQ2M3544ZKAADPWBXHY71",
+ "options": {
+ "disableLivePreview": true
+ },
+ "adminForm": [
+ {
+ "key": "iframe-form-1",
+ "input": "header",
+ "text": "Skabelon: IFrame",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "iframe-form-2",
+ "input": "input",
+ "name": "source",
+ "label": "URL til iframe",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "iframe-form-3",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/iframe/iframe.jsx b/assets/shared/templates/iframe.jsx
similarity index 65%
rename from assets/shared/template/iframe/iframe.jsx
rename to assets/shared/templates/iframe.jsx
index 1ba8cb29a..64a5548ff 100644
--- a/assets/shared/template/iframe/iframe.jsx
+++ b/assets/shared/templates/iframe.jsx
@@ -1,8 +1,26 @@
import React, { useEffect } from "react";
-import PropTypes from "prop-types";
-import BaseSlideExecution from "../base-slide-execution";
-import { ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import templateConfig from './iframe.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* IFrame component.
@@ -46,20 +64,4 @@ function IFrame({ slide, content, run, slideDone, executionId }) {
);
}
-IFrame.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- instanceId: PropTypes.string,
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number.isRequired,
- source: PropTypes.string,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default IFrame;
+export default { id, config, renderSlide };
diff --git a/assets/shared/templates/image-text.json b/assets/shared/templates/image-text.json
new file mode 100644
index 000000000..5b1a4d8f2
--- /dev/null
+++ b/assets/shared/templates/image-text.json
@@ -0,0 +1,248 @@
+{
+ "title": "Billede og tekst",
+ "id": "01FP2SNGFN0BZQH03KCBXHKYHG",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "image-text-form-1",
+ "input": "header",
+ "text": "Skabelon: Tekst og billede",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "image-text-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "image-text-form-3",
+ "input": "textarea",
+ "name": "title",
+ "label": "Overskrift på slide",
+ "helpText": "Her kan du skrive overskriften til slidet",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "image-text-form-4",
+ "input": "rich-text-input",
+ "name": "text",
+ "label": "Tekst på slide",
+ "helpText": "Her kan du skrive teksten til slidet",
+ "formGroupClasses": "col-md"
+ },
+ {
+ "key": "image-text-form-81",
+ "input": "select",
+ "required": true,
+ "label": "Tekststørrelse",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "fontsize1",
+ "title": "xs",
+ "value": "font-size-xs"
+ },
+ {
+ "key": "fontsize2",
+ "title": "s",
+ "value": "font-size-s"
+ },
+ {
+ "key": "fontsize3",
+ "title": "m",
+ "value": "font-size-m"
+ },
+ {
+ "key": "fontsize4",
+ "title": "l",
+ "value": "font-size-lg"
+ },
+ {
+ "key": "fontsize5",
+ "title": "xl",
+ "value": "font-size-xl"
+ }
+ ],
+ "name": "fontSize"
+ },
+
+ {
+ "key": "image-text-form-5",
+ "multipleImages": true,
+ "input": "image",
+ "name": "image",
+ "label": "Billeder",
+ "helpText": "Hvis du tilføjer mere end et billede bliver de vist i (varighed / antal_billeder) sekunder hver."
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ },
+ {
+ "key": "image-text-form-6",
+ "input": "header-h3",
+ "text": "Opsætning",
+ "name": "header3",
+ "formGroupClasses": "h5 mt-3 mb-3"
+ },
+ {
+ "key": "image-text-form-7",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-8",
+ "input": "select",
+ "required": true,
+ "label": "Hvor skal tekstboksen være placeret",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "placement1",
+ "title": "Toppen",
+ "value": "top"
+ },
+ {
+ "key": "placement2",
+ "title": "Højre",
+ "value": "right"
+ },
+ {
+ "key": "placement3",
+ "title": "Bunden",
+ "value": "bottom"
+ },
+ {
+ "key": "placement4",
+ "title": "Venstre",
+ "value": "left"
+ }
+ ],
+ "name": "boxAlign"
+ },
+ {
+ "key": "image-text-form-9",
+ "input": "checkbox",
+ "label": "Margin rundt om boks",
+ "name": "boxMargin",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-10",
+ "input": "checkbox",
+ "label": "Animeret streg under overskrift",
+ "name": "separator",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-11",
+ "input": "checkbox",
+ "label": "Gør tekstboksen mindre",
+ "name": "halfSize",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-12",
+ "input": "checkbox",
+ "label": "Alternativt layout uden tekstboks",
+ "helpText": "Denne kan ikke kombineres med den animerede tekst under overskriften",
+ "name": "reversed",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-13",
+ "input": "checkbox",
+ "label": "Skyggeeffekt på tekstboksen",
+ "name": "shadow",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-14",
+ "input": "checkbox",
+ "label": "Vis logo fra tema",
+ "name": "showLogo",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-15",
+ "input": "select",
+ "required": false,
+ "label": "Logostørrelse",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "logosize1",
+ "title": "s",
+ "value": "logo-size-s"
+ },
+ {
+ "key": "logosize2",
+ "title": "m",
+ "value": "logo-size-m"
+ },
+ {
+ "key": "logosize3",
+ "title": "l",
+ "value": "logo-size-l"
+ }
+ ],
+ "name": "logoSize"
+ },
+ {
+ "key": "image-text-form-16",
+ "input": "select",
+ "required": false,
+ "label": "Logoposition",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "logoposition1",
+ "title": "Top/venstre",
+ "value": "logo-position-top-left"
+ },
+ {
+ "key": "logoposition2",
+ "title": "Top/højre",
+ "value": "logo-position-top-right"
+ },
+ {
+ "key": "logoposition3",
+ "title": "Bund/venstre",
+ "value": "logo-position-bottom-left"
+ },
+ {
+ "key": "logoposition4",
+ "title": "Bund/højre",
+ "value": "logo-position-bottom-right"
+ }
+ ],
+ "name": "logoPosition"
+ },
+ {
+ "key": "image-text-form-17",
+ "input": "checkbox",
+ "label": "Margin om logo",
+ "name": "logoMargin",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "image-text-form-18",
+ "input": "checkbox",
+ "label": "Deaktiver fade ved flere billeder",
+ "name": "disableImageFade",
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/image-text/image-text.jsx b/assets/shared/templates/image-text.jsx
similarity index 79%
rename from assets/shared/template/image-text/image-text.jsx
rename to assets/shared/templates/image-text.jsx
index 163feaf9e..54bbb86b6 100644
--- a/assets/shared/template/image-text/image-text.jsx
+++ b/assets/shared/templates/image-text.jsx
@@ -1,16 +1,32 @@
import React, { createRef, useEffect, useRef, useState } from "react";
import parse from "html-react-parser";
import DOMPurify from "dompurify";
-import PropTypes from "prop-types";
import { CSSTransition, TransitionGroup } from "react-transition-group";
-import BaseSlideExecution from "../base-slide-execution";
-import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./image-text.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./image-text/image-text.scss";
+import imageTextConfig from './image-text.json';
+
+function id() {
+ return imageTextConfig.id;
+}
+
+function config() {
+ return imageTextConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
- * ImageText component.
- *
* @param {object} props Props.
* @param {object} props.slide The slide.
* @param {object} props.content The slide content.
@@ -25,9 +41,7 @@ function ImageText({ slide, content, run, slideDone, executionId }) {
const [currentImage, setCurrentImage] = useState();
const [themeCss, setThemeCss] = useState(null);
const logo = slide?.theme?.logo;
- const { showLogo, logoSize, logoPosition, logoMargin, mediaContain } =
- content;
- const { disableImageFade } = content;
+ const { showLogo, logoSize, logoPosition, logoMargin, mediaContain, disableImageFade } = content;
const logoUrl = showLogo && logo?.assets?.uri ? logo.assets.uri : "";
@@ -236,47 +250,4 @@ function ImageText({ slide, content, run, slideDone, executionId }) {
);
}
-ImageText.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- logo: PropTypes.shape({
- assets: PropTypes.shape({
- url: PropTypes.string,
- }),
- }),
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number,
- image: PropTypes.arrayOf(PropTypes.string),
- mediaContain: PropTypes.bool,
- title: PropTypes.string,
- text: PropTypes.string,
- textColor: PropTypes.string,
- boxColor: PropTypes.string,
- styling: PropTypes.shape({
- // Accepted values: top, bottom, left, right.
- boxAlign: PropTypes.string,
- boxMargin: PropTypes.bool,
- separator: PropTypes.bool,
- reversed: PropTypes.bool,
- halfSize: PropTypes.bool,
- fontSize: PropTypes.string,
- }),
- showLogo: PropTypes.bool,
- logoSize: PropTypes.string,
- logoMargin: PropTypes.bool,
- logoPosition: PropTypes.string,
- disableImageFade: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default ImageText;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/image-text/image-text.scss b/assets/shared/templates/image-text/image-text.scss
similarity index 100%
rename from assets/shared/template/image-text/image-text.scss
rename to assets/shared/templates/image-text/image-text.scss
diff --git a/assets/shared/templates/instagram-feed.json b/assets/shared/templates/instagram-feed.json
new file mode 100644
index 000000000..8a3cfadb0
--- /dev/null
+++ b/assets/shared/templates/instagram-feed.json
@@ -0,0 +1,97 @@
+{
+ "title": "Instagram feed",
+ "id": "01FTZC0RKJYHG4JVZG5K709G46",
+ "adminForm": [
+ {
+ "key": "instagram-feed-form-1",
+ "input": "header",
+ "text": "Skabelon: Instagram feed",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "instagram-feed-form-2",
+ "input": "header-h3",
+ "text": "Opsæt feed",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "instagram-feed-form-3",
+ "input": "feed",
+ "text": "Opsæt feed",
+ "name": "feeds",
+ "supportedFeedOutputType": "instagram",
+ "formGroupClasses": "mb-3",
+ "singleSelect": true
+ },
+ {
+ "key": "instagram-feed-form-entry-duration",
+ "input": "input",
+ "name": "entryDuration",
+ "type": "number",
+ "label": "Varighed pr. billede/video (i sekunder)",
+ "helpText": "Her skal du skrive varigheden pr. billede/video.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "instagram-feed-hashtag-text",
+ "input": "input",
+ "name": "hashtagText",
+ "type": "text",
+ "label": "Hashtag-tekst",
+ "helpText": "Her kan du skrive hashtag-teksten.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "instagram-feed-orientation",
+ "input": "select",
+ "name": "orientation",
+ "label": "Vælg skærmorientering",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "orientation1",
+ "title": "Horisontalt",
+ "value": "landscape"
+ },
+ {
+ "key": "orientation2",
+ "title": "Vertikal",
+ "value": "vertical"
+ }
+ ]
+ },
+ {
+ "key": "instagram-feed-image-width",
+ "input": "input",
+ "name": "imageWidth",
+ "type": "number",
+ "min": 0,
+ "max": 100.0,
+ "label": "Billedebredde %",
+ "helpText": "Her kan du skrive billedebredden i procent.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "instagram-feed-max-entries",
+ "input": "input",
+ "name": "maxEntries",
+ "type": "number",
+ "min": 1,
+ "label": "Maksimalt antal billeder/videoer fra feedet",
+ "helpText": "Her kan du skrive maksimalt antal billeder/videoer fra feedet.",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ }
+ ],
+ "options": {}
+}
diff --git a/assets/shared/template/instagram-feed/instagram-feed.jsx b/assets/shared/templates/instagram-feed.jsx
similarity index 80%
rename from assets/shared/template/instagram-feed/instagram-feed.jsx
rename to assets/shared/templates/instagram-feed.jsx
index 4709f6a57..730b90a56 100644
--- a/assets/shared/template/instagram-feed/instagram-feed.jsx
+++ b/assets/shared/templates/instagram-feed.jsx
@@ -1,16 +1,34 @@
import React, { useState, useEffect } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import relativeTime from "dayjs/plugin/relativeTime";
import localizedFormat from "dayjs/plugin/localizedFormat";
import parse from "html-react-parser";
import DOMPurify from "dompurify";
-import Shape from "./shape.svg";
-import InstagramLogo from "./instagram-logo.svg";
-import { ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./instagram-feed.scss";
+import Shape from "./instagram-feed/shape.svg";
+import InstagramLogo from "./instagram-feed/instagram-logo.svg";
+import { ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./instagram-feed/instagram-feed.scss";
+import templateConfig from './instagram-feed.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Sparkle component.
@@ -153,33 +171,4 @@ function InstagramFeed({ slide, content, run, slideDone, executionId }) {
);
}
-InstagramFeed.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- feedData: PropTypes.arrayOf(
- PropTypes.shape({
- text: PropTypes.string,
- textMarkup: PropTypes.string,
- mediaUrl: PropTypes.string,
- videoUrl: PropTypes.string,
- username: PropTypes.string,
- createdTime: PropTypes.string,
- })
- ).isRequired,
- }).isRequired,
- content: PropTypes.shape({
- hashtagText: PropTypes.string,
- orientation: PropTypes.string,
- entryDuration: PropTypes.number,
- maxEntries: PropTypes.number,
- imageWidth: PropTypes.number,
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default InstagramFeed;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/instagram-feed/instagram-feed.scss b/assets/shared/templates/instagram-feed/instagram-feed.scss
similarity index 100%
rename from assets/shared/template/instagram-feed/instagram-feed.scss
rename to assets/shared/templates/instagram-feed/instagram-feed.scss
diff --git a/assets/shared/template/instagram-feed/instagram-logo.svg b/assets/shared/templates/instagram-feed/instagram-logo.svg
similarity index 100%
rename from assets/shared/template/instagram-feed/instagram-logo.svg
rename to assets/shared/templates/instagram-feed/instagram-logo.svg
diff --git a/assets/shared/template/instagram-feed/shape.svg b/assets/shared/templates/instagram-feed/shape.svg
similarity index 100%
rename from assets/shared/template/instagram-feed/shape.svg
rename to assets/shared/templates/instagram-feed/shape.svg
diff --git a/assets/shared/templates/news-feed.json b/assets/shared/templates/news-feed.json
new file mode 100644
index 000000000..aeb6037e8
--- /dev/null
+++ b/assets/shared/templates/news-feed.json
@@ -0,0 +1,64 @@
+{
+ "title": "Nyheder",
+ "id": "01JEWPAFF93YSF418TH72W1SBA",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "news-feed-form-1",
+ "input": "header",
+ "text": "Skabelon: Nyheder",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "news-feed-form-2",
+ "input": "header-h3",
+ "text": "Opsæt feed",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "news-feed-form-3",
+ "input": "feed",
+ "text": "Opsæt feed",
+ "name": "feeds",
+ "supportedFeedOutputType": "rss",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "news-feed-form-entry-duration",
+ "input": "input",
+ "name": "entryDuration",
+ "type": "number",
+ "label": "Varighed pr. nyhed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden pr. nyhed.",
+ "formGroupClasses": "mb-3",
+ "required": true
+ },
+ {
+ "key": "news-feed-form-readmore",
+ "input": "input",
+ "label": "Læs mere tekst",
+ "helpText": "Hvis den ikke overskrives, står der \"Læs hele nyheden\"",
+ "name": "readMore",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "rss-form-4",
+ "multipleImages": false,
+ "input": "image",
+ "name": "fallbackImage",
+ "label": "Fallback-billede",
+ "helpText": "Dette billede kan vises, hvis nyheden mangler et billede.",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring",
+ "formGroupClasses": "mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/news-feed/news-feed.jsx b/assets/shared/templates/news-feed.jsx
similarity index 77%
rename from assets/shared/template/news-feed/news-feed.jsx
rename to assets/shared/templates/news-feed.jsx
index 5e1072722..479bf338e 100644
--- a/assets/shared/template/news-feed/news-feed.jsx
+++ b/assets/shared/templates/news-feed.jsx
@@ -1,14 +1,31 @@
import React, { useState, useEffect, useRef } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import relativeTime from "dayjs/plugin/relativeTime";
import localizedFormat from "dayjs/plugin/localizedFormat";
import QRCode from "qrcode";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./news-feed.scss";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./news-feed/news-feed.scss";
+import templateConfig from './news-feed.json';
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* News feed slide.
*
@@ -158,44 +175,4 @@ function NewsFeed({ slide, content, run, slideDone, executionId }) {
);
}
-NewsFeed.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- feedData: PropTypes.shape({
- title: PropTypes.string,
- entries: PropTypes.arrayOf(
- PropTypes.shape({
- title: PropTypes.string,
- content: PropTypes.string,
- author: PropTypes.shape({
- name: PropTypes.string,
- }),
- medias: PropTypes.arrayOf(
- PropTypes.shape({
- url: PropTypes.string,
- })
- ),
- lastModified: PropTypes.string,
- link: PropTypes.string,
- })
- ),
- }).isRequired,
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- }).isRequired,
- content: PropTypes.shape({
- readMore: PropTypes.string,
- entryDuration: PropTypes.number,
- mediaContain: PropTypes.bool,
- fallbackImage: PropTypes.arrayOf(PropTypes.string),
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default NewsFeed;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/news-feed/news-feed.scss b/assets/shared/templates/news-feed/news-feed.scss
similarity index 100%
rename from assets/shared/template/news-feed/news-feed.scss
rename to assets/shared/templates/news-feed/news-feed.scss
diff --git a/assets/shared/templates/poster.json b/assets/shared/templates/poster.json
new file mode 100644
index 000000000..b19ac22d6
--- /dev/null
+++ b/assets/shared/templates/poster.json
@@ -0,0 +1,54 @@
+{
+ "title": "Plakat",
+ "id": "01FWJZQ25A1868V63CWYYHQFKQ",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "poster-form-1",
+ "input": "header",
+ "text": "Skabelon: Plakat fra feed",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "poster-form-2",
+ "input": "header-h3",
+ "text": "Opsæt feed",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "poster-form-3",
+ "input": "feed",
+ "text": "Opsæt feed",
+ "name": "feeds",
+ "supportedFeedOutputType": "poster",
+ "formGroupClasses": "mb-3",
+ "singleSelect": true
+ },
+ {
+ "key": "poster-form-entry-duration",
+ "input": "duration",
+ "name": "duration",
+ "type": "number",
+ "label": "Varighed pr. begivenhed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden pr. begivenhed.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "poster-form-4",
+ "input": "checkbox",
+ "label": "Vis logo fra tema",
+ "name": "showLogo",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ }
+ ]
+}
diff --git a/assets/shared/template/poster/poster.jsx b/assets/shared/templates/poster.jsx
similarity index 80%
rename from assets/shared/template/poster/poster.jsx
rename to assets/shared/templates/poster.jsx
index 8ec12e4ad..d5f3c5cd9 100644
--- a/assets/shared/template/poster/poster.jsx
+++ b/assets/shared/templates/poster.jsx
@@ -1,13 +1,31 @@
import React, { useEffect, useRef, useState } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { IntlProvider, FormattedMessage } from "react-intl";
-import da from "./lang/da.json";
-import { ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./poster.scss";
+import da from "./poster/lang/da.json";
+import { ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./poster/poster.scss";
+import templateConfig from './poster.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Poster component.
@@ -265,60 +283,4 @@ function Poster({ slide, content, run, slideDone, executionId }) {
);
}
-Poster.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- logo: PropTypes.shape({
- assets: PropTypes.shape({
- url: PropTypes.string,
- }),
- }),
- }),
- feed: PropTypes.shape({
- configuration: PropTypes.shape({
- overrideTitle: PropTypes.string,
- overrideSubTitle: PropTypes.string,
- overrideTicketPrice: PropTypes.string,
- overrideReadMoreUrl: PropTypes.string,
- hideTime: PropTypes.bool,
- readMoreText: PropTypes.string,
- }),
- }),
- feedData: PropTypes.arrayOf(
- PropTypes.shape({
- endDate: PropTypes.string,
- eventStatusText: PropTypes.string,
- excerpt: PropTypes.string,
- image: PropTypes.string,
- name: PropTypes.string,
- place: PropTypes.shape({
- addressLocality: PropTypes.string,
- image: PropTypes.string,
- name: PropTypes.string,
- postalCode: PropTypes.string,
- streetAddress: PropTypes.string,
- telephone: PropTypes.string,
- }),
- startDate: PropTypes.string,
- ticketPriceRange: PropTypes.string,
- ticketPurchaseUrl: PropTypes.string,
- url: PropTypes.string,
- })
- ),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number,
- showLogo: PropTypes.bool,
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Poster;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/poster/lang/da.json b/assets/shared/templates/poster/lang/da.json
similarity index 100%
rename from assets/shared/template/poster/lang/da.json
rename to assets/shared/templates/poster/lang/da.json
diff --git a/assets/shared/template/poster/poster.scss b/assets/shared/templates/poster/poster.scss
similarity index 100%
rename from assets/shared/template/poster/poster.scss
rename to assets/shared/templates/poster/poster.scss
diff --git a/assets/shared/templates/rss.json b/assets/shared/templates/rss.json
new file mode 100644
index 000000000..14296d2ef
--- /dev/null
+++ b/assets/shared/templates/rss.json
@@ -0,0 +1,79 @@
+{
+ "title": "RSS",
+ "id": "01FQC300GGWCA7A8H0SXY6P9FG",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "rss-form-1",
+ "input": "header",
+ "text": "Skabelon: RSS",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "rss-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "rss-form-3",
+ "input": "feed",
+ "text": "Opsæt feed",
+ "name": "feed",
+ "supportedFeedOutputType": "rss",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "rss-form-4",
+ "multipleImages": false,
+ "input": "image",
+ "name": "image",
+ "label": "Baggrundsbillede",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ },
+ {
+ "key": "rss-form-5",
+ "input": "select",
+ "required": false,
+ "label": "Tekststørrelse",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "fontsize1",
+ "title": "xs",
+ "value": "font-size-xs"
+ },
+ {
+ "key": "fontsize2",
+ "title": "s",
+ "value": "font-size-s"
+ },
+ {
+ "key": "fontsize3",
+ "title": "m",
+ "value": "font-size-m"
+ },
+ {
+ "key": "fontsize4",
+ "title": "l",
+ "value": "font-size-lg"
+ },
+ {
+ "key": "fontsize5",
+ "title": "xl",
+ "value": "font-size-xl"
+ }
+ ],
+ "name": "fontSize"
+ }
+ ]
+}
diff --git a/assets/shared/template/rss/rss.jsx b/assets/shared/templates/rss.jsx
similarity index 82%
rename from assets/shared/template/rss/rss.jsx
rename to assets/shared/templates/rss.jsx
index 35dd35b0f..44288a604 100644
--- a/assets/shared/template/rss/rss.jsx
+++ b/assets/shared/templates/rss.jsx
@@ -1,12 +1,30 @@
import React, { useEffect, useRef, useState } from "react";
-import PropTypes from "prop-types";
import dayjs from "dayjs";
import localeDa from "dayjs/locale/da";
import localizedFormat from "dayjs/plugin/localizedFormat";
import styled from "styled-components";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import GlobalStyles from "../GlobalStyles";
-import "./rss.scss";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import GlobalStyles from "../slide-utils/GlobalStyles.js";
+import "./rss/rss.scss";
+import templateConfig from './rss.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* RSS component.
@@ -205,41 +223,4 @@ const Description = styled.p`
}
`;
-RSS.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- feed: PropTypes.shape({
- configuration: PropTypes.shape({
- numberOfEntries: PropTypes.number,
- entryDuration: PropTypes.number,
- showFeedProgress: PropTypes.bool,
- }),
- }),
- feedData: PropTypes.shape({
- title: PropTypes.string,
- entries: PropTypes.arrayOf(
- PropTypes.shape({
- title: PropTypes.string,
- lastModified: PropTypes.string,
- content: PropTypes.string,
- })
- ),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- image: PropTypes.arrayOf(PropTypes.string),
- fontSize: PropTypes.string,
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default RSS;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/rss/rss.scss b/assets/shared/templates/rss/rss.scss
similarity index 100%
rename from assets/shared/template/rss/rss.scss
rename to assets/shared/templates/rss/rss.scss
diff --git a/assets/shared/templates/slideshow.json b/assets/shared/templates/slideshow.json
new file mode 100644
index 000000000..e29ade32f
--- /dev/null
+++ b/assets/shared/templates/slideshow.json
@@ -0,0 +1,179 @@
+{
+ "title": "Slideshow",
+ "id": "01FP2SNSC9VXD10ZKXQR819NS9",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "slideshow-form-1",
+ "input": "header",
+ "text": "Skabelon: Slideshow",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "slideshow-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "slideshow-form-3",
+ "multipleImages": true,
+ "input": "image",
+ "name": "images",
+ "label": "Billeder"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ },
+ {
+ "key": "slideshow-form-4",
+ "input": "header-h3",
+ "text": "Opsætning",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "slideshow-form-5",
+ "input": "input",
+ "name": "imageDuration",
+ "type": "number",
+ "label": "Varighed pr. billede (i sekunder)",
+ "helpText": "Her skal du skrive varigheden pr. billede.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "name": "animation",
+ "key": "slideshow-form-6",
+ "input": "select",
+ "label": "Vælg animation",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "animation0",
+ "title": "Ingen animation",
+ "value": null
+ },
+ {
+ "key": "animation1",
+ "title": "Tilfældig",
+ "value": "random"
+ },
+ {
+ "key": "animation2",
+ "title": "Zoom mod midten",
+ "value": "zoom-in-middle"
+ },
+ {
+ "key": "animation3",
+ "title": "Zoom ud fra midten",
+ "value": "zoom-out-middle"
+ },
+ {
+ "key": "animation4",
+ "title": "Zoom mod tilfældigt punkt",
+ "value": "zoom-in-random"
+ },
+ {
+ "key": "animation5",
+ "title": "Zoom ud fra tilfældigt punkt",
+ "value": "zoom-out-random"
+ }
+ ]
+ },
+ {
+ "name": "transition",
+ "key": "slideshow-form-7",
+ "input": "select",
+ "label": "Vælg overgang",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "transition1",
+ "title": "Ingen overgang",
+ "value": null
+ },
+ {
+ "key": "transition2",
+ "title": "Cross fade",
+ "value": "fade"
+ }
+ ]
+ },
+ {
+ "key": "slideshow-form-8",
+ "input": "checkbox",
+ "label": "Vis logo fra tema",
+ "name": "showLogo",
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "slideshow-form-9",
+ "input": "select",
+ "required": false,
+ "label": "Logostørrelse",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "logosize1",
+ "title": "s",
+ "value": "logo-size-s"
+ },
+ {
+ "key": "logosize2",
+ "title": "m",
+ "value": "logo-size-m"
+ },
+ {
+ "key": "logosize3",
+ "title": "l",
+ "value": "logo-size-l"
+ }
+ ],
+ "name": "logoSize"
+ },
+ {
+ "key": "slideshow-form-10",
+ "input": "select",
+ "required": false,
+ "label": "Logoposition",
+ "formGroupClasses": "col-md-6 mb-3",
+ "options": [
+ {
+ "key": "logoposition1",
+ "title": "Top/venstre",
+ "value": "logo-position-top-left"
+ },
+ {
+ "key": "logoposition2",
+ "title": "Top/højre",
+ "value": "logo-position-top-right"
+ },
+ {
+ "key": "logoposition3",
+ "title": "Bund/venstre",
+ "value": "logo-position-bottom-left"
+ },
+ {
+ "key": "logoposition4",
+ "title": "Bund/højre",
+ "value": "logo-position-bottom-right"
+ }
+ ],
+ "name": "logoPosition"
+ },
+ {
+ "key": "slideshow-form-11",
+ "input": "checkbox",
+ "label": "Margin om logo",
+ "name": "logoMargin",
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/slideshow/slideshow.jsx b/assets/shared/templates/slideshow.jsx
similarity index 89%
rename from assets/shared/template/slideshow/slideshow.jsx
rename to assets/shared/templates/slideshow.jsx
index feceda1e7..74309a21e 100644
--- a/assets/shared/template/slideshow/slideshow.jsx
+++ b/assets/shared/templates/slideshow.jsx
@@ -1,8 +1,26 @@
import React, { useState, useRef, useEffect } from "react";
-import PropTypes from "prop-types";
-import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./slideshow.scss";
+import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./slideshow/slideshow.scss";
+import templateConfig from './slideshow.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Slideshow component.
@@ -297,35 +315,4 @@ function Slideshow({ slide, content, run, slideDone, executionId }) {
);
}
-Slideshow.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- logo: PropTypes.shape({
- assets: PropTypes.shape({
- url: PropTypes.string,
- }),
- }),
- }),
- }).isRequired,
- content: PropTypes.shape({
- images: PropTypes.arrayOf(PropTypes.string),
- mediaContain: PropTypes.bool,
- imageDuration: PropTypes.number,
- animation: PropTypes.string,
- transition: PropTypes.string,
- showLogo: PropTypes.bool,
- logoSize: PropTypes.string,
- logoMargin: PropTypes.bool,
- logoPosition: PropTypes.string,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Slideshow;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/slideshow/slideshow.scss b/assets/shared/templates/slideshow/slideshow.scss
similarity index 100%
rename from assets/shared/template/slideshow/slideshow.scss
rename to assets/shared/templates/slideshow/slideshow.scss
diff --git a/assets/shared/templates/table.json b/assets/shared/templates/table.json
new file mode 100644
index 000000000..424152736
--- /dev/null
+++ b/assets/shared/templates/table.json
@@ -0,0 +1,129 @@
+{
+ "title": "Tabel",
+ "id": "01FQBJFKM0YFX1VW5K94VBSNCP",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "table-form-1",
+ "input": "header",
+ "text": "Skabelon: Tabel",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "table-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "table-form-3",
+ "input": "input",
+ "name": "title",
+ "label": "Overskrift på tabelslide",
+ "helpText": "Her kan du skrive overskriften til tabelslide"
+ },
+ {
+ "key": "table-form-4",
+ "input": "input",
+ "name": "text",
+ "label": "Et tekstfelt med ekstra tekst",
+ "helpText": "Kan placeres ovenover eller nedenunder tabellen"
+ },
+ {
+ "key": "table-form-5",
+ "input": "select",
+ "required": true,
+ "label": "Hvor skal teksten være placeret",
+ "formGroupClasses": " mb-3",
+ "options": [
+ {
+ "key": "placement1",
+ "title": "Over tabellen",
+ "value": "top"
+ },
+ {
+ "key": "placement2",
+ "title": "Under tabellen",
+ "value": "bottom"
+ }
+ ],
+ "name": "fontPlacement"
+ },
+ {
+ "key": "table-form-6",
+ "input": "select",
+ "required": true,
+ "label": "Tekststørrelse",
+ "formGroupClasses": " mb-3",
+ "options": [
+ {
+ "key": "fontsize1",
+ "title": "xs",
+ "value": "font-size-xs"
+ },
+ {
+ "key": "fontsize2",
+ "title": "s",
+ "value": "font-size-s"
+ },
+ {
+ "key": "fontsize3",
+ "title": "m",
+ "value": "font-size-m"
+ },
+ {
+ "key": "fontsize4",
+ "title": "l",
+ "value": "font-size-lg"
+ },
+ {
+ "key": "fontsize5",
+ "title": "xl",
+ "value": "font-size-xl"
+ }
+ ],
+ "name": "fontSize"
+ },
+ {
+ "key": "table-form-7",
+ "multipleImages": false,
+ "input": "image",
+ "name": "image",
+ "label": "Baggrundsbillede"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ },
+ {
+ "key": "table-form-8",
+ "input": "header-h3",
+ "text": "Tabellen",
+ "name": "header3",
+ "formGroupClasses": "h5 mt-3 mb-3"
+ },
+ {
+ "key": "table-form-9",
+ "input": "table",
+ "name": "table",
+ "label": "Tabellen",
+ "helpText": "Her kan du angive tabeldata"
+ },
+ {
+ "key": "table-form-10",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/table/table.jsx b/assets/shared/templates/table.jsx
similarity index 83%
rename from assets/shared/template/table/table.jsx
rename to assets/shared/templates/table.jsx
index 96f984e5d..333de487d 100644
--- a/assets/shared/template/table/table.jsx
+++ b/assets/shared/templates/table.jsx
@@ -1,10 +1,28 @@
import React, { useEffect, Fragment } from "react";
-import PropTypes from "prop-types";
import styled from "styled-components";
-import BaseSlideExecution from "../base-slide-execution";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import GlobalStyles from "../GlobalStyles";
-import "./table.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import GlobalStyles from "../slide-utils/GlobalStyles.js";
+import "./table/table.scss";
+import templateConfig from './table.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Table component.
@@ -204,31 +222,4 @@ const Description = styled.div`
margin: var(--margin-size-base) 0;
`;
-Table.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- instanceId: PropTypes.string,
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number.isRequired,
- fontSize: PropTypes.string,
- fontPlacement: PropTypes.string,
- title: PropTypes.string,
- text: PropTypes.string,
- table: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.any)),
- image: PropTypes.arrayOf(PropTypes.string),
- separator: PropTypes.bool,
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Table;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/table/table.scss b/assets/shared/templates/table/table.scss
similarity index 100%
rename from assets/shared/template/table/table.scss
rename to assets/shared/templates/table/table.scss
diff --git a/assets/shared/templates/travel.json b/assets/shared/templates/travel.json
new file mode 100644
index 000000000..71c86df91
--- /dev/null
+++ b/assets/shared/templates/travel.json
@@ -0,0 +1,145 @@
+{
+ "title": "Rejseplanen",
+ "id": "01FZD7K807VAKZ99BGSSCHRJM6",
+ "options": {
+ "disableLivePreview": true
+ },
+ "adminForm": [
+ {
+ "key": "station-template-name",
+ "input": "header",
+ "text": "Skabelon: Rejseplanen",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "station-template-content",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "station-template-headline",
+ "input": "input",
+ "name": "title",
+ "label": "Overskrift på slide",
+ "helpText": "Her kan du skrive overskriften til slidet",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-text",
+ "input": "rich-text-input",
+ "name": "text",
+ "label": "Tekst på slide",
+ "helpText": "Her kan du skrive teksten til slidet",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-distance",
+ "input": "input",
+ "name": "distance",
+ "label": "Afstand",
+ "helpText": "Her kan du skrive teksten til afstandsfeltet",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-time-moderate",
+ "input": "input",
+ "name": "timeModerate",
+ "label": "Tid (Moderat)",
+ "helpText": "Her kan du skrive teksten til tidsfeltet (moderat)",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-time-fast",
+ "input": "input",
+ "name": "timeFast",
+ "label": "Tid (Hurtigt)",
+ "helpText": "Her kan du skrive teksten til tidsfeltet (hurtigt)",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-image",
+ "multipleImages": false,
+ "input": "image",
+ "name": "image",
+ "label": "Billede",
+ "helpText": "Her uploades dit billede med indtegnet rute",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-number-of-journeys",
+ "input": "input",
+ "name": "numberOfJourneys",
+ "label": "Antal afgange der skal vises",
+ "helpText": "Hvis du for eksempel skriver 3, vises de næste 3 afgange fra stoppestedet",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-iframe-title",
+ "input": "input",
+ "name": "iframeTitle",
+ "label": "Tekst i rejseplan",
+ "helpText": "Her kan du skrive hvorfra bussen kører",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-stations",
+ "input": "travel-plan",
+ "label": "Vælg stoppested",
+ "helpText": "Hvis du er i tvivl om hvilke stoppesteder, henviser vi til din lokale busservice",
+ "formGroupClasses": "mb-3",
+ "name": "station"
+ },
+ {
+ "key": "station-template-bus-or-tram",
+ "input": "radio",
+ "label": "Er det valgte stoppested bus eller letbane",
+ "options": [
+ {
+ "id": "bus",
+ "label": "Bus"
+ },
+ {
+ "id": "tram",
+ "label": "Letbane"
+ }
+ ],
+ "name": "busOrTram",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-darkmode",
+ "input": "radio",
+ "label": "Darkmode",
+ "options": [
+ {
+ "id": "",
+ "label": "Deaktiver"
+ },
+ {
+ "id": "auto",
+ "label": "Auto"
+ },
+ {
+ "id": "night",
+ "label": "Night"
+ }
+ ],
+ "name": "monitorLayout",
+ "formGroupClasses": "mb-3"
+ },
+ {
+ "key": "station-template-duration",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "mb-3"
+ }
+ ]
+}
diff --git a/assets/shared/template/travel/travel.jsx b/assets/shared/templates/travel.jsx
similarity index 80%
rename from assets/shared/template/travel/travel.jsx
rename to assets/shared/templates/travel.jsx
index f3f0627c7..29002d2b3 100644
--- a/assets/shared/template/travel/travel.jsx
+++ b/assets/shared/templates/travel.jsx
@@ -1,13 +1,31 @@
import React, { useEffect, useState } from "react";
-import PropTypes from "prop-types";
import DOMPurify from "dompurify";
import parse from "html-react-parser";
import { IntlProvider, FormattedMessage } from "react-intl";
-import BaseSlideExecution from "../base-slide-execution";
-import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-util.jsx";
-import da from "./lang/da.json";
-import "../global-styles.css";
-import "./travel.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { getFirstMediaUrlFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import da from "./travel/lang/da.json";
+import "../slide-utils/global-styles.css";
+import "./travel/travel.scss";
+import templateConfig from './travel.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Travel component.
@@ -189,36 +207,4 @@ function Travel({
);
}
-Travel.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- instanceId: PropTypes.string,
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number,
- station: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string })),
- timeFast: PropTypes.string,
- timeModerate: PropTypes.string,
- title: PropTypes.string,
- text: PropTypes.string,
- image: PropTypes.arrayOf(PropTypes.string),
- distance: PropTypes.string,
- iframeTitle: PropTypes.string,
- busOrTram: PropTypes.string,
- numberOfJourneys: PropTypes.number,
- monitorLayout: PropTypes.string,
- disableIcons: PropTypes.bool,
- mediaContain: PropTypes.bool,
- }),
- executionId: PropTypes.string.isRequired,
-};
-
-export default Travel;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/travel/fonts/rubik-regular.woff b/assets/shared/templates/travel/fonts/rubik-regular.woff
similarity index 100%
rename from assets/shared/template/travel/fonts/rubik-regular.woff
rename to assets/shared/templates/travel/fonts/rubik-regular.woff
diff --git a/assets/shared/template/travel/lang/da.json b/assets/shared/templates/travel/lang/da.json
similarity index 100%
rename from assets/shared/template/travel/lang/da.json
rename to assets/shared/templates/travel/lang/da.json
diff --git a/assets/shared/template/travel/travel.scss b/assets/shared/templates/travel/travel.scss
similarity index 96%
rename from assets/shared/template/travel/travel.scss
rename to assets/shared/templates/travel/travel.scss
index 0a860e69d..08021c1ee 100644
--- a/assets/shared/template/travel/travel.scss
+++ b/assets/shared/templates/travel/travel.scss
@@ -1,6 +1,6 @@
@font-face {
font-family: "rubics";
- src: url("./fonts/rubik-regular.woff") format("woff");
+ src: url("fontsubik-regular.woff") format("woff");
}
.grid {
diff --git a/assets/shared/templates/video.json b/assets/shared/templates/video.json
new file mode 100644
index 000000000..e0f092516
--- /dev/null
+++ b/assets/shared/templates/video.json
@@ -0,0 +1,45 @@
+{
+ "title": "Video",
+ "id": "01FQBJFKM0YFX1VW5K94VBSNCC",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "video-form-1",
+ "input": "header",
+ "text": "Skabelon: Video",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "video-form-2",
+ "input": "header-h3",
+ "text": "Indhold",
+ "name": "header2",
+ "formGroupClasses": "h5 mb-3"
+ },
+ {
+ "key": "video-form-video",
+ "input": "file",
+ "text": "Video",
+ "name": "video",
+ "acceptedMimetypes": ["video/webm", "video/mp4"],
+ "formGroupClasses": "h5 mb-3",
+ "helpText": "Følgende formatter accepteres: .webm, mp4"
+ },
+ {
+ "key": "video-form-sound",
+ "input": "checkbox",
+ "label": "Afspil lyd",
+ "name": "sound",
+ "formGroupClasses": "col-md-9 mt-3",
+ "helpText": "Hvis videoen har lyd, skal enheden konfigureres til at automatisk afspilning er muligt."
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ }
+ ]
+}
diff --git a/assets/shared/template/video/video.jsx b/assets/shared/templates/video.jsx
similarity index 75%
rename from assets/shared/template/video/video.jsx
rename to assets/shared/templates/video.jsx
index 1967f8a24..d23d71713 100644
--- a/assets/shared/template/video/video.jsx
+++ b/assets/shared/templates/video.jsx
@@ -1,8 +1,26 @@
import React, { useEffect, useRef } from "react";
-import PropTypes from "prop-types";
-import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./video.scss";
+import { getAllMediaUrlsFromField, ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./video/video.scss";
+import templateConfig from './video.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Video component.
@@ -80,25 +98,4 @@ function Video({ slide, content, run, slideDone, executionId }) {
);
}
-Video.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- instanceId: PropTypes.string,
- mediaData: PropTypes.shape({
- url: PropTypes.string,
- assets: PropTypes.shape({ uri: PropTypes.string }),
- }),
- theme: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- sound: PropTypes.bool,
- video: PropTypes.arrayOf(PropTypes.string),
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default Video;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/video/video.scss b/assets/shared/templates/video/video.scss
similarity index 100%
rename from assets/shared/template/video/video.scss
rename to assets/shared/templates/video/video.scss
diff --git a/assets/shared/templates/vimeo-player.json b/assets/shared/templates/vimeo-player.json
new file mode 100644
index 000000000..299d9abe4
--- /dev/null
+++ b/assets/shared/templates/vimeo-player.json
@@ -0,0 +1,39 @@
+{
+ "title": "Vimeo Player",
+ "id": "01FQBJQ2M3544ZKAADPWBXHY17",
+ "options": {},
+ "adminForm": [
+ {
+ "key": "vimeo-form-1",
+ "input": "header",
+ "text": "Skabelon: Vimeo Video",
+ "name": "header1",
+ "formGroupClasses": "h4 mb-3"
+ },
+ {
+ "key": "vimeo-form-2",
+ "input": "input",
+ "name": "vimeoid",
+ "label": "Vimeo Id",
+ "formGroupClasses": "col-md-6"
+ },
+ {
+ "key": "vimeo-form-3",
+ "input": "duration",
+ "name": "duration",
+ "min": "1",
+ "type": "number",
+ "label": "Varighed (i sekunder)",
+ "helpText": "Her skal du skrive varigheden på slidet.",
+ "required": true,
+ "formGroupClasses": "col-md-6 mb-3"
+ },
+ {
+ "key": "form-media-contain",
+ "input": "checkbox",
+ "name": "mediaContain",
+ "label": "Deaktivér billedbeskæring",
+ "helpText": "Billedet vil blive vist ubeskåret med tomme områder omkring"
+ }
+ ]
+}
diff --git a/assets/shared/template/vimeo-player/vimeo-player.jsx b/assets/shared/templates/vimeo-player.jsx
similarity index 66%
rename from assets/shared/template/vimeo-player/vimeo-player.jsx
rename to assets/shared/templates/vimeo-player.jsx
index e370aa718..6fd5ef65d 100644
--- a/assets/shared/template/vimeo-player/vimeo-player.jsx
+++ b/assets/shared/templates/vimeo-player.jsx
@@ -1,10 +1,28 @@
import React, { useEffect } from "react";
import Vimeo from "@u-wave/react-vimeo"; // eslint-disable-line import/no-unresolved
-import PropTypes from "prop-types";
-import BaseSlideExecution from "../base-slide-execution";
-import { ThemeStyles } from "../slide-util.jsx";
-import "../global-styles.css";
-import "./vimeo.scss";
+import BaseSlideExecution from "../slide-utils/base-slide-execution.js";
+import { ThemeStyles } from "../slide-utils/slide-util.jsx";
+import "../slide-utils/global-styles.css";
+import "./vimeo-player/vimeo-player.scss";
+import templateConfig from './vimeo-player.json';
+
+function id() {
+ return templateConfig.id;
+}
+
+function config() {
+ return templateConfig;
+}
+
+function renderSlide(slide, run, slideDone) {
+ return
+}
/**
* Vimeo Player component.
@@ -51,21 +69,4 @@ function VimeoPlayer({ slide, content, run, slideDone, executionId }) {
);
}
-VimeoPlayer.propTypes = {
- run: PropTypes.string.isRequired,
- slideDone: PropTypes.func.isRequired,
- slide: PropTypes.shape({
- instanceId: PropTypes.string,
- themeData: PropTypes.shape({
- cssStyles: PropTypes.string,
- }),
- }).isRequired,
- content: PropTypes.shape({
- duration: PropTypes.number.isRequired,
- vimeoid: PropTypes.string,
- mediaContain: PropTypes.bool,
- }).isRequired,
- executionId: PropTypes.string.isRequired,
-};
-
-export default VimeoPlayer;
+export default { id, config, renderSlide };
diff --git a/assets/shared/template/vimeo-player/vimeo.scss b/assets/shared/templates/vimeo-player/vimeo-player.scss
similarity index 100%
rename from assets/shared/template/vimeo-player/vimeo.scss
rename to assets/shared/templates/vimeo-player/vimeo-player.scss
diff --git a/assets/template/fixtures/screen-fixtures.js b/assets/template/fixtures/screen-fixtures.js
new file mode 100644
index 000000000..fcbdc8f86
--- /dev/null
+++ b/assets/template/fixtures/screen-fixtures.js
@@ -0,0 +1,50 @@
+import twoBoxes from "../screen-layouts/two-boxes.json";
+import threeBoxes from "../screen-layouts/three-boxes.json";
+import threeBoxesHorizontal from "../screen-layouts/three-boxes-horizontal.json";
+import twoBoxesVertical from "../screen-layouts/two-boxes-vertical.json";
+import touchTemplate from "../screen-layouts/touch-template.json";
+import sixAreas from "../screen-layouts/six-areas.json";
+import fullScreen from "../screen-layouts/full-screen.json";
+import fourAreas from "../screen-layouts/four-areas.json";
+import twoBoxesVerticalReversed from "../screen-layouts/two-boxes-vertical-reversed.json";
+
+const screenFixtures = [
+ {
+ id: "two-split",
+ screenLayout: twoBoxes,
+ },
+ {
+ id: "two-split-vertical",
+ screenLayout: twoBoxesVertical,
+ },
+ {
+ id: "two-split-vertical-reversed",
+ screenLayout: twoBoxesVerticalReversed,
+ },
+ {
+ id: "touch-template",
+ screenLayout: touchTemplate,
+ },
+ {
+ id: "three-split",
+ screenLayout: threeBoxes,
+ },
+ {
+ id: "three-split-horizontal",
+ screenLayout: threeBoxesHorizontal,
+ },
+ {
+ id: "six-areas",
+ screenLayout: sixAreas,
+ },
+ {
+ id: "full-screen",
+ screenLayout: fullScreen,
+ },
+ {
+ id: "four-areas",
+ screenLayout: fourAreas,
+ },
+];
+
+export default screenFixtures;
diff --git a/assets/shared/template/slides.js b/assets/template/fixtures/slide-fixtures.js
similarity index 99%
rename from assets/shared/template/slides.js
rename to assets/template/fixtures/slide-fixtures.js
index 6376a552a..abe134b75 100644
--- a/assets/shared/template/slides.js
+++ b/assets/template/fixtures/slide-fixtures.js
@@ -1,6 +1,6 @@
import dayjs from "dayjs";
-const slides = [
+const slideFixtures = [
{
id: "book-review-0",
templateData: {
@@ -1646,7 +1646,9 @@ const slides = [
},
{
id: "vimeo-player-0",
- type: "vimeo-player",
+ templateData: {
+ id: "01FQBJQ2M3544ZKAADPWBXHY17",
+ },
content: {
duration: 5000,
vimeoid: "882393277",
@@ -1655,4 +1657,4 @@ const slides = [
},
];
-export default slides;
+export default slideFixtures;
diff --git a/assets/shared/template/index.css b/assets/template/index.css
similarity index 100%
rename from assets/shared/template/index.css
rename to assets/template/index.css
diff --git a/assets/shared/template/index.jsx b/assets/template/index.jsx
similarity index 94%
rename from assets/shared/template/index.jsx
rename to assets/template/index.jsx
index 295ffeb49..5bf72e71f 100644
--- a/assets/shared/template/index.jsx
+++ b/assets/template/index.jsx
@@ -4,7 +4,7 @@ import {
createGridArea,
createGrid,
determineGridArea,
-} from "../grid-generator/grid-generator.js";
+} from "../shared/grid-generator/grid-generator.js";
import {
BrowserRouter,
Link,
@@ -13,9 +13,9 @@ import {
useParams,
} from "react-router-dom";
import PropTypes from "prop-types";
-import renderSlide from "./slide.jsx";
-import slides from "./slides";
-import screens from "./screens";
+import { renderSlide } from "../shared/slide-utils/templates.js";
+import slideFixtures from "./fixtures/slide-fixtures.js";
+import screenFixtures from "./fixtures/screen-fixtures.js";
import "./index.css";
export const renderScreen = (screen) => {
@@ -105,11 +105,13 @@ export const Slide = ({slide: inputSlide}) => {
export const DisplayElement = () => {
const {id} = useParams();
- const foundSlide = slides.find((slide) => slide.id === id);
- const foundScreen = screens.find((screen) => screen.id === id);
+ const foundSlide = slideFixtures.find((slide) => slide.id === id);
+ const foundScreen = screenFixtures.find((screen) => screen.id === id);
+
if (foundSlide) {
return
;
}
+
if (foundScreen) {
return ;
}
@@ -133,7 +135,7 @@ export const Overview = () => {
Slideskabeloner
- {slides.map((slide) => (
+ {slideFixtures.map((slide) => (
-
{slide.id}
@@ -143,7 +145,7 @@ export const Overview = () => {
Skærmskabeloner
- {screens.map((screen) => (
+ {screenFixtures.map((screen) => (
-
{screen.id}
diff --git a/assets/shared/template/screen-layouts/four-areas.json b/assets/template/screen-layouts/four-areas.json
similarity index 100%
rename from assets/shared/template/screen-layouts/four-areas.json
rename to assets/template/screen-layouts/four-areas.json
diff --git a/assets/shared/template/screen-layouts/full-screen.json b/assets/template/screen-layouts/full-screen.json
similarity index 100%
rename from assets/shared/template/screen-layouts/full-screen.json
rename to assets/template/screen-layouts/full-screen.json
diff --git a/assets/shared/template/screen-layouts/six-areas.json b/assets/template/screen-layouts/six-areas.json
similarity index 100%
rename from assets/shared/template/screen-layouts/six-areas.json
rename to assets/template/screen-layouts/six-areas.json
diff --git a/assets/shared/template/screen-layouts/three-boxes-horizontal.json b/assets/template/screen-layouts/three-boxes-horizontal.json
similarity index 100%
rename from assets/shared/template/screen-layouts/three-boxes-horizontal.json
rename to assets/template/screen-layouts/three-boxes-horizontal.json
diff --git a/assets/shared/template/screen-layouts/three-boxes.json b/assets/template/screen-layouts/three-boxes.json
similarity index 100%
rename from assets/shared/template/screen-layouts/three-boxes.json
rename to assets/template/screen-layouts/three-boxes.json
diff --git a/assets/shared/template/screen-layouts/touch-template.json b/assets/template/screen-layouts/touch-template.json
similarity index 100%
rename from assets/shared/template/screen-layouts/touch-template.json
rename to assets/template/screen-layouts/touch-template.json
diff --git a/assets/shared/template/screen-layouts/two-boxes-vertical-reversed.json b/assets/template/screen-layouts/two-boxes-vertical-reversed.json
similarity index 100%
rename from assets/shared/template/screen-layouts/two-boxes-vertical-reversed.json
rename to assets/template/screen-layouts/two-boxes-vertical-reversed.json
diff --git a/assets/shared/template/screen-layouts/two-boxes-vertical.json b/assets/template/screen-layouts/two-boxes-vertical.json
similarity index 100%
rename from assets/shared/template/screen-layouts/two-boxes-vertical.json
rename to assets/template/screen-layouts/two-boxes-vertical.json
diff --git a/assets/shared/template/screen-layouts/two-boxes.json b/assets/template/screen-layouts/two-boxes.json
similarity index 100%
rename from assets/shared/template/screen-layouts/two-boxes.json
rename to assets/template/screen-layouts/two-boxes.json
diff --git a/composer.json b/composer.json
index 90708ca71..f2eb1e476 100644
--- a/composer.json
+++ b/composer.json
@@ -31,6 +31,7 @@
"symfony/dom-crawler": "~6.4.0",
"symfony/dotenv": "~6.4.0",
"symfony/expression-language": "~6.4.0",
+ "symfony/finder": "~6.4.0",
"symfony/flex": "^2.0",
"symfony/framework-bundle": "~6.4.0",
"symfony/http-client": "~6.4.0",
diff --git a/composer.lock b/composer.lock
index 3888ba8d1..edaae5488 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "e17b8074ba5e1eee3d1fd7014d14036c",
+ "content-hash": "1bd6fd456477d1e03a03bb4359ad3e77",
"packages": [
{
"name": "api-platform/core",
@@ -6890,16 +6890,16 @@
},
{
"name": "symfony/finder",
- "version": "v6.4.17",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7"
+ "reference": "73089124388c8510efb8d2d1689285d285937b08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7",
- "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/73089124388c8510efb8d2d1689285d285937b08",
+ "reference": "73089124388c8510efb8d2d1689285d285937b08",
"shasum": ""
},
"require": {
@@ -6934,7 +6934,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v6.4.17"
+ "source": "https://github.com/symfony/finder/tree/v6.4.24"
},
"funding": [
{
@@ -6945,12 +6945,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-12-29T13:51:37+00:00"
+ "time": "2025-07-15T12:02:45+00:00"
},
{
"name": "symfony/flex",
diff --git a/docs/custom-templates.md b/docs/custom-templates.md
new file mode 100644
index 000000000..5d5fbf034
--- /dev/null
+++ b/docs/custom-templates.md
@@ -0,0 +1,46 @@
+# Custom Templates
+
+It is possible to include your own templates in your installation.
+
+## Location
+
+Custom templates should be placed in the folder `assets/shared/custom-templates/`.
+This folder is in `.gitignore` so the contents will not be added to the git repository.
+
+How you populate this folder with your custom templates is up to you:
+
+* A git repository with root in the `assets/shared/custom-templates/` folder.
+* A symlink from another folder.
+* Maintaining a fork of the display repository.
+* ...
+
+## Files
+
+The following files are required for a custom template:
+
+* `custom-template-name.jsx` - A javascript module for the template.
+* `custom-template-name.json` - A configuration file for the template.
+
+Replace `custom-template-name` with a unique name for the template.
+
+### custom-template-name.jsx
+
+The `.jsx` should expose the following functions:
+
+* id() - The ULID of the template. Generate a ULID for your custom template.
+* config() - Should contain the following keys: id (as above), title (the titel displayed in the admin), options,
+ adminForm.
+* renderSlide(slide, run, slideDone) - Should return the JSX for the template.
+
+For an example of a custom template see `assets/shared/custom-templates-example/`.
+
+## Contributing template
+
+If you think the template could be used by other, consider contributing the template to the project as a pull request.
+
+### Guide for contributing template
+
+* Fork the `os2display/display` repository.
+* Move your custom template files (the .json and .jsx files and other required files) from the
+ `assets/shared/custom-templates/` folder to the `assets/shared/templates/` folder.
+* Create a PR to `os2display/display` repository.
diff --git a/fixtures/slide.yaml b/fixtures/slide.yaml
index a2f8a70d8..782e8d2a0 100644
--- a/fixtures/slide.yaml
+++ b/fixtures/slide.yaml
@@ -30,7 +30,7 @@ App\Entity\Tenant\Slide:
tenant: "@tenant_xyz"
slide_abc_notified (extends slide):
title: "slide_abc_notified"
- template: "@template_notified"
+ template: "@template_instagram_feed"
content:
maxEntries: 6
tenant: "@tenant_abc"
diff --git a/fixtures/template.yaml b/fixtures/template.yaml
index d8671f62a..edc17fb84 100644
--- a/fixtures/template.yaml
+++ b/fixtures/template.yaml
@@ -1,16 +1,85 @@
---
App\Entity\Template:
- template_image_text:
- title: "template_image_text"
- description: A template with different formats of image and text
- resources:
+ template_book_review:
+ id: ''
+ title: "Anmeldelse"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_calendar:
+ id: ''
+ title: "Kalender"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_contacts:
+ id: ''
+ title: "Kontakter"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_iframe:
+ id: ''
+ title: "IFrame"
createdAt (unique): ''
modifiedAt: ''
+
+ template_image_text:
id: ''
- template_notified:
- title: "template_notified"
- description: A template with different that serves notified data
- resources:
+ title: "Billede og tekst"
createdAt (unique): ''
modifiedAt: ''
+
+ template_instagram_feed:
id: ''
+ title: "Instagram feed"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_news_feed:
+ id: ''
+ title: "Nyheder"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_poster:
+ id: ''
+ title: "Plakat"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_slideshow:
+ id: ''
+ title: "Slideshow"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_rss:
+ id: ''
+ title: "RSS"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_table:
+ id: ''
+ title: "Tabel"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_travel:
+ id: ''
+ title: "Rejseplanen"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_video:
+ id: ''
+ title: "Video"
+ createdAt (unique): ''
+ modifiedAt: ''
+
+ template_vimeo_player:
+ id: ''
+ title: "Vimeo Player"
+ createdAt (unique): ''
+ modifiedAt: ''
diff --git a/migrations/Version20250815092648.php b/migrations/Version20250815092648.php
new file mode 100644
index 000000000..e6833738d
--- /dev/null
+++ b/migrations/Version20250815092648.php
@@ -0,0 +1,28 @@
+addSql('ALTER TABLE template DROP icon, DROP resources, DROP description');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql('ALTER TABLE template ADD icon VARCHAR(255) DEFAULT \'\' NOT NULL, ADD resources JSON NOT NULL COMMENT \'(DC2Type:json)\', ADD description VARCHAR(255) DEFAULT \'\' NOT NULL');
+ }
+}
diff --git a/public/api-spec-v2.json b/public/api-spec-v2.json
index 9439d206c..0a05b36eb 100644
--- a/public/api-spec-v2.json
+++ b/public/api-spec-v2.json
@@ -5878,20 +5878,6 @@
"explode": false,
"allowReserved": false
},
- {
- "name": "description",
- "in": "query",
- "description": "",
- "required": false,
- "deprecated": false,
- "allowEmptyValue": false,
- "schema": {
- "type": "string"
- },
- "style": "form",
- "explode": false,
- "allowReserved": false
- },
{
"name": "createdBy",
"in": "query",
@@ -14649,15 +14635,6 @@
"title": {
"type": "string"
},
- "description": {
- "type": "string"
- },
- "resources": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
"modifiedBy": {
"type": "string"
},
@@ -14686,15 +14663,6 @@
"title": {
"type": "string"
},
- "description": {
- "type": "string"
- },
- "resources": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
"modifiedBy": {
"type": "string"
},
@@ -14758,15 +14726,6 @@
"title": {
"type": "string"
},
- "description": {
- "type": "string"
- },
- "resources": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
"modifiedBy": {
"type": "string"
},
@@ -14830,15 +14789,6 @@
"title": {
"type": "string"
},
- "description": {
- "type": "string"
- },
- "resources": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
"modifiedBy": {
"type": "string"
},
diff --git a/public/api-spec-v2.yaml b/public/api-spec-v2.yaml
index 0391c09e9..687d694aa 100644
--- a/public/api-spec-v2.yaml
+++ b/public/api-spec-v2.yaml
@@ -4164,18 +4164,6 @@ paths:
style: form
explode: false
allowReserved: false
- -
- name: description
- in: query
- description: ''
- required: false
- deprecated: false
- allowEmptyValue: false
- schema:
- type: string
- style: form
- explode: false
- allowReserved: false
-
name: createdBy
in: query
@@ -10214,12 +10202,6 @@ components:
properties:
title:
type: string
- description:
- type: string
- resources:
- type: array
- items:
- type: string
modifiedBy:
type: string
createdBy:
@@ -10240,12 +10222,6 @@ components:
properties:
title:
type: string
- description:
- type: string
- resources:
- type: array
- items:
- type: string
modifiedBy:
type: string
createdBy:
@@ -10289,12 +10265,6 @@ components:
additionalProperties: true
title:
type: string
- description:
- type: string
- resources:
- type: array
- items:
- type: string
modifiedBy:
type: string
createdBy:
@@ -10338,12 +10308,6 @@ components:
type: string
title:
type: string
- description:
- type: string
- resources:
- type: array
- items:
- type: string
modifiedBy:
type: string
createdBy:
diff --git a/src/Command/LoadTemplateCommand.php b/src/Command/LoadTemplateCommand.php
deleted file mode 100644
index 48a3c7a23..000000000
--- a/src/Command/LoadTemplateCommand.php
+++ /dev/null
@@ -1,206 +0,0 @@
-addArgument('filename', InputArgument::REQUIRED, 'json file to load. Can be a local file or a URL');
- $this->addOption('path-from-filename', 'p', InputOption::VALUE_NONE, 'Set path to component and admin from filename. Assumes that the config file loaded has the naming format: [templateName]-config[.*].json.', null);
- $this->addOption('timestamp', 't', InputOption::VALUE_NONE, 'Add a timestamp to the component and admin urls: ?ts=. Only applies if path-from-filename option is active.', null);
- }
-
- final protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $io = new SymfonyStyle($input, $output);
- $successMessage = 'Template updated';
-
- try {
- /** @var string $filename */
- $filename = $input->getArgument('filename');
-
- $content = json_decode(file_get_contents($filename), false, 512, JSON_THROW_ON_ERROR);
-
- // Validate template json.
- $schemaStorage = new SchemaStorage();
- $jsonSchemaObject = $this->getSchema();
- $schemaStorage->addSchema('file://contentSchema', $jsonSchemaObject);
- $validator = new Validator(new Factory($schemaStorage));
- $validator->validate($content, $jsonSchemaObject);
-
- if ($validator->isValid()) {
- $io->info('The supplied JSON validates against the schema.');
- } else {
- $message = "JSON does not validate. Violations:\n";
- foreach ($validator->getErrors() as $error) {
- $message .= sprintf("\n[%s] %s", $error['property'], $error['message']);
- }
-
- $io->error($message);
-
- return Command::INVALID;
- }
-
- if (!Ulid::isValid($content->id)) {
- $io->error('The Ulid is not valid');
-
- return Command::INVALID;
- }
-
- $repository = $this->entityManager->getRepository(Template::class);
- $template = $repository->findOneBy(['id' => Ulid::fromString($content->id)]);
-
- if (!$template) {
- $template = new Template();
- $metadata = $this->entityManager->getClassMetaData($template::class);
- $metadata->setIdGenerator(new AssignedGenerator());
-
- $ulid = Ulid::fromString($content->id);
-
- $template->setId($ulid);
-
- $this->entityManager->persist($template);
- $successMessage = 'Template added';
- }
-
- $template->setIcon($content->icon);
-
- $resources = get_object_vars($content->resources);
-
- if ($input->getOption('path-from-filename')) {
- // Set paths to component and admin from filename.
- $resources['component'] = preg_replace("/-config.*\.json$/", '.js', $filename);
- $resources['admin'] = preg_replace("/-config.*\.json$/", '-admin.json', $filename);
-
- if ($input->getOption('timestamp')) {
- $resources['component'] = $resources['component'].'?ts='.time();
- $resources['admin'] = $resources['admin'].'?ts='.time();
- }
- }
-
- $template->setResources($resources);
- $template->setTitle($content->title);
- $template->setDescription($content->description);
-
- $this->entityManager->flush();
-
- $io->success($successMessage);
-
- return Command::SUCCESS;
- } catch (\JsonException) {
- $io->error('Invalid json');
-
- return Command::INVALID;
- }
- }
-
- /**
- * Supplies json schema for validation.
- *
- * @return mixed
- * Json schema
- *
- * @throws \JsonException
- */
- private function getSchema(): mixed
- {
- $jsonSchema = <<<'JSON'
- {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://os2display.dk/config-schema.json",
- "title": "Config file schema",
- "description": "Schema for defining config files for templates",
- "type": "object",
- "properties": {
- "id": {
- "description": "Ulid",
- "type": "string"
- },
- "title": {
- "description": "The title of the template",
- "type": "string"
- },
- "description": {
- "description": "A description of the template",
- "type": "string"
- },
- "icon": {
- "description": "An icon for the template",
- "type": "string"
- },
- "resources": {
- "type": "object",
- "properties": {
- "schema": {
- "description": "Path to the json schema for the content",
- "type": "string"
- },
- "component": {
- "description": "Path to the react remote component that renders the content",
- "type": "string"
- },
- "admin": {
- "description": "Path to the json array describing the content form in the administration interface",
- "type": "string"
- },
- "assets": {
- "description": "Assets that are needed by the template",
- "type": "array",
- "items": {
- "type": "object",
- "description": "Asset item",
- "properties": {
- "type": {
- "type": "string",
- "url": "string"
- }
- }
- }
- },
- "options": {
- "description": "Default option values for the template",
- "type": "object"
- },
- "content": {
- "description": "Default content values for the template",
- "type": "object"
- }
- },
- "required": ["schema", "component", "admin", "assets", "options", "content"]
- }
- },
- "required": ["id", "icon", "description", "resources", "title"]
- }
- JSON;
-
- return json_decode($jsonSchema, false, 512, JSON_THROW_ON_ERROR);
- }
-}
diff --git a/src/Command/TemplatesInstallCommand.php b/src/Command/TemplatesInstallCommand.php
new file mode 100644
index 000000000..c25fe1292
--- /dev/null
+++ b/src/Command/TemplatesInstallCommand.php
@@ -0,0 +1,78 @@
+addOption('all', 'a', InputOption::VALUE_NONE, 'Install all available templates');
+ $this->addOption('update', 'u', InputOption::VALUE_NONE, 'Update already installed templates');
+ $this->addArgument('templateUlid', InputArgument::OPTIONAL, 'Install the template with the given ULID');
+ }
+
+ final protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $all = $input->getOption('all');
+ $update = $input->getOption('update');
+
+ $templates = $this->templateService->getAllTemplates();
+
+ if ($all) {
+ foreach ($templates as $templateToInstall) {
+ $this->templateService->installTemplate($templateToInstall, $update);
+ }
+
+ $io->success('Installed all available templates');
+
+ return Command::SUCCESS;
+ }
+
+ $templateUlid = $input->getArgument('templateUlid');
+
+ if (null === $templateUlid) {
+ $io->warning('Template ULID not supplied.');
+
+ return Command::INVALID;
+ }
+
+ $templatesFound = array_find($templates, fn (TemplateData $templateData): bool => $templateData->id === $templateUlid);
+
+ if (1 !== count($templatesFound)) {
+ $io->error('Template not found.');
+
+ return Command::FAILURE;
+ }
+
+ $templateToInstall = $templatesFound[0];
+
+ $this->templateService->installTemplate($templateToInstall);
+ $io->success('Template '.$templateToInstall->title.' installed');
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/src/Command/TemplatesListCommand.php b/src/Command/TemplatesListCommand.php
new file mode 100644
index 000000000..4466c9d28
--- /dev/null
+++ b/src/Command/TemplatesListCommand.php
@@ -0,0 +1,56 @@
+templateService->getCoreTemplates();
+
+ if (0 === count($templates)) {
+ $io->error('No core templates found.');
+
+ return Command::INVALID;
+ }
+
+ $customTemplates = $this->templateService->getCustomTemplates();
+
+ $io->table(['ID', 'Title', 'Status', 'Type'], array_map(fn (TemplateData $templateData) => [
+ $templateData->id,
+ $templateData->title,
+ $templateData->installed ? 'Installed' : 'Not Installed',
+ $templateData->type,
+ ], array_merge($templates, $customTemplates)));
+
+ return Command::SUCCESS;
+ } catch (\Exception $e) {
+ $io->error($e->getMessage());
+
+ return Command::INVALID;
+ }
+ }
+}
diff --git a/src/Command/UpdateCommand.php b/src/Command/UpdateCommand.php
new file mode 100644
index 000000000..9e85424cb
--- /dev/null
+++ b/src/Command/UpdateCommand.php
@@ -0,0 +1,12 @@
+ ''])]
- private string $icon = '';
-
- #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON)]
- private array $resources = [];
+ #[ORM\Column(type: Types::STRING, length: 255, nullable: false, options: ['default' => ''])]
+ private string $title = '';
/**
* @var Collection
@@ -41,30 +37,8 @@ public function __construct()
{
$this->slides = new ArrayCollection();
$this->tenants = new ArrayCollection();
- }
-
- public function getIcon(): string
- {
- return $this->icon;
- }
- public function setIcon(string $icon): self
- {
- $this->icon = $icon;
-
- return $this;
- }
-
- public function getResources(): array
- {
- return $this->resources;
- }
-
- public function setResources(array $resources): self
- {
- $this->resources = $resources;
-
- return $this;
+ parent::__construct();
}
/**
@@ -110,4 +84,14 @@ public function removeAllSlides(): self
return $this;
}
+
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ public function setTitle(string $title): void
+ {
+ $this->title = $title;
+ }
}
diff --git a/src/Model/TemplateData.php b/src/Model/TemplateData.php
new file mode 100644
index 000000000..6b01484f0
--- /dev/null
+++ b/src/Model/TemplateData.php
@@ -0,0 +1,20 @@
+templateEntity;
+
+ if (null === $template) {
+ $template = new Template();
+
+ $metadata = $this->entityManager->getClassMetaData($template::class);
+ $metadata->setIdGenerator(new AssignedGenerator());
+
+ $ulid = Ulid::fromString($templateData->id);
+ $template->setId($ulid);
+
+ $this->entityManager->persist($template);
+ }
+
+ if ($update) {
+ $template->setTitle($templateData->title);
+ }
+
+ $this->entityManager->flush();
+ }
+
+ public function getAllTemplates(): array
+ {
+ return array_merge($this->getCoreTemplates(), $this->getCustomTemplates());
+ }
+
+ public function getCoreTemplates(): array
+ {
+ $finder = new Finder();
+
+ if (is_dir('assets/shared/templates')) {
+ $finder->files()->followLinks()->ignoreUnreadableDirs()->in('assets/shared/templates')->depth('== 0')->name('*.json');
+
+ if ($finder->hasResults()) {
+ return $this->getTemplates($finder);
+ }
+ }
+
+ return [];
+ }
+
+ public function getCustomTemplates(): array
+ {
+ $finder = new Finder();
+
+ if (is_dir('assets/shared/custom-templates')) {
+ $finder->files()->followLinks()->ignoreUnreadableDirs()->in('assets/shared/custom-templates')->depth('== 0')->name('*.json');
+
+ if ($finder->hasResults()) {
+ return $this->getTemplates($finder, true);
+ }
+ }
+
+ return [];
+ }
+
+ public function getTemplates(iterable $finder, bool $customTemplates = false): array
+ {
+ $templates = [];
+
+ // Validate template json.
+ $schemaStorage = new SchemaStorage();
+ $jsonSchemaObject = $this->getSchema();
+ $schemaStorage->addSchema('file://contentSchema', $jsonSchemaObject);
+ $validator = new Validator(new Factory($schemaStorage));
+
+ foreach ($finder as $file) {
+ $content = json_decode((string) $file->getContents());
+ $validator->validate($content, $jsonSchemaObject);
+
+ if (!$validator->isValid()) {
+ $message = 'JSON file '.$file->getFilename()." does not validate. Violations:\n";
+ foreach ($validator->getErrors() as $error) {
+ $message .= sprintf("\n[%s] %s", $error['property'], $error['message']);
+ }
+
+ throw new \Exception($message);
+ }
+
+ if (!Ulid::isValid($content->id)) {
+ throw new \Exception('The Ulid is not valid');
+ }
+
+ $repository = $this->entityManager->getRepository(Template::class);
+ $template = $repository->findOneBy(['id' => Ulid::fromString($content->id)]);
+
+ $templates[] = new TemplateData(
+ $content->id,
+ $content->title,
+ $content->adminForm,
+ $content->options,
+ $template,
+ null !== $template,
+ $customTemplates ? 'Custom' : 'Core',
+ );
+ }
+
+ return $templates;
+ }
+
+ /**
+ * Supplies json schema for validation.
+ *
+ * @return mixed
+ * Json schema
+ *
+ * @throws \JsonException
+ */
+ public function getSchema(): object
+ {
+ $jsonSchema = <<<'JSON'
+ {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://os2display.dk/config-schema.json",
+ "title": "Config file schema",
+ "description": "Schema for defining config files for templates",
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "Ulid",
+ "type": "string"
+ },
+ "title": {
+ "description": "The title of the template",
+ "type": "string"
+ },
+ "options": {
+ "description": "Template options",
+ "type": "object"
+ },
+ "adminForm": {
+ "description": "The admin form description",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Form element"
+ }
+ }
+ },
+ "required": ["id", "title", "options", "adminForm"]
+ }
+ JSON;
+
+ return json_decode($jsonSchema, false, 512, JSON_THROW_ON_ERROR);
+ }
+}
diff --git a/src/State/TemplateProvider.php b/src/State/TemplateProvider.php
index 6a5178775..c124e0c81 100644
--- a/src/State/TemplateProvider.php
+++ b/src/State/TemplateProvider.php
@@ -24,12 +24,10 @@ public function toOutput(object $object): TemplateDTO
$output = new TemplateDTO();
$output->id = $object->getId();
$output->title = $object->getTitle();
- $output->description = $object->getDescription();
$output->modified = $object->getModifiedAt();
$output->created = $object->getCreatedAt();
$output->modifiedBy = $object->getModifiedBy();
$output->createdBy = $object->getCreatedBy();
- $output->resources = $object->getResources();
return $output;
}
diff --git a/tests/Api/TemplatesTest.php b/tests/Api/TemplatesTest.php
index a1318c067..46a1cf34a 100644
--- a/tests/Api/TemplatesTest.php
+++ b/tests/Api/TemplatesTest.php
@@ -19,14 +19,14 @@ public function testGetCollection(): void
'@context' => '/contexts/Template',
'@id' => '/v2/templates',
'@type' => 'hydra:Collection',
- 'hydra:totalItems' => 2,
+ 'hydra:totalItems' => 14,
'hydra:view' => [
- '@id' => '/v2/templates?itemsPerPage=5',
+ '@id' => '/v2/templates?itemsPerPage=5&page=1',
'@type' => 'hydra:PartialCollectionView',
],
]);
- $this->assertCount(2, $response->toArray()['hydra:member']);
+ $this->assertCount(5, $response->toArray()['hydra:member']);
// @TODO: resources: Object value found, but an array is required. In JSON it's an object but in the entity
// it's an key array? So this test will fail.
diff --git a/tests/EventListener/RelationsChecksumListenerTest.php b/tests/EventListener/RelationsChecksumListenerTest.php
index c94aa6154..56da30fe7 100644
--- a/tests/EventListener/RelationsChecksumListenerTest.php
+++ b/tests/EventListener/RelationsChecksumListenerTest.php
@@ -175,7 +175,7 @@ public function testPersistSlide(): void
$media = $this->em->getRepository(Tenant\Media::class)->findOneBy(['tenant' => $tenant]);
$feedSource = $this->em->getRepository(Tenant\FeedSource::class)->findOneBy(['tenant' => $tenant]);
$theme = $this->em->getRepository(Tenant\Theme::class)->findOneBy(['tenant' => $tenant]);
- $template = $this->em->getRepository(Template::class)->findOneBy(['title' => 'template_image_text']);
+ $template = $this->em->getRepository(Template::class)->findOneBy(['title' => 'Billede og tekst']);
$feed = new Tenant\Feed();
$feed->setTenant($tenant);
diff --git a/vite.config.js b/vite.config.js
index 71e961190..20d4ae98b 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -3,7 +3,7 @@ import symfonyPlugin from "vite-plugin-symfony";
import react from "@vitejs/plugin-react-oxc";
import svgr from "vite-plugin-svgr";
-export default defineConfig(({ command }) => {
+export default defineConfig(() => {
return {
base: "/build",
css: {
@@ -35,7 +35,7 @@ export default defineConfig(({ command }) => {
input: {
admin: "./assets/admin/index.jsx",
client: "./assets/client/index.jsx",
- template: "./assets/shared/template/index.jsx"
+ template: "./assets/template/index.jsx"
}
}
},