From 375b5796d95774f152d5f85cb97dd3af8f307a59 Mon Sep 17 00:00:00 2001
From: Fabian Dill <Berserker66@users.noreply.github.com>
Date: Wed, 16 Oct 2024 23:28:42 +0200
Subject: [PATCH] WebHost: noscript faq and glossary (#4061)

---
 WebHostLib/misc.py                            | 23 +++++++--
 WebHostLib/requirements.txt                   |  2 +
 WebHostLib/static/assets/faq.js               | 51 -------------------
 .../static/assets/faq/{faq_en.md => en.md}    |  0
 WebHostLib/static/assets/glossary.js          | 51 -------------------
 .../{faq/glossary_en.md => glossary/en.md}    |  0
 WebHostLib/templates/faq.html                 | 17 -------
 WebHostLib/templates/glossary.html            | 17 -------
 WebHostLib/templates/markdown_document.html   | 13 +++++
 9 files changed, 34 insertions(+), 140 deletions(-)
 delete mode 100644 WebHostLib/static/assets/faq.js
 rename WebHostLib/static/assets/faq/{faq_en.md => en.md} (100%)
 delete mode 100644 WebHostLib/static/assets/glossary.js
 rename WebHostLib/static/assets/{faq/glossary_en.md => glossary/en.md} (100%)
 delete mode 100644 WebHostLib/templates/faq.html
 delete mode 100644 WebHostLib/templates/glossary.html
 create mode 100644 WebHostLib/templates/markdown_document.html

diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py
index 4784fcd9..1f86e210 100644
--- a/WebHostLib/misc.py
+++ b/WebHostLib/misc.py
@@ -5,6 +5,7 @@ from typing import Any, IO, Dict, Iterator, List, Tuple, Union
 import jinja2.exceptions
 from flask import request, redirect, url_for, render_template, Response, session, abort, send_from_directory
 from pony.orm import count, commit, db_session
+from werkzeug.utils import secure_filename
 
 from worlds.AutoWorld import AutoWorldRegister
 from . import app, cache
@@ -69,14 +70,28 @@ def tutorial_landing():
 
 @app.route('/faq/<string:lang>/')
 @cache.cached()
-def faq(lang):
-    return render_template("faq.html", lang=lang)
+def faq(lang: str):
+    import markdown
+    with open(os.path.join(app.static_folder, "assets", "faq", secure_filename(lang)+".md")) as f:
+        document = f.read()
+    return render_template(
+        "markdown_document.html",
+        title="Frequently Asked Questions",
+        html_from_markdown=markdown.markdown(document,  extensions=["mdx_breakless_lists"]),
+    )
 
 
 @app.route('/glossary/<string:lang>/')
 @cache.cached()
-def terms(lang):
-    return render_template("glossary.html", lang=lang)
+def glossary(lang: str):
+    import markdown
+    with open(os.path.join(app.static_folder, "assets", "glossary", secure_filename(lang)+".md")) as f:
+        document = f.read()
+    return render_template(
+        "markdown_document.html",
+        title="Glossary",
+        html_from_markdown=markdown.markdown(document,  extensions=["mdx_breakless_lists"]),
+    )
 
 
 @app.route('/seed/<suuid:seed>')
diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt
index c593cd63..20203870 100644
--- a/WebHostLib/requirements.txt
+++ b/WebHostLib/requirements.txt
@@ -9,3 +9,5 @@ bokeh>=3.1.1; python_version <= '3.8'
 bokeh>=3.4.3; python_version == '3.9'
 bokeh>=3.5.2; python_version >= '3.10'
 markupsafe>=2.1.5
+Markdown>=3.7
+mdx-breakless-lists>=1.0.1
diff --git a/WebHostLib/static/assets/faq.js b/WebHostLib/static/assets/faq.js
deleted file mode 100644
index 1bf5e5a6..00000000
--- a/WebHostLib/static/assets/faq.js
+++ /dev/null
@@ -1,51 +0,0 @@
-window.addEventListener('load', () => {
-    const tutorialWrapper = document.getElementById('faq-wrapper');
-    new Promise((resolve, reject) => {
-        const ajax = new XMLHttpRequest();
-        ajax.onreadystatechange = () => {
-            if (ajax.readyState !== 4) { return; }
-            if (ajax.status === 404) {
-                reject("Sorry, the tutorial is not available in that language yet.");
-                return;
-            }
-            if (ajax.status !== 200) {
-                reject("Something went wrong while loading the tutorial.");
-                return;
-            }
-            resolve(ajax.responseText);
-        };
-        ajax.open('GET', `${window.location.origin}/static/assets/faq/` +
-          `faq_${tutorialWrapper.getAttribute('data-lang')}.md`, true);
-        ajax.send();
-    }).then((results) => {
-        // Populate page with HTML generated from markdown
-        showdown.setOption('tables', true);
-        showdown.setOption('strikethrough', true);
-        showdown.setOption('literalMidWordUnderscores', true);
-        tutorialWrapper.innerHTML += (new showdown.Converter()).makeHtml(results);
-        adjustHeaderWidth();
-
-        // Reset the id of all header divs to something nicer
-        for (const header of document.querySelectorAll('h1, h2, h3, h4, h5, h6')) {
-            const headerId = header.innerText.replace(/\s+/g, '-').toLowerCase();
-            header.setAttribute('id', headerId);
-            header.addEventListener('click', () => {
-                window.location.hash = `#${headerId}`;
-                header.scrollIntoView();
-            });
-        }
-
-        // Manually scroll the user to the appropriate header if anchor navigation is used
-        document.fonts.ready.finally(() => {
-            if (window.location.hash) {
-                const scrollTarget = document.getElementById(window.location.hash.substring(1));
-                scrollTarget?.scrollIntoView();
-            }
-        });
-    }).catch((error) => {
-        console.error(error);
-        tutorialWrapper.innerHTML =
-            `<h2>This page is out of logic!</h2>
-            <h3>Click <a href="${window.location.origin}">here</a> to return to safety.</h3>`;
-    });
-});
diff --git a/WebHostLib/static/assets/faq/faq_en.md b/WebHostLib/static/assets/faq/en.md
similarity index 100%
rename from WebHostLib/static/assets/faq/faq_en.md
rename to WebHostLib/static/assets/faq/en.md
diff --git a/WebHostLib/static/assets/glossary.js b/WebHostLib/static/assets/glossary.js
deleted file mode 100644
index 04a29200..00000000
--- a/WebHostLib/static/assets/glossary.js
+++ /dev/null
@@ -1,51 +0,0 @@
-window.addEventListener('load', () => {
-    const tutorialWrapper = document.getElementById('glossary-wrapper');
-    new Promise((resolve, reject) => {
-        const ajax = new XMLHttpRequest();
-        ajax.onreadystatechange = () => {
-            if (ajax.readyState !== 4) { return; }
-            if (ajax.status === 404) {
-                reject("Sorry, the glossary page is not available in that language yet.");
-                return;
-            }
-            if (ajax.status !== 200) {
-                reject("Something went wrong while loading the glossary.");
-                return;
-            }
-            resolve(ajax.responseText);
-        };
-        ajax.open('GET', `${window.location.origin}/static/assets/faq/` +
-          `glossary_${tutorialWrapper.getAttribute('data-lang')}.md`, true);
-        ajax.send();
-    }).then((results) => {
-        // Populate page with HTML generated from markdown
-        showdown.setOption('tables', true);
-        showdown.setOption('strikethrough', true);
-        showdown.setOption('literalMidWordUnderscores', true);
-        tutorialWrapper.innerHTML += (new showdown.Converter()).makeHtml(results);
-        adjustHeaderWidth();
-
-        // Reset the id of all header divs to something nicer
-        for (const header of document.querySelectorAll('h1, h2, h3, h4, h5, h6')) {
-            const headerId = header.innerText.replace(/\s+/g, '-').toLowerCase();
-            header.setAttribute('id', headerId);
-            header.addEventListener('click', () => {
-                window.location.hash = `#${headerId}`;
-                header.scrollIntoView();
-            });
-        }
-
-        // Manually scroll the user to the appropriate header if anchor navigation is used
-        document.fonts.ready.finally(() => {
-            if (window.location.hash) {
-                const scrollTarget = document.getElementById(window.location.hash.substring(1));
-                scrollTarget?.scrollIntoView();
-            }
-        });
-    }).catch((error) => {
-        console.error(error);
-        tutorialWrapper.innerHTML =
-            `<h2>This page is out of logic!</h2>
-            <h3>Click <a href="${window.location.origin}">here</a> to return to safety.</h3>`;
-    });
-});
diff --git a/WebHostLib/static/assets/faq/glossary_en.md b/WebHostLib/static/assets/glossary/en.md
similarity index 100%
rename from WebHostLib/static/assets/faq/glossary_en.md
rename to WebHostLib/static/assets/glossary/en.md
diff --git a/WebHostLib/templates/faq.html b/WebHostLib/templates/faq.html
deleted file mode 100644
index 76bdb96d..00000000
--- a/WebHostLib/templates/faq.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends 'pageWrapper.html' %}
-
-{% block head %}
-    {% include 'header/grassHeader.html' %}
-    <title>Frequently Asked Questions</title>
-    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename="styles/markdown.css") }}" />
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
-            integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
-            crossorigin="anonymous"></script>
-    <script type="application/ecmascript" src="{{ url_for('static', filename="assets/faq.js") }}"></script>
-{% endblock %}
-
-{% block body %}
-    <div id="faq-wrapper" data-lang="{{ lang }}" class="markdown">
-        <!-- Content generated by JavaScript -->
-    </div>
-{%  endblock %}
diff --git a/WebHostLib/templates/glossary.html b/WebHostLib/templates/glossary.html
deleted file mode 100644
index 921f6781..00000000
--- a/WebHostLib/templates/glossary.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends 'pageWrapper.html' %}
-
-{% block head %}
-    {% include 'header/grassHeader.html' %}
-    <title>Glossary</title>
-    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename="styles/markdown.css") }}" />
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
-            integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
-            crossorigin="anonymous"></script>
-    <script type="application/ecmascript" src="{{ url_for('static', filename="assets/glossary.js") }}"></script>
-{% endblock %}
-
-{% block body %}
-    <div id="glossary-wrapper" data-lang="{{ lang }}" class="markdown">
-        <!-- Content generated by JavaScript -->
-    </div>
-{%  endblock %}
diff --git a/WebHostLib/templates/markdown_document.html b/WebHostLib/templates/markdown_document.html
new file mode 100644
index 00000000..07b3c835
--- /dev/null
+++ b/WebHostLib/templates/markdown_document.html
@@ -0,0 +1,13 @@
+{% extends 'pageWrapper.html' %}
+
+{% block head %}
+    {% include 'header/grassHeader.html' %}
+    <title>{{ title }}</title>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename="styles/markdown.css") }}" />
+{% endblock %}
+
+{% block body %}
+    <div class="markdown">
+        {{ html_from_markdown | safe}}
+    </div>
+{%  endblock %}