From 5f2851159731ff6d1bcc6d272a244abd6f0118b9 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Thu, 26 Mar 2026 09:48:31 -0400 Subject: [PATCH 01/15] Add new architecture diagram --- docs/css/custom.css | 6 +++++ docs/js/mermaid.mjs | 13 ++++++++++ docs/technical-documentation/diagram.md | 34 ++++++++++++++++++++++++- mkdocs.yml | 9 ++++++- requirements.txt | 4 +-- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 docs/js/mermaid.mjs diff --git a/docs/css/custom.css b/docs/css/custom.css index e5b9c586a..808e31cc1 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -22,3 +22,9 @@ --text: #f5fffa; } +/** mermaid animation helper for architecture diagram **/ +@keyframes dash { + to { + stroke-dashoffset: -100; + } +} diff --git a/docs/js/mermaid.mjs b/docs/js/mermaid.mjs new file mode 100644 index 000000000..858ca752b --- /dev/null +++ b/docs/js/mermaid.mjs @@ -0,0 +1,13 @@ +import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; +import elkLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0/dist/mermaid-layout-elk.esm.min.mjs'; + +mermaid.registerLayoutLoaders(elkLayouts); +mermaid.initialize({ + startOnLoad: false, + securityLevel: "loose", + layout: "elk", +}); + +// Important: necessary to make it visible to Material for MkDocs +window.mermaid = mermaid; + diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index 7ff38415e..9fdf665de 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -1,6 +1,38 @@ # Islandora Architecture Diagram -![Detailed diagram of the Islandora platform and its components](../assets/diagram.png) +```mermaid +flowchart TD + drupal([fa:fa-drupal Islandora Drupal Website]) + + drupal e1@==>|publishes entity event| activemq + + subgraph broker[Message Broker] + activemq[ActiveMQ] + alpaca[Alpaca] + activemq e2@==> alpaca + end + + alpaca e3@==> houdini + + subgraph microservices[scyllaridae microservices] + houdini[Houdini] + end + + houdini e4@-.->|derivative stream| alpaca + alpaca e5@-.->|saves derivative & responds ok| drupal + + classDef flow1 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s linear infinite; + classDef flow2 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 1s linear infinite; + classDef flow3 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 2s linear infinite; + classDef flow4 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 3s linear infinite; + classDef flow5 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 4s linear infinite; + + class e1 flow1; + class e2 flow2; + class e3 flow3; + class e4 flow4; + class e5 flow5; +``` Diagram prepared by [Bethany Seeger](https://github.com/bseeger) based on work done by [Gavin Morris](https://github.com/g7morris) diff --git a/mkdocs.yml b/mkdocs.yml index 8375bb2af..cdbaa43e8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,9 +34,16 @@ markdown_extensions: pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + extra_css: - css/custom.css +extra_javascript: + - js/mermaid.mjs plugins: - search - git-revision-date-localized diff --git a/requirements.txt b/requirements.txt index 2c8fb6a71..a787b430b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,8 +14,8 @@ mergedeep==1.3.4 mkdocs==1.6.1 mkdocs-get-deps==0.2.0 mkdocs-git-revision-date-localized-plugin==1.1.0 -mkdocs-material==9.0.12 -mkdocs-material-extensions==1.1.1 +mkdocs-material==9.7.6 +mkdocs-material-extensions==1.3.1 mkdocs-redirects==1.2.1 packaging==23.0 pathspec==1.0.1 From b977a3642b8e3a3e3e2b0bcaf30511547db39347 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Thu, 26 Mar 2026 11:37:29 -0400 Subject: [PATCH 02/15] switch to zensical And work on diagram animation --- .github/actions/build/action.yml | 4 +- Dockerfile | 12 +++++ Makefile | 11 +++++ docs/css/custom.css | 7 +++ docs/js/diagram-animation.js | 78 ++++++++++++++++++++++++++++++++ docs/js/mermaid-init.mjs | 45 ++++++++++++++++++ docs/js/mermaid.mjs | 13 ------ mkdocs.yml | 2 +- requirements.txt | 35 +------------- 9 files changed, 157 insertions(+), 50 deletions(-) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 docs/js/diagram-animation.js create mode 100644 docs/js/mermaid-init.mjs delete mode 100644 docs/js/mermaid.mjs diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 59a6ec142..acb0c5dd7 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -1,6 +1,6 @@ --- name: Build -description: Setup to run and run mkdocs +description: Setup to install dependencies and build docs with zensical runs: using: "composite" steps: @@ -14,4 +14,4 @@ runs: run: pip install -r requirements.txt - name: Build docs shell: bash - run: mkdocs build + run: zensical build diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..8605f490b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12-alpine3.22 + +WORKDIR /work + +RUN apk add --no-cache git && \ + pip install uv && uv --version && \ + uv pip install --break-system-packages --system zensical==0.0.29 + +EXPOSE 8080 + +ENTRYPOINT ["zensical", "serve"] +CMD ["--dev-addr", "0.0.0.0:8080"] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..dfca1e3fa --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +.PHONY: docs docs-build + +docs-build: + docker build -t islandora-docs . + +docs: docs-build + docker run --rm -it \ + -p 8080:8080 \ + -v "$(CURDIR):/work" \ + -w /work \ + islandora-docs diff --git a/docs/css/custom.css b/docs/css/custom.css index 808e31cc1..0c23a6fc4 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -28,3 +28,10 @@ stroke-dashoffset: -100; } } + +@media (prefers-reduced-motion: reduce) { + .mermaid * { + animation: none !important; + transition: none !important; + } +} diff --git a/docs/js/diagram-animation.js b/docs/js/diagram-animation.js new file mode 100644 index 000000000..5ceae6311 --- /dev/null +++ b/docs/js/diagram-animation.js @@ -0,0 +1,78 @@ +(() => { + const timings = [ + [3000, 0], + [3000, 1000], + [3000, 2000], + [3000, 3000], + [3000, 4000], + ]; + + const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); + + const findEdges = () => Array.from(document.querySelectorAll(".mermaid svg path[data-edge=true]")); + + const stopAnimations = () => { + findEdges().forEach((element) => { + if (element.__islandoraDashAnimation) { + element.__islandoraDashAnimation.cancel(); + element.__islandoraDashAnimation = null; + } + element.style.animation = "none"; + element.style.strokeDasharray = "9,5"; + element.style.strokeDashoffset = "0"; + }); + }; + + const startAnimations = () => { + findEdges().forEach((element, index) => { + const [duration, delay] = timings[index % timings.length]; + + if (element.__islandoraDashAnimation) { + element.__islandoraDashAnimation.cancel(); + } + + element.style.animation = "none"; + element.style.strokeDasharray = "9,5"; + element.style.strokeDashoffset = "900"; + + element.__islandoraDashAnimation = element.animate( + [ + { strokeDashoffset: 900 }, + { strokeDashoffset: 0 }, + ], + { + duration, + delay, + easing: "linear", + iterations: Infinity, + fill: "forwards", + }, + ); + }); + }; + + const applyEdgeMotion = () => { + if (reduceMotion.matches) { + stopAnimations(); + return; + } + + startAnimations(); + }; + + const run = () => window.requestAnimationFrame(applyEdgeMotion); + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", run, { once: true }); + } else { + run(); + } + + new MutationObserver(run).observe(document.body, { childList: true, subtree: true }); + + if (typeof reduceMotion.addEventListener === "function") { + reduceMotion.addEventListener("change", run); + } else if (typeof reduceMotion.addListener === "function") { + reduceMotion.addListener(run); + } +})(); diff --git a/docs/js/mermaid-init.mjs b/docs/js/mermaid-init.mjs new file mode 100644 index 000000000..29fd31d66 --- /dev/null +++ b/docs/js/mermaid-init.mjs @@ -0,0 +1,45 @@ +import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"; +import elkLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0/dist/mermaid-layout-elk.esm.min.mjs"; + +mermaid.registerLayoutLoaders(elkLayouts); +mermaid.initialize({ + startOnLoad: false, + securityLevel: "loose", + layout: "elk", +}); + +const renderDiagram = async (container, index) => { + if (container.dataset.mermaidRendered === "true") { + return; + } + + const code = container.querySelector("code"); + const source = code ? code.textContent : container.textContent; + if (!source || !source.trim()) { + return; + } + + const renderTarget = document.createElement("div"); + renderTarget.className = "mermaid"; + + try { + const result = await mermaid.render(`islandora-mermaid-${index}`, source); + renderTarget.innerHTML = result.svg; + container.replaceWith(renderTarget); + document.dispatchEvent(new CustomEvent("mermaid:rendered", { detail: renderTarget })); + } catch (error) { + console.error("Mermaid render failed", error); + } +}; + +const renderAll = () => { + document.querySelectorAll("pre.mermaid").forEach((container, index) => { + void renderDiagram(container, index); + }); +}; + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", renderAll, { once: true }); +} else { + renderAll(); +} diff --git a/docs/js/mermaid.mjs b/docs/js/mermaid.mjs deleted file mode 100644 index 858ca752b..000000000 --- a/docs/js/mermaid.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; -import elkLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0/dist/mermaid-layout-elk.esm.min.mjs'; - -mermaid.registerLayoutLoaders(elkLayouts); -mermaid.initialize({ - startOnLoad: false, - securityLevel: "loose", - layout: "elk", -}); - -// Important: necessary to make it visible to Material for MkDocs -window.mermaid = mermaid; - diff --git a/mkdocs.yml b/mkdocs.yml index cdbaa43e8..bd2c69863 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,7 +43,7 @@ markdown_extensions: extra_css: - css/custom.css extra_javascript: - - js/mermaid.mjs + - js/diagram-animation.js plugins: - search - git-revision-date-localized diff --git a/requirements.txt b/requirements.txt index a787b430b..d91c01a0b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,34 +1 @@ -Babel==2.11.0 -certifi==2024.7.4 -charset-normalizer==3.0.1 -click==8.1.3 -colorama==0.4.6 -ghp-import==2.1.0 -gitdb==4.0.10 -GitPython==3.1.41 -idna==3.7 -Jinja2==3.1.6 -Markdown==3.10 -MarkupSafe==2.1.2 -mergedeep==1.3.4 -mkdocs==1.6.1 -mkdocs-get-deps==0.2.0 -mkdocs-git-revision-date-localized-plugin==1.1.0 -mkdocs-material==9.7.6 -mkdocs-material-extensions==1.3.1 -mkdocs-redirects==1.2.1 -packaging==23.0 -pathspec==1.0.1 -platformdirs==4.5.1 -Pygments==2.15.0 -pymdown-extensions==10.20 -python-dateutil==2.8.2 -pytz==2022.7.1 -PyYAML==6.0.1 -pyyaml_env_tag==0.1 -regex==2022.10.31 -requests==2.32.4 -six==1.16.0 -smmap==5.0.0 -urllib3==2.6.3 -watchdog==2.2.1 +zensical==0.0.29 From 3065f2a88f194e09c5932bbe8045726780f177e4 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Thu, 26 Mar 2026 16:18:23 -0400 Subject: [PATCH 03/15] work on docs --- docs/css/custom.css | 55 +++++- docs/js/diagram-animation.js | 220 ++++++++++++++++++------ docs/js/mermaid-init.mjs | 27 ++- docs/technical-documentation/diagram.md | 100 ++++++++++- mkdocs.yml | 1 + 5 files changed, 346 insertions(+), 57 deletions(-) diff --git a/docs/css/custom.css b/docs/css/custom.css index 0c23a6fc4..4961a655d 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -1,4 +1,3 @@ - .md-typeset .admonition.islandora, .md-typeset details.islandora { border-color: rgb(194, 19, 19); @@ -22,13 +21,65 @@ --text: #f5fffa; } -/** mermaid animation helper for architecture diagram **/ +/** Mermaid animation helpers for stepped flow diagrams. */ @keyframes dash { to { stroke-dashoffset: -100; } } +/** + * Sequential flow animation for Mermaid diagrams. + * + * Author diagrams by assigning flow step classes to edges: + * + * class e1 flow0; + * class e2 flow1; + * class e3 flow2; + * + * The JavaScript runtime finds flow0..flow10-style classes, applies the dash + * styling, and animates those steps in sequence. Edges sharing the same flowN + * value animate in parallel. By default each step lasts 3 s, followed by a 2 s + * pause before the full sequence repeats. + */ +@keyframes dashFlash { + 0% { stroke-dashoffset: 900; } + 12.5% { stroke-dashoffset: -100; } + 12.6% { stroke-dashoffset: 900; } + 100% { stroke-dashoffset: 900; } +} + +/** Play / Pause / Restart controls injected below animated diagrams **/ +.mermaid-controls { + display: flex; + gap: 0.3rem; + justify-content: center; + margin-top: 0.5rem; +} + +.mermaid-controls .mermaid-btn { + appearance: none; + background: transparent; + border: 1px solid currentColor; + border-radius: 3px; + cursor: pointer; + font-size: 0.8rem; + line-height: 1; + opacity: 0.35; + padding: 0.25rem 0.5rem; + transition: opacity 0.15s; +} + +.mermaid-controls .mermaid-btn:hover, +.mermaid-controls .mermaid-btn:focus-visible { + opacity: 0.85; +} + +.mermaid-controls .mermaid-btn:disabled { + cursor: default; + opacity: 0.85; +} + @media (prefers-reduced-motion: reduce) { .mermaid * { animation: none !important; diff --git a/docs/js/diagram-animation.js b/docs/js/diagram-animation.js index 5ceae6311..546a6e192 100644 --- a/docs/js/diagram-animation.js +++ b/docs/js/diagram-animation.js @@ -1,78 +1,202 @@ +/** + * Sequential flow animation for Mermaid diagrams. + * + * Listens for the `mermaid:rendered` custom event dispatched by mermaid-init.mjs + * and animates any edges assigned to flow steps such as `flow0`, `flow1`, etc. + * + * Author diagrams by assigning step classes to edge IDs: + * class e1 flow0; + * class e2 flow1; + */ (() => { - const timings = [ - [3000, 0], - [3000, 1000], - [3000, 2000], - [3000, 3000], - [3000, 4000], - ]; + const STEP_DURATION = 1500; + const PAUSE_DURATION = 100; + const EDGE_SELECTOR = "path"; const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); - const findEdges = () => Array.from(document.querySelectorAll(".mermaid svg path[data-edge=true]")); - - const stopAnimations = () => { - findEdges().forEach((element) => { - if (element.__islandoraDashAnimation) { - element.__islandoraDashAnimation.cancel(); - element.__islandoraDashAnimation = null; + const getStep = (element) => { + let el = element; + while (el) { + for (const cls of el.classList) { + const match = cls.match(/^flow(\d+)$/); + if (match) return parseInt(match[1], 10); } - element.style.animation = "none"; - element.style.strokeDasharray = "9,5"; - element.style.strokeDashoffset = "0"; - }); + el = el.parentElement; + } + return null; }; - const startAnimations = () => { - findEdges().forEach((element, index) => { - const [duration, delay] = timings[index % timings.length]; + const getEdgeSelector = (edgeId) => { + const escaped = window.CSS?.escape ? window.CSS.escape(edgeId) : edgeId; + return `[id="${escaped}"], [id$="-${escaped}"]`; + }; - if (element.__islandoraDashAnimation) { - element.__islandoraDashAnimation.cancel(); - } + const getSteppedEdges = (container) => { + const flowSteps = container.__islandoraFlowSteps || {}; + const mappedEdges = Object.entries(flowSteps) + .map(([edgeId, step]) => { + const el = container.querySelector(getEdgeSelector(edgeId)); + return el ? { el, step } : null; + }) + .filter(Boolean); + + if (mappedEdges.length > 0) { + return mappedEdges; + } + + return Array.from(new Set(container.querySelectorAll(EDGE_SELECTOR))) + .map((el) => ({ el, step: getStep(el) })) + .filter(({ step }) => step !== null); + }; + + const animateContainer = (container) => { + const steppedEdges = getSteppedEdges(container); + if (steppedEdges.length === 0) return false; - element.style.animation = "none"; - element.style.strokeDasharray = "9,5"; - element.style.strokeDashoffset = "900"; + const maxStep = Math.max(...steppedEdges.map(({ step }) => step)); + const totalCycle = (maxStep + 1) * STEP_DURATION + PAUSE_DURATION; + const activeRatio = STEP_DURATION / totalCycle; - element.__islandoraDashAnimation = element.animate( + container.__islandoraAnimations = steppedEdges.map(({ el, step }) => { + el.__islandoraDashAnimation?.cancel(); + el.style.animation = "none"; + el.style.strokeDasharray = "9,5"; + el.style.strokeDashoffset = "900"; + + const anim = el.animate( [ - { strokeDashoffset: 900 }, - { strokeDashoffset: 0 }, + { strokeDashoffset: 900, offset: 0 }, + { strokeDashoffset: -100, offset: activeRatio }, + { strokeDashoffset: 900, offset: Math.min(activeRatio + 0.001, 1) }, + { strokeDashoffset: 900, offset: 1 }, ], { - duration, - delay, + duration: totalCycle, + delay: step * STEP_DURATION, easing: "linear", iterations: Infinity, - fill: "forwards", - }, + } ); + + el.__islandoraDashAnimation = anim; + return anim; + }); + + return true; + }; + + const stopContainer = (container) => { + container.querySelectorAll(EDGE_SELECTOR).forEach((el) => { + el.__islandoraDashAnimation?.cancel(); + el.__islandoraDashAnimation = null; + el.style.animation = ""; + el.style.strokeDasharray = ""; + el.style.strokeDashoffset = ""; + }); + container.__islandoraAnimations = null; + }; + + const injectControls = (container) => { + container.querySelector(".mermaid-controls")?.remove(); + + const controls = document.createElement("div"); + controls.className = "mermaid-controls"; + controls.setAttribute("role", "group"); + controls.setAttribute("aria-label", "Diagram animation controls"); + + let playing = true; + + const makeBtn = (text, label) => { + const btn = document.createElement("button"); + btn.className = "mermaid-btn"; + btn.type = "button"; + btn.textContent = text; + btn.setAttribute("aria-label", label); + return btn; + }; + + const playBtn = makeBtn("▶", "Play animation"); + const pauseBtn = makeBtn("⏸", "Pause animation"); + const restartBtn = makeBtn("↺", "Restart animation"); + const syncUI = () => { + playBtn.disabled = playing; + pauseBtn.disabled = !playing; + }; + + playBtn.addEventListener("click", () => { + playing = true; + syncUI(); + container.__islandoraAnimations?.forEach((animation) => animation.play()); + }); + + pauseBtn.addEventListener("click", () => { + playing = false; + syncUI(); + container.__islandoraAnimations?.forEach((animation) => animation.pause()); + }); + + restartBtn.addEventListener("click", () => { + playing = true; + syncUI(); + animateContainer(container); }); + + syncUI(); + controls.append(playBtn, pauseBtn, restartBtn); + container.prepend(controls); }; - const applyEdgeMotion = () => { - if (reduceMotion.matches) { - stopAnimations(); - return; + const activateContainer = (container) => { + if (reduceMotion.matches) return; + const hasSteps = animateContainer(container); + if (hasSteps && !container.querySelector(".mermaid-controls")) { + injectControls(container); } + }; - startAnimations(); + document.addEventListener("mermaid:rendered", ({ detail: container }) => { + activateContainer(container); + }); + + const onMotionChange = () => { + document.querySelectorAll(".mermaid").forEach((container) => { + if (reduceMotion.matches) { + stopContainer(container); + container.querySelector(".mermaid-controls")?.remove(); + } else { + activateContainer(container); + } + }); + }; + + const observeMermaidContainers = (root) => { + if (!(root instanceof Element || root instanceof Document)) return; + if (root instanceof Element && root.matches(".mermaid")) { + activateContainer(root); + } + root.querySelectorAll?.(".mermaid").forEach((container) => { + activateContainer(container); + }); }; - const run = () => window.requestAnimationFrame(applyEdgeMotion); + observeMermaidContainers(document); - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", run, { once: true }); - } else { - run(); - } + const observer = new MutationObserver((records) => { + records.forEach((record) => { + record.addedNodes.forEach((node) => { + observeMermaidContainers(node); + }); + }); + }); - new MutationObserver(run).observe(document.body, { childList: true, subtree: true }); + if (document.body) { + observer.observe(document.body, { childList: true, subtree: true }); + } if (typeof reduceMotion.addEventListener === "function") { - reduceMotion.addEventListener("change", run); + reduceMotion.addEventListener("change", onMotionChange); } else if (typeof reduceMotion.addListener === "function") { - reduceMotion.addListener(run); + reduceMotion.addListener(onMotionChange); } })(); diff --git a/docs/js/mermaid-init.mjs b/docs/js/mermaid-init.mjs index 29fd31d66..814794437 100644 --- a/docs/js/mermaid-init.mjs +++ b/docs/js/mermaid-init.mjs @@ -1,17 +1,31 @@ -import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"; -import elkLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0/dist/mermaid-layout-elk.esm.min.mjs"; +import mermaid from "https://unpkg.com/mermaid@11.13.0/dist/mermaid.esm.min.mjs"; -mermaid.registerLayoutLoaders(elkLayouts); mermaid.initialize({ startOnLoad: false, securityLevel: "loose", - layout: "elk", }); +const parseFlowSteps = (source) => { + const flowSteps = {}; + const pattern = /^\s*class\s+([^;]+?)\s+flow(\d+)\s*;/gm; + let match; + + while ((match = pattern.exec(source)) !== null) { + const ids = match[1].split(",").map((id) => id.trim()).filter(Boolean); + const step = parseInt(match[2], 10); + ids.forEach((id) => { + flowSteps[id] = step; + }); + } + + return flowSteps; +}; + const renderDiagram = async (container, index) => { if (container.dataset.mermaidRendered === "true") { return; } + container.dataset.mermaidRendered = "true"; const code = container.querySelector("code"); const source = code ? code.textContent : container.textContent; @@ -19,8 +33,10 @@ const renderDiagram = async (container, index) => { return; } + const flowSteps = parseFlowSteps(source); const renderTarget = document.createElement("div"); renderTarget.className = "mermaid"; + renderTarget.__islandoraFlowSteps = flowSteps; try { const result = await mermaid.render(`islandora-mermaid-${index}`, source); @@ -43,3 +59,6 @@ if (document.readyState === "loading") { } else { renderAll(); } + +// Required for Zensical/Material to find the mermaid instance. +window.mermaid = mermaid; diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index 9fdf665de..b07c5ef60 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -1,8 +1,104 @@ # Islandora Architecture Diagram +## Site Serving Path + +### Page Request + +```mermaid +flowchart TD + user([Client / Browser]) + user e1@==>|HTTP request| nginx + + subgraph webserver[Nginx Web Server] + nginx[Nginx] e2@==>|forward request| drupal[Drupal] + end + + drupal e3@-->|query| mariadb[(MariaDB)] + drupal e4@-->|query| solr[(Solr)] + drupal e5@-->|image request| cantaloupe[Cantaloupe\nIIIF Image Server] + + mariadb e6@-.->|data| drupal + solr e7@-.->|results| drupal + cantaloupe e8@-.->|image| drupal + drupal e9@-.->|HTML response| nginx + nginx e10@-.->|HTML response| user + + classDef flow0 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 0s linear infinite; + classDef flow1 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 1s linear infinite; + classDef flow2 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 2s linear infinite; + classDef flow3 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 3s linear infinite; + classDef flow4 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 4s linear infinite; + classDef flow5 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 5s linear infinite; + + class e1 flow0; + class e2 flow1; + class e3 flow2; + class e4 flow2; + class e5 flow2; + class e6 flow3; + class e7 flow3; + class e8 flow3; + class e9 flow4; + class e10 flow5; +``` + +### IIIF Image Request + ```mermaid flowchart TD - drupal([fa:fa-drupal Islandora Drupal Website]) + user([Client / Browser]) + user --> cantaloupe[Cantaloupe\nIIIF Image Server] + cantaloupe -.->|fetch source file| fedora[(Fedora\nFile Storage)] +``` + +## Event Driven System + +```mermaid +flowchart TD + drupal([Islandora Drupal Website]) + + drupal ==>|publishes drupal entity event| activemq + + subgraph broker[Message Broker] + activemq[ActiveMQ] + alpaca[Alpaca] + activemq ==> alpaca + note[\"Alpaca forwards the message to the\nappropriate service based on its config"/] + end + + + subgraph microservices[scyllaridae microservices] + fits[FITS] + homarus[Homarus] + houdini[Houdini] + hypercube[Hypercube] + + s[\"scyllaridae generates a derivative and saves the message to the\nappropriate service based on its config"/] + + end + + alpaca ==> fits + alpaca ==> homarus + alpaca ==> houdini + alpaca ==> hypercube + alpaca ==> milliner + + fits -.->|derivative saved to| alpaca + homarus -.->|derivative saved to| alpaca + houdini -.->|derivative saved to| alpaca + hypercube -.->|derivative saved to| alpaca +alpaca -.->|ok| drupal + fedora[(Fedora)] + milliner -.->|syncs resource to| fedora + blazegraph[(blazegraph)] + alpaca -.->|sends RDF| blazegraph +``` + +## Event Example + +```mermaid +flowchart TD + drupal([Islandora Drupal Website]) drupal e1@==>|publishes entity event| activemq @@ -34,8 +130,6 @@ flowchart TD class e5 flow5; ``` -Diagram prepared by [Bethany Seeger](https://github.com/bseeger) based on work done by [Gavin Morris](https://github.com/g7morris) - ## Components ### Islandora diff --git a/mkdocs.yml b/mkdocs.yml index bd2c69863..d43461079 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,7 @@ markdown_extensions: extra_css: - css/custom.css extra_javascript: + - js/mermaid-init.mjs - js/diagram-animation.js plugins: - search From 6efc770a8377b88c25e4c806029186da1e8bff8b Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 13:45:55 -0400 Subject: [PATCH 04/15] Leverage zenscial snippets from our glossary --- Makefile | 5 +- docs/technical-documentation/diagram.md | 67 +++++---- includes/abbreviations.md | 174 ++++++++++++++++++++++++ mkdocs.yml | 107 +++++++++++---- scripts/generate-abbreviations.sh | 63 +++++++++ 5 files changed, 365 insertions(+), 51 deletions(-) create mode 100644 includes/abbreviations.md create mode 100755 scripts/generate-abbreviations.sh diff --git a/Makefile b/Makefile index dfca1e3fa..e37ecac5d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ -.PHONY: docs docs-build +.PHONY: docs docs-build abbreviations + +abbreviations: + ./scripts/generate-abbreviations.sh docs-build: docker build -t islandora-docs . diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index b07c5ef60..8d442f808 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -2,8 +2,10 @@ ## Site Serving Path -### Page Request - +When a client visits an Islandora website, the request flow looks like a typical Drupal request. +The request is received by an nginx web server, which forwards the request to `php-fpm` process that points to a Drupal codebase. +Drupal bootstraps and queries the backend database (Islandora ships with `mariadb`). +If the request was for a search page, Drupal make also query `solr` to include search results in the HTML response. ```mermaid flowchart TD user([Client / Browser]) @@ -15,21 +17,12 @@ flowchart TD drupal e3@-->|query| mariadb[(MariaDB)] drupal e4@-->|query| solr[(Solr)] - drupal e5@-->|image request| cantaloupe[Cantaloupe\nIIIF Image Server] mariadb e6@-.->|data| drupal solr e7@-.->|results| drupal - cantaloupe e8@-.->|image| drupal drupal e9@-.->|HTML response| nginx nginx e10@-.->|HTML response| user - classDef flow0 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 0s linear infinite; - classDef flow1 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 1s linear infinite; - classDef flow2 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 2s linear infinite; - classDef flow3 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 3s linear infinite; - classDef flow4 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 4s linear infinite; - classDef flow5 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dashFlash 8s 5s linear infinite; - class e1 flow0; class e2 flow1; class e3 flow2; @@ -40,15 +33,41 @@ flowchart TD class e8 flow3; class e9 flow4; class e10 flow5; + ``` -### IIIF Image Request + +### IIIF manifests + +For some items in Islandora, the HTML response may include a [IIIF Manifest]. This typically happens when showing items that have image media. For those requests, Islandora's IIIF module may also query the [Cantaloupe] IIIF server for metadata needed to generate a IIIF manifest. The HTML response will then reference the location of the images in the IIIF server to allow a IIIF viewer to render the image in the browser. Practically what this means is in addition to the typical drupal request flow, Drupal may also query the IIIF server for basic image metadata (e.g. width/height) which are needed in the IIIF manifest response. The client web browser will then read that manifest using Javascript and the IIIF viewer will `GET` the images referenced in the IIIF manifest directly from the IIIF server. ```mermaid flowchart TD user([Client / Browser]) - user --> cantaloupe[Cantaloupe\nIIIF Image Server] - cantaloupe -.->|fetch source file| fedora[(Fedora\nFile Storage)] + user e1@==>|HTTP request| nginx + + subgraph webserver[Nginx Web Server] + nginx[Nginx] e2@==>|forward request| drupal[Drupal] + end + + drupal e3@-->|GET /info.json| cantaloupe[Cantaloupe IIIF Image Server] + + cantaloupe e4@-->|info.json| drupal + drupal e5@-.->|HTML response| nginx + nginx e6@-.->|HTML response| user + user e7@-->|GET /image.png| cantaloupe + cantaloupe e8@-.->|image.png| user + + class e1 flow0; + class e2 flow1; + class e3 flow2; + class e4 flow3; + class e5 flow4; + class e6 flow5; + class e7 flow6; + class e8 flow7; + class e9 flow8; + ``` ## Event Driven System @@ -96,6 +115,8 @@ alpaca -.->|ok| drupal ## Event Example +As an example, when an Islandora Repository manager uploades an image to Islandora, an event is emitted to generate a thumbnail for that image. That event is put on the event queue, alpaca reads the message from the queue, and forwards the event to the configured service. In the case of a thumbnail, houdini handles generating the thumbnail for the uploaded image + ```mermaid flowchart TD drupal([Islandora Drupal Website]) @@ -110,24 +131,18 @@ flowchart TD alpaca e3@==> houdini - subgraph microservices[scyllaridae microservices] + subgraph microservices[scyllaridae microservice] houdini[Houdini] end houdini e4@-.->|derivative stream| alpaca alpaca e5@-.->|saves derivative & responds ok| drupal - classDef flow1 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s linear infinite; - classDef flow2 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 1s linear infinite; - classDef flow3 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 2s linear infinite; - classDef flow4 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 3s linear infinite; - classDef flow5 stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 3s 4s linear infinite; - - class e1 flow1; - class e2 flow2; - class e3 flow3; - class e4 flow4; - class e5 flow5; + class e1 flow0; + class e2 flow1; + class e3 flow2; + class e4 flow3; + class e5 flow4; ``` ## Components diff --git a/includes/abbreviations.md b/includes/abbreviations.md new file mode 100644 index 000000000..9e729b18e --- /dev/null +++ b/includes/abbreviations.md @@ -0,0 +1,174 @@ +[Alpaca]: ../user-documentation/glossary.md#alpaca +[Alpacas]: ../user-documentation/glossary.md#alpaca +[Ansible]: ../user-documentation/glossary.md#ansible +[Ansibles]: ../user-documentation/glossary.md#ansible +[Apache]: ../user-documentation/glossary.md#apache +[Apaches]: ../user-documentation/glossary.md#apache +[API]: ../user-documentation/glossary.md#api +[APIs]: ../user-documentation/glossary.md#api +[Application Programming Interface]: ../user-documentation/glossary.md#application-programming-interface +[Application Programming Interfaces]: ../user-documentation/glossary.md#application-programming-interface +[Blazegraph]: ../user-documentation/glossary.md#blazegraph +[Blazegraphs]: ../user-documentation/glossary.md#blazegraph +[Bundle]: ../user-documentation/glossary.md#bundle +[Bundles]: ../user-documentation/glossary.md#bundle +[Cantaloupe]: ../user-documentation/glossary.md#cantaloupe +[Cantaloupes]: ../user-documentation/glossary.md#cantaloupe +[Checksum]: ../user-documentation/glossary.md#checksum +[Checksums]: ../user-documentation/glossary.md#checksum +[CLAW]: ../user-documentation/glossary.md#claw +[CLAWs]: ../user-documentation/glossary.md#claw +[Collection]: ../user-documentation/glossary.md#collection +[Collections]: ../user-documentation/glossary.md#collection +[Configuration]: ../user-documentation/glossary.md#configuration +[Configurations]: ../user-documentation/glossary.md#configuration +[Configuration entity]: ../user-documentation/glossary.md#configuration-entity +[Configuration entitys]: ../user-documentation/glossary.md#configuration-entity +[Content]: ../user-documentation/glossary.md#content +[Contents]: ../user-documentation/glossary.md#content +[Content entity]: ../user-documentation/glossary.md#content-entity +[Content entitys]: ../user-documentation/glossary.md#content-entity +[Content model]: ../user-documentation/glossary.md#content-model +[Content models]: ../user-documentation/glossary.md#content-model +[Content type]: ../user-documentation/glossary.md#content-type +[Content types]: ../user-documentation/glossary.md#content-type +[Context]: ../user-documentation/glossary.md#context +[Contexts]: ../user-documentation/glossary.md#context +[Crayfish]: ../user-documentation/glossary.md#crayfish +[Crayfishs]: ../user-documentation/glossary.md#crayfish +[Datastream]: ../user-documentation/glossary.md#datastream +[Datastreams]: ../user-documentation/glossary.md#datastream +[Derivative]: ../user-documentation/glossary.md#derivative +[Derivatives]: ../user-documentation/glossary.md#derivative +[Docker]: ../user-documentation/glossary.md#docker +[Dockers]: ../user-documentation/glossary.md#docker +[Drupal]: ../user-documentation/glossary.md#drupal +[Drupals]: ../user-documentation/glossary.md#drupal +[Drupal Core]: ../user-documentation/glossary.md#drupal-core +[Drupal Cores]: ../user-documentation/glossary.md#drupal-core +[Drupal Roles]: ../user-documentation/glossary.md#drupal-roles +[Entity]: ../user-documentation/glossary.md#entity +[Entitys]: ../user-documentation/glossary.md#entity +[Fedora (Repository Software)]: ../user-documentation/glossary.md#fedora-repository-software +[Fedora (Repository Software)s]: ../user-documentation/glossary.md#fedora-repository-software +[FFmpeg]: ../user-documentation/glossary.md#ffmpeg +[FFmpegs]: ../user-documentation/glossary.md#ffmpeg +[Field]: ../user-documentation/glossary.md#field +[Fields]: ../user-documentation/glossary.md#field +[Field instance]: ../user-documentation/glossary.md#field-instance +[Field instances]: ../user-documentation/glossary.md#field-instance +[Field formatter]: ../user-documentation/glossary.md#field-formatter +[Field formatters]: ../user-documentation/glossary.md#field-formatter +[Field type]: ../user-documentation/glossary.md#field-type +[Field types]: ../user-documentation/glossary.md#field-type +[Field Storage]: ../user-documentation/glossary.md#field-storage +[Field Storages]: ../user-documentation/glossary.md#field-storage +[FITS]: ../user-documentation/glossary.md#fits +[Fixity]: ../user-documentation/glossary.md#fixity +[Fixitys]: ../user-documentation/glossary.md#fixity +[Flysystem]: ../user-documentation/glossary.md#flysystem +[Flysystems]: ../user-documentation/glossary.md#flysystem +[GLAM]: ../user-documentation/glossary.md#glam +[GLAMs]: ../user-documentation/glossary.md#glam +[GUI]: ../user-documentation/glossary.md#gui +[GUIs]: ../user-documentation/glossary.md#gui +[Greenfield]: ../user-documentation/glossary.md#greenfield +[Greenfields]: ../user-documentation/glossary.md#greenfield +[Imagemagick]: ../user-documentation/glossary.md#imagemagick +[Imagemagicks]: ../user-documentation/glossary.md#imagemagick +[hOCR]: ../user-documentation/glossary.md#hocr +[hOCRs]: ../user-documentation/glossary.md#hocr +[Homarus]: ../user-documentation/glossary.md#homarus +[Houdini]: ../user-documentation/glossary.md#houdini +[Houdinis]: ../user-documentation/glossary.md#houdini +[Hypercube]: ../user-documentation/glossary.md#hypercube +[Hypercubes]: ../user-documentation/glossary.md#hypercube +[IIIF]: ../user-documentation/glossary.md#iiif +[IIIFs]: ../user-documentation/glossary.md#iiif +[IIIF Manifest]: ../user-documentation/glossary.md#iiif-manifest +[IIIF Manifests]: ../user-documentation/glossary.md#iiif-manifest +[Ingest]: ../user-documentation/glossary.md#ingest +[Ingests]: ../user-documentation/glossary.md#ingest +[Islandora 8 (8.x, 2.0)]: ../user-documentation/glossary.md#islandora-8-8x-20 +[Islandora 8 (8.x, 2.0)s]: ../user-documentation/glossary.md#islandora-8-8x-20 +[Islandora Install Profile]: ../user-documentation/glossary.md#islandora-install-profile +[Islandora Install Profiles]: ../user-documentation/glossary.md#islandora-install-profile +[Islandora Starter Site]: ../user-documentation/glossary.md#islandora-starter-site +[Islandora Starter Sites]: ../user-documentation/glossary.md#islandora-starter-site +[Islandora model]: ../user-documentation/glossary.md#islandora-model +[Islandora models]: ../user-documentation/glossary.md#islandora-model +[Islandora playbook]: ../user-documentation/glossary.md#islandora-playbook +[Islandora playbooks]: ../user-documentation/glossary.md#islandora-playbook +[ISLE]: ../user-documentation/glossary.md#isle +[ISLEs]: ../user-documentation/glossary.md#isle +[JSON-LD]: ../user-documentation/glossary.md#json-ld +[JSON-LDs]: ../user-documentation/glossary.md#json-ld +[Linked data]: ../user-documentation/glossary.md#linked-data +[Linked datas]: ../user-documentation/glossary.md#linked-data +[Manifest]: ../user-documentation/glossary.md#manifest +[Manifests]: ../user-documentation/glossary.md#manifest +[Matomo]: ../user-documentation/glossary.md#matomo +[Matomos]: ../user-documentation/glossary.md#matomo +[Media]: ../user-documentation/glossary.md#media +[Medias]: ../user-documentation/glossary.md#media +[Memento]: ../user-documentation/glossary.md#memento +[Mementos]: ../user-documentation/glossary.md#memento +[Mirador]: ../user-documentation/glossary.md#mirador +[Miradors]: ../user-documentation/glossary.md#mirador +[Microservice]: ../user-documentation/glossary.md#microservice +[Microservices]: ../user-documentation/glossary.md#microservice +[Module]: ../user-documentation/glossary.md#module +[Modules]: ../user-documentation/glossary.md#module +[Node]: ../user-documentation/glossary.md#node +[Nodes]: ../user-documentation/glossary.md#node +[OAI-PMH]: ../user-documentation/glossary.md#oai-pmh +[OAI-PMHs]: ../user-documentation/glossary.md#oai-pmh +[Ontology]: ../user-documentation/glossary.md#ontology +[Ontologys]: ../user-documentation/glossary.md#ontology +[Open Source]: ../user-documentation/glossary.md#open-source +[Open Sources]: ../user-documentation/glossary.md#open-source +[OpenSeadragon]: ../user-documentation/glossary.md#openseadragon +[OpenSeadragons]: ../user-documentation/glossary.md#openseadragon +[PR]: ../user-documentation/glossary.md#pr +[PRs]: ../user-documentation/glossary.md#pr +[Pull request]: ../user-documentation/glossary.md#pull-request +[Pull requests]: ../user-documentation/glossary.md#pull-request +[RDF]: ../user-documentation/glossary.md#rdf +[RDFs]: ../user-documentation/glossary.md#rdf +[Repository Item]: ../user-documentation/glossary.md#repository-item +[Repository Items]: ../user-documentation/glossary.md#repository-item +[Resource Description Framework]: ../user-documentation/glossary.md#resource-description-framework +[Resource Description Frameworks]: ../user-documentation/glossary.md#resource-description-framework +[Resource Node]: ../user-documentation/glossary.md#resource-node +[Resource Nodes]: ../user-documentation/glossary.md#resource-node +[Source Field]: ../user-documentation/glossary.md#source-field +[Source Fields]: ../user-documentation/glossary.md#source-field +[Taxonomy term]: ../user-documentation/glossary.md#taxonomy-term +[Taxonomy terms]: ../user-documentation/glossary.md#taxonomy-term +[Tesseract]: ../user-documentation/glossary.md#tesseract +[Tesseracts]: ../user-documentation/glossary.md#tesseract +[Theme]: ../user-documentation/glossary.md#theme +[Themes]: ../user-documentation/glossary.md#theme +[Vagrant]: ../user-documentation/glossary.md#vagrant +[Vagrants]: ../user-documentation/glossary.md#vagrant +[VBO]: ../user-documentation/glossary.md#vbo +[VBOs]: ../user-documentation/glossary.md#vbo +[View]: ../user-documentation/glossary.md#view +[Views]: ../user-documentation/glossary.md#view +[View Mode]: ../user-documentation/glossary.md#view-mode +[View Modes]: ../user-documentation/glossary.md#view-mode +[Viewer]: ../user-documentation/glossary.md#viewer +[Viewers]: ../user-documentation/glossary.md#viewer +[Views Bulk Operations]: ../user-documentation/glossary.md#views-bulk-operations +[Virtual Machine Image]: ../user-documentation/glossary.md#virtual-machine-image +[Virtual Machine Images]: ../user-documentation/glossary.md#virtual-machine-image +[Vocabulary]: ../user-documentation/glossary.md#vocabulary +[Vocabularys]: ../user-documentation/glossary.md#vocabulary +[Weight]: ../user-documentation/glossary.md#weight +[Weights]: ../user-documentation/glossary.md#weight +[White Screen of Death ]: ../user-documentation/glossary.md#white-screen-of-death +[White Screen of Death s]: ../user-documentation/glossary.md#white-screen-of-death +[WSoD]: ../user-documentation/glossary.md#wsod +[WSoDs]: ../user-documentation/glossary.md#wsod +[YAML]: ../user-documentation/glossary.md#yaml +[YAMLs]: ../user-documentation/glossary.md#yaml diff --git a/mkdocs.yml b/mkdocs.yml index d43461079..703c7e8e6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,4 @@ site_name: Islandora -site_dir: site -docs_dir: docs site_description: Documentation for Islandora dev_addr: 'localhost:8111' @@ -9,36 +7,94 @@ site_url: https://islandora.github.io/documentation/ edit_uri: 'edit/main/docs/' theme: - name: 'material' - palette: - primary: 'green' - accent: 'blue' - font: - text: 'Roboto' - code: 'Roboto Mono' - logo: 'assets/Islandora_logo.png' - language: 'en' + name: material features: - content.action.edit + - content.action.view + - content.code.annotate + - content.code.copy + - content.code.select + - content.footnote.tooltips + - content.tabs.link + - content.tooltips + - search.highlight + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: lucide/sun-moon + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: indigo + toggle: + icon: lucide/sun + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + accent: orange + toggle: + icon: lucide/moon-star + name: Switch to system preference icon: - edit: material/pencil + tag: + default: lucide/hash + logo: 'assets/Islandora_logo.png' + markdown_extensions: + - abbr - admonition + - attr_list + - def_list - footnotes + - md_in_html - toc: - permalink: True + permalink: true + - zensical.extensions.preview: + targets: + include: + - user-documentation/glossary.md + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:zensical.extensions.emoji.to_svg + emoji_index: !!python/name:zensical.extensions.emoji.twemoji - pymdownx.highlight: anchor_linenums: true line_spans: __span pygments_lang_class: true - pymdownx.inlinehilite - - pymdownx.snippets + - pymdownx.keys + - pymdownx.magiclink: + normalize_issue_symbols: true + repo_url_shorthand: true + user: zensical + repo: zensical + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.snippets: + auto_append: + - includes/abbreviations.md - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + combine_header_slug: true + slugify: !!python/object/apply:pymdownx.slugs.slugify + kwds: + case: lower + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + extra_css: - css/custom.css @@ -47,6 +103,7 @@ extra_javascript: - js/diagram-animation.js plugins: - search + - tags - git-revision-date-localized - redirects: redirect_maps: @@ -89,17 +146,19 @@ plugins: extra: - font: - text: 'Roboto' - code: 'Roboto Mono' - author: - twitter: 'islandora' - palette: - primary: 'red' - accent: 'red' + scope: / + annotate: + json: [.s2] + status: + new: Recently added + deprecated: Deprecated social: - - icon: fontawesome/brands/twitter - link: 'https://twitter.com/islandora' + - icon: fontawesome/brands/github + link: https://github.com/islandora + - icon: fontawesome/brands/bluesky + link: https://bsky.app/profile/islandora.bsky.social + tags: + default: default copyright: This documentation is user-sourced! Suggestions and comments are welcome in our issue queue. diff --git a/scripts/generate-abbreviations.sh b/scripts/generate-abbreviations.sh new file mode 100755 index 000000000..bca41b77a --- /dev/null +++ b/scripts/generate-abbreviations.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +GLOSSARY_FILE="${1:-$ROOT_DIR/docs/user-documentation/glossary.md}" +OUTPUT_FILE="${2:-$ROOT_DIR/includes/abbreviations.md}" +TARGET_PATH="../user-documentation/glossary.md" + +declare -A seen_labels=() + +if [[ ! -f "$GLOSSARY_FILE" ]]; then + printf 'Glossary file not found: %s\n' "$GLOSSARY_FILE" >&2 + exit 1 +fi + +mkdir -p "$(dirname "$OUTPUT_FILE")" + +slugify() { + local value="$1" + + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')" + value="$(printf '%s' "$value" | sed -E 's/[^a-z0-9 _-]+//g')" + value="$(printf '%s' "$value" | sed -E 's/[[:space:]_-]+/-/g; s/^-+//; s/-+$//')" + + printf '%s\n' "$value" +} + +emit_label() { + local label="$1" + local slug="$2" + + [[ -n $label ]] || return + if [[ -v "seen_labels[$label]" ]]; then + return + fi + + printf '[%s]: %s#%s\n' "$label" "$TARGET_PATH" "$slug" + seen_labels["$label"]=1 +} + +pluralize() { + local value="$1" + + if [[ $value =~ [sS]$ ]]; then + printf '%s\n' "$value" + return + fi + + printf '%ss\n' "$value" +} + +while IFS= read -r line; do + [[ $line == '## '* ]] || continue + + heading="${line#\#\# }" + slug="$(slugify "$heading")" + + [[ -n $slug ]] || continue + + emit_label "$heading" "$slug" + emit_label "$(pluralize "$heading")" "$slug" +done < "$GLOSSARY_FILE" > "$OUTPUT_FILE" From 8b9e18da9aa53ee748507bdcf625b83c4b9d98a3 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 14:08:22 -0400 Subject: [PATCH 05/15] Enlarge mermaid diagrams --- docs/css/custom.css | 50 ++++++++++++++++++++++ docs/js/mermaid-init.mjs | 91 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/docs/css/custom.css b/docs/css/custom.css index 4961a655d..4f793da96 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -49,6 +49,56 @@ 100% { stroke-dashoffset: 900; } } +.mermaid-expandable { + cursor: zoom-in; +} + +.mermaid-expandable:focus-visible { + outline: 2px solid var(--md-accent-fg-color); + outline-offset: 0.3rem; +} + +.mermaid-lightbox { + background: transparent; + border: 0; + max-width: min(96vw, 1400px); + padding: 0; + width: 96vw; +} + +.mermaid-lightbox::backdrop { + background: rgba(0, 0, 0, 0.72); +} + +.mermaid-lightbox__frame { + background: var(--md-default-bg-color); + border-radius: 0.6rem; + box-shadow: var(--md-shadow-z3); + max-height: 90vh; + overflow: hidden; +} + +.mermaid-lightbox__close { + appearance: none; + background: transparent; + border: 1px solid currentColor; + border-radius: 999px; + cursor: pointer; + display: block; + margin: 1rem 1rem 0 auto; + padding: 0.35rem 0.75rem; +} + +.mermaid-lightbox__body { + overflow: auto; + padding: 1rem; +} + +.mermaid-lightbox__body svg { + display: block; + margin: 0 auto; +} + /** Play / Pause / Restart controls injected below animated diagrams **/ .mermaid-controls { display: flex; diff --git a/docs/js/mermaid-init.mjs b/docs/js/mermaid-init.mjs index 814794437..012974db2 100644 --- a/docs/js/mermaid-init.mjs +++ b/docs/js/mermaid-init.mjs @@ -5,6 +5,8 @@ mermaid.initialize({ securityLevel: "loose", }); +const LIGHTBOX_ID = "mermaid-lightbox"; + const parseFlowSteps = (source) => { const flowSteps = {}; const pattern = /^\s*class\s+([^;]+?)\s+flow(\d+)\s*;/gm; @@ -21,6 +23,94 @@ const parseFlowSteps = (source) => { return flowSteps; }; +const ensureLightbox = () => { + let lightbox = document.getElementById(LIGHTBOX_ID); + if (lightbox) { + return lightbox; + } + + lightbox = document.createElement("dialog"); + lightbox.id = LIGHTBOX_ID; + lightbox.className = "mermaid-lightbox"; + lightbox.setAttribute("aria-label", "Expanded Mermaid diagram"); + + const frame = document.createElement("div"); + frame.className = "mermaid-lightbox__frame"; + + const closeButton = document.createElement("button"); + closeButton.type = "button"; + closeButton.className = "mermaid-lightbox__close"; + closeButton.setAttribute("aria-label", "Close expanded diagram"); + closeButton.textContent = "Close"; + closeButton.addEventListener("click", () => { + lightbox.close(); + }); + + const body = document.createElement("div"); + body.className = "mermaid-lightbox__body"; + + frame.append(closeButton, body); + lightbox.append(frame); + + lightbox.addEventListener("click", (event) => { + if (event.target === lightbox) { + lightbox.close(); + } + }); + + document.body.append(lightbox); + return lightbox; +}; + +const openLightbox = (container) => { + const svg = container.querySelector("svg"); + if (!svg) { + return; + } + + const lightbox = ensureLightbox(); + const body = lightbox.querySelector(".mermaid-lightbox__body"); + const clone = svg.cloneNode(true); + + clone.removeAttribute("width"); + clone.removeAttribute("height"); + clone.style.width = "100%"; + clone.style.height = "auto"; + + body.replaceChildren(clone); + + if (!lightbox.open) { + lightbox.showModal(); + } +}; + +const attachExpandBehavior = (container) => { + if (container.dataset.mermaidExpandable === "true") { + return; + } + container.dataset.mermaidExpandable = "true"; + container.classList.add("mermaid-expandable"); + container.tabIndex = 0; + container.setAttribute("role", "button"); + container.setAttribute("aria-label", "Open Mermaid diagram in a larger view"); + container.title = "Click to enlarge diagram"; + + container.addEventListener("click", (event) => { + if (event.target.closest(".mermaid-controls, button, a")) { + return; + } + openLightbox(container); + }); + + container.addEventListener("keydown", (event) => { + if (event.key !== "Enter" && event.key !== " ") { + return; + } + event.preventDefault(); + openLightbox(container); + }); +}; + const renderDiagram = async (container, index) => { if (container.dataset.mermaidRendered === "true") { return; @@ -41,6 +131,7 @@ const renderDiagram = async (container, index) => { try { const result = await mermaid.render(`islandora-mermaid-${index}`, source); renderTarget.innerHTML = result.svg; + attachExpandBehavior(renderTarget); container.replaceWith(renderTarget); document.dispatchEvent(new CustomEvent("mermaid:rendered", { detail: renderTarget })); } catch (error) { From 50be399e5be8d8ba39b98ca445dbdf806460ac95 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 15:17:34 -0400 Subject: [PATCH 06/15] Fixup technical docs --- docs/installation/component-overview.md | 41 ++-- docs/js/diagram-animation.js | 189 ++++++++++++------ docs/js/mermaid-init.mjs | 3 + docs/technical-documentation/alpaca-tips.md | 6 +- docs/technical-documentation/diagram.md | 56 +++--- .../ppa-documentation.md | 143 ------------- docs/technical-documentation/resizing-vm.md | 30 --- docs/user-documentation/glossary.md | 61 +++++- includes/abbreviations.md | 34 +++- mkdocs.yml | 12 +- scripts/generate-abbreviations.sh | 5 + 11 files changed, 266 insertions(+), 314 deletions(-) delete mode 100644 docs/technical-documentation/ppa-documentation.md delete mode 100644 docs/technical-documentation/resizing-vm.md diff --git a/docs/installation/component-overview.md b/docs/installation/component-overview.md index 7e9e421a1..96c4ca220 100644 --- a/docs/installation/component-overview.md +++ b/docs/installation/component-overview.md @@ -9,59 +9,60 @@ This list includes four different kinds of components: - Components that can't easily be swapped out but are not necessarily required (such as using Solr as the site's internal search engine) - Components which do not have official alternatives and are not necessarily required, but will likely exist on the vast majority of Islandora installations (such as Alpaca and Crayfish) -## The Webserver Stack - Apache, PHP, and MySQL/PostgreSQL +## The Webserver Stack - Traefik, NGINX, PHP, and MariaDB/PostgreSQL -Combined together, Apache, PHP, and MySQL/PostgreSQL comprise a LAMP or LAPP server used to provide end-user-facing components - namely, the website. +Combined together, Apache, PHP, and MariaDB/PostgreSQL comprise a LAMP or LAPP server used to provide end-user-facing components - namely, the website. -**Apache** is the webserver that will serve up webpages to the public. It will also manage some internal functionality provided by Crayfish, and will expose Cantaloupe to the public. We’ll be making changes to the VirtualHost entry, enabling some modules, and modifying the ports configuration. The VirtualHost entry will eventually be modified when we need to expose other services like Cantaloupe to the public. +**[Traefik]** acts as the Islandora stack's front-end proxy. It will terminate TLS and routes requests to services in the Islandora stack i.e. Drupal, Cantaloupe, and Fedora. -**PHP** is the runtime interpreter for all the code Drupal and Crayfish need to be processed. By default, installing PHP 7.2 will give us a command-line interpreter, as well as an interpreter for Apache. We’re going to install several PHP modules required and/or useful for the components that make use of PHP. -**MySQL** and **PostgreSQL** are database management systems that we will use to store information for many different components like Drupal and Fedora. By default, the Ansible playbook installs MySQL, though this can be switched to PostgreSQL. The manual installation guide recommends and walks through installing and using PostgreSQL. +**[NGINX]** is the webserver that will serve up webpages to the public. + +**PHP** is the runtime interpreter for all the code Drupal needs to be processed. By default, installing PHP 8.3 will give us a command-line interpreter, as well as an interpreter for NGINX. + +**[MariaDB]** and **[PostgreSQL]** are database management systems that we will use to store information for many different components like Drupal and Fedora. By default, the [ISLE Site Template] installs MariaDB, though this can be switched to PostgreSQL. ## The Front-Facing CDM - Composer, Drush, and Drupal -Composer will be used to install both Drupal and Drush simultaneously using Islandora's fork of the [drupal-project](https://github.com/Islandora/drupal-project) repository. +Composer will be used to install both Drupal and Drush simultaneously using [Islandora Starter Site] -**Composer** is an installer and dependency manager for PHP projects. We're going to need it to install components for any PHP code we need to make use of, including Drupal and Crayfish. +**[Composer]** is an installer and dependency manager for PHP projects. We're going to need it to install components for Drupal. -**Drush** and **Drupal** are installed simultaneously using [drupal-project](https://github.com/Islandora/drupal-project). Drupal will serve up webpages and manage Islandora content, and Drush will help us get some things done from the command-line. +**[Drush]** and **[Drupal]** are installed simultaneously using [Composer]. Drupal will serve up webpages and manage Islandora content, and Drush will help us get some things done from the command-line. ## The Web Application Server - Tomcat and Cantaloupe -Several applets will be deployed via their `.war` files into Tomcat, including Fedora and Cantaloupe. +Several applets will be deployed via their `.war` files into Tomcat, including [Fedora (Repository Software)] and [Cantaloupe]. -**Tomcat** serves up webpages and other kinds of content much like Apache, but is specifically designed to deploy Java applications as opposed to running PHP code. +**[Tomcat]** serves up webpages and other kinds of content much like NGINX, but is specifically designed to deploy Java applications as opposed to running PHP code. -**Cantaloupe** is an image tileserver that Islandora will connect to and use to serve up extremely large images in a way that doesn't have an adverse effect on the overall system. +**[Cantaloupe]** is an image tileserver that Islandora will connect to and use to serve up extremely large images in a way that doesn't have an adverse effect on the overall system. -## The Back-End File Management Repository - Fedora, Syn, and Blazegraph +## The Back-End File Management Repository - Fedora and Blazegraph Fedora will be installed in its own section, rather than as part of the Tomcat installation, as the installation process is rather involved and requires some authorization pieces to be set up in order to connect them back to Drupal and other components. -**Fedora** is the default backend repository that Islandora content will be synchronized with and stored in. A great deal of configuration will be required to get it up and running, including ensuring a database is created and accessible. - -**Syn** is the authorization piece that allows Fedora to connect to other components. +**[Fedora (Repository Software)]** is the default backend repository that Islandora content will be synchronized with and stored in. A great deal of configuration will be required to get it up and running, including ensuring a database is created and accessible. -**Blazegraph** will store representative graph data about the repository that can be queried using SPARQL. Some configuration will also be required to link it back to Fedora, as well as to ensure it is being properly indexed. +**[Blazegraph]** will store representative graph data about the repository that can be queried using SPARQL. Some configuration will also be required to link it back to Fedora, as well as to ensure it is being properly indexed. ## The Search Engine - Solr and search_api_solr The installation of Solr itself is rather straightforward, but a configuration will have to be generated and applied from the Drupal side. -**Solr** will be installed as a standalone application. Nothing of particular importance needs to happen here; the configuration will be applied when `search_api_solr` is installed. +**[Solr]** will be installed as a standalone application. Nothing of particular importance needs to happen here; the configuration will be applied when `search_api_solr` is installed. **search_api_solr** is the Drupal module that implements the Solr API for Drupal-side searches. After installing and configuring the module, the `drush solr-gsc` command will be used to generate Solr configs, and these configs will be moved to the Solr configuration location. -## The Asynchronous Background Services - Crayfish +## The Asynchronous Background Services - scyllaridae -**Crayfish** is a series of microservices that perform different asynchronous tasks kicked off by Islandora. It contains a series of submodules that will be installed via Composer. Later, these configured components will be connected to Alpaca. +**[scyllaridae]** is a series of microservices that perform different asynchronous tasks kicked off by Islandora. It contains a series of submodules that will be installed via Composer. Later, these configured components will be connected to Alpaca. ## The Broker Connecting Everything - Karaf and Alpaca **Karaf**’s job is similar to Tomcat, except where Tomcat is a web-accessible endpoint for Java applets, Karaf is simply meant to be a container for system-level applets to communicate via its OSGI. Alpaca is one such applet; it will broker messages between Fedora and Drupal, and between Drupal and various derivative generation applications. -**Alpaca** contains Karaf services to manage moving information between Islandora, Fedora, and Blazegraph as well as kicking off derivative services in Crayfish. These will be configured to broker between Drupal and Fedora using an ActiveMQ queue. +**[Alpaca]** contains Karaf services to manage moving information between Islandora, Fedora, and Blazegraph as well as kicking off derivative services in Crayfish. These will be configured to broker between Drupal and Fedora using an ActiveMQ queue. ## Finalized Drupal Configurations diff --git a/docs/js/diagram-animation.js b/docs/js/diagram-animation.js index 546a6e192..fa0358e17 100644 --- a/docs/js/diagram-animation.js +++ b/docs/js/diagram-animation.js @@ -1,16 +1,9 @@ /** * Sequential flow animation for Mermaid diagrams. - * - * Listens for the `mermaid:rendered` custom event dispatched by mermaid-init.mjs - * and animates any edges assigned to flow steps such as `flow0`, `flow1`, etc. - * - * Author diagrams by assigning step classes to edge IDs: - * class e1 flow0; - * class e2 flow1; */ (() => { const STEP_DURATION = 1500; - const PAUSE_DURATION = 100; + const STEP_GAP = 150; const EDGE_SELECTOR = "path"; const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); @@ -28,8 +21,8 @@ }; const getEdgeSelector = (edgeId) => { - const escaped = window.CSS?.escape ? window.CSS.escape(edgeId) : edgeId; - return `[id="${escaped}"], [id$="-${escaped}"]`; + const escaped = window.CSS && window.CSS.escape ? window.CSS.escape(edgeId) : edgeId; + return '[id="' + escaped + '"], [id$="-' + escaped + '"]'; }; const getSteppedEdges = (container) => { @@ -50,37 +43,86 @@ .filter(({ step }) => step !== null); }; + const ensureAnimatedPath = (edgePath) => { + if (edgePath.__islandoraAnimatedPath && edgePath.__islandoraAnimatedPath.isConnected) { + return edgePath.__islandoraAnimatedPath; + } + + const animatedPath = edgePath.cloneNode(false); + const computed = window.getComputedStyle(edgePath); + const strokeWidth = parseFloat(computed.strokeWidth || edgePath.getAttribute("stroke-width") || "2") || 2; + + animatedPath.removeAttribute("id"); + animatedPath.removeAttribute("marker-start"); + animatedPath.removeAttribute("marker-end"); + animatedPath.setAttribute("aria-hidden", "true"); + animatedPath.style.fill = "none"; + animatedPath.style.opacity = "0"; + animatedPath.style.pointerEvents = "none"; + animatedPath.style.stroke = computed.stroke; + animatedPath.style.strokeWidth = String(Math.max(strokeWidth * 1.75, strokeWidth + 1)); + animatedPath.style.strokeLinecap = "round"; + animatedPath.style.strokeLinejoin = "round"; + animatedPath.style.strokeDasharray = "0 1"; + animatedPath.style.strokeDashoffset = "0"; + + edgePath.parentNode && edgePath.parentNode.appendChild(animatedPath); + edgePath.__islandoraAnimatedPath = animatedPath; + return animatedPath; + }; + + const attachAnimationListeners = (container, syncUI) => { + (container.__islandoraAnimations || []).forEach((animation) => { + if (animation.__islandoraSyncAttached) { + return; + } + animation.addEventListener("finish", syncUI); + animation.addEventListener("cancel", syncUI); + animation.__islandoraSyncAttached = true; + }); + }; + const animateContainer = (container) => { const steppedEdges = getSteppedEdges(container); if (steppedEdges.length === 0) return false; const maxStep = Math.max(...steppedEdges.map(({ step }) => step)); - const totalCycle = (maxStep + 1) * STEP_DURATION + PAUSE_DURATION; - const activeRatio = STEP_DURATION / totalCycle; + const totalCycle = (maxStep + 1) * STEP_DURATION + maxStep * STEP_GAP; container.__islandoraAnimations = steppedEdges.map(({ el, step }) => { - el.__islandoraDashAnimation?.cancel(); - el.style.animation = "none"; - el.style.strokeDasharray = "9,5"; - el.style.strokeDashoffset = "900"; + const animatedPath = ensureAnimatedPath(el); + const pathLength = Math.max(el.getTotalLength(), 1); + const segmentLength = Math.max(Math.min(pathLength * 0.35, 120), 18); + const hiddenOffset = pathLength + segmentLength; + const visibleDash = String(segmentLength) + ' ' + String(pathLength); + const stepDelay = step * (STEP_DURATION + STEP_GAP); + const startOffset = Math.min(stepDelay / totalCycle, 1); + const finishOffset = Math.min((stepDelay + STEP_DURATION) / totalCycle, 1); + const hideOffset = Math.min(finishOffset + 0.0005, 1); + + animatedPath.__islandoraDashAnimation && animatedPath.__islandoraDashAnimation.cancel(); + animatedPath.style.strokeDasharray = visibleDash; + animatedPath.style.strokeDashoffset = String(hiddenOffset); + animatedPath.style.opacity = '0'; - const anim = el.animate( + const animation = animatedPath.animate( [ - { strokeDashoffset: 900, offset: 0 }, - { strokeDashoffset: -100, offset: activeRatio }, - { strokeDashoffset: 900, offset: Math.min(activeRatio + 0.001, 1) }, - { strokeDashoffset: 900, offset: 1 }, + { strokeDashoffset: hiddenOffset, opacity: 0, offset: 0 }, + { strokeDashoffset: hiddenOffset, opacity: 0, offset: startOffset }, + { strokeDashoffset: hiddenOffset, opacity: 1, offset: startOffset }, + { strokeDashoffset: segmentLength * 2, opacity: 1, offset: finishOffset }, + { strokeDashoffset: segmentLength * 2, opacity: 0, offset: hideOffset }, + { strokeDashoffset: hiddenOffset, opacity: 0, offset: 1 }, ], { duration: totalCycle, - delay: step * STEP_DURATION, - easing: "linear", + easing: 'linear', + fill: 'forwards', iterations: Infinity, } ); - - el.__islandoraDashAnimation = anim; - return anim; + animatedPath.__islandoraDashAnimation = animation; + return animation; }); return true; @@ -88,82 +130,97 @@ const stopContainer = (container) => { container.querySelectorAll(EDGE_SELECTOR).forEach((el) => { - el.__islandoraDashAnimation?.cancel(); - el.__islandoraDashAnimation = null; - el.style.animation = ""; - el.style.strokeDasharray = ""; - el.style.strokeDashoffset = ""; + if (el.__islandoraAnimatedPath && el.__islandoraAnimatedPath.__islandoraDashAnimation) { + el.__islandoraAnimatedPath.__islandoraDashAnimation.cancel(); + } + el.__islandoraAnimatedPath && el.__islandoraAnimatedPath.remove(); + el.__islandoraAnimatedPath = null; }); container.__islandoraAnimations = null; + delete container.dataset.islandoraAnimated; }; const injectControls = (container) => { - container.querySelector(".mermaid-controls")?.remove(); - - const controls = document.createElement("div"); - controls.className = "mermaid-controls"; - controls.setAttribute("role", "group"); - controls.setAttribute("aria-label", "Diagram animation controls"); + container.querySelector('.mermaid-controls') && container.querySelector('.mermaid-controls').remove(); - let playing = true; + const controls = document.createElement('div'); + controls.className = 'mermaid-controls'; + controls.setAttribute('role', 'group'); + controls.setAttribute('aria-label', 'Diagram animation controls'); const makeBtn = (text, label) => { - const btn = document.createElement("button"); - btn.className = "mermaid-btn"; - btn.type = "button"; + const btn = document.createElement('button'); + btn.className = 'mermaid-btn'; + btn.type = 'button'; btn.textContent = text; - btn.setAttribute("aria-label", label); + btn.setAttribute('aria-label', label); return btn; }; - const playBtn = makeBtn("▶", "Play animation"); - const pauseBtn = makeBtn("⏸", "Pause animation"); - const restartBtn = makeBtn("↺", "Restart animation"); + const playBtn = makeBtn('▶', 'Play animation'); + const pauseBtn = makeBtn('⏸', 'Pause animation'); + const restartBtn = makeBtn('↺', 'Restart animation'); + const enlargeBtn = makeBtn('+', 'Enlarge diagram'); + const syncUI = () => { - playBtn.disabled = playing; - pauseBtn.disabled = !playing; + const activeAnimations = container.__islandoraAnimations || []; + const running = activeAnimations.some((animation) => animation.playState === 'running'); + playBtn.disabled = running || activeAnimations.length === 0; + pauseBtn.disabled = !running; }; - playBtn.addEventListener("click", () => { - playing = true; + playBtn.addEventListener('click', () => { + (container.__islandoraAnimations || []).forEach((animation) => animation.play()); syncUI(); - container.__islandoraAnimations?.forEach((animation) => animation.play()); }); - pauseBtn.addEventListener("click", () => { - playing = false; + pauseBtn.addEventListener('click', () => { + (container.__islandoraAnimations || []).forEach((animation) => animation.pause()); syncUI(); - container.__islandoraAnimations?.forEach((animation) => animation.pause()); }); - restartBtn.addEventListener("click", () => { - playing = true; - syncUI(); + restartBtn.addEventListener('click', () => { animateContainer(container); + attachAnimationListeners(container, syncUI); + (container.__islandoraAnimations || []).forEach((animation) => { + animation.currentTime = 0; + animation.play(); + }); + syncUI(); + }); + + enlargeBtn.addEventListener('click', () => { + container.__islandoraOpenLightbox && container.__islandoraOpenLightbox(); }); + attachAnimationListeners(container, syncUI); syncUI(); - controls.append(playBtn, pauseBtn, restartBtn); + controls.append(playBtn, pauseBtn, restartBtn, enlargeBtn); container.prepend(controls); }; const activateContainer = (container) => { if (reduceMotion.matches) return; + if (container.dataset.islandoraAnimated === 'true') return; + const hasSteps = animateContainer(container); - if (hasSteps && !container.querySelector(".mermaid-controls")) { + if (!hasSteps) return; + + container.dataset.islandoraAnimated = 'true'; + if (!container.querySelector('.mermaid-controls')) { injectControls(container); } }; - document.addEventListener("mermaid:rendered", ({ detail: container }) => { + document.addEventListener('mermaid:rendered', ({ detail: container }) => { activateContainer(container); }); const onMotionChange = () => { - document.querySelectorAll(".mermaid").forEach((container) => { + document.querySelectorAll('.mermaid').forEach((container) => { if (reduceMotion.matches) { stopContainer(container); - container.querySelector(".mermaid-controls")?.remove(); + container.querySelector('.mermaid-controls') && container.querySelector('.mermaid-controls').remove(); } else { activateContainer(container); } @@ -172,10 +229,10 @@ const observeMermaidContainers = (root) => { if (!(root instanceof Element || root instanceof Document)) return; - if (root instanceof Element && root.matches(".mermaid")) { + if (root instanceof Element && root.matches('.mermaid')) { activateContainer(root); } - root.querySelectorAll?.(".mermaid").forEach((container) => { + root.querySelectorAll && root.querySelectorAll('.mermaid').forEach((container) => { activateContainer(container); }); }; @@ -194,9 +251,9 @@ observer.observe(document.body, { childList: true, subtree: true }); } - if (typeof reduceMotion.addEventListener === "function") { - reduceMotion.addEventListener("change", onMotionChange); - } else if (typeof reduceMotion.addListener === "function") { + if (typeof reduceMotion.addEventListener === 'function') { + reduceMotion.addEventListener('change', onMotionChange); + } else if (typeof reduceMotion.addListener === 'function') { reduceMotion.addListener(onMotionChange); } })(); diff --git a/docs/js/mermaid-init.mjs b/docs/js/mermaid-init.mjs index 012974db2..019e0ebea 100644 --- a/docs/js/mermaid-init.mjs +++ b/docs/js/mermaid-init.mjs @@ -94,6 +94,9 @@ const attachExpandBehavior = (container) => { container.setAttribute("role", "button"); container.setAttribute("aria-label", "Open Mermaid diagram in a larger view"); container.title = "Click to enlarge diagram"; + container.__islandoraOpenLightbox = () => { + openLightbox(container); + }; container.addEventListener("click", (event) => { if (event.target.closest(".mermaid-controls, button, a")) { diff --git a/docs/technical-documentation/alpaca-tips.md b/docs/technical-documentation/alpaca-tips.md index acf0fa15b..fb9bec185 100644 --- a/docs/technical-documentation/alpaca-tips.md +++ b/docs/technical-documentation/alpaca-tips.md @@ -1,8 +1,6 @@ # Alpaca Tips -[Alpaca](https://github.com/Islandora/Alpaca) is event-driven middleware based on [Apache Camel](https://camel.apache.org/) for Islandora - -Currently, Alpaca ships with four event-driven components +[Alpaca] ships with four event-driven components - [islandora-connector-derivative](#islandora-connector-derivative) - [islandora-http-client](#islandora-http-client) @@ -10,7 +8,7 @@ Currently, Alpaca ships with four event-driven components - [islandora-indexing-triplestore](#islandora-indexing-triplestore) ## islandora-connector-derivative -This service receives requests from Drupal when it wants to create derivatives and passes that request along to a microservice in [Crayfish](https://github.com/Islandora/Crayfish). When it receives the derivative file back from the microservice, it passes the file back to Drupal. +This service receives requests from Drupal when it wants to create derivatives and passes that request along to a microservice in [scyllaridae]. When it receives the derivative file back from the microservice, it passes the file back to Drupal. ## islandora-http-client This service overrides the default http client with Islandora specific configuration. diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index 8d442f808..5ff2ee8d1 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -1,18 +1,18 @@ -# Islandora Architecture Diagram +# Islandora Architecture ## Site Serving Path -When a client visits an Islandora website, the request flow looks like a typical Drupal request. +When a client visits an Islandora website, the request flow looks like a typical [Drupal] request. The request is received by an nginx web server, which forwards the request to `php-fpm` process that points to a Drupal codebase. Drupal bootstraps and queries the backend database (Islandora ships with `mariadb`). If the request was for a search page, Drupal make also query `solr` to include search results in the HTML response. ```mermaid flowchart TD user([Client / Browser]) - user e1@==>|HTTP request| nginx + user e1@-->|HTTP request| nginx subgraph webserver[Nginx Web Server] - nginx[Nginx] e2@==>|forward request| drupal[Drupal] + nginx[Nginx] e2@-->|forward request| drupal[Drupal] end drupal e3@-->|query| mariadb[(MariaDB)] @@ -39,15 +39,17 @@ flowchart TD ### IIIF manifests -For some items in Islandora, the HTML response may include a [IIIF Manifest]. This typically happens when showing items that have image media. For those requests, Islandora's IIIF module may also query the [Cantaloupe] IIIF server for metadata needed to generate a IIIF manifest. The HTML response will then reference the location of the images in the IIIF server to allow a IIIF viewer to render the image in the browser. Practically what this means is in addition to the typical drupal request flow, Drupal may also query the IIIF server for basic image metadata (e.g. width/height) which are needed in the IIIF manifest response. The client web browser will then read that manifest using Javascript and the IIIF viewer will `GET` the images referenced in the IIIF manifest directly from the IIIF server. +For some [Resource Node]s in Islandora, the HTML response may include a [IIIF Manifest]. This typically happens when showing items that have image media. For those requests, Islandora's IIIF module may also query the [Cantaloupe] IIIF server for metadata needed to generate a [IIIF Manifest]. The HTML response will then download the images from Cantaloupe to allow a IIIF viewer (e.g. [OpenSeadragon] or [Mirador]) to render the image in the browser. + +In addition to [the typical drupal request flow](##site-serving-path), Drupal may also query Cantaloupe for basic image metadata (e.g. width/height) which are needed to complete a valid [IIIF Manifest] response. The client web browser will then read that IIIF Manifest using Javascript and the IIIF viewer will `GET` the images referenced in the IIIF Manifest directly from the IIIF server. ```mermaid flowchart TD user([Client / Browser]) - user e1@==>|HTTP request| nginx + user e1@-->|HTTP request| nginx subgraph webserver[Nginx Web Server] - nginx[Nginx] e2@==>|forward request| drupal[Drupal] + nginx[Nginx] e2@-->|forward request| drupal[Drupal] end drupal e3@-->|GET /info.json| cantaloupe[Cantaloupe IIIF Image Server] @@ -72,6 +74,9 @@ flowchart TD ## Event Driven System +When you create, update, or delete Drupal [entities], Drupal emits an event message which is emitted to [ActiveMQ] and put on ActiveMQ's queue. + + ```mermaid flowchart TD drupal([Islandora Drupal Website]) @@ -115,7 +120,7 @@ alpaca -.->|ok| drupal ## Event Example -As an example, when an Islandora Repository manager uploades an image to Islandora, an event is emitted to generate a thumbnail for that image. That event is put on the event queue, alpaca reads the message from the queue, and forwards the event to the configured service. In the case of a thumbnail, houdini handles generating the thumbnail for the uploaded image +As an example, when an Islandora Repository manager uploades an image to their Islandora repository, an event is emitted to generate a thumbnail for that image. That event is put on the event queue, alpaca reads the message from the queue, and forwards the event to the configured service. In the case of a thumbnail, [houdini] handles generating the thumbnail for the uploaded image ```mermaid flowchart TD @@ -151,28 +156,23 @@ flowchart TD The following components are microservices developed and maintained by the Islandora community. They are bundled under [Islandora Crayfish](https://github.com/Islandora/Crayfish): -* [FITS](https://github.com/roblib/CrayFits) - A Symfony 4 Microservice to generate FITS data and persist it as a Drupal media node. Works with [Islandora FITS](https://github.com/roblib/islandora_fits) -* [Homarus](https://github.com/Islandora/Crayfish/tree/dev/Homarus) - Provides [FFmpeg](https://www.ffmpeg.org/) as a microservice for generating video and audio derivatives. -* [Houdini](https://github.com/Islandora/Crayfish/tree/dev/Houdini) - [ImageMagick](https://www.imagemagick.org/script/index.php) as a microservice for generating image-based derivatives, including thumbnails. -* [Hypercube](https://github.com/Islandora/Crayfish/tree/dev/Hypercube) - [Tesseract](https://github.com/tesseract-ocr) as a microservice for optical character recognition (OCR). -* [Milliner](https://github.com/Islandora/Crayfish/tree/dev/Milliner) - A microservice that converts Drupal entities into Fedora resources. -* [Recast](https://github.com/Islandora/Crayfish/tree/dev/Recast) - A microservice that remaps Drupal URIs to add Fedora-to-Fedora links based on associated Drupal URIs in RDF. - +* [FITS] +* [Homarus] +* [Houdini] +* [Hypercube] +* [Milliner] ### Other Open Source The following components are deployed with Islandora, but are developed and maintained by other open source projects: - -* [Apache](https://www.apache.org/) - The Apache HTTP Server, colloquially called Apache, is a free and open-source cross-platform web server software. Provides the environment in which Islandora and its components run. - * [ActiveMQ](https://activemq.apache.org/) - Apache ActiveMQ is an open source message broker written in Java together with a full Java Message Service client. - * [Karaf](https://karaf.apache.org/) - A modular open source OSGi runtime environment. - * [Tomcat](http://tomcat.apache.org/) - an open-source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and WebSocket technologies. Tomcat provides a "pure Java" HTTP web server environment in which Java code can run. - * [Solr](https://lucene.apache.org/solr/) - An open-source enterprise-search platform. Solr is the default search and discover layer of Islandora, and a key component in some methods for [migration to Islandora from Islandora Legacy](https://github.com/Islandora-devops/migrate_7x_claw) -* [Blazegraph](https://blazegraph.com/) - Blazegraph is a triplestore and graph database. -* [Cantaloupe](https://cantaloupe-project.github.io/) - an open-source dynamic image server for on-demand generation of derivatives of high-resolution source images. Used in Islandora to support [IIIF](https://iiif.io/) -* [Drupal](https://www.drupal.org/) - Drupal is an open source content management system, and the heart of Islandora. All user and site-building aspects of Islandora are experienced through Drupal as a graphical user interface. -* [Fedora](https://wiki.lyrasis.org/display/FF/Fedora+Repository+Home) - A robust, modular, open source repository system for the management and dissemination of digital content. The default smart storage for Islandora. -* [Matomo](https://matomo.org/) - Matomo, formerly Piwik, is a free and open source web analytics application. It provides usage statistics and a rich dashboard for Islandora. -* [MySQL](https://www.mysql.com/) - MySQL is an open-source relational database management system. Used as a Drupal database in Islandora, it can be easily replaced with other database management systems such as [PostgreSQL](https://www.postgresql.org/) -* Triplestore - See Blazegraph. +* [Apache] + * [ActiveMQ] + * [Tomcat] + * [Solr] +* [Blazegraph] +* [Cantaloupe] +* [Drupal] +* [Fedora (Repository Software)] +* [MariaDB] +* Triplestore - See [Blazegraph]. diff --git a/docs/technical-documentation/ppa-documentation.md b/docs/technical-documentation/ppa-documentation.md deleted file mode 100644 index c6bd02675..000000000 --- a/docs/technical-documentation/ppa-documentation.md +++ /dev/null @@ -1,143 +0,0 @@ -# Updating a `deb` and adding it to LYRASIS PPA - -## This page is here for archival purposes. - -If you are running Ubuntu 20.04 or higher, installing a specially-compiled imagemagick is no longer necessary. We are leaving this page available in case in the future we have a need to package our own `deb` and add it to the Lyrasis PPA. - -## Background - -Ubuntu removed JPEG 2000 support from ImageMagick since Vivid Vervet's 8:6.8.9.9-5 version. The PPA that we have created adds JPEG 2000 support back into ImageMagick for Ubuntu 16.04 and 18.04. More information on why JPEG 2000 support was removed can be found [here](https://bugs.launchpad.net/ubuntu/+source/imagemagick/+bug/1447968), and more information on `openjpeg` in Ubuntu can be found [here](https://bugs.launchpad.net/ubuntu/+source/openjpeg2/+bug/711061). - -## Prerequisites - -Review the [Ubuntu Packaging Guide](http://packaging.ubuntu.com/html/). Key items needed are in the [Getting Set Up section](http://packaging.ubuntu.com/html/getting-set-up.html): - -- Basic packaging software: - - `sudo apt install gnupg pbuilder ubuntu-dev-tools apt-file` -- Make sure you have your GPG, if you do not, follow the instructions in the guide. -- Setup `pbuilder`: - - `pbuilder-dist bionic create` -- Configure your shell with some exports for `debuild`: - - export DEBFULLNAME="Bob Dobbs" - - export DEBEMAIL="subgenius@example.com" -- Contact [Jonathan Green](https://github.com/jonathangreen) to get setup on the [LYRASIS PPA team](https://launchpad.net/~lyrasis). - -## Patching source - -`imagemagick` is used an example. If you need to only change or patch the actual source code, then you will need to use `quilt`. More information on using `quilt` can be found in the [Patches to Packages section](http://packaging.ubuntu.com/html/patches-to-packages.html). If you are only change dependencies, or information in the `debian` directory of a package, `quilt` is not required, and [if used will cause build failures](https://stackoverflow.com/questions/29634868/adding-a-file-in-a-quilt-dquilt-patch-patch-applies-correctly-by-hand-but-brea). - -- Create a directory to work in: - - `mkdir imagemagick-bionic-jp2` - - `cd imagemagick-bionic-jp2` -- Pull down the source: - - `pull-lp-source imagemagick bionic` - - `cd imagemagick-6.9.7.4+dfsg` -- Edit files as needed -- Document the fix: - - `dch` (**Make sure to change `UNRELEASED` to the Ubuntu release name. For example: `bionic`**) -- Build the package: - - `debuild -S` - -## Example Patch - -```diff -Index: imagemagick-6.9.7.4+dfsg/debian/control -=================================================================== ---- imagemagick-6.9.7.4+dfsg.orig/debian/control -+++ imagemagick-6.9.7.4+dfsg/debian/control -@@ -26,8 +26,7 @@ Build-Depends: debhelper (>= 10), - libx11-dev, libxext-dev, libxt-dev, - # for plugins - ghostscript, libdjvulibre-dev, libexif-dev, -- libjpeg-dev, --# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061 -+ libjpeg-dev, libopenjp2-7-dev, - libopenexr-dev, libperl-dev, libpng-dev, libtiff-dev, - libwmf-dev, - # libgraphviz-dev, incompatible license against fftw -@@ -273,8 +272,7 @@ Depends: libmagickcore-6-headers (= ${so - libmagickcore-6.q16-3 (= ${binary:Version}), - libmagickcore-6.q16-3-extra (= ${binary:Version}), - libbz2-dev, libdjvulibre-dev, -- libexif-dev, libfreetype6-dev, libjpeg-dev, --# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061 -+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev, - liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev, - librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev, - libxt-dev, zlib1g-dev, -@@ -483,8 +481,7 @@ Depends: libmagickcore-6-headers (= ${so - libmagickcore-6.q16hdri-3 (= ${binary:Version}), - libmagickcore-6.q16hdri-3-extra (= ${binary:Version}), - libbz2-dev, libdjvulibre-dev, -- libexif-dev, libfreetype6-dev, libjpeg-dev, --# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061 -+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev, - liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev, - librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev, - libxt-dev, zlib1g-dev, -Index: imagemagick-6.9.7.4+dfsg/debian/control.d/noquantum.in -=================================================================== ---- imagemagick-6.9.7.4+dfsg.orig/debian/control.d/noquantum.in -+++ imagemagick-6.9.7.4+dfsg/debian/control.d/noquantum.in -@@ -26,8 +26,7 @@ Build-Depends: debhelper (>= 10), - libx11-dev, libxext-dev, libxt-dev, - # for plugins - ghostscript, libdjvulibre-dev, libexif-dev, -- libjpeg-dev, --# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061 -+ libjpeg-dev, libopenjp2-7-dev, - libopenexr-dev, libperl-dev, libpng-dev, libtiff-dev, - libwmf-dev, - # libgraphviz-dev, incompatible license against fftw -Index: imagemagick-6.9.7.4+dfsg/debian/control.d/quantum.in -=================================================================== ---- imagemagick-6.9.7.4+dfsg.orig/debian/control.d/quantum.in -+++ imagemagick-6.9.7.4+dfsg/debian/control.d/quantum.in -@@ -78,8 +78,7 @@ Depends: libmagickcore-${IMVERSION}-head - libmagickcore-${IMVERSION}.${QUANTUMDEPTH}-${CORESOVERSION} (= ${binary:Version}), - libmagickcore-${IMVERSION}.${QUANTUMDEPTH}-${CORESOVERSION}-extra (= ${binary:Version}), - libbz2-dev, libdjvulibre-dev, -- libexif-dev, libfreetype6-dev, libjpeg-dev, --# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061 -+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev, - liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev, - librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev, - libxt-dev, zlib1g-dev, -Index: imagemagick-6.9.7.4+dfsg/debian/rules -=================================================================== ---- imagemagick-6.9.7.4+dfsg.orig/debian/rules -+++ imagemagick-6.9.7.4+dfsg/debian/rules -@@ -98,7 +98,7 @@ STATIC_CONFIGURE_OPTIONS := \ - --with-gs-font-dir=/usr/share/fonts/type1/gsfonts \ - --with-magick-plus-plus \ - --with-djvu \ -- --with-openjp2 \ -+ --with-openjp2 \ - --with-wmf \ - --without-gvc \ - --enable-shared \ -``` - -## Uploading to LYRASIS PPA - -Once the package is built successfully, you'll upload the `changes` file to Launchpad. For example: - -- `dput ppa:lyrasis/imagemagick-jp2 imagemagick_6.9.7.4+dfsg-16ubuntu6.8_source.changes` - -After the package is successfully uploaded to the PPA, you'll receive an email from Launchpad. Something like this: - -``` -Accepted: - OK: imagemagick_6.9.7.4+dfsg.orig.tar.xz - OK: imagemagick_6.9.7.4+dfsg-16ubuntu6.8.debian.tar.xz - OK: imagemagick_6.9.7.4+dfsg-16ubuntu6.8.dsc - -> Component: main Section: graphics - -imagemagick (8:6.9.7.4+dfsg-16ubuntu6.8) bionic; urgency=medium - - * Add jp2 support. -``` - -And you will see it in the interface for the Lyrasis PPA: - -![ppa-example](../assets/ppa-example.png) diff --git a/docs/technical-documentation/resizing-vm.md b/docs/technical-documentation/resizing-vm.md deleted file mode 100644 index ba7237c7b..000000000 --- a/docs/technical-documentation/resizing-vm.md +++ /dev/null @@ -1,30 +0,0 @@ -## Resize vagrant machine -To expand virtual machine's hard drive for testing of larger files. Once the VM has started, you'll need to `halt` the VM, download and run the script, tell it what size (in MB) and then start the VM. -The last step `vagrant ssh --command "sudo resize2fs /dev/sda1"` is a check. It should return there was nothing to do. If you already provisioned you VM you can skip the 2 steps with provisioning in them. - -```shell -# Skip this if you VM is already provisioned. -$ vagrant up --no-provision <-- Exclude if already running and provisioned. - -$ vagrant halt - -# Download and run. This will default to the correct name (just press enter) then give the size. -# Example: `350000` is equal to 350GB - -$ wget https://gist.githubusercontent.com/DonRichards/6dc6c81ae9fc22cba8d7a57b90ab1509/raw/45017e07a3b93657f8822dfbbe4fc690169cdabc/expand_disk.py -$ chmod +x expand_disk.py -$ python expand_disk.py -$ vagrant up --no-provision - -# This step isn't needed but acts as a check to verify it worked. -$ vagrant ssh --command "sudo resize2fs /dev/sda1" - -# Skip this if you VM is already provisioned. -$ vagrant provision <-- Exclude if already provisioned. -``` - -### Troubleshooting expand_disk.py -You may need to remove the "resized" version. Assuming your VM location is `~/VirtualBox\ VMs` -```shell -$ rm -rf ~/VirtualBox\ VMs/Islandora\ CLAW\ Ansible_resized -``` diff --git a/docs/user-documentation/glossary.md b/docs/user-documentation/glossary.md index 321460a27..341f000b9 100644 --- a/docs/user-documentation/glossary.md +++ b/docs/user-documentation/glossary.md @@ -2,6 +2,9 @@ The following glossary of terms addresses an Islandora context. When comparing new Islandora and Fedora to older versions it may also be helpful to reference [the Islandora 7 Glossary](https://wiki.duraspace.org/display/ISLANDORA/APPENDIX+E+-+Glossary). +## ActiveMQ +Apache ActiveMQ is a JMS compliant Messaging Queue. Messaging client can make use of JMS to send messages. + ## Alpaca Islandora's event-driven middleware based on [Apache Camel](http://camel.apache.org/) that handles communication between various components of Islandora, for instance synchronizing [Drupal](#drupal) data with a [Fedora](#fedora-repository-software) repository and the [Blazegraph](#blazegraph) triple store. @@ -80,6 +83,10 @@ The standard Drupal Content types are 'Article' and 'Basic page'. _Islandora Sta ## Context An "if-this-then-that" configuration created using the Drupal [Context](https://www.drupal.org/project/context) contrib module. Islandora extends the capabilities of Context by adding custom Conditions, custom Reactions, and by evaluating context at specific times to allow Contexts to be used for derivatives, indexing, and display. +## Composer +Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you. + +[https://getcomposer.org/](https://getcomposer.org/) ## Crayfish A collection of Islandora [microservices](#microservice). Some microservices are built specifically for use with a [Fedora](#fedora-repository-software) repository, while others are just for general use within Islandora. @@ -102,6 +109,12 @@ The files, themes, profile, and modules included with the standard project softw ## Drupal Roles Roles are a way of assigning specific permissions to a group of users. Any user assigned to a role will have the same permissions as all other users assigned to that role. This allows you to control which users have permission to view, edit, or delete content in [Drupal](#drupal). Islandora provides a special role called _fedoraAdmin_ that is required to have actions in Drupal reflected in [Fedora](#fedora-repository-software). +## Drush + +Drush is a command line shell and Unix scripting interface for Drupal. Drush core ships with lots of [useful commands](https://www.drush.org/13.x/commands/all/) and [generators](https://www.drush.org/13.x/generators/all/). Similarly, it runs update.php, executes SQL queries, runs content migrations, and misc utilities like cron or cache rebuild. Drush can be extended by [3rd party commandfiles](https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A4654). + +[https://www.drush.org/](https://www.drush.org/) + ## Entity A [Drupal](#drupal) term for an item of either content or configuration data. Examples include [Nodes](#node) (content items), Blocks, [Taxonomy terms](#taxonomy-term), and definitions of [content types](#content-type); the first three are [content entities](#content-entity), and the last is a [configuration entity](#configuration-entity). In common usage, the term often refers to Drupal content entities like [Nodes](#node) or [Taxonomy terms](#taxonomy-term). @@ -174,21 +187,19 @@ Acronym for "Graphical User Interface". Often refers to taking actions through D An installation without legacy constraints. Usually refers to a brand new system where users load new content, as opposed to migrating content from a previous system. ## Imagemagick -[Imagemagick](https://imagemagick.org/index.php) is an open-source image processing library. In Islandora, Imagemagick is provided by the [Crayfish](#crayfish) [Microservice](#microservice), [Houdini](#houdini). +[Imagemagick](https://imagemagick.org/index.php) is an open-source image processing library. In Islandora, Imagemagick is provided by the [scyllaridae](#scyllaridae) [Microservice](#microservice), [Houdini](#houdini). ## hOCR [hOCR](https://kba.github.io/hocr-spec/1.2/) is an open standard for representing OCR (Optical Character Recognition) results, including text positioning, as HTML. hOCR can be produced by [Tesseract](#tesseract), and can be displayed as an overlay on an image by [Mirador](#mirador). - ## Homarus -Homarus is a [microservice](#microservice) wrapper for [FFMpeg](#ffmpeg). It is part of [Crayfish](#crayfish). +Homarus is a [microservice](#microservice) wrapper of [FFMpeg](#ffmpeg) for generating video and audio derivatives. It is implemented using [scyllaridae](#scyllaridae). ## Houdini -Houdini is a [microservice](#microservice) wrapper for [Imagemagick](#imagemagick). It is part of [Crayfish](#crayfish). - +Houdini is a [microservice](#microservice) wrapper of [Imagemagick](#imagemagick) for generating image-based derivatives, including thumbnails. It is implemented using [scyllaridae](#scyllaridae). ## Hypercube -Hypercube is a [microservice](#microservice) wrapper for [Tesseract](#tesseract). It is part of [Crayfish](#crayfish). +Hypercube is a [microservice](#microservice) wrapper of [Tesseract](#tesseract) for optical character recognition (OCR) and [hOCR](#hOCR). It is implemented using [scyllaridae](#scyllaridae). ## IIIF The [International Image Interoperability Framework](https://iiif.io/). Generally pronounced "triple-eye-eff." A set of open standards and APIs that help archives, libraries, and museums make the most of their digitized collections with deep zoom, annotation capabilities, and more, and also the community of users and developers that support the framework. @@ -248,6 +259,10 @@ In computing, linked data is structured data which is interlinked with other dat ## Manifest See [IIIF Manifest](#iiif-manifest). +## MariaDB +MariaDB Server is one of the most popular database servers in the world. It’s made by the original developers of MySQL and guaranteed to stay open source. Notable users include Wikipedia, WordPress.com and Google. + +[https://mariadb.org/](https://mariadb.org/) ## Matomo [Matomo](https://matomo.org/), formerly called Piwik, is a software for tracking visits to websites. It is an open source alternative to Google Analytics and allows the generation of website usage reports. @@ -266,9 +281,18 @@ Protocol specification that allows a web client to request an earlier/historic s ## Microservice A software development technique — a variant of the service-oriented architecture (SOA) structural style — that arranges an application as a collection of loosely coupled services. In a microservices' architecture, services are fine-grained and the protocols are lightweight. +## Milliner + +A microservice that converts Drupal entities into Fedora resources. + ## Module Software (usually PHP, JavaScript, and/or CSS) that extends site features and adds functionality. [Drupal](#drupal) modules conform to a specific structure allowing them to integrate with the Drupal architecture. +## NGINX +nginx ("engine x") is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server. Originally written by [Igor Sysoev](http://sysoev.ru/en/) and distributed under the [2-clause BSD License](https://nginx.org/LICENSE). Enterprise distributions, commercial support and training are [available from F5, Inc.](https://nginx.org/en/enterprise.html) + +[https://nginx.org/](https://nginx.org/) + ## Node Usually refers to a piece of Drupal [Content](#content-entity) of the type 'Node'. This includes actual pages, articles, and [Resource nodes](#resource-node). Nodes must belong to a specific node bundle, called a ["Content Type"](#content-type). @@ -284,6 +308,11 @@ Open source describes a method of software development that promotes access to t ## OpenSeadragon [OpenSeadragon](https://openseadragon.github.io/) is javascript-based zoomable image [Viewer](#viewer). It has the ability to do zooming and display multiple pages. To render an image through OpenSeadragon, it must be provided in a [IIIF Manifest](#iiif-manifest). +## PostgreSQL +PostgreSQL is a powerful, open source object-relational database system with over 35 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance. + +[https://www.postgresql.org/](https://www.postgresql.org/) + ## PR See [Pull request](#pull-request) @@ -307,6 +336,14 @@ The term 'Resource node' is specific to Islandora. Typically, Resource nodes in For example, a video stored in Islandora will have a Resource node, with metadata stored in [Fields](#field). Attached to the Resource node is a [Media](#media) entity, which encapsulates the preservation-grade file. The Resource node may be linked to further [Media](#media), for instance for a thumbnail, web-friendly derivative, and technical metadata associated with the resource node. The Resource node may also belong to one or more collections. +## Solr +An open-source enterprise-search platform. Solr is the default search and discover layer of Islandora, and a key component in some methods for [migration to Islandora from Islandora Legacy](https://github.com/Islandora-devops/migrate_7x_claw) + +[https://lucene.apache.org/solr/](https://lucene.apache.org/solr/) + +## scyllaridae +A framework for building Islandora [microservices](#microservice). + ## Source Field A Drupal term for the main file-[type](#field-type) [field](#field) on a [Media](#media). The names of these fields differ across Media Types, such as "Image" (`field_media_image`) on Image media, and "Video File" (`field_media_video_file`) on Video media. While it is possible to add other fields, including file fields, to a Media, the source field is the one configured during the creation of a Media Type. Islandora provides utility functions to get the source field from a Media ([MediaSourceService.php](https://github.com/Islandora/islandora/blob/2.x/src/MediaSource/MediaSourceService.php)). @@ -316,10 +353,20 @@ A [Drupal](#drupal) [Content Entity](#content-entity) of the type 'taxonomy term ## Tesseract [Tesseract](https://github.com/tesseract-ocr/tesseract) is an open-source OCR (Optical Character Recognition) software. It can perform OCR in multiple languages. It can produce OCR (plain text) and [hOCR](#hocr) (HTML, which includes positional data). In Islandora, Tesseract is provided by the [Crayfish](#crayfish) [Microservice](#microservice), [Hypercube](#hypercube). - ## Theme Software and asset files (images, CSS, PHP code, and/or templates) that determine the style and layout of the site. The [Drupal](#drupal) project distinguishes between core and contributed themes. +## Tomcat +An open-source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and WebSocket technologies. Tomcat provides a "pure Java" HTTP web server environment in which Java code can run. + +[http://tomcat.apache.org/](http://tomcat.apache.org/) + +## Traefik + +Traefik is a leading modern open source reverse proxy and ingress controller that makes deploying services and APIs easy. Traefik integrates with your existing infrastructure components and configures itself automatically and dynamically. + +[https://traefik.io/traefik](https://traefik.io/traefik) + ## Vagrant [Vagrant](https://www.vagrantup.com/) is an open-source software product for building and maintaining portable virtual software development environments (virtual machines). The [Islandora Playbook](#islandora-playbook) includes a 'vagrantfile', a set of instructions that allows users to create a local virtual machine environment which will subsequently run [Ansible](#ansible) to execute the configuration and installation steps recorded in the [Islandora Playbook](#islandora-playbook). diff --git a/includes/abbreviations.md b/includes/abbreviations.md index 9e729b18e..0ffa05a1f 100644 --- a/includes/abbreviations.md +++ b/includes/abbreviations.md @@ -1,3 +1,5 @@ +[ActiveMQ]: ../user-documentation/glossary.md#activemq +[ActiveMQs]: ../user-documentation/glossary.md#activemq [Alpaca]: ../user-documentation/glossary.md#alpaca [Alpacas]: ../user-documentation/glossary.md#alpaca [Ansible]: ../user-documentation/glossary.md#ansible @@ -23,17 +25,19 @@ [Configuration]: ../user-documentation/glossary.md#configuration [Configurations]: ../user-documentation/glossary.md#configuration [Configuration entity]: ../user-documentation/glossary.md#configuration-entity -[Configuration entitys]: ../user-documentation/glossary.md#configuration-entity +[Configuration entities]: ../user-documentation/glossary.md#configuration-entity [Content]: ../user-documentation/glossary.md#content [Contents]: ../user-documentation/glossary.md#content [Content entity]: ../user-documentation/glossary.md#content-entity -[Content entitys]: ../user-documentation/glossary.md#content-entity +[Content entities]: ../user-documentation/glossary.md#content-entity [Content model]: ../user-documentation/glossary.md#content-model [Content models]: ../user-documentation/glossary.md#content-model [Content type]: ../user-documentation/glossary.md#content-type [Content types]: ../user-documentation/glossary.md#content-type [Context]: ../user-documentation/glossary.md#context [Contexts]: ../user-documentation/glossary.md#context +[Composer]: ../user-documentation/glossary.md#composer +[Composers]: ../user-documentation/glossary.md#composer [Crayfish]: ../user-documentation/glossary.md#crayfish [Crayfishs]: ../user-documentation/glossary.md#crayfish [Datastream]: ../user-documentation/glossary.md#datastream @@ -47,8 +51,10 @@ [Drupal Core]: ../user-documentation/glossary.md#drupal-core [Drupal Cores]: ../user-documentation/glossary.md#drupal-core [Drupal Roles]: ../user-documentation/glossary.md#drupal-roles +[Drush]: ../user-documentation/glossary.md#drush +[Drushs]: ../user-documentation/glossary.md#drush [Entity]: ../user-documentation/glossary.md#entity -[Entitys]: ../user-documentation/glossary.md#entity +[Entities]: ../user-documentation/glossary.md#entity [Fedora (Repository Software)]: ../user-documentation/glossary.md#fedora-repository-software [Fedora (Repository Software)s]: ../user-documentation/glossary.md#fedora-repository-software [FFmpeg]: ../user-documentation/glossary.md#ffmpeg @@ -65,7 +71,7 @@ [Field Storages]: ../user-documentation/glossary.md#field-storage [FITS]: ../user-documentation/glossary.md#fits [Fixity]: ../user-documentation/glossary.md#fixity -[Fixitys]: ../user-documentation/glossary.md#fixity +[Fixities]: ../user-documentation/glossary.md#fixity [Flysystem]: ../user-documentation/glossary.md#flysystem [Flysystems]: ../user-documentation/glossary.md#flysystem [GLAM]: ../user-documentation/glossary.md#glam @@ -107,6 +113,8 @@ [Linked datas]: ../user-documentation/glossary.md#linked-data [Manifest]: ../user-documentation/glossary.md#manifest [Manifests]: ../user-documentation/glossary.md#manifest +[MariaDB]: ../user-documentation/glossary.md#mariadb +[MariaDBs]: ../user-documentation/glossary.md#mariadb [Matomo]: ../user-documentation/glossary.md#matomo [Matomos]: ../user-documentation/glossary.md#matomo [Media]: ../user-documentation/glossary.md#media @@ -117,18 +125,24 @@ [Miradors]: ../user-documentation/glossary.md#mirador [Microservice]: ../user-documentation/glossary.md#microservice [Microservices]: ../user-documentation/glossary.md#microservice +[Milliner]: ../user-documentation/glossary.md#milliner +[Milliners]: ../user-documentation/glossary.md#milliner [Module]: ../user-documentation/glossary.md#module [Modules]: ../user-documentation/glossary.md#module +[NGINX]: ../user-documentation/glossary.md#nginx +[NGINXs]: ../user-documentation/glossary.md#nginx [Node]: ../user-documentation/glossary.md#node [Nodes]: ../user-documentation/glossary.md#node [OAI-PMH]: ../user-documentation/glossary.md#oai-pmh [OAI-PMHs]: ../user-documentation/glossary.md#oai-pmh [Ontology]: ../user-documentation/glossary.md#ontology -[Ontologys]: ../user-documentation/glossary.md#ontology +[Ontologies]: ../user-documentation/glossary.md#ontology [Open Source]: ../user-documentation/glossary.md#open-source [Open Sources]: ../user-documentation/glossary.md#open-source [OpenSeadragon]: ../user-documentation/glossary.md#openseadragon [OpenSeadragons]: ../user-documentation/glossary.md#openseadragon +[PostgreSQL]: ../user-documentation/glossary.md#postgresql +[PostgreSQLs]: ../user-documentation/glossary.md#postgresql [PR]: ../user-documentation/glossary.md#pr [PRs]: ../user-documentation/glossary.md#pr [Pull request]: ../user-documentation/glossary.md#pull-request @@ -141,6 +155,10 @@ [Resource Description Frameworks]: ../user-documentation/glossary.md#resource-description-framework [Resource Node]: ../user-documentation/glossary.md#resource-node [Resource Nodes]: ../user-documentation/glossary.md#resource-node +[Solr]: ../user-documentation/glossary.md#solr +[Solrs]: ../user-documentation/glossary.md#solr +[scyllaridae]: ../user-documentation/glossary.md#scyllaridae +[scyllaridaes]: ../user-documentation/glossary.md#scyllaridae [Source Field]: ../user-documentation/glossary.md#source-field [Source Fields]: ../user-documentation/glossary.md#source-field [Taxonomy term]: ../user-documentation/glossary.md#taxonomy-term @@ -149,6 +167,10 @@ [Tesseracts]: ../user-documentation/glossary.md#tesseract [Theme]: ../user-documentation/glossary.md#theme [Themes]: ../user-documentation/glossary.md#theme +[Tomcat]: ../user-documentation/glossary.md#tomcat +[Tomcats]: ../user-documentation/glossary.md#tomcat +[Traefik]: ../user-documentation/glossary.md#traefik +[Traefiks]: ../user-documentation/glossary.md#traefik [Vagrant]: ../user-documentation/glossary.md#vagrant [Vagrants]: ../user-documentation/glossary.md#vagrant [VBO]: ../user-documentation/glossary.md#vbo @@ -163,7 +185,7 @@ [Virtual Machine Image]: ../user-documentation/glossary.md#virtual-machine-image [Virtual Machine Images]: ../user-documentation/glossary.md#virtual-machine-image [Vocabulary]: ../user-documentation/glossary.md#vocabulary -[Vocabularys]: ../user-documentation/glossary.md#vocabulary +[Vocabularies]: ../user-documentation/glossary.md#vocabulary [Weight]: ../user-documentation/glossary.md#weight [Weights]: ../user-documentation/glossary.md#weight [White Screen of Death ]: ../user-documentation/glossary.md#white-screen-of-death diff --git a/mkdocs.yml b/mkdocs.yml index 703c7e8e6..af8047b4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -321,12 +321,7 @@ nav: - 'Islandora 7': 'technical-documentation/migrate-7x.md' - Developer Resources: - 'Stack Overview': 'installation/component-overview.md' - # moved from "Installation" section; alternatively duplicate a simplified version in the overview section - # also see Architecture Diagram in Developer documentation - # also see user-intro.md#architecture for the cheeseburger/bento box analogy - # Conceptual/ reference, all user roles/ sys admins/ developers: - # procedural information should be moved out installation guides - - 'Architecture Diagram': 'technical-documentation/diagram.md' + - 'Islandora Architecture': 'technical-documentation/diagram.md' - REST Documentation: - 'Introduction': 'technical-documentation/using-rest-endpoints.md' - 'Authorization': 'technical-documentation/rest-authorization.md' @@ -338,10 +333,7 @@ nav: - Alpaca: - 'Alpaca Technical Stack': 'alpaca/alpaca-technical-stack.md' - 'Alpaca Tips': 'technical-documentation/alpaca-tips.md' - - Tests: - - 'Testing Notes': 'technical-documentation/testing-notes.md' - - 'Resizing a VM': 'technical-documentation/resizing-vm.md' - - 'Updating a `deb` and adding it to Lyrasis PPA': 'technical-documentation/ppa-documentation.md' + - Tests: 'technical-documentation/testing-notes.md' - 'Adding back ?_format=jsonld': 'technical-documentation/adding-format-jsonld.md' - Contribute to Islandora: - 'How to contribute': 'contributing/CONTRIBUTING.md' diff --git a/scripts/generate-abbreviations.sh b/scripts/generate-abbreviations.sh index bca41b77a..08f6687e9 100755 --- a/scripts/generate-abbreviations.sh +++ b/scripts/generate-abbreviations.sh @@ -47,6 +47,11 @@ pluralize() { return fi + if [[ $value =~ [^aeiouAEIOU]y$ ]]; then + printf '%sies\n' "${value::-1}" + return + fi + printf '%ss\n' "$value" } From 59706dafa570016a0767489da65a9e9b3564e25b Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 15:44:51 -0400 Subject: [PATCH 07/15] remove old note --- docs/technical-documentation/adding-format-jsonld.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/technical-documentation/adding-format-jsonld.md b/docs/technical-documentation/adding-format-jsonld.md index 2dd5165e6..6eac2580b 100644 --- a/docs/technical-documentation/adding-format-jsonld.md +++ b/docs/technical-documentation/adding-format-jsonld.md @@ -3,16 +3,8 @@ Drupal requires the use of a `_format` query parameter to get alternate represen By default, Islandora deploys with the [jsonld](https://github.com/Islandora/jsonld) module and the [Milliner](https://github.com/Islandora/Crayfish/tree/main/Milliner) microservice. These two components are configured to strip this `_format` query parameter off of the end of URIs. This means that when your content is indexed in Fedora, the triplestore, etc... it's URI will -be something like `http://localhost:8000/node/1` and not `http://localhost:8000/node/1?_format=jsonld`. +be something like `http://islandora.traefik.me/node/1` and not `http://islandora.traefik.me/node/1?_format=jsonld`. -## Pre-1.0 installations. - -If you are using a __very__ early version of Islandora "8" (pre-release), then you may have URIs with `_format=jsonld` at the end of them. - -If you update to newer code, you will need to ensure that your site is configured to add `?_format=jsonld` -back to the URLs if you want to maintain consistency. - -If you **don't** do this, you can end up with two copies of your objects in your Fedora repository (one with and one without `?_format=jsonld`). You will also have two sets of triples in your triplestore. ## Adding ?_format=jsonld to your URIs From 8b42fb5a6570c21c4175d1abf9a2df3e694388af Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 15:45:00 -0400 Subject: [PATCH 08/15] I don't know what CDM means --- docs/installation/component-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/component-overview.md b/docs/installation/component-overview.md index 96c4ca220..550df0ce4 100644 --- a/docs/installation/component-overview.md +++ b/docs/installation/component-overview.md @@ -22,7 +22,7 @@ Combined together, Apache, PHP, and MariaDB/PostgreSQL comprise a LAMP or LAPP s **[MariaDB]** and **[PostgreSQL]** are database management systems that we will use to store information for many different components like Drupal and Fedora. By default, the [ISLE Site Template] installs MariaDB, though this can be switched to PostgreSQL. -## The Front-Facing CDM - Composer, Drush, and Drupal +## Composer, Drush, and Drupal Composer will be used to install both Drupal and Drush simultaneously using [Islandora Starter Site] From 013baab4bd7988f878f483b9e4742310de4ac315 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 16:50:38 -0400 Subject: [PATCH 09/15] polish --- docs/technical-documentation/diagram.md | 115 ++++++++++++++---------- docs/user-documentation/flysystem.md | 2 +- docs/user-documentation/glossary.md | 4 + includes/abbreviations.md | 1 + 4 files changed, 75 insertions(+), 47 deletions(-) diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index 5ff2ee8d1..ed50cb01a 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -1,11 +1,18 @@ # Islandora Architecture -## Site Serving Path +At its core, Islandora is a [Drupal] website that leverages a suite of [Microservices]. To help illustrate this, different site operations are described below. + +- The diagram under [Islandora is a Drupal Website](#islandora-is-a-drupal-website) describes how a typical web page is displayed when a site visitor arrives at an Islandora repository on the internet. How most HTML responses are generated for Islandora sites are identical to any Drupal website that integrates with [Solr]. The exception being Islandora's [IIIF] integration, which is explained in a bit more detail in [IIIF Integration](#iiif-integration) +- The diagrams under [Microservices](#microservices) explain how events are emitted from an Islandora Drupal website and processed by Islandora's event driven architecture +- [Components](#components) is a list of software used in the Islandora tech stack + +## Islandora is a Drupal Website When a client visits an Islandora website, the request flow looks like a typical [Drupal] request. -The request is received by an nginx web server, which forwards the request to `php-fpm` process that points to a Drupal codebase. -Drupal bootstraps and queries the backend database (Islandora ships with `mariadb`). -If the request was for a search page, Drupal make also query `solr` to include search results in the HTML response. +The request is received by [NGINX], which forwards the request to [Drupal]. +Drupal bootstraps and queries [MariaDB] to generate an HTML response. +If the request was for a search page, Drupal may also query [Solr] to include search results in the HTML response. + ```mermaid flowchart TD user([Client / Browser]) @@ -37,16 +44,16 @@ flowchart TD ``` -### IIIF manifests +### IIIF Integration -For some [Resource Node]s in Islandora, the HTML response may include a [IIIF Manifest]. This typically happens when showing items that have image media. For those requests, Islandora's IIIF module may also query the [Cantaloupe] IIIF server for metadata needed to generate a [IIIF Manifest]. The HTML response will then download the images from Cantaloupe to allow a IIIF viewer (e.g. [OpenSeadragon] or [Mirador]) to render the image in the browser. +For some [Resource Nodes] in Islandora, the HTML response may include a link to a [IIIF Manifest] that is dynamically generated by Drupal for the given node. This typically happens when rendering [Resource Nodes] that have image [media]. For these types of requests, Islandora's [IIIF] module may also query the [Cantaloupe] IIIF server for metadata needed to generate the [IIIF Manifest]. The IIIF viewer configured on the Islandora site (e.g. [OpenSeadragon] or [Mirador]) will communicate with Cantaloupe to render the image(s) included in the IIIF Manifest. -In addition to [the typical drupal request flow](##site-serving-path), Drupal may also query Cantaloupe for basic image metadata (e.g. width/height) which are needed to complete a valid [IIIF Manifest] response. The client web browser will then read that IIIF Manifest using Javascript and the IIIF viewer will `GET` the images referenced in the IIIF Manifest directly from the IIIF server. +All this to say, in addition to [the typical drupal request flow](#islandora-is-a-drupal-website), Drupal may also query Cantaloupe for basic image metadata (e.g. height/width) which are needed to generate a valid [IIIF Manifest]. The client's web browser will then read that IIIF Manifest using Javascript and the IIIF viewer will `GET` the images referenced in the IIIF Manifest from [Cantaloupe]. ```mermaid flowchart TD user([Client / Browser]) - user e1@-->|HTTP request| nginx + user e1@-->|GET IIIF Manifest| nginx subgraph webserver[Nginx Web Server] nginx[Nginx] e2@-->|forward request| drupal[Drupal] @@ -72,22 +79,36 @@ flowchart TD ``` -## Event Driven System -When you create, update, or delete Drupal [entities], Drupal emits an event message which is emitted to [ActiveMQ] and put on ActiveMQ's queue. +### Fedora Flysystem Adapter + +Islandora uses [Flysystem] and the [associated Drupal module](https://www.drupal.org/project/flysystem) to store Drupal managed files in [Fedora (Repository Software)]. + +You can read more about this in [Islandora's Flysystem documentation](../user-documentation/flysystem/). + +## Microservices + +In addition to all the tools Drupal provides, Islandora extends the Drupal site's capabilities using an event-driven, distributed architecture of [Microservices]. When a repository manager creates, updates, or deletes Drupal [entities], the Islandora Drupal module generates an event message which is put on Islandora's [ActiveMQ] queue. +There are two different types of events Islandora emits: + +- [Derivative] Events - these types of events create [derivatives] from files uploaded to Islandora +- Index Events - these types of events create an indexed representation of a [Resource Node] in a system external to [Drupal] + +### Derivative Events + +Below is a full diagram of the different microservices Islandora provides. You can see as an animation in the diagram what happens when an Islandora repository manager uploads an image to their Islandora repository. First, Drupal emits an event to generate a thumbnail for that image. That event is put on the [ActiveMQ] event queue, [alpaca] reads the message from the queue, and forwards the event to the configured service. In the case of a thumbnail, [houdini] handles generating the thumbnail for the uploaded image. [Houdini] creates the thumbnail and alpaca saves the thumbnail in Drupal. ```mermaid flowchart TD drupal([Islandora Drupal Website]) - drupal ==>|publishes drupal entity event| activemq + drupal e1@-->|publishes drupal entity event| activemq subgraph broker[Message Broker] activemq[ActiveMQ] alpaca[Alpaca] - activemq ==> alpaca - note[\"Alpaca forwards the message to the\nappropriate service based on its config"/] + activemq e2@-->|alpaca receives event| alpaca end @@ -96,52 +117,52 @@ flowchart TD homarus[Homarus] houdini[Houdini] hypercube[Hypercube] + end - s[\"scyllaridae generates a derivative and saves the message to the\nappropriate service based on its config"/] + alpaca --> fits + alpaca --> homarus + alpaca e3@--> houdini + alpaca --> hypercube - end + fits -.->|derivative streamed back| alpaca + homarus -.->|derivative streamed back| alpaca + houdini e4@-.->|derivative streamed back| alpaca + hypercube -.->|derivative streamed back| alpaca + alpaca e5@-.->|alpaca saves the derivative| drupal - alpaca ==> fits - alpaca ==> homarus - alpaca ==> houdini - alpaca ==> hypercube - alpaca ==> milliner - - fits -.->|derivative saved to| alpaca - homarus -.->|derivative saved to| alpaca - houdini -.->|derivative saved to| alpaca - hypercube -.->|derivative saved to| alpaca -alpaca -.->|ok| drupal - fedora[(Fedora)] - milliner -.->|syncs resource to| fedora - blazegraph[(blazegraph)] - alpaca -.->|sends RDF| blazegraph + class e1 flow0; + class e2 flow1; + class e3 flow2; + class e4 flow3; + class e5 flow4; ``` -## Event Example +### Index Events + +There are two systems that are populated using Islandora Index Events: [Blazegraph] and [Fedora (Repository Software)]. -As an example, when an Islandora Repository manager uploades an image to their Islandora repository, an event is emitted to generate a thumbnail for that image. That event is put on the event queue, alpaca reads the message from the queue, and forwards the event to the configured service. In the case of a thumbnail, [houdini] handles generating the thumbnail for the uploaded image +- For [Blazegraph], [Alpaca] is fully implemented to be able to index content from Drupal directly into Blazegraph using [RDF]. +- For [Fedora (Repository Software)] an intermediate service is used called [Milliner] to store the metadata in Fedora. ```mermaid flowchart TD drupal([Islandora Drupal Website]) - drupal e1@==>|publishes entity event| activemq + drupal e1@-->|publishes drupal entity event| activemq subgraph broker[Message Broker] activemq[ActiveMQ] alpaca[Alpaca] - activemq e2@==> alpaca + activemq e2@-->|alpaca receives event| alpaca end - alpaca e3@==> houdini - subgraph microservices[scyllaridae microservice] - houdini[Houdini] - end + alpaca e3@--> milliner - houdini e4@-.->|derivative stream| alpaca - alpaca e5@-.->|saves derivative & responds ok| drupal + fedora[(Fedora)] + milliner e4@-.->|syncs resource to| fedora + blazegraph[(blazegraph)] + alpaca -.->|sends RDF| blazegraph class e1 flow0; class e2 flow1; @@ -154,13 +175,14 @@ flowchart TD ### Islandora -The following components are microservices developed and maintained by the Islandora community. They are bundled under [Islandora Crayfish](https://github.com/Islandora/Crayfish): +The following components are microservices developed and maintained by the Islandora community. They are bundled under [scyllaridae] and [Islandora Crayfish](https://github.com/Islandora/Crayfish): -* [FITS] -* [Homarus] -* [Houdini] -* [Hypercube] -* [Milliner] +* [scyllaridae] + * [Crayfits] + * [Homarus] + * [Houdini] + * [Hypercube] +* [Milliner] (uses [Crayfish]) ### Other Open Source @@ -173,6 +195,7 @@ The following components are deployed with Islandora, but are developed and main * [Blazegraph] * [Cantaloupe] * [Drupal] +* [FITS] * [Fedora (Repository Software)] * [MariaDB] * Triplestore - See [Blazegraph]. diff --git a/docs/user-documentation/flysystem.md b/docs/user-documentation/flysystem.md index 5d4bd4b0c..efcef381b 100644 --- a/docs/user-documentation/flysystem.md +++ b/docs/user-documentation/flysystem.md @@ -1,6 +1,6 @@ # Flysystem -Islandora uses [Flysystem](https://github.com/thephpleague/flysystem) and the [associated Drupal module](https://www.drupal.org/project/flysystem) to persist binary files to Fedora instead of keeping a copy in both Drupal and Fedora. +Islandora uses [Flysystem] and the [associated Drupal module](https://www.drupal.org/project/flysystem) to persist binary files to Fedora instead of keeping a copy in both Drupal and [Fedora (Repository Software)]. ## Background diff --git a/docs/user-documentation/glossary.md b/docs/user-documentation/glossary.md index 341f000b9..357d8bf0c 100644 --- a/docs/user-documentation/glossary.md +++ b/docs/user-documentation/glossary.md @@ -91,6 +91,10 @@ Composer is a tool for dependency management in PHP. It allows you to declare th ## Crayfish A collection of Islandora [microservices](#microservice). Some microservices are built specifically for use with a [Fedora](#fedora-repository-software) repository, while others are just for general use within Islandora. +## Crayfits +Crayfits is a [microservice](#microservice) wrapper of [FITS](#fits) for identifying, validating and extracting of technical metadata for a wide range of file formats. + + ## Datastream Deprecated terminology, refers to how [Fedora 3](#fedora-repository-software)/Islandora Legacy stored files as part of a resource ('object') in the [Fedora](#fedora-repository-software) repository. Replaced by [Drupal Media entities](https://www.drupal.org/docs/8/core/modules/media/overview), which 'wraps' [Files](https://www.drupal.org/docs/8/core/modules/file/overview) in an intermediate structure. This allows Fields to be attached to files, for instance for storing technical metadata. diff --git a/includes/abbreviations.md b/includes/abbreviations.md index 0ffa05a1f..6dd7cde91 100644 --- a/includes/abbreviations.md +++ b/includes/abbreviations.md @@ -40,6 +40,7 @@ [Composers]: ../user-documentation/glossary.md#composer [Crayfish]: ../user-documentation/glossary.md#crayfish [Crayfishs]: ../user-documentation/glossary.md#crayfish +[Crayfits]: ../user-documentation/glossary.md#crayfits [Datastream]: ../user-documentation/glossary.md#datastream [Datastreams]: ../user-documentation/glossary.md#datastream [Derivative]: ../user-documentation/glossary.md#derivative From 0bd84579acf5ed119f612abcf676b3f11cc4def9 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Fri, 27 Mar 2026 17:57:01 -0400 Subject: [PATCH 10/15] Fix preview snippets --- .github/actions/build/action.yml | 16 ++++++---------- .github/workflows/docs-preview.yml | 2 ++ Dockerfile | 4 ++-- Makefile | 21 ++++++++++++++++++++- mkdocs.yml | 4 +++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index acb0c5dd7..cd48feb2d 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -1,17 +1,13 @@ --- name: Build description: Setup to install dependencies and build docs with zensical +inputs: + site_url: + description: 'Override site_url (e.g. for PR previews)' + required: false + default: "https://islandora.github.io/documentation/" runs: using: "composite" steps: - - name: Setup build environment - uses: actions/setup-python@v4 - with: - python-version: '3.x' - cache: 'pip' - - name: Install build requirements + - run: make build SITE_URL="${{ inputs.site_url }}" shell: bash - run: pip install -r requirements.txt - - name: Build docs - shell: bash - run: zensical build diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index ac4123226..bc02bf30f 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v3 - name: Build docs uses: ./.github/actions/build + with: + site_url: https://islandora.github.io/documentation/pr-preview/pr-${{ github.event.number }}/ - name: Deploy docs preview # XXX: Avoid attempting to do preview things across fork boundaries, as it doesn't work. # See: https://github.com/rossjrw/pr-preview-action/pull/6 diff --git a/Dockerfile b/Dockerfile index 8605f490b..73f1eec8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,5 +8,5 @@ RUN apk add --no-cache git && \ EXPOSE 8080 -ENTRYPOINT ["zensical", "serve"] -CMD ["--dev-addr", "0.0.0.0:8080"] +ENTRYPOINT ["zensical"] +CMD ["serve", "--dev-addr", "0.0.0.0:8080"] diff --git a/Makefile b/Makefile index e37ecac5d..11c712bc8 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: docs docs-build abbreviations +.PHONY: docs docs-build build abbreviations preview abbreviations: ./scripts/generate-abbreviations.sh @@ -6,9 +6,28 @@ abbreviations: docs-build: docker build -t islandora-docs . +build: docs-build + rm -rf site + docker run --rm \ + $(if $(SITE_URL),-e SITE_URL=$(SITE_URL)) \ + -v "$(CURDIR):/work" \ + -w /work \ + islandora-docs \ + build --clean + docs: docs-build docker run --rm -it \ -p 8080:8080 \ -v "$(CURDIR):/work" \ -w /work \ islandora-docs + +preview: + $(MAKE) build SITE_URL=http://localhost:8080 + docker run --rm -it \ + -p 8080:8080 \ + -v "$(CURDIR)/site:/site" \ + -w /site \ + --entrypoint python3 \ + islandora-docs \ + -m http.server 8080 diff --git a/mkdocs.yml b/mkdocs.yml index af8047b4d..e247e4c88 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,12 +3,14 @@ site_description: Documentation for Islandora dev_addr: 'localhost:8111' repo_url: https://github.com/Islandora/documentation -site_url: https://islandora.github.io/documentation/ +site_url: !ENV [SITE_URL, 'https://islandora.github.io/documentation/'] edit_uri: 'edit/main/docs/' theme: name: material features: + - navigation.instant + - navigation.instant.prefetch - content.action.edit - content.action.view - content.code.annotate From bf53c21539cd1ddafdc426895d4953b2bacefd2a Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 29 Mar 2026 00:57:36 -0400 Subject: [PATCH 11/15] Update docs/installation/component-overview.md --- docs/installation/component-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/component-overview.md b/docs/installation/component-overview.md index 550df0ce4..f6ae757c3 100644 --- a/docs/installation/component-overview.md +++ b/docs/installation/component-overview.md @@ -11,7 +11,7 @@ This list includes four different kinds of components: ## The Webserver Stack - Traefik, NGINX, PHP, and MariaDB/PostgreSQL -Combined together, Apache, PHP, and MariaDB/PostgreSQL comprise a LAMP or LAPP server used to provide end-user-facing components - namely, the website. +Combined together, [Traefik], [NGINX], PHP, and [MariaDB] or [PostgreSQL] comprise a solution stack used to provide end-user-facing components - namely, the website.``` **[Traefik]** acts as the Islandora stack's front-end proxy. It will terminate TLS and routes requests to services in the Islandora stack i.e. Drupal, Cantaloupe, and Fedora. From 4fcc1e6f71a02b3b2bd8c1f14d2be16518763e72 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 29 Mar 2026 01:00:52 -0400 Subject: [PATCH 12/15] Update docs/technical-documentation/diagram.md --- docs/technical-documentation/diagram.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index ed50cb01a..247379e0e 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -198,4 +198,7 @@ The following components are deployed with Islandora, but are developed and main * [FITS] * [Fedora (Repository Software)] * [MariaDB] -* Triplestore - See [Blazegraph]. +* [NGINX] +* [PostgreSQL] +* Triplestore - See [Blazegraph] +* [Traefik] From e70a410b1248bb52868da6d225a56275c6978f1b Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 29 Mar 2026 01:05:37 -0400 Subject: [PATCH 13/15] Update docs/technical-documentation/diagram.md --- docs/technical-documentation/diagram.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/technical-documentation/diagram.md b/docs/technical-documentation/diagram.md index 247379e0e..67bb77a52 100644 --- a/docs/technical-documentation/diagram.md +++ b/docs/technical-documentation/diagram.md @@ -200,5 +200,5 @@ The following components are deployed with Islandora, but are developed and main * [MariaDB] * [NGINX] * [PostgreSQL] -* Triplestore - See [Blazegraph] * [Traefik] +* Triplestore - See [Blazegraph] From 69c44fcb74557987eae31589e439264707f46de6 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 29 Mar 2026 01:28:28 -0400 Subject: [PATCH 14/15] don't make it sound like we run mariadb and postgresql together --- docs/installation/component-overview.md | 4 ++-- docs/user-documentation/glossary.md | 14 ++++++++++++-- includes/abbreviations.md | 4 ++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/installation/component-overview.md b/docs/installation/component-overview.md index f6ae757c3..6649e6037 100644 --- a/docs/installation/component-overview.md +++ b/docs/installation/component-overview.md @@ -9,9 +9,9 @@ This list includes four different kinds of components: - Components that can't easily be swapped out but are not necessarily required (such as using Solr as the site's internal search engine) - Components which do not have official alternatives and are not necessarily required, but will likely exist on the vast majority of Islandora installations (such as Alpaca and Crayfish) -## The Webserver Stack - Traefik, NGINX, PHP, and MariaDB/PostgreSQL +## The Webserver Stack - Traefik, NGINX, PHP, and MariaDB (or PostgreSQL) -Combined together, [Traefik], [NGINX], PHP, and [MariaDB] or [PostgreSQL] comprise a solution stack used to provide end-user-facing components - namely, the website.``` +Combined together, [Traefik], [NGINX], PHP, and [MariaDB] (or [PostgreSQL]) comprise a solution stack used to provide end-user-facing components - namely, the website.``` **[Traefik]** acts as the Islandora stack's front-end proxy. It will terminate TLS and routes requests to services in the Islandora stack i.e. Drupal, Cantaloupe, and Fedora. diff --git a/docs/user-documentation/glossary.md b/docs/user-documentation/glossary.md index 357d8bf0c..86942e6e2 100644 --- a/docs/user-documentation/glossary.md +++ b/docs/user-documentation/glossary.md @@ -94,7 +94,6 @@ A collection of Islandora [microservices](#microservice). Some microservices are ## Crayfits Crayfits is a [microservice](#microservice) wrapper of [FITS](#fits) for identifying, validating and extracting of technical metadata for a wide range of file formats. - ## Datastream Deprecated terminology, refers to how [Fedora 3](#fedora-repository-software)/Islandora Legacy stored files as part of a resource ('object') in the [Fedora](#fedora-repository-software) repository. Replaced by [Drupal Media entities](https://www.drupal.org/docs/8/core/modules/media/overview), which 'wraps' [Files](https://www.drupal.org/docs/8/core/modules/file/overview) in an intermediate structure. This allows Fields to be attached to files, for instance for storing technical metadata. @@ -104,6 +103,9 @@ A version of a file which is derived from an uploaded file. For example, a thumb ## Docker [Docker](https://www.docker.com/) is a platform that use OS-level virtualization to deliver software in packages called containers. Islandora uses Docker as part of [ISLE](#isle), a suite of Docker containers that run the various components of Islandora. +## Docker Compose +[Docker Compose](https://docs.docker.com/compose/) is a tool for defining and running multi-container applications. It is the key to unlocking a streamlined and efficient development and deployment experience. + ## Drupal Drupal is an open source web content management system (CMS) written in PHP. Known for being extremely flexible and extensible, Drupal is supported by a community of over 630,000 users and developers. Drupal sites can be customized and themed in a wide variety of ways. Drupal sites must include [Drupal Core](#drupal-core) and usually involve additional, Contributed code. @@ -114,7 +116,6 @@ The files, themes, profile, and modules included with the standard project softw Roles are a way of assigning specific permissions to a group of users. Any user assigned to a role will have the same permissions as all other users assigned to that role. This allows you to control which users have permission to view, edit, or delete content in [Drupal](#drupal). Islandora provides a special role called _fedoraAdmin_ that is required to have actions in Drupal reflected in [Fedora](#fedora-repository-software). ## Drush - Drush is a command line shell and Unix scripting interface for Drupal. Drush core ships with lots of [useful commands](https://www.drush.org/13.x/commands/all/) and [generators](https://www.drush.org/13.x/generators/all/). Similarly, it runs update.php, executes SQL queries, runs content migrations, and misc utilities like cron or cache rebuild. Drush can be extended by [3rd party commandfiles](https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A4654). [https://www.drush.org/](https://www.drush.org/) @@ -254,6 +255,15 @@ A set of human-readable [YAML](#yaml) files, containing instructions for automat ## ISLE ISLE, or ISLandora Enterprise, is a community initiative to ease the installation and maintenance of Islandora by using [Docker](#docker). ISLE is one of the installation methods currently supported by the Islandora community. +## ISLE Site Template +[ISLE Site Template](https://github.com/Islandora-Devops/isle-site-template) is a [Docker Compose] project provided in [a GitHub Template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template). The project is used for building and customizing your institution's Islandora installation, for use as both a development and production environment. + +Creating a GitHub repository from a template is similar to [forking a git repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo), but there are important differences: + +- A new GitHub fork includes the entire commit history of the parent git repository, while a git repository created from a template starts with a single commit. +- A GitHub fork can be a temporary way to contribute code to an existing project, while creating a git repository from a template starts a **new** project quickly. + + ## JSON-LD [JSON-LD (JavaScript Object Notation for Linked Data)](https://json-ld.org/) is a method of encoding [linked data](#linked-data) using JSON. diff --git a/includes/abbreviations.md b/includes/abbreviations.md index 6dd7cde91..23c2cf7d1 100644 --- a/includes/abbreviations.md +++ b/includes/abbreviations.md @@ -47,6 +47,8 @@ [Derivatives]: ../user-documentation/glossary.md#derivative [Docker]: ../user-documentation/glossary.md#docker [Dockers]: ../user-documentation/glossary.md#docker +[Docker Compose]: ../user-documentation/glossary.md#docker-compose +[Docker Composes]: ../user-documentation/glossary.md#docker-compose [Drupal]: ../user-documentation/glossary.md#drupal [Drupals]: ../user-documentation/glossary.md#drupal [Drupal Core]: ../user-documentation/glossary.md#drupal-core @@ -108,6 +110,8 @@ [Islandora playbooks]: ../user-documentation/glossary.md#islandora-playbook [ISLE]: ../user-documentation/glossary.md#isle [ISLEs]: ../user-documentation/glossary.md#isle +[ISLE Site Template]: ../user-documentation/glossary.md#isle-site-emplate +[ISLE Site Templates]: ../user-documentation/glossary.md#isle-site-emplate [JSON-LD]: ../user-documentation/glossary.md#json-ld [JSON-LDs]: ../user-documentation/glossary.md#json-ld [Linked data]: ../user-documentation/glossary.md#linked-data From c87b8d1f0a5e29fd164d33c97ee2d87463161316 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 29 Mar 2026 02:37:09 -0400 Subject: [PATCH 15/15] update docs build and add snippets to style guide --- Makefile | 18 +++-- docs/contributing/docs-style-guide.md | 2 +- docs/technical-documentation/docs-build.md | 92 +++++++++------------- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index 11c712bc8..691c44468 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,18 @@ -.PHONY: docs docs-build build abbreviations preview +.PHONY: docker-build build abbreviations help preview serve -abbreviations: +help: ## Show this help message + echo 'Usage: make [target]' + echo '' + echo 'Available targets:' + awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%s\033[0m\t%s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort | column -t -s $$'\t' + +abbreviations: ## Parse ./docs/user-documentation/glossary.md and create Zensical Snippets other docs can reference ./scripts/generate-abbreviations.sh -docs-build: +docker-build: ## Build the Zensical python application docker build -t islandora-docs . -build: docs-build +build: docker-build ## Run zensical build --clean rm -rf site docker run --rm \ $(if $(SITE_URL),-e SITE_URL=$(SITE_URL)) \ @@ -15,14 +21,14 @@ build: docs-build islandora-docs \ build --clean -docs: docs-build +serve: docker-build ## Run zensical serve to make the docs available at http://localhost:8080. You can then make live updates to the docs. docker run --rm -it \ -p 8080:8080 \ -v "$(CURDIR):/work" \ -w /work \ islandora-docs -preview: +preview: ## Make the contents from zensical build --clean available at http://localhost:8080. Use for troubleshooting GitHuub Pages issues. $(MAKE) build SITE_URL=http://localhost:8080 docker run --rm -it \ -p 8080:8080 \ diff --git a/docs/contributing/docs-style-guide.md b/docs/contributing/docs-style-guide.md index d4692a226..966815e1e 100644 --- a/docs/contributing/docs-style-guide.md +++ b/docs/contributing/docs-style-guide.md @@ -8,7 +8,7 @@ - Submit documentation formatted in [Markdown](https://en.wikipedia.org/wiki/Markdown) format. - Include a top-level heading for the whole page (using `#`) - Please add Markdown headings (`#` and `##`) to the content sections. - +- Wrap glossary terms used in Markdown with brackets `[]` when you want that term to leverage [Zensical's snippets/glossary](https://zensical.org/docs/authoring/tooltips/#add-a-glossary) feature. A snippet provides the definition of a term without needing to look it up in the glossary by simply mousing over the term. - Use the "bold/emphasis" style in Markdown by enclosing text in double asterisks or underscores, `**bold text**` or `__bold text__`, for UI elements that users will interact with. For example, a button label for a button that must be pressed should be made bold in Markdown. - Use the "italics" style in Markdown by enclosing text in single asterisks or underscores, `*italic text*` or `_italic text_`, for UI elements that have a label or title if you need to reference them in the documentation. For example, a title of a screen or page that will visit should be made italic in Markdown. - Use `>>` and `**bold text**` to indicate clicking through nested menu items, and also include the direct path. _Example:_ diff --git a/docs/technical-documentation/docs-build.md b/docs/technical-documentation/docs-build.md index a84e06621..5ef536d99 100644 --- a/docs/technical-documentation/docs-build.md +++ b/docs/technical-documentation/docs-build.md @@ -1,83 +1,67 @@ # Introduction -This documentation is built using [MkDocs](http://www.mkdocs.org/), a static site generator that is geared towards building project documentation. The documentation is created in the [Markdown](http://en.wikipedia.org/wiki/Markdown) format, and it all resides in the [`docs`](https://github.com/Islandora/documentation/tree/main/docs) directory in the repository. The organization of the documentation is controlled by the [`mkdocs.yml`](https://github.com/Islandora/documentation/blob/main/mkdocs.yml) in the root of the repository. - -!!! Tip "Video version available" - Some of the material in this tutorial is presented in our video, [How to Build Documentation](https://youtu.be/YgSXicNow5w). - +This documentation is built using [Zensical](https://zensical.org/docs/get-started/), a static site generator that is geared towards building project documentation. The documentation is created in the [Markdown](http://en.wikipedia.org/wiki/Markdown) format. All of the documentation resides in the [`docs`](https://github.com/Islandora/documentation/tree/main/docs) directory in the repository. The organization of the documentation is controlled by the [`mkdocs.yml`](https://github.com/Islandora/documentation/blob/main/mkdocs.yml) in the root of the repository. ## Prerequisites -You will need to have `mkdocs` software installed locally, as well as a required plugin and the MkDocs _Material_ theme. Below we will show you how to install `mkdocs` using the Python language's `pip` tool. For more details on installing and using MkDocs visit the [MkDocs installation guide](https://www.mkdocs.org/#installation). - -- Open a terminal window. - -- Install `mkdocs`: - - Windows / Linux: - - `sudo -H pip install mkdocs` - - macOS: - - `pip3 install mkdocs` - - - -- Install plugin to enable display of the last revision date: - - Windows / Linux: - - `sudo -H pip install mkdocs-git-revision-date-localized-plugin` - - macOS: - - `pip3 install mkdocs-git-revision-date-localized-plugin` - - -- Install plugin to enable redirects: +On your local machine you will need to have - Windows / Linux: +- [Docker](https://docs.docker.com/engine/install/) +- `git` +- `make` - `sudo -H pip install mkdocs-redirects` +If you're an Islandora committer or have access to edit Islandora Documentation, you can checkout [https://github.com/Islandora/documentation](https://github.com/Islandora/documentation) - macOS: +``` +git clone git@github.com:Islandora/documentation +cd documentation +git checkout -B "$(whoami)-patch-1" +``` - `pip3 install mkdocs-redirects` +If you can not make edits to the documentation you will need to fork [https://github.com/Islandora/documentation](https://github.com/Islandora/documentation) and run a similar command as above using your fork repo instead of `git@github.com:Islandora/documentation` +## Make edits to the documentation locally -- Install Material theme: +You can view the docs at [http://localhost:8080](http://localhost:8080) by running - Windows / Linux: +``` +make serve +``` - `sudo -H pip install mkdocs-material` +This will create a live preview of Islandora's docs. While `make serve` is running you can then make edits to the markdown files and your web browser will automatically reload when you save the file. - macOS: +To stop the `make serve` command just type the key combination "Control-c". - `pip3 install mkdocs-material` +### Glossary and Snippets +Islandora's docs leverage [Zensical's glossary](https://zensical.org/docs/authoring/tooltips/#add-a-glossary) feature. +> The Snippets extension can be used to implement a simple glossary by moving all abbreviations in a dedicated file, and auto-append this file to all pages. -## Build and Deploy documentation +Our docs are configured so any term in [Islandora's Glossary](https://islandora.github.io/documentation/user-documentation/glossary/) can be referenced in any markdown file by wrapping the term in brackets `[]`. Adding brackets to a term will add a rich snippet for the term when someone hovers their mouse over the word. A snippet provides the definition of a term without needing to look it up in the glossary. -Make sure you have all the submodules: +If you a create a new term in `glossary.md` you can run the following `make` helper to get the snippets to work properly. Namely, it populates `./includes/abbreviations.md` with the correct markdown to the glossary terms so docs can reference them. This also runs in GitHub Actions before deploying incase this step is forgotten by a docs editor. -`git submodule update --init --recursive` +``` +make abbreviations +``` -Documentation is build by running to the following command in the root of the repository: +Only add terms to the glossary if they will be referenced by other documentation pages. If you just want a couple snippets on a particular docs page and they aren't generally useful you can define snippets on individual markdown pages. -`mkdocs build --clean` +Once you're done with your edits, put up a PR and a preview site will be created to show your changes. Make sure the changes are the same as your local build was showing you. If something doesn't look right, you can debug with the steps below. -This command will create a static `site` folder in the root of the repository. +## Debug GitHub Pages Preview -You can preview any changes you have made to the documentation by running the following command: +Islandora's documentation is deployed to GitHub Pages using GitHub Actions. When you create a PR on [https://github.com/Islandora/documentation](https://github.com/Islandora/documentation) a preview site of your changes will be made available by an automated comment on the PR. -`mkdocs serve` +If something in GitHub Pages doesn't look the same as it did on your local machine, you can use the steps below to help troubleshoot: -And then visiting http://localhost:8111 in your browser. +``` +make preview +``` -To deploy documentation to GitHub Pages, issue the following command: +This command will create a static `site` folder in the root of the repository. That set of HTML files is what GitHub Pages shows. The HTML will be available at [http://localhost:8080](http://localhost:8080). -`mkdocs gh-deploy --clean` +`make preview` won't have live edits like `make serve` does but you can use this to more closely replicate GitHub Pages' infrastructure and troubleshoot bugs or subtle differences between zensical's `serve` and `build` commands that may be undocumented or unknown. -To stop the `mkdocs serve` command just type the key combination "Control-c". +To stop the `make preview` command just type the key combination "Control-c".