first commit for v0.2

This commit is contained in:
Élie Bouttier 2014-08-30 15:38:06 -07:00
parent 6ba03afc73
commit 1463854a45
143 changed files with 20775 additions and 2764 deletions

0
accounts/__init__.py Normal file
View file

7
accounts/admin.py Normal file
View file

@ -0,0 +1,7 @@
from django.contrib import admin
from accounts.models import *
admin.site.register(User)
admin.site.register(Team)

17
accounts/forms.py Normal file
View file

@ -0,0 +1,17 @@
from django.forms.models import modelform_factory
from django.forms.widgets import PasswordInput
from accounts.models import *
__all__ = [ 'UserForm', 'GroupForm', 'TeamForm' ]
UserForm = modelform_factory(User,
fields=['username', 'first_name', 'last_name',
'password', 'email', 'is_superuser'],
widgets={'password': PasswordInput})
GroupForm = modelform_factory(Group,
fields=['name'])
TeamForm = modelform_factory(Team,
fields=['name'])

View file

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
import django.core.validators
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, verbose_name='superuser status', help_text='Designates that this user has all permissions without explicitly assigning them.')),
('username', models.CharField(verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True)),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
('is_staff', models.BooleanField(default=False, verbose_name='staff status', help_text='Designates whether the user can log into this admin site.')),
('is_active', models.BooleanField(default=True, verbose_name='active', help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='groups', blank=True)),
('user_permissions', models.ManyToManyField(to='auth.Permission', verbose_name='user permissions', blank=True)),
],
options={
'ordering': ['username'],
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
('name', models.CharField(max_length=128, unique=True)),
('users', models.ManyToManyField(null=True, to=settings.AUTH_USER_MODEL, blank=True)),
],
options={
'ordering': ['name'],
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Group',
fields=[
],
options={
'ordering': ['name'],
'proxy': True,
},
bases=('auth.group',),
),
migrations.AddField(
model_name='team',
name='groups',
field=models.ManyToManyField(null=True, to='accounts.Group', blank=True),
preserve_default=True,
),
]

View file

72
accounts/models.py Normal file
View file

@ -0,0 +1,72 @@
from django.db import models
from django.db.models import Q
from django.contrib.auth.models import AbstractUser
from django.contrib import auth
from django.utils.encoding import python_2_unicode_compatible
__all__ = [ 'User', 'Group', 'Team' ]
@python_2_unicode_compatible
class User(AbstractUser):
class Meta:
ordering = [ 'username' ]
@property
def teams(self):
query = Q(groups__in=self.groups.all()) | Q(users=self)
return Team.objects.filter(query).distinct()
@property
def username_and_fullname(self):
fullname = self.fullname
if fullname:
return "%s (%s)" % (self.username, fullname)
else:
return self.username
@property
def fullname(self):
fullname = ''
if self.first_name:
fullname += self.first_name
if self.last_name:
if fullname:
fullname += ' '
fullname += self.last_name
return fullname
def __str__(self):
return self.username
class Group(auth.models.Group):
class Meta:
ordering = [ 'name' ]
proxy = True
@property
def users(self):
return User.objects.filter(groups=self)
@python_2_unicode_compatible
class Team(models.Model):
class Meta:
ordering = [ 'name' ]
name = models.CharField(max_length=128, unique=True)
# We dont want related field on User object because we use
# a special function that retrieve also team through group
users = models.ManyToManyField(User, blank=True, null=True,
related_name='+')
groups = models.ManyToManyField(Group, blank=True, null=True,
related_name='teams')
def __str__(self):
return self.name

1225
accounts/static/css/jquery-ui.css vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
/* This script is used to remove user from group,
* user from team or group from team dynamically
* with a ajax request. */
$('a[role="remove"]').on("click", function () {
var a = $(this);
var href = a.data('href');
var type = a.data('type');
a.html('removing...');
$.ajax(href)
.done(function(data, textStatus) {
a.parents('li').remove();
var counter = $('#' + type + '-counter');
var empty = $('#' + type + '-empty');
var count = parseInt(counter.html());
count--;
counter.html(count);
if (count < 0) {
// should not happen
window.location.reload();
} else if (count == 0) {
empty.removeClass('hidden');
} else {
empty.addClass('hidden');
}
})
.fail(function () {
window.location.reload();
});
});

View file

@ -0,0 +1,11 @@
/* This script set the action url of the deletion form
* and update messages. */
$('#confirm-delete').on('show.bs.modal', function(e) {
var item = $(e.relatedTarget).data('item');
if (!item) {
item = 'item';
}
$('#confirm-delete-form').attr('action', $(e.relatedTarget).data('action'));
$('#confirm-delete-title').html('Delete ' + item);
$('#confirm-delete-message').html('Are you sure to delete this ' + item + '?');
});

View file

@ -0,0 +1,13 @@
/* This script switch the visible add form when we
* change between user and group tab on team page. */
$('a[data-toggle="tab"]').on("show.bs.tab", function () {
var tab = $(this).data('tab');
var hiddentab;
if (tab == 'user') {
hiddentab = 'group';
} else {
hiddentab = 'user';
}
$('#add-' + hiddentab + '-form').addClass('hidden');
$('#add-' + tab + '-form').removeClass('hidden');
});

View file

View file

@ -0,0 +1,12 @@
from django import template
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.html import escape
register = template.Library()
@register.inclusion_tag('accounts/tags/delete_modal.html')
def delete_modal():
return {}

3
accounts/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

32
accounts/urls.py Normal file
View file

@ -0,0 +1,32 @@
from django.conf.urls import url, include
urlpatterns = [
# Profile
url(r'^profile$', 'accounts.views.profile', name='profile'),
# Users
url(r'^admin/users/$', 'accounts.views.user_list', name='list-user'),
url(r'^admin/users/add/$', 'accounts.views.user_edit', name='add-user'),
url(r'^admin/users/(?P<user>[0-9]+)/edit/$', 'accounts.views.user_edit', name='edit-user'),
url(r'^admin/users/(?P<user>[0-9]+)/delete/$', 'accounts.views.user_delete', name='delete-user'),
url(r'^admin/users/(?P<user>[0-9]+)/activate/$', 'accounts.views.user_activate', name='activate-user'),
url(r'^admin/users/(?P<user>[0-9]+)/disable/$', 'accounts.views.user_disable', name='disable-user'),
# Groups
url(r'^admin/groups/$', 'accounts.views.group_list', name='list-group'),
url(r'^admin/groups/add/$', 'accounts.views.group_edit', name='add-group'),
url(r'^admin/groups/(?P<group>[0-9]+)/$', 'accounts.views.group_details', name='show-group'),
url(r'^admin/groups/(?P<group>[0-9]+)/edit/$', 'accounts.views.group_edit', name='edit-group'),
url(r'^admin/groups/(?P<group>[0-9]+)/delete/$', 'accounts.views.group_delete', name='delete-group'),
url(r'^admin/groups/(?P<group>[0-9]+)/add-user/$', 'accounts.views.group_add_user', name='add-user-to-group'),
url(r'^admin/groups/(?P<group>[0-9]+)/remove-user/(?P<user>[0-9]+)/$', 'accounts.views.group_remove_user', name='remove-user-from-group'),
# Teams
url(r'^admin/teams/$', 'accounts.views.team_list', name='list-team'),
url(r'^admin/teams/add/$', 'accounts.views.team_edit', name='add-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/$', 'accounts.views.team_details', name='show-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/edit$', 'accounts.views.team_edit', name='edit-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/delete$', 'accounts.views.team_delete', name='delete-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/add-user/$', 'accounts.views.team_add_user', name='add-user-to-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/remove-user/(?P<user>[0-9]+)/$', 'accounts.views.team_remove_user', name='remove-user-from-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/add-group/$', 'accounts.views.team_add_group', name='add-group-to-team'),
url(r'^admin/teams/(?P<team>[0-9]+)/remove-group/(?P<group>[0-9]+)/$', 'accounts.views.team_remove_group', name='remove-group-from-team'),
]

329
accounts/views.py Normal file
View file

@ -0,0 +1,329 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.contrib import messages
from django.db.models import Q
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpResponse, JsonResponse
from permissions.decorators import project_perm_required
from accounts.models import *
from accounts.forms import *
###########
# Profile #
###########
@login_required
def profile(request):
return render(request, 'accounts/profile.html')
#########
# Users #
#########
@project_perm_required('manage_user')
def user_list(request):
return render(request, 'accounts/user_list.html', {
'users': User.objects.all(),
})
@project_perm_required('manage_user')
def user_edit(request, user=None):
if user:
user = get_object_or_404(User, id=user)
form = UserForm(request.POST or None, instance=user)
if request.method == 'POST' and form.is_valid():
form.save()
if user:
messages.success(request, 'User modified successfully.')
else:
messages.success(request, 'User added successfully.')
return redirect('list-user')
return render(request, 'accounts/user_edit.html', {
'user': user,
'form': form,
})
@project_perm_required('manage_user')
def user_activate(request, user):
user = get_object_or_404(User, id=user)
if user.is_active:
messages.info(request, 'Account already activated.')
else:
user.is_active = True
user.save()
messages.success(request, 'Account activated successfully.')
return redirect('list-user')
@project_perm_required('manage_user')
def user_disable(request, user):
user = get_object_or_404(User, id=user)
if user.is_active:
user.is_active = False
user.save()
messages.success(request, 'Account disabled successfully.')
else:
messages.info(request, 'Account already disabled.')
return redirect('list-user')
@require_http_methods(["POST"])
@project_perm_required('manage_user')
def user_delete(request, user):
user = get_object_or_404(User, id=user)
user.delete()
messages.success(request, 'User deleted successfully.')
return redirect('list-user')
##########
# Groups #
##########
@project_perm_required('manage_group')
def group_list(request):
return render(request, 'accounts/group_list.html', {
'groups': Group.objects.all(),
})
@project_perm_required('manage_group')
def group_details(request, group):
return render(request, 'accounts/group_details.html', {
'group': get_object_or_404(Group, id=group),
})
@project_perm_required('manage_group')
def group_edit(request, group=None):
if group:
group = get_object_or_404(Group, id=group)
form = GroupForm(request.POST or None, instance=group)
if request.method == 'POST' and form.is_valid():
formgroup = form.save()
if group:
messages.success(request, 'Group modified successfully.')
else:
messages.success(request, 'Group added successfully.')
return redirect('show-group', formgroup.id)
return render(request, 'accounts/group_edit.html', {
'group': group,
'form': form,
})
@require_http_methods(["POST"])
@project_perm_required('manage_group')
def group_delete(request, group):
group = get_object_or_404(Group, id=group)
group.delete()
messages.success(request, 'Group deleted successfully.')
return redirect('list-group')
@project_perm_required('manage_group')
def group_add_user(request, group):
group = get_object_or_404(Group, id=group)
if request.method == 'POST':
user = request.POST.get('user')
if user:
try:
user = User.objects.get(username=user)
except ObjectDoesNotExist:
messages.error(request, 'User not found.')
else:
if group.users.filter(id=user.id).exists():
messages.info(request, 'User already in group.')
else:
user.groups.add(group)
user.save()
messages.success(request, 'User added to group successfully.')
else:
messages.error(request, 'User not found.')
return redirect('show-group', group.id)
else:
term = request.GET.get('term')
if not term:
return Http404()
query = Q(username__icontains=term) \
| Q(first_name__icontains=term) \
| Q(last_name__icontains=term)
users = User.objects.exclude(groups=group).filter(query)[:10]
response = []
for user in users:
response += [ {
'label': user.username_and_fullname,
'value': user.username,
}]
return JsonResponse(response, safe=False)
@project_perm_required('manage_group')
def group_remove_user(request, group, user):
group = get_object_or_404(Group, id=group)
user = get_object_or_404(User, id=user)
user.groups.remove(group)
user.save()
return HttpResponse()
#########
# Teams #
#########
@project_perm_required('manage_team')
def team_list(request):
return render(request, 'accounts/team_list.html', {
'teams': Team.objects.all(),
})
@project_perm_required('manage_team')
def team_details(request, team):
tab = request.session.pop('team-tab', 'user')
return render(request, 'accounts/team_details.html', {
'team': get_object_or_404(Team, pk=team),
'tab': tab,
})
@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.id)
c = {
'team': team,
'form': form,
}
return render(request, 'accounts/team_edit.html', c)
@require_http_methods(["POST"])
@project_perm_required('manage_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')
@project_perm_required('manage_team')
def team_add_user(request, team):
team = get_object_or_404(Team, id=team)
if request.method == 'POST':
user = request.POST.get('user')
if user:
try:
user = User.objects.get(username=user)
except ObjectDoesNotExist:
messages.error(request, 'User not found.')
else:
if team.users.filter(id=user.id).exists():
messages.info(request, 'User already in team.')
else:
team.users.add(user)
team.save()
messages.success(request, 'User added to team successfully.')
else:
messages.error(request, 'User not found.')
request.session['team-tab'] = 'user'
return redirect('show-team', team.id)
else:
term = request.GET.get('term')
if not term:
return Http404()
query = Q(username__icontains=term) \
| Q(first_name__icontains=term) \
| Q(last_name__icontains=term)
users = User.objects \
.exclude(groups__in=team.groups.all()) \
.exclude(id__in=team.users.values('id')) \
.filter(query)[:10]
response = []
for user in users:
response += [ {
'label': user.username_and_fullname,
'value': user.username,
}]
return JsonResponse(response, safe=False)
@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)
team.users.remove(user)
team.save()
return HttpResponse()
@project_perm_required('manage_team')
def team_add_group(request, team):
team = get_object_or_404(Team, id=team)
if request.method == 'POST':
group = request.POST.get('group')
if group:
try:
group = Group.objects.get(name=group)
except ObjectDoesNotExist:
messages.error(request, 'Group not found.')
else:
if team.groups.filter(id=group.id).exists():
messages.info(request, 'Group already in team.')
else:
team.groups.add(group)
team.save()
messages.success(request, 'Group added to team successfully.')
else:
messages.error(request, 'Group not found.')
request.session['team-tab'] = 'group'
return redirect('show-team', team.id)
else:
term = request.GET.get('term')
if not term:
return Http404()
groups = Group.objects \
.exclude(id__in=team.groups.values('id')) \
.filter(name__icontains=term)[:10]
response = []
for group in groups:
response += [ {
'label': group.name,
'value': group.name,
}]
return JsonResponse(response, safe=False)
@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)
team.groups.remove(group)
team.save()
return HttpResponse()