commit
						687e3ace51
					
				
					 13 changed files with 562 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								symposion/teams/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								symposion/teams/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								symposion/teams/admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								symposion/teams/admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								symposion/teams/backends.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								symposion/teams/backends.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								symposion/teams/forms.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								symposion/teams/forms.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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()
 | 
			
		||||
							
								
								
									
										66
									
								
								symposion/teams/models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								symposion/teams/models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
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)
 | 
			
		||||
    permissions = models.ManyToManyField(Permission, blank=True)
 | 
			
		||||
    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)
 | 
			
		||||
							
								
								
									
										0
									
								
								symposion/teams/templatetags/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								symposion/teams/templatetags/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										39
									
								
								symposion/teams/templatetags/teams_tags.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								symposion/teams/templatetags/teams_tags.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								symposion/teams/urls.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								symposion/teams/urls.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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"),
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										182
									
								
								symposion/teams/views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								symposion/teams/views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,182 @@
 | 
			
		|||
from django.http import Http404
 | 
			
		||||
from django.shortcuts import render, redirect, get_object_or_404
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
 | 
			
		||||
from symposion.teams.forms import TeamInvitationForm
 | 
			
		||||
from symposion.teams.models import Team, Membership
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## perm checks
 | 
			
		||||
#
 | 
			
		||||
# @@@ these can be moved
 | 
			
		||||
 | 
			
		||||
def can_join(team, user):
 | 
			
		||||
    state = team.get_state_for_user(user)
 | 
			
		||||
    if team.access == "open" and state is None:
 | 
			
		||||
        return True
 | 
			
		||||
    elif state == "invited":
 | 
			
		||||
        return True
 | 
			
		||||
    elif user.is_staff and state is None:
 | 
			
		||||
        return True
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def can_leave(team, user):
 | 
			
		||||
    state = team.get_state_for_user(user)
 | 
			
		||||
    if state == "member":  # managers can't leave at the moment
 | 
			
		||||
        return True
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def can_apply(team, user):
 | 
			
		||||
    state = team.get_state_for_user(user)
 | 
			
		||||
    if team.access == "application" and state is None:
 | 
			
		||||
        return True
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def can_invite(team, user):
 | 
			
		||||
    state = team.get_state_for_user(user)
 | 
			
		||||
    if team.access == "invitation":
 | 
			
		||||
        if state == "manager" or user.is_staff:
 | 
			
		||||
            return True
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## views
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_detail(request, slug):
 | 
			
		||||
    team = get_object_or_404(Team, slug=slug)
 | 
			
		||||
    state = team.get_state_for_user(request.user)
 | 
			
		||||
    if team.access == "invitation" and state is None and not request.user.is_staff:
 | 
			
		||||
        raise Http404()
 | 
			
		||||
    
 | 
			
		||||
    if can_invite(team, request.user):
 | 
			
		||||
        if request.method == "POST":
 | 
			
		||||
            form = TeamInvitationForm(request.POST, team=team)
 | 
			
		||||
            if form.is_valid():
 | 
			
		||||
                form.invite()
 | 
			
		||||
                messages.success(request, "Invitation created.")
 | 
			
		||||
                return redirect("team_detail", slug=slug)
 | 
			
		||||
        else:
 | 
			
		||||
            form = TeamInvitationForm(team=team)
 | 
			
		||||
    else:
 | 
			
		||||
        form = None
 | 
			
		||||
    
 | 
			
		||||
    return render(request, "teams/team_detail.html", {
 | 
			
		||||
        "team": team,
 | 
			
		||||
        "state": state,
 | 
			
		||||
        "invite_form": form,
 | 
			
		||||
        "can_join": can_join(team, request.user),
 | 
			
		||||
        "can_leave": can_leave(team, request.user),
 | 
			
		||||
        "can_apply": can_apply(team, request.user),
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_join(request, slug):
 | 
			
		||||
    team = get_object_or_404(Team, slug=slug)
 | 
			
		||||
    state = team.get_state_for_user(request.user)
 | 
			
		||||
    if team.access == "invitation" and state is None and not request.user.is_staff:
 | 
			
		||||
        raise Http404()
 | 
			
		||||
    
 | 
			
		||||
    if can_join(team, request.user) and request.method == "POST":
 | 
			
		||||
        membership, created = Membership.objects.get_or_create(team=team, user=request.user)
 | 
			
		||||
        membership.state = "member"
 | 
			
		||||
        membership.save()
 | 
			
		||||
        messages.success(request, "Joined team.")
 | 
			
		||||
        return redirect("team_detail", slug=slug)
 | 
			
		||||
    else:
 | 
			
		||||
        return redirect("team_detail", slug=slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_leave(request, slug):
 | 
			
		||||
    team = get_object_or_404(Team, slug=slug)
 | 
			
		||||
    state = team.get_state_for_user(request.user)
 | 
			
		||||
    if team.access == "invitation" and state is None and not request.user.is_staff:
 | 
			
		||||
        raise Http404()
 | 
			
		||||
    
 | 
			
		||||
    if can_leave(team, request.user) and request.method == "POST":
 | 
			
		||||
        membership = Membership.objects.get(team=team, user=request.user)
 | 
			
		||||
        membership.delete()
 | 
			
		||||
        messages.success(request, "Left team.")
 | 
			
		||||
        return redirect("dashboard")
 | 
			
		||||
    else:
 | 
			
		||||
        return redirect("team_detail", slug=slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_apply(request, slug):
 | 
			
		||||
    team = get_object_or_404(Team, slug=slug)
 | 
			
		||||
    state = team.get_state_for_user(request.user)
 | 
			
		||||
    if team.access == "invitation" and state is None and not request.user.is_staff:
 | 
			
		||||
        raise Http404()
 | 
			
		||||
    
 | 
			
		||||
    if can_apply(team, request.user) and request.method == "POST":
 | 
			
		||||
        membership, created = Membership.objects.get_or_create(team=team, user=request.user)
 | 
			
		||||
        membership.state = "applied"
 | 
			
		||||
        membership.save()
 | 
			
		||||
        messages.success(request, "Applied to join team.")
 | 
			
		||||
        return redirect("team_detail", slug=slug)
 | 
			
		||||
    else:
 | 
			
		||||
        return redirect("team_detail", slug=slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_promote(request, pk):
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        membership = get_object_or_404(Membership, pk=pk)
 | 
			
		||||
        state = membership.team.get_state_for_user(request.user)
 | 
			
		||||
        if request.user.is_staff or state == "manager":
 | 
			
		||||
            if membership.state == "member":
 | 
			
		||||
                membership.state = "manager"
 | 
			
		||||
                membership.save()
 | 
			
		||||
                messages.success(request, "Promoted to manager.")
 | 
			
		||||
    return redirect("team_detail", slug=membership.team.slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_demote(request, pk):
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        membership = get_object_or_404(Membership, pk=pk)
 | 
			
		||||
        state = membership.team.get_state_for_user(request.user)
 | 
			
		||||
        if request.user.is_staff or state == "manager":
 | 
			
		||||
            if membership.state == "manager":
 | 
			
		||||
                membership.state = "member"
 | 
			
		||||
                membership.save()
 | 
			
		||||
                messages.success(request, "Demoted from manager.")
 | 
			
		||||
    return redirect("team_detail", slug=membership.team.slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_accept(request, pk):
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        membership = get_object_or_404(Membership, pk=pk)
 | 
			
		||||
        state = membership.team.get_state_for_user(request.user)
 | 
			
		||||
        if request.user.is_staff or state == "manager":
 | 
			
		||||
            if membership.state == "applied":
 | 
			
		||||
                membership.state = "member"
 | 
			
		||||
                membership.save()
 | 
			
		||||
                messages.success(request, "Accepted application.")
 | 
			
		||||
    return redirect("team_detail", slug=membership.team.slug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def team_reject(request, pk):
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        membership = get_object_or_404(Membership, pk=pk)
 | 
			
		||||
        state = membership.team.get_state_for_user(request.user)
 | 
			
		||||
        if request.user.is_staff or state == "manager":
 | 
			
		||||
            if membership.state == "applied":
 | 
			
		||||
                membership.state = "rejected"
 | 
			
		||||
                membership.save()
 | 
			
		||||
                messages.success(request, "Rejected application.")
 | 
			
		||||
    return redirect("team_detail", slug=membership.team.slug)
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +167,7 @@ INSTALLED_APPS = [
 | 
			
		|||
    "symposion.boxes",
 | 
			
		||||
    "symposion.proposals",
 | 
			
		||||
    "symposion.speakers",
 | 
			
		||||
    "symposion.teams",
 | 
			
		||||
    
 | 
			
		||||
    # project
 | 
			
		||||
    "symposion_project.proposals",
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +194,10 @@ ACCOUNT_LOGOUT_REDIRECT_URL = "home"
 | 
			
		|||
ACCOUNT_USER_DISPLAY = lambda user: user.email
 | 
			
		||||
 | 
			
		||||
AUTHENTICATION_BACKENDS = [
 | 
			
		||||
    # Permissions Backends
 | 
			
		||||
    "symposion.teams.backends.TeamPermissionsBackend",
 | 
			
		||||
    
 | 
			
		||||
    # Auth backends
 | 
			
		||||
    "account.auth_backends.EmailAuthenticationBackend",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load proposal_tags %}
 | 
			
		||||
{% load teams_tags %}
 | 
			
		||||
 | 
			
		||||
{% block head_title %}Dashboard{% endblock %}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -121,4 +122,53 @@
 | 
			
		|||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="dashboard-panel">
 | 
			
		||||
        <div class="dashboard-panel-header">
 | 
			
		||||
            <i class="icon-group"></i>
 | 
			
		||||
            <h3>{% trans "Teams" %}</h3>
 | 
			
		||||
        </div>
 | 
			
		||||
        
 | 
			
		||||
        <div class="dashboard-panel-content">
 | 
			
		||||
            {% if user.memberships.exists %}
 | 
			
		||||
                <h4>Your Teams</h4>
 | 
			
		||||
                <table class="table table-striped">
 | 
			
		||||
                    {% for membership in user.memberships.all %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <a href="{% url team_detail membership.team.slug %}">{{ membership.team.name }}</a>
 | 
			
		||||
                                {% if membership.team.description %}<br>{{ membership.team.description }}{% endif %}
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <span class="label{% if membership.state == 'invited' %} label-info{% endif %}">{{ membership.get_state_display }}</span>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                {% if membership.state == "manager" or user.is_staff %}
 | 
			
		||||
                                    {% if membership.team.applicants %}{{ membership.team.applicants.count }} applicant{{ membership.team.applicants.count|pluralize }}{% endif %}
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </table>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% available_teams as available_teams %}
 | 
			
		||||
            {% if available_teams %}
 | 
			
		||||
                <h4>Available Teams</h4>
 | 
			
		||||
                <table class="table table-striped">
 | 
			
		||||
                    {% for team in available_teams %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <a href="{% url team_detail team.slug %}">{{ team }}</a>
 | 
			
		||||
                                {% if team.description %}<br>{{ team.description }}{% endif %}
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <span class="label">{{ team.get_access_display }}</span>
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </table>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										103
									
								
								symposion_project/templates/teams/team_detail.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								symposion_project/templates/teams/team_detail.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
{% extends "site_base.html" %}
 | 
			
		||||
 | 
			
		||||
{% load bootstrap_tags %}
 | 
			
		||||
 | 
			
		||||
{% block head_title %}{{ team.name }}{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block body_outer %}
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="span12">
 | 
			
		||||
            <div class="pull-right">
 | 
			
		||||
            {% if can_join %}
 | 
			
		||||
                <form method="post" action="{% url team_join team.slug %}">
 | 
			
		||||
                    {% csrf_token %}
 | 
			
		||||
                    <input type="submit" class="btn btn-primary" value="join">
 | 
			
		||||
                </form>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            
 | 
			
		||||
            {% if can_leave %}
 | 
			
		||||
                <form method="post" action="{% url team_leave team.slug %}">
 | 
			
		||||
                    {% csrf_token %}
 | 
			
		||||
                    <input type="submit" class="btn" value="leave">
 | 
			
		||||
                </form>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            
 | 
			
		||||
            {% if can_apply %}
 | 
			
		||||
                <form method="post" action="{% url team_apply team.slug %}"> 
 | 
			
		||||
                    {% csrf_token %}
 | 
			
		||||
                    <input type="submit" class="btn btn-primary" value="apply">
 | 
			
		||||
                </form>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
            
 | 
			
		||||
            <h1>{{ team.name }}{% if state %} <span class="label">{{ state }}</span>{% endif %}</h1>
 | 
			
		||||
            
 | 
			
		||||
            {% if team.description %}<p>{{ team.description }}</p>{% endif %}
 | 
			
		||||
            
 | 
			
		||||
            {% if state == "invited" %}<p>You have been invited to join this team. Click <b>join</b> to the right to accept.</p>{% endif %}
 | 
			
		||||
            
 | 
			
		||||
            {% if user.is_staff or state == "manager" %}
 | 
			
		||||
                {% if team.managers %}
 | 
			
		||||
                    <h2>Managers</h2>
 | 
			
		||||
                    <table class="table table-striped">
 | 
			
		||||
                        {% for membership in team.managers %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ membership.user.email }}{% if user == membership.user %} <span class="label label-info">you</span>{% endif %}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <form style="margin: 0;" method="post" action="{% url team_demote membership.pk %}">{% csrf_token %}<button type="submit" class="btn btn-mini">demote</button></form>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </table>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% if team.members %}
 | 
			
		||||
                    <h2>Team Members</h2>
 | 
			
		||||
                    <table class="table table-striped">
 | 
			
		||||
                        {% for membership in team.members %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ membership.user.email }}{% if user == membership.user %} <span class="label label-info">you</span>{% endif %}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <form style="margin: 0;" method="post" action="{% url team_promote membership.pk %}">{% csrf_token %}<button type="submit" class="btn btn-mini">promote</button></form>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </table>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% if team.applicants and team.access == "application" %}
 | 
			
		||||
                    <h2>Applicants</h2>
 | 
			
		||||
                    <table class="table table-striped">
 | 
			
		||||
                        {% for membership in team.applicants %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ membership.user.email }}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <form style="margin: 0; float: left;" method="post" action="{% url team_accept membership.pk %}">{% csrf_token %}<button type="submit" class="btn btn-mini">accept</button></form>
 | 
			
		||||
                                    <form style="margin: 0; float: left;" method="post" action="{% url team_reject membership.pk %}">{% csrf_token %}<button type="submit" class="btn btn-mini">reject</button></form>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </table>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% if team.invitees %}
 | 
			
		||||
                    <h2>Invitees</h2>
 | 
			
		||||
                    <table class="table table-striped">
 | 
			
		||||
                        {% for membership in team.invitees %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ membership.user.email }}</td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </table>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% if invite_form %}
 | 
			
		||||
                    <form method="POST" action="" class="form-horizontal">
 | 
			
		||||
                        {% csrf_token %}
 | 
			
		||||
                        <legend>Invite User to Team</legend>
 | 
			
		||||
                        {{ invite_form|as_bootstrap }}
 | 
			
		||||
                        <div class="form-actions">
 | 
			
		||||
                            <input class="btn btn-primary" type="submit" value="Invite" />
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ urlpatterns = patterns("",
 | 
			
		|||
    url(r"^proposals/", include("symposion.proposals.urls")),
 | 
			
		||||
    url(r"^sponsors/", include("symposion.sponsorship.urls")),
 | 
			
		||||
    url(r"^boxes/", include("symposion.boxes.urls")),
 | 
			
		||||
    url(r"^teams/", include("symposion.teams.urls")),
 | 
			
		||||
    url(r"^markitup/", include("markitup.urls")),
 | 
			
		||||
    
 | 
			
		||||
    url(r"^", include("symposion.cms.urls")),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue