Add clean method to QuestionForm to unwrap single <p> tags from TinyMCE HTML fields

This commit is contained in:
Ross
2025-11-10 10:00:42 +00:00
parent e078b5a98f
commit 53ff56edc0
+40
View File
@@ -22,6 +22,7 @@ from django.contrib.admin.widgets import FilteredSelectMultiple
from django.forms.widgets import RadioSelect, TextInput, Textarea from django.forms.widgets import RadioSelect, TextInput, Textarea
from tinymce.widgets import TinyMCE from tinymce.widgets import TinyMCE
import re
from dal import autocomplete from dal import autocomplete
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
@@ -160,6 +161,45 @@ class QuestionForm(ModelForm):
return instance return instance
def clean(self):
"""Strip a single wrapping <p>...</p> from TinyMCE-provided HTML fields.
This handles the common TinyMCE behaviour where the editor wraps
inline content in a root <p>. We only unwrap when the entire value
is a single <p> element (so multi-paragraph content is preserved).
"""
cleaned = super(QuestionForm, self).clean()
def _unwrap_single_p(html: str) -> str:
if not html or not isinstance(html, str):
return html
# Match a single <p ...>...</p> that spans the whole string
m = re.fullmatch(r"\s*<p\b[^>]*>(.*)</p>\s*", html, flags=re.DOTALL | re.IGNORECASE)
if m:
return m.group(1)
return html
fields_to_unwrap = [
"stem",
"feedback",
"a_answer",
"a_feedback",
"b_answer",
"b_feedback",
"c_answer",
"c_feedback",
"d_answer",
"d_feedback",
"e_answer",
"e_feedback",
]
for fname in fields_to_unwrap:
if fname in cleaned:
cleaned[fname] = _unwrap_single_p(cleaned.get(fname))
return cleaned
class Meta: class Meta:
model = Question model = Question