Clean forms/views/urls related to kfetauth.Group…

…and it becomes possible to add extra forms/formsets to the create and
update group views.
This commit is contained in:
Aurélien Delobelle 2017-10-15 23:46:54 +02:00
parent 5502c6876a
commit 82582866b4
20 changed files with 353 additions and 154 deletions

View file

@ -4,13 +4,12 @@ from django.utils.translation import ugettext_lazy as _
from utils.forms import KeepUnselectableModelFormMixin from utils.forms import KeepUnselectableModelFormMixin
from .fields import GroupsField, CorePermissionsField from .fields import GroupsField, CorePermissionsField
from .models import Group from .models import Group
class GroupForm(KeepUnselectableModelFormMixin, forms.ModelForm): class GroupForm(KeepUnselectableModelFormMixin, forms.ModelForm):
permissions = CorePermissionsField(label=_("Permissions"), required=False) permissions = CorePermissionsField(label='', required=False)
keep_unselectable_fields = ['permissions'] keep_unselectable_fields = ['permissions']

View file

@ -0,0 +1,58 @@
{% extends "kfet/base_form.html" %}
{% load i18n %}
{% block title %}
{% if not form.instance.pk %}
{% trans "Création d'un groupe" %}
{% else %}
{% blocktrans with name=form.instance.name %}
Modification du groupe "{{ name }}"
{% endblocktrans %}
{% endif %}
{% endblock %}
{% block header-title %}
{% if not form.instance.pk %}
{% trans "Création d'un groupe" %}
{% else %}
{% blocktrans with name=form.instance.name %}
{{ name }}
<small>Modification du groupe</small>
{% endblocktrans %}
{% endif %}
{% endblock %}
{% block main %}
<form action="" method="post" class="group-form">
{% csrf_token %}
{# Base form #}
<div class="form-horizontal">
{% include "kfet/form_snippet.html" with form=form %}
</div>
{# Extra forms #}
{% for extra in extras %}
<h3>
{{ extra.title }}<br>
<small>{{ extra.description }}</small>
</h3>
{% for extra_form in extra.forms %}
<div class="extra-form">
{% with as_panel=extra_form.as_admin_panel %}
{% if as_panel %}
{{ as_panel }}
{% else %}
{% include "kfet/form_snippet.html" with form=extra_form %}
{% endif %}
{% endwith %}
</div>
{% endfor %}
{% endfor %}
{% include "kfet/form_submit_snippet.html" %}
</form>
{% endblock %}

View file

@ -0,0 +1,58 @@
{% extends "kfet/base_col_2.html" %}
{% load i18n %}
{% block title %}{% trans "Groupes de comptes" %}{% endblock %}
{% block header-title %}{% trans "Groupes de comptes" %}{% endblock %}
{% block fixed %}
<div class="buttons">
<a class="btn btn-primary" href="{% url 'kfet.group.create' %}">
<span class="glyphicon glyphicon-plus"></span><span>{% trans "Créer un groupe" %}</span>
</a>
</div>
{% endblock %}
{% block main %}
{% for group in groups %}
<section>
<div class="heading">
{{ group.name }}
<div class="buttons">
<a class="btn btn-default" href="{% url 'kfet.group.update' group.pk %}">
<span class="glyphicon glyphicon-cog"></span><span class="hidden-xs">{% trans "Éditer" %}</span>
</a>
</div>
</div>
<div>
<h3>Comptes</h3>
{% with users=group.user_set.all %}
{% if users %}
<div class="sub-block column-sm-2 column-md-3">
<ul>
{% for user in group.user_set.all %}
{% with kfet_user=user.profile.account_kfet %}
<li>
<a href="{{ kfet_user.get_absolute_url }}">{{ kfet_user }}</a>
</li>
{% endwith %}
{% endfor %}
</ul>
{% else %}
<div class="sub-block">
<p>
{% blocktrans %}
Aucun compte n'est associé à ce groupe. Rendez-vous sur la page
d'édition d'un compte pour l'y ajouter.
{% endblocktrans %}
</p>
</div>
{% endif %}
{% endwith %}
</div>
</section>
{% endfor %}
{% endblock %}

18
kfet/auth/urls.py Normal file
View file

@ -0,0 +1,18 @@
from django.conf.urls import include, url
from . import views
group_patterns = [
url(r'^$', views.group_index,
name='kfet.group'),
url(r'^nouveau/$', views.group_create,
name='kfet.group.create'),
url('^(?P<pk>\d+)/edition/$', views.group_update,
name='kfet.group.update'),
]
urlpatterns = [
url(r'^groupes/', include(group_patterns)),
url(r'^login/generic', views.login_generic,
name='kfet.login.generic'),
]

View file

@ -1,22 +1,21 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth import authenticate, get_user_model, login
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse
from django.db.models import Prefetch from django.db.models import Prefetch
from django.http import QueryDict from django.http import QueryDict
from django.shortcuts import redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import View from django.views.generic import View
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.views.generic.edit import CreateView, UpdateView
from .forms import GroupForm from .forms import GroupForm
from .models import GenericTeamToken, Group from .models import GenericTeamToken, Group
User = get_user_model()
class GenericLoginView(View): class GenericLoginView(View):
""" """
@ -105,37 +104,102 @@ login_generic = GenericLoginView.as_view()
@permission_required('kfetauth.view_group') @permission_required('kfetauth.view_group')
def account_group(request): def group_index(request):
user_pre = Prefetch( user_pre = Prefetch(
'user_set', 'user_set',
queryset=User.objects.select_related('profile__account_kfet'), queryset=User.objects.select_related('profile__account_kfet'),
) )
groups = ( groups = Group.objects.prefetch_related(user_pre)
Group.objects return render(request, 'kfet/group_list.html', {
.prefetch_related('permissions', user_pre)
)
return render(request, 'kfet/account_group.html', {
'groups': groups, 'groups': groups,
}) })
class BaseAccountGroupFormViewMixin: _group_formview_extras = None
model = Group
form_class = GroupForm
template_name = 'kfet/account_group_form.html'
success_url = reverse_lazy('kfet.account.group')
class AccountGroupFormViewMixin( def get_group_formview_extras():
SuccessMessageMixin, global _group_formview_extras
BaseAccountGroupFormViewMixin,
): if _group_formview_extras is None:
pass _group_formview_extras = []
# Register additional group forms below.
return [extra.copy() for extra in _group_formview_extras]
class AccountGroupCreate(AccountGroupFormViewMixin, CreateView): @permission_required('kfetauth.add_group')
success_message = 'Nouveau groupe : %(name)s' def group_create(request):
group = Group()
extras = get_group_formview_extras()
if request.method == 'POST':
form = GroupForm(request.POST, instance=group)
for extra in extras:
extra['forms'] = [
form_cls(
request.POST, request.FILES, instance=group, **form_kwargs)
for form_cls, form_kwargs in extra['form_classes']
]
extra_forms = sum((extra['forms'] for extra in extras), [])
if form.is_valid() and all(form.is_valid() for form in extra_forms):
group = form.save()
for extra_form in extra_forms:
extra_form.save()
messages.success(request, _("Nouveau groupe : {}").format(group))
return redirect('kfet.group')
else:
form = GroupForm(instance=group)
for extra in extras:
extra['forms'] = [
form_cls(instance=group, **form_kwargs)
for form_cls, form_kwargs in extra['form_classes']
]
return render(request, 'kfet/group_form.html', {
'form': form,
'extras': extras,
})
class AccountGroupUpdate(AccountGroupFormViewMixin, UpdateView): @permission_required('kfetauth.change_group')
success_message = 'Groupe modifié : %(name)s' def group_update(request, pk):
group = get_object_or_404(Group, pk=pk)
extras = get_group_formview_extras()
if request.method == 'POST':
form = GroupForm(request.POST, instance=group)
for extra in extras:
extra['forms'] = [
form_cls(
request.POST, request.FILES, instance=group, **form_kwargs)
for form_cls, form_kwargs in extra['form_classes']
]
extra_forms = sum((extra['forms'] for extra in extras), [])
if form.is_valid() and all(form.is_valid() for form in extra_forms):
group = form.save()
for extra_form in extra_forms:
extra_form.save()
messages.success(request, _("Groupe modifié : {}").format(group))
return redirect('kfet.group')
else:
form = GroupForm(instance=group)
for extra in extras:
extra['forms'] = [
form_cls(instance=group, **form_kwargs)
for form_cls, form_kwargs in extra['form_classes']
]
return render(request, 'kfet/group_form.html', {
'form': form,
'extras': extras,
})

View file

@ -13,8 +13,6 @@ from kfet.models import (
TransferGroup, Supplier) TransferGroup, Supplier)
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
from .auth.forms import UserGroupForm # noqa
# ----- # -----
# Widgets # Widgets

View file

@ -101,6 +101,11 @@ class Account(models.Model):
def __str__(self): def __str__(self):
return '%s (%s)' % (self.trigramme, self.name) return '%s (%s)' % (self.trigramme, self.name)
def get_absolute_url(self):
return reverse('kfet.account.read', kwargs={
'trigramme': self.trigramme,
})
# Propriétés pour accéder aux attributs de cofprofile et user # Propriétés pour accéder aux attributs de cofprofile et user
@property @property
def user(self): def user(self):

View file

@ -0,0 +1,52 @@
.extra-form {
margin-bottom: 15px;
}
/* Checkbox select multiple field */
.checkbox-select-multiple > ul,
.checkbox-select-multiple > ul > li > ul {
padding-left: 0;
list-style-type: none;
}
.checkbox-select-multiple label {
font-weight: normal;
}
/* Permissions field */
.permissions-field > ul {
font-weight: bold;
}
.permissions-field > ul > li {
margin-bottom: 15px;
}
.permissions-field > ul > li > ul {
display: flex;
flex-flow: row wrap;
margin-top: 10px;
padding: 5px 10px 0;
border: 1px solid #CCC;
border-radius: 3px;
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
}
.permissions-field > ul > li > ul > li {
float: left;
flex: 0 1 100%;
}
.permissions-field > ul > li > ul > li:not(:last-child) {
margin-right: 15px;
}
@media (min-width: 768px) {
.permissions-field > ul > li > ul > li { flex: 0 1 auto; }
}

View file

@ -8,6 +8,7 @@
/* Base */ /* Base */
@import url("base/misc.css"); @import url("base/misc.css");
@import url("base/buttons.css"); @import url("base/buttons.css");
@import url("base/forms.css");
/* Blocks */ /* Blocks */
@import url("base/main.css"); @import url("base/main.css");
@ -35,6 +36,11 @@
font-weight: bold; font-weight: bold;
} }
.header small {
color: #FFF;
opacity: 0.95;
}
.nopadding { .nopadding {
padding: 0 !important; padding: 0 !important;
} }
@ -296,13 +302,6 @@ thead .tooltip {
} }
/* Checkbox select multiple */
.checkbox-select-multiple label {
font-weight: normal;
margin-bottom: 0;
}
/* Statement creation */ /* Statement creation */
.statement-create-summary table { .statement-create-summary table {

View file

@ -1,4 +1,5 @@
{% extends "kfet/base_col_2.html" %} {% extends "kfet/base_col_2.html" %}
{% load i18n %}
{% block title %}Comptes{% endblock %} {% block title %}Comptes{% endblock %}
{% block header-title %}Comptes{% endblock %} {% block header-title %}Comptes{% endblock %}
@ -23,7 +24,7 @@
</div> </div>
{% if perms.kfetauth.view_group %} {% if perms.kfetauth.view_group %}
<a class="btn btn-primary" href="{% url 'kfet.account.group' %}">Permissions</a> <a class="btn btn-primary" href="{% url 'kfet.group' %}">{% trans "Permissions" %}</a>
{% endif %} {% endif %}
{% if perms.kfet.view_accountnegative %} {% if perms.kfet.view_accountnegative %}

View file

@ -1,61 +0,0 @@
{% extends "kfet/base_col_2.html" %}
{% block title %}Groupes de comptes{% endblock %}
{% block header-title %}Groupes de comptes{% endblock %}
{% block fixed %}
<div class="buttons">
<a class="btn btn-primary" href="{% url 'kfet.account.group.create' %}">
<span class="glyphicon glyphicon-plus"></span><span>Créer un groupe</span>
</a>
</div>
{% endblock %}
{% block main %}
{% for group in groups %}
<section>
<div class="heading">
{{ group.name }}
<div class="buttons">
<a class="btn btn-default" href="{% url 'kfet.account.group.update' group.pk %}">
<span class="glyphicon glyphicon-cog"></span><span class="hidden-xs">Éditer</span>
</a>
</div>
</div>
<div>
<h3>Comptes</h3>
<div class="sub-block column-sm-2 column-md-3">
<ul>
{% for user in group.user_set.all %}
<li>
<a href="{% url "kfet.account.update" user.profile.account_kfet.trigramme %}">
{{ user.profile.account_kfet }}
</a>
</li>
{% endfor %}
</ul>
</div>
<h3>Permissions</h3>
<div class="column-sm-2 column-lg-3">
{% regroup group.permissions.all by content_type as grouped_perms %}
<ul class="list-unstyled">
{% for perms_group in grouped_perms %}
<li class="unbreakable">
<b>{{ perms_group.grouper|title }}</b>
<ul>
{% for perm in perms_group.list %}
<li>{{ perm.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
</div>
</section>
{% endfor %}
{% endblock %}

View file

@ -1,10 +0,0 @@
{% extends 'kfet/base_form.html' %}
{% block title %}Permissions - Édition{% endblock %}
{% block header-title %}Modification des permissions{% endblock %}
{% block main %}
{% include "kfet/form_full_snippet.html" with authz=perms.kfet.manage_perms submit_text="Enregistrer" %}
{% endblock %}

View file

@ -0,0 +1,19 @@
{% load widget_tweaks %}
{% with widget=field.field.widget %}
{% if field|widget_type == "checkboxselectmultiple" %}
<div class="checkbox-select-multiple {{ widget.attrs.field_class }}">
{{ field }}
</div>
{% elif field|widget_type == "checkboxinput" %}
<div class="checkbox">
<label>
{{ field }} {{ field.label }}
</label>
</div>
{% else %}
{{ field|add_class:'form-control' }}
{% endif %}
{% endwith %}

View file

@ -1,26 +1,24 @@
{% load widget_tweaks %} {% load widget_tweaks %}
<div class="form-group"> <div class="form-group">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> {% if not field.label %}
<div class="col-sm-10"> {% elif field|widget_type == "checkboxinput" %}
{% if field|widget_type == "checkboxselectmultiple" %} {# label is displayed along the checkbox #}
<ul class="list-unstyled checkbox-select-multiple">
{% for choice in field %}
<li class="col-sm-6 col-lg-4">
<label for="{{ choice.id_for_label }}">
{{ choice.tag }} {{ choice.choice_label }}
</label>
</li>
{% endfor %}
</ul>
{% else %} {% else %}
{{ field|add_class:'form-control' }} <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">
{{ field.label }}
</label>
{% endif %} {% endif %}
<div class="{% if not field.label %}col-sm-12{% elif field|widget_type == "checkboxinput" %}col-sm-10 col-sm-offset-2{% else %}col-sm-10{% endif %}">
{% include "kfet/form_field_base_snippet.html" with field=field %}
{% if field.errors %} {% if field.errors %}
<span class="help-block">{{ field.errors }}</span> <span class="help-block">{{ field.errors }}</span>
{% endif %} {% endif %}
{% if field.help_text %} {% if field.help_text %}
<span class="help-block">{{ field.help_text }}</span> <span class="help-block">{{ field.help_text|safe }}</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -1,4 +1,4 @@
<form action="" method="post" class="form-horizontal"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
{% include "kfet/form_snippet.html" %} {% include "kfet/form_snippet.html" %}
{% if not authz %} {% if not authz %}

View file

@ -1,3 +1,5 @@
<div class="form-horizontal">
{% for field in form %} {% for field in form %}
{% include 'kfet/form_field_snippet.html' with field=field %} {% include 'kfet/form_field_snippet.html' with field=field %}
{% endfor %} {% endfor %}
</div>

View file

@ -1,5 +1,11 @@
{% load i18n %}
{% trans "Enregistrer" as default_value %}
<div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<div class="col-sm-6 col-sm-offset-3 text-center"> <div class="col-sm-6 col-sm-offset-3 text-center">
<input type="submit" value="{{ value }}" class="btn btn-primary btn-lg"> <input type="submit" value="{% firstof value default_value %}" class="btn btn-primary btn-lg">
</div>
</div> </div>
</div> </div>

View file

@ -12,6 +12,16 @@ class AccountTests(TestCase):
self.account = Account(trigramme='000') self.account = Account(trigramme='000')
self.account.save({'username': 'user'}) self.account.save({'username': 'user'})
def test_get_absolute_url(self):
self.assertEqual(
self.account.get_absolute_url(), '/k-fet/accounts/000')
account_space = Account(trigramme=' ')
account_space.save({'username': 'space'})
self.assertEqual(
account_space.get_absolute_url(), '/k-fet/accounts/%20%20%20')
def test_password(self): def test_password(self):
self.account.change_pwd('anna') self.account.change_pwd('anna')
self.account.save() self.account.save()

View file

@ -8,8 +8,6 @@ from kfet.decorators import teamkfet_required
urlpatterns = [ urlpatterns = [
url(r'^login/generic$', views.login_generic,
name='kfet.login.generic'),
url(r'^history$', views.history, url(r'^history$', views.history,
name='kfet.history'), name='kfet.history'),
@ -50,19 +48,6 @@ urlpatterns = [
url(r'^accounts/(?P<trigramme>.{3})/edit$', views.account_update, url(r'^accounts/(?P<trigramme>.{3})/edit$', views.account_update,
name='kfet.account.update'), name='kfet.account.update'),
# Account - Groups
url(r'^accounts/groups$', views.account_group,
name='kfet.account.group'),
url(r'^accounts/groups/new$',
permission_required('kfetauth.add_group')
(views.AccountGroupCreate.as_view()),
name='kfet.account.group.create'),
url(r'^accounts/groups/(?P<pk>\d+)/edit$',
permission_required('kfetauth.change_group')
(views.AccountGroupUpdate.as_view()),
name='kfet.account.group.update'),
url(r'^accounts/negatives$', url(r'^accounts/negatives$',
permission_required('kfet.view_accountnegative') permission_required('kfet.view_accountnegative')
(views.AccountNegativeList.as_view()), (views.AccountNegativeList.as_view()),
@ -245,6 +230,6 @@ urlpatterns = [
] ]
urlpatterns += [ urlpatterns += [
# K-Fêt Open urls url(r'^', include('kfet.auth.urls')),
url('^open/', include('kfet.open.urls')), url(r'^open/', include('kfet.open.urls')),
] ]

View file

@ -24,6 +24,8 @@ from django.utils.decorators import method_decorator
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
from .auth.forms import UserGroupForm
from kfet.config import KFetConfigForm, kfet_config from kfet.config import KFetConfigForm, kfet_config
from kfet.decorators import teamkfet_required from kfet.decorators import teamkfet_required
from kfet.models import ( from kfet.models import (
@ -33,7 +35,7 @@ from kfet.models import (
TransferGroup, Transfer, ArticleCategory) TransferGroup, Transfer, ArticleCategory)
from kfet.forms import ( from kfet.forms import (
AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm, AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm,
UserRestrictTeamForm, UserGroupForm, AccountForm, CofRestrictForm, UserRestrictTeamForm, AccountForm, CofRestrictForm,
AccountPwdForm, AccountNegativeForm, UserRestrictForm, AccountRestrictForm, AccountPwdForm, AccountNegativeForm, UserRestrictForm, AccountRestrictForm,
CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm,
CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm, CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm,
@ -50,10 +52,6 @@ import heapq
import statistics import statistics
from kfet.statistic import ScaleMixin, last_stats_manifest, tot_ventes, WeekScale from kfet.statistic import ScaleMixin, last_stats_manifest, tot_ventes, WeekScale
from .auth.views import ( # noqa
account_group, login_generic, AccountGroupCreate, AccountGroupUpdate,
)
def put_cleaned_data_in_dict(dict, form): def put_cleaned_data_in_dict(dict, form):
for field in form.cleaned_data: for field in form.cleaned_data: