diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 143585e..b321968 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -14,32 +14,38 @@ jobs: max-parallel: 4 matrix: include: - - python-version: 3.6 - django-version: Django==1.11 - - - python-version: 3.6 - django-version: Django==2.0 - - - python-version: 3.6 - django-version: Django==2.1 - - - python-version: 3.6 + - python-version: "3.6" django-version: Django==2.2 - - python-version: 3.6 - django-version: Django==3.0 + - python-version: "3.6" + django-version: Django==3.2 - - python-version: 3.7 + - python-version: "3.7" django-version: Django==2.2 - - python-version: 3.7 - django-version: Django==3.0 + - python-version: "3.7" + django-version: Django==3.2 - - python-version: 3.8 - django-version: Django==3.1 + - python-version: "3.8" + django-version: Django==3.2 - - python-version: 3.9 - django-version: Django==3.1 + - python-version: "3.8" + django-version: Django==4.0 + + - python-version: "3.9" + django-version: Django==2.2 + + - python-version: "3.9" + django-version: Django==3.2 + + - python-version: "3.9" + django-version: Django==4.0 + + - python-version: "3.10" + django-version: Django==3.2 + + - python-version: "3.10" + django-version: Django==4.0 steps: - uses: actions/checkout@v2 diff --git a/runtests.py b/runtests.py index b566ecd..0eaea76 100755 --- a/runtests.py +++ b/runtests.py @@ -7,11 +7,6 @@ import django from django.conf import settings from django.core.management import execute_from_command_line -from tos.compat import get_middleware_settings_key - - -middleware_settings_key = get_middleware_settings_key() - if not settings.configured: django_settings = { @@ -20,6 +15,7 @@ if not settings.configured: 'ENGINE': 'django.db.backends.sqlite3', } }, + 'DEFAULT_AUTO_FIELD': 'django.db.models.AutoField', 'INSTALLED_APPS': [ 'django.contrib.auth', 'django.contrib.contenttypes', @@ -46,7 +42,7 @@ if not settings.configured: ], 'ROOT_URLCONF': 'tos.tests.test_urls', 'LOGIN_URL': '/login/', - 'SITE_ID': '1', + 'SITE_ID': 1, 'CACHES': { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', @@ -55,30 +51,19 @@ if not settings.configured: 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', } }, + 'SECRET_KEY': '7v%d@z##e=8z5#oc=cc-o%!cka5ibyy7#9r!#2fyiwn7ki020y', 'TOS_CACHE_NAME': 'tos' } - if django.VERSION >= (1, 10, 0): - django_settings[middleware_settings_key] = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - ] - else: - django_settings[middleware_settings_key] = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - ] + django_settings['MIDDLEWARE'] = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + ] settings.configure(**django_settings) diff --git a/tos/__init__.py b/tos/__init__.py index 51e8a3a..745750e 100644 --- a/tos/__init__.py +++ b/tos/__init__.py @@ -1,3 +1 @@ -default_app_config = 'tos.apps.TOSConfig' - VERSION = (0, 9, 0) diff --git a/tos/apps.py b/tos/apps.py index 4491b41..ac86781 100644 --- a/tos/apps.py +++ b/tos/apps.py @@ -4,18 +4,13 @@ from django.db.models.signals import pre_save from .signal_handlers import invalidate_cached_agreements -from .compat import get_middleware_settings_key - - -middleware_settings_key = get_middleware_settings_key() -MIDDLEWARES = getattr(settings, middleware_settings_key, []) - class TOSConfig(AppConfig): name = 'tos' verbose_name = 'Terms Of Service' def ready(self): + MIDDLEWARES = getattr(settings, 'MIDDLEWARE', []) if 'tos.middleware.UserAgreementMiddleware' in MIDDLEWARES: TermsOfService = self.get_model('TermsOfService') diff --git a/tos/compat.py b/tos/compat.py deleted file mode 100644 index 60f3ee9..0000000 --- a/tos/compat.py +++ /dev/null @@ -1,87 +0,0 @@ -import django -from django.conf import settings -from django.utils import deprecation - - -def patterns(mod, *urls): - if mod != '' or django.VERSION < (1, 9): - from django.conf.urls import patterns - return patterns(mod, *urls) - else: - return list(urls) - - -def get_fk_user_model(): - if django.VERSION >= (1, 5): - return settings.AUTH_USER_MODEL - from django.contrib.auth.models import User - return User - - -def get_runtime_user_model(): - if django.VERSION >= (1, 5): - from django.contrib.auth import get_user_model - return get_user_model() - from django.contrib.auth.models import User - return User - - -def get_request_site(): - if django.VERSION >= (1, 9): - from django.contrib.sites.requests import RequestSite - else: - from django.contrib.sites.models import RequestSite - return RequestSite - - -def get_library(): - if django.VERSION >= (1, 9): - from django.template.library import Library - else: - from django.template.base import Library - return Library - - -def get_cache(cache_name): - if django.VERSION >= (1, 7): - from django.core.cache import caches - return caches[cache_name] - else: - from django.core.cache import get_cache - return get_cache(cache_name) - - -def get_middleware_mixin(): - if django.VERSION >= (1, 10, 0): - return deprecation.MiddlewareMixin - else: - return object - - -def get_middleware_settings_key(): - if django.VERSION >= (1, 10, 0): - return 'MIDDLEWARE' - else: - return 'MIDDLEWARE_CLASSES' - - -def get_render(request, template_name, context): - if django.VERSION >= (1, 10, 0): - from django.shortcuts import render - return render(request, template_name, context) - - else: - from django.shortcuts import render_to_response - from django.template import RequestContext - return render_to_response(template_name, context, RequestContext(request)) - - -if django.VERSION < (1, 5): - from django.templatetags.future import url -else: - from django.template.defaulttags import url - -if django.VERSION >= (1, 10, 0): - from django.urls import reverse -else: - from django.core.urlresolvers import reverse diff --git a/tos/middleware.py b/tos/middleware.py index a10bea1..c11352e 100644 --- a/tos/middleware.py +++ b/tos/middleware.py @@ -1,29 +1,34 @@ from django.conf import settings from django.contrib.auth import SESSION_KEY as session_key from django.contrib.auth import REDIRECT_FIELD_NAME +from django.core.cache import caches from django.http import HttpResponseRedirect +from django.urls import reverse from django.utils.cache import add_never_cache_headers +from django.utils.deprecation import MiddlewareMixin -from .compat import get_cache, get_middleware_mixin, reverse from .models import UserAgreement -cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) +cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] tos_check_url = reverse('tos_check_tos') -middleware_mixin = get_middleware_mixin() -class UserAgreementMiddleware(middleware_mixin): + +class UserAgreementMiddleware(MiddlewareMixin): """ Some middleware to check if users have agreed to the latest TOS """ + def __init__(self, get_response=None): + self.get_response = get_response + def process_request(self, request): # Don't get in the way of any mutating requests if request.method != 'GET': return None # Ignore ajax requests - if request.is_ajax(): + if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest': return None # Don't redirect users when they're trying to get to the confirm page diff --git a/tos/models.py b/tos/models.py index 9bd33f5..e424d29 100644 --- a/tos/models.py +++ b/tos/models.py @@ -1,8 +1,8 @@ +from django.conf import settings from django.core.exceptions import ValidationError from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ -from tos.compat import get_fk_user_model class NoActiveTermsOfService(ValidationError): pass @@ -22,7 +22,7 @@ class TermsOfServiceManager(models.Manager): return self.get(active=True) except self.model.DoesNotExist: raise NoActiveTermsOfService( - u'Please create an active Terms-of-Service' + 'Please create an active Terms-of-Service' ) @@ -31,7 +31,7 @@ class TermsOfService(BaseModel): default=False, verbose_name=_('active'), help_text=_( - u'Only one terms of service is allowed to be active' + 'Only one terms of service is allowed to be active' ) ) content = models.TextField(verbose_name=_('content'), blank=True) @@ -43,7 +43,7 @@ class TermsOfService(BaseModel): verbose_name = _('Terms of Service') verbose_name_plural = _('Terms of Service') - def __unicode__(self): + def __str__(self): active = 'inactive' if self.active: active = 'active' @@ -61,7 +61,7 @@ class TermsOfService(BaseModel): .filter(active=True)\ .exists(): raise NoActiveTermsOfService( - u'One of the terms of service must be marked active' + 'One of the terms of service must be marked active' ) super(TermsOfService, self).save(*args, **kwargs) @@ -69,10 +69,10 @@ class TermsOfService(BaseModel): class UserAgreement(BaseModel): terms_of_service = models.ForeignKey(TermsOfService, related_name='terms', on_delete=models.CASCADE) - user = models.ForeignKey(get_fk_user_model(), related_name='user_agreement', on_delete=models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_agreement', on_delete=models.CASCADE) - def __unicode__(self): - return u'%s agreed to TOS: %s' % (self.user.username, + def __str__(self): + return '%s agreed to TOS: %s' % (self.user.username, unicode(self.terms_of_service)) diff --git a/tos/signal_handlers.py b/tos/signal_handlers.py index 63f770f..c29398c 100644 --- a/tos/signal_handlers.py +++ b/tos/signal_handlers.py @@ -1,9 +1,9 @@ from django.conf import settings +from django.core.cache import caches -from .compat import get_cache # Force the user to create a separate cache -cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) +cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] def invalidate_cached_agreements(TermsOfService, instance, **kwargs): diff --git a/tos/templates/tos/tos_check.html b/tos/templates/tos/tos_check.html index 41841e4..42dd643 100644 --- a/tos/templates/tos/tos_check.html +++ b/tos/templates/tos/tos_check.html @@ -1,5 +1,4 @@ {% load i18n %} -{% load url from compat %} {% if note %}

{{ note }} {% else %} diff --git a/tos/templatetags/compat.py b/tos/templatetags/compat.py index 9e0499d..12c1df7 100644 --- a/tos/templatetags/compat.py +++ b/tos/templatetags/compat.py @@ -1,10 +1 @@ -from tos.compat import url as tos_url, get_library - - -Library = get_library() -register = Library() - - -@register.tag -def url(parser, token): - return tos_url(parser, token) +from django.template.defaulttags import url diff --git a/tos/tests/test_middleware.py b/tos/tests/test_middleware.py index 85a96e9..bb64376 100644 --- a/tos/tests/test_middleware.py +++ b/tos/tests/test_middleware.py @@ -1,18 +1,16 @@ from django.conf import settings -from django.contrib.auth import REDIRECT_FIELD_NAME +from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model +from django.core.cache import caches from django.test import TestCase from django.test.utils import modify_settings +from django.urls import reverse -from tos.compat import get_cache, get_runtime_user_model, reverse from tos.middleware import UserAgreementMiddleware from tos.models import TermsOfService, UserAgreement from tos.signal_handlers import invalidate_cached_agreements @modify_settings( - MIDDLEWARE_CLASSES={ - 'append': 'tos.middleware.UserAgreementMiddleware', - }, MIDDLEWARE={ 'append': 'tos.middleware.UserAgreementMiddleware', }, @@ -21,15 +19,15 @@ class TestMiddleware(TestCase): def setUp(self): # Clear cache between tests - cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) + cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] cache.clear() # User that has agreed to TOS - self.user1 = get_runtime_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') + self.user1 = get_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') # User that has not yet agreed to TOS - self.user2 = get_runtime_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass') - self.user3 = get_runtime_user_model().objects.create_user('user3', 'user3@example.com', 'user3pass') + self.user2 = get_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass') + self.user3 = get_user_model().objects.create_user('user3', 'user3@example.com', 'user3pass') self.tos1 = TermsOfService.objects.create( content="first edition of the terms of service", @@ -113,9 +111,6 @@ class TestMiddleware(TestCase): @modify_settings( - MIDDLEWARE_CLASSES={ - 'append': 'tos.middleware.UserAgreementMiddleware', - }, MIDDLEWARE={ 'append': 'tos.middleware.UserAgreementMiddleware', }, @@ -124,7 +119,7 @@ class BumpCoverage(TestCase): def setUp(self): # User that has agreed to TOS - self.user1 = get_runtime_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') + self.user1 = get_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') self.tos1 = TermsOfService.objects.create( content="first edition of the terms of service", @@ -144,6 +139,9 @@ class BumpCoverage(TestCase): def test_ajax_request(self): class Request(object): method = 'GET' + META = { + 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest' + } def is_ajax(self): return True @@ -155,7 +153,7 @@ class BumpCoverage(TestCase): self.assertIsNone(response) def test_skip_for_user(self): - cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) + cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] key_version = cache.get('django:tos:key_version') @@ -167,7 +165,7 @@ class BumpCoverage(TestCase): self.assertEqual(response.status_code, 200) def test_invalidate_cached_agreements(self): - cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) + cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] invalidate_cached_agreements(TermsOfService, {}) diff --git a/tos/tests/test_models.py b/tos/tests/test_models.py index f978376..4d5bf8b 100644 --- a/tos/tests/test_models.py +++ b/tos/tests/test_models.py @@ -1,25 +1,25 @@ +from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.test import TestCase -from tos.compat import get_runtime_user_model from tos.models import ( - NoActiveTermsOfService, - TermsOfService, - UserAgreement, - has_user_agreed_latest_tos, - ) + NoActiveTermsOfService, + TermsOfService, + UserAgreement, + has_user_agreed_latest_tos, +) class TestModels(TestCase): def setUp(self): - self.user1 = get_runtime_user_model().objects.create_user('user1', + self.user1 = get_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') - self.user2 = get_runtime_user_model().objects.create_user('user2', + self.user2 = get_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass') - self.user3 = get_runtime_user_model().objects.create_user('user3', + self.user3 = get_user_model().objects.create_user('user3', 'user3@example.com', 'user3pass') diff --git a/tos/tests/test_urls.py b/tos/tests/test_urls.py index 6c91a25..f258956 100644 --- a/tos/tests/test_urls.py +++ b/tos/tests/test_urls.py @@ -1,12 +1,12 @@ -from django.conf.urls import include, url +from django.urls import include, re_path from django.views.generic import TemplateView -from tos.compat import patterns from tos import views -urlpatterns = patterns('', - url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'), - url(r'^login/$', views.login, {}, 'login'), - url(r'^tos/', include('tos.urls')), -) +urlpatterns = [ + re_path(r'^$', TemplateView.as_view(template_name='index.html'), name='index'), + + re_path(r'^login/$', views.login, {}, 'login'), + re_path(r'^tos/', include('tos.urls')), +] diff --git a/tos/tests/test_views.py b/tos/tests/test_views.py index 1813885..0be8b2c 100644 --- a/tos/tests/test_views.py +++ b/tos/tests/test_views.py @@ -1,7 +1,8 @@ from django.conf import settings +from django.contrib.auth import get_user_model from django.test import TestCase +from django.urls import reverse -from tos.compat import get_runtime_user_model, reverse from tos.models import TermsOfService, UserAgreement, has_user_agreed_latest_tos @@ -9,10 +10,10 @@ class TestViews(TestCase): def setUp(self): # User that has agreed to TOS - self.user1 = get_runtime_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') + self.user1 = get_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass') # User that has not yet agreed to TOS - self.user2 = get_runtime_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass') + self.user2 = get_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass') self.tos1 = TermsOfService.objects.create( content="first edition of the terms of service", @@ -88,7 +89,7 @@ class TestViews(TestCase): response = self.client.post(self.login_url, dict(username='user1', password='user1pass', next='http://example.com')) self.assertEqual(302, response.status_code) - self.assertIn(settings.LOGIN_REDIRECT_URL, response._headers['location'][1]) + self.assertIn(settings.LOGIN_REDIRECT_URL, response.url) def test_need_to_log_in(self): """ GET to login url shows login tempalte.""" diff --git a/tos/urls.py b/tos/urls.py index 7b680df..89f26b7 100644 --- a/tos/urls.py +++ b/tos/urls.py @@ -1,13 +1,12 @@ -from django.conf.urls import url +from django.urls import re_path -from tos.compat import patterns from tos.views import check_tos, TosView -urlpatterns = patterns('', +urlpatterns = [ # Terms of Service conform - url(r'^confirm/$', check_tos, name='tos_check_tos'), + re_path(r'^confirm/$', check_tos, name='tos_check_tos'), # Terms of service simple display - url(r'^$', TosView.as_view(), name='tos'), -) + re_path(r'^$', TosView.as_view(), name='tos'), +] diff --git a/tos/views.py b/tos/views.py index 12ee57e..e5961fc 100644 --- a/tos/views.py +++ b/tos/views.py @@ -5,19 +5,22 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth import login as auth_login from django.contrib.auth import REDIRECT_FIELD_NAME +from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.contrib.sites.models import Site +from django.contrib.sites.requests import RequestSite +from django.core.cache import caches from django.http import HttpResponseRedirect +from django.shortcuts import render +from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect from django.views.generic import TemplateView -from django.utils.translation import ugettext_lazy as _ -from tos.compat import get_cache, get_runtime_user_model, get_request_site, get_render from tos.models import has_user_agreed_latest_tos, TermsOfService, UserAgreement -cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) +cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')] class TosView(TemplateView): @@ -54,7 +57,7 @@ def check_tos(request, template_name='tos/tos_check.html', tos = TermsOfService.objects.get_current_tos() if request.method == "POST": if request.POST.get("accept", "") == "accept": - user = get_runtime_user_model().objects.get(pk=request.session['tos_user']) + user = get_user_model().objects.get(pk=request.session['tos_user']) user.backend = request.session['tos_backend'] # Save the user agreement to the new TOS @@ -73,14 +76,14 @@ def check_tos(request, template_name='tos/tos_check.html', else: messages.error( request, - _(u"You cannot login without agreeing to the terms of this site.") + _("You cannot login without agreeing to the terms of this site.") ) context = { 'tos': tos, 'redirect_field_name': redirect_field_name, 'next': redirect_to, } - return get_render(request, template_name, context) + return render(request, template_name, context) @csrf_protect @@ -127,7 +130,7 @@ def login(request, template_name='registration/login.html', 'tos': TermsOfService.objects.get_current_tos() } - return get_render(request, 'tos/tos_check.html', context) + return render(request, 'tos/tos_check.html', context) else: form = authentication_form(request) @@ -136,7 +139,7 @@ def login(request, template_name='registration/login.html', if Site._meta.installed: current_site = Site.objects.get_current() else: - current_site = get_request_site()(request) + current_site = RequestSite(request) context = { 'form': form, @@ -144,6 +147,4 @@ def login(request, template_name='registration/login.html', 'site': current_site, 'site_name': current_site.name, } - return get_render(request, template_name, context) - - + return render(request, template_name, context)