Merge pull request #257 from bornhack/feature/team_refactor
Team site refactor
This commit is contained in:
commit
2ad0568f64
|
@ -62,7 +62,7 @@ a, a:active, a:focus {
|
|||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.nav li a {
|
||||
#top-navbar > .nav li a {
|
||||
padding: 30px 7px;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,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
|
||||
|
@ -216,6 +216,7 @@ class Team(CampRelatedModel):
|
|||
|
||||
|
||||
class TeamMember(CampRelatedModel):
|
||||
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
on_delete=models.PROTECT,
|
||||
|
|
|
@ -13,6 +13,6 @@ Fix IRC permissions for NickServ user {{ request.user.profile.nickserv_username
|
|||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<button class="btn btn-success" type="submit"><i class="fas fa-check"></i> Yes Please</button>
|
||||
<a href="{% url 'teams:detail' camp_slug=team.camp.slug team_slug=team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
<a href="{% url 'teams:general' camp_slug=team.camp.slug team_slug=team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
|
||||
{% block title %}
|
||||
{{ task.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block team_content %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h4>Task: {{ task.name }} ({% if not task.completed %}Not {% endif %}Completed)</h4></div>
|
||||
<div class="panel-body">
|
||||
|
|
|
@ -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 %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>
|
||||
|
@ -30,7 +30,7 @@ for {{ team.name }} Team
|
|||
<button type="submit" class="btn btn-primary">{% if form.instance.id %}Save{% else %}Create{% endif %}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer"><i>This task belongs to the <a href="{% url 'teams:detail' team_slug=team.slug camp_slug=team.camp.slug %}">{{ team.name }} Team</a></i></div>
|
||||
<div class="panel-footer"><i>This task belongs to the <a href="{% url 'teams:general' team_slug=team.slug camp_slug=team.camp.slug %}">{{ team.name }} Team</a></i></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
77
src/teams/templates/team_base.html
Normal file
77
src/teams/templates/team_base.html
Normal file
|
@ -0,0 +1,77 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
{% block title %}
|
||||
Team: {{ team.name }} | {{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="page-header">
|
||||
<h1>{{ team.name }} Team</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li {% if view.active_menu == "general" %}class="active"{% endif %}>
|
||||
<a href="{% url "teams:general" camp_slug=team.camp.slug team_slug=team.slug %}">
|
||||
General
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li {% if view.active_menu == "members" %}class="active"{% endif %}>
|
||||
<a href="{% url "teams:members" camp_slug=team.camp.slug team_slug=team.slug %}">
|
||||
Members
|
||||
{% if request.user in team.responsible_members.all and team.unapproved_members %}
|
||||
<span class="label label-danger">Pending</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li {% if view.active_menu == "tasks" %}class="active"{% endif %}>
|
||||
<a href="{% url "teams:tasks" camp_slug=team.camp.slug team_slug=team.slug %}">
|
||||
Tasks
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<li {% if view.active_menu == "info_categories" %}class="active"{% endif %}>
|
||||
<a href="{% url "teams:info_categories" camp_slug=team.camp.slug team_slug=team.slug %}">
|
||||
Info categories
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
|
||||
{% if request.user in team.members.all %}
|
||||
<p>Your membership status: <b>{% membershipstatus user team %}</b></p>
|
||||
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<a href="{% url 'teams:manage' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fas fa-cog"></i> Manage Team</a>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if team.needs_members %}
|
||||
<b>This team is looking for members!</b> <a href="{% url 'teams:join' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-xs btn-success"><i class="fas fa-plus"></i> Join Team</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-10">
|
||||
|
||||
{% block team_content %}{% endblock %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,155 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
{% block title %}
|
||||
Team: {{ team.name }} | {{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="page-header">
|
||||
<h1>{{ team.name }} Team Details</h1>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Description</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ team.description|untrustedcommonmark }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Team communications #}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Communication Channels</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ team.camp.title }} teams primarily use mailing lists and IRC to communicate. The <b>{{ team.name }} team</b> can be contacted in the following ways:</p>
|
||||
|
||||
<h5>Mailing List</h5>
|
||||
{% if team.mailing_list and request.user in team.approved_members.all %}
|
||||
<p>The {{ team.name }} Team mailinglist is <b>{{ team.mailing_list }}</b>{% 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.</p>
|
||||
{% elif team.mailing_list and team.mailinglist_nonmember_posts %}
|
||||
<p>The {{ team.name }} Team mailinglist is <b>{{ team.mailing_list }}</b>{% 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.</p>
|
||||
{% else %}
|
||||
<p>The {{ team.name }} Team does not have a public mailing list, but it can be contacted through our main email <a href="mailto:info@bornhack.dk">info@bornhack.dk</a>.
|
||||
{% endif %}
|
||||
|
||||
<h5>IRC Channel</h5>
|
||||
{% if team.public_irc_channel_name %}
|
||||
<p>The {{ team.name }} Team public IRC channel is <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ team.public_irc_channel_name }}">{{ team.public_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.
|
||||
{% else %}
|
||||
<p>The {{ team.name }} Team does not have a public IRC channel, but it can be reached through our main IRC channel <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ IRCBOT_PUBLIC_CHANNEL }}">{{ IRCBOT_PUBLIC_CHANNEL }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user in team.approved_members.all and team.private_irc_channel_name %}
|
||||
<p>The {{ team.name }} Team private IRC channel is <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ team.private_irc_channel_name }}">{{ team.private_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'includes/team_tasks.html' %}
|
||||
|
||||
{# Team members #}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Members</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>The following <b>{{ team.approved_members.count }}</b> people {% if team.unapproved_members.count %}(and {{ team.unapproved_members.count }} pending){% endif %} are members of the <b>{{ team.name }} Team</b>:</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for teammember in team.memberships.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ teammember.user.profile.get_public_credit_name }} {% if teammember.user == request.user %}(this is you!){% endif %}
|
||||
</td>
|
||||
<td>
|
||||
Team {% if teammember.responsible %}Responsible{% else %}Member{% endif %}
|
||||
{% if not teammember.approved %}(pending approval){% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>Your membership status: <b>{% membershipstatus user team %}</b></p>
|
||||
|
||||
{% if request.user in team.members.all %}
|
||||
{% if team.irc_channel and team.irc_channel_managed and request.user.profile.nickserv_username %}
|
||||
<a href="{% url 'teams:fix_irc_acl' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fas fa-wrench"></i> Fix IRC ACL</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'teams:leave' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Leave Team</a>
|
||||
{% else %}
|
||||
{% if team.needs_members %}
|
||||
<b>This team is looking for members!</b> <a href="{% url 'teams:join' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-xs btn-success"><i class="fas fa-plus"></i> Join Team</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<a href="{% url 'teams:manage' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fas fa-cog"></i> Manage Team</a>
|
||||
{% endif %}
|
||||
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Team info categories section - only visible for team responsible #}
|
||||
{% if request.user in team.responsible_members.all and team.info_categories.exists %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Info Categories</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
{% for info_category in team.info_categories.all %}
|
||||
|
||||
<h4>{{ info_category.headline }}</h4>
|
||||
<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 category_anchor=info_category.anchor item_anchor=item.anchor %}"
|
||||
class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="{% url 'teams:info_item_create' camp_slug=camp.slug team_slug=team.slug category_anchor=info_category.anchor %}" class="btn btn-primary"><i class="fas fa-plus"></i> Create Info Item</a>
|
||||
|
||||
<hr />
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
48
src/teams/templates/team_general.html
Normal file
48
src/teams/templates/team_general.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
|
||||
{% block team_content %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Description</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ team.description|untrustedcommonmark }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Team communications #}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Communication Channels</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ team.camp.title }} teams primarily use mailing lists and IRC to communicate. The <b>{{ team.name }} team</b> can be contacted in the following ways:</p>
|
||||
|
||||
<h5>Mailing List</h5>
|
||||
{% if team.mailing_list and request.user in team.approved_members.all %}
|
||||
<p>The {{ team.name }} Team mailinglist is <b>{{ team.mailing_list }}</b>{% 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.</p>
|
||||
{% elif team.mailing_list and team.mailinglist_nonmember_posts %}
|
||||
<p>The {{ team.name }} Team mailinglist is <b>{{ team.mailing_list }}</b>{% 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.</p>
|
||||
{% else %}
|
||||
<p>The {{ team.name }} Team does not have a public mailing list, but it can be contacted through our main email <a href="mailto:info@bornhack.dk">info@bornhack.dk</a>.
|
||||
{% endif %}
|
||||
|
||||
<h5>IRC Channel</h5>
|
||||
{% if team.public_irc_channel_name %}
|
||||
<p>The {{ team.name }} Team public IRC channel is <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ team.public_irc_channel_name }}">{{ team.public_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.
|
||||
{% else %}
|
||||
<p>The {{ team.name }} Team does not have a public IRC channel, but it can be reached through our main IRC channel <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ IRCBOT_PUBLIC_CHANNEL }}">{{ IRCBOT_PUBLIC_CHANNEL }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user in team.approved_members.all and team.private_irc_channel_name %}
|
||||
<p>The {{ team.name }} Team private IRC channel is <a href="irc://{{ IRCBOT_SERVER_HOSTNAME }}/{{ team.private_irc_channel_name }}">{{ team.private_irc_channel_name }} on {{ IRCBOT_SERVER_HOSTNAME }}</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
48
src/teams/templates/team_info_categories.html
Normal file
48
src/teams/templates/team_info_categories.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
|
||||
{% block team_content %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Info Categories</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
{% for info_category in team.info_categories.all %}
|
||||
|
||||
<h4>{{ info_category.headline }}</h4>
|
||||
<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 category_anchor=info_category.anchor item_anchor=item.anchor %}"
|
||||
class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a href="{% url 'teams:info_item_create' camp_slug=camp.slug team_slug=team.slug category_anchor=info_category.anchor %}" class="btn btn-primary"><i class="fas fa-plus"></i> Create Info Item</a>
|
||||
|
||||
<hr />
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -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 %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>
|
|
@ -1,26 +1,28 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}
|
||||
{% if object %}
|
||||
Editing "{{ object.headline }}"
|
||||
in "{{ form.instance.category.headline }}"
|
||||
{% else %}
|
||||
Create Info item
|
||||
in "{{ category.headline }}"
|
||||
{% endif %}
|
||||
in "{{ form.instance.category.headline }}"
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block team_content %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>
|
||||
{% if object %}
|
||||
Editing "{{ object.headline }}"
|
||||
in "{{ object.category.headline }}"
|
||||
{% else %}
|
||||
Create Info Item
|
||||
in "{{ category.headline }}"
|
||||
{% endif %}
|
||||
in "{{ object.category.headline }}"
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
@ -36,6 +38,6 @@ in "{{ form.instance.category.headline }}"
|
|||
{% endif %}
|
||||
</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 class="panel-footer"><i>This info item belongs to the <a href="{% url 'teams:general' team_slug=team.slug camp_slug=team.camp.slug %}">{{ team.name }} Team</a></i></div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -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 %}
|
||||
|
||||
<p class="lead">Really join the <b>{{ team.name }}</b> Team for <b>{{ team.camp.title }}</b>?</p>
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<h3>Leave {{ team.name }} Team</h3>
|
||||
<p class="lead">Really leave the <b>{{ team.name }}</b> team?<p>
|
||||
|
|
|
@ -33,7 +33,7 @@ Teams | {{ block.super }}
|
|||
{% for team in teams %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'teams:detail' camp_slug=camp.slug team_slug=team.slug %}">
|
||||
<a href="{% url 'teams:general' camp_slug=camp.slug team_slug=team.slug %}">
|
||||
{{ team.name }} Team
|
||||
</a>
|
||||
</td>
|
||||
|
@ -63,7 +63,7 @@ Teams | {{ block.super }}
|
|||
|
||||
<td>
|
||||
<div class="btn-group-vertical">
|
||||
<a class="btn btn-primary" href="{% url 'teams:detail' camp_slug=camp.slug team_slug=team.slug %}"><i class="fas fa-search"></i> Details</a>
|
||||
<a class="btn btn-primary" href="{% url 'teams:general' camp_slug=camp.slug team_slug=team.slug %}"><i class="fas fa-search"></i> Details</a>
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<a href="{% url 'teams:manage' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fas fa-cog"></i> Manage</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -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 %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h4>Manage {{ team.name }} Team</h4></div>
|
||||
<div class="panel-body" style="margin-left: 1em; margin-right: 1em;">
|
||||
|
@ -18,85 +18,11 @@ Manage Team: {{ team.name }} | {{ block.super }}
|
|||
|
||||
{% buttons %}
|
||||
<button class="btn btn-success pull-right" type="submit"><i class="fas fa-check"></i> Save Team</button>
|
||||
<a class="btn btn-primary pull-right" href="{% url 'teams:detail' team_slug=team.slug camp_slug=camp.slug %}"><i class="fas fa-times"></i> Cancel</a>
|
||||
<a class="btn btn-primary pull-right" href="{% url 'teams:general' team_slug=team.slug camp_slug=camp.slug %}"><i class="fas fa-times"></i> Cancel</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h4>Manage {{ team.name }} Team Members</h4></div>
|
||||
<div class="panel-body" style="margin-left: 1em; margin-right: 1em;">
|
||||
{% if team.teammember_set.exists %}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Username
|
||||
</th>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
Email
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
<th>
|
||||
Public Credit Name
|
||||
</th>
|
||||
<th>
|
||||
Membership
|
||||
</th>
|
||||
<th>
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for membership in team.teammember_set.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ membership.user }}
|
||||
</td>
|
||||
<td>
|
||||
{{ membership.user.profile.name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ membership.user.profile.email }}
|
||||
</td>
|
||||
<td>
|
||||
{{ membership.user.profile.description }}
|
||||
</td>
|
||||
<td>
|
||||
{{ 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 %}<span class="text-warning">(name not approved)</span>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if membership.approved %}member{% else %}pending{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group-vertical">
|
||||
<a class="btn btn-danger" href="{% url 'teams:teammember_remove' camp_slug=camp.slug pk=membership.id %}"><i class="fas fa-trash-o"></i> Remove Member</a>
|
||||
{% if not membership.approved %}
|
||||
<a class="btn btn-success" href="{% url 'teams:teammember_approve' camp_slug=camp.slug pk=membership.id %}"><i class="fas fa-check"></i> Approve Member</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No members found!</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'includes/team_tasks.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
86
src/teams/templates/team_members.html
Normal file
86
src/teams/templates/team_members.html
Normal file
|
@ -0,0 +1,86 @@
|
|||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
|
||||
{% block team_content %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Members</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>The following <b>{{ team.approved_members.count }}</b> people {% if team.unapproved_members.count %}(and {{ team.unapproved_members.count }} pending){% endif %} are members of the <b>{{ team.name }} Team</b>:</p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
Status
|
||||
</th>
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<th>
|
||||
Action
|
||||
</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in team.memberships.all %}
|
||||
{% if member.approved or not member.approved and request.user in team.responsible_members.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ member.user.profile.get_public_credit_name }} {% if member.user == request.user %}(this is you!){% endif %}
|
||||
</td>
|
||||
<td>
|
||||
Team {% if member.responsible %}Responsible{% else %}Member{% endif %}
|
||||
{% if not member.approved %}(pending approval){% endif %}
|
||||
</td>
|
||||
{% if request.user in team.responsible_members.all %}
|
||||
<td>
|
||||
<div class="btn-group-vertical">
|
||||
<a class="btn btn-danger"
|
||||
href="{% url 'teams:member_remove' camp_slug=camp.slug team_slug=team.slug pk=member.id %}">
|
||||
<i class="fas fa-trash"></i> Remove
|
||||
</a>
|
||||
{% if not member.approved %}
|
||||
<a class="btn btn-success"
|
||||
href="{% url 'teams:member_approve' camp_slug=camp.slug team_slug=team.slug pk=member.id %}">
|
||||
<i class="fas fa-check"></i> Approve
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<p>No members found!</p>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if request.user.authorized %}
|
||||
|
||||
<p>Your membership status: <b>{% membershipstatus user team %}</b></p>
|
||||
|
||||
{% if request.user in team.members.all %}
|
||||
{% if team.irc_channel and team.irc_channel_managed and request.user.profile.nickserv_username %}
|
||||
<a href="{% url 'teams:fix_irc_acl' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-primary"><i class="fas fa-wrench"></i> Fix IRC ACL</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'teams:leave' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-danger"><i class="fas fa-times"></i> Leave Team</a>
|
||||
{% else %}
|
||||
{% if team.needs_members %}
|
||||
<b>This team is looking for members!</b> <a href="{% url 'teams:join' camp_slug=camp.slug team_slug=team.slug %}" class="btn btn-xs btn-success"><i class="fas fa-plus"></i> Join Team</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,3 +1,10 @@
|
|||
{% extends 'team_base.html' %}
|
||||
{% load commonmark %}
|
||||
{% load bootstrap3 %}
|
||||
{% load teams_tags %}
|
||||
|
||||
{% block team_content %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>Tasks</h4>
|
||||
|
@ -10,7 +17,7 @@
|
|||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>When</th>
|
||||
<th>Completed</th>
|
||||
<th>Completed?</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -41,4 +48,4 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -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 %}
|
||||
|
||||
<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>
|
||||
|
@ -13,6 +13,7 @@ Approve team member {{ teammember.user.profile.name }} for the {{ teammember.tea
|
|||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<button class="btn btn-success" type="submit"><i class="fas fa-check"></i> Add teammember</button>
|
||||
<a href="{% url 'teams:detail' camp_slug=teammember.team.camp.slug team_slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
<a href="{% url 'teams:general' camp_slug=teammember.team.camp.slug team_slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
<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>
|
||||
|
@ -13,6 +13,6 @@ Remove member {{ teammember.user.profile.name }} from the {{ teammember.team.nam
|
|||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<button class="btn btn-danger" type="submit"><i class="fas fa-trash-o"></i> Remove teammember</button>
|
||||
<a href="{% url 'teams:detail' camp_slug=teammember.team.camp.slug team_slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
<a href="{% url 'teams:general' camp_slug=teammember.team.camp.slug team_slug=teammember.team.slug %}" class="btn btn-default" type="submit"><i class="fas fa-times"></i> Cancel</a>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,17 +2,28 @@ 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 (
|
||||
InfoCategoriesListView,
|
||||
InfoItemUpdateView,
|
||||
InfoItemCreateView,
|
||||
InfoItemDeleteView,
|
||||
)
|
||||
|
||||
from teams.views.tasks import (
|
||||
TeamTasksView,
|
||||
TaskCreateView,
|
||||
TaskDetailView,
|
||||
TaskUpdateView,
|
||||
|
@ -26,26 +37,12 @@ urlpatterns = [
|
|||
TeamListView.as_view(),
|
||||
name='list'
|
||||
),
|
||||
path(
|
||||
'members/', include([
|
||||
path(
|
||||
'<int:pk>/remove/',
|
||||
TeamMemberRemoveView.as_view(),
|
||||
name='teammember_remove',
|
||||
),
|
||||
path(
|
||||
'<int:pk>/approve/',
|
||||
TeamMemberApproveView.as_view(),
|
||||
name='teammember_approve',
|
||||
),
|
||||
]),
|
||||
),
|
||||
path(
|
||||
'<slug:team_slug>/', include([
|
||||
path(
|
||||
'',
|
||||
TeamDetailView.as_view(),
|
||||
name='detail'
|
||||
TeamGeneralView.as_view(),
|
||||
name='general'
|
||||
),
|
||||
path(
|
||||
'join/',
|
||||
|
@ -67,8 +64,32 @@ urlpatterns = [
|
|||
FixIrcAclView.as_view(),
|
||||
name='fix_irc_acl',
|
||||
),
|
||||
path(
|
||||
'members/', include([
|
||||
path(
|
||||
'',
|
||||
TeamMembersView.as_view(),
|
||||
name='members'
|
||||
),
|
||||
path(
|
||||
'<int:pk>/remove/',
|
||||
TeamMemberRemoveView.as_view(),
|
||||
name='member_remove',
|
||||
),
|
||||
path(
|
||||
'<int:pk>/approve/',
|
||||
TeamMemberApproveView.as_view(),
|
||||
name='member_approve',
|
||||
),
|
||||
]),
|
||||
),
|
||||
path(
|
||||
'tasks/', include([
|
||||
path(
|
||||
'',
|
||||
TeamTasksView.as_view(),
|
||||
name='tasks',
|
||||
),
|
||||
path(
|
||||
'create/',
|
||||
TaskCreateView.as_view(),
|
||||
|
@ -92,7 +113,15 @@ urlpatterns = [
|
|||
]),
|
||||
),
|
||||
path(
|
||||
'info/<slug:category_anchor>/', include([
|
||||
'info/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
InfoCategoriesListView.as_view(),
|
||||
name='info_categories'
|
||||
),
|
||||
path(
|
||||
'<slug:category_anchor>/', include([
|
||||
path(
|
||||
'create/',
|
||||
InfoItemCreateView.as_view(),
|
||||
|
@ -114,6 +143,8 @@ urlpatterns = [
|
|||
),
|
||||
])
|
||||
)
|
||||
])
|
||||
)
|
||||
]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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,15 @@ 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'
|
||||
active_menu = 'general'
|
||||
|
||||
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 +37,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 +67,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 +93,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 +111,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)
|
||||
|
||||
|
|
|
@ -1,23 +1,39 @@
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic import CreateView, UpdateView, DeleteView
|
||||
from django.views.generic import CreateView, UpdateView, DeleteView, ListView
|
||||
from reversion.views import RevisionMixin
|
||||
|
||||
from camps.mixins import CampViewMixin
|
||||
from info.models import InfoItem, InfoCategory
|
||||
from teams.views.mixins import EnsureTeamResponsibleMixin
|
||||
from ..models import Team
|
||||
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
||||
|
||||
|
||||
class InfoItemCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||
class InfoCategoriesListView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamResponsibleMixin, ListView):
|
||||
model = InfoCategory
|
||||
template_name = "team_info_categories.html"
|
||||
slug_field = 'anchor'
|
||||
active_menu = 'info_categories'
|
||||
|
||||
def get_team(self):
|
||||
return Team.objects.get(
|
||||
camp__slug=self.kwargs['camp_slug'],
|
||||
slug=self.kwargs['team_slug']
|
||||
)
|
||||
|
||||
|
||||
class InfoItemCreateView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||
model = InfoItem
|
||||
template_name = "info_item_form.html"
|
||||
template_name = "team_info_item_form.html"
|
||||
fields = ['headline', 'body', 'anchor', 'weight']
|
||||
slug_field = 'anchor'
|
||||
active_menu = 'info_categories'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['team'] = self.team
|
||||
return context
|
||||
def get_team(self):
|
||||
return Team.objects.get(
|
||||
camp__slug=self.kwargs['camp_slug'],
|
||||
slug=self.kwargs['team_slug']
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
info_item = form.save(commit=False)
|
||||
|
@ -29,18 +45,28 @@ class InfoItemCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibl
|
|||
def get_success_url(self):
|
||||
return self.team.get_absolute_url()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['category'] = InfoCategory.objects.get(
|
||||
team__camp__slug=self.kwargs['camp_slug'],
|
||||
anchor=self.kwargs['category_anchor']
|
||||
)
|
||||
return context
|
||||
|
||||
class InfoItemUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, RevisionMixin, UpdateView):
|
||||
|
||||
class InfoItemUpdateView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamResponsibleMixin, RevisionMixin, UpdateView):
|
||||
model = InfoItem
|
||||
template_name = "info_item_form.html"
|
||||
template_name = "team_info_item_form.html"
|
||||
fields = ['headline', 'body', 'anchor', 'weight']
|
||||
slug_field = 'anchor'
|
||||
slug_url_kwarg = 'item_anchor'
|
||||
active_menu = 'info_categories'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['team'] = self.team
|
||||
return context
|
||||
def get_team(self):
|
||||
return Team.objects.get(
|
||||
camp__slug=self.kwargs['camp_slug'],
|
||||
slug=self.kwargs['team_slug']
|
||||
)
|
||||
|
||||
def get_success_url(self):
|
||||
next = self.request.GET.get('next')
|
||||
|
@ -49,11 +75,18 @@ class InfoItemUpdateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibl
|
|||
return self.team.get_absolute_url()
|
||||
|
||||
|
||||
class InfoItemDeleteView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, RevisionMixin, DeleteView):
|
||||
class InfoItemDeleteView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamResponsibleMixin, RevisionMixin, DeleteView):
|
||||
model = InfoItem
|
||||
template_name = "info_item_delete_confirm.html"
|
||||
template_name = "team_info_item_delete_confirm.html"
|
||||
slug_field = 'anchor'
|
||||
slug_url_kwarg = 'item_anchor'
|
||||
active_menu = 'info_categories'
|
||||
|
||||
def get_team(self):
|
||||
return Team.objects.get(
|
||||
camp__slug=self.kwargs['camp_slug'],
|
||||
slug=self.kwargs['team_slug']
|
||||
)
|
||||
|
||||
def get_success_url(self):
|
||||
next = self.request.GET.get('next')
|
||||
|
|
111
src/teams/views/members.py
Normal file
111
src/teams/views/members.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
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, TeamViewMixin
|
||||
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'
|
||||
active_menu = 'members'
|
||||
|
||||
|
||||
class TeamJoinView(LoginRequiredMixin, CampViewMixin, UpdateView):
|
||||
template_name = "team_join.html"
|
||||
model = Team
|
||||
fields = []
|
||||
slug_url_kwarg = 'team_slug'
|
||||
active_menu = 'members'
|
||||
|
||||
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'
|
||||
active_menu = 'members'
|
||||
|
||||
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, TeamViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView):
|
||||
template_name = "teammember_remove.html"
|
||||
model = TeamMember
|
||||
fields = []
|
||||
active_menu = 'members'
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TeamMemberApproveView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamMemberResponsibleMixin, UpdateView):
|
||||
template_name = "teammember_approve.html"
|
||||
model = TeamMember
|
||||
fields = []
|
||||
active_menu = 'members'
|
||||
|
||||
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)
|
|
@ -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,8 +29,19 @@ 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
|
||||
)
|
||||
|
||||
|
||||
class TeamViewMixin:
|
||||
|
||||
def get_team(self):
|
||||
return self.get_object().team
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['team'] = self.get_team()
|
||||
return context
|
||||
|
|
|
@ -1,44 +1,74 @@
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic import DetailView, CreateView, UpdateView
|
||||
from django import forms
|
||||
|
||||
from camps.mixins import CampViewMixin
|
||||
from ..models import TeamTask
|
||||
from .mixins import EnsureTeamResponsibleMixin
|
||||
from ..models import Team, TeamTask
|
||||
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
||||
|
||||
|
||||
class TaskDetailView(CampViewMixin, DetailView):
|
||||
class TeamTasksView(CampViewMixin, DetailView):
|
||||
template_name = "team_tasks.html"
|
||||
context_object_name = 'team'
|
||||
model = Team
|
||||
slug_url_kwarg = 'team_slug'
|
||||
active_menu = 'tasks'
|
||||
|
||||
|
||||
class TaskDetailView(CampViewMixin, TeamViewMixin, DetailView):
|
||||
template_name = "task_detail.html"
|
||||
context_object_name = "task"
|
||||
model = TeamTask
|
||||
active_menu = 'tasks'
|
||||
|
||||
|
||||
class TaskCreateView(LoginRequiredMixin, CampViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||
class TaskForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TeamTask
|
||||
fields = ['name', 'description', 'when', 'completed']
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.fields['when'].widget.widgets = [
|
||||
forms.DateTimeInput(
|
||||
attrs={"placeholder": "Start"}
|
||||
),
|
||||
forms.DateTimeInput(
|
||||
attrs={"placeholder": "End"}
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class TaskCreateView(LoginRequiredMixin, CampViewMixin, TeamViewMixin, EnsureTeamResponsibleMixin, CreateView):
|
||||
model = TeamTask
|
||||
template_name = "task_form.html"
|
||||
fields = ['name', 'description', 'when', 'completed']
|
||||
|
||||
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', 'when', 'completed']
|
||||
form_class = TaskForm
|
||||
active_menu = 'tasks'
|
||||
|
||||
def get_team(self):
|
||||
return Team.objects.get(
|
||||
camp__slug=self.kwargs['camp_slug'],
|
||||
slug=self.kwargs['team_slug']
|
||||
)
|
||||
|
||||
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, TeamViewMixin, EnsureTeamResponsibleMixin, UpdateView):
|
||||
model = TeamTask
|
||||
template_name = "task_form.html"
|
||||
form_class = TaskForm
|
||||
active_menu = 'tasks'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<div id="top-navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{% url 'news:index' %}">News</a></li>
|
||||
<li><a href="{% url 'shop:index' %}">Shop</a></li>
|
||||
|
|
Loading…
Reference in a new issue