team managment

This commit is contained in:
Élie Bouttier 2014-08-14 20:10:30 -07:00
parent c54dda8dc5
commit b2b9144cbd
9 changed files with 291 additions and 9 deletions

View file

@ -27,26 +27,27 @@ def project_perm_required(perm):
return decorator return decorator
def confirmation_required(message, previous=None): def confirmation_required(message, prev=None):
def decorator(view): def decorator(view):
@wraps(view) @wraps(view)
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
if request.GET.get('force'): if request.GET.get('force'):
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
prev = previous previous = request.GET.get('prev')
if not prev: if not previous:
prev = request.GET.get('prev') if prev:
if not prev: previous = reverse(prev)
else:
# improvising # improvising
if hasattr(request, 'project'): if hasattr(request, 'project'):
prev = reverse('list-issue', previous = reverse('list-issue',
args=[request.project.name]) args=[request.project.name])
else: else:
prev = reverse('list-project') previous = reverse('list-project')
c = { c = {
'message': message, 'message': message,
'prev': prev, 'prev': previous,
'next': request.path + '?force=1', 'next': request.path + '?force=1',
} }
return render(request, 'confirm.html', c) return render(request, 'confirm.html', c)

View file

@ -13,6 +13,8 @@ EditProjectForm = modelform_factory(Project,
fields=['display_name', 'description', 'public']) fields=['display_name', 'description', 'public'])
LabelForm = modelform_factory(Label, LabelForm = modelform_factory(Label,
fields=['name', 'color', 'inverted']) fields=['name', 'color', 'inverted'])
TeamForm = modelform_factory(Team,
fields=['name', 'users', 'groups'])
class MilestoneForm(forms.ModelForm): class MilestoneForm(forms.ModelForm):

View file

@ -70,7 +70,8 @@
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ request.user.username }} <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ request.user.username }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li role="presentation"><a role="menuitem" href="{% url 'profile' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Profile</a></li> <li role="presentation"><a role="menuitem" href="{% url 'profile' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;My profile</a></li>
<li role="presentation"><a role="menuitem" href="{% url 'list-team' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Teams</a></li>
{% if perm.manage_global_permission %} {% if perm.manage_global_permission %}
<li role="presentation"><a role="menuitem" href="{% url 'list-global-permission' %}"><span class="glyphicon glyphicon-cog"></span>&nbsp;Manage permissions</a></li> <li role="presentation"><a role="menuitem" href="{% url 'list-global-permission' %}"><span class="glyphicon glyphicon-cog"></span>&nbsp;Manage permissions</a></li>
{% endif %} {% endif %}

View file

@ -0,0 +1,47 @@
{% extends 'base.html' %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h1>
Teams
<div class="pull-right">
<a href="{% url 'list-team' %}" class="btn btn-success">Go back to teams</a>
</div>
</h1>
</div>
<div class="panel-body">
<h3>Users</h3>
{% if team.users.exists %}
<ul>
{% for user in team.users.all %}
<li>{{ user }}</li>
{% endfor %}
</ul>
{% else %}
<em>There aren't any users in this team.</em>
{% endif %}
<hr />
<h3>Groups</h3>
{% if team.groups.exists %}
<ul>
{% for group in team.groups.all %}
<li>{{ group }}</li>
{% endfor %}
</ul>
{% else %}
<em>There aren't any groups in this team.</em>
{% endif %}
<hr />
<div class="row text-center">
<a href="{% url 'list-team' %}" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span> Go back to list</a>
<a href="{% url 'edit-team' team.pk %}" class="btn btn-primary"><span class="glyphicon glyphicon-edit"></span> Modify team</a>
<a href="{% url 'delete-team' team.pk %}?prev={{ request.path }}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> Delete team</a>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,38 @@
{% extends 'base.html' %}
{% load bootstrap_tags %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h1>
{% if team %}
Edit team
{% else %}
Add team
{% endif %}
<div class="pull-right">
{% if team %}
<a href="{% url 'show-team' team.pk %}" class="btn btn-warning">Go back to team</a>
{% else %}
<a href="{% url 'list-team' %}" class="btn btn-warning">Go back to teams</a>
{% endif %}
</div>
</h1>
</div>
<div class="panel-body">
<div class="col-md-offset-3 col-md-6">
<form action="" method="post" role="form">
{{ form|as_bootstrap }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,47 @@
{% extends 'base.html' %}
{% load humanize %}
{% load issue_filters %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<h1>
Teams
{% if perm.add_team %}
<div class="pull-right">
<a href="{% url 'add-team' %}" class="btn btn-success">Add team</a>
</div>
{% endif %}
</h1>
</div>
{% if teams.exists %}
<table class="table table-hover">
<tr>
<th>Name</th>
<th>Users</th>
<th>Groups</th>
</tr>
{% for team in teams %}
<tr>
<td><a href="{% url 'show-team' team.pk %}"><b>{{ team }}</b></a></td>
<td>{{ team.users|first_few:'user' }}</td>
<td>{{ team.groups|first_few:'group' }}</td>
<td class="text-center">
<a href="{% url 'edit-team' team.pk %}" class="btn btn-primary btn-xs">Edit</a>
<a href="{% url 'delete-team' team.pk %}" class="btn btn-danger btn-xs">Delete</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="panel-body">
<em>There aren't any teams quite yet.</em>
</div>
{% endif %}
{% endblock %}

View file

@ -13,3 +13,16 @@ def boolean(value):
glyph = 'remove' glyph = 'remove'
return mark_safe('<span class="glyphicon glyphicon-' return mark_safe('<span class="glyphicon glyphicon-'
+ glyph + '" style="vertical-align: middle;"></span>') + glyph + '" style="vertical-align: middle;"></span>')
@register.filter
def first_few(items, arg='item'):
if items.exists():
if items.count() < 4:
return ', '.join(map(lambda x: x.__str__(), items.all()))
else:
r = ', '.join(map(lambda x: x.__str__(), items.all()[0:3]))
plural = 's' if items.count() > 4 else ''
r += ', … (%s other%s)' % (items.count() - 3, plural)
return r
else:
return 'no ' + arg + 's'

View file

@ -38,6 +38,15 @@ urlpatterns = [
url(r'^permissions/(?P<id>[0-9]+)/edit$', 'issue.views.global_permission_edit', name='edit-global-permission'), url(r'^permissions/(?P<id>[0-9]+)/edit$', 'issue.views.global_permission_edit', name='edit-global-permission'),
url(r'^permissions/(?P<id>[0-9]+)/toggle/(?P<perm>[a-z-]+)$', 'issue.views.global_permission_toggle', name='toggle-global-permission'), url(r'^permissions/(?P<id>[0-9]+)/toggle/(?P<perm>[a-z-]+)$', 'issue.views.global_permission_toggle', name='toggle-global-permission'),
url(r'^permissions/(?P<id>[0-9]+)/delete$', 'issue.views.global_permission_delete', name='delete-global-permission'), url(r'^permissions/(?P<id>[0-9]+)/delete$', 'issue.views.global_permission_delete', name='delete-global-permission'),
url(r'^teams$', 'issue.views.team_list', name='list-team'),
url(r'^teams/add$', 'issue.views.team_edit', name='add-team'),
url(r'^teams/(?P<team>[0-9]+)$', 'issue.views.team', name='show-team'),
url(r'^teams/(?P<team>[0-9]+)/edit$', 'issue.views.team_edit', name='edit-team'),
url(r'^teams/(?P<team>[0-9]+)/users/(?P<user>[0-9]+)/add$', 'issue.views.team_add_user', name='add-user-to-team'),
url(r'^teams/(?P<team>[0-9]+)/users/(?P<user>[0-9]+)/delete$', 'issue.views.team_remove_user', name='remove-user-from-team'),
url(r'^teams/(?P<team>[0-9]+)/groups/(?P<group>[0-9]+)/add$', 'issue.views.team_add_group', name='add-group-to-team'),
url(r'^teams/(?P<team>[0-9]+)/groups/(?P<group>[0-9]+)/delete$', 'issue.views.team_remove_group', name='remove-group-from-team'),
url(r'^teams/(?P<team>[0-9]+)/delete$', 'issue.views.team_delete', name='delete-team'),
url(r'^login$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name='login'), url(r'^login$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name='login'),
url(r'^profile$', 'issue.views.profile', name='profile'), url(r'^profile$', 'issue.views.profile', name='profile'),
url(r'^logout$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'), url(r'^logout$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),

View file

@ -767,3 +767,127 @@ def milestone_delete(request, project, name):
messages.success(request, "Label deleted successfully.") messages.success(request, "Label deleted successfully.")
return redirect('list-milestone', project.name) return redirect('list-milestone', project.name)
def team_list(request):
teams = Team.objects.all()
c = {
'teams': teams,
}
return render(request, 'issue/team_list.html', c)
def team(request, team):
team = get_object_or_404(Team, pk=team)
c = {
'team': team,
}
return render(request, 'issue/team.html', c)
@project_perm_required('manage_team')
def team_edit(request, team=None):
if team:
team = get_object_or_404(Team, pk=team)
form = TeamForm(request.POST or None, instance=team)
if request.method == 'POST' and form.is_valid():
formteam = form.save()
if team:
messages.success(request, 'Team modified successfully.')
else:
messages.success(request, 'Team added successfully.')
return redirect('show-team', formteam.pk)
c = {
'team': team,
'form': form,
}
return render(request, 'issue/team_edit.html', c)
@project_perm_required('manage_team')
def team_add_user(request, team, user):
team = get_object_or_404(Team, pk=team)
user = get_object_or_404(User, pk=user)
if user in team.users.all():
messages.warning(request, 'This user already belong to this team.')
else:
team.users.add(user)
team.save()
messages.success(request, 'User added to team successfully.')
return redirect('show-team', team.pk)
@project_perm_required('manage_team')
def team_remove_user(request, team, user):
team = get_object_or_404(Team, pk=team)
user = get_object_or_404(User, pk=user)
if user in team.users.all():
team.users.remove(user)
team.save()
messages.success(request, 'User removed from team successfully.')
else:
messages.error(request, 'This user does not belong to this team.')
return redirect('show-team', team.pk)
@project_perm_required('manage_team')
def team_add_group(request, team, group):
team = get_object_or_404(Team, pk=team)
group = get_object_or_404(Group, pk=group)
if group in team.groups.all():
messages.warning(request, 'This group already belong to this team.')
else:
team.groups.add(group)
team.save()
messages.success(request, 'Group added to team successfully.')
return redirect('show-team', team.pk)
@project_perm_required('manage_team')
def team_remove_group(request, team, group):
team = get_object_or_404(Team, pk=team)
group = get_object_or_404(Group, pk=group)
if group in team.groups.all():
team.groups.remove(group)
team.save()
messages.success(request, 'Group removed from team successfully.')
else:
messages.error(request, 'This group does not belong to this team.')
return redirect('show-team', team.pk)
@project_perm_required('manage_team')
@confirmation_required('Are you sure to delete this team?', prev='list-team')
def team_delete(request, team):
team = get_object_or_404(Team, pk=team)
team.delete()
messages.success(request, 'Team deleted successfully.')
return redirect('list-team')