diff --git a/bluebottle/activities/documents.py b/bluebottle/activities/documents.py index 8f3b74b66e..0a4a054bd6 100644 --- a/bluebottle/activities/documents.py +++ b/bluebottle/activities/documents.py @@ -1,6 +1,7 @@ from builtins import str from builtins import object -from django_elasticsearch_dsl import DocType, fields + +from bluebottle.initiatives.documents import DocType, fields from bluebottle.utils.documents import MultiTenantIndex from bluebottle.activities.models import Activity diff --git a/bluebottle/auth/middleware.py b/bluebottle/auth/middleware.py index 488486885a..2eaa5a3c99 100644 --- a/bluebottle/auth/middleware.py +++ b/bluebottle/auth/middleware.py @@ -1,4 +1,3 @@ -from builtins import object import json import logging from calendar import timegm @@ -12,6 +11,7 @@ from django.http.request import RawPostDataException from django.shortcuts import render_to_response from django.utils import timezone +from django.utils.deprecation import MiddlewareMixin from lockdown import settings as lockdown_settings from lockdown.middleware import (LockdownMiddleware as BaseLockdownMiddleware, compile_url_exceptions, get_lockdown_form) @@ -32,7 +32,7 @@ def isAdminRequest(request): return request.path.startswith('/downloads') or base_path in ['jet', 'admin', 'jet-dashboard'] -class UserJwtTokenMiddleware(object): +class UserJwtTokenMiddleware(MiddlewareMixin): """ Custom middleware to set the User on the request when using Jwt Token authentication. @@ -41,7 +41,8 @@ class UserJwtTokenMiddleware(object): def process_request(self, request): """ Override only the request to add the user """ try: - return request.user + request.user + return except AttributeError: pass @@ -64,7 +65,7 @@ def process_request(self, request): return -class SlidingJwtTokenMiddleware(object): +class SlidingJwtTokenMiddleware(MiddlewareMixin): """ Custom middleware to set a sliding window for the jwt auth token expiration. """ @@ -177,7 +178,7 @@ def process_request(self, request): super(AdminOnlyAuthenticationMiddleware, self).process_request(request) -class AdminOnlyCsrf(object): +class AdminOnlyCsrf(MiddlewareMixin): """ Disable csrf for non-Admin requests, eg API """ @@ -280,7 +281,7 @@ def process_request(self, request): authorization_logger = logging.getLogger(__name__) -class LogAuthFailureMiddleWare(object): +class LogAuthFailureMiddleWare(MiddlewareMixin): def process_request(self, request): # TODO: Handle this more cleanly. The exception is raised when using IE11. # Possibly related to the following issue: diff --git a/bluebottle/bb_accounts/views.py b/bluebottle/bb_accounts/views.py index 466225f14a..3124f1275f 100644 --- a/bluebottle/bb_accounts/views.py +++ b/bluebottle/bb_accounts/views.py @@ -17,7 +17,10 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.exceptions import PermissionDenied, NotAuthenticated, ValidationError -from rest_framework_jwt.views import ObtainJSONWebToken +try: + from rest_framework_jwt.views import ObtainJSONWebToken +except ImportError: + from rest_framework_jwt.views import ObtainJSONWebTokenView as ObtainJSONWebToken from rest_framework_json_api.views import AutoPrefetchMixin diff --git a/bluebottle/bluebottle_dashboard/templates/admin/change_list.html b/bluebottle/bluebottle_dashboard/templates/admin/change_list.html index f79338065d..8aea16b06f 100644 --- a/bluebottle/bluebottle_dashboard/templates/admin/change_list.html +++ b/bluebottle/bluebottle_dashboard/templates/admin/change_list.html @@ -1,5 +1,5 @@ {% extends "admin:admin/change_list.html" %} -{% load i18n admin_static admin_list %} +{% load i18n static admin_list %} {% load admin_urls %} {% load humanize %} {% load admin_extra %} diff --git a/bluebottle/bluebottle_drf2/middleware.py b/bluebottle/bluebottle_drf2/middleware.py index ad60815179..8d2aae48ab 100644 --- a/bluebottle/bluebottle_drf2/middleware.py +++ b/bluebottle/bluebottle_drf2/middleware.py @@ -1,8 +1,9 @@ -from builtins import object +from django.utils.deprecation import MiddlewareMixin + METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE' -class MethodOverrideMiddleware(object): +class MethodOverrideMiddleware(MiddlewareMixin): def process_view(self, request, callback, callback_args, callback_kwargs): if request.method != 'POST': return diff --git a/bluebottle/bluebottle_drf2/renderers.py b/bluebottle/bluebottle_drf2/renderers.py index 9f34fea991..27662510b2 100644 --- a/bluebottle/bluebottle_drf2/renderers.py +++ b/bluebottle/bluebottle_drf2/renderers.py @@ -12,7 +12,11 @@ ) from rest_framework.settings import api_settings -from rest_framework_json_api.compat import collections_abc +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc + from rest_framework_json_api.relations import HyperlinkedMixin, ResourceRelatedField, SkipDataMixin from rest_framework_json_api.renderers import JSONRenderer from rest_framework_json_api import utils diff --git a/bluebottle/clients/__init__.py b/bluebottle/clients/__init__.py index ba787d262b..79af7079da 100644 --- a/bluebottle/clients/__init__.py +++ b/bluebottle/clients/__init__.py @@ -5,8 +5,6 @@ from django.conf import settings from django.utils._os import safe_join -from tenant_schemas.postgresql_backend.base import FakeTenant - logger = logging.getLogger(__name__) @@ -35,6 +33,8 @@ def set_tenant(self, tenant): self.tenant_properties) except (ImportError, AttributeError, IOError): + from tenant_schemas.postgresql_backend.base import FakeTenant + if not isinstance(tenant, FakeTenant): logger.debug('No tenant properties found for: {0}'.format(tenant.client_name)) diff --git a/bluebottle/clients/middleware.py b/bluebottle/clients/middleware.py index 48cbcc525f..9ea3db3db1 100644 --- a/bluebottle/clients/middleware.py +++ b/bluebottle/clients/middleware.py @@ -1,14 +1,14 @@ from future import standard_library standard_library.install_aliases() -from builtins import object from urllib.parse import urljoin from django.db import connection from django.conf import settings from django.shortcuts import redirect +from django.utils.deprecation import MiddlewareMixin -class MediaMiddleware(object): +class MediaMiddleware(MiddlewareMixin): def process_response(self, request, response): if ( response.status_code == 404 and diff --git a/bluebottle/cms/templates/admin/edit_inline/stacked-nested.html b/bluebottle/cms/templates/admin/edit_inline/stacked-nested.html index 26329104e9..6b3908e36c 100644 --- a/bluebottle/cms/templates/admin/edit_inline/stacked-nested.html +++ b/bluebottle/cms/templates/admin/edit_inline/stacked-nested.html @@ -1,4 +1,4 @@ -{% load i18n admin_urls admin_static django_template_additions %} +{% load i18n admin_urls static django_template_additions %} <div class="js-inline-admin-formset sortable sortable-ui inline-group{% if recursive_formset %} {{ recursive_formset.formset.prefix|default:"Root" }}-nested-inline {% if prev_prefix %} {{ prev_prefix }}-{{ loopCounter }}-nested-inline{% endif %} nested-inline{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-group"> {% with recursive_formset=inline_admin_formset stacked_template='admin/edit_inline/stacked-nested.html' tabular_template='admin/edit_inline/tabular-nested.html'%} <h2>{{ recursive_formset.opts.verbose_name_plural|title }}</h2> diff --git a/bluebottle/common/admin_utils.py b/bluebottle/common/admin_utils.py index 8e42f63e68..5f3a70d616 100644 --- a/bluebottle/common/admin_utils.py +++ b/bluebottle/common/admin_utils.py @@ -58,9 +58,8 @@ def label_for_value(self, value): class ImprovedModelForm(admin.ModelAdmin): - def formfield_for_dbfield(self, db_field, **kwargs): + def formfield_for_dbfield(self, db_field, request=None, **kwargs): if db_field.name in self.raw_id_fields: - kwargs.pop("request", None) type = db_field.rel.__class__.__name__ if type == "ManyToOneRel": kwargs['widget'] = VerboseForeignKeyRawIdWidget(db_field.rel, @@ -69,5 +68,7 @@ def formfield_for_dbfield(self, db_field, **kwargs): kwargs['widget'] = VerboseManyToManyRawIdWidget(db_field.rel, site) return db_field.formfield(**kwargs) + if request: + kwargs["request"] = request return super(ImprovedModelForm, self).formfield_for_dbfield(db_field, **kwargs) diff --git a/bluebottle/exports/templates/exportdb/base.html b/bluebottle/exports/templates/exportdb/base.html index 31332d1f7f..479bba451c 100755 --- a/bluebottle/exports/templates/exportdb/base.html +++ b/bluebottle/exports/templates/exportdb/base.html @@ -1,4 +1,4 @@ -{% extends "admin/base_site.html" %}{% load i18n admin_static %} +{% extends "admin/base_site.html" %}{% load i18n static %} {% block extrahead %}{{ block.super }} {% if jquery_in_vendor %} diff --git a/bluebottle/exports/templates/exportdb/in_progress.html b/bluebottle/exports/templates/exportdb/in_progress.html index e5cfe02b3d..72461a78c0 100755 --- a/bluebottle/exports/templates/exportdb/in_progress.html +++ b/bluebottle/exports/templates/exportdb/in_progress.html @@ -1,4 +1,4 @@ -{% extends "exportdb/base.html" %}{% load i18n admin_static %} +{% extends "exportdb/base.html" %}{% load i18n static %} {% block extrahead %}{{ block.super }} <style type="text/css"> diff --git a/bluebottle/files/views.py b/bluebottle/files/views.py index dfd1d92f2d..a22c98209a 100644 --- a/bluebottle/files/views.py +++ b/bluebottle/files/views.py @@ -15,7 +15,10 @@ from bluebottle.files.serializers import FileSerializer, ImageSerializer, PrivateFileSerializer from bluebottle.utils.views import CreateAPIView, RetrieveAPIView -mime = magic.Magic(mime=True) +try: + mime = magic.Magic(mime=True) +except TypeError: + mime = magic.Magic() class FileList(AutoPrefetchMixin, CreateAPIView): diff --git a/bluebottle/fsm/forms.py b/bluebottle/fsm/forms.py index 15ab9cf643..3d0d958658 100644 --- a/bluebottle/fsm/forms.py +++ b/bluebottle/fsm/forms.py @@ -17,8 +17,9 @@ class StateWidget(forms.TextInput): class StateMachineModelFormMetaClass(ModelFormMetaclass): def __new__(cls, name, bases, attrs): - if 'Meta' in attrs: - for field, machine in list(attrs['Meta'].model._state_machines.items()): + model = getattr(attrs.get('Meta', None), 'model', None) + if hasattr(model, '_state_machines'): + for field, machine in list(model._state_machines.items()): attrs[field] = forms.ChoiceField( required=False, widget=TransitionSelectWidget() diff --git a/bluebottle/funding/admin.py b/bluebottle/funding/admin.py index d590184763..5af532af2c 100644 --- a/bluebottle/funding/admin.py +++ b/bluebottle/funding/admin.py @@ -110,7 +110,7 @@ class PayoutInline(StateMachineAdminMixin, admin.TabularInline): extra = 0 can_delete = False - def has_add_permission(self, request): + def has_add_permission(self, request, obj=None): return False def payout_link(self, obj): @@ -642,7 +642,7 @@ class DonorInline(PaymentLinkMixin, admin.TabularInline): extra = 0 can_delete = False - def has_add_permission(self, request): + def has_add_permission(self, request, obj=None): return False diff --git a/bluebottle/funding/documents.py b/bluebottle/funding/documents.py index 1fc0dac43a..131fdfd4ad 100644 --- a/bluebottle/funding/documents.py +++ b/bluebottle/funding/documents.py @@ -18,6 +18,8 @@ class Meta(object): model = Funding related_models = (Initiative, Member, Donor) + Django = Meta + def get_instances_from_related(self, related_instance): if isinstance(related_instance, Initiative): return Funding.objects.filter(initiative=related_instance) diff --git a/bluebottle/funding/templates/admin/funding/payment/change_form.html b/bluebottle/funding/templates/admin/funding/payment/change_form.html index 7ab8be3546..438beede16 100644 --- a/bluebottle/funding/templates/admin/funding/payment/change_form.html +++ b/bluebottle/funding/templates/admin/funding/payment/change_form.html @@ -1,5 +1,5 @@ {% extends "admin/change_form.html" %} -{% load i18n admin_static admin_list admin_urls %} +{% load i18n static admin_list admin_urls %} {% block object-tools-items %} {% if original.can_update %} diff --git a/bluebottle/funding_stripe/templates/admin/funding_stripe/stripepayoutaccount/change_form.html b/bluebottle/funding_stripe/templates/admin/funding_stripe/stripepayoutaccount/change_form.html index 6b71eab880..fa4702463e 100644 --- a/bluebottle/funding_stripe/templates/admin/funding_stripe/stripepayoutaccount/change_form.html +++ b/bluebottle/funding_stripe/templates/admin/funding_stripe/stripepayoutaccount/change_form.html @@ -1,5 +1,5 @@ {% extends "admin/change_form.html" %} -{% load i18n admin_static admin_list admin_urls %} +{% load i18n static admin_list admin_urls %} {% block object-tools-items %} <li> diff --git a/bluebottle/initiatives/documents.py b/bluebottle/initiatives/documents.py index 1dfc5891d2..7ce3d5d506 100644 --- a/bluebottle/initiatives/documents.py +++ b/bluebottle/initiatives/documents.py @@ -1,5 +1,10 @@ from builtins import object -from django_elasticsearch_dsl import DocType, fields + +try: + from django_elasticsearch_dsl.documents import DocType +except ImportError: + from django_elasticsearch_dsl import DocType +from django_elasticsearch_dsl import fields from bluebottle.time_based.models import PeriodActivity, DateActivity from bluebottle.utils.documents import MultiTenantIndex @@ -95,6 +100,8 @@ class Meta(object): DateActivity ) + Django = Meta + def get_queryset(self): return super(InitiativeDocument, self).get_queryset().select_related( 'theme', 'place', 'owner', 'promoter', diff --git a/bluebottle/members/admin.py b/bluebottle/members/admin.py index e6d6aa50f2..03062a0bab 100644 --- a/bluebottle/members/admin.py +++ b/bluebottle/members/admin.py @@ -7,6 +7,7 @@ from django import forms from django.conf.urls import url from django.contrib import admin +from django.contrib.admin.sites import NotRegistered from django.contrib.auth.admin import UserAdmin, GroupAdmin from django.contrib.auth.models import Group, Permission from django.contrib.auth.tokens import default_token_generator @@ -203,7 +204,7 @@ class UserActivityInline(admin.TabularInline): formset = LimitModelFormset - def has_add_permission(self, request): + def has_add_permission(self, request, obj=None): return False @@ -577,5 +578,8 @@ class TokenAdmin(admin.ModelAdmin): fields = ('user', 'key') -admin.site.unregister(Token) +try: + admin.site.unregister(Token) +except NotRegistered: + pass admin.site.register(Token, TokenAdmin) diff --git a/bluebottle/members/serializers.py b/bluebottle/members/serializers.py index f65fc64e83..301a65acb3 100644 --- a/bluebottle/members/serializers.py +++ b/bluebottle/members/serializers.py @@ -1,5 +1,4 @@ from builtins import object -from axes.attempts import is_already_locked from django import forms from django.conf import settings from django.contrib.auth import get_user_model, password_validation, authenticate @@ -23,6 +22,11 @@ from bluebottle.tasks.models import Skill from bluebottle.utils.serializers import PermissionField, TruncatedCharField, CaptchaField +try: + from axes.attempts import is_already_locked +except ImportError: + is_already_locked = lambda request: request.axes_locked_out + BB_USER_MODEL = get_user_model() diff --git a/bluebottle/notifications/admin.py b/bluebottle/notifications/admin.py index 30133e558b..4a322803d4 100644 --- a/bluebottle/notifications/admin.py +++ b/bluebottle/notifications/admin.py @@ -23,7 +23,7 @@ class MessageAdminInline(GenericTabularInline): readonly_fields = ['sent', 'subject', 'recipient'] fields = readonly_fields - def has_add_permission(self, request): + def has_add_permission(self, request, obj=None): return False extra = 0 diff --git a/bluebottle/redirects/middleware.py b/bluebottle/redirects/middleware.py index 2cfbe2713d..5017cfe948 100644 --- a/bluebottle/redirects/middleware.py +++ b/bluebottle/redirects/middleware.py @@ -1,17 +1,17 @@ from __future__ import unicode_literals -from builtins import object import regex from django import http from django.conf import settings from django.db import connection +from django.utils.deprecation import MiddlewareMixin from bluebottle.redirects.models import Redirect from bluebottle.clients import properties -class RedirectFallbackMiddleware(object): +class RedirectFallbackMiddleware(MiddlewareMixin): """ A modified version of django.contrib.redirects, this app allows us to optionally redirect users using regular expressions. diff --git a/bluebottle/scim/templates/admin/scim/scimplatformsettings/change_form.html b/bluebottle/scim/templates/admin/scim/scimplatformsettings/change_form.html index fee92445b4..311caf596a 100644 --- a/bluebottle/scim/templates/admin/scim/scimplatformsettings/change_form.html +++ b/bluebottle/scim/templates/admin/scim/scimplatformsettings/change_form.html @@ -1,5 +1,5 @@ {% extends "admin/change_form.html" %} -{% load i18n admin_static admin_list admin_urls %} +{% load i18n static admin_list admin_urls %} {% block object-tools-items %} <li> diff --git a/bluebottle/settings/base.py b/bluebottle/settings/base.py index 33477ef7c9..241aac6946 100644 --- a/bluebottle/settings/base.py +++ b/bluebottle/settings/base.py @@ -118,7 +118,7 @@ 'tenant_extras.template_loaders.FilesystemLoader', 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', - 'django.template.loaders.eggs.Loader', + # 'django.template.loaders.eggs.Loader', 'admin_tools.template_loaders.Loader', ], 'context_processors': [ @@ -149,7 +149,7 @@ 'bluebottle.clients.middleware.MediaMiddleware', 'tenant_extras.middleware.TenantLocaleMiddleware', 'bluebottle.redirects.middleware.RedirectFallbackMiddleware', - 'bluebottle.auth.middleware.UserJwtTokenMiddleware', + 'bluebottle.auth.middleware.UserJwtTokenMiddleware', # 'Member' object has no attribute 'has_header' 'bluebottle.utils.middleware.SubDomainSessionMiddleware', 'bluebottle.utils.middleware.APILanguageMiddleware', 'bluebottle.auth.middleware.AdminOnlySessionMiddleware', diff --git a/bluebottle/urls/i18n_urls.py b/bluebottle/urls/i18n_urls.py index 13750c08ee..767667bae3 100644 --- a/bluebottle/urls/i18n_urls.py +++ b/bluebottle/urls/i18n_urls.py @@ -1,6 +1,6 @@ +from django import VERSION from django.conf.urls import include, url from django.contrib import admin -from django.contrib.auth.views import password_reset_done, password_reset_confirm from django.core.urlresolvers import reverse_lazy from django.views.generic import RedirectView @@ -9,10 +9,23 @@ from bluebottle.bluebottle_dashboard.views import locked_out from bluebottle.looker.dashboard_views import LookerEmbedView # noqa This has to be imported early so that custom urls will work +try: + from django.contrib.auth.views import password_reset_done, password_reset_confirm +except ImportError: + from django.contrib.auth.views import PasswordResetDoneView, PasswordResetConfirmView + password_reset_done = PasswordResetDoneView.as_view() + password_reset_confirm = PasswordResetConfirmView.as_view() admin.autodiscover() +def dj1_include_with_namespace(dotted, namespace): + if VERSION >= (2, 0): + return include(dotted) + else: + return include(dotted, namespace) + + urlpatterns = [ # Django JET URLS @@ -41,7 +54,7 @@ # account login/logout, password reset, and password change url(r'^accounts/', - include('django.contrib.auth.urls', namespace='accounts')), + dj1_include_with_namespace('django.contrib.auth.urls', namespace='accounts')), url(r'^admin/summernote/', include('django_summernote.urls')), url(r'^admin', RedirectView.as_view(url=reverse_lazy('admin:index')), name='admin-slash'), diff --git a/bluebottle/utils/managers.py b/bluebottle/utils/managers.py index 089a52ada8..9653748784 100644 --- a/bluebottle/utils/managers.py +++ b/bluebottle/utils/managers.py @@ -5,11 +5,15 @@ from django.db.models.query import QuerySet from django.db.models.query_utils import Q from django.utils.timezone import now -from django_subquery.expressions import Subquery, OuterRef from parler.managers import TranslatableQuerySet, TranslatableManager from polymorphic.managers import PolymorphicManager from polymorphic.query import PolymorphicQuerySet +try: + from django.db.models.expressions import Subquery, OuterRef +except ImportError: + from django_subquery.expressions import Subquery, OuterRef + class GenericForeignKeyManagerMixin(object): """ diff --git a/bluebottle/utils/templates/utils/admin/total_amount_change_list.html b/bluebottle/utils/templates/utils/admin/total_amount_change_list.html index ea43e4e09b..2626877600 100644 --- a/bluebottle/utils/templates/utils/admin/total_amount_change_list.html +++ b/bluebottle/utils/templates/utils/admin/total_amount_change_list.html @@ -1,5 +1,5 @@ {% extends "admin/change_list.html" %} -{% load i18n admin_static admin_list %} +{% load i18n static admin_list %} {% load admin_urls %} {% load humanize %} diff --git a/bluebottle/utils/validators.py b/bluebottle/utils/validators.py index e935d410db..29955d7b37 100644 --- a/bluebottle/utils/validators.py +++ b/bluebottle/utils/validators.py @@ -21,7 +21,10 @@ logger = logging.getLogger(__name__) -mime = magic.Magic(mime=True) +try: + mime = magic.Magic(mime=True) +except TypeError: + mime = magic.Magic() # Can safely add more post code form fields here. postal_code_mapping = { diff --git a/bluebottle/utils/views.py b/bluebottle/utils/views.py index edc24ca8b1..5bf37f7dba 100644 --- a/bluebottle/utils/views.py +++ b/bluebottle/utils/views.py @@ -28,7 +28,10 @@ from .models import Language from .serializers import LanguageSerializer -mime = magic.Magic(mime=True) +try: + mime = magic.Magic(mime=True) +except TypeError: + mime = magic.Magic() class TagList(views.APIView): diff --git a/bluebottle/wallposts/admin.py b/bluebottle/wallposts/admin.py index bfa95e72d0..2a0abf1a66 100644 --- a/bluebottle/wallposts/admin.py +++ b/bluebottle/wallposts/admin.py @@ -195,9 +195,9 @@ class WallpostParentAdmin(PolymorphicParentModelAdmin): 'author__first_name', 'author__last_name', 'ip_address' ) child_models = ( - (MediaWallpost, MediaWallpostAdmin), - (TextWallpost, TextWallpostAdmin), - (SystemWallpost, SystemWallpostAdmin), + MediaWallpost, + TextWallpost, + SystemWallpost, ) def type(self, obj): @@ -310,5 +310,5 @@ def wallpost(self, obj): url = reverse('admin:wallposts_wallpost_change', args=(obj.id,)) return format_html(u'<a href="{}">{}</a>', url, obj) - def has_add_permission(self, request): + def has_add_permission(self, request, obj=None): return False diff --git a/setup.py b/setup.py index d09b64d8cc..05ff22c220 100755 --- a/setup.py +++ b/setup.py @@ -49,6 +49,7 @@ def read_file(name): 'django-memoize==2.1.0', 'django-modeltranslation==0.12.1', 'django-money==0.15.1', + 'django-nested-inline==0.4.2', 'django-parler==1.9.2', 'django-permissions-widget==1.5.1', 'django_polymorphic==1.2',