From dc7581fef4aa17017a0a603c762ba9b65dde8f64 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:33:57 +0000 Subject: [PATCH 1/6] Changed NullBoleanField() to BooleanField(null=True), according to the 2.1 upgrade recommendations. - Legacy-Id: 18051 --- .../doc/migrations/0032_auto_20200624_1332.py | 23 +++++++++++++++++++ ietf/doc/models.py | 4 ++-- .../migrations/0012_auto_20200624_1332.py | 23 +++++++++++++++++++ ietf/person/models.py | 2 +- .../migrations/0005_auto_20200624_1332.py | 18 +++++++++++++++ ietf/submit/models.py | 2 +- 6 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 ietf/doc/migrations/0032_auto_20200624_1332.py create mode 100644 ietf/person/migrations/0012_auto_20200624_1332.py create mode 100644 ietf/submit/migrations/0005_auto_20200624_1332.py diff --git a/ietf/doc/migrations/0032_auto_20200624_1332.py b/ietf/doc/migrations/0032_auto_20200624_1332.py new file mode 100644 index 000000000..1dd656a31 --- /dev/null +++ b/ietf/doc/migrations/0032_auto_20200624_1332.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.15 on 2020-06-24 13:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('doc', '0031_set_state_for_charters_of_replaced_groups'), + ] + + operations = [ + migrations.AlterField( + model_name='ballotpositiondocevent', + name='send_email', + field=models.BooleanField(default=None, null=True), + ), + migrations.AlterField( + model_name='consensusdocevent', + name='consensus', + field=models.BooleanField(default=None, null=True), + ), + ] diff --git a/ietf/doc/models.py b/ietf/doc/models.py index dc92d356d..ea3f80f72 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -1073,7 +1073,7 @@ class StateDocEvent(DocEvent): state = ForeignKey(State, blank=True, null=True) class ConsensusDocEvent(DocEvent): - consensus = models.NullBooleanField(default=None) + consensus = models.BooleanField(null=True, default=None) # IESG events class BallotType(models.Model): @@ -1164,7 +1164,7 @@ class BallotPositionDocEvent(DocEvent): discuss_time = models.DateTimeField(help_text="Time discuss text was written", blank=True, null=True) comment = models.TextField(help_text="Optional comment", blank=True) comment_time = models.DateTimeField(help_text="Time optional comment was written", blank=True, null=True) - send_email = models.NullBooleanField(default=None) + send_email = models.BooleanField(null=True, default=None) @memoize def any_email_sent(self): diff --git a/ietf/person/migrations/0012_auto_20200624_1332.py b/ietf/person/migrations/0012_auto_20200624_1332.py new file mode 100644 index 000000000..bba2ff621 --- /dev/null +++ b/ietf/person/migrations/0012_auto_20200624_1332.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.15 on 2020-06-24 13:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('person', '0011_auto_20200608_1212'), + ] + + operations = [ + migrations.AlterField( + model_name='historicalperson', + name='consent', + field=models.BooleanField(default=None, null=True, verbose_name='I hereby give my consent to the use of the personal details I have provided (photo, bio, name, email) within the IETF Datatracker'), + ), + migrations.AlterField( + model_name='person', + name='consent', + field=models.BooleanField(default=None, null=True, verbose_name='I hereby give my consent to the use of the personal details I have provided (photo, bio, name, email) within the IETF Datatracker'), + ), + ] diff --git a/ietf/person/models.py b/ietf/person/models.py index d3315ad5a..3d41e7fb7 100644 --- a/ietf/person/models.py +++ b/ietf/person/models.py @@ -48,7 +48,7 @@ class Person(models.Model): photo = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None) photo_thumb = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None) name_from_draft = models.CharField("Full Name (from submission)", null=True, max_length=255, editable=False, help_text="Name as found in a draft submission.") - consent = models.NullBooleanField("I hereby give my consent to the use of the personal details I have provided (photo, bio, name, email) within the IETF Datatracker", null=True, default=None) + consent = models.BooleanField("I hereby give my consent to the use of the personal details I have provided (photo, bio, name, email) within the IETF Datatracker", null=True, default=None) def __str__(self): return self.plain_name() diff --git a/ietf/submit/migrations/0005_auto_20200624_1332.py b/ietf/submit/migrations/0005_auto_20200624_1332.py new file mode 100644 index 000000000..74a5fa9fb --- /dev/null +++ b/ietf/submit/migrations/0005_auto_20200624_1332.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.15 on 2020-06-24 13:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submit', '0004_rename_field_document2'), + ] + + operations = [ + migrations.AlterField( + model_name='submissioncheck', + name='passed', + field=models.BooleanField(default=False, null=True), + ), + ] diff --git a/ietf/submit/models.py b/ietf/submit/models.py index 5ec16f7ef..ae21aec92 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -78,7 +78,7 @@ class SubmissionCheck(models.Model): time = models.DateTimeField(default=datetime.datetime.now) submission = ForeignKey(Submission, related_name='checks') checker = models.CharField(max_length=256, blank=True) - passed = models.NullBooleanField(default=False) + passed = models.BooleanField(null=True, default=False) message = models.TextField(null=True, blank=True) errors = models.IntegerField(null=True, blank=True, default=None) warnings = models.IntegerField(null=True, blank=True, default=None) From 7c7c5da39f8222576b1295ae222a3527150ca855 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:36:46 +0000 Subject: [PATCH 2/6] Changed the 2.1 removed django.contrib.auth.views.logout to LogoutView. - Legacy-Id: 18052 --- ietf/ietfauth/tests.py | 5 ++--- ietf/ietfauth/urls.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py index f6e6319d3..ab8375f17 100644 --- a/ietf/ietfauth/tests.py +++ b/ietf/ietfauth/tests.py @@ -24,7 +24,6 @@ from pyquery import PyQuery from unittest import skipIf from urllib.parse import urlsplit -import django.contrib.auth.views from django.urls import reverse as urlreverse from django.contrib.auth.models import User from django.conf import settings @@ -91,7 +90,7 @@ class IetfAuthTests(TestCase): self.assertEqual(urlsplit(r["Location"])[2], urlreverse(ietf.ietfauth.views.profile)) # try logging out - r = self.client.get(urlreverse(django.contrib.auth.views.logout)) + r = self.client.get(urlreverse('django.contrib.auth.views.logout')) self.assertEqual(r.status_code, 200) r = self.client.get(urlreverse(ietf.ietfauth.views.profile)) @@ -198,7 +197,7 @@ class IetfAuthTests(TestCase): self.assertContains(r, "Whitelist entry creation successful") # log out - r = self.client.get(urlreverse(django.contrib.auth.views.logout)) + r = self.client.get(urlreverse('django.contrib.auth.views.logout')) self.assertEqual(r.status_code, 200) # register and verify whitelisted email diff --git a/ietf/ietfauth/urls.py b/ietf/ietfauth/urls.py index d1b206c07..f31255542 100644 --- a/ietf/ietfauth/urls.py +++ b/ietf/ietfauth/urls.py @@ -1,7 +1,7 @@ # Copyright The IETF Trust 2007-2019, All Rights Reserved # Copyright The IETF Trust 2007, 2009, All Rights Reserved -from django.contrib.auth.views import logout # type: ignore +from django.contrib.auth.views import LogoutView # type: ignore from ietf.ietfauth import views from ietf.utils.urls import url @@ -15,7 +15,7 @@ urlpatterns = [ url(r'^create/$', views.create_account), url(r'^create/confirm/(?P[^/]+)/$', views.confirm_account), url(r'^login/$', views.login), - url(r'^logout/$', logout), + url(r'^logout/$', LogoutView.as_view(), name="django.contrib.auth.views.logout"), url(r'^password/$', views.change_password), url(r'^profile/$', views.profile), url(r'^reset/$', views.password_reset), From 06fa5e517aa4b614d7e35e972218f64b20bb36fa Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:39:27 +0000 Subject: [PATCH 3/6] Adjusted the code to the changed signature of admin.utils.get_deleted_objects() - Legacy-Id: 18053 --- ietf/person/tests.py | 8 +++++++- ietf/person/utils.py | 13 +++---------- ietf/person/views.py | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ietf/person/tests.py b/ietf/person/tests.py index f0c7196b6..61089eee5 100644 --- a/ietf/person/tests.py +++ b/ietf/person/tests.py @@ -6,6 +6,8 @@ import datetime from pyquery import PyQuery from io import StringIO + +from django.http import HttpRequest from django.urls import reverse as urlreverse from django.utils.encoding import iri_to_uri @@ -229,13 +231,17 @@ class PersonUtilsTests(TestCase): self.assertTrue(source_alias in target.alias_set.all()) def test_merge_persons(self): + secretariat_role = RoleFactory(group__acronym='secretariat', name_id='secr') + user = secretariat_role.person.user + request = HttpRequest() + request.user = user source = PersonFactory() target = PersonFactory() source_id = source.pk source_email = source.email_set.first() source_alias = source.alias_set.first() source_user = source.user - merge_persons(source, target, file=StringIO()) + merge_persons(request, source, target, file=StringIO()) self.assertTrue(source_email in target.email_set.all()) self.assertTrue(source_alias in target.alias_set.all()) self.assertFalse(Person.objects.filter(id=source_id)) diff --git a/ietf/person/utils.py b/ietf/person/utils.py index 8afe79791..b19931813 100755 --- a/ietf/person/utils.py +++ b/ietf/person/utils.py @@ -9,7 +9,6 @@ import sys import syslog from django.contrib import admin -from django.contrib.auth.models import User from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist @@ -18,7 +17,7 @@ import debug # pyflakes:ignore from ietf.person.models import Person from ietf.utils.mail import send_mail -def merge_persons(source, target, file=sys.stdout, verbose=False): +def merge_persons(request, source, target, file=sys.stdout, verbose=False): changes = [] # write log @@ -44,12 +43,8 @@ def merge_persons(source, target, file=sys.stdout, verbose=False): # check for any remaining relationships and exit if more found objs = [source] - opts = Person._meta - user = User.objects.filter(is_superuser=True).first() - admin_site = admin.site - using = 'default' - deletable_objects = admin.utils.get_deleted_objects( - objs, opts, user, admin_site, using) +# request.user = User.objects.filter(is_superuser=True).first() + deletable_objects = admin.utils.get_deleted_objects(objs, request, admin.site) deletable_objects_summary = deletable_objects[1] if len(deletable_objects_summary) > 1: # should only inlcude one object (Person) print("Not Deleting Person: {}({})".format(source.ascii,source.pk), file=file) @@ -194,7 +189,6 @@ def get_active_balloters(ballot_type): return active_balloters def get_active_ads(): - from ietf.person.models import Person cache_key = "doc:active_ads" active_ads = cache.get(cache_key) if not active_ads: @@ -203,7 +197,6 @@ def get_active_ads(): return active_ads def get_active_irsg(): - from ietf.person.models import Person cache_key = "doc:active_irsg_balloters" active_irsg_balloters = cache.get(cache_key) if not active_irsg_balloters: diff --git a/ietf/person/views.py b/ietf/person/views.py index dade5c1ff..0fb967743 100644 --- a/ietf/person/views.py +++ b/ietf/person/views.py @@ -111,7 +111,7 @@ def merge(request): target = form.cleaned_data.get('target') # Do merge with force output = StringIO() - success, changes = merge_persons(source, target, file=output) + success, changes = merge_persons(request, source, target, file=output) if success: messages.success(request, 'Merged {} ({}) to {} ({}). {})'.format( source.name, source_id, target.name, target.id, changes)) From 6459638fad9f1bb98c309fa5cff72386f5bbc956 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:42:05 +0000 Subject: [PATCH 4/6] Adjusted the code to the 2.1 attribute name change from ._size to .size in upload file objects. - Legacy-Id: 18054 --- ietf/utils/validators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/utils/validators.py b/ietf/utils/validators.py index fba46e564..b38127154 100644 --- a/ietf/utils/validators.py +++ b/ietf/utils/validators.py @@ -55,8 +55,8 @@ class RegexStringValidator(object): validate_regular_expression_string = RegexStringValidator() def validate_file_size(file): - if file._size > settings.SECR_MAX_UPLOAD_SIZE: - raise ValidationError('Please keep filesize under %s. Requested upload size was %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE), filesizeformat(file._size))) + if file.size > settings.SECR_MAX_UPLOAD_SIZE: + raise ValidationError('Please keep filesize under %s. Requested upload size was %s' % (filesizeformat(settings.SECR_MAX_UPLOAD_SIZE), filesizeformat(file.size))) def validate_mime_type(file, valid): file.open() From a9348f129ad54e12af449a50cc80023496acd1b2 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:42:39 +0000 Subject: [PATCH 5/6] Updated requirements for Django 2.1 - Legacy-Id: 18055 --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index c071785da..570195120 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ coverage>=4.0.1,!=4.0.2,<5.0 # Coverage 5.x moves from a json database to SQL #cssselect>=0.6.1 # for PyQuery decorator>=4.0.4 defusedxml>=0.4.1 # for TastyPie when ussing xml; not a declared dependency -Django>=2.0,<2.1 +Django>=2.1,<2.2 django-bootstrap3>=9.1.0,<14.0 # 14.0 requires Django 2.2 django-csp>=3.5 django-cors-headers>=2.4.0 @@ -22,12 +22,12 @@ django-referrer-policy>=1.0 django-request-profiler>=0.15 # 0.15 and above requires Django 2.x django-simple-history>=2.3.0 django-stubs==1.3.0 # The django-stubs version used determines the the mypy version indicated below -django-tastypie>=0.14.1 # For Django 2.0. Django 2.1 will require 0.14.2; Django 3.0 will require 0.14.3 +django-tastypie>=0.14.2 # Django 2.1 will require 0.14.2; Django 3.0 will require 0.14.3 django-webtest>=1.9.7 django-widget-tweaks>=1.4.2 docutils>=0.12,!=0.15 factory-boy>=2.9.0 -Faker>=0.8.8,!=0.8.9,!=0.8.10 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. +Faker>=0.8.8,!=0.8.9,!=0.8.10 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. hashids>=1.1.0 html2text>=2019.8.11 html5lib>=1.0.1 From 27da6e86d4e3c9a89bc3b3b3927714c7e00cb6c2 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Wed, 24 Jun 2020 20:44:47 +0000 Subject: [PATCH 6/6] Widgets' .render() method now must accept an additional keyword argument (renderer); adjusted the code accordingly (including a library patch -- pull request also submitted). - Legacy-Id: 18056 --- ietf/liaisons/widgets.py | 4 +-- .../fix-django-password-strength-kwargs.patch | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 patch/fix-django-password-strength-kwargs.patch diff --git a/ietf/liaisons/widgets.py b/ietf/liaisons/widgets.py index dde4d168e..9581679b2 100644 --- a/ietf/liaisons/widgets.py +++ b/ietf/liaisons/widgets.py @@ -17,7 +17,7 @@ class ButtonWidget(Widget): self.required_label = kwargs.pop('required_label', None) super(ButtonWidget, self).__init__(*args, **kwargs) - def render(self, name, value, attrs=None): + def render(self, name, value, **kwargs): html = '' % conditional_escape(self.show_on) html += '' % conditional_escape(self.label) if self.require: @@ -30,7 +30,7 @@ class ButtonWidget(Widget): class ShowAttachmentsWidget(Widget): - def render(self, name, value, attrs=None): + def render(self, name, value, **kwargs): html = '
' % name html += '' html += '
' diff --git a/patch/fix-django-password-strength-kwargs.patch b/patch/fix-django-password-strength-kwargs.patch new file mode 100644 index 000000000..9f24ce932 --- /dev/null +++ b/patch/fix-django-password-strength-kwargs.patch @@ -0,0 +1,36 @@ +--- django_password_strength/widgets.py.orig 2020-06-24 16:07:28.479533134 +0200 ++++ django_password_strength/widgets.py 2020-06-24 16:08:09.540714290 +0200 +@@ -8,7 +8,7 @@ + Form widget to show the user how strong his/her password is. + """ + +- def render(self, name, value, attrs=None): ++ def render(self, name, value, **kwargs): + strength_markup = """ +
+
+@@ -30,7 +30,7 @@ + except KeyError: + self.attrs['class'] = 'password_strength' + +- return mark_safe( super(PasswordInput, self).render(name, value, attrs) + strength_markup ) ++ return mark_safe( super(PasswordInput, self).render(name, value, **kwargs) + strength_markup ) + + class Media: + js = ( +@@ -48,7 +48,7 @@ + super(PasswordConfirmationInput, self).__init__(attrs, render_value) + self.confirm_with=confirm_with + +- def render(self, name, value, attrs=None): ++ def render(self, name, value, **kwargs): + if self.confirm_with: + self.attrs['data-confirm-with'] = 'id_%s' % self.confirm_with + +@@ -68,4 +68,4 @@ + except KeyError: + self.attrs['class'] = 'password_confirmation' + +- return mark_safe( super(PasswordInput, self).render(name, value, attrs) + confirmation_markup ) ++ return mark_safe( super(PasswordInput, self).render(name, value, **kwargs) + confirmation_markup ) +