Merge pull request #5601 from jennifer-richards/django4

chore: Upgrade to Django 3.0
This commit is contained in:
Jennifer Richards 2023-05-11 11:04:36 -04:00 committed by GitHub
commit 264ff60cd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 200 additions and 178 deletions

View file

@ -10,7 +10,7 @@ DATABASES = {
'HOST': '__DBHOST__', 'HOST': '__DBHOST__',
'PORT': 5432, 'PORT': 5432,
'NAME': 'datatracker', 'NAME': 'datatracker',
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'USER': 'django', 'USER': 'django',
'PASSWORD': 'RkTkDPFnKpko', 'PASSWORD': 'RkTkDPFnKpko',
}, },

View file

@ -10,7 +10,7 @@ DATABASES = {
'HOST': '__DBHOST__', 'HOST': '__DBHOST__',
'PORT': 5432, 'PORT': 5432,
'NAME': 'datatracker', 'NAME': 'datatracker',
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'USER': 'django', 'USER': 'django',
'PASSWORD': 'RkTkDPFnKpko', 'PASSWORD': 'RkTkDPFnKpko',
}, },

View file

@ -10,7 +10,7 @@ DATABASES = {
'HOST': 'db', 'HOST': 'db',
'PORT': 5432, 'PORT': 5432,
'NAME': 'datatracker', 'NAME': 'datatracker',
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'USER': 'django', 'USER': 'django',
'PASSWORD': 'RkTkDPFnKpko', 'PASSWORD': 'RkTkDPFnKpko',
}, },

View file

@ -3,7 +3,7 @@ DATABASES = {
'HOST': 'db', 'HOST': 'db',
'PORT': 5432, 'PORT': 5432,
'NAME': 'datatracker', 'NAME': 'datatracker',
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'USER': 'django', 'USER': 'django',
'PASSWORD': 'RkTkDPFnKpko', 'PASSWORD': 'RkTkDPFnKpko',
}, },

View file

@ -9,7 +9,7 @@ from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist, FieldError from django.core.exceptions import ObjectDoesNotExist, FieldError
from django.core.serializers.json import Serializer from django.core.serializers.json import Serializer
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
from django.db.models import Field from django.db.models import Field
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.db.models.signals import post_save, post_delete, m2m_changed from django.db.models.signals import post_save, post_delete, m2m_changed
@ -121,7 +121,7 @@ class AdminJsonSerializer(Serializer):
for name in expansions: for name in expansions:
try: try:
field = getattr(obj, name) field = getattr(obj, name)
#self._current["_"+name] = smart_text(field) #self._current["_"+name] = smart_str(field)
if not isinstance(field, Field): if not isinstance(field, Field):
options = self.options.copy() options = self.options.copy()
options["expand"] = [ v[len(name)+2:] for v in options["expand"] if v.startswith(name+"__") ] options["expand"] = [ v[len(name)+2:] for v in options["expand"] if v.startswith(name+"__") ]
@ -188,10 +188,10 @@ class AdminJsonSerializer(Serializer):
related = related.natural_key() related = related.natural_key()
elif field.remote_field.field_name == related._meta.pk.name: elif field.remote_field.field_name == related._meta.pk.name:
# Related to remote object via primary key # Related to remote object via primary key
related = smart_text(related._get_pk_val(), strings_only=True) related = smart_str(related._get_pk_val(), strings_only=True)
else: else:
# Related to remote object via other field # Related to remote object via other field
related = smart_text(getattr(related, field.remote_field.field_name), strings_only=True) related = smart_str(getattr(related, field.remote_field.field_name), strings_only=True)
self._current[field.name] = related self._current[field.name] = related
def handle_m2m_field(self, obj, field): def handle_m2m_field(self, obj, field):
@ -201,7 +201,7 @@ class AdminJsonSerializer(Serializer):
elif self.use_natural_keys and hasattr(field.remote_field.to, 'natural_key'): elif self.use_natural_keys and hasattr(field.remote_field.to, 'natural_key'):
m2m_value = lambda value: value.natural_key() m2m_value = lambda value: value.natural_key()
else: else:
m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True) m2m_value = lambda value: smart_str(value._get_pk_val(), strings_only=True)
self._current[field.name] = [m2m_value(related) self._current[field.name] = [m2m_value(related)
for related in getattr(obj, field.name).iterator()] for related in getattr(obj, field.name).iterator()]
@ -221,7 +221,7 @@ class JsonExportMixin(object):
# obj = None # obj = None
# #
# if obj is None: # if obj is None:
# raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(self.model._meta.verbose_name), 'key': escape(object_id)}) # raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_str(self.model._meta.verbose_name), 'key': escape(object_id)})
# #
# content_type = 'application/json' # content_type = 'application/json'
# return HttpResponse(serialize([ obj ], sort_keys=True, indent=3)[2:-2], content_type=content_type) # return HttpResponse(serialize([ obj ], sort_keys=True, indent=3)[2:-2], content_type=content_type)
@ -264,6 +264,6 @@ class JsonExportMixin(object):
qd = dict( ( k, json.loads(v)[0] ) for k,v in items ) qd = dict( ( k, json.loads(v)[0] ) for k,v in items )
except (FieldError, ValueError) as e: except (FieldError, ValueError) as e:
return HttpResponse(json.dumps({"error": str(e)}, sort_keys=True, indent=3), content_type=content_type) return HttpResponse(json.dumps({"error": str(e)}, sort_keys=True, indent=3), content_type=content_type)
text = json.dumps({smart_text(self.model._meta): qd}, sort_keys=True, indent=3) text = json.dumps({smart_str(self.model._meta): qd}, sort_keys=True, indent=3)
return HttpResponse(text, content_type=content_type) return HttpResponse(text, content_type=content_type)

View file

@ -11,7 +11,7 @@ from django.utils.html import strip_tags
from django.conf import settings from django.conf import settings
from django.urls import reverse as urlreverse from django.urls import reverse as urlreverse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
from ietf.doc.templatetags.mail_filters import std_level_prompt from ietf.doc.templatetags.mail_filters import std_level_prompt
@ -175,7 +175,7 @@ def generate_ballot_writeup(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "Ballot writeup was generated" e.desc = "Ballot writeup was generated"
e.text = force_text(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana, 'doc': doc })) e.text = force_str(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana, 'doc': doc }))
# caller is responsible for saving, if necessary # caller is responsible for saving, if necessary
return e return e
@ -187,7 +187,7 @@ def generate_ballot_rfceditornote(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "RFC Editor Note for ballot was generated" e.desc = "RFC Editor Note for ballot was generated"
e.text = force_text(render_to_string("doc/mail/ballot_rfceditornote.txt")) e.text = force_str(render_to_string("doc/mail/ballot_rfceditornote.txt"))
e.save() e.save()
return e return e
@ -232,7 +232,7 @@ def generate_last_call_announcement(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "Last call announcement was generated" e.desc = "Last call announcement was generated"
e.text = force_text(mail) e.text = force_str(mail)
# caller is responsible for saving, if necessary # caller is responsible for saving, if necessary
return e return e
@ -252,7 +252,7 @@ def generate_approval_mail(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "Ballot approval text was generated" e.desc = "Ballot approval text was generated"
e.text = force_text(mail) e.text = force_str(mail)
# caller is responsible for saving, if necessary # caller is responsible for saving, if necessary
return e return e

View file

@ -23,7 +23,7 @@ from django.urls import reverse as urlreverse
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import mark_safe # type:ignore from django.utils.html import mark_safe # type:ignore
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
@ -1131,7 +1131,7 @@ class DocHistory(DocumentInfo):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
def __str__(self): def __str__(self):
return force_text(self.doc.name) return force_str(self.doc.name)
def get_related_session(self): def get_related_session(self):
return self.doc.get_related_session() return self.doc.get_related_session()
@ -1193,7 +1193,7 @@ class DocAlias(models.Model):
return self.docs.first() return self.docs.first()
def __str__(self): def __str__(self):
return u"%s-->%s" % (self.name, ','.join([force_text(d.name) for d in self.docs.all() if isinstance(d, Document) ])) return u"%s-->%s" % (self.name, ','.join([force_str(d.name) for d in self.docs.all() if isinstance(d, Document) ]))
document_link = admin_link("document") document_link = admin_link("document")
class Meta: class Meta:
verbose_name = "document alias" verbose_name = "document alias"

View file

@ -13,8 +13,7 @@ from django.utils.html import escape
from django.template.defaultfilters import truncatewords_html, linebreaksbr, stringfilter, striptags from django.template.defaultfilters import truncatewords_html, linebreaksbr, stringfilter, striptags
from django.utils.safestring import mark_safe, SafeData from django.utils.safestring import mark_safe, SafeData
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.encoding import force_str # pyflakes:ignore force_str is used in the doctests
from django.urls import reverse as urlreverse from django.urls import reverse as urlreverse
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -132,7 +131,7 @@ register.filter('fill', fill)
@register.filter @register.filter
def prettystdname(string, space=" "): def prettystdname(string, space=" "):
from ietf.doc.utils import prettify_std_name from ietf.doc.utils import prettify_std_name
return prettify_std_name(force_text(string or ""), space) return prettify_std_name(force_str(string or ""), space)
@register.filter @register.filter
def rfceditor_info_url(rfcnum : str): def rfceditor_info_url(rfcnum : str):

View file

@ -12,7 +12,7 @@ from django.conf import settings
from django.urls import reverse as urlreverse from django.urls import reverse as urlreverse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import smart_text, force_text from django.utils.encoding import smart_str, force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -153,7 +153,7 @@ def generate_ballot_writeup(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev, e.rev = doc.rev,
e.desc = "Ballot writeup was generated" e.desc = "Ballot writeup was generated"
e.text = force_text(render_to_string("doc/charter/ballot_writeup.txt")) e.text = force_str(render_to_string("doc/charter/ballot_writeup.txt"))
# caller is responsible for saving, if necessary # caller is responsible for saving, if necessary
return e return e
@ -197,7 +197,7 @@ def derive_new_work_text(review_text,group):
'Reply_to':'<iesg@ietf.org>'}) 'Reply_to':'<iesg@ietf.org>'})
if not addrs.cc: if not addrs.cc:
del m['Cc'] del m['Cc']
return smart_text(m.as_string()) return smart_str(m.as_string())
def default_review_text(group, charter, by): def default_review_text(group, charter, by):
now = timezone.now() now = timezone.now()

View file

@ -17,7 +17,7 @@ from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import escape from django.utils.html import escape
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -821,7 +821,7 @@ def charter_with_milestones_txt(request, name, rev):
try: try:
with io.open(os.path.join(settings.CHARTER_PATH, filename), 'r') as f: with io.open(os.path.join(settings.CHARTER_PATH, filename), 'r') as f:
charter_text = force_text(f.read(), errors='ignore') charter_text = force_str(f.read(), errors='ignore')
except IOError: except IOError:
charter_text = "Error reading charter text %s" % filename charter_text = "Error reading charter text %s" % filename

View file

@ -15,7 +15,7 @@ from django.http import Http404, HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.conf import settings from django.conf import settings
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import escape from django.utils.html import escape
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -531,7 +531,7 @@ def rfc_status_changes(request):
) )
@role_required("Area Director","Secretariat") @role_required("Area Director","Secretariat")
def start_rfc_status_change(request,name): def start_rfc_status_change(request, name=None):
"""Start the RFC status change review process, setting the initial shepherding AD, and possibly putting the review on a telechat.""" """Start the RFC status change review process, setting the initial shepherding AD, and possibly putting the review on a telechat."""
if name: if name:
@ -665,7 +665,7 @@ def generate_last_call_text(request, doc):
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = 'Last call announcement was generated' e.desc = 'Last call announcement was generated'
e.text = force_text(new_text) e.text = force_str(new_text)
e.save() e.save()
return e return e

View file

@ -14,7 +14,7 @@ from django.contrib.admin.utils import unquote
from django.core.management import load_command_class from django.core.management import load_command_class
from django.http import Http404 from django.http import Http404
from django.shortcuts import render from django.shortcuts import render
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -152,7 +152,7 @@ class GroupAdmin(admin.ModelAdmin):
permission_denied(request, "You don't have edit permissions for this change.") permission_denied(request, "You don't have edit permissions for this change.")
if obj is None: if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)}) raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_str(opts.verbose_name), 'key': escape(object_id)})
return self.send_reminder(request, sdo=obj) return self.send_reminder(request, sdo=obj)

View file

@ -300,8 +300,27 @@ def active_groups(request, group_type=None):
raise Http404 raise Http404
def active_group_types(request): def active_group_types(request):
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','rag','team','dir','review','area','program','iabasg','adm']).filter(group__state='active').annotate(group_count=Count('group')) grouptypes = (
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes}) GroupTypeName.objects.filter(
slug__in=[
"wg",
"rg",
"ag",
"rag",
"team",
"dir",
"review",
"area",
"program",
"iabasg",
"adm",
]
)
.filter(group__state="active")
.order_by('order', 'name') # default ordering ignored for "GROUP BY" queries, make it explicit
.annotate(group_count=Count("group"))
)
return render(request, "group/active_groups.html", {"grouptypes": grouptypes})
def active_dirs(request): def active_dirs(request):
dirs = Group.objects.filter(type__in=['dir', 'review'], state="active").order_by("name") dirs = Group.objects.filter(type__in=['dir', 'review'], state="active").order_by("name")

View file

@ -7,7 +7,7 @@
import oidc_provider.lib.claims import oidc_provider.lib.claims
from functools import wraps from functools import wraps, WRAPPER_ASSIGNMENTS
from django.conf import settings from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
@ -15,7 +15,6 @@ from django.core.exceptions import PermissionDenied
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.decorators import available_attrs
from django.utils.http import urlquote from django.utils.http import urlquote
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -113,7 +112,7 @@ def passes_test_decorator(test_func, message):
error. The test function should be on the form fn(user) -> error. The test function should be on the form fn(user) ->
true/false.""" true/false."""
def decorate(view_func): def decorate(view_func):
@wraps(view_func, assigned=available_attrs(view_func)) @wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)
def inner(request, *args, **kwargs): def inner(request, *args, **kwargs):
if not request.user.is_authenticated: if not request.user.is_authenticated:
return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path()))) return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path())))

View file

@ -6,7 +6,7 @@ from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed from django.utils.feedgenerator import Atom1Feed
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.encoding import force_text from django.utils.encoding import force_str
from ietf.ipr.models import IprDisclosureBase from ietf.ipr.models import IprDisclosureBase
@ -25,7 +25,7 @@ class LatestIprDisclosuresFeed(Feed):
return mark_safe(item.title) return mark_safe(item.title)
def item_description(self, item): def item_description(self, item):
return force_text(item.title) return force_str(item.title)
def item_pubdate(self, item): def item_pubdate(self, item):
return item.time return item.time

View file

@ -12,7 +12,7 @@ from email import message_from_bytes
from email.utils import parsedate_tz from email.utils import parsedate_tz
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.encoding import force_text, force_bytes from django.utils.encoding import force_str, force_bytes
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -102,7 +102,7 @@ def get_reply_to():
address with "plus addressing" using a random string. Guaranteed to be unique""" address with "plus addressing" using a random string. Guaranteed to be unique"""
local,domain = get_base_ipr_request_address().split('@') local,domain = get_base_ipr_request_address().split('@')
while True: while True:
rand = force_text(base64.urlsafe_b64encode(os.urandom(12))) rand = force_str(base64.urlsafe_b64encode(os.urandom(12)))
address = "{}+{}@{}".format(local,rand,domain) address = "{}+{}@{}".format(local,rand,domain)
q = Message.objects.filter(reply_to=address) q = Message.objects.filter(reply_to=address)
if not q: if not q:

View file

@ -9,7 +9,6 @@ import operator
from typing import Union # pyflakes:ignore from typing import Union # pyflakes:ignore
from email.utils import parseaddr from email.utils import parseaddr
from form_utils.forms import BetterModelForm
from django import forms from django import forms
from django.conf import settings from django.conf import settings
@ -132,7 +131,7 @@ class AddCommentForm(forms.Form):
# def render(self): # def render(self):
# output = [] # output = []
# for widget in self: # for widget in self:
# output.append(format_html(force_text(widget))) # output.append(format_html(force_str(widget)))
# return mark_safe('\n'.join(output)) # return mark_safe('\n'.join(output))
@ -213,7 +212,7 @@ class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField):
return super(CustomModelMultipleChoiceField, self).prepare_value(value) return super(CustomModelMultipleChoiceField, self).prepare_value(value)
class LiaisonModelForm(BetterModelForm): class LiaisonModelForm(forms.ModelForm):
'''Specify fields which require a custom widget or that are not part of the model. '''Specify fields which require a custom widget or that are not part of the model.
''' '''
from_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),label='Groups',required=False) from_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),label='Groups',required=False)
@ -238,13 +237,6 @@ class LiaisonModelForm(BetterModelForm):
class Meta: class Meta:
model = LiaisonStatement model = LiaisonStatement
exclude = ('attachments','state','from_name','to_name') exclude = ('attachments','state','from_name','to_name')
fieldsets = [('From', {'fields': ['from_groups','from_contact', 'response_contacts'], 'legend': ''}),
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
('Other email addresses', {'fields': ['technical_contacts','action_holder_contacts','cc_contacts'], 'legend': ''}),
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
def __init__(self, user, *args, **kwargs): def __init__(self, user, *args, **kwargs):
super(LiaisonModelForm, self).__init__(*args, **kwargs) super(LiaisonModelForm, self).__init__(*args, **kwargs)
@ -476,14 +468,6 @@ class OutgoingLiaisonForm(LiaisonModelForm):
class Meta: class Meta:
model = LiaisonStatement model = LiaisonStatement
exclude = ('attachments','state','from_name','to_name','action_holder_contacts') exclude = ('attachments','state','from_name','to_name','action_holder_contacts')
# add approved field, no action_holder_contacts
fieldsets = [('From', {'fields': ['from_groups','from_contact','response_contacts','approved'], 'legend': ''}),
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
('Other email addresses', {'fields': ['technical_contacts','cc_contacts'], 'legend': ''}),
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
def is_approved(self): def is_approved(self):
return self.cleaned_data['approved'] return self.cleaned_data['approved']

View file

@ -15,7 +15,7 @@ from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -699,7 +699,7 @@ def handle_upload_file(file, filename, meeting, subdir, request=None, encoding=N
) )
else: else:
try: try:
text = smart_text(text) text = smart_str(text)
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
return "Failure trying to save '%s'. Hint: Try to upload as UTF-8: %s..." % (filename, str(e)[:120]) return "Failure trying to save '%s'. Hint: Try to upload as UTF-8: %s..." % (filename, str(e)[:120])
# Whole file sanitization; add back what's missing from a complete # Whole file sanitization; add back what's missing from a complete

View file

@ -17,6 +17,7 @@ import tempfile
from calendar import timegm from calendar import timegm
from collections import OrderedDict, Counter, deque, defaultdict, namedtuple from collections import OrderedDict, Counter, deque, defaultdict, namedtuple
from functools import partialmethod
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit
from tempfile import mkstemp from tempfile import mkstemp
from wsgiref.handlers import format_date_time from wsgiref.handlers import format_date_time
@ -38,7 +39,6 @@ from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.functional import curry
from django.utils.text import slugify from django.utils.text import slugify
from django.views.decorators.cache import cache_page from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt
@ -2730,7 +2730,7 @@ def upload_session_agenda(request, session_id, num):
}) })
def upload_session_slides(request, session_id, num, name): def upload_session_slides(request, session_id, num, name=None):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure # num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id) session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user): if not session.can_manage_materials(request.user):
@ -3210,8 +3210,8 @@ def interim_request(request):
if meeting_type in ('single', 'multi-day'): if meeting_type in ('single', 'multi-day'):
meeting = form.save(date=get_earliest_session_date(formset)) meeting = form.save(date=get_earliest_session_date(formset))
# need to use curry here to pass custom variable to form init # need to use partialmethod here to pass custom variable to form init
SessionFormset.form.__init__ = curry( SessionFormset.form.__init__ = partialmethod(
InterimSessionModelForm.__init__, InterimSessionModelForm.__init__,
user=request.user, user=request.user,
group=group, group=group,
@ -3233,7 +3233,7 @@ def interim_request(request):
# subsequently dealt with individually # subsequently dealt with individually
elif meeting_type == 'series': elif meeting_type == 'series':
series = [] series = []
SessionFormset.form.__init__ = curry( SessionFormset.form.__init__ = partialmethod(
InterimSessionModelForm.__init__, InterimSessionModelForm.__init__,
user=request.user, user=request.user,
group=group, group=group,
@ -3453,7 +3453,7 @@ def interim_request_edit(request, number):
group = Group.objects.get(pk=form.data['group']) group = Group.objects.get(pk=form.data['group'])
is_approved = is_interim_meeting_approved(meeting) is_approved = is_interim_meeting_approved(meeting)
SessionFormset.form.__init__ = curry( SessionFormset.form.__init__ = partialmethod(
InterimSessionModelForm.__init__, InterimSessionModelForm.__init__,
user=request.user, user=request.user,
group=group, group=group,

View file

@ -6,7 +6,7 @@ import re
from django import template from django import template
from django.conf import settings from django.conf import settings
from django.template.defaultfilters import linebreaksbr, force_escape from django.template.defaultfilters import linebreaksbr, force_escape
from django.utils.encoding import force_text, DjangoUnicodeDecodeError from django.utils.encoding import force_str, DjangoUnicodeDecodeError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -68,7 +68,7 @@ def decrypt(string, request, year, plain=False):
code, out, error = pipe(command % (settings.OPENSSL_COMMAND, code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
encrypted_file.name), key) encrypted_file.name), key)
try: try:
out = force_text(out) out = force_str(out)
except DjangoUnicodeDecodeError: except DjangoUnicodeDecodeError:
pass pass
if code != 0: if code != 0:

View file

@ -18,7 +18,7 @@ from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_str
from ietf.dbtemplate.models import DBTemplate from ietf.dbtemplate.models import DBTemplate
@ -684,7 +684,7 @@ def private_questionnaire(request, year):
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, 'The questionnaire response has been registered.') messages.success(request, 'The questionnaire response has been registered.')
questionnaire_response = force_text(form.cleaned_data['comment_text']) questionnaire_response = force_str(form.cleaned_data['comment_text'])
form = QuestionnaireForm(nomcom=nomcom, user=request.user) form = QuestionnaireForm(nomcom=nomcom, user=request.user)
else: else:
form = QuestionnaireForm(nomcom=nomcom, user=request.user) form = QuestionnaireForm(nomcom=nomcom, user=request.user)

View file

@ -16,7 +16,7 @@ from unicodedata import normalize
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.encoding import force_text from django.utils.encoding import force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -68,7 +68,7 @@ class PersonFactory(factory.django.DjangoModelFactory):
# Some i18n names, e.g., "शिला के.सी." have a dot at the end that is also part of the ASCII, e.g., "Shilaa Kesii." # Some i18n names, e.g., "शिला के.सी." have a dot at the end that is also part of the ASCII, e.g., "Shilaa Kesii."
# That trailing dot breaks extract_authors(). Avoid this issue by stripping the dot from the ASCII. # That trailing dot breaks extract_authors(). Avoid this issue by stripping the dot from the ASCII.
# Some others have a trailing semicolon (e.g., "உயிரோவியம் தங்கராஐ;") - strip those, too. # Some others have a trailing semicolon (e.g., "உயிரோவியம் தங்கராஐ;") - strip those, too.
ascii = factory.LazyAttribute(lambda p: force_text(unidecode_name(p.name)).rstrip(".;")) ascii = factory.LazyAttribute(lambda p: force_str(unidecode_name(p.name)).rstrip(".;"))
class Params: class Params:
with_bio = factory.Trait(biography = "\n\n".join(fake.paragraphs())) # type: ignore with_bio = factory.Trait(biography = "\n\n".join(fake.paragraphs())) # type: ignore

View file

@ -4,10 +4,11 @@
import datetime import datetime
from functools import partialmethod
from django.contrib import messages from django.contrib import messages
from django.forms.formsets import formset_factory from django.forms.formsets import formset_factory
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.utils.functional import curry
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -215,7 +216,7 @@ def doc_detail(request, date, name):
initial_state = {'state':doc.get_state(state_type).pk, initial_state = {'state':doc.get_state(state_type).pk,
'substate':tag} 'substate':tag}
# need to use curry here to pass custom variable to form init # need to use partialmethod here to pass custom variable to form init
if doc.active_ballot(): if doc.active_ballot():
ballot_type = doc.active_ballot().ballot_type ballot_type = doc.active_ballot().ballot_type
elif doc.type.slug == 'draft': elif doc.type.slug == 'draft':
@ -223,7 +224,7 @@ def doc_detail(request, date, name):
else: else:
ballot_type = BallotType.objects.get(doc_type=doc.type) ballot_type = BallotType.objects.get(doc_type=doc.type)
BallotFormset = formset_factory(BallotForm, extra=0) BallotFormset = formset_factory(BallotForm, extra=0)
BallotFormset.form.__init__ = curry(BallotForm.__init__, ballot_type=ballot_type) BallotFormset.form.__init__ = partialmethod(BallotForm.__init__, ballot_type=ballot_type)
agenda = agenda_data(date=date) agenda = agenda_data(date=date)
header = get_section_header(doc, agenda) header = get_section_header(doc, agenda)

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Announcement{% endblock %} {% block title %}Announcement{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Areas{% endblock %} {% block title %}Areas{% endblock %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Areas - People{% endblock %} {% block title %}Areas - People{% endblock %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Areas - View{% endblock %} {% block title %}Areas - View{% endblock %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
{% load staticfiles %} {% load static %}
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
{% load staticfiles %} {% load static %}
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">

View file

@ -1,7 +1,7 @@
{% extends "base_secr.html" %} {% extends "base_secr.html" %}
{% load i18n %} {% load i18n %}
{% load ietf_filters %} {% load ietf_filters %}
{% load staticfiles %} {% load static %}
{% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} IETF Dashboard {% endif %}{% endblock %} {% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} IETF Dashboard {% endif %}{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "base_secr_bootstrap.html" %} {% extends "base_secr_bootstrap.html" %}
{% load i18n %} {% load i18n %}
{% load ietf_filters %} {% load ietf_filters %}
{% load staticfiles %} {% load static %}
{% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} WG Chair Dashboard {% endif %}{% endblock %} {% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} WG Chair Dashboard {% endif %}{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Confirm Cancel{% endblock %} {% block title %}Confirm Cancel{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Confirm Delete{% endblock %} {% block title %}Confirm Delete{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings - Add{% endblock %} {% block title %}Meetings - Add{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site_bootstrap.html" %} {% extends "base_site_bootstrap.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings{% endblock %} {% block title %}Meetings{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings - Blue Sheet{% endblock %} {% block title %}Meetings - Blue Sheet{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings - Edit{% endblock %} {% block title %}Meetings - Edit{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings{% endblock %} {% block title %}Meetings{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings{% endblock %} {% block title %}Meetings{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles tz %} {% load static tz %}
{% block title %}Meetings{% endblock %} {% block title %}Meetings{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Meetings{% endblock %} {% block title %}Meetings{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Rolodex - Add{% endblock %} {% block title %}Rolodex - Add{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Rolodex - Edit{% endblock %} {% block title %}Rolodex - Edit{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Rolodex - Search{% endblock %} {% block title %}Rolodex - Search{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions - Confirm{% endblock %} {% block title %}Sessions - Confirm{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions - Edit{% endblock %} {% block title %}Sessions - Edit{% endblock %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions{% endblock %} {% block title %}Sessions{% endblock %}

View file

@ -1,6 +1,6 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load ietf_filters %} {% load ietf_filters %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions{% endblock %} {% block title %}Sessions{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions- New{% endblock %} {% block title %}Sessions- New{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions{% endblock %} {% block title %}Sessions{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Sessions - View{% endblock %} {% block title %}Sessions - View{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "base_site.html" %} {% extends "base_site.html" %}
{% load staticfiles %} {% load static %}
{% block title %}Telechat{% endblock %} {% block title %}Telechat{% endblock %}

View file

@ -13,12 +13,11 @@ import warnings
from typing import Any, Dict, List, Tuple # pyflakes:ignore from typing import Any, Dict, List, Tuple # pyflakes:ignore
warnings.simplefilter("always", DeprecationWarning) warnings.simplefilter("always", DeprecationWarning)
warnings.filterwarnings("ignore", message="'urllib3\[secure\]' extra is deprecated") warnings.filterwarnings("ignore", message="'urllib3\\[secure\\]' extra is deprecated")
warnings.filterwarnings("ignore", message="The logout\(\) view is superseded by") warnings.filterwarnings("ignore", message="The logout\\(\\) view is superseded by")
warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report") warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report")
warnings.filterwarnings("ignore", message="{% load staticfiles %} is deprecated")
warnings.filterwarnings("ignore", message="Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated", module="bleach") warnings.filterwarnings("ignore", message="Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated", module="bleach")
warnings.filterwarnings("ignore", message="HTTPResponse.getheader\(\) is deprecated", module='selenium.webdriver') warnings.filterwarnings("ignore", message="HTTPResponse.getheader\\(\\) is deprecated", module='selenium.webdriver')
try: try:
import syslog import syslog
syslog.openlog(str("datatracker"), syslog.LOG_PID, syslog.LOG_USER) syslog.openlog(str("datatracker"), syslog.LOG_PID, syslog.LOG_USER)
@ -430,7 +429,6 @@ INSTALLED_APPS = [
'corsheaders', 'corsheaders',
'django_markup', 'django_markup',
'django_password_strength', 'django_password_strength',
'form_utils',
'oidc_provider', 'oidc_provider',
'simple_history', 'simple_history',
'tastypie', 'tastypie',

View file

@ -39,7 +39,7 @@ DATABASES = {
'HOST': 'db', 'HOST': 'db',
'PORT': '5432', 'PORT': '5432',
'NAME': 'test.db', 'NAME': 'test.db',
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'USER': 'django', 'USER': 'django',
'PASSWORD': 'RkTkDPFnKpko', 'PASSWORD': 'RkTkDPFnKpko',
}, },

View file

@ -13,7 +13,7 @@ from django.urls import reverse as urlreverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.encoding import force_text, force_str from django.utils.encoding import force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -202,7 +202,7 @@ def get_reply_to():
address with "plus addressing" using a random string. Guaranteed to be unique""" address with "plus addressing" using a random string. Guaranteed to be unique"""
local,domain = get_base_submission_message_address().split('@') local,domain = get_base_submission_message_address().split('@')
while True: while True:
rand = force_text(base64.urlsafe_b64encode(os.urandom(12))) rand = force_str(base64.urlsafe_b64encode(os.urandom(12)))
address = "{}+{}@{}".format(local,rand,domain) address = "{}+{}@{}".format(local,rand,domain)
q = Message.objects.filter(reply_to=address) q = Message.objects.filter(reply_to=address)
if not q: if not q:

View file

@ -24,7 +24,7 @@ from django.test import override_settings
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.urls import reverse as urlreverse from django.urls import reverse as urlreverse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_str, force_text from django.utils.encoding import force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
from ietf.submit.utils import (expirable_submissions, expire_submission, find_submission_filenames, from ietf.submit.utils import (expirable_submissions, expire_submission, find_submission_filenames,
@ -750,10 +750,10 @@ class SubmitTests(BaseSubmitTestCase):
self.assertTrue("New Version Notification" in outbox[-2]["Subject"]) self.assertTrue("New Version Notification" in outbox[-2]["Subject"])
self.assertTrue(name in get_payload_text(outbox[-2])) self.assertTrue(name in get_payload_text(outbox[-2]))
interesting_address = {'ietf':'mars', 'irtf':'irtf-chair', 'iab':'iab-chair', 'ise':'rfc-ise'}[draft.stream_id] interesting_address = {'ietf':'mars', 'irtf':'irtf-chair', 'iab':'iab-chair', 'ise':'rfc-ise'}[draft.stream_id]
self.assertTrue(interesting_address in force_text(outbox[-2].as_string())) self.assertTrue(interesting_address in force_str(outbox[-2].as_string()))
if draft.stream_id == 'ietf': if draft.stream_id == 'ietf':
self.assertTrue(draft.ad.role_email("ad").address in force_text(outbox[-2].as_string())) self.assertTrue(draft.ad.role_email("ad").address in force_str(outbox[-2].as_string()))
self.assertTrue(ballot_position.balloter.role_email("ad").address in force_text(outbox[-2].as_string())) self.assertTrue(ballot_position.balloter.role_email("ad").address in force_str(outbox[-2].as_string()))
self.assertTrue("New Version Notification" in outbox[-1]["Subject"]) self.assertTrue("New Version Notification" in outbox[-1]["Subject"])
self.assertTrue(name in get_payload_text(outbox[-1])) self.assertTrue(name in get_payload_text(outbox[-1]))
r = self.client.get(urlreverse('ietf.doc.views_search.recent_drafts')) r = self.client.get(urlreverse('ietf.doc.views_search.recent_drafts'))

View file

@ -12,7 +12,7 @@ from xml.dom import pulldom, Node
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import smart_bytes, force_str, force_text from django.utils.encoding import smart_bytes, force_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -583,7 +583,7 @@ def post_approved_draft(url, name):
if r.status_code != 200: if r.status_code != 200:
raise RuntimeError("Status code is not 200 OK (it's %s)." % r.status_code) raise RuntimeError("Status code is not 200 OK (it's %s)." % r.status_code)
if force_text(r.text) != "OK": if force_str(r.text) != "OK":
raise RuntimeError('Response is not "OK" (it\'s "%s").' % r.text) raise RuntimeError('Response is not "OK" (it\'s "%s").' % r.text)
except Exception as e: except Exception as e:

View file

@ -47,21 +47,43 @@
method="post" method="post"
enctype="multipart/form-data" enctype="multipart/form-data"
data-edit-form="{{ form.edit }}" data-edit-form="{{ form.edit }}"
data-ajax-info-url="{% url "ietf.liaisons.views.ajax_get_liaison_info" %}"> data-ajax-info-url="{% url 'ietf.liaisons.views.ajax_get_liaison_info' %}">
{% csrf_token %} {% csrf_token %}
{% for fieldset in form.fieldsets %} <h2>From</h2>
<h2>{{ fieldset.name }}</h2> {% bootstrap_field form.from_groups layout="horizontal" %}
{% for field in fieldset %} {% bootstrap_field form.from_contact layout="horizontal" %}
{% if field.id_for_label != "id_attachments" %} {% bootstrap_field form.response_contacts layout="horizontal" %}
{% bootstrap_field field layout="horizontal" %} {% if form.approved %}
{% else %} {% bootstrap_field form.approved layout="horizontal" %}
<div class="row mb-3"> {% endif %}
<p class="col-md-2 fw-bold col-form-label">{{ field.label }}</p> <h2>To</h2>
<div class="col-md-10">{{ field }}</div> {% bootstrap_field form.to_groups layout="horizontal" %}
</div> {% bootstrap_field form.to_contacts layout="horizontal" %}
{% endif %} <h2>Other email addresses</h2>
{% endfor %} {% bootstrap_field form.technical_contacts layout="horizontal" %}
{% endfor %} {% if form.action_holder_contacts %}
{% bootstrap_field form.action_holder_contacts layout="horizontal" %}
{% endif %}
{% bootstrap_field form.cc_contacts layout="horizontal" %}
<h2>Purpose</h2>
{% bootstrap_field form.purpose layout="horizontal" %}
{% bootstrap_field form.deadline layout="horizontal" %}
<h2>Reference</h2>
{% bootstrap_field form.other_identifiers layout="horizontal" %}
{% bootstrap_field form.related_to layout="horizontal" %}
<h2>Liaison Statement</h2>
{% bootstrap_field form.title layout="horizontal" %}
{% bootstrap_field form.submitted_date layout="horizontal" %}
{% bootstrap_field form.body layout="horizontal" %}
<div class="row mb-3">
<p class="col-md-2 fw-bold col-form-label">{{ form.attachments.label }}</p>
<div class="col-md-10">{{ form.attachments }}</div>
</div>
<h2>Add attachment</h2>
{% bootstrap_field form.attach_title layout="horizontal" %}
{% bootstrap_field form.attach_file layout="horizontal" %}
{% bootstrap_field form.attach_button layout="horizontal" %}
<a class="btn btn-danger float-end" <a class="btn btn-danger float-end"
href="{% if liaison %}{% url 'ietf.liaisons.views.liaison_detail' object_id=liaison.pk %}{% else %}{% url 'ietf.liaisons.views.liaison_list' %}{% endif %}"> href="{% if liaison %}{% url 'ietf.liaisons.views.liaison_detail' object_id=liaison.pk %}{% else %}{% url 'ietf.liaisons.views.liaison_list' %}{% endif %}">
Cancel Cancel

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{# Copyright The IETF Trust 2015-2020, All Rights Reserved #} {# Copyright The IETF Trust 2015-2020, All Rights Reserved #}
{% load origin %} {% load origin %}
{% load staticfiles %} {% load static %}
{% load ietf_filters %} {% load ietf_filters %}
{% load django_bootstrap5 %} {% load django_bootstrap5 %}
{% block title %}{{ schedule.name }}: IETF {{ meeting.number }} meeting agenda{% endblock %} {% block title %}{{ schedule.name }}: IETF {{ meeting.number }} meeting agenda{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{# Copyright The IETF Trust 2015-2020, All Rights Reserved #} {# Copyright The IETF Trust 2015-2020, All Rights Reserved #}
{% load origin %} {% load origin %}
{% load staticfiles %} {% load static %}
{% load ietf_filters %} {% load ietf_filters %}
{% load django_bootstrap5 %} {% load django_bootstrap5 %}
{% block content %} {% block content %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{# Copyright The IETF Trust 2020, All Rights Reserved #} {# Copyright The IETF Trust 2020, All Rights Reserved #}
{% load origin staticfiles django_bootstrap5 %} {% load origin static django_bootstrap5 %}
{% block title %} {% block title %}
Approved Slides for {{ submission.session.meeting }} : {{ submission.session.group.acronym }} Approved Slides for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}
{% endblock %} {% endblock %}

View file

@ -5,7 +5,7 @@
import time, random, hashlib import time, random, hashlib
from django.conf import settings from django.conf import settings
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_str
def generate_random_key(max_length=32): def generate_random_key(max_length=32):
@ -18,4 +18,4 @@ def generate_access_token(key, max_length=32):
# we hash it with the private key to make sure only we can # we hash it with the private key to make sure only we can
# generate and use the final token - so storing the key in the # generate and use the final token - so storing the key in the
# database is safe # database is safe
return force_text(hashlib.sha256(force_bytes(settings.SECRET_KEY) + force_bytes(key)).hexdigest()[:max_length]) return force_str(hashlib.sha256(force_bytes(settings.SECRET_KEY) + force_bytes(key)).hexdigest()[:max_length])

View file

@ -3,7 +3,7 @@
from django.contrib import admin from django.contrib import admin
from django.utils.encoding import force_text from django.utils.encoding import force_str
from ietf.utils.models import VersionInfo from ietf.utils.models import VersionInfo
@ -14,7 +14,7 @@ def name(obj):
if callable(obj.name): if callable(obj.name):
name = obj.name() name = obj.name()
else: else:
name = force_text(obj.name) name = force_str(obj.name)
if name: if name:
return name return name
return str(obj) return str(obj)

View file

@ -27,7 +27,7 @@ from django.core.validators import validate_email
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.template import Context,RequestContext from django.template import Context,RequestContext
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text, force_str, force_bytes from django.utils.encoding import force_str, force_bytes
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -137,7 +137,7 @@ def send_smtp(msg, bcc=None):
server.quit() server.quit()
except smtplib.SMTPServerDisconnected: except smtplib.SMTPServerDisconnected:
pass pass
subj = force_text(msg.get('Subject', '[no subject]')) subj = force_str(msg.get('Subject', '[no subject]'))
tau = time.time() - mark tau = time.time() - mark
log("sent email (%.3fs) from '%s' to %s id %s subject '%s'" % (tau, frm, to, msg.get('Message-ID', ''), subj)) log("sent email (%.3fs) from '%s' to %s id %s subject '%s'" % (tau, frm, to, msg.get('Message-ID', ''), subj))
@ -166,7 +166,7 @@ def copy_email(msg, to, toUser=False, originalBcc=None):
# Overwrite the From: header, so that the copy from a development or # Overwrite the From: header, so that the copy from a development or
# test server doesn't look like spam. # test server doesn't look like spam.
new['From'] = settings.DEFAULT_FROM_EMAIL new['From'] = settings.DEFAULT_FROM_EMAIL
new['Subject'] = '[Django %s] %s' % (settings.SERVER_MODE, force_text(msg.get('Subject', '[no subject]'))) new['Subject'] = '[Django %s] %s' % (settings.SERVER_MODE, force_str(msg.get('Subject', '[no subject]')))
new['To'] = to new['To'] = to
send_smtp(new) send_smtp(new)
@ -325,7 +325,7 @@ def show_that_mail_was_sent(request,leadline,msg,bcc):
from ietf.ietfauth.utils import has_role from ietf.ietfauth.utils import has_role
if has_role(request.user,['Area Director','Secretariat','IANA','RFC Editor','ISE','IAD','IRTF Chair','WG Chair','RG Chair','WG Secretary','RG Secretary']): if has_role(request.user,['Area Director','Secretariat','IANA','RFC Editor','ISE','IAD','IRTF Chair','WG Chair','RG Chair','WG Secretary','RG Secretary']):
info = "%s at %s %s\n" % (leadline,timezone.now().strftime("%Y-%m-%d %H:%M:%S"),settings.TIME_ZONE) info = "%s at %s %s\n" % (leadline,timezone.now().strftime("%Y-%m-%d %H:%M:%S"),settings.TIME_ZONE)
info += "Subject: %s\n" % force_text(msg.get('Subject','[no subject]')) info += "Subject: %s\n" % force_str(msg.get('Subject','[no subject]'))
info += "To: %s\n" % msg.get('To','[no to]') info += "To: %s\n" % msg.get('To','[no to]')
if msg.get('Cc'): if msg.get('Cc'):
info += "Cc: %s\n" % msg.get('Cc') info += "Cc: %s\n" % msg.get('Cc')
@ -336,7 +336,7 @@ def show_that_mail_was_sent(request,leadline,msg,bcc):
def save_as_message(request, msg, bcc): def save_as_message(request, msg, bcc):
by = ((request and request.user and not request.user.is_anonymous and request.user.person) by = ((request and request.user and not request.user.is_anonymous and request.user.person)
or ietf.person.models.Person.objects.get(name="(System)")) or ietf.person.models.Person.objects.get(name="(System)"))
headers, body = force_text(str(msg)).split('\n\n', 1) headers, body = force_str(str(msg)).split('\n\n', 1)
kwargs = {'by': by, 'body': body, 'content_type': msg.get_content_type(), 'bcc': bcc or '' } kwargs = {'by': by, 'body': body, 'content_type': msg.get_content_type(), 'bcc': bcc or '' }
for (arg, field) in [ for (arg, field) in [
('cc', 'Cc'), ('cc', 'Cc'),

View file

@ -18,7 +18,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core import serializers from django.core import serializers
from django.db import DEFAULT_DB_ALIAS, DatabaseError, IntegrityError, connections from django.db import DEFAULT_DB_ALIAS, DatabaseError, IntegrityError, connections
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.utils.encoding import force_text from django.utils.encoding import force_str
import django.core.management.commands.loaddata as loaddata import django.core.management.commands.loaddata as loaddata
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -91,7 +91,7 @@ class Command(loaddata.Command):
obj.save(using=self.using) obj.save(using=self.using)
self.loaded_object_count += 1 self.loaded_object_count += 1
except (DatabaseError, IntegrityError, ObjectDoesNotExist, AttributeError) as e: except (DatabaseError, IntegrityError, ObjectDoesNotExist, AttributeError) as e:
error_msg = force_text(e) error_msg = force_str(e)
if "Duplicate entry" in error_msg: if "Duplicate entry" in error_msg:
pass pass
else: else:

View file

@ -15,7 +15,7 @@ from django.core.management.base import CommandError
from django.core.management.commands.loaddata import Command as LoadCommand, humanize from django.core.management.commands.loaddata import Command as LoadCommand, humanize
from django.db import DatabaseError, IntegrityError, router, transaction from django.db import DatabaseError, IntegrityError, router, transaction
from django.db.models import ManyToManyField from django.db.models import ManyToManyField
from django.utils.encoding import force_text from django.utils.encoding import force_str
from ietf.utils.models import ForeignKey from ietf.utils.models import ForeignKey
@ -234,7 +234,7 @@ class Command(LoadCommand):
'object_name': obj.object._meta.object_name, 'object_name': obj.object._meta.object_name,
'pk': obj.object.pk, 'pk': obj.object.pk,
'data': obj_to_dict(obj.object), 'data': obj_to_dict(obj.object),
'error_msg': force_text(e) 'error_msg': force_str(e)
},) },)
raise raise
if objects and show_progress: if objects and show_progress:

View file

@ -7,7 +7,7 @@ import datetime
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
import debug # pyflakes:ignore import debug # pyflakes:ignore
@ -38,7 +38,7 @@ def create_person(group, role_name, name=None, username=None, email_address=None
user = User.objects.create(username=username,is_staff=is_staff,is_superuser=is_superuser) user = User.objects.create(username=username,is_staff=is_staff,is_superuser=is_superuser)
user.set_password(password) user.set_password(password)
user.save() user.save()
person = Person.objects.create(name=name, ascii=unidecode_name(smart_text(name)), user=user) person = Person.objects.create(name=name, ascii=unidecode_name(smart_str(name)), user=user)
email = Email.objects.create(address=email_address, person=person, origin=user.username) email = Email.objects.create(address=email_address, person=person, origin=user.username)
Role.objects.create(group=group, name_id=role_name, person=person, email=email) Role.objects.create(group=group, name_id=role_name, person=person, email=email)
return person return person

View file

@ -95,7 +95,7 @@ old_create = None
template_coverage_collection = None template_coverage_collection = None
code_coverage_collection = None code_coverage_collection = None
url_coverage_collection = None url_coverage_collection = None
validation_settings = {"validate_html": None, "validate_html_harder": None, "show_logging": False}
def start_vnu_server(port=8888): def start_vnu_server(port=8888):
"Start a vnu validation server on the indicated port" "Start a vnu validation server on the indicated port"
@ -282,7 +282,7 @@ class ValidatingTemplates(DjangoTemplates):
def __init__(self, params): def __init__(self, params):
super().__init__(params) super().__init__(params)
if not settings.validate_html: if not validation_settings["validate_html"]:
return return
self.validation_cache = set() self.validation_cache = set()
self.cwd = str(pathlib.Path.cwd()) self.cwd = str(pathlib.Path.cwd())
@ -298,7 +298,7 @@ class ValidatingTemplate(Template):
def render(self, context=None, request=None): def render(self, context=None, request=None):
content = super().render(context, request) content = super().render(context, request)
if not settings.validate_html: if not validation_settings["validate_html"]:
return content return content
if not self.origin.name.endswith("html"): if not self.origin.name.endswith("html"):
@ -310,7 +310,7 @@ class ValidatingTemplate(Template):
return content return content
fingerprint = hash(content) + sys.maxsize + 1 # make hash positive fingerprint = hash(content) + sys.maxsize + 1 # make hash positive
if not settings.validate_html_harder and fingerprint in self.backend.validation_cache: if not validation_settings["validate_html_harder"] and fingerprint in self.backend.validation_cache:
# already validated this HTML fragment, skip it # already validated this HTML fragment, skip it
# as an optimization, make page a bit smaller by not returning HTML for the menus # as an optimization, make page a bit smaller by not returning HTML for the menus
# FIXME: figure out why this still includes base/menu.html # FIXME: figure out why this still includes base/menu.html
@ -326,7 +326,7 @@ class ValidatingTemplate(Template):
# don't validate each template by itself, causes too much overhead # don't validate each template by itself, causes too much overhead
# instead, save a batch of them and then validate them all in one go # instead, save a batch of them and then validate them all in one go
# this delays error detection a bit, but is MUCH faster # this delays error detection a bit, but is MUCH faster
settings.validate_html.batches[kind].append( validation_settings["validate_html"].batches[kind].append(
(self.origin.name, content, fingerprint) (self.origin.name, content, fingerprint)
) )
return content return content
@ -726,9 +726,10 @@ class IetfTestRunner(DiscoverRunner):
self.html_report = html_report self.html_report = html_report
self.permit_mixed_migrations = permit_mixed_migrations self.permit_mixed_migrations = permit_mixed_migrations
self.show_logging = show_logging self.show_logging = show_logging
settings.validate_html = self if validate_html else None global validation_settings
settings.validate_html_harder = self if validate_html and validate_html_harder else None validation_settings["validate_html"] = self if validate_html else None
settings.show_logging = show_logging validation_settings["validate_html_harder"] = self if validate_html and validate_html_harder else None
validation_settings["show_logging"] = show_logging
# #
self.root_dir = os.path.dirname(settings.BASE_DIR) self.root_dir = os.path.dirname(settings.BASE_DIR)
self.coverage_file = os.path.join(self.root_dir, settings.TEST_COVERAGE_MAIN_FILE) self.coverage_file = os.path.join(self.root_dir, settings.TEST_COVERAGE_MAIN_FILE)
@ -843,7 +844,7 @@ class IetfTestRunner(DiscoverRunner):
s[1] = tuple(s[1]) # random.setstate() won't accept a list in lieu of a tuple s[1] = tuple(s[1]) # random.setstate() won't accept a list in lieu of a tuple
factory.random.set_random_state(s) factory.random.set_random_state(s)
if not settings.validate_html: if not validation_settings["validate_html"]:
print(" Not validating any generated HTML; " print(" Not validating any generated HTML; "
"please do so at least once before committing changes") "please do so at least once before committing changes")
else: else:
@ -912,7 +913,7 @@ class IetfTestRunner(DiscoverRunner):
self.config_file[kind].flush() self.config_file[kind].flush()
pathlib.Path(self.config_file[kind].name).chmod(0o644) pathlib.Path(self.config_file[kind].name).chmod(0o644)
if not settings.validate_html_harder: if not validation_settings["validate_html_harder"]:
print("") print("")
self.vnu = None self.vnu = None
else: else:
@ -941,7 +942,7 @@ class IetfTestRunner(DiscoverRunner):
with open(self.coverage_file, "w") as file: with open(self.coverage_file, "w") as file:
json.dump(self.coverage_master, file, indent=2, sort_keys=True) json.dump(self.coverage_master, file, indent=2, sort_keys=True)
if settings.validate_html: if validation_settings["validate_html"]:
for kind in self.batches: for kind in self.batches:
if len(self.batches[kind]): if len(self.batches[kind]):
print(f" WARNING: not all templates of kind '{kind}' were validated") print(f" WARNING: not all templates of kind '{kind}' were validated")
@ -1007,7 +1008,7 @@ class IetfTestRunner(DiscoverRunner):
+ "\n" + "\n"
) )
if settings.validate_html_harder and kind != "frag": if validation_settings["validate_html_harder"] and kind != "frag":
files = [ files = [
os.path.join(d, f) os.path.join(d, f)
for d, dirs, files in os.walk(tmppath) for d, dirs, files in os.walk(tmppath)
@ -1084,7 +1085,7 @@ class IetfTestRunner(DiscoverRunner):
self.test_apps, self.test_paths = self.get_test_paths(test_labels) self.test_apps, self.test_paths = self.get_test_paths(test_labels)
if settings.validate_html: if validation_settings["validate_html"]:
extra_tests += [ extra_tests += [
TemplateValidationTests( TemplateValidationTests(
test_runner=self, test_runner=self,

View file

@ -1,6 +1,6 @@
--- django/http/response.py.orig 2020-07-08 14:34:42.776562458 +0200 --- django/http/response.py.orig 2020-07-08 14:34:42.776562458 +0200
+++ django/http/response.py 2020-07-08 14:35:56.454687322 +0200 +++ django/http/response.py 2020-07-08 14:35:56.454687322 +0200
@@ -197,8 +197,8 @@ @@ -196,8 +196,8 @@
if httponly: if httponly:
self.cookies[key]['httponly'] = True self.cookies[key]['httponly'] = True
if samesite: if samesite:

View file

@ -11,7 +11,7 @@
--- django/http/response.py.orig 2020-08-13 11:16:04.060627793 +0200 --- django/http/response.py.orig 2020-08-13 11:16:04.060627793 +0200
+++ django/http/response.py 2020-08-13 11:54:03.482476973 +0200 +++ django/http/response.py 2020-08-13 11:54:03.482476973 +0200
@@ -210,12 +210,18 @@ @@ -209,12 +209,18 @@
value = signing.get_cookie_signer(salt=key + salt).sign(value) value = signing.get_cookie_signer(salt=key + salt).sign(value)
return self.set_cookie(key, value, **kwargs) return self.set_cookie(key, value, **kwargs)
@ -35,12 +35,12 @@
--- django/contrib/sessions/middleware.py.orig 2020-08-13 12:12:12.401898114 +0200 --- django/contrib/sessions/middleware.py.orig 2020-08-13 12:12:12.401898114 +0200
+++ django/contrib/sessions/middleware.py 2020-08-13 12:14:52.690520659 +0200 +++ django/contrib/sessions/middleware.py 2020-08-13 12:14:52.690520659 +0200
@@ -39,6 +39,8 @@ @@ -38,6 +38,8 @@
settings.SESSION_COOKIE_NAME, settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH, path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN, domain=settings.SESSION_COOKIE_DOMAIN,
+ secure=settings.SESSION_COOKIE_SECURE or None, + secure=settings.SESSION_COOKIE_SECURE or None,
+ httponly=settings.SESSION_COOKIE_HTTPONLY or None, + httponly=settings.SESSION_COOKIE_HTTPONLY or None,
samesite=settings.SESSION_COOKIE_SAMESITE, samesite=settings.SESSION_COOKIE_SAMESITE,
) )
else: patch_vary_headers(response, ('Cookie',))

View file

@ -1,5 +1,5 @@
# -*- conf-mode -*- # -*- conf-mode -*-
setuptools>=51.1.0 # Require this first, to prevent later errors setuptools>=51.1.0,<67.5.0 # Require this first, to prevent later errors
# #
argon2-cffi>=21.3.0 # For the Argon2 password hasher option argon2-cffi>=21.3.0 # For the Argon2 password hasher option
beautifulsoup4>=4.11.1 # Only used in tests beautifulsoup4>=4.11.1 # Only used in tests
@ -9,14 +9,13 @@ celery>=5.2.6
coverage>=4.5.4,<5.0 # Coverage 5.x moves from a json database to SQLite. Moving to 5.x will require substantial rewrites in ietf.utils.test_runner and ietf.release.views coverage>=4.5.4,<5.0 # Coverage 5.x moves from a json database to SQLite. Moving to 5.x will require substantial rewrites in ietf.utils.test_runner and ietf.release.views
decorator>=5.1.1 decorator>=5.1.1
defusedxml>=0.7.1 # for TastyPie when using xml; not a declared dependency defusedxml>=0.7.1 # for TastyPie when using xml; not a declared dependency
Django>=2.2.28,<3.0 Django<3.1
django-analytical>=3.1.0 django-analytical>=3.1.0
django-bootstrap5>=21.3 django-bootstrap5>=21.3
django-celery-beat>=2.3.0 django-celery-beat>=2.3.0
django-csp>=3.7 django-csp>=3.7
django-cors-headers>=3.11.0 django-cors-headers>=3.11.0
django-debug-toolbar>=3.2.4 django-debug-toolbar>=3.2.4
django-form-utils>=1.0.3 # Only one use, in the liaisons app. Last release was in 2015.
django-markup>=1.5 # Limited use - need to reconcile against direct use of markdown django-markup>=1.5 # Limited use - need to reconcile against direct use of markdown
django-oidc-provider>=0.7,<0.8 # 0.8 dropped Django 2 support django-oidc-provider>=0.7,<0.8 # 0.8 dropped Django 2 support
django-password-strength>=1.2.1 django-password-strength>=1.2.1