From 5f9d7e31c92680c87baed0d9b812784b41d32a68 Mon Sep 17 00:00:00 2001 From: nievax Date: Mon, 21 Oct 2024 10:02:42 +0200 Subject: [PATCH 1/8] Update utils.py add function inject_textblocks(); it makes it possible to inject templates into freetext fields via Markdown --- rdmo/core/utils.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rdmo/core/utils.py b/rdmo/core/utils.py index 155109c6d..0cee6221f 100644 --- a/rdmo/core/utils.py +++ b/rdmo/core/utils.py @@ -9,7 +9,7 @@ from django.conf import settings from django.http import Http404, HttpResponse, HttpResponseBadRequest -from django.template.loader import get_template +from django.template.loader import get_template, render_to_string from django.utils.dateparse import parse_date from django.utils.encoding import force_str from django.utils.formats import get_format @@ -267,6 +267,18 @@ def markdown2html(markdown_string): f' ({hide_string})

', html ) + html = inject_textblocks(html) + return html + + +def inject_textblocks(html): + '''textblocks (e.g. for help texts) can be injected into free text fields as small templates via Markdown''' + for textblock_id in re.findall(r'{{(.*?)}}', html): # strings between curly brackets + template_path: str = settings.MARKDOWN_TEMPLATES[textblock_id] + html = re.sub( '{{' + textblock_id + '}}', + render_to_string(template_path), + html + ) return html From 7b32f9ed37568b172c107048f23b8de8d2de34c9 Mon Sep 17 00:00:00 2001 From: Axel Nieder-Vahrenholz Date: Wed, 23 Oct 2024 09:11:02 +0200 Subject: [PATCH 2/8] set data type for MARKDOWN_TEMPLATES --- rdmo/core/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index aaa3fa80e..7af016d94 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -415,3 +415,7 @@ # necessary since django 3.2, explicitly set primary key type to avoid warnings DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + +MARKDOWN_TEMPLATES: dict[str, str] = { + # for example: 'not_empty': 'core/text_blocks/template_for_not_empty.html', +} From f7285aada1d50cc00b14d962162804f6e34e6504 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 20 Feb 2025 14:11:49 +0100 Subject: [PATCH 3/8] Slightly refactor inject_textblocks --- rdmo/core/settings.py | 4 ++++ rdmo/core/utils.py | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 7af016d94..29169f9bb 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -309,6 +309,10 @@ EXPORT_MIN_REQUIRED_VERSION = '2.1.0' +MARKDOWN_TEMPLATES: dict[str, str] = { + # for example: 'not_empty': 'core/text_blocks/template_for_not_empty.html', +} + PROJECT_TABLE_PAGE_SIZE = 20 PROJECT_VISIBILITY = True diff --git a/rdmo/core/utils.py b/rdmo/core/utils.py index 0cee6221f..0f9861347 100644 --- a/rdmo/core/utils.py +++ b/rdmo/core/utils.py @@ -267,18 +267,19 @@ def markdown2html(markdown_string): f' ({hide_string})

', html ) + + # textblocks (e.g. for help texts) can be injected into free text fields as small templates via Markdown html = inject_textblocks(html) + return html def inject_textblocks(html): - '''textblocks (e.g. for help texts) can be injected into free text fields as small templates via Markdown''' - for textblock_id in re.findall(r'{{(.*?)}}', html): # strings between curly brackets - template_path: str = settings.MARKDOWN_TEMPLATES[textblock_id] - html = re.sub( '{{' + textblock_id + '}}', - render_to_string(template_path), - html - ) + # loop over all strings between curly brackets, e.g. {{ test }} + for template_code in re.findall(r'{{(.*?)}}', html): + template_name = settings.MARKDOWN_TEMPLATES.get(template_code.strip()) + if template_name: + html = re.sub('{{' + template_code + '}}', render_to_string(template_name), html) return html From f526b9376c4c5ad2c803cff44eca422cd71fb804 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 20 Feb 2025 14:49:41 +0100 Subject: [PATCH 4/8] Add useLayoutEffect to execute containing scripts to HTML component --- rdmo/core/assets/js/components/Html.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/rdmo/core/assets/js/components/Html.js b/rdmo/core/assets/js/components/Html.js index acd13bbd2..f2400afb2 100644 --- a/rdmo/core/assets/js/components/Html.js +++ b/rdmo/core/assets/js/components/Html.js @@ -1,10 +1,28 @@ -import React from 'react' +import React, { useRef, useLayoutEffect } from 'react' import PropTypes from 'prop-types' import { isEmpty } from 'lodash' const Html = ({ html = '' }) => { + const ref = useRef() + + // if html contains any element with `data-execute="true"`, it will be executed + // using the method in https://macarthur.me/posts/script-tags-in-react/ + useLayoutEffect(() => { + if (ref.current && !isEmpty(ref.current.querySelectorAll('[data-execute="true"]'))) { + // create a range objectm, set it contain the referenced node + // and create a new fragment with the html code within that range + const range = document.createRange() + range.selectNode(ref.current) + const documentFragment = range.createContextualFragment(html) + + // remove the rendered html and inject it again, triggering a re-run of the JS code + ref.current.innerHTML = '' + ref.current.append(documentFragment) + } + }, [html]) + return !isEmpty(html) && ( -
+
) } From 279e92b185adb45d4c9924dbf9967c805f23dfb4 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 25 Feb 2025 12:13:50 +0100 Subject: [PATCH 5/8] Add TEMPLATES_EXECUTE_SCRIPT_TAGS to settings to control JS execution within React --- rdmo/core/assets/js/components/Html.js | 9 ++++++--- rdmo/core/assets/js/utils/meta.js | 2 ++ rdmo/core/settings.py | 9 ++++----- rdmo/projects/templates/projects/project_interview.html | 3 ++- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/rdmo/core/assets/js/components/Html.js b/rdmo/core/assets/js/components/Html.js index f2400afb2..464683114 100644 --- a/rdmo/core/assets/js/components/Html.js +++ b/rdmo/core/assets/js/components/Html.js @@ -2,13 +2,16 @@ import React, { useRef, useLayoutEffect } from 'react' import PropTypes from 'prop-types' import { isEmpty } from 'lodash' +import { executeScriptTags } from 'rdmo/core/assets/js/utils/meta' + + const Html = ({ html = '' }) => { const ref = useRef() - // if html contains any element with `data-execute="true"`, it will be executed - // using the method in https://macarthur.me/posts/script-tags-in-react/ + // if html contains a