diff --git a/opentech/apply/activity/forms.py b/opentech/apply/activity/forms.py index 904fde0e68bc065009390ddc754b3188088eb967..7207068278ddd6893a3418d54dc87475f5318775 100644 --- a/opentech/apply/activity/forms.py +++ b/opentech/apply/activity/forms.py @@ -2,6 +2,8 @@ from django import forms from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe +from pagedown.widgets import PagedownWidget + from .models import Activity, VISIBILILTY_HELP_TEXT, VISIBILITY @@ -15,6 +17,7 @@ class CommentForm(forms.ModelForm): } widgets = { 'visibility': forms.RadioSelect(), + 'message': PagedownWidget(), } def __init__(self, *args, user=None, **kwargs): diff --git a/opentech/apply/activity/templates/activity/include/listing_base.html b/opentech/apply/activity/templates/activity/include/listing_base.html index 15ff754d5f125cb2f217ed09ec2dca03601af066..e76d3ec0297c39e7bd63338ad06a7d5e1bea5d19 100644 --- a/opentech/apply/activity/templates/activity/include/listing_base.html +++ b/opentech/apply/activity/templates/activity/include/listing_base.html @@ -1,4 +1,4 @@ -{% load activity_tags %} +{% load activity_tags bleach_tags markdown_tags %} <div class="feed__item feed__item--{{ activity.type }}"> <div class="feed__pre-content"> <p class="feed__label feed__label--{{ activity.type }}">{{ activity.type|capfirst }}</p> @@ -19,13 +19,13 @@ updated <a href="{{ activity.submission.get_absolute_url }}">{{ activity.submission.title }}</a> {% endif %} - {{ activity.message|linebreaksbr }} + {{ activity.message|markdown|bleach }} {% if not submission_title and activity|user_can_see_related:request.user %} {% with url=activity.related_object.get_absolute_url %} {% if url %} <a href="{{ url }}" class="feed__related-item"> - <svg><use xlink:href="#arrow-head-pixels--solid"></use></svg> + {{ activity.related_object }} <svg><use xlink:href="#arrow-head-pixels--solid"></use></svg> </a> {% endif %} {% endwith %} diff --git a/opentech/apply/funds/migrations/0047_add_markdown.py b/opentech/apply/funds/migrations/0047_add_markdown.py new file mode 100644 index 0000000000000000000000000000000000000000..b761c3f1157cf925f3a7b3a45904ab371f2f63d1 --- /dev/null +++ b/opentech/apply/funds/migrations/0047_add_markdown.py @@ -0,0 +1,27 @@ +# Generated by Django 2.0.9 on 2018-11-14 12:29 + +from django.db import migrations +import opentech.apply.categories.blocks +import wagtail.core.blocks +import wagtail.core.blocks.static_block +import wagtail.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('funds', '0046_rename_fields'), + ] + + operations = [ + migrations.AlterField( + model_name='applicationform', + name='form_fields', + field=wagtail.core.fields.StreamField([('text_markup', wagtail.core.blocks.RichTextBlock(group='Custom', label='Section text/header')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('number', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('radios', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('checkboxes', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('checkboxes', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Checkbox')))], group='Fields')), ('date', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.DateBlock(required=False))], group='Fields')), ('time', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TimeBlock(required=False))], group='Fields')), ('datetime', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.DateTimeBlock(required=False))], group='Fields')), ('image', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('file', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('multi_file', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('category', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(help_text='Leave blank to use the default Category label', label='Label', required=False)), ('help_text', wagtail.core.blocks.TextBlock(label='Leave blank to use the default Category help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('category', opentech.apply.categories.blocks.ModelChooserBlock('categories.Category')), ('multi', wagtail.core.blocks.BooleanBlock(label='Multi select', required=False))], group='Custom')), ('title', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('email', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('full_name', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('duration', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('value', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group='Custom')), ('address', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group='Custom'))]), + ), + migrations.AlterField( + model_name='applicationsubmission', + name='form_fields', + field=wagtail.core.fields.StreamField([('text_markup', wagtail.core.blocks.RichTextBlock(group='Custom', label='Section text/header')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('number', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('radios', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('checkboxes', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('checkboxes', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Checkbox')))], group='Fields')), ('date', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.DateBlock(required=False))], group='Fields')), ('time', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TimeBlock(required=False))], group='Fields')), ('datetime', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.DateTimeBlock(required=False))], group='Fields')), ('image', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('file', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('multi_file', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('category', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(help_text='Leave blank to use the default Category label', label='Label', required=False)), ('help_text', wagtail.core.blocks.TextBlock(label='Leave blank to use the default Category help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('category', opentech.apply.categories.blocks.ModelChooserBlock('categories.Category')), ('multi', wagtail.core.blocks.BooleanBlock(label='Multi select', required=False))], group='Custom')), ('title', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('email', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('full_name', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('duration', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('value', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group='Custom')), ('address', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group='Custom'))]), + ), + ] diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_admin_detail.html b/opentech/apply/funds/templates/funds/applicationsubmission_admin_detail.html index 5cc8cc9b0e583c3e7c5bbf49c32130517068c74f..e0a41610a6575d80050c321abbd4416b41a59bb6 100644 --- a/opentech/apply/funds/templates/funds/applicationsubmission_admin_detail.html +++ b/opentech/apply/funds/templates/funds/applicationsubmission_admin_detail.html @@ -38,6 +38,7 @@ {% block extra_js %} {{ reviewer_form.media.js }} + {{ comment_form.media.js }} <script src="//cdnjs.cloudflare.com/ajax/libs/fancybox/3.4.1/jquery.fancybox.min.js"></script> <script src="{% static 'js/apply/fancybox-global.js' %}"></script> <script src="{% static 'js/apply/tabs.js' %}"></script> diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html index ab0d66ee88020181b763d932bef573136a516b00..43041ef7aa72a58aa0aa4a55cc8670795cea2821 100644 --- a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html @@ -130,6 +130,7 @@ {% endblock %} {% block extra_js %} + {{ comment_form.media.js }} <script src="{% static 'js/apply/tabs.js' %}"></script> <script src="{% static 'js/apply/submission-text-cleanup.js' %}"></script> {% endblock %} diff --git a/opentech/apply/funds/templatetags/markdown_tags.py b/opentech/apply/funds/templatetags/markdown_tags.py new file mode 100644 index 0000000000000000000000000000000000000000..9ba5ff19dc0828092dcfab40fdcd9ab6876f7b58 --- /dev/null +++ b/opentech/apply/funds/templatetags/markdown_tags.py @@ -0,0 +1,11 @@ +import mistune + +from django import template + +register = template.Library() + + +@register.filter +def markdown(value): + markdown = mistune.Markdown() + return markdown(value) diff --git a/opentech/apply/review/migrations/0014_add_markdown.py b/opentech/apply/review/migrations/0014_add_markdown.py new file mode 100644 index 0000000000000000000000000000000000000000..b17c3c6cfb55f2f4ad058e53cfe342deaf286392 --- /dev/null +++ b/opentech/apply/review/migrations/0014_add_markdown.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.9 on 2018-11-14 12:29 + +from django.db import migrations +import wagtail.core.blocks +import wagtail.core.blocks.static_block +import wagtail.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('review', '0013_rename_fields'), + ] + + operations = [ + migrations.AlterField( + model_name='review', + name='form_fields', + field=wagtail.core.fields.StreamField([('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('text_markup', wagtail.core.blocks.RichTextBlock(group='Fields', label='Paragraph')), ('score', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('recommendation', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('comments', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required'))]), + ), + migrations.AlterField( + model_name='reviewform', + name='form_fields', + field=wagtail.core.fields.StreamField([('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False))], group='Fields')), ('text_markup', wagtail.core.blocks.RichTextBlock(group='Fields', label='Paragraph')), ('score', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('recommendation', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('comments', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required'))]), + ), + ] diff --git a/opentech/apply/stream_forms/templates/stream_forms/render_markdown_field.html b/opentech/apply/stream_forms/templates/stream_forms/render_markdown_field.html new file mode 100644 index 0000000000000000000000000000000000000000..f9b58617702719f094a6549eb5aa0aaf303b021c --- /dev/null +++ b/opentech/apply/stream_forms/templates/stream_forms/render_markdown_field.html @@ -0,0 +1,9 @@ +{% extends "stream_forms/render_field.html" %} +{% load bleach_tags markdown_tags %} +{% block data_display %} + {% if data %} + {{ data|markdown|bleach }} + {% else %} + {{ block.super }} + {% endif %} +{% endblock %} diff --git a/opentech/apply/utils/blocks.py b/opentech/apply/utils/blocks.py index 6ce1eeb639c0eed13132e5a474d3362e57678975..a4f9efb4d179d27568c7467342c24eb7d0b43025 100644 --- a/opentech/apply/utils/blocks.py +++ b/opentech/apply/utils/blocks.py @@ -1,6 +1,8 @@ from collections import Counter +from pagedown.widgets import PagedownWidget import bleach + from django.core.exceptions import ValidationError from django.forms.utils import ErrorList from django.utils.translation import ugettext_lazy as _ @@ -42,8 +44,25 @@ class RichTextFieldBlock(TextFieldBlock): return '<p>No response</p>' +class MarkdownTextFieldBlock(TextFieldBlock): + widget = PagedownWidget() + template = '' + + class Meta: + label = _('Markdown text field') + icon = 'form' + template = 'stream_forms/render_markdown_field.html' + + def get_searchable_content(self, value, data): + return bleach.clean(data or '', tags=[], strip=True) + + def no_response(self): + return '<p>No response</p>' + + class CustomFormFieldsBlock(StreamBlock): rich_text = RichTextFieldBlock(group=_('Fields')) + markdown_text = MarkdownTextFieldBlock(group=_('Fields')) required_blocks = [] single_blocks = [] diff --git a/opentech/settings/base.py b/opentech/settings/base.py index dd2c51d8b7c60631ea719d1334fedf2d173cfbf4..ba0565c37b55af5775515ffa66fca01cd55d4311 100644 --- a/opentech/settings/base.py +++ b/opentech/settings/base.py @@ -118,6 +118,7 @@ INSTALLED_APPS = [ 'hijack', 'compat', + 'pagedown', 'django.contrib.admin', 'django.contrib.auth', diff --git a/opentech/static_src/src/images/editor-buttons.png b/opentech/static_src/src/images/editor-buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..50b37090363e6757e7bd0ba75cd1e0dfaabd13d2 Binary files /dev/null and b/opentech/static_src/src/images/editor-buttons.png differ diff --git a/opentech/static_src/src/sass/apply/components/_editor.scss b/opentech/static_src/src/sass/apply/components/_editor.scss new file mode 100644 index 0000000000000000000000000000000000000000..bca2454437a34cd8327be78c34a2b27685a2df48 --- /dev/null +++ b/opentech/static_src/src/sass/apply/components/_editor.scss @@ -0,0 +1,87 @@ +.wmd-panel { +} + +.wmd-input { +} + +.wmd-preview { + background-color: $color--sky-blue; + padding: 0 10px; + width: 100%; + overflow: hidden; /* prevent collapsing margins */ + + ul { + padding-left: 20px; + list-style: outside disc; + } + + ol { + list-style: inside decimal; + } + + @include media-query(tablet-portrait) { + max-width: 70%; + } +} + +.wmd-button-row { + position: relative; + background-color: $color--white; + height: 35px; + padding-left: 5px; + padding-right: 5px; + padding-bottom: 5px; + padding-top: 10px; +} + +.wmd-spacer { + position: absolute; + display: inline-block; + background-color: $color--mid-grey; + width: 1px; + height: 20px; + margin-left: 14px; + list-style: none; +} + +.wmd-button { + position: absolute; + display: inline-block; + width: 20px; + height: 20px; + padding-left: 2px; + padding-right: 3px; + list-style: none; + cursor: pointer; + + & > span { + display: inline-block; + background-image: url('./../../images/editor-buttons.png'); + background-repeat: no-repeat; + background-position: 0 0; + width: 20px; + height: 20px; + } +} + +.wmd-spacer1 { + left: 50px; +} + +.wmd-spacer2 { + left: 175px; +} + +.wmd-spacer3 { + left: 300px; +} + +.wmd-prompt-background { + background-color: $color--black; +} + +.wmd-prompt-dialog { + background-color: $color--light-grey; + padding: .5em; + border: 4px solid $color--primary; +} diff --git a/opentech/static_src/src/sass/apply/components/_feed.scss b/opentech/static_src/src/sass/apply/components/_feed.scss index a3862c226e0306734e1a663e54e95f412e6e5e6e..2ab489a4bf4b1029ce4e6df43d4de6063046599f 100644 --- a/opentech/static_src/src/sass/apply/components/_feed.scss +++ b/opentech/static_src/src/sass/apply/components/_feed.scss @@ -15,6 +15,15 @@ max-height: 300px; overflow: hidden; } + + ul { + padding-left: 20px; + list-style: outside disc; + } + + ol { + list-style: inside decimal; + } } &__label { diff --git a/opentech/static_src/src/sass/apply/main.scss b/opentech/static_src/src/sass/apply/main.scss index 014c32220309acca56ec5a01cef8566e06d075ad..745f50807ad3b43d597b5a7f82021f9bec2de050 100644 --- a/opentech/static_src/src/sass/apply/main.scss +++ b/opentech/static_src/src/sass/apply/main.scss @@ -13,6 +13,7 @@ @import 'components/activity-feed'; @import 'components/comment'; @import 'components/button'; +@import 'components/editor'; @import 'components/feed'; @import 'components/filters'; @import 'components/grid'; diff --git a/opentech/static_src/src/sass/public/components/_editor.scss b/opentech/static_src/src/sass/public/components/_editor.scss new file mode 100644 index 0000000000000000000000000000000000000000..136e5f10e4d50c24c18176020dd14e06935abb34 --- /dev/null +++ b/opentech/static_src/src/sass/public/components/_editor.scss @@ -0,0 +1,83 @@ +.wmd-panel { +} + +.wmd-input { +} + +.wmd-preview { + background-color: $color--sky-blue; + padding: 0 10px; + width: 100%; + overflow: hidden; /* prevent collapsing margins */ + + ul { + padding-left: 20px; + list-style: outside disc; + } + + ol { + list-style: inside decimal; + } +} + +.wmd-button-row { + position: relative; + background-color: $color--white; + height: 35px; + padding-left: 5px; + padding-right: 5px; + padding-bottom: 5px; + padding-top: 10px; +} + +.wmd-spacer { + position: absolute; + display: inline-block; + background-color: $color--mid-grey; + width: 1px; + height: 20px; + margin-left: 14px; + list-style: none; +} + +.wmd-button { + position: absolute; + display: inline-block; + width: 20px; + height: 20px; + padding-left: 2px; + padding-right: 3px; + list-style: none; + cursor: pointer; + + & > span { + display: inline-block; + background-image: url('./../../images/editor-buttons.png'); + background-repeat: no-repeat; + background-position: 0 0; + width: 20px; + height: 20px; + } +} + +.wmd-spacer1 { + left: 50px; +} + +.wmd-spacer2 { + left: 175px; +} + +.wmd-spacer3 { + left: 300px; +} + +.wmd-prompt-background { + background-color: $color--black; +} + +.wmd-prompt-dialog { + background-color: $color--light-grey; + padding: .5em; + border: 4px solid $color--primary; +} diff --git a/opentech/static_src/src/sass/public/main.scss b/opentech/static_src/src/sass/public/main.scss index 2a52fca42fd15d4f6053c39c2bf0624dba6f8c29..6cb1a024548c35c364e249c8c52fac82483efa19 100644 --- a/opentech/static_src/src/sass/public/main.scss +++ b/opentech/static_src/src/sass/public/main.scss @@ -11,6 +11,7 @@ @import 'components/apply-bar'; @import 'components/button'; @import 'components/blockquote'; +@import 'components/editor'; @import 'components/card'; @import 'components/call-to-action'; @import 'components/form'; diff --git a/public/pagedown/demo/browser/demo.css b/public/pagedown/demo/browser/demo.css new file mode 100644 index 0000000000000000000000000000000000000000..7086db615bad19150540a53c1e3e684fdf8c3fbd --- /dev/null +++ b/public/pagedown/demo/browser/demo.css @@ -0,0 +1 @@ +/* Override pagedown css */ diff --git a/requirements.txt b/requirements.txt index 470eff2f7ef90b5b758071ed42be1c94679f85da..662083dc29a3e7358dd740608165d11bb3fca26e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,3 +42,5 @@ django-storages==1.6.6 boto3==1.7.75 mailchimp3==3.0.4 scout-apm==1.3.4 +mistune==0.8.4 +django-pagedown==1.0.6