diff --git a/scipost_django/forums/forms.py b/scipost_django/forums/forms.py index 5ee198234354b7a9a61ae5be624fde5e836d03db..b732ca425c36dbfc32533d056134511de244b417 100644 --- a/scipost_django/forums/forms.py +++ b/scipost_django/forums/forms.py @@ -61,6 +61,8 @@ class MeetingForm(ForumForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.fields["preamble"].widget = TextareaWithPreview() + self.fields["minutes"].widget = TextareaWithPreview() if self.instance: self.fields["parent"].initial = self.instance.parent diff --git a/scipost_django/markup/constants.py b/scipost_django/markup/constants.py index 4be31e14be8369eacc24bda12b5c1ac5c294cb67..ad4fa9761445c8e57deea447ef731acc937bf791 100644 --- a/scipost_django/markup/constants.py +++ b/scipost_django/markup/constants.py @@ -2,6 +2,13 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" +recognized_markup_languages = { + "plain": "plain", + "md": "Markdown", + "reST": "reStructuredText", +} + + # Dictionary for regex expressions to recognize reStructuredText headers. # This follows the Python conventions: order is #, *, =, -, ", ^ and # for the first two levels (# and *), over- and underlining are necessary, while diff --git a/scipost_django/markup/templates/markup/forms/widgets/_hx_textarea_widget_value_processed.html b/scipost_django/markup/templates/markup/forms/widgets/_hx_textarea_widget_value_processed.html index 4f55342302bc4a619c3486d7ccd9c9cb676fe9c6..7c3d2cc4304d307761770dc96eae415d5d754abc 100644 --- a/scipost_django/markup/templates/markup/forms/widgets/_hx_textarea_widget_value_processed.html +++ b/scipost_django/markup/templates/markup/forms/widgets/_hx_textarea_widget_value_processed.html @@ -1,12 +1,12 @@ <div style="border: 2px solid orange; padding: 0.5rem;"> <span style="padding: 0.5rem; background-color: #f3f6ff; color: #004057;"> - <em><strong>Preview</strong> (detected language: <strong>{{ markup.language }}</strong></em>) + <em><strong>Preview</strong> (markup language: <strong>{{ markup.language }}</strong></em>) </span> {% if markup.errors %} - <span style="text-color: red;">Errors: {{ markup.errors }}</span> +  <span style="color: red;">Errors: {{ markup.errors }}</span> {% endif %} {% if markup.warnings %} - <span style="text-color: orange;">Errors: {{ markup.warnings }}</span> +  <span style="color: orange;">Warnings: {{ markup.warnings }}</span> {% endif %} <div style="margin: 1rem;">{{ markup.processed|safe }}</div> </div> diff --git a/scipost_django/markup/utils.py b/scipost_django/markup/utils.py index d40017faae6d14ea02a423f1fa4f60db94343a05..4db7bb0088b6ce39a3350448320796d4de86e1d9 100644 --- a/scipost_django/markup/utils.py +++ b/scipost_django/markup/utils.py @@ -13,6 +13,7 @@ from django.utils.encoding import force_text from django.utils.safestring import mark_safe from .constants import ( + recognized_markup_languages, ReST_HEADER_REGEX_DICT, ReST_ROLES, ReST_DIRECTIVES, @@ -375,7 +376,7 @@ def apply_markdown_preserving_displayed_maths(text): ) -def process_markup(text, language_forced=None, include_errors=False): +def process_markup(text_given, language_forced=None, include_errors=False): """ Process a text in a markup language into HTML. @@ -385,16 +386,40 @@ def process_markup(text, language_forced=None, include_errors=False): Parameters: * `language_forced=None`: override language auto-detection """ - markup_detector = detect_markup_language(text) markup = {"language": "plain", "errors": None, "warnings": None, "processed": ""} - if language_forced and language_forced != markup_detector["language"]: + if not text_given: + return markup + + # See if text_given contains a language coerce + coerced = False + if text_given.partition("\n")[0].startswith("#coerce:"): + language_requested_code = text_given.partition("\n")[0].partition("#coerce:")[2] + coerced = True + text = text_given.partition("\n")[2] + if language_requested_code not in recognized_markup_languages: + markup["errors"] = ( + f"Unknown language ({language_requested_code}) requested for coerce.\n" + "Your options are: " + f"{recognized_markup_languages}\n" + "To coerce, at the start of the first line, write #coerce:[code] " + "with the language code, e.g. #coerce:reST" + ) + return markup + language_requested = recognized_markup_languages[language_requested_code] + markup["warnings"] = f"Coercing language: {language_requested}" + else: + text = text_given + + markup_detector = detect_markup_language(text) + + if coerced and language_requested != markup_detector["language"]: markup["warnings"] = ( - "Warning: markup language was forced to %s, while the detected one was %s." - ) % (language_forced, markup_detector["language"]) + "markup language was coerced to %s, while the detected one was %s." + ) % (language_requested, markup_detector["language"]) - language = language_forced if language_forced else markup_detector["language"] + language = language_requested if coerced else markup_detector["language"] markup["language"] = language markup["errors"] = markup_detector["errors"] @@ -409,7 +434,8 @@ def process_markup(text, language_forced=None, include_errors=False): "%s</span><br><br>" ) % linebreaksbr(markup["errors"]) markup["processed"] = mark_safe(error_msg + linebreaksbr(text)) - return markup + if not coerced: + return markup if language == "reStructuredText": warnStream = StringIO() diff --git a/scipost_django/profiles/views.py b/scipost_django/profiles/views.py index b82026056913a7de13e84f4c89161b6060874118..113a72382d0caff1d76b33d96fee4ff5c547d403 100644 --- a/scipost_django/profiles/views.py +++ b/scipost_django/profiles/views.py @@ -358,7 +358,7 @@ def _hx_profile_dynsel_list(request): @permission_required("scipost.can_create_profiles") def _hx_profile_specialties(request, profile_id): """ -Returns a snippet with current and possible specialties, with one-click actions. + Returns a snippet with current and possible specialties, with one-click actions. """ profile = get_object_or_404(Profile, pk=profile_id) if request.method == "POST":