Reorganize views for better clarity. Add editing capabilities for Info Items.
This commit is contained in:
parent
735f17b1f1
commit
b668ac0694
|
@ -1,4 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from reversion.admin import VersionAdmin
|
||||||
from .models import (
|
from .models import (
|
||||||
InfoItem,
|
InfoItem,
|
||||||
InfoCategory
|
InfoCategory
|
||||||
|
@ -6,7 +7,7 @@ from .models import (
|
||||||
|
|
||||||
|
|
||||||
@admin.register(InfoItem)
|
@admin.register(InfoItem)
|
||||||
class InfoItemAdmin(admin.ModelAdmin):
|
class InfoItemAdmin(VersionAdmin):
|
||||||
list_filter = ['category', 'category__camp',]
|
list_filter = ['category', 'category__camp',]
|
||||||
list_display = ['headline',]
|
list_display = ['headline',]
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ class InfoCategory(CampRelatedModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The team responsible for this info category.',
|
help_text='The team responsible for this info category.',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
related_name='info_categories'
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -109,6 +109,9 @@ class Team(CampRelatedModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} ({})'.format(self.name, self.camp)
|
return '{} ({})'.format(self.name, self.camp)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse_lazy('teams:detail', kwargs={'camp_slug': self.camp.slug, 'team_slug': self.slug})
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
# generate slug if needed
|
# generate slug if needed
|
||||||
if not self.pk or not self.slug:
|
if not self.pk or not self.slug:
|
||||||
|
|
35
src/teams/templates/info_item_form.html
Normal file
35
src/teams/templates/info_item_form.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load commonmark %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% if form.instance.id %}
|
||||||
|
Edit Info Item: {{ form.instance.headline }}
|
||||||
|
{% else %}
|
||||||
|
Create Info item
|
||||||
|
{% endif %}
|
||||||
|
for in {{ form.instance.category.headline }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4>
|
||||||
|
{% if form.instance.id %}
|
||||||
|
Edit Info Item: {{ form.instance.name }}
|
||||||
|
{% else %}
|
||||||
|
Create Info Item
|
||||||
|
{% endif %}
|
||||||
|
for in {{ form.instance.category.headline }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
<button type="submit" class="btn btn-primary">{% if form.instance.id %}Save{% else %}Create{% endif %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><i>This info item belongs to the <a href="{% url 'teams:detail' team_slug=team.slug camp_slug=team.camp.slug %}">{{ team.name }} Team</a></i></div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -115,6 +115,41 @@ Team: {{ team.name }} | {{ block.super }}
|
||||||
{% if request.user in team.responsible_members.all %}
|
{% if request.user in team.responsible_members.all %}
|
||||||
<a href="{% url 'teams:task_create' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fa fa-plus"></i> Create Task</a>
|
<a href="{% url 'teams:task_create' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fa fa-plus"></i> Create Task</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# Team info categories section - only visible for team responsible #}
|
||||||
|
{% if request.user in team.responsible_members.all %}
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h4>Info categories</h4>
|
||||||
|
|
||||||
|
{% for info_category in team.info_categories.all %}
|
||||||
|
|
||||||
|
{{ info_category.headline }}
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Item name</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in info_category.infoitems.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ item.headline }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'teams:info_item_update' camp_slug=camp.slug team_slug=team.slug anchor=item.anchor %}"
|
||||||
|
class="btn btn-primary btn-sm">
|
||||||
|
<i class="fa fa-edit"></i> Edit Task
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
from .views import *
|
|
||||||
|
|
||||||
|
from teams.views.base import (
|
||||||
|
TeamListView,
|
||||||
|
TeamMemberRemoveView,
|
||||||
|
TeamMemberApproveView,
|
||||||
|
TeamDetailView,
|
||||||
|
TeamJoinView,
|
||||||
|
TeamLeaveView,
|
||||||
|
TeamManageView,
|
||||||
|
FixIrcAclView,
|
||||||
|
)
|
||||||
|
from teams.views.info import InfoItemUpdateView
|
||||||
|
|
||||||
|
from teams.views.tasks import (
|
||||||
|
TaskCreateView,
|
||||||
|
TaskDetailView,
|
||||||
|
TaskUpdateView,
|
||||||
|
)
|
||||||
|
|
||||||
app_name = 'teams'
|
app_name = 'teams'
|
||||||
|
|
||||||
|
@ -75,6 +91,24 @@ urlpatterns = [
|
||||||
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^info_items/', include([
|
||||||
|
url(
|
||||||
|
r'^(?P<anchor>[-_\w+]+)/', include([
|
||||||
|
# url(
|
||||||
|
# r'^$',
|
||||||
|
# TaskDetailView.as_view(),
|
||||||
|
# name='task_detail',
|
||||||
|
# ),
|
||||||
|
url(
|
||||||
|
r'^update/$',
|
||||||
|
InfoItemUpdateView.as_view(),
|
||||||
|
name='info_item_update',
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
)
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
0
src/teams/views/__init__.py
Normal file
0
src/teams/views/__init__.py
Normal file
|
@ -1,52 +1,22 @@
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.views.generic.edit import CreateView, UpdateView
|
from django.views.generic.edit import UpdateView
|
||||||
from camps.mixins import CampViewMixin
|
from camps.mixins import CampViewMixin
|
||||||
from .models import Team, TeamMember, TeamTask
|
|
||||||
from .email import add_added_membership_email, add_removed_membership_email
|
|
||||||
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 HttpResponseRedirect
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from profiles.models import Profile
|
from profiles.models import Profile
|
||||||
|
|
||||||
|
from .mixins import EnsureTeamResponsibleMixin, EnsureTeamMemberResponsibleMixin
|
||||||
|
from ..models import Team, TeamMember
|
||||||
|
from ..email import add_added_membership_email, add_removed_membership_email
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class EnsureTeamResponsibleMixin(object):
|
|
||||||
"""
|
|
||||||
Use to make sure request.user is responsible for the team specified by kwargs['team_slug']
|
|
||||||
"""
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.team = Team.objects.get(slug=kwargs['team_slug'], camp=self.camp)
|
|
||||||
if request.user not in self.team.responsible_members.all():
|
|
||||||
messages.error(request, 'No thanks')
|
|
||||||
return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=self.team.slug)
|
|
||||||
|
|
||||||
return super().dispatch(
|
|
||||||
request, *args, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class EnsureTeamMemberResponsibleMixin(SingleObjectMixin):
|
|
||||||
"""
|
|
||||||
Use to make sure request.user is responsible for the team which TeamMember belongs to
|
|
||||||
"""
|
|
||||||
model = TeamMember
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
if request.user not in self.get_object().team.responsible_members.all():
|
|
||||||
messages.error(request, 'No thanks')
|
|
||||||
return redirect('teams:detail', camp_slug=self.get_object().team.camp.slug, team_slug=self.get_object().team.slug)
|
|
||||||
|
|
||||||
return super().dispatch(
|
|
||||||
request, *args, **kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TeamListView(CampViewMixin, ListView):
|
class TeamListView(CampViewMixin, ListView):
|
||||||
template_name = "team_list.html"
|
template_name = "team_list.html"
|
||||||
model = Team
|
model = Team
|
||||||
|
@ -165,56 +135,6 @@ class TeamMemberApproveView(LoginRequiredMixin, CampViewMixin, EnsureTeamMemberR
|
||||||
return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=form.instance.team.slug)
|
return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=form.instance.team.slug)
|
||||||
|
|
||||||
|
|
||||||
class TaskDetailView(CampViewMixin, DetailView):
|
|
||||||
template_name = "task_detail.html"
|
|
||||||
context_object_name = "task"
|
|
||||||
model = TeamTask
|
|
||||||
|
|
||||||
|
|
||||||
class TaskCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
|
||||||
model = TeamTask
|
|
||||||
template_name = "task_form.html"
|
|
||||||
fields = ['name', 'description']
|
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['team'] = self.team
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
task = form.save(commit=False)
|
|
||||||
task.team = self.team
|
|
||||||
if not task.name:
|
|
||||||
task.name = "noname"
|
|
||||||
task.save()
|
|
||||||
return HttpResponseRedirect(task.get_absolute_url())
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return self.get_object().get_absolute_url()
|
|
||||||
|
|
||||||
|
|
||||||
class TaskUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, UpdateView):
|
|
||||||
model = TeamTask
|
|
||||||
template_name = "task_form.html"
|
|
||||||
fields = ['name', 'description']
|
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['team'] = self.team
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
task = form.save(commit=False)
|
|
||||||
task.team = self.team
|
|
||||||
if not task.name:
|
|
||||||
task.name = "noname"
|
|
||||||
task.save()
|
|
||||||
return HttpResponseRedirect(task.get_absolute_url())
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return self.get_object().get_absolute_url()
|
|
||||||
|
|
||||||
|
|
||||||
class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView):
|
class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView):
|
||||||
template_name = "fix_irc_acl.html"
|
template_name = "fix_irc_acl.html"
|
||||||
model = Team
|
model = Team
|
39
src/teams/views/info.py
Normal file
39
src/teams/views/info.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.views.generic import CreateView, UpdateView
|
||||||
|
from reversion.views import RevisionMixin
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
from info.models import InfoItem
|
||||||
|
from teams.views.mixins import EnsureTeamResponsibleMixin
|
||||||
|
|
||||||
|
|
||||||
|
class InfoItemCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||||
|
model = InfoItem
|
||||||
|
template_name = "info_item_form.html"
|
||||||
|
fields = ['headline', 'body', 'anchor', 'weight']
|
||||||
|
slug_field = 'anchor'
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['team'] = self.object.category.team
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.category.team.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
|
class InfoItemUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, RevisionMixin, UpdateView):
|
||||||
|
model = InfoItem
|
||||||
|
template_name = "info_item_form.html"
|
||||||
|
fields = ['headline', 'body', 'anchor', 'weight']
|
||||||
|
slug_field = 'anchor'
|
||||||
|
slug_url_kwarg = 'anchor'
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['team'] = self.object.category.team
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.category.team.get_absolute_url()
|
||||||
|
|
36
src/teams/views/mixins.py
Normal file
36
src/teams/views/mixins.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
|
from teams.models import Team, TeamMember
|
||||||
|
|
||||||
|
|
||||||
|
class EnsureTeamResponsibleMixin(object):
|
||||||
|
"""
|
||||||
|
Use to make sure request.user is responsible for the team specified by kwargs['team_slug']
|
||||||
|
"""
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
self.team = Team.objects.get(slug=kwargs['team_slug'], camp=self.camp)
|
||||||
|
if request.user not in self.team.responsible_members.all():
|
||||||
|
messages.error(request, 'No thanks')
|
||||||
|
return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=self.team.slug)
|
||||||
|
|
||||||
|
return super().dispatch(
|
||||||
|
request, *args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EnsureTeamMemberResponsibleMixin(SingleObjectMixin):
|
||||||
|
"""
|
||||||
|
Use to make sure request.user is responsible for the team which TeamMember belongs to
|
||||||
|
"""
|
||||||
|
model = TeamMember
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if request.user not in self.get_object().team.responsible_members.all():
|
||||||
|
messages.error(request, 'No thanks')
|
||||||
|
return redirect('teams:detail', camp_slug=self.get_object().team.camp.slug, team_slug=self.get_object().team.slug)
|
||||||
|
|
||||||
|
return super().dispatch(
|
||||||
|
request, *args, **kwargs
|
||||||
|
)
|
57
src/teams/views/tasks.py
Normal file
57
src/teams/views/tasks.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.views.generic import DetailView, CreateView, UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
from ..models import TeamTask
|
||||||
|
from .mixins import EnsureTeamResponsibleMixin
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDetailView(CampViewMixin, DetailView):
|
||||||
|
template_name = "task_detail.html"
|
||||||
|
context_object_name = "task"
|
||||||
|
model = TeamTask
|
||||||
|
|
||||||
|
|
||||||
|
class TaskCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||||
|
model = TeamTask
|
||||||
|
template_name = "task_form.html"
|
||||||
|
fields = ['name', 'description']
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['team'] = self.team
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
task = form.save(commit=False)
|
||||||
|
task.team = self.team
|
||||||
|
if not task.name:
|
||||||
|
task.name = "noname"
|
||||||
|
task.save()
|
||||||
|
return HttpResponseRedirect(task.get_absolute_url())
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.get_object().get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
|
class TaskUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, UpdateView):
|
||||||
|
model = TeamTask
|
||||||
|
template_name = "task_form.html"
|
||||||
|
fields = ['name', 'description']
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['team'] = self.team
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
task = form.save(commit=False)
|
||||||
|
task.team = self.team
|
||||||
|
if not task.name:
|
||||||
|
task.name = "noname"
|
||||||
|
task.save()
|
||||||
|
return HttpResponseRedirect(task.get_absolute_url())
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.get_object().get_absolute_url()
|
Loading…
Reference in a new issue