diff --git a/home/blocks.py b/home/blocks.py
index f992ccb43..863c870d4 100644
--- a/home/blocks.py
+++ b/home/blocks.py
@@ -1,5 +1,3 @@
-from django.conf import settings
-from django.forms.utils import flatatt
from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
@@ -23,7 +21,8 @@ def get_context(self, value, parent_context=None):
return context
class Meta:
- template = 'blocks/media.html'
+ icon = "media"
+ template = "blocks/media.html"
class SocialMediaLinkBlock(blocks.StructBlock):
@@ -52,6 +51,9 @@ class Meta:
class EmbeddedQuestionnaireBlock(blocks.StructBlock):
direct_display = blocks.BooleanBlock(required=False)
+ class Meta:
+ icon = "form"
+
class EmbeddedPollBlock(EmbeddedQuestionnaireBlock):
poll = EmbeddedQuestionnaireChooserBlock(target_model='questionnaires.Poll')
@@ -119,6 +121,7 @@ def get_context(self, value, parent_context=None):
return context
class Meta:
+ icon = "link"
template = 'blocks/page_button.html'
@@ -137,7 +140,8 @@ def get_context(self, value, parent_context=None):
return context
class Meta:
- template = 'blocks/article.html'
+ icon = "pick"
+ template = "blocks/article.html"
class NumberedListBlock(blocks.ListBlock):
@@ -152,6 +156,9 @@ def render_basic(self, value, context=None):
)
return format_html("
{0} ", children)
+ class Meta:
+ icon = "list-ol"
+
class RawHTMLBlock(blocks.RawHTMLBlock):
def render_basic(self, value, context=None):
@@ -162,20 +169,39 @@ def render_basic(self, value, context=None):
class DownloadButtonBlock(blocks.StructBlock):
available_text = blocks.CharBlock(
- help_text=_('This text appears when it is possible for the user to install the app on their phone.'))
+ help_text=_(
+ "This text appears when it is possible for the user to install the app on"
+ " their phone"
+ )
+ )
unavailable_text = blocks.CharBlock(
required=False,
help_text=_(
- 'This text appears when the user is using a feature phone and thus cannot install the app '
- '(the button will be disabled in this case). [Currently not implemented]'),
- form_classname='red-help-text')
+ "This text appears when the user is using a feature phone and thus cannot"
+ " install the app (the button will be disabled in this case)."
+ " [Currently not implemented]"
+ ),
+ form_classname="red-help-text",
+ )
offline_text = blocks.CharBlock(
- required=False, help_text=_(
- 'This text appears when the user is navigating the site via the offline app and '
- 'thus it doesn\'t make sense to install the offline app again '
- '(the button will be disabled in this case).'))
- page = PageChooserBlock(target_model='wagtailcore.Page')
- description = blocks.RichTextBlock(features=settings.WAGTAIL_RICH_TEXT_FIELD_FEATURES)
+ required=False,
+ help_text=_(
+ "This text appears when the user is navigating the site via the offline app"
+ " and thus it does not make sense to install the offline app again (the"
+ " button will be disabled in this case)."
+ ),
+ )
+ page = PageChooserBlock(target_model="wagtailcore.Page")
+ description = blocks.RichTextBlock()
class Meta:
- template = 'blocks/download_button.html'
+ icon = "download"
+ template = "blocks/download_button.html"
+
+
+def heading_block():
+ return blocks.CharBlock(
+ icon="h1",
+ form_classname="full title",
+ template="blocks/heading.html",
+ )
diff --git a/home/migrations/0056_auto_20240603_1005.py b/home/migrations/0056_auto_20240603_1005.py
new file mode 100644
index 000000000..1e858df5a
--- /dev/null
+++ b/home/migrations/0056_auto_20240603_1005.py
@@ -0,0 +1,536 @@
+# Generated by Django 3.2.25 on 2024-06-03 10:05
+
+from django.db import migrations, models
+import home.blocks
+import messaging.blocks
+import wagtail.blocks
+import wagtail.fields
+import wagtail.images.blocks
+import wagtailmarkdown.blocks
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("home", "0055_enable_use_json_field"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="article",
+ name="body",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "heading",
+ wagtail.blocks.CharBlock(
+ form_classname="full title",
+ icon="h1",
+ template="blocks/heading.html",
+ ),
+ ),
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("markdown", wagtailmarkdown.blocks.MarkdownBlock()),
+ ("paragraph_v1_legacy", home.blocks.RawHTMLBlock(icon="code")),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ (
+ "list",
+ wagtail.blocks.ListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock(), icon="list-ul"
+ ),
+ ),
+ (
+ "numbered_list",
+ home.blocks.NumberedListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock()
+ ),
+ ),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_poll",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "poll",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Poll"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_survey",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "survey",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Survey"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_quiz",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "quiz",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Quiz"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ("media", home.blocks.MediaBlock()),
+ (
+ "chat_bot",
+ wagtail.blocks.StructBlock(
+ [
+ ("subject", wagtail.blocks.CharBlock()),
+ ("button_text", wagtail.blocks.CharBlock()),
+ ("trigger_string", wagtail.blocks.CharBlock()),
+ (
+ "channel",
+ messaging.blocks.ChatBotChannelChooserBlock(),
+ ),
+ ]
+ ),
+ ),
+ (
+ "download",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "available_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when it is possible for the user to install the app on their phone"
+ ),
+ ),
+ (
+ "unavailable_text",
+ wagtail.blocks.CharBlock(
+ form_classname="red-help-text",
+ help_text="This text appears when the user is using a feature phone and thus cannot install the app (the button will be disabled in this case). [Currently not implemented]",
+ required=False,
+ ),
+ ),
+ (
+ "offline_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when the user is navigating the site via the offline app and thus it does not make sense to install the offline app again (the button will be disabled in this case).",
+ required=False,
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ page_type=["wagtailcore.Page"]
+ ),
+ ),
+ ("description", wagtail.blocks.RichTextBlock()),
+ ]
+ ),
+ ),
+ ],
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="homepage",
+ name="home_featured_content",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_poll",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "poll",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Poll"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_survey",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "survey",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Survey"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_quiz",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "quiz",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Quiz"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "article",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "display_section_title",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "article",
+ wagtail.blocks.PageChooserBlock(
+ page_type=["home.Article"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "download",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "available_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when it is possible for the user to install the app on their phone"
+ ),
+ ),
+ (
+ "unavailable_text",
+ wagtail.blocks.CharBlock(
+ form_classname="red-help-text",
+ help_text="This text appears when the user is using a feature phone and thus cannot install the app (the button will be disabled in this case). [Currently not implemented]",
+ required=False,
+ ),
+ ),
+ (
+ "offline_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when the user is navigating the site via the offline app and thus it does not make sense to install the offline app again (the button will be disabled in this case).",
+ required=False,
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ page_type=["wagtailcore.Page"]
+ ),
+ ),
+ ("description", wagtail.blocks.RichTextBlock()),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="offlinecontentindexpage",
+ name="body",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "heading",
+ wagtail.blocks.CharBlock(
+ form_classname="full title",
+ icon="h1",
+ template="blocks/heading.html",
+ ),
+ ),
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("markdown", wagtailmarkdown.blocks.MarkdownBlock()),
+ ("paragraph_v1_legacy", home.blocks.RawHTMLBlock(icon="code")),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ (
+ "list",
+ wagtail.blocks.ListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock(), icon="list-ul"
+ ),
+ ),
+ (
+ "numbered_list",
+ home.blocks.NumberedListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock()
+ ),
+ ),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_poll",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "poll",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Poll"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_survey",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "survey",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Survey"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "embedded_quiz",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "direct_display",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "quiz",
+ home.blocks.EmbeddedQuestionnaireChooserBlock(
+ page_type=["questionnaires.Quiz"]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ("media", home.blocks.MediaBlock()),
+ (
+ "chat_bot",
+ wagtail.blocks.StructBlock(
+ [
+ ("subject", wagtail.blocks.CharBlock()),
+ ("button_text", wagtail.blocks.CharBlock()),
+ ("trigger_string", wagtail.blocks.CharBlock()),
+ (
+ "channel",
+ messaging.blocks.ChatBotChannelChooserBlock(),
+ ),
+ ]
+ ),
+ ),
+ (
+ "download",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "available_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when it is possible for the user to install the app on their phone"
+ ),
+ ),
+ (
+ "unavailable_text",
+ wagtail.blocks.CharBlock(
+ form_classname="red-help-text",
+ help_text="This text appears when the user is using a feature phone and thus cannot install the app (the button will be disabled in this case). [Currently not implemented]",
+ required=False,
+ ),
+ ),
+ (
+ "offline_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when the user is navigating the site via the offline app and thus it does not make sense to install the offline app again (the button will be disabled in this case).",
+ required=False,
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ page_type=["wagtailcore.Page"]
+ ),
+ ),
+ ("description", wagtail.blocks.RichTextBlock()),
+ ]
+ ),
+ ),
+ ],
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="section",
+ name="body",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "download",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "available_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when it is possible for the user to install the app on their phone"
+ ),
+ ),
+ (
+ "unavailable_text",
+ wagtail.blocks.CharBlock(
+ form_classname="red-help-text",
+ help_text="This text appears when the user is using a feature phone and thus cannot install the app (the button will be disabled in this case). [Currently not implemented]",
+ required=False,
+ ),
+ ),
+ (
+ "offline_text",
+ wagtail.blocks.CharBlock(
+ help_text="This text appears when the user is navigating the site via the offline app and thus it does not make sense to install the offline app again (the button will be disabled in this case).",
+ required=False,
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ page_type=["wagtailcore.Page"]
+ ),
+ ),
+ ("description", wagtail.blocks.RichTextBlock()),
+ ]
+ ),
+ )
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="sitesettings",
+ name="media_file_size_threshold",
+ field=models.IntegerField(
+ default=9437184,
+ help_text="Show warning if uploaded media file size is greater than this in bytes. Default is 9 MB (9,437,184 bytes).",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="sitesettings",
+ name="social_media_content_sharing_button",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "social_media_content_sharing_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("platform", wagtail.blocks.CharBlock(max_length=255)),
+ (
+ "is_active",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ required=False, template="blocks/image.html"
+ ),
+ ),
+ ]
+ ),
+ )
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ ]
diff --git a/home/models.py b/home/models.py
index b3cd0c413..1eee28153 100644
--- a/home/models.py
+++ b/home/models.py
@@ -14,7 +14,6 @@
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from modelcluster.contrib.taggit import ClusterTaggableManager
-from iogt.settings.base import WAGTAIL_CONTENT_LANGUAGES
from modelcluster.fields import ParentalKey
from rest_framework import status
from taggit.models import TaggedItemBase
@@ -25,7 +24,7 @@
ObjectList,
TabbedInterface,
)
-from wagtail.contrib.settings.models import BaseSetting
+from wagtail.contrib.settings.models import BaseSiteSetting
from wagtail.contrib.settings.registry import register_setting
from wagtail import blocks
from wagtail.fields import StreamField
@@ -42,8 +41,18 @@
from messaging.blocks import ChatBotButtonBlock
from comments.models import CommentableMixin
from home.blocks import (
- MediaBlock, SocialMediaLinkBlock, SocialMediaShareButtonBlock, EmbeddedPollBlock, EmbeddedSurveyBlock,
- EmbeddedQuizBlock, PageButtonBlock, NumberedListBlock, RawHTMLBlock, ArticleBlock, DownloadButtonBlock,
+ ArticleBlock,
+ DownloadButtonBlock,
+ EmbeddedPollBlock,
+ EmbeddedSurveyBlock,
+ EmbeddedQuizBlock,
+ heading_block,
+ MediaBlock,
+ PageButtonBlock,
+ NumberedListBlock,
+ RawHTMLBlock,
+ SocialMediaLinkBlock,
+ SocialMediaShareButtonBlock,
)
from .forms import SectionPageForm
from .mixins import PageUtilsMixin, TitleIconMixin
@@ -323,20 +332,20 @@ class AbstractArticle(Page, PageUtilsMixin, CommentableMixin, TitleIconMixin):
body = StreamField(
[
- ('heading', blocks.CharBlock(form_classname="full title", template='blocks/heading.html')),
- ('paragraph', blocks.RichTextBlock(features=settings.WAGTAIL_RICH_TEXT_FIELD_FEATURES)),
- ('markdown', MarkdownBlock(icon='code')),
- ('paragraph_v1_legacy', RawHTMLBlock(icon='code')),
- ('image', ImageChooserBlock(template='blocks/image.html')),
- ('list', blocks.ListBlock(MarkdownBlock(icon='code'))),
- ('numbered_list', NumberedListBlock(MarkdownBlock(icon='code'))),
- ('page_button', PageButtonBlock()),
- ('embedded_poll', EmbeddedPollBlock()),
- ('embedded_survey', EmbeddedSurveyBlock()),
- ('embedded_quiz', EmbeddedQuizBlock()),
- ('media', MediaBlock(icon='media')),
- ('chat_bot', ChatBotButtonBlock()),
- ('download', DownloadButtonBlock()),
+ ("heading", heading_block()),
+ ("paragraph", blocks.RichTextBlock()),
+ ("markdown", MarkdownBlock()),
+ ("paragraph_v1_legacy", RawHTMLBlock(icon='code')),
+ ("image", ImageChooserBlock(template='blocks/image.html')),
+ ("list", blocks.ListBlock(MarkdownBlock(), icon="list-ul")),
+ ("numbered_list", NumberedListBlock(MarkdownBlock())),
+ ("page_button", PageButtonBlock()),
+ ("embedded_poll", EmbeddedPollBlock()),
+ ("embedded_survey", EmbeddedSurveyBlock()),
+ ("embedded_quiz", EmbeddedQuizBlock()),
+ ("media", MediaBlock()),
+ ("chat_bot", ChatBotButtonBlock()),
+ ("download", DownloadButtonBlock()),
],
use_json_field=True,
)
@@ -599,7 +608,7 @@ def get_url(self, request=None, current_site=None):
@register_setting
-class SiteSettings(BaseSetting):
+class SiteSettings(BaseSiteSetting):
logo = models.ForeignKey(
'wagtailimages.Image',
null=True,
@@ -686,16 +695,28 @@ class SiteSettings(BaseSetting):
blank=True,
use_json_field=True,
)
- social_media_content_sharing_button = StreamField([
- ('social_media_content_sharing_button', SocialMediaShareButtonBlock()),
- ], null=True, blank=True)
+ social_media_content_sharing_button = StreamField(
+ [
+ ("social_media_content_sharing_button", SocialMediaShareButtonBlock()),
+ ],
+ null=True,
+ blank=True,
+ use_json_field=True,
+ )
media_file_size_threshold = models.IntegerField(
default=9437184,
- help_text=_('Show warning if uploaded media file size is greater than this in bytes. Default is 9 MB'))
+ help_text=_(
+ "Show warning if uploaded media file size is greater than this in bytes."
+ " Default is 9 MB (9,437,184 bytes)."
+ )
+ )
allow_anonymous_comment = models.BooleanField(default=False)
- registration_survey = models.ForeignKey('questionnaires.Survey', null=True,
- blank=True,
- on_delete=models.SET_NULL)
+ registration_survey = models.ForeignKey(
+ "questionnaires.Survey",
+ null=True,
+ blank=True,
+ on_delete=models.SET_NULL,
+ )
# Obsolete - Web Light service discontinued Dec 2022
opt_in_to_google_web_light = models.BooleanField(default=False)
@@ -959,7 +980,7 @@ class ManifestSettings(models.Model):
)
language = models.CharField(
max_length=3,
- choices=WAGTAIL_CONTENT_LANGUAGES,
+ choices=settings.WAGTAIL_CONTENT_LANGUAGES,
default="en",
verbose_name=_("Language"),
help_text=_("Choose language"),
@@ -1042,7 +1063,7 @@ class Meta:
@register_setting
-class ThemeSettings(BaseSetting):
+class ThemeSettings(BaseSiteSetting):
global_background_color = models.CharField(
null=True, blank=True, help_text='The background color of the website',
max_length=8, default='#FFFFFF')
diff --git a/home/wagtail_hooks.py b/home/wagtail_hooks.py
index 555ff711f..94bef83fa 100644
--- a/home/wagtail_hooks.py
+++ b/home/wagtail_hooks.py
@@ -10,8 +10,7 @@
from django.utils.translation import gettext_lazy as _
from wagtail import __version__
from wagtail.admin import widgets as wagtailadmin_widgets
-from wagtail.admin.menu import MenuItem, SubmenuMenuItem
-from wagtail.contrib.modeladmin.menus import SubMenu
+from wagtail.admin.menu import Menu, MenuItem, SubmenuMenuItem
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from wagtail import hooks
from wagtail.models import Page, PageViewRestriction
@@ -49,7 +48,10 @@ def update_menu_items(request, menu_items):
if item.name == "forms":
item.label = _("Form Data")
if item.name == 'translations':
- item.url = f'{TranslationEntryAdmin().url_helper.get_action_url("index")}?limited=yes'
+ item.url = (
+ TranslationEntryAdmin().url_helper.get_action_url("index") +
+ "?limited=yes"
+ )
if item.name == 'community-comment-moderations':
menu_items.remove(item)
@@ -87,6 +89,7 @@ def global_admin_css():
static("css/global/admin.css"),
)
+
@hooks.register("insert_global_admin_css")
def import_fontawesome_stylesheets():
return "\n".join(
@@ -118,7 +121,7 @@ def page_listing_buttons(page, page_perms, is_parent=False, next_url=None):
yield wagtailadmin_widgets.PageListingButton(
_('Sort child pages'),
'?ordering=ord',
- attrs={'title': _("Change ordering of child pages of '%(title)s'") % {'title': page.get_admin_display_title()}},
+ attrs={"title": _("Change ordering of child pages of '%(title)s'") % {'title': page.get_admin_display_title()}},
priority=60
)
@@ -138,7 +141,7 @@ def about():
return SubmenuMenuItem(
label="About",
- menu=SubMenu(items),
+ menu=Menu(items=items),
icon_name="info-circle",
order=999999,
)
@@ -188,7 +191,11 @@ class TranslationEntryAdmin(ModelAdmin):
menu_label = 'Translations'
menu_icon = 'edit'
list_display = ('original', 'language', 'translation',)
- list_filter = ('language', LimitedTranslatableStringsFilter, MissingTranslationsFilter)
+ list_filter = (
+ 'language',
+ LimitedTranslatableStringsFilter,
+ MissingTranslationsFilter
+ )
search_fields = ('original', 'translation',)
index_template_name = 'modeladmin/translation_manager/translationentry/index.html'
menu_order = 601
diff --git a/iogt/settings/base.py b/iogt/settings/base.py
index b6d91eda1..dbddb05b8 100644
--- a/iogt/settings/base.py
+++ b/iogt/settings/base.py
@@ -410,15 +410,26 @@
WAGTAILMENUS_FLAT_MENU_ITEMS_RELATED_NAME = 'iogt_flat_menu_items'
-WAGTAIL_RICH_TEXT_FIELD_FEATURES = [
- 'h2', 'h3', 'h4',
- 'bold', 'italic',
- 'ol', 'ul',
- 'hr',
- 'link',
- 'document-link',
- 'image',
-]
+WAGTAILADMIN_RICH_TEXT_EDITORS = {
+ "default": {
+ "WIDGET": "wagtail.admin.rich_text.DraftailRichTextArea",
+ "OPTIONS": {
+ "features": [
+ "h2",
+ "h3",
+ "h4",
+ "bold",
+ "italic",
+ "ol",
+ "ul",
+ "hr",
+ "link",
+ "document-link",
+ "image",
+ ],
+ }
+ },
+}
# Search results
SEARCH_RESULTS_PER_PAGE = 10
@@ -435,10 +446,12 @@
TRANSLATIONS_PROJECT_BASE_DIR = BASE_DIR
WAGTAILTRANSFER_LOOKUP_FIELDS = {
+ "contenttypes.contenttype": ["app_label", "model"],
'taggit.tag': ['slug'],
'wagtailcore.locale': ['language_code'],
'iogt_users.user': ['username'],
}
+WAGTAILTRANSFER_NO_FOLLOW_MODELS = ["wagtailcore.page", "contenttypes.contenttype"]
WAGTAILTRANSFER_SECRET_KEY = os.getenv('WAGTAILTRANSFER_SECRET_KEY')
WAGTAILTRANSFER_SHOW_ERROR_FOR_REFERENCED_PAGES = True
WAGTAILTRANSFER_SOURCES = {
diff --git a/iogt/settings/production.py b/iogt/settings/production.py
index 9dc3dfbd7..c0f0f15bb 100644
--- a/iogt/settings/production.py
+++ b/iogt/settings/production.py
@@ -33,7 +33,7 @@
},
}
-SITE_VERSION = '2.12.0'
+SITE_VERSION = '2.13.0-rc.1'
try:
from .local import *
diff --git a/iogt_users/models.py b/iogt_users/models.py
index 801eee996..4c10639d1 100644
--- a/iogt_users/models.py
+++ b/iogt_users/models.py
@@ -1,42 +1,34 @@
-import base64
-
-from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
-from rest_framework_simplejwt.tokens import RefreshToken
class User(AbstractUser):
- first_name = models.CharField('first name', max_length=150, null=True,
- blank=True)
- last_name = models.CharField('last name', max_length=150, null=True,
- blank=True)
- display_name = models.CharField('display name', max_length=255, null=True, blank=True)
+ first_name = models.CharField(
+ "first name",
+ max_length=150,
+ null=True,
+ blank=True,
+ )
+ last_name = models.CharField(
+ "last name",
+ max_length=150,
+ null=True,
+ blank=True,
+ )
+ display_name = models.CharField(
+ "display name",
+ max_length=255,
+ null=True,
+ blank=True,
+ )
email = models.EmailField('email address', null=True, blank=True)
terms_accepted = models.BooleanField(default=False)
-
has_filled_registration_survey = models.BooleanField(default=False)
has_viewed_registration_survey = models.BooleanField(default=False)
-
interactive_uuid = models.CharField(max_length=255, null=True, blank=True)
-
- @property
- def is_rapidpro_bot_user(self):
- return self.groups.filter(name=settings.RAPIDPRO_BOT_GROUP_NAME).exists()
-
- @classmethod
- def get_rapidpro_bot_auth_tokens(cls):
- users = cls.objects.filter(groups__name=settings.RAPIDPRO_BOT_GROUP_NAME)
-
- tokens = {}
- for user in users:
- tokens[user.username] = f'Bearer {RefreshToken.for_user(user).access_token}'
-
- return tokens
-
read_articles = models.ManyToManyField(to='home.Article')
@classmethod
diff --git a/messaging/blocks.py b/messaging/blocks.py
index 550370b6a..5ad8159c5 100644
--- a/messaging/blocks.py
+++ b/messaging/blocks.py
@@ -1,12 +1,12 @@
-from django import forms
from wagtail import blocks
-from .models import ChatbotChannel
+from messaging.views import chatbot_channel_viewset
-class ChatBotChannelChooserBlock(blocks.ChooserBlock):
- target_model = ChatbotChannel
- widget = forms.Select
+ChatBotChannelChooserBlock = chatbot_channel_viewset.get_block_class(
+ name="ChatBotChannelChooserBlock",
+ module_path="messaging.blocks",
+)
class ChatBotButtonBlock(blocks.StructBlock):
@@ -16,5 +16,5 @@ class ChatBotButtonBlock(blocks.StructBlock):
channel = ChatBotChannelChooserBlock()
class Meta:
- icon = 'code'
- template = 'messaging/blocks/chatbot_button.html'
+ icon = "mail"
+ template = "messaging/blocks/chatbot_button.html"
diff --git a/messaging/chat.py b/messaging/chat.py
index 9ed0d1c0f..ac9c2d656 100644
--- a/messaging/chat.py
+++ b/messaging/chat.py
@@ -6,8 +6,9 @@
from django.utils import timezone
from webpush import send_user_notification
-from .models import Message, Thread, UserThread
+from messaging.models import Message, Thread, UserThread
from messaging.rapidpro_client import RapidProClient
+from messaging.utils import is_chatbot
User = get_user_model()
logger = logging.getLogger(__name__)
@@ -81,7 +82,7 @@ def _parse_rapidpro_message(message_text):
def record_reply(self, text, sender, rapidpro_message_id=None, quick_replies=None, mark_unread=True):
if quick_replies is None:
quick_replies = []
- if not sender.is_rapidpro_bot_user:
+ if not is_chatbot(sender):
client = RapidProClient(self.thread)
client.send_reply(text)
diff --git a/messaging/management/commands/get_rapidpro_authentication_header_value.py b/messaging/management/commands/get_rapidpro_authentication_header_value.py
index a5b2692fa..bb48339ff 100644
--- a/messaging/management/commands/get_rapidpro_authentication_header_value.py
+++ b/messaging/management/commands/get_rapidpro_authentication_header_value.py
@@ -1,15 +1,15 @@
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
-User = get_user_model()
+from messaging.utils import get_auth_tokens
class Command(BaseCommand):
"""
- This command prints the Authorization Header Value for RapidPro
+ Print the authorization header for each chatbot user
"""
-
def handle(self, *args, **options):
- tokens = User.get_rapidpro_bot_auth_tokens()
+ tokens = get_auth_tokens()
+
for username, token in tokens.items():
self.stdout.write(self.style.SUCCESS(f'{username}: {token}'))
diff --git a/messaging/templates/messaging/chatbotchannel/index.html b/messaging/templates/messaging/chatbotchannel/index.html
index 1090ee08f..d8d9fca5d 100644
--- a/messaging/templates/messaging/chatbotchannel/index.html
+++ b/messaging/templates/messaging/chatbotchannel/index.html
@@ -1,39 +1,6 @@
{% extends "modeladmin/index.html" %}
{% load messaging_tags %}
-{% load i18n modeladmin_tags wagtailadmin_tags %}
-{% block header %}
-
+{% block extra_rows %}
+{% chatbot_auth_tokens %}
{% endblock %}
diff --git a/messaging/templates/messaging/tags/chatbot_auth_tokens.html b/messaging/templates/messaging/tags/chatbot_auth_tokens.html
index 1847c9c40..4aa4cb8db 100644
--- a/messaging/templates/messaging/tags/chatbot_auth_tokens.html
+++ b/messaging/templates/messaging/tags/chatbot_auth_tokens.html
@@ -1,18 +1,24 @@
-Chatbot Authentication Headers
-
- {% for username, token in tokens.items %}
-
-
{{ username }}:
-
{{ token }}
-
Reveal Token
+
+
+
+
Authentication headers
+
+ {% for username, token in tokens.items %}
+ {{ username }}
+
+ {{ token }}
+ Reveal Token
+
+ {% endfor %}
+
+
- {% endfor %}
-
+
\ No newline at end of file
+
diff --git a/messaging/templates/messaging/thread_detail.html b/messaging/templates/messaging/thread_detail.html
index 439d333ff..751a04a0d 100644
--- a/messaging/templates/messaging/thread_detail.html
+++ b/messaging/templates/messaging/thread_detail.html
@@ -10,7 +10,7 @@
{{ thread.subject }}
{% for message in thread_messages %}
- {% if message.sender.is_rapidpro_bot_user %}
+ {% if message.sender|is_chatbot %}
{% else %}
@@ -55,4 +55,4 @@
{{ thread.subject }}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/messaging/templatetags/messaging_tags.py b/messaging/templatetags/messaging_tags.py
index fba022be4..583e7d7d7 100644
--- a/messaging/templatetags/messaging_tags.py
+++ b/messaging/templatetags/messaging_tags.py
@@ -1,8 +1,10 @@
from django import template
-from django.contrib.auth import get_user_model
+
+import messaging.utils as utils
+
register = template.Library()
-User = get_user_model()
+
@register.filter
def unread(thread, user):
@@ -12,17 +14,20 @@ def unread(thread, user):
return thread.user_threads.filter(user=user, is_read=False).exists()
-@register.inclusion_tag('messaging/tags/quick_reply_form.html')
+@register.inclusion_tag("messaging/tags/quick_reply_form.html")
def render_quick_reply_form(thread, user, text):
return {
- 'thread': thread,
- 'user': user,
- 'text': text,
+ "thread": thread,
+ "user": user,
+ "text": text,
}
-@register.inclusion_tag('messaging/tags/chatbot_auth_tokens.html')
-def render_chatbot_auth_tokens():
- return {
- 'tokens': User.get_rapidpro_bot_auth_tokens(),
- }
+@register.inclusion_tag("messaging/tags/chatbot_auth_tokens.html")
+def chatbot_auth_tokens():
+ return {"tokens": utils.get_auth_tokens()}
+
+
+@register.filter
+def is_chatbot(user):
+ return utils.is_chatbot(user)
diff --git a/messaging/utils.py b/messaging/utils.py
new file mode 100644
index 000000000..7b5f22eac
--- /dev/null
+++ b/messaging/utils.py
@@ -0,0 +1,22 @@
+from django.conf import settings
+from django.contrib.auth import get_user_model
+from rest_framework_simplejwt.tokens import RefreshToken
+
+
+User = get_user_model()
+
+
+def get_auth_tokens():
+ users = User.objects.filter(groups__name=settings.RAPIDPRO_BOT_GROUP_NAME)
+
+ return {
+ user.username: f"Bearer {RefreshToken.for_user(user).access_token}"
+ for user in users
+ }
+
+
+def is_chatbot(user) -> bool:
+ try:
+ return user.groups.filter(name=settings.RAPIDPRO_BOT_GROUP_NAME).exists()
+ except Exception:
+ return False
diff --git a/messaging/views.py b/messaging/views.py
index 3604c803d..fbad9937c 100644
--- a/messaging/views.py
+++ b/messaging/views.py
@@ -1,17 +1,18 @@
from django.contrib import messages
from django.contrib.auth import get_user_model
+from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
-from django.views.generic import (DeleteView, TemplateView, )
+from django.views.generic import DeleteView, TemplateView
+from wagtail.admin.viewsets.chooser import ChooserViewSet
from .chat import ChatManager
from .forms import MessageReplyForm, NewMessageForm
from .models import Thread, UserThread
-from django.contrib.auth.decorators import login_required
User = get_user_model()
@@ -103,3 +104,15 @@ class ThreadDeleteView(DeleteView):
def delete(self, request, *args, **kwargs):
self.get_object().user_threads.filter(user=request.user).update(is_active=False)
return HttpResponseRedirect(reverse("messaging:inbox"))
+
+
+class ChatbotChannelChooserViewSet(ChooserViewSet):
+ model = "messaging.ChatbotChannel"
+ icon = "code"
+ choose_one_text = "Choose a channel"
+ choose_another_text = "Choose another channel"
+ edit_item_text = "Edit this channel"
+ form_fields = ["display_name", "request_url"]
+
+
+chatbot_channel_viewset = ChatbotChannelChooserViewSet("chatbot_channel_chooser")
diff --git a/messaging/wagtail_hooks.py b/messaging/wagtail_hooks.py
index d50e6536c..93bc83c11 100644
--- a/messaging/wagtail_hooks.py
+++ b/messaging/wagtail_hooks.py
@@ -1,6 +1,17 @@
+from wagtail import hooks
from wagtail.contrib.modeladmin.options import (
- ModelAdminGroup, ModelAdmin, modeladmin_register)
-from .models import ChatbotChannel
+ ModelAdminGroup,
+ ModelAdmin,
+ modeladmin_register,
+)
+
+from messaging.models import ChatbotChannel
+from messaging.views import chatbot_channel_viewset
+
+
+@hooks.register("register_admin_viewset")
+def register_chatbot_channel_viewset():
+ return chatbot_channel_viewset
class ChatbotChannelAdmin(ModelAdmin):
diff --git a/questionnaires/migrations/0031_auto_20240603_1005.py b/questionnaires/migrations/0031_auto_20240603_1005.py
new file mode 100644
index 000000000..ed835ba16
--- /dev/null
+++ b/questionnaires/migrations/0031_auto_20240603_1005.py
@@ -0,0 +1,337 @@
+# Generated by Django 3.2.25 on 2024-06-03 10:05
+
+from django.db import migrations
+import home.blocks
+import questionnaires.blocks
+import wagtail.blocks
+import wagtail.fields
+import wagtail.images.blocks
+import wagtailmarkdown.blocks
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("questionnaires", "0030_enable_use_json_field"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="poll",
+ name="description",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "heading",
+ wagtail.blocks.CharBlock(
+ form_classname="full title",
+ icon="h1",
+ template="blocks/heading.html",
+ ),
+ ),
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("paragraph_v1_legacy", home.blocks.RawHTMLBlock(icon="code")),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ("list", wagtailmarkdown.blocks.MarkdownBlock()),
+ (
+ "numbered_list",
+ home.blocks.NumberedListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock()
+ ),
+ ),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="poll",
+ name="terms_and_conditions",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="poll",
+ name="thank_you_text",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("media", home.blocks.MediaBlock()),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="quiz",
+ name="description",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "heading",
+ wagtail.blocks.CharBlock(
+ form_classname="full title",
+ icon="h1",
+ template="blocks/heading.html",
+ ),
+ ),
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("paragraph_v1_legacy", home.blocks.RawHTMLBlock(icon="code")),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ("list", wagtailmarkdown.blocks.MarkdownBlock()),
+ (
+ "numbered_list",
+ home.blocks.NumberedListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock()
+ ),
+ ),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="quiz",
+ name="terms_and_conditions",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="quiz",
+ name="thank_you_text",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("media", home.blocks.MediaBlock()),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="survey",
+ name="description",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "heading",
+ wagtail.blocks.CharBlock(
+ form_classname="full title",
+ icon="h1",
+ template="blocks/heading.html",
+ ),
+ ),
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("paragraph_v1_legacy", home.blocks.RawHTMLBlock(icon="code")),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ("list", wagtailmarkdown.blocks.MarkdownBlock()),
+ (
+ "numbered_list",
+ home.blocks.NumberedListBlock(
+ wagtailmarkdown.blocks.MarkdownBlock()
+ ),
+ ),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="survey",
+ name="terms_and_conditions",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ (
+ "page_button",
+ wagtail.blocks.StructBlock(
+ [
+ ("page", wagtail.blocks.PageChooserBlock()),
+ (
+ "text",
+ wagtail.blocks.CharBlock(
+ max_length=255, required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="survey",
+ name="thank_you_text",
+ field=wagtail.fields.StreamField(
+ [
+ ("paragraph", wagtail.blocks.RichTextBlock()),
+ ("media", home.blocks.MediaBlock()),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ template="blocks/image.html"
+ ),
+ ),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="surveyformfield",
+ name="skip_logic",
+ field=questionnaires.blocks.SkipLogicField(
+ [
+ (
+ "skip_logic",
+ wagtail.blocks.StructBlock(
+ [
+ ("choice", wagtail.blocks.CharBlock(required=False)),
+ (
+ "skip_logic",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("next", "Next default question"),
+ ("end", "End of survey"),
+ ("question", "Another question"),
+ ],
+ required=False,
+ ),
+ ),
+ (
+ "question",
+ questionnaires.blocks.QuestionSelectBlock(
+ help_text="Please save the survey as a draft to populate or update the list of questions.",
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ )
+ ],
+ blank=True,
+ help_text="Checkbox must include exactly 2 Skip Logic Options: true and false, in that order. ",
+ null=True,
+ use_json_field=True,
+ verbose_name="Answer options",
+ ),
+ ),
+ ]
diff --git a/questionnaires/models.py b/questionnaires/models.py
index 0035aefbd..eb3a2394f 100644
--- a/questionnaires/models.py
+++ b/questionnaires/models.py
@@ -14,6 +14,7 @@
from wagtailsvg.models import Svg
from home.blocks import (
+ heading_block,
MediaBlock,
NumberedListBlock,
PageButtonBlock,
@@ -63,13 +64,13 @@ class QuestionnairePage(Page, PageUtilsMixin, TitleIconMixin):
description = StreamField(
[
- ('heading', blocks.CharBlock(form_classname="full title", template='blocks/heading.html')),
- ('paragraph', blocks.RichTextBlock(features=settings.WAGTAIL_RICH_TEXT_FIELD_FEATURES)),
- ('paragraph_v1_legacy', RawHTMLBlock(icon='code')),
- ("image", ImageChooserBlock(template='blocks/image.html')),
- ('list', MarkdownBlock(icon='code')),
- ('numbered_list', NumberedListBlock(MarkdownBlock(icon='code'))),
- ('page_button', PageButtonBlock()),
+ ("heading", heading_block()),
+ ("paragraph", blocks.RichTextBlock()),
+ ("paragraph_v1_legacy", RawHTMLBlock(icon='code')),
+ ("image", ImageChooserBlock(template="blocks/image.html")),
+ ("list", MarkdownBlock()),
+ ("numbered_list", NumberedListBlock(MarkdownBlock())),
+ ("page_button", PageButtonBlock()),
],
null=True,
blank=True,
@@ -77,9 +78,9 @@ class QuestionnairePage(Page, PageUtilsMixin, TitleIconMixin):
)
thank_you_text = StreamField(
[
- ("paragraph", blocks.RichTextBlock(features=settings.WAGTAIL_RICH_TEXT_FIELD_FEATURES)),
- ("media", MediaBlock(icon="media")),
- ("image", ImageChooserBlock(template='blocks/image.html')),
+ ("paragraph", blocks.RichTextBlock()),
+ ("media", MediaBlock()),
+ ("image", ImageChooserBlock(template="blocks/image.html")),
],
null=True,
blank=True,
@@ -107,7 +108,7 @@ class QuestionnairePage(Page, PageUtilsMixin, TitleIconMixin):
terms_and_conditions = StreamField(
[
- ("paragraph", blocks.RichTextBlock(features=settings.WAGTAIL_RICH_TEXT_FIELD_FEATURES)),
+ ("paragraph", blocks.RichTextBlock()),
('page_button', PageButtonBlock()),
],
null=True,
@@ -322,7 +323,7 @@ class SurveyFormField(AbstractFormField):
help_text=_('Column header used during CSV export of survey '
'responses.'),
)
- skip_logic = SkipLogicField(null=True, blank=True)
+ skip_logic = SkipLogicField(null=True, blank=True, use_json_field=True)
default_value = models.TextField(
verbose_name=_('default value'),
blank=True,
diff --git a/requirements.dev.txt b/requirements.dev.txt
index c5a62c68a..aec6c5814 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -1021,9 +1021,9 @@ urllib3[socks]==1.26.19 \
# elasticsearch
# requests
# selenium
-wagtail==3.0.3 \
- --hash=sha256:111ed9a0a6ff26d5d881d52deb4bf52b627d79a53c43829611752dbb68a9192f \
- --hash=sha256:23b3e541401355ea183372582050ea52b049c956dd5b506197f957bb68423ab3
+wagtail==4.0.4 \
+ --hash=sha256:43b35e8e29bcc83a3ddf60f08e8c22fea1e819f338bcba73a4b290771233809a \
+ --hash=sha256:8799c7550cc033c8e85aeeaf6f91b5343d7dee72fecd5d4093499af9d3aeaa1d
# via
# -r requirements.txt
# wagtail-cache
@@ -1031,9 +1031,9 @@ wagtail==3.0.3 \
# wagtail-localize
# wagtail-markdown
# wagtailmedia
-wagtail-cache==2.1.1 \
- --hash=sha256:1fe3ca20a3cdf23f31fc9df662a52f743084cd7f60bd870950094d63a87f695a \
- --hash=sha256:2ac16921d022a58240a009a19220554e41963c5172e7dfe4ef741ddd887e991f
+wagtail-cache==2.2.0 \
+ --hash=sha256:a231bc3941eb3ce3e8c73b9aea5d9a54e2f580009a0c87bf86ac59a6545a9609 \
+ --hash=sha256:e9e6a8391795d5faab877183c025451c82905ea94b7a0d5eb4aca29ea136499f
# via -r requirements.txt
wagtail-factories==4.0.0 \
--hash=sha256:3e39ec1cc13b61c6e467f1bf223ce2d134e823fa9fe4dc7e32d0222cc8d350ec \
diff --git a/requirements.in b/requirements.in
index 92c7a8628..5ce9dba8b 100644
--- a/requirements.in
+++ b/requirements.in
@@ -21,10 +21,10 @@ psycopg2~=2.9.9
pyjwt~=2.8.0
redis~=3.0
tqdm~=4.66
-wagtail-cache~=2.1.1
+wagtail-cache~=2.2.0
wagtail-localize~=1.4.0
wagtail-markdown~=0.10.0
-wagtail~=3.0.3
+wagtail~=4.0.4
wagtailmedia~=0.12.0
wagtailmenus==3.1.3
wagtailsvg==0.0.37
diff --git a/requirements.txt b/requirements.txt
index d7221cae1..f048e04ed 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -783,18 +783,18 @@ urllib3==1.26.19 \
# via
# elasticsearch
# requests
-wagtail==3.0.3 \
- --hash=sha256:111ed9a0a6ff26d5d881d52deb4bf52b627d79a53c43829611752dbb68a9192f \
- --hash=sha256:23b3e541401355ea183372582050ea52b049c956dd5b506197f957bb68423ab3
+wagtail==4.0.4 \
+ --hash=sha256:43b35e8e29bcc83a3ddf60f08e8c22fea1e819f338bcba73a4b290771233809a \
+ --hash=sha256:8799c7550cc033c8e85aeeaf6f91b5343d7dee72fecd5d4093499af9d3aeaa1d
# via
# -r requirements.in
# wagtail-cache
# wagtail-localize
# wagtail-markdown
# wagtailmedia
-wagtail-cache==2.1.1 \
- --hash=sha256:1fe3ca20a3cdf23f31fc9df662a52f743084cd7f60bd870950094d63a87f695a \
- --hash=sha256:2ac16921d022a58240a009a19220554e41963c5172e7dfe4ef741ddd887e991f
+wagtail-cache==2.2.0 \
+ --hash=sha256:a231bc3941eb3ce3e8c73b9aea5d9a54e2f580009a0c87bf86ac59a6545a9609 \
+ --hash=sha256:e9e6a8391795d5faab877183c025451c82905ea94b7a0d5eb4aca29ea136499f
# via -r requirements.in
wagtail-generic-chooser==0.5.1 \
--hash=sha256:135f8cc413d83b82bb8956f625f4b9572aa7139ab3835e2ba2d7e19f2171b354 \