diff --git a/src/backoffice/templates/approve_public_credit_names.html b/src/backoffice/templates/approve_public_credit_names.html index 0ed6beb8..75d3804b 100644 --- a/src/backoffice/templates/approve_public_credit_names.html +++ b/src/backoffice/templates/approve_public_credit_names.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/badge_handout.html b/src/backoffice/templates/badge_handout.html index c92ed64b..4d49fff4 100644 --- a/src/backoffice/templates/badge_handout.html +++ b/src/backoffice/templates/badge_handout.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/camp_select.html b/src/backoffice/templates/camp_select.html index 02f0b88d..68d89551 100644 --- a/src/backoffice/templates/camp_select.html +++ b/src/backoffice/templates/camp_select.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %} diff --git a/src/backoffice/templates/index.html b/src/backoffice/templates/index.html index 1a5c46bf..520e25c5 100644 --- a/src/backoffice/templates/index.html +++ b/src/backoffice/templates/index.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %} diff --git a/src/backoffice/templates/info_desk/scan.html b/src/backoffice/templates/info_desk/scan.html index 7a15b2dc..3f566c65 100644 --- a/src/backoffice/templates/info_desk/scan.html +++ b/src/backoffice/templates/info_desk/scan.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% load static from staticfiles %} +{% load static %} {% load qrcode %} {% block content %} diff --git a/src/backoffice/templates/manage_proposals.html b/src/backoffice/templates/manage_proposals.html index 40a9eaf0..81d19e86 100644 --- a/src/backoffice/templates/manage_proposals.html +++ b/src/backoffice/templates/manage_proposals.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% load bornhack %} diff --git a/src/backoffice/templates/merchandise_to_order.html b/src/backoffice/templates/merchandise_to_order.html index edcb9aed..8854c10a 100644 --- a/src/backoffice/templates/merchandise_to_order.html +++ b/src/backoffice/templates/merchandise_to_order.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/orders_merchandise.html b/src/backoffice/templates/orders_merchandise.html index 7c4566fb..d78370ca 100644 --- a/src/backoffice/templates/orders_merchandise.html +++ b/src/backoffice/templates/orders_merchandise.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/orders_village.html b/src/backoffice/templates/orders_village.html index 358c7659..e676e0e0 100644 --- a/src/backoffice/templates/orders_village.html +++ b/src/backoffice/templates/orders_village.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/product_handout.html b/src/backoffice/templates/product_handout.html index a8db0054..eedcc1ce 100644 --- a/src/backoffice/templates/product_handout.html +++ b/src/backoffice/templates/product_handout.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/reimbursement_create_userselect.html b/src/backoffice/templates/reimbursement_create_userselect.html index 429a5315..12efaaa3 100644 --- a/src/backoffice/templates/reimbursement_create_userselect.html +++ b/src/backoffice/templates/reimbursement_create_userselect.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %} diff --git a/src/backoffice/templates/shop_ticket_overview.html b/src/backoffice/templates/shop_ticket_overview.html index f1448106..761f85fe 100644 --- a/src/backoffice/templates/shop_ticket_overview.html +++ b/src/backoffice/templates/shop_ticket_overview.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/ticket_checkin.html b/src/backoffice/templates/ticket_checkin.html index 85344325..d6283fb8 100644 --- a/src/backoffice/templates/ticket_checkin.html +++ b/src/backoffice/templates/ticket_checkin.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/backoffice/templates/village_to_order.html b/src/backoffice/templates/village_to_order.html index 9f8099e5..5b6fae1d 100644 --- a/src/backoffice/templates/village_to_order.html +++ b/src/backoffice/templates/village_to_order.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block extra_head %} diff --git a/src/bar/templates/bar_menu.html b/src/bar/templates/bar_menu.html index cffe202a..e868ba4b 100644 --- a/src/bar/templates/bar_menu.html +++ b/src/bar/templates/bar_menu.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load bootstrap3 %} {% load menubutton %} {% static "" as baseurl %} diff --git a/src/bornhack/settings.py b/src/bornhack/settings.py index 24834d92..9892513c 100644 --- a/src/bornhack/settings.py +++ b/src/bornhack/settings.py @@ -59,7 +59,6 @@ INSTALLED_APPS = [ "bootstrap3", "django_extensions", "reversion", - "betterforms", ] # MEDIA_URL = '/media/' diff --git a/src/camps/templates/bornhack-2016_camp_detail.html b/src/camps/templates/bornhack-2016_camp_detail.html index 4aa9ee31..aff703aa 100644 --- a/src/camps/templates/bornhack-2016_camp_detail.html +++ b/src/camps/templates/bornhack-2016_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/bornhack-2017_camp_detail.html b/src/camps/templates/bornhack-2017_camp_detail.html index 330728bd..65731ec4 100644 --- a/src/camps/templates/bornhack-2017_camp_detail.html +++ b/src/camps/templates/bornhack-2017_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/bornhack-2018_camp_detail.html b/src/camps/templates/bornhack-2018_camp_detail.html index c2126b1c..15c0b862 100644 --- a/src/camps/templates/bornhack-2018_camp_detail.html +++ b/src/camps/templates/bornhack-2018_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/bornhack-2019_camp_detail.html b/src/camps/templates/bornhack-2019_camp_detail.html index 98077133..6b2c1881 100644 --- a/src/camps/templates/bornhack-2019_camp_detail.html +++ b/src/camps/templates/bornhack-2019_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/bornhack-2020_camp_detail.html b/src/camps/templates/bornhack-2020_camp_detail.html index d6364d3a..9e8545c3 100644 --- a/src/camps/templates/bornhack-2020_camp_detail.html +++ b/src/camps/templates/bornhack-2020_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/bornhack-2021_camp_detail.html b/src/camps/templates/bornhack-2021_camp_detail.html index 7ac80432..2e0ea936 100644 --- a/src/camps/templates/bornhack-2021_camp_detail.html +++ b/src/camps/templates/bornhack-2021_camp_detail.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/camps/templates/camp_list.html b/src/camps/templates/camp_list.html index abc8537d..92638a07 100644 --- a/src/camps/templates/camp_list.html +++ b/src/camps/templates/camp_list.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% load commonmark %} -{% load static from staticfiles %} +{% load static %} {% load imageutils %} {% block content %}
diff --git a/src/info/templates/info.html b/src/info/templates/info.html index 8a82a1c9..7ed5d0ac 100644 --- a/src/info/templates/info.html +++ b/src/info/templates/info.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% load static from staticfiles %} +{% load static %} {% load commonmark %}$ {% block title %} diff --git a/src/profiles/templates/tokens/tokenfind_list.html b/src/profiles/templates/tokens/tokenfind_list.html index c07adc58..ecffbb86 100644 --- a/src/profiles/templates/tokens/tokenfind_list.html +++ b/src/profiles/templates/tokens/tokenfind_list.html @@ -1,5 +1,5 @@ {% extends 'profile_base.html' %} -{% load static from staticfiles %} +{% load static %} {% load commonmark %}$ {% load token_tags %} diff --git a/src/program/multiform.py b/src/program/multiform.py new file mode 100644 index 00000000..a04b3ce6 --- /dev/null +++ b/src/program/multiform.py @@ -0,0 +1,219 @@ +# Copied from https://github.com/fusionbox/django-betterforms/blob/master/betterforms/multiform.py + +# +# From https://github.com/fusionbox/django-betterforms/blob/master/LICENSE +# +# Copyright (c) 2013, Fusionbox, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from itertools import chain +from functools import reduce +from operator import add + +from collections import OrderedDict +from django.forms.utils import ErrorDict, ErrorList +from django.core.exceptions import ValidationError, NON_FIELD_ERRORS +from django.utils.safestring import mark_safe + + +class MultiForm(object): + """ + A container that allows you to treat multiple forms as one form. This is + great for using more than one form on a page that share the same submit + button. MultiForm imitates the Form API so that it is invisible to anybody + else that you are using a MultiForm. + """ + form_classes = {} + + def __init__(self, data=None, files=None, *args, **kwargs): + # Some things, such as the WizardView expect these to exist. + self.data, self.files = data, files + kwargs.update( + data=data, + files=files, + ) + + self.initials = kwargs.pop('initial', None) + if self.initials is None: + self.initials = {} + self.forms = OrderedDict() + self.crossform_errors = [] + + for key, form_class in self.form_classes.items(): + fargs, fkwargs = self.get_form_args_kwargs(key, args, kwargs) + self.forms[key] = form_class(*fargs, **fkwargs) + + def get_form_args_kwargs(self, key, args, kwargs): + """ + Returns the args and kwargs for initializing one of our form children. + """ + fkwargs = kwargs.copy() + prefix = kwargs.get('prefix') + if prefix is None: + prefix = key + else: + prefix = '{0}__{1}'.format(key, prefix) + fkwargs.update( + initial=self.initials.get(key), + prefix=prefix, + ) + return args, fkwargs + + def __str__(self): + return self.as_table() + + def __getitem__(self, key): + return self.forms[key] + + @property + def errors(self): + errors = {} + for form_name in self.forms: + form = self.forms[form_name] + for field_name in form.errors: + errors[form.add_prefix(field_name)] = form.errors[field_name] + if self.crossform_errors: + errors[NON_FIELD_ERRORS] = self.crossform_errors + return errors + + @property + def fields(self): + fields = [] + for form_name in self.forms: + form = self.forms[form_name] + for field_name in form.fields: + fields += [form.add_prefix(field_name)] + return fields + + def __iter__(self): + # TODO: Should the order of the fields be controllable from here? + return chain.from_iterable(self.forms.values()) + + @property + def is_bound(self): + return any(form.is_bound for form in self.forms.values()) + + def clean(self): + """ + Raises any ValidationErrors required for cross form validation. Should + return a dict of cleaned_data objects for any forms whose data should + be overridden. + """ + return self.cleaned_data + + def add_crossform_error(self, e): + self.crossform_errors.append(e) + + def is_valid(self): + forms_valid = all(form.is_valid() for form in self.forms.values()) + try: + self.cleaned_data = self.clean() + except ValidationError as e: + self.add_crossform_error(e) + return forms_valid and not self.crossform_errors + + def non_field_errors(self): + form_errors = ( + form.non_field_errors() for form in self.forms.values() + if hasattr(form, 'non_field_errors') + ) + return ErrorList(chain(self.crossform_errors, *form_errors)) + + def as_table(self): + return mark_safe(''.join(form.as_table() for form in self.forms.values())) + + def as_ul(self): + return mark_safe(''.join(form.as_ul() for form in self.forms.values())) + + def as_p(self): + return mark_safe(''.join(form.as_p() for form in self.forms.values())) + + def is_multipart(self): + return any(form.is_multipart() for form in self.forms.values()) + + @property + def media(self): + return reduce(add, (form.media for form in self.forms.values())) + + def hidden_fields(self): + # copy implementation instead of delegating in case we ever + # want to override the field ordering. + return [field for field in self if field.is_hidden] + + def visible_fields(self): + return [field for field in self if not field.is_hidden] + + @property + def cleaned_data(self): + return OrderedDict( + (key, form.cleaned_data) + for key, form in self.forms.items() if form.is_valid() + ) + + @cleaned_data.setter + def cleaned_data(self, data): + for key, value in data.items(): + child_form = self[key] + if hasattr(child_form, 'forms'): + for formlet, formlet_data in zip(child_form.forms, value): + formlet.cleaned_data = formlet_data + else: + child_form.cleaned_data = value + + +class MultiModelForm(MultiForm): + """ + MultiModelForm adds ModelForm support on top of MultiForm. That simply + means that it includes support for the instance parameter in initialization + and adds a save method. + """ + def __init__(self, *args, **kwargs): + self.instances = kwargs.pop('instance', None) + if self.instances is None: + self.instances = {} + super(MultiModelForm, self).__init__(*args, **kwargs) + + def get_form_args_kwargs(self, key, args, kwargs): + fargs, fkwargs = super(MultiModelForm, self).get_form_args_kwargs(key, args, kwargs) + try: + # If we only pass instance when there was one specified, we make it + # possible to use non-ModelForms together with ModelForms. + fkwargs['instance'] = self.instances[key] + except KeyError: + pass + return fargs, fkwargs + + def save(self, commit=True): + objects = OrderedDict( + (key, form.save(commit)) + for key, form in self.forms.items() + ) + + if any(hasattr(form, 'save_m2m') for form in self.forms.values()): + def save_m2m(): + for form in self.forms.values(): + if hasattr(form, 'save_m2m'): + form.save_m2m() + self.save_m2m = save_m2m + + return objects diff --git a/src/program/views.py b/src/program/views.py index 166ea5cf..dd21a617 100644 --- a/src/program/views.py +++ b/src/program/views.py @@ -13,7 +13,6 @@ from django.urls import reverse, reverse_lazy from django.template import Engine, Context from django.shortcuts import redirect from django.shortcuts import get_object_or_404 -from betterforms.multiform import MultiModelForm import icalendar from camps.mixins import CampViewMixin @@ -31,6 +30,7 @@ from .email import ( add_eventproposal_updated_email, ) from . import models +from .multiform import MultiModelForm from .forms import SpeakerProposalForm, EventProposalForm diff --git a/src/requirements/production.txt b/src/requirements/production.txt index 5d6fc183..36d08b8f 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -1,12 +1,11 @@ -Django==2.2.9 # 3.0 is waiting for https://github.com/bornhack/bornhack-website/issues/435 +Django==3.0.3 channels==2.4.0 channels-redis==2.4.1 asyncio==3.4.3 commonmark==0.9.1 -django-allauth-2fa==0.7 -django-betterforms==1.2 +django-allauth-2fa==0.8 django-bleach==0.6.1 django-bootstrap3==12.0.3 django-cors-headers==3.2.1 diff --git a/src/shop/templates/pdf/creditnote.html b/src/shop/templates/pdf/creditnote.html index 88070f0a..4388ffa1 100644 --- a/src/shop/templates/pdf/creditnote.html +++ b/src/shop/templates/pdf/creditnote.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load shop_tags %} diff --git a/src/shop/templates/pdf/custominvoice.html b/src/shop/templates/pdf/custominvoice.html index eb5ed092..35f112ae 100644 --- a/src/shop/templates/pdf/custominvoice.html +++ b/src/shop/templates/pdf/custominvoice.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load shop_tags %} diff --git a/src/shop/templates/pdf/invoice.html b/src/shop/templates/pdf/invoice.html index a6d0561b..b2a70ffa 100644 --- a/src/shop/templates/pdf/invoice.html +++ b/src/shop/templates/pdf/invoice.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load shop_tags %} diff --git a/src/shop/templates/pdf/proforma_invoice.html b/src/shop/templates/pdf/proforma_invoice.html index ddf63927..04db9a19 100644 --- a/src/shop/templates/pdf/proforma_invoice.html +++ b/src/shop/templates/pdf/proforma_invoice.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load shop_tags %} diff --git a/src/sponsors/templates/sponsors.html b/src/sponsors/templates/sponsors.html index fb506c53..fc32f4ba 100644 --- a/src/sponsors/templates/sponsors.html +++ b/src/sponsors/templates/sponsors.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% load static from staticfiles %} +{% load static %} {% load commonmark %} {% block title %} diff --git a/src/templates/base.html b/src/templates/base.html index b068adbf..8a1e19e1 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} {% load bootstrap3 %} {% load menubutton %} {% static "" as baseurl %} diff --git a/src/tickets/templates/pdf/ticket.html b/src/tickets/templates/pdf/ticket.html index 914d4cec..42e1ce12 100644 --- a/src/tickets/templates/pdf/ticket.html +++ b/src/tickets/templates/pdf/ticket.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load static %} diff --git a/src/tokens/templates/token_detail.html b/src/tokens/templates/token_detail.html index 39851f0d..5e63aea7 100644 --- a/src/tokens/templates/token_detail.html +++ b/src/tokens/templates/token_detail.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% load static from staticfiles %} +{% load static %} {% load commonmark %}$ {% block title %}