diff --git a/issue/admin.py b/issue/admin.py index 4735da7..6154203 100644 --- a/issue/admin.py +++ b/issue/admin.py @@ -6,3 +6,7 @@ admin.site.register(Issue) admin.site.register(Event) admin.site.register(Label) admin.site.register(Milestone) +admin.site.register(Settings) +admin.site.register(Team) +admin.site.register(GlobalPermission) +admin.site.register(ProjectPermission) diff --git a/issue/migrations/0006_auto_20140807_2032.py b/issue/migrations/0006_auto_20140807_2032.py new file mode 100644 index 0000000..0a77e26 --- /dev/null +++ b/issue/migrations/0006_auto_20140807_2032.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0001_initial'), + ('contenttypes', '0001_initial'), + ('sites', '0001_initial'), + ('issue', '0005_project_public'), + ] + + operations = [ + migrations.CreateModel( + name='GlobalPermission', + fields=[ + ('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), + ('grantee', models.CharField(max_length=50)), + ('create_project', models.BooleanField(default=True)), + ('modify_project', models.BooleanField(default=False)), + ('delete_project', models.BooleanField(default=False)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='ProjectPermission', + fields=[ + ('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), + ('grantee', models.CharField(max_length=50)), + ('create_issue', models.BooleanField(default=True)), + ('modify_issue', models.BooleanField(default=False)), + ('delete_issue', models.BooleanField(default=False)), + ('create_comment', models.BooleanField(default=True)), + ('modify_comment', models.BooleanField(default=False)), + ('delete_comment', models.BooleanField(default=False)), + ('create_label', models.BooleanField(default=True)), + ('modify_label', models.BooleanField(default=False)), + ('delete_label', models.BooleanField(default=False)), + ('create_milestone', models.BooleanField(default=True)), + ('modify_milestone', models.BooleanField(default=False)), + ('delete_milestone', models.BooleanField(default=False)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('project', models.ForeignKey(to='issue.Project')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='Settings', + fields=[ + ('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), + ('site', models.OneToOneField(to='sites.Site')), + ], + options={ + 'verbose_name_plural': 'Settings', + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.AutoField(primary_key=True, auto_created=True, verbose_name='ID', serialize=False)), + ('name', models.CharField(max_length=128, unique=True)), + ('groups', models.ManyToManyField(to='auth.Group', blank=True, null=True)), + ('users', models.ManyToManyField(to=settings.AUTH_USER_MODEL, blank=True, null=True)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='User', + fields=[ + ], + options={ + 'proxy': True, + }, + bases=('auth.user',), + ), + migrations.AlterField( + model_name='event', + name='author', + field=models.ForeignKey(to='issue.User'), + ), + migrations.AlterField( + model_name='issue', + name='assignee', + field=models.ForeignKey(to='issue.User', null=True, blank=True), + ), + migrations.AlterField( + model_name='issue', + name='author', + field=models.ForeignKey(to='issue.User'), + ), + ] diff --git a/issue/models.py b/issue/models.py index 2c9c2d3..9e916e5 100644 --- a/issue/models.py +++ b/issue/models.py @@ -1,10 +1,14 @@ from django.db import models -from django.contrib.auth.models import User +from django.db.models import Q +from django.contrib import auth +from django.contrib.auth.models import Group from django.core.validators import RegexValidator from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe from django.utils.html import escape from django.core.urlresolvers import reverse +from django.contrib.sites.models import Site +from django.contrib.contenttypes.models import ContentType from colorful.fields import RGBColorField @@ -13,6 +17,15 @@ import json from issue.templatetags.issue_tags import same_label, labeled +class User(auth.models.User): + + class Meta: + proxy = True + + @property + def teams(self): + return Team.objects.filter(Q(groups__in=self.groups.all()) | Q(users=self)) + class Project(models.Model): url_name_validator = RegexValidator(regex='^[a-z0-9_-]+$', @@ -301,3 +314,73 @@ class Event(models.Model): safe_args = {k: escape(v) for k, v in args.items()} return mark_safe(description.format(**safe_args)) + +class Settings(models.Model): + + site = models.OneToOneField(Site) + + class Meta: + verbose_name_plural = 'Settings' + +class Team(models.Model): + + name = models.CharField(max_length=128, unique=True) + + users = models.ManyToManyField(auth.models.User, blank=True, null=True, related_name='teams') + groups = models.ManyToManyField(auth.models.Group, blank=True, null=True, related_name='teams') + + def __str__(self): + return self.name + +class PermissionModel(models.Model): + + grantee = models.CharField(max_length=50) + # type of grantee (user, group, team) + content_type = models.ForeignKey(ContentType) + + class Meta: + abstract = True + + def granted_to(self, user): + if self.content_type == ContentType.objects.get_for_model(User): + return user.username == grantee + elif self.content_type == ContentType.objects.get_for_model(Group): + group = self.content_type.get_object_for_this_type(name=grantee) + return user.groups.filter(name=grantee).exists() + elif self.content_type == ContentType.objects.get_for_model(Team): + team = self.content_type.get_object_for_this_type(name=grantee) + return team in user.teams + else: + return False + +class GlobalPermission(PermissionModel): + + create_project = models.BooleanField(default=True) + modify_project = models.BooleanField(default=False) + delete_project = models.BooleanField(default=False) + + def __str__(self): + return self.grantee + "'s global permissions" + +class ProjectPermission(PermissionModel): + + project = models.ForeignKey(Project, related_name='permissions') + + create_issue = models.BooleanField(default=True) + modify_issue = models.BooleanField(default=False) + delete_issue = models.BooleanField(default=False) + + create_comment = models.BooleanField(default=True) + modify_comment = models.BooleanField(default=False) + delete_comment = models.BooleanField(default=False) + + create_label = models.BooleanField(default=True) + modify_label = models.BooleanField(default=False) + delete_label = models.BooleanField(default=False) + + create_milestone = models.BooleanField(default=True) + modify_milestone = models.BooleanField(default=False) + delete_milestone = models.BooleanField(default=False) + + def __str__(self): + return self.grantee + "'s permissions on " + self.project.name + " project" diff --git a/ponytracker/settings.py b/ponytracker/settings.py index 36ad1af..ba95f25 100644 --- a/ponytracker/settings.py +++ b/ponytracker/settings.py @@ -38,6 +38,7 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'django.contrib.humanize', + 'django.contrib.sites', 'django_markdown', 'bootstrap3_datetime', @@ -109,3 +110,5 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.request', 'issue.context_processors.projects', ) + +SITE_ID = 1