From 58f2711ea2422e6d8b846d2fe402681bd75e37d0 Mon Sep 17 00:00:00 2001 From: Miriam Forner Date: Wed, 31 May 2023 16:24:46 +0100 Subject: [PATCH 1/4] Upgrade to django 4.1.9 --- conf/urls.py | 83 +++++++++++++++--------------------------- core/models.py | 83 ++++++++++++++---------------------------- requirements.in | 18 ++++----- requirements.txt | 42 +++++++++++---------- requirements_test.txt | 44 ++++++++++++---------- users/forms.py | 27 +++----------- users/mixins.py | 2 +- users/urls.py | 10 ++--- users/urls_sso.py | 10 ++--- users/views.py | 32 +++++----------- users/wagtail_hooks.py | 7 ++-- 11 files changed, 141 insertions(+), 217 deletions(-) diff --git a/conf/urls.py b/conf/urls.py index 0f0688df..eab87460 100644 --- a/conf/urls.py +++ b/conf/urls.py @@ -1,4 +1,3 @@ - import directory_components.views from directory_components.decorators import skip_ga360 import directory_healthcheck.views @@ -8,13 +7,13 @@ from wagtail import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls -from django.conf.urls import include, url +from django.conf.urls import include from django.conf.urls.static import static from django.conf import settings from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from django.views.generic import RedirectView -from django.urls import path +from django.urls import path, re_path import core.views from groups.views import GroupInfoModalView @@ -24,104 +23,80 @@ api_urls = [ - url(r'^', api_router.urls), - url( + re_path(r'^', api_router.urls), + re_path( r'^pages/lookup-by-slug/(?P[\w-]+)/', api_router.wrap_view(core.views.PageLookupBySlugAPIEndpoint.as_view({'get': 'detail_view'})), - name='lookup-by-slug' + name='lookup-by-slug', ), - url( + re_path( r'^pages/lookup-by-path/(?P[0-9]+)/(?P[\w\-/]*)$', api_router.wrap_view(core.views.PageLookupByPathAPIEndpoint.as_view({'get': 'detail_view'})), - name='lookup-by-path' - ), - url( - r'^pages/types/$', - core.views.PageTypeView.as_view(), - name='pages-types-list' + name='lookup-by-path', ), + re_path(r'^pages/types/$', core.views.PageTypeView.as_view(), name='pages-types-list'), ] healthcheck_urls = [ - url( - r'^$', - directory_healthcheck.views.HealthcheckView.as_view(), - name='healthcheck' - ), - url( - r'^ping/$', - directory_healthcheck.views.PingView.as_view(), - name='ping' - ), + re_path(r'^$', directory_healthcheck.views.HealthcheckView.as_view(), name='healthcheck'), + re_path(r'^ping/$', directory_healthcheck.views.PingView.as_view(), name='ping'), ] urlpatterns = [ - url(r'^django-admin/', admin.site.urls), - url( - r'^api/', - include((api_urls, 'api')) - ), - url( - r'^healthcheck/', - include((healthcheck_urls, 'healthcheck')) - ), - url( + re_path(r'^django-admin/', admin.site.urls), + re_path(r'^api/', include((api_urls, 'api'))), + re_path(r'^healthcheck/', include((healthcheck_urls, 'healthcheck'))), + re_path( r"^robots\.txt$", skip_ga360(directory_components.views.RobotsView.as_view(template_name='core/robots.txt')), - name='robots' + name='robots', ), - url( - r'^$', - RedirectView.as_view(url='/admin/') - ), - url( + re_path(r'^$', RedirectView.as_view(url='/admin/')), + re_path( r'^admin/pages/(?P[0-9]+)/copy-upstream/$', login_required(core.views.CopyUpstreamView.as_view(is_edit=False)), name='copy-upstream', ), - url( + re_path( r'^admin/pages/(?P[0-9]+)/update-upstream/$', login_required(core.views.UpdateUpstreamView.as_view(is_edit=True)), name='update-upstream', ), - url( + re_path( r'^admin/pages/preload/', login_required(csrf_exempt(core.views.PreloadPageView.as_view())), name='preload-add-page', ), - url(r'^admin/group-info/$', login_required(GroupInfoModalView.as_view()), name='group-info'), - + re_path(r'^admin/group-info/$', login_required(GroupInfoModalView.as_view()), name='group-info'), # Prevent users from changing their email address - url(r'^admin/account/change_email/$', RedirectView.as_view(url='/admin/')), - - url(r'^admin/', include(wagtailadmin_urls)), - url(r'^documents/', include(wagtaildocs_urls)), - url(r'^auth/request-access/', include('users.urls_sso')), - + re_path(r'^admin/account/change_email/$', RedirectView.as_view(url='/admin/')), + re_path(r'^admin/', include(wagtailadmin_urls)), + re_path(r'^documents/', include(wagtaildocs_urls)), + re_path(r'^auth/request-access/', include('users.urls_sso')), # For anything not caught by a more specific rule above, hand over to # Wagtail's page serving mechanism. This should be the last pattern in # the list: - url(r'', include(wagtail_urls)), + re_path(r'', include(wagtail_urls)), path( 'subtitles///content.vtt', core.views.serve_subtitles, name='subtitles-serve', ), - ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.FEATURE_FLAGS['ENFORCE_STAFF_SSO_ON']: urlpatterns = [ - url('^auth/', include('authbroker_client.urls')), - url(r'^admin/login/$', RedirectView.as_view(url='/auth/login/', query_string=True)), + re_path('^auth/', include('authbroker_client.urls')), + re_path(r'^admin/login/$', RedirectView.as_view(url='/auth/login/', query_string=True)), ] + urlpatterns if settings.FEATURE_FLAGS['DEBUG_TOOLBAR_ON']: import debug_toolbar + urlpatterns = [ - url(r'^__debug__/', include(debug_toolbar.urls)), + re_path(r'^__debug__/', include(debug_toolbar.urls)), ] + urlpatterns diff --git a/core/models.py b/core/models.py index a3f0edf1..4910e520 100644 --- a/core/models.py +++ b/core/models.py @@ -14,9 +14,7 @@ from django.core import signing from django.conf import settings -from django.contrib.contenttypes.fields import ( - GenericForeignKey, GenericRelation -) +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db import models, transaction from django.shortcuts import redirect @@ -26,12 +24,11 @@ from core.helpers import get_page_full_url from core.wagtail_fields import FormHelpTextField, FormLabelField from wagtailmedia.models import Media -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.urls import reverse class GreatMedia(Media): - transcript = models.TextField( verbose_name=_('Transcript'), blank=True, null=True # left null because was an existing field ) @@ -75,12 +72,7 @@ def subtitles(self): class Breadcrumb(models.Model): - service_name = models.CharField( - max_length=50, - choices=choices.CMS_APP_CHOICES, - null=True, - db_index=True - ) + service_name = models.CharField(max_length=50, choices=choices.CMS_APP_CHOICES, null=True, db_index=True) label = models.CharField(max_length=50) slug = models.SlugField() @@ -133,8 +125,7 @@ class BasePage(Page): default=False, verbose_name="tree-based routing enabled", help_text=( - "Allow this page's URL to be determined by its slug, and " - "the slugs of its ancestors in the page tree." + "Allow this page's URL to be determined by its slug, and " "the slugs of its ancestors in the page tree." ), ) @@ -273,17 +264,14 @@ def get_url(self, is_draft=False, language_code=settings.LANGUAGE_CODE): @property def ancestors_in_app(self): """ - Used by `full_path` and `get_tree_based_breadcrumbs` - in BasePageSerializer. - Starts at 2 to exclude the root page and the app page. - Ignores 'folder' pages. + Used by `full_path` and `get_tree_based_breadcrumbs` + in BasePageSerializer. + Starts at 2 to exclude the root page and the app page. + Ignores 'folder' pages. """ ancestors = self.get_ancestors()[2:] - return [ - page for page in ancestors - if page.specific_class and not page.specific_class.folder_page - ] + return [page for page in ancestors if page.specific_class and not page.specific_class.folder_page] @property def full_path(self): @@ -333,8 +321,7 @@ def get_localized_urls(self): # available languages, so there should be no need to expose the draft # url return [ - (language_code, self.get_url(language_code=language_code)) - for language_code in self.translated_languages + (language_code, self.get_url(language_code=language_code)) for language_code in self.translated_languages ] def serve(self, request, *args, **kwargs): @@ -343,8 +330,7 @@ def serve(self, request, *args, **kwargs): def get_latest_nested_revision_as_page(self): revision = self.get_latest_revision_as_object() foreign_key_names = [ - field.name for field in revision._meta.get_fields() - if isinstance(field, models.ForeignKey) + field.name for field in revision._meta.get_fields() if isinstance(field, models.ForeignKey) ] for name in foreign_key_names: field = getattr(revision, name) @@ -360,19 +346,15 @@ def get_translatable_fields(cls): def get_translatable_string_fields(cls): text_fields = ['TextField', 'CharField'] return [ - name for name in cls.get_translatable_fields() + name + for name in cls.get_translatable_fields() if cls._meta.get_field(name).get_internal_type() in text_fields ] @classmethod def get_required_translatable_fields(cls): - fields = [ - cls._meta.get_field(name) for name in cls.get_translatable_fields() - ] - return [ - field.name for field in fields - if not field.blank and field.model is cls - ] + fields = [cls._meta.get_field(name) for name in cls.get_translatable_fields()] + return [field.name for field in fields if not field.blank and field.model is cls] @property def translated_languages(self): @@ -406,7 +388,8 @@ def translated_languages(self): def language_names(self): if len(self.translated_languages) > 1: names = [ - label for code, label, _ in settings.LANGUAGES_DETAILS + label + for code, label, _ in settings.LANGUAGES_DETAILS if code in self.translated_languages and code != settings.LANGUAGE_CODE ] return 'Translated to {}'.format(', '.join(names)) @@ -440,22 +423,12 @@ def generate_content_hash(field_file): class DocumentHash(AbstractObjectHash): document = models.ForeignKey( - 'wagtaildocs.Document', - null=True, - blank=True, - on_delete=models.CASCADE, - related_name='+' + 'wagtaildocs.Document', null=True, blank=True, on_delete=models.CASCADE, related_name='+' ) class ImageHash(AbstractObjectHash): - image = models.ForeignKey( - 'wagtailimages.Image', - null=True, - blank=True, - on_delete=models.CASCADE, - related_name='+' - ) + image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.CASCADE, related_name='+') class WagtailAdminExclusivePageMixin: @@ -479,6 +452,7 @@ class ExclusivePageMixin(WagtailAdminExclusivePageMixin): prevents anything other than the `slug_identity` class attribute value being used as the `slug` when creating new pages. """ + read_only_fields = ['slug'] def save(self, *args, **kwargs): @@ -530,13 +504,12 @@ class Meta: def allowed_subpage_models(cls): allowed_name = getattr(cls, 'service_name_value', None) return [ - model for model in Page.allowed_subpage_models() + model + for model in Page.allowed_subpage_models() if getattr(model, 'service_name_value', None) == allowed_name ] - settings_panels = [ - FieldPanel('title_en_gb') - ] + settings_panels = [FieldPanel('title_en_gb')] content_panels = [] promote_panels = [] @@ -545,6 +518,7 @@ class FormPageMetaClass(PageBase): """Metaclass that adds _label and _help_text to a Page when given a list of form_field_names. """ + def __new__(mcls, name, bases, attrs): form_field_names = attrs['form_field_names'] for field_name in form_field_names: @@ -557,11 +531,10 @@ def __new__(mcls, name, bases, attrs): children=[ FieldPanel(name + '_label'), FieldPanel(name + '_help_text'), - ] - ) for name in form_field_names + ], + ) + for name in form_field_names ] - attrs['content_panels'] = ( - attrs['content_panels_before_form'] + form_panels + attrs['content_panels_after_form'] - ) + attrs['content_panels'] = attrs['content_panels_before_form'] + form_panels + attrs['content_panels_after_form'] return super().__new__(mcls, name, bases, attrs) diff --git a/requirements.in b/requirements.in index 504052d9..4cae20dc 100644 --- a/requirements.in +++ b/requirements.in @@ -1,33 +1,33 @@ -django==3.2.19 +django==4.1.9 djangorestframework==3.14.* django-environ==0.* gunicorn==20.* sentry-sdk==1.* django_storages==1.13.* whitenoise==6.* -dj-database-url==1.* +dj-database-url==2.* psycopg2==2.9.* --no-binary psycopg2 django-pglocks==1.0.* boto3==1.24.* -sigauth==5.1.1 +sigauth==5.2.0 directory-healthcheck==3.* -directory-components==38.* -directory-constants==22.* +directory-components==39.* +directory-constants==23.* django-staff-sso-client==4.* html2text==2020.* pytube==9.2.2 django-filter>=2.4.0 django-redis celery[redis] -django-celery-beat==2.2.1 +django-celery-beat==2.5.0 kombu==5.2.3 requests[security]==2.27.1 markdown==2.* bleach==3.* bleach-whitelist==0.* wagtail==4.1.5 -wagtail-modeltranslation==0.13.0 -django-modeltranslation==0.18.2 +wagtail-modeltranslation==0.13.* +django-modeltranslation==0.18.* urllib3==1.26.* w3lib==1.22 notifications-python-client==6.3.* @@ -36,6 +36,6 @@ pycountry==19.8.18 elastic-apm>6.0 gevent==22.10.* psycogreen==1.0.2 -wagtailmedia==0.12.0 +wagtailmedia==0.14.* cryptography==39.* oauthlib==3.2.* \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9c6d5c81..abd7de6a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ amqp==5.1.1 # via kombu anyascii==0.3.2 # via wagtail -asgiref==3.6.0 +asgiref==3.7.1 # via django async-timeout==4.0.2 # via redis @@ -57,19 +57,21 @@ click-plugins==1.1.1 # via celery click-repl==0.2.0 # via celery +cron-descriptor==1.4.0 + # via django-celery-beat cryptography==39.0.2 # via -r requirements.in -directory-components==38.2.1 +directory-components==39.1.0 # via -r requirements.in -directory-constants==22.0.2 +directory-constants==23.1.0 # via # -r requirements.in # directory-components -directory-healthcheck==3.1.0 +directory-healthcheck==3.1.1 # via -r requirements.in -dj-database-url==1.3.0 +dj-database-url==2.0.0 # via -r requirements.in -django==3.2.19 +django==4.1.9 # via # -r requirements.in # directory-components @@ -86,14 +88,13 @@ django==3.2.19 # django-staff-sso-client # django-storages # django-taggit - # django-timezone-field # django-treebeard # djangorestframework # sigauth # wagtail # wagtail-modeltranslation # wagtailmedia -django-celery-beat==2.2.1 +django-celery-beat==2.5.0 # via -r requirements.in django-environ==0.10.0 # via -r requirements.in @@ -105,7 +106,7 @@ django-health-check==3.17.0 # via directory-healthcheck django-modelcluster==6.0 # via wagtail -django-modeltranslation==0.18.2 +django-modeltranslation==0.18.9 # via # -r requirements.in # wagtail-modeltranslation @@ -115,13 +116,13 @@ django-pglocks==1.0.4 # via -r requirements.in django-redis==5.2.0 # via -r requirements.in -django-staff-sso-client==4.1.1 +django-staff-sso-client==4.2.0 # via -r requirements.in django-storages==1.13.2 # via -r requirements.in django-taggit==3.1.0 # via wagtail -django-timezone-field==4.2.3 +django-timezone-field==5.0 # via django-celery-beat django-treebeard==4.7 # via wagtail @@ -207,7 +208,6 @@ pytube==9.2.2 pytz==2023.3 # via # celery - # django # django-modelcluster # django-timezone-field # djangorestframework @@ -226,15 +226,14 @@ requests-oauthlib==1.3.1 # via django-staff-sso-client s3transfer==0.6.1 # via boto3 -sentry-sdk==1.23.1 +sentry-sdk==1.24.0 # via -r requirements.in -sigauth==5.1.1 +sigauth==5.2.0 # via -r requirements.in six==1.16.0 # via # bleach # click-repl - # django-modeltranslation # django-pglocks # html5lib # jsonschema @@ -248,9 +247,14 @@ sqlparse==0.4.4 # via django telepath==0.3 # via wagtail -typing-extensions==4.5.0 - # via dj-database-url -urllib3==1.26.15 +typing-extensions==4.6.2 + # via + # asgiref + # dj-database-url + # django-modeltranslation +tzdata==2023.3 + # via django-celery-beat +urllib3==1.26.16 # via # -r requirements.in # botocore @@ -271,7 +275,7 @@ wagtail==4.1.5 # wagtailmedia wagtail-modeltranslation==0.13.0 # via -r requirements.in -wagtailmedia==0.12.0 +wagtailmedia==0.14.1 # via -r requirements.in wcwidth==0.2.6 # via prompt-toolkit diff --git a/requirements_test.txt b/requirements_test.txt index 264de945..fb8c07e9 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -10,7 +10,7 @@ amqp==5.1.1 # via kombu anyascii==0.3.2 # via wagtail -asgiref==3.6.0 +asgiref==3.7.1 # via django async-timeout==4.0.2 # via redis @@ -67,19 +67,21 @@ coverage[toml]==6.5.0 # pytest-cov coveralls==3.3.1 # via -r requirements_test.in +cron-descriptor==1.4.0 + # via django-celery-beat cryptography==39.0.2 # via -r requirements.in -directory-components==38.2.1 +directory-components==39.1.0 # via -r requirements.in -directory-constants==22.0.2 +directory-constants==23.1.0 # via # -r requirements.in # directory-components -directory-healthcheck==3.1.0 +directory-healthcheck==3.1.1 # via -r requirements.in -dj-database-url==1.3.0 +dj-database-url==2.0.0 # via -r requirements.in -django==3.2.19 +django==4.1.9 # via # -r requirements.in # directory-components @@ -97,14 +99,13 @@ django==3.2.19 # django-staff-sso-client # django-storages # django-taggit - # django-timezone-field # django-treebeard # djangorestframework # sigauth # wagtail # wagtail-modeltranslation # wagtailmedia -django-celery-beat==2.2.1 +django-celery-beat==2.5.0 # via -r requirements.in django-debug-toolbar==3.2.4 # via -r requirements_test.in @@ -118,7 +119,7 @@ django-health-check==3.17.0 # via directory-healthcheck django-modelcluster==6.0 # via wagtail -django-modeltranslation==0.18.2 +django-modeltranslation==0.18.9 # via # -r requirements.in # wagtail-modeltranslation @@ -128,13 +129,13 @@ django-pglocks==1.0.4 # via -r requirements.in django-redis==5.2.0 # via -r requirements.in -django-staff-sso-client==4.1.1 +django-staff-sso-client==4.2.0 # via -r requirements.in django-storages==1.13.2 # via -r requirements.in django-taggit==3.1.0 # via wagtail -django-timezone-field==4.2.3 +django-timezone-field==5.0 # via django-celery-beat django-treebeard==4.7 # via wagtail @@ -253,7 +254,7 @@ pytest==7.3.1 # pytest-sugar pytest-codecov==0.5.1 # via -r requirements_test.in -pytest-cov==4.0.0 +pytest-cov==4.1.0 # via # -r requirements_test.in # pytest-codecov @@ -274,7 +275,6 @@ pytube==9.2.2 pytz==2023.3 # via # celery - # django # django-modelcluster # django-timezone-field # djangorestframework @@ -298,15 +298,14 @@ requests-oauthlib==1.3.1 # via django-staff-sso-client s3transfer==0.6.1 # via boto3 -sentry-sdk==1.23.1 +sentry-sdk==1.24.0 # via -r requirements.in -sigauth==5.1.1 +sigauth==5.2.0 # via -r requirements.in six==1.16.0 # via # bleach # click-repl - # django-modeltranslation # django-pglocks # freezegun # html5lib @@ -334,9 +333,14 @@ tomli==2.0.1 # coverage # pyproject-hooks # pytest -typing-extensions==4.5.0 - # via dj-database-url -urllib3==1.26.15 +typing-extensions==4.6.2 + # via + # asgiref + # dj-database-url + # django-modeltranslation +tzdata==2023.3 + # via django-celery-beat +urllib3==1.26.16 # via # -r requirements.in # botocore @@ -360,7 +364,7 @@ wagtail-factories==2.0.1 # via -r requirements_test.in wagtail-modeltranslation==0.13.0 # via -r requirements.in -wagtailmedia==0.12.0 +wagtailmedia==0.14.1 # via -r requirements.in wcwidth==0.2.6 # via prompt-toolkit diff --git a/users/forms.py b/users/forms.py index d6ae9fa6..c82e9dd4 100644 --- a/users/forms.py +++ b/users/forms.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model from django.conf import settings from django.contrib.auth.models import Group -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from wagtail.users import forms as wagtail_forms from core.widgets import Select2Widget @@ -26,9 +26,7 @@ class EntryPointAwareUserActionForm(forms.Form): is_superuser = forms.BooleanField( label=_("Superuser"), required=False, - help_text=_( - "Superusers have full access to manage any object or setting." - ), + help_text=_("Superusers have full access to manage any object or setting."), ) groups = GroupWithSummariesMultipleChoiceField( @@ -44,9 +42,7 @@ def __init__(self, user, *args, **kwargs): """ super().__init__(*args, **kwargs) self.fields["groups"].queryset = ( - self.get_groups_queryset(user) - .select_related('info') - .order_by('info__seniority_level', 'name') + self.get_groups_queryset(user).select_related('info').order_by('info__seniority_level', 'name') ) def get_groups_queryset(self, user): @@ -69,10 +65,7 @@ def get_groups_queryset(self, user): ).distinct() -class UserEditForm( - EntryPointAwareUserActionForm, - wagtail_forms.UserEditForm -): +class UserEditForm(EntryPointAwareUserActionForm, wagtail_forms.UserEditForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) """ @@ -87,24 +80,16 @@ def __init__(self, *args, **kwargs): self.fields['last_name'].disabled = True -class UserCreationForm( - EntryPointAwareUserActionForm, - wagtail_forms.UserCreationForm -): +class UserCreationForm(EntryPointAwareUserActionForm, wagtail_forms.UserCreationForm): pass class UserNameWithEmailChoiceField(forms.ModelChoiceField): - def label_from_instance(self, obj): - return "{name} <{email}>".format( - name=obj.get_full_name(), - email=obj.email - ) + return "{name} <{email}>".format(name=obj.get_full_name(), email=obj.email) class SSORequestAccessForm(forms.ModelForm): - self_assigned_group = GroupChoiceFieldWithRolesModal( label="Which best describes your content role?", empty_label=None, diff --git a/users/mixins.py b/users/mixins.py index db401171..d9a7a755 100644 --- a/users/mixins.py +++ b/users/mixins.py @@ -1,6 +1,6 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.shortcuts import redirect -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from wagtail.admin import messages diff --git a/users/urls.py b/users/urls.py index db7edb66..ebc125ad 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.urls import re_path from wagtail.users.views import users as original_users_views @@ -6,8 +6,8 @@ app_name = 'great_users' urlpatterns = [ - url(r'^$', original_users_views.Index, name='index'), - url(r'^add/$', views.CreateUserView.as_view(), name='add'), - url(r'^(?P\d+)/$', views.EditUserView.as_view(), name='edit'), - url(r'^([^\/]+)/delete/$', original_users_views.Delete, name='delete'), + re_path(r'^$', original_users_views.Index, name='index'), + re_path(r'^add/$', views.CreateUserView.as_view(), name='add'), + re_path(r'^(?P\d+)/$', views.EditUserView.as_view(), name='edit'), + re_path(r'^([^\/]+)/delete/$', original_users_views.Delete, name='delete'), ] diff --git a/users/urls_sso.py b/users/urls_sso.py index 29dbcbfd..6054c4e6 100644 --- a/users/urls_sso.py +++ b/users/urls_sso.py @@ -1,21 +1,19 @@ -from django.conf.urls import url from django.contrib.auth.decorators import login_required +from django.urls import re_path from django.views.generic import TemplateView from . import views app_name = 'great_sso' urlpatterns = [ - url( + re_path( r'^$', login_required(views.SSORequestAccessView.as_view()), name="request_access", ), - url( + re_path( r'^thanks/$', - login_required(TemplateView.as_view( - template_name="sso/request_access_success.html" - )), + login_required(TemplateView.as_view(template_name="sso/request_access_success.html")), name="request_access_success", ), ] diff --git a/users/views.py b/users/views.py index 5870ec78..1cf89663 100644 --- a/users/views.py +++ b/users/views.py @@ -3,7 +3,7 @@ from django.shortcuts import redirect from django.urls import reverse, reverse_lazy from django.utils import timezone -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from django.views import generic from wagtail.admin import messages from wagtail.admin.views.generic import EditView @@ -40,10 +40,7 @@ def get_form_kwargs(self): return kwargs def form_invalid(self, form): - messages.error( - self.request, - _(self.form_invalid_message) - ) + messages.error(self.request, _(self.form_invalid_message)) return super().form_invalid(form) def handle_success_message(self): @@ -51,10 +48,8 @@ def handle_success_message(self): messages.success( self.request, _(self.form_valid_message).format(user), - buttons=[messages.button( - reverse('great_users:edit', args=(user.pk,)), - _('Edit') - )]) + buttons=[messages.button(reverse('great_users:edit', args=(user.pk,)), _('Edit'))], + ) hook_name = 'after_{action}_user'.format(action=self.hook_action) for fn in hooks.get_hooks(hook_name): result = fn(self.request, user) @@ -62,17 +57,14 @@ def handle_success_message(self): return result -class CreateUserView( - WagtailUserActionBaseView, - generic.CreateView -): +class CreateUserView(WagtailUserActionBaseView, generic.CreateView): form_class = forms.UserCreationForm model = User template_name = 'great_users/users/create.html' permission_required = add_user_perm hook_action = 'create' form_invalid_message = 'The user could not be created due to errors.' - form_valid_message = 'User ''{0}'' created.' + form_valid_message = 'User ' '{0}' ' created.' def form_valid(self, form): self.object = form.save() @@ -80,17 +72,14 @@ def form_valid(self, form): return redirect('great_users:index') -class EditUserView( - WagtailUserActionBaseView, - generic.UpdateView -): +class EditUserView(WagtailUserActionBaseView, generic.UpdateView): form_class = forms.UserEditForm model = User template_name = 'wagtailusers/users/edit.html' permission_required = change_user_perm hook_action = 'edit' form_invalid_message = 'The user could not be saved due to errors.' - form_valid_message = 'User ''{0}'' updated.' + form_valid_message = 'User ' '{0}' ' updated.' def dispatch(self, request, *args, **kwargs): self.object = self.get_object() @@ -100,10 +89,7 @@ def dispatch(self, request, *args, **kwargs): UserProfile.STATUS_CREATED, UserProfile.STATUS_AWAITING_APPROVAL, ) - if ( - settings.FEATURE_FLAGS['ENFORCE_STAFF_SSO_ON'] and - self.is_approval and request.method == 'GET' - ): + if settings.FEATURE_FLAGS['ENFORCE_STAFF_SSO_ON'] and self.is_approval and request.method == 'GET': # Warn the current user that this is an 'approval' message_text = ( "This user is awaiting approval and will be automatically " diff --git a/users/wagtail_hooks.py b/users/wagtail_hooks.py index e15347a5..c1b252a5 100644 --- a/users/wagtail_hooks.py +++ b/users/wagtail_hooks.py @@ -1,4 +1,5 @@ -from django.conf.urls import url, include +from django.conf.urls import include +from django.urls import re_path from wagtail import hooks from . import urls as users_urls @@ -7,6 +8,4 @@ @hooks.register('register_admin_urls', order=-1) # This will run before every hook in the wagtail core def register_admin_urls(): - return [ - url(r'^dit_users/', include(users_urls, namespace='great_users')) - ] + return [re_path(r'^dit_users/', include(users_urls, namespace='great_users'))] From de515456c1067472b02258dd898b4ac835c747bf Mon Sep 17 00:00:00 2001 From: Miriam Forner Date: Wed, 31 May 2023 16:24:56 +0100 Subject: [PATCH 2/4] Update middleware tests to pass get_response --- tests/core/test_middleware.py | 16 +++++++++++----- tests/users/test_middleware.py | 16 +++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/core/test_middleware.py b/tests/core/test_middleware.py index e290635b..6bb9cfc4 100644 --- a/tests/core/test_middleware.py +++ b/tests/core/test_middleware.py @@ -2,12 +2,18 @@ from django.conf import settings from django.utils import translation +from unittest import mock from core import middleware pytestmark = pytest.mark.django_db +@pytest.fixture +def mock_get_response(autouse=True): + return mock.MagicMock() + + def test_locale_middleware_installed(): expected = 'core.middleware.LocaleQuerystringMiddleware' assert expected in settings.MIDDLEWARE @@ -15,7 +21,7 @@ def test_locale_middleware_installed(): def test_locale_middleware_sets_querystring_language(rf): request = rf.get('/', {'lang': 'en-gb'}) - instance = middleware.LocaleQuerystringMiddleware() + instance = middleware.LocaleQuerystringMiddleware(mock_get_response) instance.process_request(request) @@ -25,7 +31,7 @@ def test_locale_middleware_sets_querystring_language(rf): def test_locale_middleware_ignored_invalid_querystring_language(rf): request = rf.get('/', {'lang': 'plip'}) - instance = middleware.LocaleQuerystringMiddleware() + instance = middleware.LocaleQuerystringMiddleware(mock_get_response) instance.process_request(request) @@ -35,7 +41,7 @@ def test_locale_middleware_ignored_invalid_querystring_language(rf): def test_locale_middleware_handles_missing_querystring_language(rf): request = rf.get('/') - instance = middleware.LocaleQuerystringMiddleware() + instance = middleware.LocaleQuerystringMiddleware(mock_get_response) instance.process_request(request) @@ -52,7 +58,7 @@ def test_maintenance_mode_middleware_feature_flag_on(rf, settings): settings.FEATURE_FLAGS['MAINTENANCE_MODE_ON'] = True request = rf.get('/') - response = middleware.MaintenanceModeMiddleware().process_request(request) + response = middleware.MaintenanceModeMiddleware(mock_get_response).process_request(request) assert response.status_code == 503 assert response.content == b'CMS is offline for maintenance' @@ -63,6 +69,6 @@ def test_maintenance_mode_middleware_feature_flag_off(rf, settings): request = rf.get('/') - response = middleware.MaintenanceModeMiddleware().process_request(request) + response = middleware.MaintenanceModeMiddleware(mock_get_response).process_request(request) assert response is None diff --git a/tests/users/test_middleware.py b/tests/users/test_middleware.py index ee4a3143..db784f97 100644 --- a/tests/users/test_middleware.py +++ b/tests/users/test_middleware.py @@ -2,6 +2,7 @@ from django.urls import reverse from django.contrib.auth.models import AnonymousUser +from unittest import mock from users.models import UserProfile from users.middleware import SSORedirectUsersToRequestAccessViews @@ -10,6 +11,11 @@ pytestmark = pytest.mark.django_db +@pytest.fixture +def mock_get_response(autouse=True): + return mock.MagicMock() + + class MockProfile: def __init__(self, assignment_status): if assignment_status is None: @@ -35,14 +41,14 @@ def is_authenticated(self): def test_process_request_returns_none_if_user_is_not_authenticated(rf): request = rf.get('/admin/') request.user = AnonymousUser() - result = SSORedirectUsersToRequestAccessViews().process_request(request) + result = SSORedirectUsersToRequestAccessViews(mock_get_response).process_request(request) assert result is None def test_process_request_returns_none_if_user_is_a_superuser(rf): request = rf.get('/admin/') request.user = MockUser(authenticated=True) - result = SSORedirectUsersToRequestAccessViews().process_request(request) + result = SSORedirectUsersToRequestAccessViews(mock_get_response).process_request(request) assert result is None @@ -57,7 +63,7 @@ def test_process_request_returns_none_if_user_is_a_superuser(rf): def test_process_request_returns_none_if_url_not_in_admin(rf, url): request = rf.get(url) request.user = MockUser(authenticated=True, superuser=False) - result = SSORedirectUsersToRequestAccessViews().process_request(request) + result = SSORedirectUsersToRequestAccessViews(mock_get_response).process_request(request) assert result is None @@ -71,7 +77,7 @@ def test_process_request_returns_none_if_url_not_in_admin(rf, url): def test_process_request_returns_none_if_user_requesting_access(rf, url): request = rf.get(url) request.user = MockUser(authenticated=True, superuser=False) - result = SSORedirectUsersToRequestAccessViews().process_request(request) + result = SSORedirectUsersToRequestAccessViews(mock_get_response).process_request(request) assert result is None @@ -89,6 +95,6 @@ def test_process_request_redirects_for_assignment_status(rf, assignment_status, superuser=False, assignment_status=assignment_status, ) - result = SSORedirectUsersToRequestAccessViews().process_request(request) + result = SSORedirectUsersToRequestAccessViews(mock_get_response).process_request(request) assert result.status_code == 302 assert result['Location'] == redirects_to From 33a6c9e3b188d1a6324f8c7d95a8d6d1920376dd Mon Sep 17 00:00:00 2001 From: Miriam Forner Date: Wed, 31 May 2023 16:25:04 +0100 Subject: [PATCH 3/4] Remove redundant migrate --plan check from CI pipeline --- .circleci/config.yml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c622a51..9756f5cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,39 +67,12 @@ jobs: pip install flake8 make flake8 - migrations: - docker: - - image: circleci/python:3.9.7 - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: true - - restore_cache: - key: v1-deps-{{ checksum "requirements_test.txt" }} - - run: - name: Create virtualenv and install dependencies - command: | - python3 -m venv venv - . venv/bin/activate - pip install --upgrade pip - make install_requirements - - save_cache: - key: v1-deps-{{ checksum "requirements_test.txt" }} - paths: - - "venv" - - run: - name: Check migrations - command: | - . venv/bin/activate - make check_migrations - workflows: version: 2 test_and_flake8: jobs: - test - flake8 - - migrations - notify: requires: - test From 15f91c1a2967a17ca98103378af0397799696137 Mon Sep 17 00:00:00 2001 From: Miriam Forner Date: Thu, 1 Jun 2023 10:05:42 +0100 Subject: [PATCH 4/4] Update whitenoise usage for 6.4.0 --- conf/settings.py | 2 +- requirements.in | 2 +- requirements.txt | 4 ++-- requirements_test.txt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/conf/settings.py b/conf/settings.py index f366c982..7a4a7fc8 100644 --- a/conf/settings.py +++ b/conf/settings.py @@ -200,7 +200,7 @@ STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles') STATIC_HOST = env.str('STATIC_HOST', '') STATIC_URL = STATIC_HOST + '/static/' -STATICFILES_STORAGE = env.str('STATICFILES_STORAGE', 'whitenoise.storage.CompressedManifestStaticFilesStorage') +STATICFILES_STORAGE = env.str('STATICFILES_STORAGE', 'whitenoise.storage.CompressedStaticFilesStorage') DEFAULT_FILE_STORAGE = env.str('DEFAULT_FILE_STORAGE', 'core.storage_backends.ImmutableFilesS3Boto3Storage') # Logging for development diff --git a/requirements.in b/requirements.in index 4cae20dc..53cd768b 100644 --- a/requirements.in +++ b/requirements.in @@ -4,7 +4,7 @@ django-environ==0.* gunicorn==20.* sentry-sdk==1.* django_storages==1.13.* -whitenoise==6.* +whitenoise==6.4.* dj-database-url==2.* psycopg2==2.9.* --no-binary psycopg2 django-pglocks==1.0.* diff --git a/requirements.txt b/requirements.txt index abd7de6a..72605fb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ amqp==5.1.1 # via kombu anyascii==0.3.2 # via wagtail -asgiref==3.7.1 +asgiref==3.7.2 # via django async-timeout==4.0.2 # via redis @@ -61,7 +61,7 @@ cron-descriptor==1.4.0 # via django-celery-beat cryptography==39.0.2 # via -r requirements.in -directory-components==39.1.0 +directory-components==39.1.2 # via -r requirements.in directory-constants==23.1.0 # via diff --git a/requirements_test.txt b/requirements_test.txt index fb8c07e9..35b31c57 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -10,7 +10,7 @@ amqp==5.1.1 # via kombu anyascii==0.3.2 # via wagtail -asgiref==3.7.1 +asgiref==3.7.2 # via django async-timeout==4.0.2 # via redis @@ -71,7 +71,7 @@ cron-descriptor==1.4.0 # via django-celery-beat cryptography==39.0.2 # via -r requirements.in -directory-components==39.1.0 +directory-components==39.1.2 # via -r requirements.in directory-constants==23.1.0 # via