commit
49bfc58dda
|
@ -1,16 +1,10 @@
|
||||||
from allauth.account.views import (
|
from allauth.account.views import (
|
||||||
SignupView,
|
|
||||||
LoginView,
|
LoginView,
|
||||||
LogoutView,
|
LogoutView,
|
||||||
ConfirmEmailView,
|
|
||||||
EmailVerificationSentView,
|
|
||||||
PasswordResetView
|
|
||||||
)
|
)
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView, RedirectView
|
|
||||||
from django.core.urlresolvers import reverse_lazy
|
|
||||||
from camps.views import *
|
from camps.views import *
|
||||||
from info.views import *
|
from info.views import *
|
||||||
from villages.views import *
|
from villages.views import *
|
||||||
|
@ -291,6 +285,16 @@ urlpatterns = [
|
||||||
TeamListView.as_view(),
|
TeamListView.as_view(),
|
||||||
name='team_list'
|
name='team_list'
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^members/(?P<pk>[0-9]+)/remove/$',
|
||||||
|
TeamMemberRemoveView.as_view(),
|
||||||
|
name='teammember_remove',
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^members/(?P<pk>[0-9]+)/approve/$',
|
||||||
|
TeamMemberApproveView.as_view(),
|
||||||
|
name='teammember_approve',
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'(?P<slug>[-_\w+]+)/join/$',
|
r'(?P<slug>[-_\w+]+)/join/$',
|
||||||
TeamJoinView.as_view(),
|
TeamJoinView.as_view(),
|
||||||
|
|
8
src/teams/forms.py
Normal file
8
src/teams/forms.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from .models import Team
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTeamForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Team
|
||||||
|
fields = ['description', 'needs_members']
|
32
src/teams/migrations/0013_auto_20170523_2046.py
Normal file
32
src/teams/migrations/0013_auto_20170523_2046.py
Normal file
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
19
src/teams/migrations/0014_remove_teammember_deleted.py
Normal file
19
src/teams/migrations/0014_remove_teammember_deleted.py
Normal file
|
@ -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',
|
||||||
|
),
|
||||||
|
]
|
|
@ -85,7 +85,7 @@ class Team(CampRelatedModel):
|
||||||
return self.area.responsible.all()
|
return self.area.responsible.all()
|
||||||
|
|
||||||
|
|
||||||
class TeamMember(models.Model):
|
class TeamMember(CampRelatedModel):
|
||||||
user = models.ForeignKey('auth.User')
|
user = models.ForeignKey('auth.User')
|
||||||
team = models.ForeignKey('teams.Team')
|
team = models.ForeignKey('teams.Team')
|
||||||
approved = models.BooleanField(default=False)
|
approved = models.BooleanField(default=False)
|
||||||
|
@ -96,6 +96,10 @@ class TeamMember(models.Model):
|
||||||
self.user, '' if self.approved else 'an unapproved', self.team
|
self.user, '' if self.approved else 'an unapproved', self.team
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def camp(self):
|
||||||
|
return self.team.camp
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=TeamMember)
|
@receiver(post_save, sender=TeamMember)
|
||||||
def add_responsible_email(sender, instance, created, **kwargs):
|
def add_responsible_email(sender, instance, created, **kwargs):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load commonmark %}
|
{% load commonmark %}
|
||||||
{% load teams_tags %}
|
{% load teams_tags %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
Team: {{ team.name }} | {{ block.super }}
|
Team: {{ team.name }} | {{ block.super }}
|
||||||
|
@ -33,4 +34,65 @@ Team: {{ team.name }} | {{ block.super }}
|
||||||
<li><b>{{ resp.get_full_name|default:"Unnamed" }}</b></li>
|
<li><b>{{ resp.get_full_name|default:"Unnamed" }}</b></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
{% if request.user in team.responsible %}
|
||||||
|
|
||||||
|
<h3>Manage {{ team.name }} Team</h3>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{% buttons %}
|
||||||
|
<button class="btn btn-primary pull-right" type="submit">Save</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Profile
|
||||||
|
<th >
|
||||||
|
Name
|
||||||
|
<th>
|
||||||
|
Email
|
||||||
|
<th>
|
||||||
|
Description
|
||||||
|
<th>
|
||||||
|
Membership Status
|
||||||
|
<th>
|
||||||
|
Action
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for membership in team.teammember_set.all %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ membership.user }}
|
||||||
|
<td>
|
||||||
|
{{ membership.user.profile.name }}
|
||||||
|
<td>
|
||||||
|
{{ membership.user.profile.email }}
|
||||||
|
<td>
|
||||||
|
{{ membership.user.profile.description }}
|
||||||
|
<td>
|
||||||
|
{% if membership.approved %}member{% else %}pending{% endif %}
|
||||||
|
<td>
|
||||||
|
{% if membership.approved %}
|
||||||
|
<a class="btn btn-danger" href="{% url 'teammember_remove' camp_slug=camp.slug pk=membership.id %}"><i class="fa fa-trash-o"></i> Remove</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-danger" href="{% url 'teammember_remove' camp_slug=camp.slug pk=membership.id %}"><i class="fa fa-trash-o"></i> Remove</a>
|
||||||
|
<a class="btn btn-success" href="{% url 'teammember_approve' camp_slug=camp.slug pk=membership.id %}"><i class="fa fa-check"></i> Approve</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
18
src/teams/templates/teammember_approve.html
Normal file
18
src/teams/templates/teammember_approve.html
Normal file
|
@ -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 %}
|
||||||
|
|
||||||
|
<h3>Approve member {{ teammember.user.profile.name }} for the {{ teammember.team.name }} team</h3>
|
||||||
|
<p class="lead">Really approve the user <b>{{ teammember.user.profile.name }}</b> for the {{ teammember.team.name }} team? The user will receive an email with a message.<p>
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<button class="btn btn-success" type="submit"><i class="fa fa-check"></i> Add teammember</button>
|
||||||
|
<a href="{% url 'team_detail' camp_slug=teammember.team.camp.slug slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fa fa-remove"></i> Cancel</a>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
18
src/teams/templates/teammember_remove.html
Normal file
18
src/teams/templates/teammember_remove.html
Normal file
|
@ -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 %}
|
||||||
|
|
||||||
|
<h3>Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.name }} team</h3>
|
||||||
|
<p class="lead">Really remove the user <b>{{ teammember.user.profile.name }}</b> from the {{ teammember.team.name }} team? The user will receive an email with a message.<p>
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<button class="btn btn-danger" type="submit"><i class="fa fa-trash-o"></i> Remove teammember</button>
|
||||||
|
<a href="{% url 'team_detail' camp_slug=teammember.team.camp.slug slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fa fa-remove"></i> Cancel</a>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -1,10 +1,14 @@
|
||||||
from django.views.generic import ListView, DetailView
|
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 camps.mixins import CampViewMixin
|
||||||
from .models import Team, TeamMember
|
from .models import Team, TeamMember
|
||||||
|
from .forms import ManageTeamForm
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.contrib import messages
|
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):
|
class TeamListView(CampViewMixin, ListView):
|
||||||
|
@ -13,9 +17,10 @@ class TeamListView(CampViewMixin, ListView):
|
||||||
context_object_name = 'teams'
|
context_object_name = 'teams'
|
||||||
|
|
||||||
|
|
||||||
class TeamDetailView(CampViewMixin, DetailView):
|
class TeamDetailView(CampViewMixin, DetailView, UpdateView, FormView):
|
||||||
template_name = "team_detail.html"
|
template_name = "team_detail.html"
|
||||||
model = Team
|
model = Team
|
||||||
|
form_class = ManageTeamForm
|
||||||
context_object_name = 'team'
|
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)
|
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)
|
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)
|
||||||
|
|
Loading…
Reference in a new issue