Skip to content

Commit

Permalink
Merge branch 'master' into feature-reviews
Browse files Browse the repository at this point in the history
Conflicts:
	symposion_project/settings.py
	symposion_project/templates/dashboard.html
	symposion_project/urls.py
  • Loading branch information
jtauber committed Aug 8, 2012
2 parents 18996cb + 0dbcbea commit 5f1670d
Show file tree
Hide file tree
Showing 17 changed files with 628 additions and 49 deletions.
Empty file added symposion/teams/__init__.py
Empty file.
17 changes: 17 additions & 0 deletions symposion/teams/admin.py
@@ -0,0 +1,17 @@
from django.contrib import admin

import reversion

from symposion.teams.models import Team, Membership

admin.site.register(Team,
prepopulated_fields={"slug": ("name",)},
)


class MembershipAdmin(reversion.VersionAdmin):
list_display = ["team", "user", "state"]
list_filter = ["team"]
search_fields = ["user__username"]

admin.site.register(Membership, MembershipAdmin)
33 changes: 33 additions & 0 deletions symposion/teams/backends.py
@@ -0,0 +1,33 @@
from django.db.models import Q

from .models import Team


class TeamPermissionsBackend(object):

def authenticate(self, username=None, password=None):
return None

def get_team_permissions(self, user_obj, obj=None):
"""
Returns a set of permission strings that this user has through his/her
team memberships.
"""
if user_obj.is_anonymous() or obj is not None:
return set()
if not hasattr(user_obj, "_team_perm_cache"):
memberships = Team.objects.filter(
Q(memberships__user=user_obj),
Q(memberships__state="manager") | Q(memberships__state="member"),
)
perms = memberships.values_list(
"permissions__content_type__app_label",
"permissions__codename"
).order_by()
user_obj._team_perm_cache = set(["%s.%s" % (ct, name) for ct, name in perms])
return user_obj._team_perm_cache

def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_active:
return False
return perm in self.get_team_permissions(user_obj, obj)
50 changes: 50 additions & 0 deletions symposion/teams/forms.py
@@ -0,0 +1,50 @@
from django import forms

from django.contrib.auth.models import User

from symposion.teams.models import Membership


class TeamInvitationForm(forms.Form):

email = forms.EmailField(help_text="email address must be that of a user on the site")

def __init__(self, *args, **kwargs):
self.team = kwargs.pop("team")
super(TeamInvitationForm, self).__init__(*args, **kwargs)

def clean(self):
cleaned_data = super(TeamInvitationForm, self).clean()
email = cleaned_data.get("email")

if email is None:
raise forms.ValidationError("valid email address required")

try:
user = User.objects.get(email=email)
except User.DoesNotExist:
# eventually we can invite them but for now assume they are
# already on the site
raise forms.ValidationError("no known user with email address %s" % email)

state = self.team.get_state_for_user(user)

if state in ["member", "manager"]:
raise forms.ValidationError("user already in team")

if state in ["invited"]:
raise forms.ValidationError("user already invited to team")

self.user = user
self.state = state

return cleaned_data

def invite(self):
if self.state is None:
Membership.objects.create(team=self.team, user=self.user, state="invited")
elif self.state == "applied":
# if they applied we shortcut invitation process
membership = Membership.objects.get(team=self.team, user=self.user)
membership.state = "member"
membership.save()
72 changes: 72 additions & 0 deletions symposion/teams/models.py
@@ -0,0 +1,72 @@
import datetime

from django.db import models

import reversion

from django.contrib.auth.models import Permission, User


TEAM_ACCESS_CHOICES = [
("open", "open"),
("application", "by application"),
("invitation", "by invitation")
]


class Team(models.Model):

slug = models.SlugField(unique=True)
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
access = models.CharField(max_length=20, choices=TEAM_ACCESS_CHOICES)

# member permissions
permissions = models.ManyToManyField(Permission, blank=True, related_name="member_teams")

# manager permissions
manager_permissions = models.ManyToManyField(Permission, blank=True, related_name="manager_teams")

created = models.DateTimeField(default=datetime.datetime.now, editable=False)

def __unicode__(self):
return self.name

def get_state_for_user(self, user):
try:
return self.memberships.get(user=user).state
except Membership.DoesNotExist:
return None

def applicants(self):
return self.memberships.filter(state="applied")

def invitees(self):
return self.memberships.filter(state="invited")

def members(self):
return self.memberships.filter(state="member")

def managers(self):
return self.memberships.filter(state="manager")


MEMBERSHIP_STATE_CHOICES = [
("applied", "applied"),
("invited", "invited"),
("declined", "declined"),
("rejected", "rejected"),
("member", "member"),
("manager", "manager"),
]


class Membership(models.Model):

user = models.ForeignKey(User, related_name="memberships")
team = models.ForeignKey(Team, related_name="memberships")
state = models.CharField(max_length=20, choices=MEMBERSHIP_STATE_CHOICES)
message = models.TextField(blank=True)


reversion.register(Membership)
Empty file.
39 changes: 39 additions & 0 deletions symposion/teams/templatetags/teams_tags.py
@@ -0,0 +1,39 @@
from django import template

from symposion.teams.models import Team

register = template.Library()


class AvailableTeamsNode(template.Node):

@classmethod
def handle_token(cls, parser, token):
bits = token.split_contents()
if len(bits) == 3 and bits[1] == "as":
return cls(bits[2])
else:
raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0])

def __init__(self, context_var):
self.context_var = context_var

def render(self, context):
request = context["request"]
teams = []
for team in Team.objects.all():
state = team.get_state_for_user(request.user)
if team.access == "open" and state is None:
teams.append(team)
elif request.user.is_staff and state is None:
teams.append(team)
context[self.context_var] = teams
return u""


@register.tag
def available_teams(parser, token):
"""
{% available_teams as available_teams %}
"""
return AvailableTeamsNode.handle_token(parser, token)
16 changes: 16 additions & 0 deletions symposion/teams/urls.py
@@ -0,0 +1,16 @@
from django.conf.urls.defaults import *


urlpatterns = patterns("symposion.teams.views",
# team specific
url(r"^(?P<slug>[\w\-]+)/$", "team_detail", name="team_detail"),
url(r"^(?P<slug>[\w\-]+)/join/$", "team_join", name="team_join"),
url(r"^(?P<slug>[\w\-]+)/leave/$", "team_leave", name="team_leave"),
url(r"^(?P<slug>[\w\-]+)/apply/$", "team_apply", name="team_apply"),

# membership specific
url(r"^promote/(?P<pk>\d+)/$", "team_promote", name="team_promote"),
url(r"^demote/(?P<pk>\d+)/$", "team_demote", name="team_demote"),
url(r"^accept/(?P<pk>\d+)/$", "team_accept", name="team_accept"),
url(r"^reject/(?P<pk>\d+)/$", "team_reject", name="team_reject"),
)

0 comments on commit 5f1670d

Please sign in to comment.