Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown templates #1247

Merged
merged 8 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions rdmo/core/assets/js/components/Html.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import React from 'react'
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 a <script> tag, and settings.TEMPLATES_EXECUTE_SCRIPT_TAGS is True,
// it will be executed using the method in https://macarthur.me/posts/script-tags-in-react/
if (executeScriptTags) {
useLayoutEffect(() => {
if (ref.current) {
// 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) && (
<div dangerouslySetInnerHTML={{ __html: html }} />
<div ref={ref} dangerouslySetInnerHTML={{ __html: html }} />
)
}

Expand Down
2 changes: 2 additions & 0 deletions rdmo/core/assets/js/utils/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export const staticUrl = document.querySelector('meta[name="staticurl"]').conten
export const siteId = Number(document.querySelector('meta[name="site_id"]').content)

export const language = document.querySelector('meta[name="language"]').content

export const executeScriptTags = document.querySelector('meta[name="execute_script_tags"]')?.content === 'true'
9 changes: 8 additions & 1 deletion rdmo/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@
'PROJECT_SEND_ISSUE',
'NESTED_PROJECTS',
'PROJECT_VIEWS_SYNC',
'PROJECT_TASKS_SYNC'
'PROJECT_TASKS_SYNC',
'TEMPLATES_EXECUTE_SCRIPT_TAGS'
]

SETTINGS_API = [
Expand Down Expand Up @@ -251,6 +252,8 @@
'projects/project_interview_sidebar.html',
]

TEMPLATES_EXECUTE_SCRIPT_TAGS = False

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = '[email protected]'

Expand Down Expand Up @@ -309,6 +312,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
Expand Down
15 changes: 14 additions & 1 deletion rdmo/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -267,6 +267,19 @@ def markdown2html(markdown_string):
f'<span class="show-less" onclick="showLess(this)"> ({hide_string})</span></p>',
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):
# 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


Expand Down
1 change: 1 addition & 0 deletions rdmo/projects/templates/projects/project_interview.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

{% block head %}
<meta name='project' content="{{ project.id }}" />
<meta name='execute_script_tags' content="{{ settings.TEMPLATES_EXECUTE_SCRIPT_TAGS|lower }}" />
{% endblock %}

{% block css %}
Expand Down
Loading