diff --git a/src/bornhack/urls.py b/src/bornhack/urls.py index 896392ad..ecb54200 100644 --- a/src/bornhack/urls.py +++ b/src/bornhack/urls.py @@ -1,16 +1,10 @@ from allauth.account.views import ( - SignupView, LoginView, LogoutView, - ConfirmEmailView, - EmailVerificationSentView, - PasswordResetView ) from django.conf import settings from django.conf.urls import include, url from django.contrib import admin -from django.views.generic import TemplateView, RedirectView -from django.core.urlresolvers import reverse_lazy from camps.views import * from info.views import * from villages.views import * @@ -291,6 +285,16 @@ urlpatterns = [ TeamListView.as_view(), name='team_list' ), + url( + r'^members/(?P[0-9]+)/remove/$', + TeamMemberRemoveView.as_view(), + name='teammember_remove', + ), + url( + r'^members/(?P[0-9]+)/approve/$', + TeamMemberApproveView.as_view(), + name='teammember_approve', + ), url( r'(?P[-_\w+]+)/join/$', TeamJoinView.as_view(), diff --git a/src/teams/forms.py b/src/teams/forms.py new file mode 100644 index 00000000..dab9796c --- /dev/null +++ b/src/teams/forms.py @@ -0,0 +1,8 @@ +from django.forms import ModelForm +from .models import Team + + +class ManageTeamForm(ModelForm): + class Meta: + model = Team + fields = ['description', 'needs_members'] diff --git a/src/teams/migrations/0013_auto_20170523_2046.py b/src/teams/migrations/0013_auto_20170523_2046.py new file mode 100644 index 00000000..fb536ace --- /dev/null +++ b/src/teams/migrations/0013_auto_20170523_2046.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-05-23 18:46 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0012_teammember_responsible'), + ] + + operations = [ + migrations.AddField( + model_name='teammember', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='teammember', + name='deleted', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='teammember', + name='updated', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/src/teams/migrations/0014_remove_teammember_deleted.py b/src/teams/migrations/0014_remove_teammember_deleted.py new file mode 100644 index 00000000..759afe2e --- /dev/null +++ b/src/teams/migrations/0014_remove_teammember_deleted.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-05-23 19:13 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0013_auto_20170523_2046'), + ] + + operations = [ + migrations.RemoveField( + model_name='teammember', + name='deleted', + ), + ] diff --git a/src/teams/models.py b/src/teams/models.py index 7c82e5c5..f1293e83 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -85,7 +85,7 @@ class Team(CampRelatedModel): return self.area.responsible.all() -class TeamMember(models.Model): +class TeamMember(CampRelatedModel): user = models.ForeignKey('auth.User') team = models.ForeignKey('teams.Team') approved = models.BooleanField(default=False) @@ -96,6 +96,10 @@ class TeamMember(models.Model): self.user, '' if self.approved else 'an unapproved', self.team ) + @property + def camp(self): + return self.team.camp + @receiver(post_save, sender=TeamMember) def add_responsible_email(sender, instance, created, **kwargs): diff --git a/src/teams/templates/team_detail.html b/src/teams/templates/team_detail.html index c71994cb..4a4dc541 100644 --- a/src/teams/templates/team_detail.html +++ b/src/teams/templates/team_detail.html @@ -1,6 +1,7 @@ {% extends 'base.html' %} {% load commonmark %} {% load teams_tags %} +{% load bootstrap3 %} {% block title %} Team: {{ team.name }} | {{ block.super }} @@ -33,4 +34,65 @@ Team: {{ team.name }} | {{ block.super }}
  • {{ resp.get_full_name|default:"Unnamed" }}
  • {% endfor %} + +
    + +{% if request.user in team.responsible %} + +

    Manage {{ team.name }} Team

    +
    + {% csrf_token %} + + {% bootstrap_form form %} + +
    + + {% buttons %} + + {% endbuttons %} +
    + +
    +
    + + + + + + {% for membership in team.teammember_set.all %} + +
    + Profile + + Name + + Email + + Description + + Membership Status + + Action + +
    + {{ membership.user }} + + {{ membership.user.profile.name }} + + {{ membership.user.profile.email }} + + {{ membership.user.profile.description }} + + {% if membership.approved %}member{% else %}pending{% endif %} + + {% if membership.approved %} + Remove + {% else %} + Remove + Approve + {% endif %} + {% endfor %} + +{% endif %} + {% endblock %} diff --git a/src/teams/templates/teammember_approve.html b/src/teams/templates/teammember_approve.html new file mode 100644 index 00000000..c1154d89 --- /dev/null +++ b/src/teams/templates/teammember_approve.html @@ -0,0 +1,18 @@ +{% extends 'base.html' %} +{% load commonmark %} + +{% block title %} +Approve team member {{ teammember.user.profile.name }} for the {{ teammember.team.name }} team +{% endblock %} + +{% block content %} + +

    Approve member {{ teammember.user.profile.name }} for the {{ teammember.team.name }} team

    +

    Really approve the user {{ teammember.user.profile.name }} for the {{ teammember.team.name }} team? The user will receive an email with a message.

    +

    +{% csrf_token %} +{{ form }} + + Cancel +
    +{% endblock %} diff --git a/src/teams/templates/teammember_remove.html b/src/teams/templates/teammember_remove.html new file mode 100644 index 00000000..9e49c15b --- /dev/null +++ b/src/teams/templates/teammember_remove.html @@ -0,0 +1,18 @@ +{% extends 'base.html' %} +{% load commonmark %} + +{% block title %} +Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.name }} team +{% endblock %} + +{% block content %} + +

    Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.name }} team

    +

    Really remove the user {{ teammember.user.profile.name }} from the {{ teammember.team.name }} team? The user will receive an email with a message.

    +

    +{% csrf_token %} +{{ form }} + + Cancel +
    +{% endblock %} diff --git a/src/teams/views.py b/src/teams/views.py index ea58fd33..e3030273 100644 --- a/src/teams/views.py +++ b/src/teams/views.py @@ -1,10 +1,14 @@ from django.views.generic import ListView, DetailView -from django.views.generic.edit import UpdateView +from django.views.generic.edit import UpdateView, FormView from camps.mixins import CampViewMixin from .models import Team, TeamMember +from .forms import ManageTeamForm from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import redirect from django.contrib import messages +from django.http import Http404, HttpResponseRedirect +from django.views.generic.detail import SingleObjectMixin +from django.core.urlresolvers import reverse_lazy class TeamListView(CampViewMixin, ListView): @@ -13,9 +17,10 @@ class TeamListView(CampViewMixin, ListView): context_object_name = 'teams' -class TeamDetailView(CampViewMixin, DetailView): +class TeamDetailView(CampViewMixin, DetailView, UpdateView, FormView): template_name = "team_detail.html" model = Team + form_class = ManageTeamForm context_object_name = 'team' @@ -58,3 +63,38 @@ class TeamLeaveView(LoginRequiredMixin, CampViewMixin, UpdateView): messages.success(self.request, "You are no longer a member of the team %s" % self.get_object().name) return redirect('team_list', camp_slug=self.get_object().camp.slug) + +class EnsureTeamResponsibleMixin(SingleObjectMixin): + model = TeamMember + + def dispatch(self, request, *args, **kwargs): + if request.user not in self.get_object().team.responsible.all(): + messages.error(request, 'No thanks') + return HttpResponseRedirect(reverse_lazy('team_detail', slug=self.get_object().team.slug)) + + return super().dispatch( + request, *args, **kwargs + ) + + +class TeamMemberRemoveView(LoginRequiredMixin, EnsureTeamResponsibleMixin, UpdateView): + template_name = "teammember_remove.html" + model = TeamMember + fields = [] + + def form_valid(self, form): + form.instance.delete() + messages.success(self.request, "Team member removed") + return redirect('team_detail', camp_slug=form.instance.team.camp.slug, slug=form.instance.team.slug) + + +class TeamMemberApproveView(LoginRequiredMixin, EnsureTeamResponsibleMixin, UpdateView): + template_name = "teammember_approve.html" + model = TeamMember + fields = [] + + def form_valid(self, form): + form.instance.approved = True + form.instance.save() + messages.success(self.request, "Team member approved") + return redirect('team_detail', camp_slug=form.instance.team.camp.slug, slug=form.instance.team.slug)