Rajout de la gestion admin
This commit is contained in:
parent
287716276d
commit
f56cd87358
18 changed files with 409 additions and 18 deletions
20
elections/migrations/0031_alter_election_options.py
Normal file
20
elections/migrations/0031_alter_election_options.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-07-12 16:37
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("elections", "0030_timestamps"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="election",
|
||||||
|
options={
|
||||||
|
"ordering": ["-start_date", "-end_date"],
|
||||||
|
"permissions": [("election_admin", "Peut administrer des élections")],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -10,7 +10,7 @@ from .models import Election, Option, Question
|
||||||
class AdminOnlyMixin(PermissionRequiredMixin):
|
class AdminOnlyMixin(PermissionRequiredMixin):
|
||||||
"""Restreint l'accès aux admins"""
|
"""Restreint l'accès aux admins"""
|
||||||
|
|
||||||
permission_required = "elections.is_admin"
|
permission_required = "elections.election_admin"
|
||||||
|
|
||||||
|
|
||||||
class SelectElectionMixin:
|
class SelectElectionMixin:
|
||||||
|
|
|
@ -6,12 +6,12 @@ from django.db import models, transaction
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from shared.auth import CONNECTION_METHODS
|
||||||
from shared.utils import choices_length
|
from shared.utils import choices_length
|
||||||
|
|
||||||
from .staticdefs import (
|
from .staticdefs import (
|
||||||
BALLOT_TYPE,
|
BALLOT_TYPE,
|
||||||
CAST_FUNCTIONS,
|
CAST_FUNCTIONS,
|
||||||
CONNECTION_METHODS,
|
|
||||||
QUESTION_TYPES,
|
QUESTION_TYPES,
|
||||||
TALLY_FUNCTIONS,
|
TALLY_FUNCTIONS,
|
||||||
VALIDATE_FUNCTIONS,
|
VALIDATE_FUNCTIONS,
|
||||||
|
@ -81,7 +81,7 @@ class Election(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = [
|
permissions = [
|
||||||
("is_admin", _("Peut administrer des élections")),
|
("election_admin", _("Peut administrer des élections")),
|
||||||
]
|
]
|
||||||
ordering = ["-start_date", "-end_date"]
|
ordering = ["-start_date", "-end_date"]
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,6 @@ MAIL_VOTE_DELETED = (
|
||||||
"Kadenios"
|
"Kadenios"
|
||||||
)
|
)
|
||||||
|
|
||||||
CONNECTION_METHODS = {
|
|
||||||
"pwd": _("mot de passe"),
|
|
||||||
"cas": _("CAS"),
|
|
||||||
}
|
|
||||||
|
|
||||||
QUESTION_TYPES = [
|
QUESTION_TYPES = [
|
||||||
("assentiment", _("Assentiment")),
|
("assentiment", _("Assentiment")),
|
||||||
("uninominal", _("Uninominal")),
|
("uninominal", _("Uninominal")),
|
||||||
|
|
17
faqs/migrations/0002_alter_faq_options.py
Normal file
17
faqs/migrations/0002_alter_faq_options.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-07-12 17:29
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("faqs", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="faq",
|
||||||
|
options={"permissions": [("faq_admin", "Can create faqs")]},
|
||||||
|
),
|
||||||
|
]
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
class AdminOnlyMixin(PermissionRequiredMixin):
|
class AdminOnlyMixin(PermissionRequiredMixin):
|
||||||
"""Restreint l'accès aux admins"""
|
"""Restreint l'accès aux admins"""
|
||||||
|
|
||||||
permission_required = "faqs.is_author"
|
permission_required = "faqs.faq_admin"
|
||||||
|
|
||||||
|
|
||||||
class CreatorOnlyMixin(AdminOnlyMixin):
|
class CreatorOnlyMixin(AdminOnlyMixin):
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Faq(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = [
|
permissions = [
|
||||||
("is_author", "Can create faqs"),
|
("faq_admin", "Can create faqs"),
|
||||||
]
|
]
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(fields=["anchor"], name="unique_faq_anchor")
|
models.UniqueConstraint(fields=["anchor"], name="unique_faq_anchor")
|
||||||
|
|
3
shared/auth/__init__.py
Normal file
3
shared/auth/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from .staticdefs import CONNECTION_METHODS
|
||||||
|
|
||||||
|
__all__ = [CONNECTION_METHODS]
|
|
@ -82,3 +82,38 @@ class PwdUserForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ["username", "full_name", "email"]
|
fields = ["username", "full_name", "email"]
|
||||||
|
|
||||||
|
|
||||||
|
class UserAdminForm(forms.Form):
|
||||||
|
"""
|
||||||
|
Allows to select an user and give them some admin permissions
|
||||||
|
"""
|
||||||
|
|
||||||
|
username = forms.CharField(label=_("Nom d'utilisateur"), max_length=150)
|
||||||
|
|
||||||
|
full_admin = forms.BooleanField(
|
||||||
|
label=_("Passer administrateur de Kadenios"), required=False
|
||||||
|
)
|
||||||
|
faq_admin = forms.BooleanField(
|
||||||
|
label=_("Autoriser à créer des FAQs"), required=False
|
||||||
|
)
|
||||||
|
election_admin = forms.BooleanField(
|
||||||
|
label=_("Autoriser à créer des élections"), required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
username = cleaned_data["username"]
|
||||||
|
|
||||||
|
if not username[:5] in ["cas__", "pwd__"]:
|
||||||
|
self.add_error(
|
||||||
|
"username",
|
||||||
|
_(
|
||||||
|
"Format de login invalide, seuls les comptes CAS ou avec "
|
||||||
|
"mot de passe sont modifiables"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
elif not User.objects.filter(username=username).exists():
|
||||||
|
self.add_error("username", _("Pas d'utilisateur·rice avec ce login"))
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
6
shared/auth/staticdefs.py
Normal file
6
shared/auth/staticdefs.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
CONNECTION_METHODS = {
|
||||||
|
"pwd": _("mot de passe"),
|
||||||
|
"cas": _("CAS"),
|
||||||
|
}
|
|
@ -9,4 +9,9 @@ urlpatterns = [
|
||||||
name="auth.election",
|
name="auth.election",
|
||||||
),
|
),
|
||||||
path("pwd-create", views.CreatePwdAccount.as_view(), name="auth.create-account"),
|
path("pwd-create", views.CreatePwdAccount.as_view(), name="auth.create-account"),
|
||||||
|
path("admin", views.AdminPanelView.as_view(), name="auth.admin"),
|
||||||
|
path(
|
||||||
|
"permissions", views.PermissionManagementView.as_view(), name="auth.permissions"
|
||||||
|
),
|
||||||
|
path("accounts", views.AccountListView.as_view(), name="auth.accounts"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,16 +1,34 @@
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
from django.urls import reverse_lazy
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
from django.utils.decorators import method_decorator
|
from django.contrib.auth.models import Permission
|
||||||
from django.views.generic.edit import CreateView
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
|
from django.urls import reverse, reverse_lazy
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import CreateView, FormView, ListView, TemplateView
|
||||||
|
|
||||||
from .forms import ElectionAuthForm, PwdUserForm
|
from .forms import ElectionAuthForm, PwdUserForm, UserAdminForm
|
||||||
from .utils import generate_password
|
from .utils import generate_password
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# Mixin to restrict access to staff members
|
||||||
|
# #############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class StaffMemberMixin(UserPassesTestMixin):
|
||||||
|
"""
|
||||||
|
Mixin permettant de restreindre l'accès aux membres `staff`, si la personne
|
||||||
|
n'est pas connectée, renvoie sur la page d'authentification
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return self.request.user.is_active and self.request.user.is_staff
|
||||||
|
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Election Specific Login
|
# Election Specific Login
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
@ -28,13 +46,21 @@ class ElectionLoginView(auth_views.LoginView):
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# Admin Panel
|
||||||
|
# #############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class AdminPanelView(StaffMemberMixin, TemplateView):
|
||||||
|
template_name = "auth/admin-panel.html"
|
||||||
|
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Creation of Password Accounts
|
# Creation of Password Accounts
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(staff_member_required, name="dispatch")
|
class CreatePwdAccount(StaffMemberMixin, CreateView):
|
||||||
class CreatePwdAccount(CreateView):
|
|
||||||
model = User
|
model = User
|
||||||
form_class = PwdUserForm
|
form_class = PwdUserForm
|
||||||
template_name = "auth/create-user.html"
|
template_name = "auth/create-user.html"
|
||||||
|
@ -46,3 +72,79 @@ class CreatePwdAccount(CreateView):
|
||||||
|
|
||||||
# On envoie un mail pour réinitialiser le mot de passe
|
# On envoie un mail pour réinitialiser le mot de passe
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# List of password and CAS users
|
||||||
|
# #############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class AccountListView(StaffMemberMixin, ListView):
|
||||||
|
model = User
|
||||||
|
template_name = "auth/account-list.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
qs = self.get_queryset()
|
||||||
|
|
||||||
|
ctx["cas_users"] = qs.filter(username__startswith="cas__")
|
||||||
|
ctx["pwd_users"] = qs.filter(username__startswith="pwd__")
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# Permission management
|
||||||
|
# #############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionManagementView(StaffMemberMixin, SuccessMessageMixin, FormView):
|
||||||
|
form_class = UserAdminForm
|
||||||
|
template_name = "auth/permission-management.html"
|
||||||
|
success_message = _("Permissions modifiées avec succès !")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs.update({"username": self.request.GET.get("user", None)})
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
username = self.request.GET.get("user", None)
|
||||||
|
if username is not None:
|
||||||
|
user = User.objects.filter(username=username).first()
|
||||||
|
|
||||||
|
if user is not None:
|
||||||
|
return {
|
||||||
|
"username": username,
|
||||||
|
"full_admin": user.is_staff,
|
||||||
|
"election_admin": user.has_perm("elections.election_admin"),
|
||||||
|
"faq_admin": user.has_perm("faqs.faq_admin"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("auth.permissions") + f"?user={self.user}"
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
user = User.objects.get(username=form.cleaned_data["username"])
|
||||||
|
self.user = user.username
|
||||||
|
|
||||||
|
# Kadenios admin
|
||||||
|
user.is_staff = form.cleaned_data["full_admin"]
|
||||||
|
|
||||||
|
# Election admin
|
||||||
|
perm_election = Permission.objects.get(codename="election_admin")
|
||||||
|
if form.cleaned_data["election_admin"]:
|
||||||
|
perm_election.user_set.add(user)
|
||||||
|
else:
|
||||||
|
perm_election.user_set.remove(user)
|
||||||
|
|
||||||
|
# FAQ admin
|
||||||
|
perm_faq = Permission.objects.get(codename="faq_admin")
|
||||||
|
if form.cleaned_data["faq_admin"]:
|
||||||
|
perm_faq.user_set.add(user)
|
||||||
|
else:
|
||||||
|
perm_faq.user_set.remove(user)
|
||||||
|
|
||||||
|
user.save()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
94
shared/templates/auth/account-list.html
Normal file
94
shared/templates/auth/account-list.html
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
<script>
|
||||||
|
function initSearch(input) {
|
||||||
|
const $search = document.getElementById(input);
|
||||||
|
const $users = $search.closest('div.panel').querySelectorAll('a.panel-block') || [];
|
||||||
|
|
||||||
|
$search.addEventListener('input', () => {
|
||||||
|
const username = $search.value.toLowerCase();
|
||||||
|
|
||||||
|
$users.forEach(user => {
|
||||||
|
if (user.id.includes(username)) {
|
||||||
|
user.classList.remove('is-hidden');
|
||||||
|
} else {
|
||||||
|
user.classList.add('is-hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
initSearch('pwd_search');
|
||||||
|
initSearch('cas_search');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1 class="title">{% trans "Liste des comptes" %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
{# Password Accounts #}
|
||||||
|
<div class="column is-half">
|
||||||
|
<div class="panel">
|
||||||
|
<p class="panel-heading is-radiusless">{% trans "Comptes avec mot de passe" %}</p>
|
||||||
|
|
||||||
|
{# Search bar #}
|
||||||
|
<div class="panel-block">
|
||||||
|
<p class="control has-icons-left">
|
||||||
|
<input class="input" type="text" id="pwd_search" placeholder="{% trans "Search" %}">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# List of users #}
|
||||||
|
{% for u in pwd_users %}
|
||||||
|
<a class="panel-block" href="{% url 'auth.permissions' %}?user={{ u.username }}" id={{ u.base_username|lower }}>
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fas fa-user-cog"></i>
|
||||||
|
</span>
|
||||||
|
<span class="ml-2">{{ u.full_name }} ({{ u.base_username }})</span>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# CAS Accounts #}
|
||||||
|
<div class="column is-half">
|
||||||
|
<div class="panel">
|
||||||
|
<p class="panel-heading is-radiusless">{% trans "Comptes CAS" %}</p>
|
||||||
|
|
||||||
|
{# Search bar #}
|
||||||
|
<div class="panel-block">
|
||||||
|
<p class="control has-icons-left">
|
||||||
|
<input class="input" type="text" id="cas_search" placeholder="{% trans "Search" %}">
|
||||||
|
<span class="icon is-left">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# List of users #}
|
||||||
|
{% for u in cas_users %}
|
||||||
|
<a class="panel-block" href="{% url 'auth.permissions' %}?user={{ u.username }}" id={{ u.base_username|lower }}>
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fas fa-user-cog"></i>
|
||||||
|
</span>
|
||||||
|
<span class="ml-2">{{ u.full_name }} ({{ u.base_username }})</span>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
38
shared/templates/auth/admin-panel.html
Normal file
38
shared/templates/auth/admin-panel.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1 class="title">{% trans "Gestion de Kadenios" %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="tile is-ancestor">
|
||||||
|
<div class="tile is-parent">
|
||||||
|
<a class="tile is-child notification is-light" href="{% url 'auth.create-account' %}">
|
||||||
|
<div class="subtitle has-text-centered">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-user-plus"></i>
|
||||||
|
</span>
|
||||||
|
<span class="ml-3">{% trans "Créer un nouveau compte" %}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tile is-parent">
|
||||||
|
<a class="tile is-child notification is-light" href="{% url 'auth.accounts' %}">
|
||||||
|
<div class="subtitle has-text-centered">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-stream"></i>
|
||||||
|
</span>
|
||||||
|
<span class="ml-3">{% trans "Liste des comptes" %}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -23,6 +23,15 @@
|
||||||
<span>{% trans "Enregistrer" %}</span>
|
<span>{% trans "Enregistrer" %}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-primary" href="{% url 'auth.admin' %}">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-undo-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Retour" %}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
56
shared/templates/auth/permission-management.html
Normal file
56
shared/templates/auth/permission-management.html
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
<script>
|
||||||
|
const cas_users = {{ cas_users|safe }};
|
||||||
|
const pwd_users = {{ pwd_users|safe }};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1 class="title">{% trans "Gestion des permissions" %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="message is-primary">
|
||||||
|
<p class="message-body">
|
||||||
|
{% trans "Pour modifier un compte CAS, le nom d'utilisateur doit commencer par <code>cas__</code>, pour un compte avec mot de passe, <code>pwd__</code>." %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-two-thirds">
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% include "forms/form.html" with errors=True %}
|
||||||
|
|
||||||
|
<div class="field is-grouped is-centered">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<button class="button is-fullwidth is-outlined is-primary is-light" type="submit">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-check"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Enregistrer" %}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-primary" href="{% url 'auth.accounts' %}#{{ username }}">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-undo-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Retour" %}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -37,5 +37,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -137,6 +137,17 @@
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<div class="level is-mobile">
|
<div class="level is-mobile">
|
||||||
|
{# Paramètres de Kadenios #}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<div class="level-item has-tooltip-light has-tooltip-bottom" data-tooltip="Administration">
|
||||||
|
<a class="button is-primary" href="{% url 'auth.admin' %}">
|
||||||
|
<span class="icon is-size-3">
|
||||||
|
<i class="fas fa-cog"></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<div class="tag">
|
<div class="tag">
|
||||||
{% blocktrans with name=user.base_username connection=user.connection_method %}Connecté·e en tant que {{ name }} par {{ connection }}{% endblocktrans %}
|
{% blocktrans with name=user.base_username connection=user.connection_method %}Connecté·e en tant que {{ name }} par {{ connection }}{% endblocktrans %}
|
||||||
|
@ -208,6 +219,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{% block layout %}
|
{% block layout %}
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
|
|
Loading…
Reference in a new issue