Reinstate Markdown support for symposion

Allow Markdown within the speaker bio and proposal abstract,
with a restricted set of tags for safety.
Render direct to markdown instead of storing plain and HTML
in the database.
Fix issue in timetable when a day had no slots created.
This commit is contained in:
Joel Addison 2025-01-03 12:00:06 +00:00
parent fb3fcc2faa
commit 447ed3eaf7
12 changed files with 67 additions and 42 deletions

View file

@ -230,6 +230,7 @@ INSTALLED_APPS = [
"django_jsonfield_backport",
"pinax.eventlog",
"timezone_field",
'markdownify.apps.MarkdownifyConfig',
# symposion
"symposion",
@ -347,6 +348,7 @@ LOGGING = {
'level': 'DEBUG'
},
}
FIXTURE_DIRS = [
os.path.join(PROJECT_ROOT, "fixtures"),
]
@ -360,6 +362,30 @@ AUTHENTICATION_BACKENDS = [
LOGIN_URL = '/saml2/login/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
MARKDOWNIFY = {
"default": {
"WHITELIST_TAGS": [
'a',
'abbr',
'acronym',
'b',
'blockquote',
'code',
'em',
'i',
'li',
'ol',
'p',
'pre',
'strong',
'ul',
],
"MARKDOWN_EXTENSIONS": [
"markdown.extensions.fenced_code",
]
}
}
CONFERENCE_ID = 1
PROPOSAL_FORMS = {
"talk": "pinaxcon.proposals.forms.TalkProposalForm",

View file

@ -1,5 +1,6 @@
{% load i18n %}
{% load lca2018_tags %}
{% load markdownify %}
<div class="row">
<div class="col-12">
@ -98,26 +99,24 @@
<div class="row">
<label class="list-label col-md-2">Abstract</label>
<div class="col-md-10">
<div class="abstract monospace-text">{{ proposal.abstract_html|safe }}&nbsp;</div>
<p></p>
<div class="abstract">{{ proposal.abstract | markdownify }}</div>
</div>
</div>
<div class="row">
<label class="list-label col-md-2">Private Abstract</label>
<div class="col-md-10">
<div class="private_abstract monospace-text">{{ proposal.private_abstract_html|safe }}&nbsp;</div>
<p></p>
<div class="private_abstract">{{ proposal.private_abstract | markdownify }}</div>
</div>
</div>
<div class="row">
<label class="list-label col-md-2">Content Warning</label>
<div class="col-md-10">
{% if proposal.content_warning_html %}
<div class="content_warning monospace-text">{{ proposal.content_warning_html|safe }}</div>
{% if proposal.content_warning %}
<div class="content_warning">{{ proposal.content_warning | markdownify }}</div>
{% else %}
<div class="content_warning monospace-text"><b>No Content Warning Provided</b></div>
<div class="content_warning"><p><b>None Provided</b></p></div>
{% endif %}
</div>
</div>
@ -137,7 +136,7 @@
<label class="list-label col-md-2">Project URL</label>
<div class="col-md-10">
{% if proposal.project_url %}
<p><a href="{{ proposal.project_url|safe }}">{{ proposal.project_url|safe }}</a>&nbsp;</p>
<p><a href="{{ proposal.project_url|safe }}">{{ proposal.project_url|safe }}</a></p>
{% else %}
<p><b>None Provided</b></p>
{% endif %}
@ -148,7 +147,7 @@
<label class="list-label col-md-2">Video URL</label>
<div class="col-md-10">
{% if proposal.video_url %}
<p><a href="{{ proposal.video_url|safe }}">{{ proposal.video_url|safe }}</a>&nbsp;</p>
<p><a href="{{ proposal.video_url|safe }}">{{ proposal.video_url|safe }}</a></p>
{% else %}
<p><b>None Provided</b></p>
{% endif %}
@ -158,10 +157,10 @@
<div class="row">
<label class="list-label col-md-2">Special Requirements</label>
<div class="col-md-10">
{% if proposal.technical_requirements_html %}
<div class="special_requirements monospace-text">{{ proposal.technical_requirements_html|safe }}</div>
{% if proposal.technical_requirements %}
<div class="special_requirements">{{ proposal.technical_requirements | markdownify }}</div>
{% else %}
<div class="special_requirements monospace-text"><b>No Special Talk Requirements Requested</b></div>
<div class="special_requirements"><p><b>None Provided</b></p></div>
{% endif %}
</div>
</div>
@ -169,21 +168,21 @@
<div class="row">
<label class="list-label col-md-2">Requires approval from employer?</label>
<div class="col-md-10">
<p>{{ proposal.require_approval }}&nbsp;</p>
<p>{{ proposal.require_approval }}</p>
</div>
</div>
<div class="row">
<label class="list-label col-md-2">Recording Release</label>
<div class="col-md-10">
<p>{{ proposal.recording_release }}&nbsp;</p>
<p>{{ proposal.recording_release }}</p>
</div>
</div>
<div class="row">
<label class="list-label col-md-2">Materials Release</label>
<div class="col-md-10">
<p>{{ proposal.materials_release }}&nbsp;</p>
<p>{{ proposal.materials_release }}</p>
</div>
</div>
@ -210,16 +209,16 @@
</div>
<div class="row">
<label class="list-label col-md-2">Biography</label>
<div class="col-md-10 monospace-text">{{ speaker.biography_html|safe }}&nbsp;</div>
<div class="col-md-10">{{ speaker.biography | markdownify }}</div>
</div>
<div class="row">
<label class="list-label col-md-2">Experience</label>
<div class="col-md-10 monospace-text">{{ speaker.experience_html|safe }}&nbsp;</div>
<div class="col-md-10">{{ speaker.experience | markdownify }}</div>
</div>
{% if speaker.accessibility_html %}
{% if speaker.accessibility %}
<div class="row">
<label class="list-label col-md-2">Accessibility Requirements</label>
<div class="col-md-10 monospace-text">{{ speaker.accessibility_html|safe }}&nbsp;</div>
<div class="col-md-10">{{ speaker.accessibility | markdownify }}</div>
</div>
{% endif %}
</div>

View file

@ -1,6 +1,6 @@
{% extends "site_base.html" %}
{% load static %}
{% load markdownify %}
{% load bootstrap %}
{% block body_class %}review{% endblock %}
@ -28,7 +28,7 @@
<h3>Abstract</h3>
<div class="abstract">
{{ proposal.abstract_html|safe }}
{{ proposal.abstract | markdownify }}
</div>
<p><b>Audience level</b>: {{ proposal.get_audience_level_display }}</p>

View file

@ -5,6 +5,7 @@
{% load sitetree %}
{% load static %}
{% load thumbnail %}
{% load markdownify %}
{% block head_title %}Presentation: {{ presentation.title }}{% endblock %}
{% block page_title %}{{ presentation.title }}{% endblock %}
@ -42,7 +43,7 @@
<a href="{{ speaker.homepage }}">{{ speaker.homepage }}</a>
{% endif %}
</p>
<div class="bio">{{ speaker.biography_html|safe}}</div>
<div class="bio">{{ speaker.biography | markdownify }}</div>
</p>
</li>
{% endfor %}
@ -51,9 +52,7 @@
<div class="col-md-9 presentation-abstract">
<h2 class="mt-4">Abstract</h4>
{% autoescape off %}
<div class="abstract pb-4"><p>{{ presentation.abstract_html|safe|clean_text|urlize }}</p></div>
{% endautoescape %}
<div class="abstract pb-4">{{ presentation.abstract | markdownify }}</div>
</div>
</div>
{% endblock %}

View file

@ -4,6 +4,7 @@
{% load lca2018_tags %}
{% load lca2019_tags %}
{% load thumbnail %}
{% load markdownify %}
{% block head_title %}Speaker - {{ speaker.name }}{% endblock %}
{% block page_title %}Speaker - {{ speaker.name }}{% endblock %}
@ -26,7 +27,7 @@
<h3>Biography</h3>
<div class="bio">{{ speaker.biography_html|safe }}</div>
<div class="bio">{{ speaker.biography | markdownify }}</div>
<h3 class="my-4">Presentations</h3>

View file

@ -30,9 +30,11 @@ django-sitetree==1.16.0
django-taggit==1.3.0
django-timezone-field==4.1.2
easy-thumbnails==2.8.5
bleach==3.2.1
bleach==6.1.0
pytz>=2020.1
django-ical==1.7.1
django-markdownify==0.9.5
markdown==3.7
# Registrasion reqs
django-nested-admin==3.3.2

View file

@ -111,7 +111,7 @@ h3, .h3 {
label.label-required:after { content: ' *'; }
.abstract, .bio, .monospace-text {
.monospace-text {
font-family: Hack, monospace;
white-space: pre-wrap;
}

View file

@ -1,5 +1,3 @@
TEXT_FIELD_MONOSPACE_NOTE=(
"This field is rendered with the monospace font "
"<a tabindex=\"-1\" href=\"https://sourcefoundry.org/hack/\">Hack</a> with "
"whitespace preserved")
TEXT_FIELD_FORMAT_NOTE=(
"This field supports <a href='https://daringfireball.net/projects/markdown/syntax' target='_blank'>Markdown</a>, " +
"with limitations on allowed elements.")

View file

@ -100,7 +100,7 @@ class ProposalBase(models.Model):
abstract = models.TextField(
_("Abstract"),
help_text=_("This will appear in the conference programme. Up to about "
"500 words. " + constants.TEXT_FIELD_MONOSPACE_NOTE)
"500 words. " + constants.TEXT_FIELD_FORMAT_NOTE)
)
abstract_html = models.TextField(blank=True)
@ -109,7 +109,7 @@ class ProposalBase(models.Model):
help_text=_("This will only be shown to organisers and reviewers. You "
"should provide any details about your proposal that you "
"don't want to be public here. " +
constants.TEXT_FIELD_MONOSPACE_NOTE)
constants.TEXT_FIELD_FORMAT_NOTE)
)
private_abstract_html = models.TextField(blank=True)
@ -121,7 +121,7 @@ class ProposalBase(models.Model):
"please list your technical requirements here. Such as: a "
"static IP address, A/V equipment or will be demonstrating "
"security-related techniques on the conference network. " +
constants.TEXT_FIELD_MONOSPACE_NOTE)
constants.TEXT_FIELD_FORMAT_NOTE)
)
technical_requirements_html = models.TextField(blank=True)

View file

@ -34,9 +34,10 @@ class TimeTable(object):
return self._rooms
def __iter__(self):
slots = self.slots()
if not self._times:
return
row = []
slots = self.slots()
total_room_count = self.rooms().count()
for time, next_time in pairwise(self._times):
row = {"time": time, "end": next_time, "slots": []}

View file

@ -41,7 +41,7 @@ class Speaker(models.Model):
help_text=_("This will appear on the conference website and in the "
"programme. Please write in the third person, eg 'Alice "
"is a Moblin hacker...', 150-200 words. " +
constants.TEXT_FIELD_MONOSPACE_NOTE),
constants.TEXT_FIELD_FORMAT_NOTE),
verbose_name=_("Biography"),
)
biography_html = models.TextField(blank=True)
@ -51,7 +51,7 @@ class Speaker(models.Model):
"we'd like to know. Anything you put here will only be "
"seen by the organisers and reviewers; use it to convince "
"them why they should accept your proposal. " +
constants.TEXT_FIELD_MONOSPACE_NOTE),
constants.TEXT_FIELD_FORMAT_NOTE),
verbose_name=_("Speaking experience"),
)
experience_html = models.TextField(blank=True)

View file

@ -1,7 +1,6 @@
import bleach
tags = bleach.sanitizer.ALLOWED_TAGS[:]
tags.extend(['p', 'pre'])
tags = bleach.sanitizer.ALLOWED_TAGS | {'p', 'pre'}
def parse(text):