diff --git a/components/content/Entryscape/ConceptPage/index.tsx b/components/content/Entryscape/ConceptPage/index.tsx index 82df7165..eeffff43 100644 --- a/components/content/Entryscape/ConceptPage/index.tsx +++ b/components/content/Entryscape/ConceptPage/index.tsx @@ -4,41 +4,36 @@ import { SettingsContext } from "@/providers/SettingsProvider"; import { EntrystoreContext } from "@/providers/EntrystoreProvider"; import { useRouter } from "next/router"; import Head from "next/head"; -import { hemvist, linkBase } from "@/utilities"; +import { linkBase } from "@/utilities"; import { Heading } from "@/components/global/Typography/Heading"; import { Container } from "@/components/layout/Container"; import { Preamble } from "@/components/global/Typography/Preamble"; import Link from "next/link"; +import { useEntryScapeBlocks } from "@/hooks/useEntryScapeBlocks"; export const ConceptPage: FC<{ curi?: string; uri?: string }> = ({ curi, uri, }) => { - let postscribe: any; - const { env, setBreadcrumb } = useContext(SettingsContext); + const { setBreadcrumb, iconSize } = useContext(SettingsContext); const entry = useContext(EntrystoreContext); const { lang, t } = useTranslation(); const { pathname } = useRouter() || {}; - - /** - * Async load scripts requiered for EntryScape blocks, - * or else blocks won't have access to DOM - */ - useEffect(() => { - //we need to reload the page when using the back/forward buttons to a blocks rendered page - if (typeof window !== "undefined") { - //check if reffereing search params is set to hash - if ( - window.location && - window.location.hash && - window.location.hash.includes("ref=?") - ) - window.onpopstate = () => { - window.location.reload(); - }; - } - addScripts(); - }, [pathname]); + const isTerminology = + pathname.startsWith("/terminology") || + pathname.startsWith("/externalterminology"); + + useEntryScapeBlocks({ + entrystoreBase: entry.entrystore.getBaseURI(), + env: entry.env, + lang, + curi, + uri, + iconSize, + pageType: isTerminology ? "terminology" : "concept", + context: entry.context, + esId: entry.esId, + }); useEffect(() => { setBreadcrumb && @@ -54,359 +49,6 @@ export const ConceptPage: FC<{ curi?: string; uri?: string }> = ({ }); }, [pathname, entry.title]); - const addScripts = () => { - if (typeof window !== "undefined") { - postscribe = (window as any).postscribe; - - if (curi || uri) { - postscribe( - "#scriptsPlaceholder", - ` - - - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( @@ -426,25 +68,23 @@ export const ConceptPage: FC<{ curi?: string; uri?: string }> = ({
{/* Left column */}
- {entry.publisher ? ( + {entry.publisher && ( {entry.publisher} - ) : entry.termPublisher ? ( - {entry.termPublisher} - ) : null} + )} {entry.description !== "" && (

{entry.description}

)} - {entry.definition !== "" && ( -

{entry.definition}

- )}
- +
{/* Right column */} @@ -454,84 +94,97 @@ export const ConceptPage: FC<{ curi?: string; uri?: string }> = ({ size={"sm"} className="mb-sm font-strong text-textSecondary md:mb-md" > - {pathname.startsWith("/terminology") || - pathname.startsWith("/externalterminology") + {isTerminology ? t("pages|concept_page$about_terminology") : t("pages|concept_page$about_concept")} - - {entry.hasResource && entry.hasResource?.length > 0 && ( + +
- {t("pages|datasetpage$related_specifications")} + {isTerminology + ? t("pages|concept_page$term_adress") + : t("pages|concept_page$concept_adress")} - {entry.hasResource.map(({ title, url }, idx) => ( + + + {entry.address} + +
+ + {entry.relatedSpecifications && + entry.relatedSpecifications?.length > 0 && ( +
+ + {t("pages|datasetpage$related_specifications")} + + {entry.relatedSpecifications.map(({ title, url }, idx) => ( + + {title} + + ))} +
+ )} + + {entry.relatedTerm && ( +
+ + {t("pages|concept_page$terminology_concept")} + - {title} + {entry.relatedTerm.title} - ))} -
- )} - - - {/* Download formats */} - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( @@ -145,20 +68,7 @@ export const DataServicePage: React.FC<{
{/* Left column */}
- {/* Publisher */} - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( @@ -493,6 +166,7 @@ export const DataSetPage: React.FC = () => {
+ {/* Right column */}
{/* About dataset - wrapper */} @@ -505,37 +179,80 @@ export const DataSetPage: React.FC = () => { {t("pages|datasetpage$about-dataset")} - {/* About dataset */} -
- {entry.conformsTo && entry.conformsTo.length > 0 && ( -
- - {t("pages|datasetpage$related_specifications")} - - {relatedSpecs?.map((spec, idx) => ( - + {/* About dataset */} +
+ {/* TODO: Fix the order of the datasets so the keywords can be + handled the same way on all pages */} + {/* + {entry.keywords && entry.keywords?.length > 0 && ( +
+ - {spec.title} - - ))} - {entry.conformsTo?.length > 4 && ( + {t("pages|datasetpage$keyword")} + +
+ {keywords?.map((k, idx) => ( + + {k} + + ))} +
+ )} + */} + {entry.relatedSpecifications && + entry.relatedSpecifications.length > 0 && ( +
+ + {t("pages|datasetpage$related_specifications")} + + {relatedSpecs?.map((spec, idx) => ( + + {spec.title} + + ))} + {entry.relatedSpecifications?.length > 4 && ( +
)} -
- )} +
{/* Catalog informaton wrapper */} @@ -547,89 +264,64 @@ export const DataSetPage: React.FC = () => { > {t("pages|datasetpage$catalog")} - {entry.mqaCatalog && ( - <> -

- {t("pages|datasetpage$mqa-catalog")} -

- - {entry.mqaCatalog} - - - )} -
- - {/* Catalog */} -
- - - {/* Download formats */} - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( diff --git a/components/content/Entryscape/MQACategoryPage/index.tsx b/components/content/Entryscape/MQACategoryPage/index.tsx index 8a6c5850..24188aba 100644 --- a/components/content/Entryscape/MQACategoryPage/index.tsx +++ b/components/content/Entryscape/MQACategoryPage/index.tsx @@ -4,24 +4,21 @@ import { usePathname } from "next/navigation"; import { FC, useContext, useEffect } from "react"; import { linkBase } from "@/utilities"; import { EntrystoreContext } from "@/providers/EntrystoreProvider"; +import { useEntryScapeBlocks } from "@/hooks/useEntryScapeBlocks"; export const MQACategoryPage: FC = () => { const entry = useContext(EntrystoreContext); - const { env, setBreadcrumb } = useContext(SettingsContext); + const { setBreadcrumb } = useContext(SettingsContext); const pathname = usePathname(); - const ids = pathname.split("/"); - const eid = ids[3]; - const cid = ids[4]; - let postscribe: any; - - /** - * Async load scripts requiered for EntryScape blocks, - * or else blocks wont have access to DOM - */ - useEffect(() => { - addScripts(); - }, []); + useEntryScapeBlocks({ + entrystoreBase: entry.env.ENTRYSCAPE_MQA_PATH, + env: entry.env, + lang: "sv", + pageType: "mqa", + context: entry.context, + esId: entry.esId, + }); useEffect(() => { setBreadcrumb && @@ -40,63 +37,13 @@ export const MQACategoryPage: FC = () => { }); }, [pathname, entry.title]); - /* This is kept for when the pathname url should be improved */ - const addScripts = () => { - if (typeof window !== "undefined") { - postscribe = (window as any).postscribe; - - if (entry) { - postscribe( - "#scriptsPlaceholder", - ` - - - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( -
+
+
); }; diff --git a/components/content/Entryscape/MQAPage/index.tsx b/components/content/Entryscape/MQAPage/index.tsx index 9d114138..ce230a98 100644 --- a/components/content/Entryscape/MQAPage/index.tsx +++ b/components/content/Entryscape/MQAPage/index.tsx @@ -4,34 +4,23 @@ import { Container } from "@/components/layout/Container"; import { SettingsContext } from "@/providers/SettingsProvider"; import { linkBase } from "@/utilities"; import { usePathname } from "next/navigation"; +import { useEntryScapeBlocks } from "@/hooks/useEntryScapeBlocks"; +import useTranslation from "next-translate/useTranslation"; export const MQAPage: FC = () => { const { env, setBreadcrumb } = useContext(SettingsContext); + const { lang } = useTranslation(); const pathname = usePathname(); const pageTitle = "Metadatakvalitet per katalog"; - /** - * Async load scripts requiered for EntryScape blocks, - * or else blocks wont have access to DOM - */ - useEffect(() => { - const script1 = document.createElement("script"); - script1.src = env.ENTRYSCAPE_MQA_URL; - - script1.async = true; - document.body.appendChild(script1); - - const script2 = document.createElement("script"); - script2.src = env.ENTRYSCAPE_BLOCKS_URL; - script2.async = true; - document.body.appendChild(script2); - - return () => { - document.body.removeChild(script1); - document.body.removeChild(script2); - }; - }, []); - + useEntryScapeBlocks({ + entrystoreBase: `https://${env.ENTRYSCAPE_MQA_PATH}/store`, + env: env, + lang: lang, + pageType: "mqa", + context: "", + esId: "", + }); useEffect(() => { setBreadcrumb && setBreadcrumb({ @@ -49,10 +38,9 @@ export const MQAPage: FC = () => {
+ /> -
+
); }; diff --git a/components/content/Entryscape/SpecificationPage/index.tsx b/components/content/Entryscape/SpecificationPage/index.tsx index 377afcf0..b42089cd 100644 --- a/components/content/Entryscape/SpecificationPage/index.tsx +++ b/components/content/Entryscape/SpecificationPage/index.tsx @@ -4,44 +4,42 @@ import { EntrystoreContext } from "@/providers/EntrystoreProvider"; import { SettingsContext } from "@/providers/SettingsProvider"; import { useRouter } from "next/router"; import Head from "next/head"; -import { hemvist, keyword, linkBase } from "@/utilities"; +import { linkBase } from "@/utilities"; import { Container } from "@/components/layout/Container"; import { Heading } from "@/components/global/Typography/Heading"; import { Preamble } from "@/components/global/Typography/Preamble"; import { Button } from "@/components/global/Button"; +import { useEntryScapeBlocks } from "@/hooks/useEntryScapeBlocks"; +import Link from "next/link"; export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({ curi, uri, }) => { - const { env, setBreadcrumb, iconSize } = useContext(SettingsContext); + const { setBreadcrumb, iconSize } = useContext(SettingsContext); const entry = useContext(EntrystoreContext); const { lang, t } = useTranslation(); const { pathname } = useRouter() || {}; const [showAllDatasets, setShowAllDatasets] = useState(false); + const [showAllKeywords, setShowAllKeywords] = useState(false); const relatedDatasets = showAllDatasets - ? entry.conformsTo - : entry.conformsTo?.slice(0, 4); - - /** - * Async load scripts requiered for EntryScape blocks, - * or else blocks wont have access to DOM - */ - useEffect(() => { - //we need to reload the page when using the back/forward buttons to a blocks rendered page - if (typeof window !== "undefined") { - //check if reffereing search params is set to hash - if ( - window.location && - window.location.hash && - window.location.hash.includes("ref=?") - ) - window.onpopstate = () => { - window.location.reload(); - }; - } - addScripts(); - }, []); + ? entry.relatedDatasets + : entry.relatedDatasets?.slice(0, 4); + const keywords = showAllKeywords + ? entry.keywords + : entry.keywords?.slice(0, 4); + + useEntryScapeBlocks({ + entrystoreBase: entry.entrystore.getBaseURI(), + env: entry.env, + lang, + curi, + uri, + iconSize, + pageType: "specification", + context: entry.context, + esId: entry.esId, + }); useEffect(() => { setBreadcrumb && @@ -60,220 +58,6 @@ export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({ }); }, [pathname, entry.title]); - const addScripts = () => { - if (typeof window !== "undefined") { - const postscribe = (window as any).postscribe; - - if (curi || uri) { - postscribe( - "#scriptsPlaceholder", - - ` - - - - - - - `, - { - done: function () {}, - }, - ); - } - } - }; - return ( @@ -289,6 +73,7 @@ export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({ content={`${entry.title} - Sveriges dataportal`} /> +
{entry.title} @@ -300,11 +85,9 @@ export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({ {entry.publisher} )} - + + {entry.description} + {t("pages|specification_page$resource_specification")} @@ -328,6 +111,7 @@ export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({

+ {/* End left column */} {/* Right column */}
@@ -339,48 +123,125 @@ export const SpecificationPage: FC<{ curi?: string; uri?: string }> = ({ > {t("pages|specification_page$about_specification")} - -
-
-
-
- {entry.conformsTo && entry.conformsTo.length > 0 && ( -
- - {t("pages|specification_page$related_datasets")} - - {relatedDatasets?.map((ds, idx) => ( - +
+ - {ds.title} - - ))} - {entry.conformsTo?.length > 4 && ( -
+ + {entry.keywords && entry.keywords?.length > 0 && ( +
+ + {t("pages|datasetpage$keyword")} + +
+ {keywords?.map((k, idx) => ( + + {k} + + ))} +
+
+ )} + +
+ +
+ + {entry.relatedDatasets && entry.relatedDatasets.length > 0 && ( +
+ + {t("pages|specification_page$related_datasets")} + + {relatedDatasets?.map((ds, idx) => ( + + {ds.title} + + ))} + {entry.relatedDatasets?.length > 4 && ( +
+ )} + + {/* Download formats */} + {entry.downloadFormats && entry.downloadFormats?.length > 0 && ( +
+ + {t("pages|datasetpage$download_link")} + +
+ {entry.downloadFormats.map(({ title, url }, idx) => ( + + {title} + + ))} +
+
)}
- )} + {/* End right column */} +
diff --git a/components/global/Typography/Heading/index.tsx b/components/global/Typography/Heading/index.tsx index 2e8b63e6..d3371195 100644 --- a/components/global/Typography/Heading/index.tsx +++ b/components/global/Typography/Heading/index.tsx @@ -8,6 +8,7 @@ const headingVariants = cva(["text-wrap"], { md: ["text-lg md:text-xl"], sm: ["text-md md:text-lg"], xs: ["text-sm md:text-md"], + xxs: ["text-sm mb-sm"], }, }, defaultVariants: { diff --git a/env/EnvSettings.ts b/env/EnvSettings.ts index 98f447cc..0e142a2c 100644 --- a/env/EnvSettings.ts +++ b/env/EnvSettings.ts @@ -15,7 +15,8 @@ export abstract class EnvSettings { abstract ENTRYSCAPE_BLOCKS_URL: string; - abstract ENTRYSCAPE_MQA_URL: string; + abstract ENTRYSCAPE_MQA_SV_URL: string; + abstract ENTRYSCAPE_MQA_EN_URL: string; abstract ENTRYSCAPE_OPENDATA_SV_URL: string; diff --git a/env/Settings.Dev.ts b/env/Settings.Dev.ts index 9862f8d5..6416496e 100644 --- a/env/Settings.Dev.ts +++ b/env/Settings.Dev.ts @@ -22,8 +22,10 @@ export class Settings_Dev extends EnvSettings { ENTRYSCAPE_BLOCKS_URL = "https://static.cdn.entryscape.com/blocks/1/app.js"; - ENTRYSCAPE_MQA_URL = + ENTRYSCAPE_MQA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-sv.js"; + ENTRYSCAPE_MQA_EN_URL = + "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-en.js"; ENTRYSCAPE_OPENDATA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/opendata-sv.js"; diff --git a/env/Settings.Prod.ts b/env/Settings.Prod.ts index c2056597..10dc5609 100644 --- a/env/Settings.Prod.ts +++ b/env/Settings.Prod.ts @@ -20,8 +20,10 @@ export class Settings_Prod extends EnvSettings { ENTRYSCAPE_BLOCKS_URL = "https://static.cdn.entryscape.com/blocks/1/app.js"; - ENTRYSCAPE_MQA_URL = + ENTRYSCAPE_MQA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-sv.js"; + ENTRYSCAPE_MQA_EN_URL = + "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-en.js"; ENTRYSCAPE_OPENDATA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/opendata-sv.js"; diff --git a/env/Settings.Sandbox.ts b/env/Settings.Sandbox.ts index d2285a12..2d606242 100644 --- a/env/Settings.Sandbox.ts +++ b/env/Settings.Sandbox.ts @@ -22,8 +22,10 @@ export class Settings_Sandbox extends EnvSettings { ENTRYSCAPE_BLOCKS_URL = "https://static.cdn.entryscape.com/blocks/0.20/app.js"; - ENTRYSCAPE_MQA_URL = + ENTRYSCAPE_MQA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-sv.js"; + ENTRYSCAPE_MQA_EN_URL = + "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-en.js"; ENTRYSCAPE_OPENDATA_SV_URL = "https://static.entryscape.com/blocks/0.20/ext/opendata-sv.js"; diff --git a/env/Settings.Test.ts b/env/Settings.Test.ts index 77d4be82..a43c69b0 100644 --- a/env/Settings.Test.ts +++ b/env/Settings.Test.ts @@ -21,8 +21,10 @@ export class Settings_Test extends EnvSettings { ENTRYSCAPE_BLOCKS_URL = "https://static.cdn.entryscape.com/blocks/0.20/app.js"; - ENTRYSCAPE_MQA_URL = + ENTRYSCAPE_MQA_SV_URL = "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-sv.js"; + ENTRYSCAPE_MQA_EN_URL = + "https://static.cdn.entryscape.com/blocks-ext/1/opendata/mqa-en.js"; ENTRYSCAPE_OPENDATA_SV_URL = "https://static.entryscape.com/blocks/0.20/ext/opendata-sv.js"; diff --git a/hooks/useEntryScapeBlocks.ts b/hooks/useEntryScapeBlocks.ts new file mode 100644 index 00000000..ebe61da5 --- /dev/null +++ b/hooks/useEntryScapeBlocks.ts @@ -0,0 +1,144 @@ +import { useEffect } from "react"; +import { EnvSettings } from "@/env/EnvSettings"; +import { createBlocksConfig } from "@/utilities/entryscape/blocks/config"; +import useTranslation from "next-translate/useTranslation"; + +interface BlocksConfig { + entrystoreBase: string; + context: string; + esId: string; + env: EnvSettings; + lang: string; + curi?: string; + uri?: string; + iconSize?: number; + pageType: + | "specification" + | "dataset" + | "concept" + | "terminology" + | "dataservice" + | "apiexplore" + | "mqa"; +} + +export const useEntryScapeBlocks = ({ + entrystoreBase, + env, + lang, + curi, + uri, + iconSize, + pageType, + context, + esId, +}: BlocksConfig) => { + const { t } = useTranslation(); + + useEffect(() => { + //this is to make sure that the correct entryscape blocks are loaded when navigating to a new page + const handleClick = (event: MouseEvent) => { + const link = (event.target as HTMLElement).closest("a"); + + if (link && link.href.startsWith(window.origin)) { + event.preventDefault(); + window.location.href = link.href; + } + }; + + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("click", handleClick); + }; + }, []); + + useEffect(() => { + // Create the ready promise if it doesn't exist + if (!(window as any).__entryscape_blocks_ready) { + (window as any).__entryscape_blocks_ready = new Promise((resolve) => { + (window as any).__entryscape_blocks_resolve = resolve; + }); + } + + const initializeBlocks = async () => { + try { + const newConfig = createBlocksConfig({ + entrystoreBase, + env, + lang, + curi, + uri, + iconSize, + t, + pageType, + context, + esId, + }); + + (window as any).__entryscape_config = ( + (window as any).__entryscape_config || [] + ).concat(newConfig); + + if (pageType !== "mqa") { + await loadScript( + lang === "sv" + ? env.ENTRYSCAPE_OPENDATA_SV_URL + : env.ENTRYSCAPE_OPENDATA_EN_URL, + ); + } else { + await loadScript( + lang === "sv" + ? env.ENTRYSCAPE_MQA_SV_URL + : env.ENTRYSCAPE_MQA_EN_URL, + ); + } + + await loadScript(env.ENTRYSCAPE_BLOCKS_URL); + + // Wait for blocks to be ready + await (window as any).__entryscape_blocks_ready; + + if ((window as any).__entryscape_blocks) { + (window as any).__entryscape_blocks.init(); + } + } catch (error) { + console.error("Error initializing EntryScape blocks:", error); + } + }; + + initializeBlocks(); + + return () => { + (window as any).__entryscape_config = []; + if ((window as any).__entryscape_blocks?.clear) { + (window as any).__entryscape_blocks.clear(); + } + }; + }, []); +}; + +const loadScript = (url: string): Promise => { + return new Promise((resolve, reject) => { + // Get the scriptsPlaceholder element + const container = document.getElementById("scriptsPlaceholder"); + if (!container) { + reject(new Error("scriptsPlaceholder element not found")); + return; + } + + // Check if script already exists + const existingScript = document.querySelector(`script[src="${url}"]`); + if (existingScript) { + resolve(); + return; + } + + const script = document.createElement("script"); + script.src = url; + script.async = false; + script.onload = () => resolve(); + script.onerror = reject; + container.appendChild(script); // Append to container instead of body + }); +}; diff --git a/hooks/useScript.ts b/hooks/useScript.ts deleted file mode 100644 index 65ebae33..00000000 --- a/hooks/useScript.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { useState, useEffect } from "react"; - -// Hook -export const useScript = ( - src: string, - integrity?: string, - crossOrigin?: string, -) => { - // Keep track of script status ("idle", "loading", "ready", "error") - const [status, setStatus] = useState(src ? "loading" : "idle"); - - useEffect( - () => { - // Allow falsy src value if waiting on other data needed for - // constructing the script URL passed to this hook. - if (!src) { - setStatus("idle"); - return; - } - - // Fetch existing script element by src - // It may have been added by another intance of this hook - const existingScript: HTMLScriptElement | null = document.querySelector( - `script[src="${src}"]`, - ); - let script: HTMLScriptElement; - - if (!existingScript) { - // Create script - script = document.createElement("script"); - script.src = src; - script.async = true; - integrity && (script.integrity = integrity); - crossOrigin && (script.crossOrigin = crossOrigin); - script.setAttribute("data-status", "loading"); - // Add script to document body - document.body.appendChild(script); - - // Store status in attribute on script - // This can be read by other instances of this hook - const setAttributeFromEvent = (event: any) => { - script.setAttribute( - "data-status", - event.type === "load" ? "ready" : "error", - ); - }; - - script.addEventListener("load", setAttributeFromEvent); - script.addEventListener("error", setAttributeFromEvent); - } else { - script = existingScript; - // Grab existing script status from attribute and set to state. - setStatus(existingScript.getAttribute("data-status") || "error"); - } - - // Script event handler to update status in state - // Note: Even if the script already exists we still need to add - // event handlers to update the state for *this* hook instance. - const setStateFromEvent = (event: any) => { - setStatus(event.type === "load" ? "ready" : "error"); - }; - - // Add event listeners - script.addEventListener("load", setStateFromEvent); - script.addEventListener("error", setStateFromEvent); - - // Remove event listeners on cleanup - return () => { - if (script) { - script.removeEventListener("load", setStateFromEvent); - script.removeEventListener("error", setStateFromEvent); - } - }; - }, - [src], // Only re-run effect if script src changes - ); - - return status; -}; diff --git a/pages/concepts/[concept]/[param].tsx b/pages/concepts/[concept]/[param].tsx index c300ce4f..62e69e10 100644 --- a/pages/concepts/[concept]/[param].tsx +++ b/pages/concepts/[concept]/[param].tsx @@ -3,7 +3,6 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; export default function Concept() { const { env } = useContext(SettingsContext); @@ -16,23 +15,14 @@ export default function Concept() { entryUri = `https://www-sandbox.dataportal.se/concepts/${curi}`; else entryUri = `https://dataportal.se/concepts/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/pages/concepts/[concept]/index.tsx b/pages/concepts/[concept]/index.tsx index 4488a18d..b1d1af5f 100644 --- a/pages/concepts/[concept]/index.tsx +++ b/pages/concepts/[concept]/index.tsx @@ -3,7 +3,6 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; export default function Concept() { const { env } = useContext(SettingsContext); @@ -16,23 +15,14 @@ export default function Concept() { entryUri = `https://www-sandbox.dataportal.se/concepts/${curi}`; else entryUri = `https://dataportal.se/concepts/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/pages/dataservice/[dataSet]/index.tsx b/pages/dataservice/[dataSet]/index.tsx index 2eb8b7e7..1de50e96 100644 --- a/pages/dataservice/[dataSet]/index.tsx +++ b/pages/dataservice/[dataSet]/index.tsx @@ -3,32 +3,24 @@ import { useContext } from "react"; import { EntrystoreProvider } from "@/providers/EntrystoreProvider"; import { SettingsContext } from "@/providers/SettingsProvider"; import { DataServicePage } from "@/components/content/Entryscape/DataServicePage"; -import { useScript } from "@/hooks/useScript"; export default function ExploreApiPage() { const { env } = useContext(SettingsContext); - const { query } = useRouter() || {}; const { dataSet, name } = query || {}; const ids = (typeof dataSet === "string" && dataSet.split("_")) || []; const cid = ids[0]; const eid = ids[1]; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - return postscribeStatus === "ready" ? ( + + return ( - ) : ( - <> ); } diff --git a/pages/datasets/[dataSet]/apiexplore/[apieid].tsx b/pages/datasets/[dataSet]/apiexplore/[apieid].tsx index 0c30707c..b100583f 100644 --- a/pages/datasets/[dataSet]/apiexplore/[apieid].tsx +++ b/pages/datasets/[dataSet]/apiexplore/[apieid].tsx @@ -2,7 +2,6 @@ import { DataSetExploreApiPage } from "@/components/content/Entryscape/DatasetEx // export default DataSetExploreApiPage; import { useRouter } from "next/router"; import { useContext } from "react"; -import { useScript } from "@/hooks/useScript"; import { ApiIndexProvider } from "@/providers/ApiIndexContext"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { SettingsContext } from "@/providers/SettingsProvider"; @@ -15,24 +14,18 @@ export default function ExploreApiPage() { const ids = (typeof dataSet === "string" && dataSet.split("_")) || []; const cid = ids[0]; const eid = ids[1]; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - return postscribeStatus === "ready" ? ( + + return ( - ) : ( - <> ); } diff --git a/pages/datasets/[dataSet]/index.tsx b/pages/datasets/[dataSet]/index.tsx index 8d1aae19..b68e3edd 100644 --- a/pages/datasets/[dataSet]/index.tsx +++ b/pages/datasets/[dataSet]/index.tsx @@ -4,7 +4,6 @@ import { ApiIndexProvider } from "@/providers/ApiIndexContext"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { SettingsContext } from "@/providers/SettingsProvider"; import { DataSetPage } from "@/components/content/Entryscape/DataSetPage"; -import { useScript } from "@/hooks/useScript"; export default function DataSet() { const { env } = useContext(SettingsContext); @@ -14,24 +13,18 @@ export default function DataSet() { const ids = (typeof dataSet === "string" && dataSet.split("_")) || []; const cid = ids[0]; const eid = ids[1]; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - return postscribeStatus === "ready" ? ( + + return ( - ) : ( - <> ); } diff --git a/pages/externalconcept/index.tsx b/pages/externalconcept/index.tsx index fbdd1b0d..a08462cf 100644 --- a/pages/externalconcept/index.tsx +++ b/pages/externalconcept/index.tsx @@ -4,27 +4,19 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; export default function Concept() { const { env } = useContext(SettingsContext); const { resource } = useRouter().query; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - if (postscribeStatus !== "ready" || !resource) return null; + if (!resource) return null; return ( diff --git a/pages/externalspecification/index.tsx b/pages/externalspecification/index.tsx index 96b8af0b..db32a45a 100644 --- a/pages/externalspecification/index.tsx +++ b/pages/externalspecification/index.tsx @@ -3,27 +3,20 @@ import { useRouter } from "next/router"; import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; -import { useScript } from "@/hooks/useScript"; import { SpecificationPage } from "@/components/content/Entryscape/SpecificationPage"; export default function Specification() { const { env } = useContext(SettingsContext); const { resource } = useRouter().query; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - if (postscribeStatus !== "ready" || !resource) return null; + if (!resource) return null; return ( diff --git a/pages/externalterminology/index.tsx b/pages/externalterminology/index.tsx index 9a532a77..9c3d1608 100644 --- a/pages/externalterminology/index.tsx +++ b/pages/externalterminology/index.tsx @@ -3,27 +3,20 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import { EntrystoreProvider } from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; import { GetServerSideProps } from "next"; export default function Terminology() { const { env } = useContext(SettingsContext); const { resource } = useRouter().query; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - if (postscribeStatus !== "ready" || !resource) return null; + if (!resource) return null; return ( diff --git a/pages/metadatakvalitet/[...catalog]/index.tsx b/pages/metadatakvalitet/[...catalog]/index.tsx index 36a0a32b..a38dd7e3 100644 --- a/pages/metadatakvalitet/[...catalog]/index.tsx +++ b/pages/metadatakvalitet/[...catalog]/index.tsx @@ -1,5 +1,4 @@ import { MQACategoryPage } from "@/components/content/Entryscape/MQACategoryPage"; -import { useScript } from "@/hooks/useScript"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { SettingsContext } from "@/providers/SettingsProvider"; import { useRouter } from "next/router"; @@ -11,23 +10,18 @@ export default function MqaCategoryPage() { const ids = asPath.split("/"); const eid = ids[3]; const cid = ids[4]; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - return postscribeStatus === "ready" ? ( + if (!cid || !eid) return null; + + return ( - ) : ( - <> ); } diff --git a/pages/specifications/[specification]/[param].tsx b/pages/specifications/[specification]/[param].tsx index f7ecf301..4e440f23 100644 --- a/pages/specifications/[specification]/[param].tsx +++ b/pages/specifications/[specification]/[param].tsx @@ -1,7 +1,6 @@ import { useRouter } from "next/router"; import { useContext } from "react"; import { SpecificationPage } from "@/components/content/Entryscape/SpecificationPage"; -import { useScript } from "@/hooks/useScript"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; @@ -16,22 +15,14 @@ export default function Specification() { entryUri = `https://www-sandbox.dataportal.se/specifications/${curi}`; else entryUri = `https://dataportal.se/specifications/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/pages/specifications/[specification]/index.tsx b/pages/specifications/[specification]/index.tsx index b541d601..0061906c 100644 --- a/pages/specifications/[specification]/index.tsx +++ b/pages/specifications/[specification]/index.tsx @@ -1,7 +1,6 @@ import { useRouter } from "next/router"; import { useContext } from "react"; import { SpecificationPage } from "@/components/content/Entryscape/SpecificationPage"; -import { useScript } from "@/hooks/useScript"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; @@ -16,22 +15,14 @@ export default function Specification() { entryUri = `https://www-sandbox.dataportal.se/specifications/${curi}`; else entryUri = `https://dataportal.se/specifications/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/pages/terminology/[terminology]/[param].tsx b/pages/terminology/[terminology]/[param].tsx index fccea35a..312eb953 100644 --- a/pages/terminology/[terminology]/[param].tsx +++ b/pages/terminology/[terminology]/[param].tsx @@ -3,7 +3,6 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; export default function Concept() { const { env } = useContext(SettingsContext); @@ -16,22 +15,14 @@ export default function Concept() { entryUri = `https://www-sandbox.dataportal.se/concepts/${curi}`; else entryUri = `https://dataportal.se/concepts/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/pages/terminology/[terminology]/index.tsx b/pages/terminology/[terminology]/index.tsx index eec5b28c..c161bf03 100644 --- a/pages/terminology/[terminology]/index.tsx +++ b/pages/terminology/[terminology]/index.tsx @@ -3,7 +3,6 @@ import { useContext } from "react"; import { SettingsContext } from "@/providers/SettingsProvider"; import EntrystoreProvider from "@/providers/EntrystoreProvider"; import { ConceptPage } from "@/components/content/Entryscape/ConceptPage"; -import { useScript } from "@/hooks/useScript"; export default function Concept() { const { env } = useContext(SettingsContext); @@ -16,23 +15,16 @@ export default function Concept() { entryUri = `https://www-sandbox.dataportal.se/concepts/${curi}`; else entryUri = `https://dataportal.se/concepts/${curi}`; const hasResourceUri = `https://www.dataportal.se/terminology/${curi}`; - const postscribeStatus = useScript( - "/postscribe.min.js", - "sha384-1nPAWyZS0cvGLWSoWOrkTZAy8Xq8g6llEe985qo5NRPAeDi+F9h9U+0R8v56XWCM", - "anonymous", - ); - return postscribeStatus === "ready" ? ( + return ( - ) : ( - <> ); } diff --git a/providers/EntrystoreProvider/index.tsx b/providers/EntrystoreProvider/index.tsx index d501996a..0ac55b42 100644 --- a/providers/EntrystoreProvider/index.tsx +++ b/providers/EntrystoreProvider/index.tsx @@ -3,10 +3,13 @@ import { useRouter } from "next/router"; import React, { createContext, useEffect, useState } from "react"; import { EnvSettings } from "@/env/EnvSettings"; import { SettingsUtil } from "@/env/SettingsUtil"; -import { getLocalizedValue } from "@/utilities"; - -//unfortunate hack to get a entrystore class instance, script is inserted in head -declare var ESJS: any; +import { getSimplifiedLocalizedValue } from "@/utilities"; +import { + Entry, + EntryStore, + EntryStoreUtil, + Metadata, +} from "@entryscape/entrystore-js"; type RelationObj = { title: string; @@ -20,15 +23,28 @@ export interface EntrystoreProviderProps { children?: React.ReactNode; entryUri?: string; entrystoreUrl: string | "admin.dataportal.se"; - fetchMore: boolean; isConcept?: boolean; hasResourceUri?: string; + includeContact?: boolean; + pageType: PageType; } +type PageType = + | "concept" + | "terminology" + | "specification" + | "dataset" + | "dataservice" + | "apiexplore" + | "mqa"; + export interface ESEntry { env: EnvSettings; - entrystore: any; - entry: any; + entrystore: EntryStore; + entry: Entry; + context: string; + esId: string; + loading: boolean; title: string; description: string; publisher: string; @@ -37,6 +53,13 @@ export interface ESEntry { contact?: ESContact; conformsTo?: RelationObj[]; hasResource?: RelationObj[]; + address: string; + downloadFormats?: Array<{ title: string; url: string }>; + relatedSpecifications?: Array<{ title: string; url: string }>; + relatedTerm?: { title: string; url: string }; + relatedConcepts?: Array<{ title: string; url: string }>; + relatedDatasets?: Array<{ title: string; url: string }>; + keywords?: Array; mqaCatalog?: string; } @@ -47,8 +70,9 @@ export interface ESContact { const defaultESEntry: ESEntry = { env: SettingsUtil.getDefault(), - entrystore: {}, - entry: {}, + entrystore: {} as EntryStore, + entry: {} as Entry, + loading: true, title: "", description: "", publisher: "", @@ -56,6 +80,9 @@ const defaultESEntry: ESEntry = { definition: "", conformsTo: [], hasResource: [], + address: "", + context: "", + esId: "", }; export const EntrystoreContext = createContext(defaultESEntry); @@ -64,7 +91,6 @@ export const EntrystoreContext = createContext(defaultESEntry); * Provider for entrystore entry, * if contextid and entryid is sent in, we try to retrieve an entry from the configured EntryStore instance * - * TODO - if this is to be used in both datasets, terms etc, we need to expose the graph via the provider, instead of * setting properties in the provider state (eg. title) */ export const EntrystoreProvider: React.FC = ({ @@ -73,355 +99,313 @@ export const EntrystoreProvider: React.FC = ({ eid, entryUri, entrystoreUrl, - fetchMore, - isConcept, hasResourceUri, + includeContact, + pageType, }) => { const [state, setState] = useState(defaultESEntry); - const { lang: nextLang } = useTranslation("common"); const router = useRouter(); + const { lang, t } = useTranslation(); - const addScripts = (callback: Function) => { - if (typeof window !== "undefined" && (window as any).postscribe) { - const postscribe = (window as any).postscribe; - - postscribe( - "#scriptsPlaceholder", - ` - - `, - { - done: function () { - callback(); - }, - }, - ); - } - }; + const es = new EntryStore( + `https://${entrystoreUrl}/store` || "https://admin.dataportal.se/store", + ); + const esu = new EntryStoreUtil(es); + esu.loadOnlyPublicEntries(true); + let entry = {} as Entry; - const parseEmail = (mailStr: string) => { - if (mailStr && mailStr.includes("mailto:")) { - return mailStr.replace("mailto:", ""); - } + useEffect(() => { + fetchEntry(); + }, []); - return mailStr; - }; + const fetchEntry = async () => { + try { + if (entryUri) { + entry = await esu.getEntryByResourceURI(entryUri); + } else if (cid && eid) { + entry = await es.getEntry(es.getEntryURI(cid, eid)); + } - useEffect(() => { - addScripts(async () => { - //if we have an ES url, try to get a active instance of EntryScape - if (defaultESEntry.env) { - defaultESEntry.entrystore = new ESJS.EntryStore( - `https://${entrystoreUrl}/store`, - ); - defaultESEntry.entrystore.getREST().disableJSONP(); - defaultESEntry.entrystore.getREST().disableCredentials(); - - var util = new ESJS.EntryStoreUtil(defaultESEntry.entrystore); - const es = defaultESEntry.entrystore; - //we have entryUri - if (entryUri) { - util - .getEntryByResourceURI(entryUri) - .then(async (entry: any) => { - defaultESEntry.entry = entry; - - const graph = entry.getAllMetadata(); - const resourceURI = entry.getResourceURI(); - const valuePromises: Promise[] = []; - - const datasets = await es - .newSolrQuery() - .rdfType(["dcat:Dataset", "dcat:DataService"]) - .publicRead(true) - .uriProperty("dcterms:conformsTo", resourceURI) - .getEntries(); - - const hasResource = await es - .newSolrQuery() - .uriProperty( - "http://www.w3.org/ns/dx/prof/hasResource", - hasResourceUri || entryUri, - ) - .rdfType(["dcterms:Standard", "prof:Profile"]) - .publicRead(true) - .getEntries(); - - const datasetArr = await Promise.all( - datasets.map(async (ds: any) => { - const title = await getLocalizedValue( - ds.getAllMetadata(), - "dcterms:title", - nextLang, - es, - ); - return { - title: title, - url: `/${es.getContextId( - ds.getEntryInfo().getMetadataURI(), - )}_${ds.getId()}/${title.toLowerCase().replace(/ /g, "-")}`, - }; - }), - ); + if (!entry) return router.push("/404"); - const resourceArr = await Promise.all( - hasResource.map(async (spec: any) => { - const title = await getLocalizedValue( - spec.getAllMetadata(), - "dcterms:title", - nextLang, - es, - ); - return { - title: title, - url: spec.getResourceURI(), - }; - }), - ); + const metadata = entry.getAllMetadata(); + const resourceUri = entry.getResourceURI(); - //the getLocalizedValue function might fetch from network, so start all IO with promises - valuePromises.push( - getLocalizedValue(graph, "dcterms:title", nextLang, es, { - resourceURI, - }), - ); - valuePromises.push( - getLocalizedValue( - graph, - "http://www.w3.org/2004/02/skos/core#prefLabel", - nextLang, - es, - ), - ); - valuePromises.push( - getLocalizedValue(graph, "dcterms:description", nextLang, es), - ); - valuePromises.push( - getLocalizedValue(graph, "dcterms:publisher", nextLang, es), - ); - valuePromises.push( - getLocalizedValue(graph, "skos:definition", nextLang, es), + const title = + getSimplifiedLocalizedValue(metadata, "dcterms:title") || + getSimplifiedLocalizedValue(metadata, "skos:prefLabel"); + + const description = + getSimplifiedLocalizedValue(metadata, "skos:definition") || + getSimplifiedLocalizedValue(metadata, "dcterms:description"); + + const publisherUri = metadata.findFirstValue(null, "dcterms:publisher"); + + let publisher = ""; + if (pageType !== "mqa") { + if (publisherUri) { + try { + const publisherEntry = + await esu.getEntryByResourceURI(publisherUri); + if (publisherEntry) { + publisher = getSimplifiedLocalizedValue( + publisherEntry.getAllMetadata(), + "foaf:name", ); - if (fetchMore && !isConcept) { - valuePromises.push( - getLocalizedValue(graph, "dcat:contactPoint", nextLang, es, { - uriTypeName: "http://www.w3.org/2006/vcard/ns#fn", - }), - ); - valuePromises.push( - getLocalizedValue(graph, "dcat:contactPoint", nextLang, es, { - uriTypeName: "http://www.w3.org/2006/vcard/ns#hasEmail", - }), - ); - } - if (isConcept && !fetchMore) { - const termEntry = await util.getEntryByResourceURI( - graph.findFirstValue(resourceURI, "skos:inScheme"), - ); - const termGraph = termEntry.getAllMetadata(); - - valuePromises.push( - getLocalizedValue( - termGraph, - "dcterms:publisher", - nextLang, - es, - ), + } + } catch (error) {} + } else { + try { + const specification = metadata.findFirstValue( + null, + "skos:inScheme", + ); + if (specification) { + const specificationEntry = + await esu.getEntryByResourceURI(specification); + if (specificationEntry) { + const specificationMeta = specificationEntry.getAllMetadata(); + const publisherUri = specificationMeta.findFirstValue( + null, + "dcterms:publisher", ); - } - //wait for all values to be fetched - let results = await Promise.all(valuePromises); - - if (results && results.length > 0) { - defaultESEntry.title = results[0] || results[1]; - defaultESEntry.description = results[2]; - defaultESEntry.publisher = results[3]; - defaultESEntry.definition = results[4]; - defaultESEntry.conformsTo = datasetArr || null; - defaultESEntry.hasResource = resourceArr || null; - - if (fetchMore && !isConcept) { - if (results[5] || results[6]) { - defaultESEntry.contact = { - name: results[5], - email: parseEmail(results[6]), - }; + if (publisherUri) { + const publisherEntry = + await esu.getEntryByResourceURI(publisherUri); + if (publisherEntry) { + publisher = getSimplifiedLocalizedValue( + publisherEntry.getAllMetadata(), + "foaf:name", + ); } } - if (isConcept && !fetchMore) { - defaultESEntry.termPublisher = results[5]; - } } - - setState({ - ...defaultESEntry, - }); - }) - .catch((err: any) => { - console.error(err); - router.push("/404"); - }); + } + } catch (error) {} } - //we have contextID and entryId, - else if (cid && eid) { - let mqaCataog = es.getEntryURI(cid, "_quality"); - const mqaEntry = await es.getEntry(mqaCataog); - const mqaMetadata = await mqaEntry.getAllMetadata(); - - let entryURI = ""; - entryURI = es.getEntryURI(cid, eid); - //fetch entry from entryscape https://entrystore.org/js/stable/doc/ - - es.getEntry(entryURI) - - .then(async (entry: any) => { - defaultESEntry.entry = entry; - if (!entry) return; - - const graph = entry.getAllMetadata(); - const resourceURI = entry.getResourceURI(); - const valuePromises: Promise[] = []; - - const conformsToURIs = graph - .find(resourceURI, "dcterms:conformsTo") - .map((stmt: any) => stmt.getValue()); - const util = new ESJS.EntryStoreUtil( - new ESJS.EntryStore(`https://editera.dataportal.se/store`), - ); - util.loadOnlyPublicEntries(true); + } - const conformsToEntries = await util.loadEntriesByResourceURIs( - conformsToURIs, - undefined, - true, - ); - const specfications = conformsToEntries.filter((s: any) => s); - const extractHREF = (s: any) => { - if (s.getResourceURI().startsWith("https://dataportal.se")) - return s.getResourceURI(); - return `https://dataportal.se/externalspecification/${s.getResourceURI()}`; - }; - - const specificationHREF = specfications.map((s: any) => - extractHREF(s), - ); + const entryData: Partial = { + entrystore: es, + entry, + context: entry.getContext().getId(), + esId: entry.getId(), + title, + address: resourceUri, + description, + publisher, + loading: false, + }; + + if (includeContact) entryData.contact = await getContactInfo(metadata); + + switch (pageType) { + case "dataset": + entryData.relatedSpecifications = await getRelatedSpecifications( + entry, + metadata, + pageType, + ); + entryData.keywords = await getKeywords(entry); + entryData.downloadFormats = getDownloadFormats( + entry.getEntryInfo().getMetadataURI(), + ); + break; + case "dataservice": + break; + case "apiexplore": + entryData.contact = await getContactInfo(metadata); + break; + case "terminology": + entryData.relatedSpecifications = await getRelatedSpecifications( + entry, + metadata, + pageType, + ); + entryData.address = resourceUri.startsWith("https://dataportal.se") + ? resourceUri.replace("concepts", "terminology") + : resourceUri; + entryData.downloadFormats = getDownloadFormats( + entry.getEntryInfo().getMetadataURI(), + ); + break; + case "specification": + entryData.relatedDatasets = await getRelatedDatasets(entry); + entryData.keywords = await getKeywords(entry); + entryData.downloadFormats = getDownloadFormats( + entry.getEntryInfo().getMetadataURI(), + ); + break; + case "concept": + entryData.relatedTerm = await getRelatedTerm(metadata); + entryData.downloadFormats = getDownloadFormats( + entry.getEntryInfo().getMetadataURI(), + ); + break; + case "mqa": + break; + } - const specArr = await Promise.all( - conformsToEntries.map(async (spec: any) => { - return { - title: await getLocalizedValue( - spec.getAllMetadata(), - "dcterms:title", - nextLang, - es, - ), - url: specificationHREF[0], - }; - }), - ); + setState({ + ...defaultESEntry, + ...entryData, + }); + } catch (error) { + router.push("/404"); + console.error("Failed to fetch entry:", error); + } + }; - //the getLocalizedValue function might fetch from network, so start all IO with promises - valuePromises.push( - getLocalizedValue(graph, "dcterms:title", nextLang, es, { - resourceURI, - }), - ); + const getContactInfo = async (metadata: Metadata) => { + const contactPoint = metadata.findFirstValue(null, "dcat:contactPoint"); + + const contactEntry = await esu.getEntryByResourceURI(contactPoint); + const name = getSimplifiedLocalizedValue( + contactEntry.getAllMetadata(), + "http://www.w3.org/2006/vcard/ns#fn", + ); + const email = parseEmail( + getSimplifiedLocalizedValue( + contactEntry.getAllMetadata(), + "http://www.w3.org/2006/vcard/ns#hasEmail", + ), + ); + + return { name, email }; + }; - valuePromises.push( - getLocalizedValue( - graph, - "http://www.w3.org/2004/02/skos/core#prefLabel", - nextLang, - es, - ), - ); - valuePromises.push( - getLocalizedValue(graph, "dcterms:description", nextLang, es, { - resourceURI, - }), - ); - valuePromises.push( - getLocalizedValue(graph, "dcterms:publisher", nextLang, es), - ); - valuePromises.push( - getLocalizedValue(graph, "skos:definition", nextLang, es), - ); - if (fetchMore && !isConcept) { - valuePromises.push( - getLocalizedValue(graph, "dcat:contactPoint", nextLang, es, { - uriTypeName: "http://www.w3.org/2006/vcard/ns#fn", - }), - ); - valuePromises.push( - getLocalizedValue(graph, "dcat:contactPoint", nextLang, es, { - uriTypeName: "http://www.w3.org/2006/vcard/ns#hasEmail", - }), - ); - } - valuePromises.push( - getLocalizedValue(mqaMetadata, "dcterms:title", nextLang, es), - ); - if (isConcept && !fetchMore) { - const termEntry = await util.getEntryByResourceURI( - graph.findFirstValue(resourceURI, "skos:inScheme"), - ); - const termGraph = termEntry.getAllMetadata(); - - valuePromises.push( - getLocalizedValue( - termGraph, - "dcterms:publisher", - nextLang, - es, - ), - ); - } - //wait for all values to be fetched - let results = await Promise.all(valuePromises); - - if (results && results.length > 0) { - defaultESEntry.title = results[0] || results[1]; - defaultESEntry.description = results[2]; - defaultESEntry.publisher = results[3]; - defaultESEntry.definition = results[4]; - defaultESEntry.mqaCatalog = results[5]; - defaultESEntry.conformsTo = specArr || null; - - if (fetchMore && !isConcept) { - if (results[5] || results[6]) { - defaultESEntry.contact = { - name: results[5], - email: parseEmail(results[6]), - }; - } - } - if (isConcept && !fetchMore) { - defaultESEntry.termPublisher = results[5]; - } - } + const getRelatedDatasets = async (entry: Entry) => { + const datasets = await es + .newSolrQuery() + .rdfType(["dcat:Dataset", "esterms:IndependentDataService"]) + .publicRead(true) + .uriProperty("dcterms:conformsTo", entry.getResourceURI()) + .getEntries(); + + const datasetArray = datasets.map((ds: any) => { + return { + title: getSimplifiedLocalizedValue( + ds.getAllMetadata(), + "dcterms:title", + ), + url: `/${lang}/datasets/${es.getContextId( + ds.getEntryInfo().getMetadataURI(), + )}_${ds.getId()}`, + }; + }); - setState({ - ...defaultESEntry, - }); - }) - .catch((error: any) => { - console.error({ error }); - if ( - error.message === - "Failed fetching entry. Error: Connection issue" - ) { - router.push("/404"); - } - }); - } else { - router.push("/404"); - } + return datasetArray; + }; + + const getDownloadFormats = (baseUri: string) => { + return [ + { + title: t("pages|datasetpage$download-metadata-as") + " RDF/XML", + url: baseUri, + }, + { + title: t("pages|datasetpage$download-metadata-as") + " TURTLE", + url: baseUri + "?format=text/turtle", + }, + { + title: t("pages|datasetpage$download-metadata-as") + " N-TRIPLES", + url: baseUri + "?format=text/n-triples", + }, + { + title: t("pages|datasetpage$download-metadata-as") + " JSON-LD", + url: baseUri + "?format=application/ld+json", + }, + ]; + }; + + const getRelatedTerm = async (metadata: Metadata) => { + const termUri = metadata.findFirstValue(null, "skos:inScheme"); + const termEntry = await esu.getEntryByResourceURI(termUri); + + console.log("termEntry", termUri); + + return { + title: getSimplifiedLocalizedValue( + termEntry.getAllMetadata(), + "dcterms:title", + ), + url: termUri.startsWith("https://dataportal.se") + ? new URL(termUri).pathname.replace("concepts", "terminology") + : `/${lang}/externalterminology?resource=${termUri}`, + }; + }; + + const getRelatedSpecifications = async ( + entry: Entry, + metadata: Metadata, + pageType: PageType, + ) => { + try { + if (pageType === "dataset") { + const specifications = metadata + .find(entry.getResourceURI(), "dcterms:conformsTo") + .map((stmt: any) => stmt.getValue()); + + const resourceEntries = await esu.loadEntriesByResourceURIs( + specifications, + null, + true, + ); + + return resourceEntries + .filter((e: any) => e) + .map((e: any) => ({ + title: getSimplifiedLocalizedValue( + e.getAllMetadata(), + "dcterms:title", + ), + url: e.getResourceURI().startsWith("https://dataportal.se") + ? new URL(e.getResourceURI()).pathname + : `/${lang}/externalspecification?resource=${e.getResourceURI()}`, + })); + } else if (pageType === "terminology") { + const specifications = await es + .newSolrQuery() + .uriProperty( + "http://www.w3.org/ns/dx/prof/hasResource", + hasResourceUri || entry.getResourceURI(), + ) + .rdfType(["dcterms:Standard", "prof:Profile"]) + .publicRead(true) + .getEntries(); + + return specifications + .filter((e: any) => e) + .map((e: any) => ({ + title: getSimplifiedLocalizedValue( + e.getAllMetadata(), + "dcterms:title", + ), + url: e.getResourceURI().startsWith("https://dataportal.se") + ? new URL(e.getResourceURI()).pathname + : `/${lang}/externalspecification?resource=${e.getResourceURI()}`, + })); } - }); - }, []); + return []; + } catch (error) { + console.error("Error fetching specifications:", error); + return []; + } + }; + + const getKeywords = async (entry: Entry) => { + return entry + .getAllMetadata() + .find(null, "dcat:keyword") + .map((k: any) => k.getValue()); + }; + + const parseEmail = (email: string) => { + return email.startsWith("mailto:") ? email : `mailto:${email}`; + }; + + if (state.loading) return null; return ( diff --git a/public/postscribe.min.js b/public/postscribe.min.js deleted file mode 100644 index 9bc743de..00000000 --- a/public/postscribe.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @file postscribe - * @description Asynchronously write javascript, even with document.write. - * @version v2.0.6 - * @see {@link https://krux.github.io/postscribe} - * @license MIT - * @author Derek Brans - * @copyright 2016 Krux Digital, Inc - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.postscribe=e():t.postscribe=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}var o=n(1),i=r(o);t.exports=i["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function o(t){return t&&t.__esModule?t:{"default":t}}function i(){}function s(){var t=m.shift();if(t){var e=h.last(t);e.afterDequeue(),t.stream=a.apply(void 0,t),e.afterStreamStart()}}function a(t,e,n){function r(t){t=n.beforeWrite(t),g.write(t),n.afterWrite(t)}g=new p["default"](t,n),g.id=y++,g.name=n.name||g.id,u.streams[g.name]=g;var o=t.ownerDocument,a={close:o.close,open:o.open,write:o.write,writeln:o.writeln};c(o,{close:i,open:i,write:function(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];return r(e.join(""))},writeln:function(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];return r(e.join("")+"\n")}});var f=g.win.onerror||i;return g.win.onerror=function(t,e,r){n.error({msg:t+" - "+e+": "+r}),f.apply(g.win,[t,e,r])},g.write(e,function(){c(o,a),g.win.onerror=f,n.done(),g=null,s()}),g}function u(t,e,n){if(h.isFunction(n))n={done:n};else if("clear"===n)return m=[],g=null,void(y=0);n=h.defaults(n,d),t=/^#/.test(t)?window.document.getElementById(t.substr(1)):t.jquery?t[0]:t;var r=[t,e,n];return t.postscribe={cancel:function(){r.stream?r.stream.abort():r[1]=i}},n.beforeEnqueue(r),m.push(r),g||s(),t.postscribe}e.__esModule=!0;var c=Object.assign||function(t){for(var e=1;e0&&this._writeStaticTokens(o),n&&this._handleScriptToken(e),r&&this._handleStyleToken(e)},t.prototype._writeStaticTokens=function(t){var e=this._buildChunk(t);return e.actual?(e.html=this.proxyHistory+e.actual,this.proxyHistory+=e.proxy,this.proxyRoot.innerHTML=e.html,h&&(e.proxyInnerHTML=this.proxyRoot.innerHTML),this._walkChunk(),h&&(e.actualInnerHTML=this.root.innerHTML),e):null},t.prototype._buildChunk=function(t){for(var e=this.actuals.length,n=[],r=[],o=[],i=t.length,s=0;i>s;s++){var a=t[s],u=a.toString();if(n.push(u),a.attrs){if(!/^noscript$/i.test(a.tagName)){var c=e++;r.push(u.replace(/(\/?>)/," "+d+"id="+c+" $1")),a.attrs.id!==m&&a.attrs.id!==y&&o.push("atomicTag"===a.type?"":"<"+a.tagName+" "+d+"proxyof="+c+(a.unary?" />":">"))}}else r.push(u),o.push("endTag"===a.type?u:"")}return{tokens:t,raw:n.join(""),actual:r.join(""),proxy:o.join("")}},t.prototype._walkChunk=function(){for(var t=void 0,e=[this.proxyRoot];l.existy(t=e.shift());){var n=1===t.nodeType,r=n&&s(t,"proxyof");if(!r){n&&(this.actuals[s(t,"id")]=t,a(t,"id"));var o=t.parentNode&&s(t.parentNode,"proxyof");o&&this.actuals[o].appendChild(t)}e.unshift.apply(e,l.toArray(t.childNodes))}},t.prototype._handleScriptToken=function(t){var e=this,n=this.parser.clear();n&&this.writeQueue.unshift(n),t.src=t.attrs.src||t.attrs.SRC,t=this.options.beforeWriteToken(t),t&&(t.src&&this.scriptStack.length?this.deferredRemote=t:this._onScriptStart(t),this._writeScriptToken(t,function(){e._onScriptDone(t)}))},t.prototype._handleStyleToken=function(t){var e=this.parser.clear();e&&this.writeQueue.unshift(e),t.type=t.attrs.type||t.attrs.TYPE||"text/css",t=this.options.beforeWriteToken(t),t&&this._writeStyleToken(t),e&&this.write()},t.prototype._writeStyleToken=function(t){var e=this._buildStyle(t);this._insertCursor(e,y),t.content&&(e.styleSheet&&!e.sheet?e.styleSheet.cssText=t.content:e.appendChild(this.doc.createTextNode(t.content)))},t.prototype._buildStyle=function(t){var e=this.doc.createElement(t.tagName);return e.setAttribute("type",t.type),l.eachKey(t.attrs,function(t,n){e.setAttribute(t,n)}),e},t.prototype._insertCursor=function(t,e){this._writeImpl('');var n=this.doc.getElementById(e);n&&n.parentNode.replaceChild(t,n)},t.prototype._onScriptStart=function(t){t.outerWrites=this.writeQueue,this.writeQueue=[],this.scriptStack.unshift(t)},t.prototype._onScriptDone=function(t){return t!==this.scriptStack[0]?void this.options.error({message:"Bad script nesting or script finished twice"}):(this.scriptStack.shift(),this.write.apply(this,t.outerWrites),void(!this.scriptStack.length&&this.deferredRemote&&(this._onScriptStart(this.deferredRemote),this.deferredRemote=null)))},t.prototype._writeScriptToken=function(t,e){var n=this._buildScript(t),r=this._shouldRelease(n),o=this.options.afterAsync;t.src&&(n.src=t.src,this._scriptLoadHandler(n,r?o:function(){e(),o()}));try{this._insertCursor(n,m),n.src&&!r||e()}catch(i){this.options.error(i),e()}},t.prototype._buildScript=function(t){var e=this.doc.createElement(t.tagName);return l.eachKey(t.attrs,function(t,n){e.setAttribute(t,n)}),t.content&&(e.text=t.content),e},t.prototype._scriptLoadHandler=function(t,e){function n(){t=t.onload=t.onreadystatechange=t.onerror=null}function r(){n(),e()}function o(t){n(),i(t),e()}var i=this.options.error;u(t,{onload:function(){return r()},onreadystatechange:function(){/^(loaded|complete)$/.test(t.readyState)&&r()},onerror:function(){return o({message:"remote script failed "+t.src})}})},t.prototype._shouldRelease=function(t){var e=/^script$/i.test(t.nodeName);return!e||!!(this.options.releaseAsync&&t.src&&t.hasAttribute("async"))},t}();e["default"]=g},function(t,e,n){!function(e,n){t.exports=n()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}var o=n(1),i=r(o);t.exports=i["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.__esModule=!0;var s=n(2),a=o(s),u=n(3),c=o(u),f=n(6),p=r(f),l=n(5),h={comment:/^");return e>=0?new c.CommentToken(t.substr(4,e-1),e+3):void 0}function o(t){var e=t.indexOf("<");return new c.CharsToken(e>=0?e:t.length)}function i(t){var e=t.indexOf(">");if(-1!==e){var n=t.match(f.startTag);if(n){var r=function(){var t={},e={},r=n[2];return n[2].replace(f.attr,function(n,o){arguments[2]||arguments[3]||arguments[4]||arguments[5]?arguments[5]?(t[arguments[5]]="",e[arguments[5]]=!0):t[o]=arguments[2]||arguments[3]||arguments[4]||f.fillAttr.test(o)&&o||"":t[o]="",r=r.replace(n,"")}),{v:new c.StartTagToken(n[1],n[0].length,t,e,!!n[3],r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""))}}();if("object"===("undefined"==typeof r?"undefined":u(r)))return r.v}}}function s(t){var e=i(t);if(e){var n=t.slice(e.length);if(n.match(new RegExp("","i"))){var r=n.match(new RegExp("([\\s\\S]*?)","i"));if(r)return new c.AtomicTagToken(e.tagName,r[0].length+e.length,e.attrs,e.booleanAttrs,r[1])}}}function a(t){var e=t.match(f.endTag);return e?new c.EndTagToken(e[1],e[0].length):void 0}e.__esModule=!0;var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t};e.comment=r,e.chars=o,e.startTag=i,e.atomicTag=s,e.endTag=a;var c=n(4),f={startTag:/^<([\-A-Za-z0-9_]+)((?:\s+[\w\-]+(?:\s*=?\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,endTag:/^<\/([\-A-Za-z0-9_]+)[^>]*>/,attr:/(?:([\-A-Za-z0-9_]+)\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))|(?:([\-A-Za-z0-9_]+)(\s|$)+)/g,fillAttr:/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noresize|noshade|nowrap|readonly|selected)$/i}},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.__esModule=!0,e.EndTagToken=e.AtomicTagToken=e.StartTagToken=e.TagToken=e.CharsToken=e.CommentToken=e.Token=void 0;var o=n(5),i=(e.Token=function s(t,e){r(this,s),this.type=t,this.length=e,this.text=""},e.CommentToken=function(){function t(e,n){r(this,t),this.type="comment",this.length=n||(e?e.length:0),this.text="",this.content=e}return t.prototype.toString=function(){return"