Add public name customization and small style adjustments

This commit is contained in:
Guillaume Bertholon 2020-12-20 01:10:02 +01:00
parent f401c66f97
commit 0d838cafee
20 changed files with 176 additions and 162 deletions

View file

@ -81,6 +81,8 @@ AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
] ]
LOGIN_URL = "gestiojeux_auth:login"
# Use markdown extensions # Use markdown extensions
MARKDOWNX_MARKDOWN_EXTENSIONS = [ MARKDOWNX_MARKDOWN_EXTENSIONS = [
"markdown.extensions.extra", "markdown.extensions.extra",

24
gestiojeux_auth/forms.py Normal file
View file

@ -0,0 +1,24 @@
from django.forms import ModelForm, ValidationError
from django.contrib.auth.models import User
class AccountSettingsForm(ModelForm):
class Meta:
model = User
fields = ["first_name"]
labels = {"first_name": "Nom ou pseudo"}
help_texts = {
"first_name": "Ce nom sera utilisé pour toutes vos interactions publiques sur GestioJeux. Si laissé vide, votre login sera utilisé à la place."
}
def clean_first_name(self):
""" Check there is no conflict that could lead to imprersonation """
public_name = self.cleaned_data["first_name"]
public_name = public_name.strip()
if public_name == self.instance.first_name or public_name == "":
return public_name
if User.objects.filter(first_name=public_name).count() > 0:
raise ValidationError("Un autre compte utilise déjà ce nom ou ce pseudo.")
if User.objects.filter(username=public_name).count() > 0:
raise ValidationError("Ce nom est déjà le login de quelqu'un.")
return public_name

View file

@ -0,0 +1,20 @@
{% extends "small_page.html" %}
{% block "content" %}
<h1>Paramètres du compte</h1>
<p>Vous êtes connecté en tant que <tt>{{ request.user.username }}</tt></p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Enregistrer</button>
</form>
{% if request.user.password and request.user.has_usable_password %}
<hr/>
<a href="{% url "gestiojeux_auth:change_password" %}">Changer mon mot de passe</a>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends "small_page.html" %}
{% block "content" %}
<h1>Changement de mot de passe</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Changer de mot de passe</button>
</form>
{% endblock %}

View file

@ -3,7 +3,7 @@
{% block "content" %} {% block "content" %}
<h1>Connexion par mot de passe</h1> <h1>Connexion par mot de passe</h1>
<form method="post" action="{% url "gestiojeux_auth:password_login" %}?next={{ next|urlencode }}"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<button type="submit">Connexion</button> <button type="submit">Connexion</button>

View file

@ -1,6 +1,6 @@
from django.urls import include, path from django.urls import include, path
import django.contrib.auth.views as dj_auth_views import django.contrib.auth.views as dj_auth_views
from .views import LoginView, LogoutView from .views import LoginView, LogoutView, PasswordChangeView, AccountSettingsView
import django_cas_ng.views import django_cas_ng.views
app_name = "gestiojeux_auth" app_name = "gestiojeux_auth"
@ -20,6 +20,8 @@ accounts_patterns = [
path("login/", LoginView.as_view(), name="login"), path("login/", LoginView.as_view(), name="login"),
path("logout/", LogoutView.as_view(), name="logout"), path("logout/", LogoutView.as_view(), name="logout"),
path("password_login/", dj_auth_views.LoginView.as_view(), name="password_login"), path("password_login/", dj_auth_views.LoginView.as_view(), name="password_login"),
path("change_password/", PasswordChangeView.as_view(), name="change_password"),
path("account_settings/", AccountSettingsView.as_view(), name="account_settings"),
] ]
urlpatterns = [ urlpatterns = [

View file

@ -1,13 +1,18 @@
from django.views.generic import TemplateView, RedirectView from django.views.generic import TemplateView, RedirectView
from django.views.generic.edit import UpdateView
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
from django.contrib.auth import user_logged_in, user_logged_out, user_login_failed from django.contrib.auth import user_logged_in, user_logged_out, user_login_failed
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import PasswordChangeView
from django.contrib import messages from django.contrib import messages
from urllib.parse import quote as urlquote from urllib.parse import quote as urlquote
from .forms import AccountSettingsForm
class LoginView(TemplateView): class LoginView(TemplateView):
template_name = "registration/login_switch.html" template_name = "registration/login_switch.html"
@ -81,3 +86,26 @@ def on_logout(request, **kwargs):
@receiver(user_login_failed) @receiver(user_login_failed)
def on_login_failed(request, **kwargs): def on_login_failed(request, **kwargs):
messages.error(request, "Connexion échouée.") messages.error(request, "Connexion échouée.")
class PasswordChangeView(PasswordChangeView):
template_name = "registration/change_password.html"
def get_success_url(self):
messages.info(self.request, "Mot de passe mis à jour")
return reverse("gestiojeux_auth:account_settings")
class AccountSettingsView(LoginRequiredMixin, UpdateView):
template_name = "registration/account_settings.html"
form_class = AccountSettingsForm
def get_object(self):
return self.request.user
def get_success_url(self):
return self.request.get_full_path()
def form_valid(self, form):
messages.success(self.request, "Paramètres du compte mis à jour")
return super().form_valid(form)

View file

@ -4,11 +4,9 @@
<h1><i class="fa fa-bookmark" aria-hidden="true"></i> {{ category.name }}</h1> <h1><i class="fa fa-bookmark" aria-hidden="true"></i> {{ category.name }}</h1>
{% with game_list=category.game_set.all %} {% with game_list=category.game_set.all %}
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} dans cette catégorie&nbsp;: <p>Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} dans cette catégorie&nbsp;:</p>
<ul> {% for game in game_list %}
{% for game in game_list %} {% include "./partials/game_item.html" %}
<li>{% include "./partials/game_item.html" %}</li> {% endfor %}
{% endfor %}
</ul>
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}

View file

@ -3,10 +3,8 @@
{% block "content" %} {% block "content" %}
<h1>Liste des catégories</h1> <h1>Liste des catégories</h1>
Il y a {{ category_list|length }} catégorie{{ category_list|pluralize }} de jeux&nbsp;: <p>Il y a {{ category_list|length }} catégorie{{ category_list|pluralize }} de jeux&nbsp;:</p>
<ul> {% for category in category_list %}
{% for category in category_list %} {% include "./partials/category_item.html" %}
<li>{% include "./partials/category_item.html" %}</li> {% endfor %}
{% endfor %}
</ul>
{% endblock %} {% endblock %}

View file

@ -38,32 +38,34 @@
{% endif %} {% endif %}
<h2>Commentaires et propositions de variantes</h2> <h2>Commentaires et propositions de variantes</h2>
<ul>
{% for comment in game.comments.all %} {% for comment in game.comments.all %}
{% if comment == edited_comment %} {% if comment == edited_comment %}
<li id="edited_comment"> <form id="edited_comment" class="comment" method="post">
<form class="comment" method="post">
{% csrf_token %}
<textarea name="comment_text" required>{{ comment.text }}</textarea>
<button type="submit"><i class="fa fa-pencil" aria-hidden="true"></i> Modifier mon commentaire</button>
</form>
</li>
{% else %}
<li class="comment">
<div class="meta"> <div class="meta">
<span class="author">{{ comment.author }}</span> <span class="author">{% include "partials/user.html" with user=comment.author %}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}</span>
<a href="{% url "inventory:game" game.slug %}" title="Annuler la modification"><i class="fa fa-ban" aria-hidden="true"></i></a>
</div>
{% csrf_token %}
<textarea name="comment_text" required>{{ comment.text }}</textarea>
<button type="submit"><i class="fa fa-pencil" aria-hidden="true"></i> Modifier mon commentaire</button>
</form>
{% else %}
<div class="comment">
<div class="meta">
<span class="author">{% include "partials/user.html" with user=comment.author %}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}{% if comment.created_on|date:"YmdHi" != comment.modified_on|date:"YmdHi" %}, dernière modification le {{ comment.modified_on|date }} à {{ comment.modified_on|time }}{% endif %}</span> <span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}{% if comment.created_on|date:"YmdHi" != comment.modified_on|date:"YmdHi" %}, dernière modification le {{ comment.modified_on|date }} à {{ comment.modified_on|time }}{% endif %}</span>
{% if comment.author == request.user %} {% if comment.author == request.user %}
<a href="{% url "inventory:modify_game_comment" game.slug comment.id %}#edited_comment"><i class="fa fa-pencil" aria-hidden="true"></i></a> <a href="{% url "inventory:modify_game_comment" game.slug comment.id %}#edited_comment" title="Éditer mon commentaire"><i class="fa fa-pencil" aria-hidden="true"></i></a>
{% endif %} {% endif %}
</div> </div>
{{ comment.text|linebreaks }} {{ comment.text|linebreaks }}
</li> </div>
{% endif %} {% endif %}
{% empty %} {% empty %}
<li>(Aucun commentaire sur ce jeu)</li> (Aucun commentaire sur ce jeu)
{% endfor %} {% endfor %}
</ul>
{% if not edited_comment %} {% if not edited_comment %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<form class="comment" method="post" action="{% url "inventory:add_game_comment" game.slug %}"> <form class="comment" method="post" action="{% url "inventory:add_game_comment" game.slug %}">

View file

@ -3,10 +3,8 @@
{% block "content" %} {% block "content" %}
<h1>Liste des jeux</h1> <h1>Liste des jeux</h1>
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} en salle jeux&nbsp;: <p>Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} en salle jeux&nbsp;:</p>
<ul> {% for game in game_list %}
{% for game in game_list %} {% include "./partials/game_item.html" %}
<li>{% include "./partials/game_item.html" %}</li> {% endfor %}
{% endfor %}
</ul>
{% endblock %} {% endblock %}

View file

@ -12,7 +12,6 @@
<hr/> <hr/>
<div class="btn_row"> <div class="btn_row">
<a href="{% url "inventory:category_list" %}"> <a href="{% url "inventory:category_list" %}">
Liste des catégories Liste des catégories
<p class="helptext"> <p class="helptext">

View file

@ -10,9 +10,7 @@
{% if query %} {% if query %}
<hr/> <hr/>
<ul>
{% for result in page_obj.object_list %} {% for result in page_obj.object_list %}
<li>
{% if result.model_name == "game" %} {% if result.model_name == "game" %}
{% include "./partials/game_item.html" with game=result.object %} {% include "./partials/game_item.html" with game=result.object %}
{% elif result.model_name == "category" %} {% elif result.model_name == "category" %}
@ -20,11 +18,9 @@
{% elif result.model_name == "tag" %} {% elif result.model_name == "tag" %}
{% include "./partials/tag_item.html" with tag=result.object %} {% include "./partials/tag_item.html" with tag=result.object %}
{% endif %} {% endif %}
</li>
{% empty %} {% empty %}
<li>Aucun résultat trouvé</li> Aucun résultat trouvé
{% endfor %} {% endfor %}
</ul>
{% include "./partials/pagination.html" %} {% include "./partials/pagination.html" %}
{% endif %} {% endif %}

View file

@ -4,11 +4,9 @@
<h1><i class="fa fa-tag" aria-hidden="true"></i> {{ tag.name }}</h1> <h1><i class="fa fa-tag" aria-hidden="true"></i> {{ tag.name }}</h1>
{% with game_list=tag.game_set.all %} {% with game_list=tag.game_set.all %}
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} marqué{{ game_list|pluralize }} avec ce tag&nbsp;: <p>Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} marqué{{ game_list|pluralize }} avec ce tag&nbsp;:</p>
<ul> {% for game in game_list %}
{% for game in game_list %} {% include "./partials/game_item.html" %}
<li>{% include "./partials/game_item.html" %}</li> {% endfor %}
{% endfor %}
</ul>
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}

View file

@ -3,10 +3,8 @@
{% block "content" %} {% block "content" %}
<h1>Liste des tags</h1> <h1>Liste des tags</h1>
Il y a {{ tag_list|length }} tag{{ tag_list|pluralize }} dans la ludothèque&nbsp;: <p>Il y a {{ tag_list|length }} tag{{ tag_list|pluralize }} dans la ludothèque&nbsp;:</p>
<ul> {% for tag in tag_list %}
{% for tag in tag_list %} {% include "./partials/tag_item.html" %}
<li>{% include "./partials/tag_item.html" %}</li> {% endfor %}
{% endfor %}
</ul>
{% endblock %} {% endblock %}

View file

@ -22,6 +22,7 @@ form {
} }
.helptext { .helptext {
display: block;
font-size: 0.7em; font-size: 0.7em;
color: $help_text_color; color: $help_text_color;
} }
@ -120,7 +121,9 @@ form.search {
} }
form.comment { form.comment {
font-size: 0.7em; padding: 0;
border: none;
textarea { textarea {
border-radius: 10px 10px 0 0; border-radius: 10px 10px 0 0;
border-bottom: none; border-bottom: none;
@ -130,4 +133,19 @@ form.comment {
border-radius: 0 0 10px 10px; border-radius: 0 0 10px 10px;
margin: 0; margin: 0;
} }
}
form#edited_comment {
.meta {
padding: 10px;
padding-bottom: 5px;
border-radius: 10px 10px 0 0;
border: 1px solid $indexbar_bg_color_1;
border-bottom: none;
}
textarea {
border-radius: 0;
}
} }

View file

@ -55,15 +55,6 @@ footer {
padding: 10px; padding: 10px;
} }
.help_bubble {
@media (min-width: 700px) {
font-size: 0.7em;
position: relative;
bottom: 0.3ex;
left: 0.2ex;
}
}
h1 { h1 {
font-size: 1.5em; font-size: 1.5em;
font-weight: bold; font-weight: bold;
@ -107,6 +98,11 @@ hr {
margin: 30px 60px; margin: 30px 60px;
} }
ul, ol {
padding: 0;
list-style-position: inside;
}
.btn_row { .btn_row {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@ -139,49 +135,6 @@ button, .btn_row a {
.warning { @include warning_box; } .warning { @include warning_box; }
.success { @include success_box; } .success { @include success_box; }
.tooltip {
position: relative;
display: inline-block;
opacity: 0.75;
border-radius: 3px;
.tooltiptext {
visibility: hidden;
display: block;
background-color: black;
color: rgba(white, 0.80);
text-align: justify;
padding: 10px;
border-radius: 6px;
font-size: 0.8em;
width: 250px;
@media (max-width: 400px) {
width: 150px;
position: absolute;
left: -75px;
}
/* Position the tooltip text - see examples below! */
position: absolute;
left: -75px;
z-index: 1;
ul {
margin: 0;
padding-left: 15px;
color: inherit;
}
}
&:hover, &:focus {
opacity: 1;
.tooltiptext {
visibility: visible;
}
}
}
.antispam { .antispam {
unicode-bidi: bidi-override; unicode-bidi: bidi-override;
direction: rtl; direction: rtl;
@ -286,11 +239,6 @@ iframe {
} }
} }
ul {
padding: 0;
list-style-type: none;
}
a.inventory_item { a.inventory_item {
display: block; display: block;
padding: 15px; padding: 15px;
@ -326,7 +274,7 @@ a.inventory_item {
} }
} }
li.comment { .comment {
@include box(white, $indexbar_bg_color_1); @include box(white, $indexbar_bg_color_1);
margin: 1em 0; margin: 1em 0;
font-size: 0.7em; font-size: 0.7em;

View file

@ -57,6 +57,7 @@ form {
width: 100%; } width: 100%; }
.helptext { .helptext {
display: block;
font-size: 0.7em; font-size: 0.7em;
color: rgba(37, 15, 45, 0.65); } color: rgba(37, 15, 45, 0.65); }
@ -173,7 +174,8 @@ form.search {
border-radius: 0 10px 10px 0; } border-radius: 0 10px 10px 0; }
form.comment { form.comment {
font-size: 0.7em; } padding: 0;
border: none; }
form.comment textarea { form.comment textarea {
border-radius: 10px 10px 0 0; border-radius: 10px 10px 0 0;
border-bottom: none; border-bottom: none;
@ -182,6 +184,16 @@ form.comment {
border-radius: 0 0 10px 10px; border-radius: 0 0 10px 10px;
margin: 0; } margin: 0; }
form#edited_comment .meta {
padding: 10px;
padding-bottom: 5px;
border-radius: 10px 10px 0 0;
border: 1px solid rgba(107, 184, 196, 0.75);
border-bottom: none; }
form#edited_comment textarea {
border-radius: 0; }
html { html {
box-sizing: border-box; } box-sizing: border-box; }
@ -221,13 +233,6 @@ footer {
text-align: center; text-align: center;
padding: 10px; } padding: 10px; }
@media (min-width: 700px) {
.help_bubble {
font-size: 0.7em;
position: relative;
bottom: 0.3ex;
left: 0.2ex; } }
h1 { h1 {
font-size: 1.5em; font-size: 1.5em;
font-weight: bold; } font-weight: bold; }
@ -262,6 +267,10 @@ hr {
border: 1px solid #c9dbe0; border: 1px solid #c9dbe0;
margin: 30px 60px; } margin: 30px 60px; }
ul, ol {
padding: 0;
list-style-position: inside; }
.btn_row { .btn_row {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@ -319,39 +328,6 @@ button, .btn_row a {
border: 1px solid #84ff72; border: 1px solid #84ff72;
background-color: #ddffd8; } background-color: #ddffd8; }
.tooltip {
position: relative;
display: inline-block;
opacity: 0.75;
border-radius: 3px; }
.tooltip .tooltiptext {
visibility: hidden;
display: block;
background-color: black;
color: rgba(255, 255, 255, 0.8);
text-align: justify;
padding: 10px;
border-radius: 6px;
font-size: 0.8em;
width: 250px;
/* Position the tooltip text - see examples below! */
position: absolute;
left: -75px;
z-index: 1; }
@media (max-width: 400px) {
.tooltip .tooltiptext {
width: 150px;
position: absolute;
left: -75px; } }
.tooltip .tooltiptext ul {
margin: 0;
padding-left: 15px;
color: inherit; }
.tooltip:hover, .tooltip:focus {
opacity: 1; }
.tooltip:hover .tooltiptext, .tooltip:focus .tooltiptext {
visibility: visible; }
.antispam { .antispam {
unicode-bidi: bidi-override; unicode-bidi: bidi-override;
direction: rtl; } direction: rtl; }
@ -436,10 +412,6 @@ iframe {
#game_infos #details hr { #game_infos #details hr {
margin: 1ex; } margin: 1ex; }
ul {
padding: 0;
list-style-type: none; }
a.inventory_item { a.inventory_item {
display: block; display: block;
padding: 15px; padding: 15px;
@ -465,16 +437,16 @@ a.inventory_item {
background-color: white; background-color: white;
box-shadow: 0 0 1.5px 1px #6bb8c4; } box-shadow: 0 0 1.5px 1px #6bb8c4; }
li.comment { .comment {
border-radius: 10px; border-radius: 10px;
padding: 10px; padding: 10px;
border: 1px solid rgba(107, 184, 196, 0.75); border: 1px solid rgba(107, 184, 196, 0.75);
background-color: white; background-color: white;
margin: 1em 0; margin: 1em 0;
font-size: 0.7em; } font-size: 0.7em; }
li.comment .author { .comment .author {
font-weight: bold; } font-weight: bold; }
li.comment .date { .comment .date {
font-size: 0.7em; } font-size: 0.7em; }
li.comment p { .comment p {
margin: 0.5em; } margin: 0.5em; }

View file

@ -11,7 +11,7 @@
{% endif %} {% endif %}
</nav> </nav>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class="username">{{ request.user.username }}</div> <a class="username" href="{% url "gestiojeux_auth:account_settings" %}">{% include "./user.html" with user=request.user %}</a>
<a class="login" href="{% url "gestiojeux_auth:logout" %}?next={{ request.get_full_path }}"><i class="fa fa-sign-out" aria-hidden="true"></i></a> <a class="login" href="{% url "gestiojeux_auth:logout" %}?next={{ request.get_full_path }}"><i class="fa fa-sign-out" aria-hidden="true"></i></a>
{% else %} {% else %}
<a class="login{% if url_name == "login" %} current{% endif %}" href="{% url "gestiojeux_auth:login" %}?next={{ request.get_full_path }}">Connexion</a> <a class="login{% if url_name == "login" %} current{% endif %}" href="{% url "gestiojeux_auth:login" %}?next={{ request.get_full_path }}">Connexion</a>

View file

@ -0,0 +1 @@
{% if user.first_name %}{{ user.first_name }}{% else %}{{ user.username }}{% endif %}