diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..9d7eced
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,33 @@
+[report]
+# Regexes for lines to exclude from consideration
+exclude_lines =
+ # Have to re-enable the standard pragma
+ pragma: no cover
+
+ # Don't complain about missing debug-only code:
+ def __repr__
+ if self\.debug
+ def __unicode__
+ def __repr__
+ if settings.DEBUG
+ raise NotImplementedError
+ from django\.
+
+ # Don't complain if tests don't hit defensive assertion code:
+ raise AssertionError
+ raise NotImplementedError
+
+ # Don't complain if non-runnable code isn't run:
+ if 0:
+ if __name__ == .__main__.:
+
+[run]
+omit =
+ *tests*
+ *migrations*
+ *management*
+ *urls*
+ *site-packages*
+ *src*
+ *manage*
+ *settings*
diff --git a/.gitignore b/.gitignore
index 0608b14..004a8d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
*.pyc
-templates/
+.coverage
diff --git a/.travis.yml b/.travis.yml
index 1686dae..da8e9ba 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,19 +3,20 @@ python:
- "2.6"
- "2.7"
- "3.4"
-
install:
- - pip install .
+ - pip install coverage
- pip install $DJANGO
script:
- - django-admin.py test tos --settings=tos.tests.test_settings
+ - coverage run runtests.py
+ - coverage report -m
env:
- DJANGO="Django==1.4.12"
- DJANGO="Django==1.5"
- DJANGO="Django==1.5.7"
- DJANGO="Django==1.6.4"
- DJANGO="Django==1.7.11"
-
+ - DJANGO="Django==1.8.11"
+ - DJANGO="Django==1.9.4"
matrix:
exclude:
# Python 2.6 support has been dropped in Django 1.7
@@ -23,3 +24,7 @@ matrix:
env: DJANGO="Django==1.7.11"
- python: "3.4"
env: DJANGO="Django==1.4.12"
+ - python: "2.6"
+ env: DJANGO="Django==1.8.11"
+ - python: "2.6"
+ env: DJANGO="Django==1.9.4"
diff --git a/requirements.txt b/requirements.txt
index 4a2eb22..03490d0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,2 @@
Django>=1.4
+coverage
diff --git a/runtests.py b/runtests.py
new file mode 100755
index 0000000..529fedf
--- /dev/null
+++ b/runtests.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+import sys
+
+import django
+
+from django.conf import settings
+from django.core.management import execute_from_command_line
+
+
+if not settings.configured:
+ settings.configure(
+ DATABASES={
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ }
+ },
+ INSTALLED_APPS=[
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'tos',
+ 'tos.tests'
+ ],
+ MIDDLEWARE_CLASSES=[
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ ],
+ ROOT_URLCONF='tos.tests.test_urls',
+ LOGIN_URL='/login/',
+ SITE_ID='1'
+ )
+
+
+import logging
+logging.basicConfig(
+ level = logging.DEBUG,
+ format = '%(asctime)s %(levelname)s %(message)s',
+)
+logging.disable(logging.CRITICAL)
+
+
+def runtests():
+ argv = sys.argv[:1] + ['test', 'tos']
+ execute_from_command_line(argv)
+
+
+if __name__ == '__main__':
+ runtests()
diff --git a/tos/compat.py b/tos/compat.py
new file mode 100644
index 0000000..72f42f1
--- /dev/null
+++ b/tos/compat.py
@@ -0,0 +1,39 @@
+import django
+from django.conf import settings
+
+
+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
+
+
+if django.VERSION < (1, 5):
+ from django.templatetags.future import url
+else:
+ from django.template.defaulttags import url
diff --git a/tos/models.py b/tos/models.py
index 8f16853..1174b7a 100644
--- a/tos/models.py
+++ b/tos/models.py
@@ -2,14 +2,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
-# Django 1.4 compatability
-try:
- from django.contrib.auth import get_user_model
- USER_MODEL = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
- USER_MODEL = User
-
+from tos.compat import get_fk_user_model
class NoActiveTermsOfService(ValidationError):
pass
@@ -65,7 +58,8 @@ class TermsOfService(BaseModel):
else:
if not TermsOfService.objects\
.exclude(id=self.id)\
- .filter(active=True):
+ .filter(active=True)\
+ .exists():
raise NoActiveTermsOfService(
u'One of the terms of service must be marked active'
)
@@ -75,7 +69,7 @@ class TermsOfService(BaseModel):
class UserAgreement(BaseModel):
terms_of_service = models.ForeignKey(TermsOfService, related_name='terms')
- user = models.ForeignKey(USER_MODEL, related_name='user_agreement')
+ user = models.ForeignKey(get_fk_user_model(), related_name='user_agreement')
def __unicode__(self):
return u'%s agreed to TOS: %s' % (self.user.username,
diff --git a/tos/templates/tos/tos_check.html b/tos/templates/tos/tos_check.html
index ae6ace6..cf0163a 100644
--- a/tos/templates/tos/tos_check.html
+++ b/tos/templates/tos/tos_check.html
@@ -1,4 +1,4 @@
-{% load url from future %}
+{% load url from compat %}
{% if note %}
{{ note }}
{% else %}
diff --git a/tos/templatetags/__init__.py b/tos/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tos/templatetags/compat.py b/tos/templatetags/compat.py
new file mode 100644
index 0000000..9e0499d
--- /dev/null
+++ b/tos/templatetags/compat.py
@@ -0,0 +1,10 @@
+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)
diff --git a/tos/tests/__init__.py b/tos/tests/__init__.py
index 027f01a..80ca9a7 100644
--- a/tos/tests/__init__.py
+++ b/tos/tests/__init__.py
@@ -1,2 +1,5 @@
-from tos.tests.test_models import *
-from tos.tests.test_views import *
\ No newline at end of file
+import django
+
+if django.VERSION < (1, 6):
+ from tos.tests.test_models import *
+ from tos.tests.test_views import *
diff --git a/tos/tests/templates/registration/login.html b/tos/tests/templates/registration/login.html
new file mode 100644
index 0000000..178ffda
--- /dev/null
+++ b/tos/tests/templates/registration/login.html
@@ -0,0 +1 @@
+Dummy login template.
diff --git a/tos/tests/test_models.py b/tos/tests/test_models.py
index 5987939..f978376 100644
--- a/tos/tests/test_models.py
+++ b/tos/tests/test_models.py
@@ -1,24 +1,25 @@
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,
- USER_MODEL
)
class TestModels(TestCase):
def setUp(self):
- self.user1 = USER_MODEL.objects.create_user('user1',
+ self.user1 = get_runtime_user_model().objects.create_user('user1',
'user1@example.com',
'user1pass')
- self.user2 = USER_MODEL.objects.create_user('user2',
+ self.user2 = get_runtime_user_model().objects.create_user('user2',
'user2@example.com',
'user2pass')
- self.user3 = USER_MODEL.objects.create_user('user3',
+ self.user3 = get_runtime_user_model().objects.create_user('user3',
'user3@example.com',
'user3pass')
@@ -48,10 +49,6 @@ class TestModels(TestCase):
# latest is active though
self.assertTrue(latest.active)
- def test_terms_of_service_manager(self):
-
- self.assertEquals(TermsOfService.objects.get_current_tos(), self.tos1)
-
def test_validation_error_all_set_false(self):
"""
If you try and set all to false the model will throw a ValidationError
@@ -92,3 +89,18 @@ class TestModels(TestCase):
self.assertTrue(has_user_agreed_latest_tos(self.user1))
self.assertFalse(has_user_agreed_latest_tos(self.user2))
self.assertTrue(has_user_agreed_latest_tos(self.user3))
+
+
+class TestManager(TestCase):
+ def test_terms_of_service_manager(self):
+
+ tos1 = TermsOfService.objects.create(
+ content="first edition of the terms of service",
+ active=True
+ )
+
+ self.assertEquals(TermsOfService.objects.get_current_tos(), tos1)
+
+ def test_terms_of_service_manager_raises_error(self):
+
+ self.assertRaises(NoActiveTermsOfService, TermsOfService.objects.get_current_tos)
diff --git a/tos/tests/test_settings.py b/tos/tests/test_settings.py
deleted file mode 100644
index 770a032..0000000
--- a/tos/tests/test_settings.py
+++ /dev/null
@@ -1,64 +0,0 @@
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-SITE_ID = 1
-SECRET_KEY = 'foobarbaz'
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': 'mydatabase'
- }
-}
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
- 'django.contrib.auth.context_processors.auth',
- 'django.core.context_processors.debug',
- 'django.core.context_processors.i18n',
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- 'django.core.context_processors.tz',
- 'django.core.context_processors.request',
- 'django.contrib.messages.context_processors.messages',
-)
-
-INSTALLED_APPS = (
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.sites',
- 'tos',
-)
-
-TEMPLATE_LOADERS = (
- 'django.template.loaders.app_directories.Loader',
- 'django.template.loaders.eggs.Loader',
-)
-
-ROOT_URLCONF = 'tos.tests.test_urls'
-
-LOGIN_URL = '/login/'
-
-import logging
-logging.basicConfig(
- level = logging.DEBUG,
- format = '%(asctime)s %(levelname)s %(message)s',
-)
-
-
-# Django 1.7 compatibility:
-import django
-if hasattr(django, 'setup'):
- django.setup()
-
-if django.VERSION > (1, 6):
- TEST_RUNNER = 'django.test.runner.DiscoverRunner'
diff --git a/tos/tests/test_views.py b/tos/tests/test_views.py
index 5297965..bb307cb 100644
--- a/tos/tests/test_views.py
+++ b/tos/tests/test_views.py
@@ -2,20 +2,15 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import TestCase
-# Django 1.4 compatability
-try:
- from django.contrib.auth import get_user_model
-except ImportError:
- from django.contrib.auth.models import User
- get_user_model = lambda: User
+from tos.compat import get_runtime_user_model
+from tos.models import TermsOfService, UserAgreement, has_user_agreed_latest_tos
-from tos.models import TermsOfService, UserAgreement, has_user_agreed_latest_tos, USER_MODEL as USER
class TestViews(TestCase):
def setUp(self):
- self.user1 = USER.objects.create_user('user1', 'user1@example.com', 'user1pass')
- self.user2 = USER.objects.create_user('user2', 'user2@example.com', 'user2pass')
+ self.user1 = get_runtime_user_model().objects.create_user('user1', 'user1@example.com', 'user1pass')
+ self.user2 = get_runtime_user_model().objects.create_user('user2', 'user2@example.com', 'user2pass')
self.tos1 = TermsOfService.objects.create(
content="first edition of the terms of service",
@@ -53,6 +48,34 @@ class TestViews(TestCase):
self.assertFalse(has_user_agreed_latest_tos(self.user2))
+ def test_do_not_need_agreement(self):
+ """ user2 tries to login and has already agreed"""
+
+ self.assertTrue(has_user_agreed_latest_tos(self.user1))
+
+ response = self.client.post(self.login_url, dict(username='user1',
+ password='user1pass'))
+ self.assertEqual(302, response.status_code)
+
+ def test_redirect_security(self):
+ """ redirect to outside url not allowed, should redirect to login url"""
+
+ 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])
+
+ def test_need_to_log_in(self):
+ """ GET to login url shows login tempalte."""
+
+ response = self.client.get(self.login_url)
+ self.assertContains(response, "Dummy login template.")
+
+ def test_root_tos_view(self):
+
+ response = self.client.get('/tos/')
+ self.assertIn(b'first edition of the terms of service', response.content)
+
def test_reject_agreement(self):
self.assertFalse(has_user_agreed_latest_tos(self.user2))
diff --git a/tos/views.py b/tos/views.py
index 0b0bf1f..3846196 100644
--- a/tos/views.py
+++ b/tos/views.py
@@ -5,7 +5,7 @@ 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.forms import AuthenticationForm
-from django.contrib.sites.models import Site, RequestSite
+from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
@@ -13,16 +13,9 @@ from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.utils.translation import ugettext_lazy as _
+from tos.compat import get_runtime_user_model, get_request_site
from tos.models import has_user_agreed_latest_tos, TermsOfService, UserAgreement
-# Django 1.4 compatability
-try:
- from django.contrib.auth import get_user_model
- USER_MODEL = get_user_model()
-except ImportError:
- from django.contrib.auth.models import User
- USER_MODEL = User
-
class TosView(TemplateView):
template_name = "tos/tos.html"
@@ -54,11 +47,11 @@ def _redirect_to(redirect_to):
def check_tos(request, template_name='tos/tos_check.html',
redirect_field_name=REDIRECT_FIELD_NAME,):
- redirect_to = _redirect_to(request.REQUEST.get(redirect_field_name, ''))
+ redirect_to = _redirect_to(request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, '')))
tos = TermsOfService.objects.get_current_tos()
if request.method == "POST":
if request.POST.get("accept", "") == "accept":
- user = USER_MODEL.objects.get(pk=request.session['tos_user'])
+ user = get_runtime_user_model().objects.get(pk=request.session['tos_user'])
user.backend = request.session['tos_backend']
# Save the user agreement to the new TOS
@@ -90,7 +83,7 @@ def login(request, template_name='registration/login.html',
authentication_form=AuthenticationForm):
"""Displays the login form and handles the login action."""
- redirect_to = request.REQUEST.get(redirect_field_name, '')
+ redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
if request.method == "POST":
form = authentication_form(data=request.POST)
@@ -135,7 +128,7 @@ def login(request, template_name='registration/login.html',
if Site._meta.installed:
current_site = Site.objects.get_current()
else:
- current_site = RequestSite(request)
+ current_site = get_request_site()(request)
return render_to_response(template_name, {
'form': form,