diff --git a/src/info/admin.py b/src/info/admin.py index 941ac13a..9db9c82d 100644 --- a/src/info/admin.py +++ b/src/info/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from reversion.admin import VersionAdmin from .models import ( InfoItem, InfoCategory @@ -6,7 +7,7 @@ from .models import ( @admin.register(InfoItem) -class InfoItemAdmin(admin.ModelAdmin): +class InfoItemAdmin(VersionAdmin): list_filter = ['category', 'category__camp',] list_display = ['headline',] diff --git a/src/info/models.py b/src/info/models.py index f86c60b1..75bddf20 100644 --- a/src/info/models.py +++ b/src/info/models.py @@ -37,6 +37,7 @@ class InfoCategory(CampRelatedModel): blank=True, help_text='The team responsible for this info category.', on_delete=models.PROTECT, + related_name='info_categories' ) def clean(self): diff --git a/src/teams/models.py b/src/teams/models.py index 03b8968b..0fd90245 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -109,6 +109,9 @@ class Team(CampRelatedModel): def __str__(self): 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): # generate slug if needed if not self.pk or not self.slug: diff --git a/src/teams/templates/info_item_form.html b/src/teams/templates/info_item_form.html new file mode 100644 index 00000000..465671db --- /dev/null +++ b/src/teams/templates/info_item_form.html @@ -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 %} +
+
+

+ {% if form.instance.id %} + Edit Info Item: {{ form.instance.name }} + {% else %} + Create Info Item + {% endif %} + for in {{ form.instance.category.headline }} +

+
+
+
+ {% csrf_token %} + {% bootstrap_form form %} + +
+
+ +
+{% endblock %} diff --git a/src/teams/templates/team_detail.html b/src/teams/templates/team_detail.html index 9d581363..86a96040 100644 --- a/src/teams/templates/team_detail.html +++ b/src/teams/templates/team_detail.html @@ -115,6 +115,41 @@ Team: {{ team.name }} | {{ block.super }} {% if request.user in team.responsible_members.all %} Create Task {% endif %} + + {# Team info categories section - only visible for team responsible #} + {% if request.user in team.responsible_members.all %} +
+ +

Info categories

+ + {% for info_category in team.info_categories.all %} + + {{ info_category.headline }} + + + + + + + + + {% for item in info_category.infoitems.all %} + + + + + {% endfor %} + +
Item nameAction
{{ item.headline }} + + Edit Task + +
+ + {% endfor %} + + {% endif %} diff --git a/src/teams/urls.py b/src/teams/urls.py index eecc5acc..555bc945 100644 --- a/src/teams/urls.py +++ b/src/teams/urls.py @@ -1,6 +1,22 @@ 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' @@ -75,6 +91,24 @@ urlpatterns = [ ]), ), + url( + r'^info_items/', include([ + url( + r'^(?P[-_\w+]+)/', include([ + # url( + # r'^$', + # TaskDetailView.as_view(), + # name='task_detail', + # ), + url( + r'^update/$', + InfoItemUpdateView.as_view(), + name='info_item_update', + ), + ]), + ), + ]) + ) ]), ), ] diff --git a/src/teams/views/__init__.py b/src/teams/views/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/teams/views.py b/src/teams/views/base.py similarity index 73% rename from src/teams/views.py rename to src/teams/views/base.py index f47b33c7..4ede5bd3 100644 --- a/src/teams/views.py +++ b/src/teams/views/base.py @@ -1,52 +1,22 @@ 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 .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.shortcuts import redirect 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.conf import settings + 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 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): template_name = "team_list.html" 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) -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): template_name = "fix_irc_acl.html" model = Team diff --git a/src/teams/views/info.py b/src/teams/views/info.py new file mode 100644 index 00000000..64c77916 --- /dev/null +++ b/src/teams/views/info.py @@ -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() + diff --git a/src/teams/views/mixins.py b/src/teams/views/mixins.py new file mode 100644 index 00000000..db937891 --- /dev/null +++ b/src/teams/views/mixins.py @@ -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 + ) \ No newline at end of file diff --git a/src/teams/views/tasks.py b/src/teams/views/tasks.py new file mode 100644 index 00000000..5e661df0 --- /dev/null +++ b/src/teams/views/tasks.py @@ -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() \ No newline at end of file