From 1ae8b8fae679dac851412966e5420aeca747f810 Mon Sep 17 00:00:00 2001 From: Julian Dehm Date: Thu, 12 Sep 2024 16:55:57 +0200 Subject: [PATCH] cleanup contrib app --- euth/contrib/api/__init__.py | 0 euth/contrib/api/mixins.py | 50 -------- euth/contrib/api/permissions.py | 14 --- euth/contrib/emails.py | 16 --- euth/contrib/filters.py | 78 ------------ euth/contrib/formset.py | 19 --- euth/contrib/mixins.py | 27 ----- euth/contrib/services.py | 0 euth/contrib/static/js/dateTimeInput.js | 2 - euth/contrib/static/js/formset.js | 42 ------- .../euth_contrib/includes/pagination.html | 31 ----- euth/contrib/templatetags/__init__.py | 0 euth/contrib/templatetags/contrib_tags.py | 26 ---- euth/contrib/validators.py | 30 ----- euth/contrib/widgets.py | 112 ------------------ 15 files changed, 447 deletions(-) delete mode 100644 euth/contrib/api/__init__.py delete mode 100644 euth/contrib/api/mixins.py delete mode 100644 euth/contrib/api/permissions.py delete mode 100644 euth/contrib/emails.py delete mode 100644 euth/contrib/filters.py delete mode 100644 euth/contrib/formset.py delete mode 100644 euth/contrib/mixins.py delete mode 100644 euth/contrib/services.py delete mode 100644 euth/contrib/static/js/dateTimeInput.js delete mode 100644 euth/contrib/static/js/formset.js delete mode 100644 euth/contrib/templates/euth_contrib/includes/pagination.html delete mode 100644 euth/contrib/templatetags/__init__.py delete mode 100644 euth/contrib/templatetags/contrib_tags.py delete mode 100644 euth/contrib/validators.py delete mode 100644 euth/contrib/widgets.py diff --git a/euth/contrib/api/__init__.py b/euth/contrib/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/euth/contrib/api/mixins.py b/euth/contrib/api/mixins.py deleted file mode 100644 index f33530755..000000000 --- a/euth/contrib/api/mixins.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.http import Http404 -from rest_framework import status -from rest_framework.request import clone_request -from rest_framework.response import Response - - -class AllowPUTAsCreateMixin(object): - """ - PUT-as-create mixin. - - The following mixin class may be used in order to support PUT-as-create - behavior for incoming requests. - """ - - def update(self, request, *args, **kwargs): - partial = kwargs.pop('partial', False) - instance = self.get_object_or_none() - serializer = self.get_serializer(instance, - data=request.data, - partial=partial) - serializer.is_valid(raise_exception=True) - - if instance is None: - lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field - lookup_value = self.kwargs[lookup_url_kwarg] - extra_kwargs = {self.lookup_field: lookup_value} - serializer.save(**extra_kwargs) - return Response(serializer.data, status=status.HTTP_201_CREATED) - - serializer.save() - return Response(serializer.data) - - def partial_update(self, request, *args, **kwargs): - kwargs['partial'] = True - return self.update(request, *args, **kwargs) - - def get_object_or_none(self): - try: - return self.get_object() - except Http404: - if self.request.method == 'PUT': - # For PUT-as-create operation, we need to ensure that we have - # relevant permissions, as if this was a POST request. This - # will either raise a PermissionDenied exception, or simply - # return None. - self.check_permissions(clone_request(self.request, 'POST')) - else: - # PATCH requests where the object does not exist should still - # return a 404 response. - raise diff --git a/euth/contrib/api/permissions.py b/euth/contrib/api/permissions.py deleted file mode 100644 index b231af73e..000000000 --- a/euth/contrib/api/permissions.py +++ /dev/null @@ -1,14 +0,0 @@ -from rest_framework import permissions - - -class IsInitiatorOrSuperUser(permissions.BasePermission): - """ - Object-level permission to only allow owners of an object to edit it. - - Assumes the model instance has an `owner` attribute. - """ - - def has_object_permission(self, request, view, obj): - is_initiator = obj.organisation.initiators.filter(pk=request.user.pk)\ - .exists() - return request.user.is_superuser or is_initiator diff --git a/euth/contrib/emails.py b/euth/contrib/emails.py deleted file mode 100644 index 269f14513..000000000 --- a/euth/contrib/emails.py +++ /dev/null @@ -1,16 +0,0 @@ -from email.mime.image import MIMEImage - -from django.contrib.staticfiles import finders - -from adhocracy4.emails.mixins import Email -from adhocracy4.emails.mixins import SyncEmailMixin - - -class OpinEmail(SyncEmailMixin, Email): - def get_attachments(self): - attachments = super().get_attachments() - filename = finders.find('images/logo.png') - f = open(filename, 'rb') - opin_logo = MIMEImage(f.read()) - opin_logo.add_header('Content-ID', '<{}>'.format('opin_logo')) - return attachments + [opin_logo] diff --git a/euth/contrib/filters.py b/euth/contrib/filters.py deleted file mode 100644 index e6bccf8f3..000000000 --- a/euth/contrib/filters.py +++ /dev/null @@ -1,78 +0,0 @@ -import django_filters -import pyuca -from django.utils.translation import gettext_lazy as _ -from django_countries import Countries - -from adhocracy4.filters import filters -from adhocracy4.filters import widgets -from adhocracy4.projects import models - - -class ArchivedFilterWidget(widgets.DropdownLinkWidget): - label = _('status') - - def __init__(self, attrs=None): - choices = ( - ('false', _('active')), - ('true', _('archived')), - ('all', _('all')) - ) - super().__init__(attrs, choices) - - -class ArchivedFilter(filters.DefaultsFilterSet): - - defaults = { - 'is_archived': 'false' - } - - is_archived = django_filters.BooleanFilter( - field_name='is_archived', widget=ArchivedFilterWidget) - - class Meta: - model = models.Project - fields = ['is_archived'] - - -class OrderingFilterWidget(widgets.DropdownLinkWidget): - label = _('Sort by') - - -class SortedChoiceWidgetMixin: - ignore_initial = None - collator = pyuca.Collator() - - @property - def choices(self): - if not self._unsorted_choices: - return None - - ignore_initial = self.ignore_initial or 0 - prefix = self._unsorted_choices[:ignore_initial] - to_sort = self._unsorted_choices[ignore_initial:] - - return prefix + sorted( - to_sort, - key=lambda x: self.collator.sort_key(str(x[1])) - ) - - @choices.setter - def choices(self, value): - self._unsorted_choices = list(value) - - -class CountryFilterWidget(SortedChoiceWidgetMixin, widgets.DropdownLinkWidget): - label = _('Country') - ignore_initial = 1 # exclude all option from sort - - -class FreeTextSearchFilterWidget(widgets.FreeTextFilterWidget): - label = _('Search') - - -class CountryFilter(django_filters.ChoiceFilter): - - def __init__(self, **kwargs): - kwargs.setdefault('choices', list(Countries().countries.items())) - kwargs.setdefault('widget', CountryFilterWidget) - super().__init__(**kwargs) diff --git a/euth/contrib/formset.py b/euth/contrib/formset.py deleted file mode 100644 index 47685b694..000000000 --- a/euth/contrib/formset.py +++ /dev/null @@ -1,19 +0,0 @@ -from django import forms - - -class DynamicModelFormSet(forms.BaseModelFormSet): - @property - def media(self): - additional_js = forms.Media(js={ - 'js/category_formset.js' - }) - return super().media + additional_js - - -def dynamic_modelformset_factory(*args, **kwargs): - kwargs.update({ - 'formset': DynamicModelFormSet - }) - modelformset = forms.modelformset_factory(*args, **kwargs) - - return modelformset diff --git a/euth/contrib/mixins.py b/euth/contrib/mixins.py deleted file mode 100644 index fcfe1d6b0..000000000 --- a/euth/contrib/mixins.py +++ /dev/null @@ -1,27 +0,0 @@ -from django import forms -from django.utils.translation import gettext_lazy as _ - -RIGHT_OF_USE_LABEL = _('I hereby confirm that the copyrights for this ' - 'photo are with me or that I have received ' - 'rights of use from the author. I also confirm ' - 'that the privacy rights of depicted third persons ' - 'are not violated. ') - - -class ImageRightOfUseMixin(forms.ModelForm): - right_of_use = forms.BooleanField(required=False, label=RIGHT_OF_USE_LABEL) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.instance.image: - self.initial['right_of_use'] = True - - def clean(self): - cleaned_data = super().clean() - image = cleaned_data.get('image') - right_of_use = cleaned_data.get('right_of_use') - if image and not right_of_use: - self.add_error('right_of_use', - _("You want to upload an image. " - "Please check that you have the " - "right of use for the image.")) diff --git a/euth/contrib/services.py b/euth/contrib/services.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/euth/contrib/static/js/dateTimeInput.js b/euth/contrib/static/js/dateTimeInput.js deleted file mode 100644 index 14bc4b480..000000000 --- a/euth/contrib/static/js/dateTimeInput.js +++ /dev/null @@ -1,2 +0,0 @@ -/* global $ */ -$('.flatpickr').flatpickr() diff --git a/euth/contrib/static/js/formset.js b/euth/contrib/static/js/formset.js deleted file mode 100644 index a9186c582..000000000 --- a/euth/contrib/static/js/formset.js +++ /dev/null @@ -1,42 +0,0 @@ -/* global $ */ -(function (init) { - $(init) - $(document).on('a4.embed.ready', init) -})(function () { - // Dynamically add more subforms to a formset. - // Removing subforms is not (yet) supported) - - const $formsets = $('.js-formset') - const PLACEHOLDER = /__prefix__/g - const dynamicFormSets = [] - - const DynamicFormSet = function ($formset) { - this.$formset = $formset - this.$formTemplate = this.$formset.find('.js-form-template') - this.prefix = this.$formset.data('prefix') - this.$totalInput = this.$formset.find('#id_' + this.prefix + '-TOTAL_FORMS') - this.id = parseInt(this.$formset.data('initial-id')) - this.maxNum = parseInt(this.$formset.find('#id_' + this.prefix + '-MAX_NUM_FORMS').val()) - - this.$formset.find('.js-add-form').on('click', this.addForm.bind(this)) - } - - DynamicFormSet.prototype.addForm = function () { - if (this.id + 1 < this.maxNum) { - this.id += 1 - this.$totalInput.val(this.id + 1) - const newForm = getNewForm(this.$formTemplate, this.id) - this.$formTemplate.before(newForm) - } - } - - function getNewForm ($formTemplate, id) { - return $formTemplate.html().replace(PLACEHOLDER, id) - } - - $formsets.each(function (i) { - dynamicFormSets.push( - new DynamicFormSet($formsets.eq(i)) - ) - }) -}) diff --git a/euth/contrib/templates/euth_contrib/includes/pagination.html b/euth/contrib/templates/euth_contrib/includes/pagination.html deleted file mode 100644 index 608858840..000000000 --- a/euth/contrib/templates/euth_contrib/includes/pagination.html +++ /dev/null @@ -1,31 +0,0 @@ -{% load i18n contrib_tags %} - -
- -
diff --git a/euth/contrib/templatetags/__init__.py b/euth/contrib/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/euth/contrib/templatetags/contrib_tags.py b/euth/contrib/templatetags/contrib_tags.py deleted file mode 100644 index 45baefb1a..000000000 --- a/euth/contrib/templatetags/contrib_tags.py +++ /dev/null @@ -1,26 +0,0 @@ -from django import template - -register = template.Library() - - -@register.simple_tag -def combined_url_parameter(request_query_dict, **kwargs): - combined_query_dict = request_query_dict.copy() - for key in kwargs: - combined_query_dict.setlist(key, [kwargs[key]]) - encoded_parameter = '?' + combined_query_dict.urlencode() - return encoded_parameter - - -@register.simple_tag -def limited_paginator(page_obj): - ADDITIONAL_PAGES = 3 - - current_index = page_obj.number - 1 - last_index = page_obj.paginator.num_pages - start_index = current_index - ADDITIONAL_PAGES if \ - current_index > ADDITIONAL_PAGES else 0 - end_index = current_index + ADDITIONAL_PAGES if \ - current_index < last_index - ADDITIONAL_PAGES else last_index - - return page_obj.paginator.page_range[start_index:end_index] diff --git a/euth/contrib/validators.py b/euth/contrib/validators.py deleted file mode 100644 index 8deab3e50..000000000 --- a/euth/contrib/validators.py +++ /dev/null @@ -1,30 +0,0 @@ -import magic -from django.conf import settings -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ - - -def validate_file_type_and_size(upload): - - file_max_mb = 5 - max_size = file_max_mb * 10**6 - fileformats = settings.FILE_ALIASES['*']['fileformats'] - mimetypes = [mimetype for name, mimetype in fileformats] - names = [name for name, mimetype in fileformats] - - errors = [] - - filetype = magic.from_buffer(upload.read(), mime=True) - if filetype.lower() not in mimetypes: - msg = _( - 'Unsupported file format. Supported formats are {}.'.format( - ', '.join(names) - ) - ) - errors.append(ValidationError(msg)) - if upload.size > max_size: - msg = _('File should be at most {} MB'.format(file_max_mb)) - errors.append(ValidationError(msg)) - if errors: - raise ValidationError(errors) - return upload diff --git a/euth/contrib/widgets.py b/euth/contrib/widgets.py deleted file mode 100644 index 8262fd004..000000000 --- a/euth/contrib/widgets.py +++ /dev/null @@ -1,112 +0,0 @@ -from os.path import basename - -from django.forms import widgets -from django.template import loader -from django.utils import formats -from django.utils.html import conditional_escape -from django.utils.safestring import mark_safe -from django.utils.translation import gettext - - -class DateInput(widgets.DateInput): - - class Media: - js = ( - 'flatpickr.js', - 'js/dateTimeInput.js', - ) - css = {'all': ( - 'flatpickr.css', - )} - - input_type = 'text' - format_index = 0 - - def render(self, name, value, attrs=None, renderer=None): - - if attrs: - format = formats.get_format( - self.format_key - )[self.format_index] - attrs.update({ - 'class': attrs.get('class', '') + ' flatpickr', - 'data-date-format': format.replace('%', '').replace('M', 'i'), - }) - - if hasattr(self, 'additional_attrs'): - attrs.update(self.additional_attrs) - - input = mark_safe(super().render(name, value, attrs)) - - return input - - -class DateTimeInput(DateInput): - - additional_attrs = { - 'data-time_24hr': 'true', - 'data-enable-time': 'true', - } - - format_index = 0 - format_key = 'DATETIME_INPUT_FORMATS' - - -class FileUploadWidget(widgets.ClearableFileInput): - def render(self, name, value, attrs=None, renderer=None): - has_file_set = self.is_initial(value) - is_required = self.is_required - - file_placeholder = gettext('Select a file from your local folder.') - file_input = super().render(name, None, { - 'id': name, - 'class': 'form-control form-control-file' - }) - - if has_file_set: - file_name = basename(value.name) - file_url = conditional_escape(value.url) - else: - file_name = "" - file_url = "" - - text_input = widgets.TextInput().render('__noname__', file_name, { - 'class': 'form-control form-control-file-dummy', - 'placeholder': file_placeholder - }) - - checkbox_id = self.clear_checkbox_id(name) - checkbox_name = self.clear_checkbox_name(name) - checkbox_input = widgets.CheckboxInput().render(checkbox_name, False, { - 'id': checkbox_id, - 'class': 'clear-image', - 'data-upload-clear': name, - }) - - context = { - 'name': name, - 'has_image_set': has_file_set, - 'is_required': is_required, - 'file_url': file_url, - 'file_input': file_input, - 'file_id': name + '-file', - 'text_input': text_input, - 'checkbox_input': checkbox_input, - 'checkbox_id': checkbox_id - } - - return mark_safe( - loader.render_to_string( - 'euth_offlinephases/file_upload_widget.html', - context - ) - ) - - def value_from_datadict(self, data, files, name): - file_value = super(widgets.ClearableFileInput, self) \ - .value_from_datadict(data, files, name) - checkbox_value = widgets.CheckboxInput() \ - .value_from_datadict(data, files, self.clear_checkbox_name(name)) - if not self.is_required and checkbox_value: - return False - return file_value