Skip to content

Commit

Permalink
Merge pull request #1405 from devinit/develop
Browse files Browse the repository at this point in the history
Develop to master merge for cookie notice and other improvements
  • Loading branch information
akmiller01 authored Sep 5, 2024
2 parents 87f561c + 2826f19 commit 0443800
Show file tree
Hide file tree
Showing 48 changed files with 3,641 additions and 2,737 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,7 @@ docker inspect e20e0a57d399
```

First, bash into nginx, then `curl -sX GET http://consul:8500/v1/catalog/service/blue` to get the ServiceId. Followed by `docker-compose -f docker-compose-consul.yml exec consul consul services deregister -id=ab9298cc69bd:blue:8090`

## 2FA Notes

We are using [wagtail-2fa](https://github.com/labd/wagtail-2fa/). The documentation about which version of this package matching with a specific version of wagtail is terrible. You will have to go through all the changelogs or something similar. If you intend to upgrade either `Wagtail` or `wagtail-2fa`, we advise you set `DEBUG = False` after running all your tests in debug mode, and test 2FA login as well with debug set off. This is because this package attempts to use `wagtail.svg` which keeps changing names in the Wagtail package, and `ManifestStaticFilesStorage` will not throw any errors with `DEBUG = True`. So the only way to test that the two packages (Wagtail and wagtail-2fa) are compatible is turning off debug. Also, don't forget to `collectstatic` before testing.
30 changes: 30 additions & 0 deletions di_website/api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 3.2.25 on 2024-07-02 13:13

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='CookieConsentLogEntry',
fields=[
('token', models.CharField(max_length=255, primary_key=True, serialize=False)),
('anonymised_ip_address', models.GenericIPAddressField()),
('first_seen', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
('url', models.URLField()),
('user_agent', models.TextField()),
('choice', models.CharField(max_length=255)),
],
options={
'verbose_name': 'Cookie Consent Log Entry',
'verbose_name_plural': 'Cookie Consent Log Entries',
},
),
]
19 changes: 19 additions & 0 deletions di_website/api/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
from django.db import models
from wagtail.snippets.models import register_snippet

# Create your models here.

@register_snippet
class CookieConsentLogEntry(models.Model):
token = models.CharField(max_length=255, primary_key=True)
anonymised_ip_address = models.GenericIPAddressField()
first_seen = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
url = models.URLField()
user_agent = models.TextField()
choice = models.CharField(max_length=255)


def __str__(self):
return self.token or 'Unknown log entry'

class Meta():
verbose_name = "Cookie Consent Log Entry"
verbose_name_plural = "Cookie Consent Log Entries"
5 changes: 3 additions & 2 deletions di_website/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

from di_website.api.views import (
footer_view, spotlights_navigation_view, spotlight_page_view,
spotlight_pages_view, dashboard_data_view)
spotlight_pages_view, dashboard_data_view, post_cookie_consent_log_entry)

urlpatterns = [
path('spotlights/navigation/', spotlights_navigation_view, name='spotlight-navigation'),
path('spotlights/pages/', spotlight_pages_view, name='spotlight-pages'),
path('spotlights/page/<slug:slug>/', spotlight_page_view, name='spotlight-page'),
path('footer/', footer_view),
path('dashboard/data/', dashboard_data_view)
path('dashboard/data/', dashboard_data_view),
path('cookie-consent/', post_cookie_consent_log_entry),
]
15 changes: 15 additions & 0 deletions di_website/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,18 @@ def serialise_location_comparison_page(live_pages):
serialised_data['default_locations'].append(block.value)

return serialised_data


def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip


def anonymise_ip_address(ip_address):
ip_split = ip_address.split(".")
ip_split[-1] = "0"
return ".".join(ip_split)
28 changes: 25 additions & 3 deletions di_website/api/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import csv

import json
import requests

from django.conf import settings
from django.http import JsonResponse
from django.http import JsonResponse, HttpResponse
from django.views.decorators.http import require_http_methods
from django.db.utils import DataError

from github import Github
from wagtail.models import Site
Expand All @@ -18,7 +19,9 @@
from .utils import (fetch_and_serialise_footer_sections,
fetch_and_serialise_newsletters,
serialise_location_comparison_page, serialise_page,
serialise_spotlight_theme, serialiseDatasources)
serialise_spotlight_theme, serialiseDatasources,
get_client_ip, anonymise_ip_address)
from .models import CookieConsentLogEntry


@require_http_methods(["GET"])
Expand Down Expand Up @@ -134,3 +137,22 @@ def dashboard_data_view(request):

print(e)
return JsonResponse({ 'error': 'An error occurred' }, safe=False)


@require_http_methods(["POST"])
def post_cookie_consent_log_entry(request):
json_data = json.loads(request.body)
token = json_data.pop('token')
client_ip = get_client_ip(request)
anon_ip = anonymise_ip_address(client_ip)
if anon_ip == '0':
anon_ip = '0.0.0.0'
json_data['anonymised_ip_address'] = anon_ip
try:
CookieConsentLogEntry.objects.update_or_create(
token=token,
defaults=json_data
)
except DataError:
pass
return HttpResponse(status=204)
5 changes: 5 additions & 0 deletions di_website/common/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ class Meta():
label = 'Banner Block'


class ContentBannerBlock(BannerBlock):
class Meta():
template = 'blocks/banner/content_banner_block.html'


class SectionParagraphBlock(StructBlock):
text = RichTextBlock(features=RICHTEXT_FEATURES_NO_FOOTNOTES)
center = BooleanBlock(default=False, required=False)
Expand Down
53 changes: 0 additions & 53 deletions di_website/common/edit_handlers.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,6 @@
from django.forms.utils import pretty_name
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
from django.utils.html import mark_safe

from wagtail.admin.panels import EditHandler
from wagtail.admin.panels import HelpPanel as WagtailHelpPanel


class BaseReadOnlyPanel(EditHandler):
def render(self):
value = getattr(self.instance, self.field_name)
if callable(value):
value = value()
return format_html(
'<div style="padding-top: 1.2em;">{}</div>',
mark_safe(value))

def render_as_object(self):
return format_html(
'<fieldset><legend>{}</legend>'
'<ul class="fields"><li><div class="field">{}</div></li></ul>'
'</fieldset>',
self.heading, self.render())

def render_as_field(self):
return format_html(
'<div class="field">'
'<label>{}{}</label>'
'<div class="field-content">{}</div>'
'</div>',
self.heading, _(':'), self.render())


class ReadOnlyPanel(BaseReadOnlyPanel):
"""
Custom implementation of readonly panel, refer to issue on from wagtail
https://github.com/wagtail/wagtail/issues/2893
"""

def __init__(self, field_name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.field_name = field_name

def clone_kwargs(self):
kwargs = super().clone_kwargs()
kwargs.update(
field_name=self.field_name
)
return kwargs

def bind_to_model(self, model):
return type(str(_('ReadOnlyPanel')), (BaseReadOnlyPanel,), {
'model': self.model, 'heading': self.heading, 'classname': self.classname})


def HelpPanel(
content='',
template='wagtailadmin/panels/help_panel.html',
Expand Down
2 changes: 1 addition & 1 deletion di_website/common/templatetags/responsive.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def render(self, context):
return ''

try:
rendition = image.get_rendition(self.filter)
rendition = image.get_rendition(self.filter_specs)
except SourceImageIOError:
# It's fairly routine for people to pull down remote databases to their
# local dev versions without retrieving the corresponding image files.
Expand Down
6 changes: 6 additions & 0 deletions di_website/common/templatetags/string_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,9 @@ def buzzsprout_container_id(buzzsprout_url):
return container_ids[0]
else:
return ""

@register.filter
def remove_url_parameters(url):
"""Remove parameters from a url-like string"""
split_url = url.split("?")
return split_url[0]
28 changes: 28 additions & 0 deletions di_website/home/migrations/0079_auto_20240701_1535.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.25 on 2024-07-01 15:35

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('wagtailcore', '0083_workflowcontenttype'),
('home', '0078_auto_20231117_1216'),
]

operations = [
migrations.RemoveField(
model_name='cookienotice',
name='cookie_policy',
),
migrations.RemoveField(
model_name='cookienotice',
name='download_link_caption',
),
migrations.AddField(
model_name='cookienotice',
name='set_cookie_page',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.page', verbose_name='Page'),
),
]
12 changes: 4 additions & 8 deletions di_website/home/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,19 @@ class Meta:
class CookieNotice(models.Model):
heading = models.CharField(max_length=255, blank=True, null=True)
body = models.TextField(blank=True, null=True)
download_link_caption = models.CharField(max_length=255, blank=True, null=True, verbose_name='Link Caption')
cookie_policy = models.ForeignKey(
'wagtaildocs.Document',
set_cookie_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
verbose_name='Policy Doc'
verbose_name='Page'
)

panels = [
FieldPanel('heading'),
FieldPanel('body'),
MultiFieldPanel([
FieldPanel('download_link_caption'),
FieldPanel('cookie_policy'),
], heading='Download Link'),
PageChooserPanel('set_cookie_page')
]

def __str__(self):
Expand Down
5 changes: 3 additions & 2 deletions di_website/publications/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from di_website.common.constants import RICHTEXT_FEATURES, RICHTEXT_FEATURES_NO_FOOTNOTES, FOOTNOTE_RICHTEXT_FEATURES
from .infographic import PublicationInfographic
from di_website.common.blocks import AccordionBlock, AnchorBlock, CallToActionBlock, SectionBlockQuote
from di_website.common.blocks import AccordionBlock, AnchorBlock, CallToActionBlock, SectionBlockQuote, ContentBannerBlock


class CaptionedImage(StructBlock):
Expand Down Expand Up @@ -337,7 +337,8 @@ class Meta:
('advanced_interactive_chart', AdvancedInteractiveChartBlock()),
('cta', CallToActionBlock()),
('accordion', AccordionBlock()),
('so_what', SoWhat())
('so_what', SoWhat()),
('content_banner', ContentBannerBlock()),
]

def flexible_content_streamfield(blank=False):
Expand Down
45 changes: 45 additions & 0 deletions di_website/publications/migrations/0126_auto_20240417_1654.py

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions di_website/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
'django.contrib.staticfiles',
'django.contrib.postgres',

# Wagtail 2FA (https://github.com/labd/wagtail-2fa)
'wagtail_2fa',
'django_otp',
'django_otp.plugins.otp_totp',

]

MIDDLEWARE = [
Expand All @@ -105,6 +110,8 @@
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# Wagtail 2FA (https://github.com/labd/wagtail-2fa)
'wagtail_2fa.middleware.VerifyUserMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
Expand Down Expand Up @@ -233,6 +240,10 @@

WAGTAIL_SITE_NAME = "Development Initiatives Website"

# Wagtail 2FA settings (https://github.com/labd/wagtail-2fa)
WAGTAIL_2FA_REQUIRED = True
WAGTAIL_2FA_OTP_TOTP_NAME = f"DI Website CMS ({os.getenv('ENVIRONMENT') or 'Dev'})"

# Base URL to use when referring to full URLs within the Wagtail admin backend -
# e.g. in notification emails. Don't include '/admin' or a trailing slash
WAGTAILADMIN_BASE_URL = os.getenv('BASE_URL') or 'http://devinit.org'
Expand Down
Loading

0 comments on commit 0443800

Please sign in to comment.