diff --git a/.github/jobs/baseinstall.sh b/.github/jobs/baseinstall.sh index 40d1ab2103..719f065608 100755 --- a/.github/jobs/baseinstall.sh +++ b/.github/jobs/baseinstall.sh @@ -30,7 +30,6 @@ section_end section_start "Install JS frontend dependencies" cd webapp -apt-get update; apt-get install -y yarnpkg yarnpkg install cd .. section_end diff --git a/webapp/package.json b/webapp/package.json index fcb1bdf6ff..d623b03ca5 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -5,6 +5,7 @@ "d3": "3.5.17", "file-saver": "^2.0.5", "jquery-debounce-throttle": "^1.0.6-rc.0", + "mathjax": "^4.0.0", "monaco-editor": "^0.54.0", "nvd3": "1.8.6", "select2": "^4.0.13" diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index 371ae20e92..fdb21bb8be 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -939,6 +939,7 @@ function previewClarification($input, $previewDiv) { } }).done(function (data) { $previewDiv.html(data.html); + MathJax.typesetPromise([document.getElementById($previewDiv[0].id)]); }); } } diff --git a/webapp/public/js/tex-chtml.js b/webapp/public/js/tex-chtml.js new file mode 120000 index 0000000000..47467ad03c --- /dev/null +++ b/webapp/public/js/tex-chtml.js @@ -0,0 +1 @@ +../../node_modules/mathjax/tex-chtml.js \ No newline at end of file diff --git a/webapp/public/mathjax b/webapp/public/mathjax new file mode 120000 index 0000000000..3a045cb444 --- /dev/null +++ b/webapp/public/mathjax @@ -0,0 +1 @@ +../node_modules/mathjax \ No newline at end of file diff --git a/webapp/public/mathjaxfonts b/webapp/public/mathjaxfonts new file mode 120000 index 0000000000..89fd736689 --- /dev/null +++ b/webapp/public/mathjaxfonts @@ -0,0 +1 @@ +../node_modules/@mathjax/mathjax-newcm-font/chtml/woff2 \ No newline at end of file diff --git a/webapp/src/EventListener/AddContentSecurityPolicyListener.php b/webapp/src/EventListener/AddContentSecurityPolicyListener.php index d244aa0796..7b04339aaa 100644 --- a/webapp/src/EventListener/AddContentSecurityPolicyListener.php +++ b/webapp/src/EventListener/AddContentSecurityPolicyListener.php @@ -17,7 +17,7 @@ public function __invoke(ResponseEvent $event): void // the profiler requires 'unsafe-eval' for script-src 'self'. $response = $event->getResponse(); $cspExtra = $this->profiler ? "'unsafe-eval'" : ""; - $csp = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' $cspExtra; img-src 'self' data:; worker-src 'self' blob:"; + $csp = "font-src 'self' data:; default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' $cspExtra; img-src 'self' data:; worker-src 'self' blob:"; $response->headers->set('Content-Security-Policy', $csp); } } diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 60df1de6cc..bb71508235 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -37,6 +37,7 @@ use Twig\Environment; use Twig\Extension\AbstractExtension; use Twig\Extension\GlobalsInterface; +use Twig\Extra\Markdown\MarkdownRuntime; use Twig\Runtime\EscaperRuntime; use Twig\TwigFilter; use Twig\TwigFunction; @@ -124,6 +125,7 @@ public function getFilters(): array new TwigFilter('medalType', $this->awards->medalType(...)), new TwigFilter('numTableActions', $this->numTableActions(...)), new TwigFilter('extensionToMime', $this->extensionToMime(...)), + new TwigFilter('domjudge_markdown_to_html', $this->domjudgeMarkdownToHTML(...), ['is_safe' => ['html']]), ]; } @@ -1406,4 +1408,35 @@ public function extensionToMime(string $extension): string { return DOMJudgeService::EXTENSION_TO_MIMETYPE[$extension]; } + + public function domjudgeMarkdownToHTML(string $markdown): string + { + $latexFound = []; + while (true) { + $start = strpos($markdown, '$$'); + $end = strpos(substr($markdown, $start+2), '$$'); + if ($start === false || $end === false) { + break; + } + $latexFound[] = substr($markdown, $start, $end+2+2); + $newMarkdown = substr($markdown, 0, $start); + $newMarkdown .= '$LaTeX$'; + $newMarkdown .= substr($markdown, $end+$start+4); + $markdown = $newMarkdown; + } + + /** @var MarkdownRuntime $runtime */ + $runtime = $this->twig->getRuntime(MarkdownRuntime::class); + $markdown = (string)$runtime->convert($markdown); + + $new = ''; + foreach ($latexFound as $inlineLatex) { + $replacedStart = strpos($markdown, '$LaTeX$'); + $new = substr($markdown, 0, $replacedStart); + $new .= $inlineLatex; + $new .= substr($markdown, $replacedStart+strlen('$LaTeX$')); + $markdown = $new; + } + return $markdown; + } } diff --git a/webapp/templates/base.html.twig b/webapp/templates/base.html.twig index e953356b67..ecf48113bc 100644 --- a/webapp/templates/base.html.twig +++ b/webapp/templates/base.html.twig @@ -13,6 +13,7 @@ + {% include 'partials/mathjaxhead.html.twig' %} {% for file in customAssetFiles('js') %} diff --git a/webapp/templates/jury/clarification.html.twig b/webapp/templates/jury/clarification.html.twig index e7e7c4d24b..28b7fabb79 100644 --- a/webapp/templates/jury/clarification.html.twig +++ b/webapp/templates/jury/clarification.html.twig @@ -19,6 +19,13 @@ {% endif %} {% for clar in list %} +
+| + | {{ clarification.summary | markdown_to_html | sanitize_html('app.clarification_sanitizer') }} + |