From c68015fe261b2bb96ec9c496e399dd36244fc019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=AD=C3=B0ir=20Valberg=20Gu=C3=B0mundsson?= Date: Sun, 22 Jul 2018 23:18:50 +0200 Subject: [PATCH 1/7] Teams detail page was starting to get crowded. This is the start of a mostly visual, but also structural, refactor. --- src/static_src/css/bornhack.css | 2 +- src/teams/models.py | 3 +- src/teams/templates/fix_irc_acl.html | 2 +- src/teams/templates/info_item_form.html | 6 +- src/teams/templates/task_detail.html | 5 +- src/teams/templates/task_form.html | 6 +- src/teams/templates/team_base.html | 69 +++++++ src/teams/templates/team_detail.html | 189 ------------------ src/teams/templates/team_general.html | 48 +++++ src/teams/templates/team_info_categories.html | 52 +++++ src/teams/templates/team_join.html | 4 +- src/teams/templates/team_leave.html | 4 +- src/teams/templates/team_list.html | 6 +- src/teams/templates/team_manage.html | 75 +------ src/teams/templates/team_members.html | 84 ++++++++ src/teams/templates/team_tasks.html | 42 ++++ src/teams/templates/teammember_approve.html | 7 +- src/teams/templates/teammember_remove.html | 6 +- src/teams/urls.py | 64 ++++-- src/teams/views/base.py | 113 ++--------- src/teams/views/members.py | 117 +++++++++++ src/teams/views/mixins.py | 4 +- src/teams/views/tasks.py | 16 +- src/templates/base.html | 2 +- 24 files changed, 515 insertions(+), 411 deletions(-) create mode 100644 src/teams/templates/team_base.html delete mode 100644 src/teams/templates/team_detail.html create mode 100644 src/teams/templates/team_general.html create mode 100644 src/teams/templates/team_info_categories.html create mode 100644 src/teams/templates/team_members.html create mode 100644 src/teams/templates/team_tasks.html create mode 100644 src/teams/views/members.py diff --git a/src/static_src/css/bornhack.css b/src/static_src/css/bornhack.css index a5797eaa..3f904b40 100644 --- a/src/static_src/css/bornhack.css +++ b/src/static_src/css/bornhack.css @@ -48,7 +48,7 @@ a, a:active, a:focus { margin-top: 6px; } -.nav li a { +#top-navbar > .nav li a { padding: 30px 7px; } diff --git a/src/teams/models.py b/src/teams/models.py index b145c44b..c6b3ae63 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -110,7 +110,7 @@ class Team(CampRelatedModel): 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}) + return reverse_lazy('teams:general', kwargs={'camp_slug': self.camp.slug, 'team_slug': self.slug}) def save(self, **kwargs): # generate slug if needed @@ -215,6 +215,7 @@ class Team(CampRelatedModel): class TeamMember(CampRelatedModel): + user = models.ForeignKey( 'auth.User', on_delete=models.PROTECT, diff --git a/src/teams/templates/fix_irc_acl.html b/src/teams/templates/fix_irc_acl.html index 4ee75272..8a8887e6 100644 --- a/src/teams/templates/fix_irc_acl.html +++ b/src/teams/templates/fix_irc_acl.html @@ -13,6 +13,6 @@ Fix IRC permissions for NickServ user {{ request.user.profile.nickserv_username {% csrf_token %} {{ form }} - Cancel + Cancel {% endblock %} diff --git a/src/teams/templates/info_item_form.html b/src/teams/templates/info_item_form.html index 03d56ddf..26327630 100644 --- a/src/teams/templates/info_item_form.html +++ b/src/teams/templates/info_item_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% load bootstrap3 %} @@ -11,7 +11,7 @@ Create Info item in "{{ form.instance.category.headline }}" {% endblock %} -{% block content %} +{% block team_content %}

@@ -36,6 +36,6 @@ in "{{ form.instance.category.headline }}" {% endif %}

- +
{% endblock %} diff --git a/src/teams/templates/task_detail.html b/src/teams/templates/task_detail.html index 69525bbe..f3a49019 100644 --- a/src/teams/templates/task_detail.html +++ b/src/teams/templates/task_detail.html @@ -1,15 +1,14 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% block title %} {{ task.name }} {% endblock %} -{% block content %} +{% block team_content %}

Task: {{ task.name }}

{{ task.description|untrustedcommonmark }}
-
diff --git a/src/teams/templates/task_form.html b/src/teams/templates/task_form.html index c03e64c6..69d7e603 100644 --- a/src/teams/templates/task_form.html +++ b/src/teams/templates/task_form.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% load bootstrap3 %} @@ -11,7 +11,7 @@ Create Task for {{ team.name }} Team {% endblock %} -{% block content %} +{% block team_content %}

@@ -30,7 +30,7 @@ for {{ team.name }} Team

- +
{% endblock %} diff --git a/src/teams/templates/team_base.html b/src/teams/templates/team_base.html new file mode 100644 index 00000000..1a56fa5d --- /dev/null +++ b/src/teams/templates/team_base.html @@ -0,0 +1,69 @@ +{% extends 'base.html' %} +{% load commonmark %} +{% load bootstrap3 %} +{% load teams_tags %} + +{% block title %} +Team: {{ team.name }} | {{ block.super }} +{% endblock %} + +{% block content %} + + + +
+
+ + +
+ + {% if request.user.is_authenticated %} + + {% if request.user in team.members.all %} +

Your membership status: {% membershipstatus user team %}

+ + {% if request.user in team.responsible_members.all %} + Manage Team + {% endif %} + + {% else %} + {% if team.needs_members %} + This team is looking for members! Join Team + {% endif %} + {% endif %} + + {% endif %} +
+ +
+ + {% block team_content %}{% endblock %} + +
+
+ +{% endblock %} diff --git a/src/teams/templates/team_detail.html b/src/teams/templates/team_detail.html deleted file mode 100644 index 8c7c4096..00000000 --- a/src/teams/templates/team_detail.html +++ /dev/null @@ -1,189 +0,0 @@ -{% extends 'base.html' %} -{% load commonmark %} -{% load bootstrap3 %} -{% load teams_tags %} - -{% block title %} -Team: {{ team.name }} | {{ block.super }} -{% endblock %} - -{% block content %} - - - -
-
-

Description

-
-
- {{ team.description|untrustedcommonmark }} -
-
- -{# Team communications #} -
-
-

Communication Channels

-
-
- {{ team.camp.title }} teams primarily use mailing lists and IRC to communicate. The {{ team.name }} team can be contacted in the following ways:

- -
Mailing List
- {% if team.mailing_list and request.user in team.approved_members.all %} -

The {{ team.name }} Team mailinglist is {{ team.mailing_list }}{% if team.mailing_list_archive_public %}, and the archives are publicly available{% endif %}. You should sign up for the list if you haven't already.

- {% elif team.mailing_list and team.mailinglist_nonmember_posts %} -

The {{ team.name }} Team mailinglist is {{ team.mailing_list }}{% if team.mailing_list_archive_public %}, and the archives are publicly available{% endif %}. You do not need to be a member of the list to post to it.

- {% else %} -

The {{ team.name }} Team does not have a public mailing list, but it can be contacted through our main email info@bornhack.dk. - {% endif %} - -

IRC Channel
- {% if team.public_irc_channel_name %} -

The {{ team.name }} Team public IRC channel is {{ team.public_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}. - {% else %} -

The {{ team.name }} Team does not have a public IRC channel, but it can be reached through our main IRC channel {{ IRCBOT_PUBLIC_CHANNEL }} on {{ IRCBOT_SERVER_HOSTNAME }}.

- {% endif %} - - {% if request.user in team.approved_members.all and team.private_irc_channel_name %} -

The {{ team.name }} Team private IRC channel is {{ team.private_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}.

- {% endif %} - -
-
- -{# Team tasks #} -
-
-

Tasks

-
-
-

The {{ team.name }} Team is responsible for the following tasks

- - - - - - - - - - {% for task in team.tasks.all %} - - - - - - {% endfor %} - -
NameDescriptionAction
{{ task.name }}{{ task.description }} - Details - {% if request.user in team.responsible_members.all %} - Edit Task - {% endif %} -
- {% if request.user in team.responsible_members.all %} - Create Task - {% endif %} -
-
- -{# Team members #} -
-
-

Members

-
-
-

The following {{ team.approved_members.count }} people {% if team.unapproved_members.count %}(and {{ team.unapproved_members.count }} pending){% endif %} are members of the {{ team.name }} Team:

- - - - - - - - - {% for teammember in team.memberships.all %} - - - - - {% endfor %} - -
- Name - - Status -
- {{ teammember.user.profile.get_public_credit_name }} {% if teammember.user == request.user %}(this is you!){% endif %} - - Team {% if teammember.responsible %}Responsible{% else %}Member{% endif %} - {% if not teammember.approved %}(pending approval){% endif %} -
- -

Your membership status: {% membershipstatus user team %}

- - {% if request.user in team.members.all %} - {% if team.irc_channel and team.irc_channel_managed and request.user.profile.nickserv_username %} - Fix IRC ACL  - {% endif %} - Leave Team - {% else %} - {% if team.needs_members %} - This team is looking for members! Join Team - {% endif %} - {% endif %} - - {% if request.user in team.responsible_members.all %} - Manage Team - {% endif %} - -
-
-
- -{# Team info categories section - only visible for team responsible #} -{% if request.user in team.responsible_members.all and team.info_categories.exists %} -
-
-

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 - -
- - Create Info Item - -
- - {% endfor %} -
-
- -{% endif %} - -{% endblock %} diff --git a/src/teams/templates/team_general.html b/src/teams/templates/team_general.html new file mode 100644 index 00000000..2c5bfc91 --- /dev/null +++ b/src/teams/templates/team_general.html @@ -0,0 +1,48 @@ +{% extends 'team_base.html' %} +{% load commonmark %} +{% load bootstrap3 %} +{% load teams_tags %} + + +{% block team_content %} + +
+
+

Description

+
+
+ {{ team.description|untrustedcommonmark }} +
+
+ +{# Team communications #} +
+
+

Communication Channels

+
+
+ {{ team.camp.title }} teams primarily use mailing lists and IRC to communicate. The {{ team.name }} team can be contacted in the following ways:

+ +
Mailing List
+ {% if team.mailing_list and request.user in team.approved_members.all %} +

The {{ team.name }} Team mailinglist is {{ team.mailing_list }}{% if team.mailing_list_archive_public %}, and the archives are publicly available{% endif %}. You should sign up for the list if you haven't already.

+ {% elif team.mailing_list and team.mailinglist_nonmember_posts %} +

The {{ team.name }} Team mailinglist is {{ team.mailing_list }}{% if team.mailing_list_archive_public %}, and the archives are publicly available{% endif %}. You do not need to be a member of the list to post to it.

+ {% else %} +

The {{ team.name }} Team does not have a public mailing list, but it can be contacted through our main email info@bornhack.dk. + {% endif %} + +

IRC Channel
+ {% if team.public_irc_channel_name %} +

The {{ team.name }} Team public IRC channel is {{ team.public_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}. + {% else %} +

The {{ team.name }} Team does not have a public IRC channel, but it can be reached through our main IRC channel {{ IRCBOT_PUBLIC_CHANNEL }} on {{ IRCBOT_SERVER_HOSTNAME }}.

+ {% endif %} + + {% if request.user in team.approved_members.all and team.private_irc_channel_name %} +

The {{ team.name }} Team private IRC channel is {{ team.private_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}.

+ {% endif %} + +
+
+{% endblock %} diff --git a/src/teams/templates/team_info_categories.html b/src/teams/templates/team_info_categories.html new file mode 100644 index 00000000..0490b787 --- /dev/null +++ b/src/teams/templates/team_info_categories.html @@ -0,0 +1,52 @@ +{% extends 'team_base.html' %} +{% load commonmark %} +{% load bootstrap3 %} +{% load teams_tags %} + + +{% block team_content %} + +{% if request.user in team.responsible_members.all and team.info_categories.exists %} +

SHOULD NOT HAPPEN !!!

+{% endif %} + +
+
+

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 + +
+ + Create Info Item + +
+ + {% endfor %} +
+
+ +{% endblock %} diff --git a/src/teams/templates/team_join.html b/src/teams/templates/team_join.html index 5b83f7e6..f231b7a3 100644 --- a/src/teams/templates/team_join.html +++ b/src/teams/templates/team_join.html @@ -1,11 +1,11 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% block title %} Join Team: {{ team.name }} | {{ block.super }} {% endblock %} -{% block content %} +{% block team_content %}

Really join the {{ team.name }} Team for {{ team.camp.title }}?

diff --git a/src/teams/templates/team_leave.html b/src/teams/templates/team_leave.html index 8cbc37e1..7e345e3d 100644 --- a/src/teams/templates/team_leave.html +++ b/src/teams/templates/team_leave.html @@ -1,11 +1,11 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% block title %} Leave Team: {{ team.name }} | {{ block.super }} {% endblock %} -{% block content %} +{% block team_content %}

Leave {{ team.name }} Team

Really leave the {{ team.name }} team?

diff --git a/src/teams/templates/team_list.html b/src/teams/templates/team_list.html index d09f8217..c8a16f5e 100644 --- a/src/teams/templates/team_list.html +++ b/src/teams/templates/team_list.html @@ -33,7 +33,9 @@ Teams | {{ block.super }} {% for team in teams %} - + {{ camp.slug}} + {{ team.slug}} + {{ team.name }} Team @@ -63,7 +65,7 @@ Teams | {{ block.super }}

- Details + Details {% if request.user in team.responsible_members.all %} Manage {% endif %} diff --git a/src/teams/templates/team_manage.html b/src/teams/templates/team_manage.html index a1635c26..c3b686f5 100644 --- a/src/teams/templates/team_manage.html +++ b/src/teams/templates/team_manage.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% load bootstrap3 %} @@ -6,7 +6,7 @@ Manage Team: {{ team.name }} | {{ block.super }} {% endblock %} -{% block content %} +{% block team_content %}

Manage {{ team.name }} Team

@@ -18,81 +18,12 @@ Manage Team: {{ team.name }} | {{ block.super }} {% buttons %} - Cancel  + Cancel  {% endbuttons %}
-
-

Manage {{ team.name }} Team Members

-
- {% if team.teammember_set.exists %} - - - - - - - - - - - - - - {% for membership in team.teammember_set.all %} - - - - - - - - - - {% endfor %} - -
- Username - - Name - - Email - - Description - - Public Credit Name - - Membership - - Action -
- {{ membership.user }} - - {{ membership.user.profile.name }} - - {{ membership.user.profile.email }} - - {{ membership.user.profile.description }} - - {{ membership.user.profile.public_credit_name|default:"N/A" }} - {% if membership.user.profile.public_credit_name and not membership.user.profile.public_credit_name_approved %}(name not approved){% endif %} - - {% if membership.approved %}member{% else %}pending{% endif %} - -
- Remove Member - {% if not membership.approved %} - Approve Member - {% endif %} -
-
- {% else %} -

No members found!

- {% endif %} -
-
{% endblock %} diff --git a/src/teams/templates/team_members.html b/src/teams/templates/team_members.html new file mode 100644 index 00000000..75024b49 --- /dev/null +++ b/src/teams/templates/team_members.html @@ -0,0 +1,84 @@ +{% extends 'team_base.html' %} +{% load commonmark %} +{% load bootstrap3 %} +{% load teams_tags %} + + +{% block team_content %} + +
+
+

Members

+
+
+

The following {{ team.approved_members.count }} people {% if team.unapproved_members.count %}(and {{ team.unapproved_members.count }} pending){% endif %} are members of the {{ team.name }} Team:

+ + + + + + {% if request.user in team.responsible_members.all %} + + {% endif %} + + + + {% for teammember in team.memberships.all %} + + + + {% if request.user in team.responsible_members.all %} + + {% endif %} + + {% empty %} +

No members found!

+ {% endfor %} + +
+ Name + + Status + + Action +
+ {{ teammember.user.profile.get_public_credit_name }} {% if teammember.user == request.user %}(this is you!){% endif %} + + Team {% if teammember.responsible %}Responsible{% else %}Member{% endif %} + {% if not teammember.approved %}(pending approval){% endif %} + +
+ + Remove Member + + {% if not teammember.approved %} + + Approve Member + + {% endif %} +
+
+ + {% if request.user.authorized %} + +

Your membership status: {% membershipstatus user team %}

+ + {% if request.user in team.members.all %} + {% if team.irc_channel and team.irc_channel_managed and request.user.profile.nickserv_username %} + Fix IRC ACL  + {% endif %} + Leave Team + {% else %} + {% if team.needs_members %} + This team is looking for members! Join Team + {% endif %} + {% endif %} + {% endif %} + +
+
+
+ +{% endblock %} diff --git a/src/teams/templates/team_tasks.html b/src/teams/templates/team_tasks.html new file mode 100644 index 00000000..3e71778b --- /dev/null +++ b/src/teams/templates/team_tasks.html @@ -0,0 +1,42 @@ +{% extends 'team_base.html' %} +{% load commonmark %} +{% load bootstrap3 %} +{% load teams_tags %} + +{% block team_content %} + +
+
+

Tasks

+
+
+

The {{ team.name }} Team is responsible for the following tasks

+ + + + + + + + + + {% for task in team.tasks.all %} + + + + + + {% endfor %} + +
NameDescriptionAction
{{ task.name }}{{ task.description }} + Details + {% if request.user in team.responsible_members.all %} + Edit Task + {% endif %} +
+ {% if request.user in team.responsible_members.all %} + Create Task + {% endif %} +
+
+{% endblock %} diff --git a/src/teams/templates/teammember_approve.html b/src/teams/templates/teammember_approve.html index 92afa84a..22ad3844 100644 --- a/src/teams/templates/teammember_approve.html +++ b/src/teams/templates/teammember_approve.html @@ -1,11 +1,11 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% block title %} Approve team member {{ teammember.user.profile.name }} for the {{ teammember.team.name }} team {% endblock %} -{% block content %} +{% block team_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.

@@ -13,6 +13,7 @@ Approve team member {{ teammember.user.profile.name }} for the {{ teammember.tea {% csrf_token %} {{ form }} - Cancel + Cancel + {% endblock %} diff --git a/src/teams/templates/teammember_remove.html b/src/teams/templates/teammember_remove.html index 775e2577..222c9083 100644 --- a/src/teams/templates/teammember_remove.html +++ b/src/teams/templates/teammember_remove.html @@ -1,11 +1,11 @@ -{% extends 'base.html' %} +{% extends 'team_base.html' %} {% load commonmark %} {% block title %} Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.name }} team {% endblock %} -{% block content %} +{% block team_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.

@@ -13,6 +13,6 @@ Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.nam {% csrf_token %} {{ form }} - Cancel + Cancel {% endblock %} diff --git a/src/teams/urls.py b/src/teams/urls.py index 330adfb9..6c5adfd5 100644 --- a/src/teams/urls.py +++ b/src/teams/urls.py @@ -2,17 +2,27 @@ from django.urls import path, include from teams.views.base import ( TeamListView, - TeamMemberRemoveView, - TeamMemberApproveView, - TeamDetailView, - TeamJoinView, - TeamLeaveView, + TeamGeneralView, TeamManageView, FixIrcAclView, ) -from teams.views.info import InfoItemUpdateView, InfoItemCreateView, InfoItemDeleteView + +from teams.views.members import ( + TeamMembersView, + TeamMemberRemoveView, + TeamMemberApproveView, + TeamJoinView, + TeamLeaveView, +) + +from teams.views.info import ( + InfoItemUpdateView, + InfoItemCreateView, + InfoItemDeleteView, +) from teams.views.tasks import ( + TeamTasksView, TaskCreateView, TaskDetailView, TaskUpdateView, @@ -26,26 +36,12 @@ urlpatterns = [ TeamListView.as_view(), name='list' ), - path( - 'members/', include([ - path( - '/remove/', - TeamMemberRemoveView.as_view(), - name='teammember_remove', - ), - path( - '/approve/', - TeamMemberApproveView.as_view(), - name='teammember_approve', - ), - ]), - ), path( '/', include([ path( '', - TeamDetailView.as_view(), - name='detail' + TeamGeneralView.as_view(), + name='general' ), path( 'join/', @@ -67,8 +63,32 @@ urlpatterns = [ FixIrcAclView.as_view(), name='fix_irc_acl', ), + path( + 'members/', include([ + path( + '', + TeamMembersView.as_view(), + name='members' + ), + path( + '/remove/', + TeamMemberRemoveView.as_view(), + name='teammember_remove', + ), + path( + '/approve/', + TeamMemberApproveView.as_view(), + name='teammember_approve', + ), + ]), + ), path( 'tasks/', include([ + path( + '', + TeamTasksView.as_view(), + name='tasks', + ), path( 'create/', TaskCreateView.as_view(), diff --git a/src/teams/views/base.py b/src/teams/views/base.py index 4ede5bd3..06c928e1 100644 --- a/src/teams/views/base.py +++ b/src/teams/views/base.py @@ -7,11 +7,8 @@ from django.contrib import messages from django.urls import reverse_lazy from django.conf import settings -from profiles.models import Profile - -from .mixins import EnsureTeamResponsibleMixin, EnsureTeamMemberResponsibleMixin +from .mixins import EnsureTeamResponsibleMixin from ..models import Team, TeamMember -from ..email import add_added_membership_email, add_removed_membership_email import logging logger = logging.getLogger("bornhack.%s" % __name__) @@ -23,14 +20,14 @@ class TeamListView(CampViewMixin, ListView): context_object_name = 'teams' -class TeamDetailView(CampViewMixin, DetailView): - template_name = "team_detail.html" +class TeamGeneralView(CampViewMixin, DetailView): + template_name = "team_general.html" context_object_name = 'team' model = Team slug_url_kwarg = 'team_slug' def get_context_data(self, **kwargs): - context = super(TeamDetailView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['IRCBOT_SERVER_HOSTNAME'] = settings.IRCBOT_SERVER_HOSTNAME context['IRCBOT_PUBLIC_CHANNEL'] = settings.IRCBOT_PUBLIC_CHANNEL return context @@ -39,102 +36,20 @@ class TeamDetailView(CampViewMixin, DetailView): class TeamManageView(CampViewMixin, EnsureTeamResponsibleMixin, UpdateView): model = Team template_name = "team_manage.html" - fields = ['description', 'needs_members', 'public_irc_channel_name', 'public_irc_channel_bot', 'public_irc_channel_managed', 'private_irc_channel_name', 'private_irc_channel_bot', 'private_irc_channel_managed'] + fields = ['description', 'needs_members', 'public_irc_channel_name', + 'public_irc_channel_bot', 'public_irc_channel_managed', + 'private_irc_channel_name', 'private_irc_channel_bot', + 'private_irc_channel_managed'] slug_url_kwarg = 'team_slug' def get_success_url(self): - return reverse_lazy('teams:detail', kwargs={'camp_slug': self.camp.slug, 'team_slug': self.get_object().slug}) + return reverse_lazy('teams:general', kwargs={'camp_slug': self.camp.slug, 'team_slug': self.get_object().slug}) def form_valid(self, form): messages.success(self.request, "Team has been saved") return super().form_valid(form) -class TeamJoinView(LoginRequiredMixin, CampViewMixin, UpdateView): - template_name = "team_join.html" - model = Team - fields = [] - slug_url_kwarg = 'team_slug' - - def get(self, request, *args, **kwargs): - if not Profile.objects.get(user=request.user).description: - messages.warning( - request, - "Please fill the description in your profile before joining a team" - ) - return redirect('teams:list', camp_slug=self.camp.slug) - - if request.user in self.get_object().members.all(): - messages.warning(request, "You are already a member of this team") - return redirect('teams:list', camp_slug=self.camp.slug) - - if not self.get_object().needs_members: - messages.warning(request, "This team does not need members right now") - return redirect('teams:list', camp_slug=self.get_object().camp.slug) - - return super().get(request, *args, **kwargs) - - def form_valid(self, form): - TeamMember.objects.create(team=self.get_object(), user=self.request.user) - messages.success(self.request, "You request to join the team %s has been registered, thank you." % self.get_object().name) - return redirect('teams:list', camp_slug=self.get_object().camp.slug) - - -class TeamLeaveView(LoginRequiredMixin, CampViewMixin, UpdateView): - template_name = "team_leave.html" - model = Team - fields = [] - slug_url_kwarg = 'team_slug' - - def get(self, request, *args, **kwargs): - if request.user not in self.get_object().members.all(): - messages.warning(request, "You are not a member of this team") - return redirect('teams:list', camp_slug=self.get_object().camp.slug) - - return super().get(request, *args, **kwargs) - - def form_valid(self, form): - TeamMember.objects.filter(team=self.get_object(), user=self.request.user).delete() - messages.success(self.request, "You are no longer a member of the team %s" % self.get_object().name) - return redirect('teams:list', camp_slug=self.get_object().camp.slug) - - - -class TeamMemberRemoveView(LoginRequiredMixin, CampViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView): - template_name = "teammember_remove.html" - model = TeamMember - fields = [] - - def form_valid(self, form): - form.instance.delete() - if add_removed_membership_email(form.instance): - messages.success(self.request, "Team member removed") - else: - messages.success(self.request, "Team member removed (unable to add email to outgoing queue).") - logger.error( - 'Unable to add removed email to outgoing queue for teammember: {}'.format(form.instance) - ) - return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=form.instance.team.slug) - - -class TeamMemberApproveView(LoginRequiredMixin, CampViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView): - template_name = "teammember_approve.html" - model = TeamMember - fields = [] - - def form_valid(self, form): - form.instance.approved = True - form.instance.save() - if add_added_membership_email(form.instance): - messages.success(self.request, "Team member approved") - else: - messages.success(self.request, "Team member removed (unable to add email to outgoing queue).") - logger.error( - 'Unable to add approved email to outgoing queue for teammember: {}'.format(form.instance) - ) - return redirect('teams:detail', camp_slug=self.camp.slug, team_slug=form.instance.team.slug) - - class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView): template_name = "fix_irc_acl.html" model = Team @@ -151,17 +66,17 @@ class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView): # check if the logged in user has an approved membership of this team if request.user not in self.get_object().approved_members.all(): messages.error(request, 'No thanks') - return redirect('teams:detail', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) + return redirect('teams:general', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) # check if we manage the channel for this team if not self.get_object().irc_channel or not self.get_object().irc_channel_managed: messages.error(request, 'IRC functionality is disabled for this team, or the team channel is not managed by the bot') - return redirect('teams:detail', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) + return redirect('teams:general', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) # check if user has a nickserv username if not request.user.profile.nickserv_username: messages.error(request, 'Please go to your profile and set your NickServ username first. Make sure the account is registered with NickServ first!') - return redirect('teams:detail', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) + return redirect('teams:general', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) return response @@ -177,7 +92,7 @@ class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView): except TeamMember.DoesNotExist: # this membership is already marked as membership.irc_channel_acl_ok=False, no need to do anything messages.error(request, 'No need, this membership is already marked as irc_channel_acl_ok=False, so the bot will fix the ACL soon') - return redirect('teams:detail', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) + return redirect('teams:general', camp_slug=self.get_object().camp.slug, team_slug=self.get_object().slug) return super().get( request, *args, **kwargs @@ -195,5 +110,5 @@ class FixIrcAclView(LoginRequiredMixin, CampViewMixin, UpdateView): membership.irc_channel_acl_ok = False membership.save() messages.success(self.request, "OK, hang on while we fix the permissions for your NickServ user '%s' for IRC channel '%s'" % (self.request.user.profile.nickserv_username, form.instance.irc_channel_name)) - return redirect('teams:detail', camp_slug=form.instance.camp.slug, team_slug=form.instance.slug) + return redirect('teams:general', camp_slug=form.instance.camp.slug, team_slug=form.instance.slug) diff --git a/src/teams/views/members.py b/src/teams/views/members.py new file mode 100644 index 00000000..35bdc78a --- /dev/null +++ b/src/teams/views/members.py @@ -0,0 +1,117 @@ +import logging + +from django.views.generic import DetailView, UpdateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib import messages +from django.shortcuts import redirect + +from ..models import Team, TeamMember +from profiles.models import Profile +from camps.mixins import CampViewMixin + +from .mixins import EnsureTeamMemberResponsibleMixin +from ..email import add_added_membership_email, add_removed_membership_email + +logger = logging.getLogger("bornhack.%s" % __name__) + + +class TeamMembersView(CampViewMixin, DetailView): + template_name = "team_members.html" + context_object_name = 'team' + model = Team + slug_url_kwarg = 'team_slug' + + +class TeamJoinView(LoginRequiredMixin, CampViewMixin, UpdateView): + template_name = "team_join.html" + model = Team + fields = [] + slug_url_kwarg = 'team_slug' + + def get(self, request, *args, **kwargs): + if not Profile.objects.get(user=request.user).description: + messages.warning( + request, + "Please fill the description in your profile before joining a team" + ) + return redirect('teams:list', camp_slug=self.camp.slug) + + if request.user in self.get_object().members.all(): + messages.warning(request, "You are already a member of this team") + return redirect('teams:list', camp_slug=self.camp.slug) + + if not self.get_object().needs_members: + messages.warning(request, "This team does not need members right now") + return redirect('teams:list', camp_slug=self.get_object().camp.slug) + + return super().get(request, *args, **kwargs) + + def form_valid(self, form): + TeamMember.objects.create(team=self.get_object(), user=self.request.user) + messages.success(self.request, "You request to join the team %s has been registered, thank you." % self.get_object().name) + return redirect('teams:list', camp_slug=self.get_object().camp.slug) + + +class TeamLeaveView(LoginRequiredMixin, CampViewMixin, UpdateView): + template_name = "team_leave.html" + model = Team + fields = [] + slug_url_kwarg = 'team_slug' + + def get(self, request, *args, **kwargs): + if request.user not in self.get_object().members.all(): + messages.warning(request, "You are not a member of this team") + return redirect('teams:list', camp_slug=self.get_object().camp.slug) + + return super().get(request, *args, **kwargs) + + def form_valid(self, form): + TeamMember.objects.filter(team=self.get_object(), user=self.request.user).delete() + messages.success(self.request, "You are no longer a member of the team %s" % self.get_object().name) + return redirect('teams:list', camp_slug=self.get_object().camp.slug) + + + +class TeamMemberRemoveView(LoginRequiredMixin, CampViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView): + template_name = "teammember_remove.html" + model = TeamMember + fields = [] + + def form_valid(self, form): + form.instance.delete() + if add_removed_membership_email(form.instance): + messages.success(self.request, "Team member removed") + else: + messages.success(self.request, "Team member removed (unable to add email to outgoing queue).") + logger.error( + 'Unable to add removed email to outgoing queue for teammember: {}'.format(form.instance) + ) + return redirect('teams:general', camp_slug=self.camp.slug, team_slug=form.instance.team.slug) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['team'] = self.get_object().team + return context + + +class TeamMemberApproveView(LoginRequiredMixin, CampViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView): + template_name = "teammember_approve.html" + model = TeamMember + fields = [] + + def form_valid(self, form): + form.instance.approved = True + form.instance.save() + if add_added_membership_email(form.instance): + messages.success(self.request, "Team member approved") + else: + messages.success(self.request, "Team member removed (unable to add email to outgoing queue).") + logger.error( + 'Unable to add approved email to outgoing queue for teammember: {}'.format(form.instance) + ) + return redirect('teams:general', camp_slug=self.camp.slug, team_slug=form.instance.team.slug) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['team'] = self.get_object().team + return context diff --git a/src/teams/views/mixins.py b/src/teams/views/mixins.py index db937891..87d8d5e0 100644 --- a/src/teams/views/mixins.py +++ b/src/teams/views/mixins.py @@ -13,7 +13,7 @@ class EnsureTeamResponsibleMixin(object): 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 redirect('teams:general', camp_slug=self.camp.slug, team_slug=self.team.slug) return super().dispatch( request, *args, **kwargs @@ -29,7 +29,7 @@ class EnsureTeamMemberResponsibleMixin(SingleObjectMixin): 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 redirect('teams:general', camp_slug=self.get_object().team.camp.slug, team_slug=self.get_object().team.slug) return super().dispatch( request, *args, **kwargs diff --git a/src/teams/views/tasks.py b/src/teams/views/tasks.py index 5e661df0..8d19f265 100644 --- a/src/teams/views/tasks.py +++ b/src/teams/views/tasks.py @@ -3,15 +3,27 @@ from django.http import HttpResponseRedirect from django.views.generic import DetailView, CreateView, UpdateView from camps.mixins import CampViewMixin -from ..models import TeamTask +from ..models import Team, TeamTask from .mixins import EnsureTeamResponsibleMixin +class TeamTasksView(CampViewMixin, DetailView): + template_name = "team_tasks.html" + context_object_name = 'team' + model = Team + slug_url_kwarg = 'team_slug' + + class TaskDetailView(CampViewMixin, DetailView): template_name = "task_detail.html" context_object_name = "task" model = TeamTask + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['team'] = self.object.team + return context + class TaskCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView): model = TeamTask @@ -54,4 +66,4 @@ class TaskUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMix return HttpResponseRedirect(task.get_absolute_url()) def get_success_url(self): - return self.get_object().get_absolute_url() \ No newline at end of file + return self.get_object().get_absolute_url() diff --git a/src/templates/base.html b/src/templates/base.html index 4766b709..e3b2c5eb 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -58,7 +58,7 @@ {% endif %} -