diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e00e246 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: python + +python: + - "2.7" + +env: + - DJANGO="django==1.5.1 --use-mirrors" + - DJANGO="django==1.4.5 --use-mirrors" + - DJANGO="django==1.3.7 --use-mirrors" + +install: + - pip install $DJANGO + - pip install milkman + - export PYTHONPATH=. + +script: + - python manage.py test userroles diff --git a/README.md b/README.md index 23c2495..34cc4c3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Django User Roles ================= +[![build-status-image]][travis] + Simple role-based user permissions for Django. django-user-roles is a simple, reusable app that allows you to create a set of user roles, which can be used to control which views each type of user has permission to view, and to customize how the site is presented to different types of user. diff --git a/testproject/settings.py b/testproject/settings.py index ab820ca..5b245e6 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -121,5 +121,8 @@ 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', + 'userroles.testapp', 'userroles', ) + +AUTH_USER_MODEL = 'testapp.TestUser' diff --git a/userroles/__init__.py b/userroles/__init__.py index dc50f8f..f9d0ac0 100644 --- a/userroles/__init__.py +++ b/userroles/__init__.py @@ -3,7 +3,7 @@ from django.utils import importlib -__version__ = '0.1.0' +__version__ = '0.2.0' _IMPORT_FAILED = "Could not import role profile '%s'" diff --git a/userroles/decorators.py b/userroles/decorators.py index 94bec4b..52f53a8 100644 --- a/userroles/decorators.py +++ b/userroles/decorators.py @@ -1,6 +1,7 @@ from django.contrib.auth.decorators import user_passes_test from userroles.models import UserRole + def role_required(*roles): """ Decorator for views that checks whether a user has a particular role, diff --git a/userroles/models.py b/userroles/models.py index f0f6390..139250f 100644 --- a/userroles/models.py +++ b/userroles/models.py @@ -1,10 +1,17 @@ -from django.contrib.auth.models import User +import django.contrib.auth from django.db import models from userroles import roles +from django.conf import settings + + +if hasattr(django.contrib.auth, 'get_user_model'): + user_model = settings.AUTH_USER_MODEL +else: + user_model = 'auth.User' class UserRole(models.Model): - user = models.OneToOneField(User, related_name='role') + user = models.OneToOneField(user_model, related_name='role') name = models.CharField(max_length=100, choices=roles.choices) child = models.CharField(max_length=100, blank=True) _valid_roles = roles @@ -25,7 +32,7 @@ def __getattr__(self, name): return self == role raise AttributeError("'%s' object has no attribute '%s'" % - (self.__class__.__name__, name)) + (self.__class__.__name__, name)) def __unicode__(self): return self.name @@ -50,3 +57,4 @@ def set_user_role(user, role, profile=None): profile.name = role.name profile.save() + user.role = profile diff --git a/userroles/testapp/models.py b/userroles/testapp/models.py index aba3dd6..292f0f7 100644 --- a/userroles/testapp/models.py +++ b/userroles/testapp/models.py @@ -1,6 +1,41 @@ from django.db import models from userroles.models import UserRole +import django.contrib.auth.models + + +if hasattr(django.contrib.auth.models, 'AbstractBaseUser'): + from django.contrib.auth.models import AbstractBaseUser, UserManager +else: + class AbstractBaseUser(object): + pass + + class UserManager(object): + pass class TestModeratorProfile(UserRole): stars = models.IntegerField() + + +class TestUser(AbstractBaseUser): + username = models.CharField(max_length=255, unique=True, db_index=True) + email = models.EmailField(max_length=255, unique=True) + first_name = models.CharField(max_length=255) + surname = models.CharField(max_length=255) + is_active = models.BooleanField(default=True) + + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email', 'first_name', 'surname'] + + objects = UserManager() + + def get_full_name(self): + #This is a required method + return self.first_name + ' ' + self.surname + + def get_short_name(self): + #This is a required method + return self.email + + def __unicode__(self): + return self.email diff --git a/userroles/tests.py b/userroles/tests.py index becdb86..8c3a47c 100644 --- a/userroles/tests.py +++ b/userroles/tests.py @@ -3,6 +3,7 @@ from django.conf import settings from django.contrib.auth.models import User +import django.contrib.auth from milkman.dairy import milkman from userroles.models import set_user_role, UserRole from userroles.testapp.models import TestModeratorProfile @@ -22,6 +23,13 @@ roles = Roles(roles_config) +# Handle older versions of Django +if hasattr(django.contrib.auth, 'get_user_model'): + get_user_model = django.contrib.auth.get_user_model +else: + def get_user_model(): + return User + class TestCase(SettingsTestCase): def setUp(self): @@ -29,7 +37,8 @@ def setUp(self): self.settings( INSTALLED_APPS=installed_apps_config, ROOT_URLCONF='userroles.testapp.urls', - USER_ROLES=roles_config + USER_ROLES=roles_config, + AUTH_USER_MODEL='testapp.TestUser' ) self.restore_roles = UserRole._valid_roles UserRole._valid_roles = roles @@ -69,7 +78,7 @@ class UserRoleTests(TestCase): def setUp(self): super(UserRoleTests, self).setUp() - self.user = milkman.deliver(User) + self.user = milkman.deliver(get_user_model()) set_user_role(self.user, roles.manager) def test_role_comparison(self): @@ -113,7 +122,8 @@ def test_set_role_with_profile(self): """ Set a role that takes a profile. """ - set_user_role(self.user, roles.moderator, TestModeratorProfile(stars=5)) + set_user_role(self.user, roles.moderator, + TestModeratorProfile(stars=5)) self.assertTrue(self.user.role.is_moderator) self.assertEquals(self.user.role.profile.stars, 5) @@ -144,7 +154,7 @@ def test_set_role_with_profile(self): class ViewTests(TestCase): def setUp(self): super(ViewTests, self).setUp() - self.user = milkman.deliver(User) + self.user = milkman.deliver(get_user_model()) self.user.set_password('password') self.user.save() self.client.login(username=self.user.username, password='password') diff --git a/userroles/utils.py b/userroles/utils.py index 9961077..82152cb 100644 --- a/userroles/utils.py +++ b/userroles/utils.py @@ -5,6 +5,7 @@ from django.db.models import loading from django.test import TestCase from django.core.management.commands import syncdb +from optparse import NO_DEFAULT NO_SETTING = ('!', None) @@ -31,7 +32,16 @@ def set(self, **kwargs): def syncdb(self): loading.cache.loaded = False # Use this, rather than call_command, or 'south' will screw with us. - syncdb.Command().execute(verbosity=0) + command = syncdb.Command() + defaults = {} + for opt in command.option_list: + if opt.default is NO_DEFAULT: + defaults[opt.dest] = None + else: + defaults[opt.dest] = opt.default + defaults['verbosity'] = 0 + defaults['interactive'] = False + command.execute(*[], **defaults) def revert(self): for k, v in self._original_settings.iteritems():