diff --git a/django/django_application/settings/base.py b/django/django_application/settings/base.py index 918334fa..20da722c 100644 --- a/django/django_application/settings/base.py +++ b/django/django_application/settings/base.py @@ -49,6 +49,7 @@ 'django_filters', 'parse_m2.apps.ParseM2Config', 'evaluate_m2.apps.EvaluateM2Config', + 'django_prose_editor', ] MIDDLEWARE = [ diff --git a/django/evaluate_m2/admin.py b/django/evaluate_m2/admin.py index 0e383931..205b2f12 100644 --- a/django/evaluate_m2/admin.py +++ b/django/evaluate_m2/admin.py @@ -11,7 +11,6 @@ class EvaluatorMetadataAdmin(admin.ModelAdmin): readonly_fields = [ 'id', 'category', 'fields_used', 'fields_display', - 'long_description', ] fields = [ 'id', 'category', 'fields_used', 'fields_display', @@ -20,8 +19,7 @@ class EvaluatorMetadataAdmin(admin.ModelAdmin): 'rationale', 'alternate_explanation', ] list_display = [ - 'id', 'category', 'description', 'long_description', - 'fields_used', 'fields_display', + 'id', 'category', 'description', 'fields_used', 'fields_display', ] def has_add_permission(self, request, obj=None): diff --git a/django/evaluate_m2/models.py b/django/evaluate_m2/models.py index 9822f67a..77afe06f 100644 --- a/django/evaluate_m2/models.py +++ b/django/evaluate_m2/models.py @@ -3,6 +3,8 @@ from django.db import models from django.db.models import JSONField +from django_prose_editor.fields import ProseEditorField + from parse_m2.models import AccountActivity, Metro2Event @@ -15,7 +17,21 @@ class Meta: id = models.CharField(max_length=200, primary_key=True) category = models.CharField(max_length=200, blank=True) description = models.TextField(blank=True) # short plain language description - long_description = models.TextField(blank=True) + long_description = ProseEditorField( + extensions={ + "Bold": True, + "Italic": True, + "BulletList": True, + "OrderedList": True, + "ListItem": True, + "Link": True, + "Heading": { + "levels": [4] + }, + }, + sanitize=True, # Built-in server side sanitization + blank=True, + ) fields_used = JSONField(encoder=DjangoJSONEncoder, null=True) fields_display = JSONField(encoder=DjangoJSONEncoder, null=True) crrg_reference = models.TextField(blank=True) diff --git a/django/requirements.txt b/django/requirements.txt index 6e0a081e..ec974422 100644 --- a/django/requirements.txt +++ b/django/requirements.txt @@ -12,11 +12,12 @@ gunicorn==25.3.0 whitenoise==6.12.0 smart-open[s3]==7.6.0 yappi==1.7.6 +django-prose-editor[sanitize] # Not required directly by Metro2; pinned to avoid CVEs certifi==2026.4.22 cryptography==48.0.0 idna==3.15 -PyJWT==2.12.1 +PyJWT==2.13.0 requests==2.34.2 urllib3==2.7.0 diff --git a/front-end/src/pages/Evaluator/overview/EvaluatorOverview.tsx b/front-end/src/pages/Evaluator/overview/EvaluatorOverview.tsx index 01e73fed..d49fb2f4 100644 --- a/front-end/src/pages/Evaluator/overview/EvaluatorOverview.tsx +++ b/front-end/src/pages/Evaluator/overview/EvaluatorOverview.tsx @@ -1,9 +1,9 @@ +import DOMPurify from 'dompurify' import Accordion from '@src/components/Accordion/Accordion' import type EvaluatorMetadata from '@src/types/EvaluatorMetadata' import type Event from '@src/types/Event' import type User from '@src/types/User' import type { ReactElement } from 'react' -import EvaluatorLongDescription from './components/LongDescription' import EvaluatorMetadataSection from './components/Metadata' import EvaluatorSummary from './components/Summary' @@ -33,7 +33,8 @@ export default function EvaluatorOverview({
{line}
- ) - )} -