diff --git a/base/blocks.py b/base/blocks.py index bda84802..729dcd7c 100644 --- a/base/blocks.py +++ b/base/blocks.py @@ -129,17 +129,6 @@ class Meta: template = "streams/table_block.html" -class WhatWeDoBlock(blocks.StructBlock): - icon = IconChooserBlock(required=False, label=_("Icon")) - title = blocks.CharBlock(max_length=60, required=False) - description = blocks.RichTextBlock(features=SUMMARY_RICHTEXT_FEATURES) - - class Meta: - template = 'streams/what_we_do_block.html' - icon = 'placeholder' - label = _("What we do") - - class SocialMediaBlock(blocks.StructBlock): name = blocks.CharBlock(max_length=60, ) icon = IconChooserBlock(required=False, label=_("Icon")) diff --git a/base/templates/streams/feature_block.html b/base/templates/streams/feature_block.html index 20dbc184..07a43f65 100644 --- a/base/templates/streams/feature_block.html +++ b/base/templates/streams/feature_block.html @@ -1,27 +1,31 @@ {% load wagtailcore_tags wagtailimages_tags %} +{% if value.figure_type == "image" and value.image %} +
+
+ {% if is_even %} +
+ {% include 'streams/feature_block_detail_include.html' with value=value %} +
+
+ {% include 'streams/feature_block_figure_include.html' with value=value %} +
-
-
- {% if is_even %} -
- {% include 'streams/feature_block_detail_include.html' with value=value %} -
-
- {% include 'streams/feature_block_figure_include.html' with value=value %} -
- - {% else %} - -
- {% include 'streams/feature_block_figure_include.html' with value=value %} -
-
- {% include 'streams/feature_block_detail_include.html' with value=value %} -
- {% endif %} + {% else %} + +
+ {% include 'streams/feature_block_figure_include.html' with value=value %} +
+
+ {% include 'streams/feature_block_detail_include.html' with value=value %} +
+ {% endif %} +
-
+{% endif %} + + + diff --git a/base/templates/streams/what_we_do_block.html b/base/templates/streams/what_we_do_block.html deleted file mode 100644 index a3eafdba..00000000 --- a/base/templates/streams/what_we_do_block.html +++ /dev/null @@ -1,90 +0,0 @@ -{% load wagtailimages_tags wagtailiconchooser_tags %} - -{% block extra_css %} - - - -{% endblock extra_css %} - -{% comment %}
-
-
- {% if value.icon %} - {% svg_icon value.icon %} - {% endif %} -
-
- -
- {% if value.title %} -
{{ value.title }}
- {% endif %} -

- {{ value.description }} -

-
-
{% endcomment %} - -{% load wagtailcore_tags wagtailimages_tags lazyimages_tags %} - -
-

- -
- -
-
- {% if value.icon %} - {% svg_icon value.icon %} - {% endif %} -
-
- {{ value.title }} -
- - -

- -
- diff --git a/nmhs_cms/__init__.py b/nmhs_cms/__init__.py index 5e6e42b7..09ff0d87 100644 --- a/nmhs_cms/__init__.py +++ b/nmhs_cms/__init__.py @@ -2,7 +2,7 @@ # major.minor.patch.release.number # release must be one of alpha, beta, rc, or final -VERSION = (0, 9, 0, "final", 0) +VERSION = (0, 9, 1, "beta", 1) __version__ = get_version(VERSION) diff --git a/nmhs_cms/static/css/nmhs_cms.css b/nmhs_cms/static/css/nmhs_cms.css index a0a76515..92539aab 100644 --- a/nmhs_cms/static/css/nmhs_cms.css +++ b/nmhs_cms/static/css/nmhs_cms.css @@ -118,29 +118,6 @@ select#city-option { color: #ffffff !important; /* Change the text color of the selected option */ } -select#city-option option{ +select#city-option option { color: black !important; } - -/* Width and height of the scrollbar */ -::-webkit-scrollbar { - width: 1px; /* Width of the vertical scrollbar */ - height: 1px; /* Height of the horizontal scrollbar */ -} - -/* Track */ -::-webkit-scrollbar-track { - background: #f1f1f110; - border-radius: 10px; -} - -/* Handle */ -::-webkit-scrollbar-thumb { - background: #ffffff; - border-radius: 10px; -} - -/* Handle on hover */ -::-webkit-scrollbar-thumb:hover { - background: var(--primary-color); -} diff --git a/nmhs_cms/templates/what_we_do_section.html b/nmhs_cms/templates/what_we_do_section.html deleted file mode 100644 index 86305295..00000000 --- a/nmhs_cms/templates/what_we_do_section.html +++ /dev/null @@ -1,35 +0,0 @@ -{% load wagtailcore_tags i18n %} -
-
- - {% if what_we_do_title %} - {{ what_we_do_title }} - {% else %} - {% translate "What we do" %} - {% endif %} - -
-
-
-
- {% for block in page.what_we_do_items %} - {% include_block block with is_first=forloop.first %} - {% endfor %} -
-
-
- - - - {% if page.what_we_do_button_text and page.what_we_do_button_link %} - - {% endif %} -
\ No newline at end of file diff --git a/pages/cap/sign.py b/pages/cap/sign.py index a064c29c..88657d14 100644 --- a/pages/cap/sign.py +++ b/pages/cap/sign.py @@ -1,13 +1,15 @@ +from xml.etree import ElementTree as ET + from django.conf import settings -from lxml import etree -from signxml import XMLSigner, SignatureMethod +from lxml import etree as lxml_ET +from signxml import XMLSigner, SignatureMethod, XMLVerifier cap_cert_path = getattr(settings, "CAP_CERT_PATH", "") cap_private_key_path = getattr(settings, "CAP_PRIVATE_KEY_PATH", "") cap_signature_method = getattr(settings, "CAP_SIGNATURE_METHOD", SignatureMethod.RSA_SHA256) -def sign_xml(xml_string): +def sign_cap_xml(xml_bytes): if not cap_cert_path or not cap_private_key_path: return None @@ -17,6 +19,29 @@ def sign_xml(xml_string): with open(cap_cert_path, "rb") as cert_file: cert = cert_file.read() - root = etree.fromstring(xml_string) + # register cap namespace¬ + ET.register_namespace("cap", "urn:oasis:names:tc:emergency:cap:1.2") + root = ET.fromstring(xml_bytes) + + # specify location for enveloped signature + # https://technotes.shemyak.com/posts/xml-signatures-with-python-elementtree/ + # https://xml-security.github.io/signxml/#signxml.XMLSigner + ET.register_namespace("ds", "http://www.w3.org/2000/09/xmldsig#") + ET.SubElement(root, "ds:Signature", {"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", "Id": "placeholder"}) + signed_root = XMLSigner(signature_algorithm=cap_signature_method).sign(root, key=key, cert=cert) - return etree.tostring(signed_root) + + return lxml_ET.tostring(signed_root) + + +def verify_cap_xml(xml_bytes): + if not cap_cert_path: + return False + + root = lxml_ET.fromstring(xml_bytes) + + try: + verified_data = XMLVerifier().verify(root).signed_xml + return True + except Exception as e: + return False diff --git a/pages/cap/utils.py b/pages/cap/utils.py index 3d3dfa4c..9791cc8b 100644 --- a/pages/cap/utils.py +++ b/pages/cap/utils.py @@ -18,7 +18,7 @@ from base.models import ImportantPages from base.weasyprint_utils import django_url_fetcher from pages.cap.constants import DEFAULT_STYLE, CAP_LAYERS -from .sign import sign_xml +from .sign import sign_cap_xml def create_cap_geomanager_dataset(cap_geomanager_settings, has_live_alerts, request=None): @@ -264,7 +264,7 @@ def serialize_and_sign_cap_alert(alert, request=None): signed = False try: - signed_xml = sign_xml(xml_bytes) + signed_xml = sign_cap_xml(xml_bytes) if signed_xml: xml = signed_xml signed = True diff --git a/pages/cap/views.py b/pages/cap/views.py index abc51d44..a2985587 100644 --- a/pages/cap/views.py +++ b/pages/cap/views.py @@ -75,6 +75,42 @@ def add_root_elements(self, handler): except Exception as e: pass + def add_item_elements(self, handler, item): + handler.addQuickElement("title", item["title"]) + handler.addQuickElement("link", item["link"], attrs={"type": "application/cap+xml"}) + if item["description"] is not None: + handler.addQuickElement("description", item["description"]) + + # Author information. + if item["author_name"] and item["author_email"]: + handler.addQuickElement( + "author", "%s (%s)" % (item["author_email"], item["author_name"]) + ) + elif item["author_email"]: + handler.addQuickElement("author", item["author_email"]) + elif item["author_name"]: + handler.addQuickElement( + "dc:creator", + item["author_name"], + {"xmlns:dc": "http://purl.org/dc/elements/1.1/"}, + ) + + if item["pubdate"] is not None: + handler.addQuickElement("pubDate", rfc2822_date(item["pubdate"])) + if item["comments"] is not None: + handler.addQuickElement("comments", item["comments"]) + if item["unique_id"] is not None: + guid_attrs = {} + if isinstance(item.get("unique_id_is_permalink"), bool): + guid_attrs["isPermaLink"] = str(item["unique_id_is_permalink"]).lower() + handler.addQuickElement("guid", item["unique_id"], guid_attrs) + if item["ttl"] is not None: + handler.addQuickElement("ttl", item["ttl"]) + + # Categories. + for cat in item["categories"]: + handler.addQuickElement("category", cat) + class AlertListFeed(Feed): feed_copyright = "public domain" @@ -177,6 +213,7 @@ def get_cap_xml(request, guid): if not xml: xml, signed = serialize_and_sign_cap_alert(alert, request) + xml = xml.decode("utf-8") if signed: # cache signed alerts for 5 days diff --git a/pages/glossary/templates/glossary/glossary_index_page.html b/pages/glossary/templates/glossary/glossary_index_page.html index 28317bb6..1a64579a 100644 --- a/pages/glossary/templates/glossary/glossary_index_page.html +++ b/pages/glossary/templates/glossary/glossary_index_page.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% load static wagtailiconchooser_tags i18n %} +{% load static wagtailiconchooser_tags i18n wagtailcore_tags %} {% block extra_css %} {% endblock %} @@ -8,11 +8,24 @@ {% block content %}
-
-
- {% include 'service_introduction.html' with page=page what_we_do=False %} -
-
+ {% if page.introduction_image %} +
+
+ {% include 'service_introduction.html' with page=page %} +
+
+ {% else %} +
+
+

+ {{ page.introduction_title }} +

+
+ {{ page.introduction_text|richtext }} +
+
+
+ {% endif %}

{% translate "Browse Terms" %} diff --git a/pages/mediacenter/templates/mediacenter_index.html b/pages/mediacenter/templates/mediacenter_index.html index 5f5a0c9c..aa2d1d14 100644 --- a/pages/mediacenter/templates/mediacenter_index.html +++ b/pages/mediacenter/templates/mediacenter_index.html @@ -1,5 +1,5 @@ {% extends 'services_page_base.html' %} -{% load static wagtailsettings_tags i18n %} +{% load static wagtailsettings_tags i18n wagtailcore_tags %} {% get_settings use_default_site=True %} {% block extra_css %} @@ -16,11 +16,24 @@ {% block content %}
{% include 'hero.html' with page=page %} -
-
- {% include 'service_introduction.html' with page=page %} -
-
+ {% if page.introduction_image %} +
+
+ {% include 'service_introduction.html' with page=page %} +
+
+ {% else %} +
+
+

+ {{ page.introduction_title }} +

+
+ {{ page.introduction_text|richtext }} +
+
+
+ {% endif %} {% if settings.base.OrganisationSetting.facebook or settings.base.OrganisationSetting.twitter %} {% endif %} - {% if page.events.all %}
diff --git a/pages/weather/models.py b/pages/weather/models.py index fa25d7da..2d39dfbf 100644 --- a/pages/weather/models.py +++ b/pages/weather/models.py @@ -65,7 +65,9 @@ def forecast_for_city(self, request, city_slug=None): return self.render(request, context_overrides=context_overrides) - detail_data = get_city_forecast_detail_data(city, request) + forecast_periods_count = fm_settings.periods.count() + multi_period = forecast_periods_count > 1 + detail_data = get_city_forecast_detail_data(city, multi_period=multi_period, request=request) context_overrides.update({ "city": city, diff --git a/pages/weather/templates/weather/daily_weather_report_index.html b/pages/weather/templates/weather/daily_weather_report_index.html index 97b31a57..5a9e6220 100644 --- a/pages/weather/templates/weather/daily_weather_report_index.html +++ b/pages/weather/templates/weather/daily_weather_report_index.html @@ -1,15 +1,28 @@ {% extends "base.html" %} -{% load static wagtailimages_tags i18n %} +{% load static wagtailimages_tags i18n wagtailcore_tags %} {% block body_class %}weather{% endblock %} {% block content %}
-
-
- {% include 'service_introduction.html' with page=page what_we_do=False %} -
-
+ {% if page.introduction_image %} +
+
+ {% include 'service_introduction.html' with page=page %} +
+
+ {% else %} +
+
+

+ {{ page.introduction_title }} +

+
+ {{ page.introduction_text|richtext }} +
+
+
+ {% endif %}

{% translate "Browse Updates" %} @@ -28,8 +41,6 @@

{% endblock content %} - - {% block extra_js %}