From 4aed20494568b0bc7da8c5f8bf9fa30db6d922c6 Mon Sep 17 00:00:00 2001 From: Maxence PAPILLON Date: Thu, 28 Sep 2017 14:31:46 +0200 Subject: [PATCH] Users who are not logged in via a directory can delete their avatar --- nouvelles/forms.py | 38 ++++--------------- nouvelles/signals.py | 12 ++++++ .../static/nouvelles/css/components/form.less | 22 ++++++++++- nouvelles/static/nouvelles/css/style.css | 13 +++++++ .../nouvelles/forms/avatar_input.html | 9 +++++ .../registration/details_change_form.html | 5 +++ nouvelles/views/profile.py | 35 ++++++++++++----- spectre/__init__.py | 2 +- spectre/templatetags/spectre.py | 2 +- 9 files changed, 95 insertions(+), 43 deletions(-) create mode 100644 nouvelles/templates/nouvelles/forms/avatar_input.html diff --git a/nouvelles/forms.py b/nouvelles/forms.py index f30bf06..f8d02ab 100644 --- a/nouvelles/forms.py +++ b/nouvelles/forms.py @@ -1,8 +1,9 @@ from django import forms from django.contrib.auth.models import User from django.db.models.functions import Lower +from django.forms import ClearableFileInput -from nouvelles.models import Article, Tag, Profile +from nouvelles.models import Article, Tag from nouvelles.templatetags.nouvelles import user_full_name @@ -50,38 +51,15 @@ class Meta: fields = ['title', 'criticality', 'effective_date', 'tags', 'content'] -class DetailsChangeForm(forms.Form): +class UserChangeForm(forms.ModelForm): """ A form that lets a user to change their personal information. """ - first_name = forms.CharField(max_length=30, required=False) - last_name = forms.CharField(max_length=30, required=False) - email = forms.EmailField(required=False) - avatar = forms.ImageField(required=False) - - def __init__(self, user, initial=None, *args, **kwargs): - self.user = user - object_data = forms.model_to_dict(user, ['first_name', 'last_name', 'email']) - # if initial was provided, it should override the values from instance - if initial is not None: - object_data.update(initial) - - super(DetailsChangeForm, self).__init__(initial=object_data, *args, **kwargs) - - def save(self, commit=True): - if not hasattr(self.user, 'profile'): - self.user.profile = Profile() - - self.user.first_name = self.cleaned_data['first_name'] - self.user.last_name = self.cleaned_data['last_name'] - self.user.email = self.cleaned_data['email'] + class Meta: + model = User + fields = ['first_name', 'last_name', 'email'] - if 'avatar' in self.files: - avatar = self.files['avatar'] - self.user.profile.avatar.save(avatar.name, avatar) - if commit: - self.user.profile.save() - self.user.save() - return self.user +class AvatarInput(ClearableFileInput): + template_name = 'nouvelles/forms/avatar_input.html' diff --git a/nouvelles/signals.py b/nouvelles/signals.py index fc64e27..16bae4d 100644 --- a/nouvelles/signals.py +++ b/nouvelles/signals.py @@ -1,5 +1,6 @@ from tempfile import TemporaryFile +from django.contrib.auth.models import User from django.core.files.storage import default_storage from django.db.models import signals as models from django.dispatch import receiver @@ -22,6 +23,17 @@ def send_article_mail(sender, instance, created, **kwargs): messages.send_article_to_all_users(instance) +@receiver(models.post_save, sender=User, dispatch_uid="create_profile") +def create_profile(sender, instance, created, **kwargs): + """ + Creates the user profile if it does not have one. + """ + if not created: + return + profile = Profile(user=instance) + profile.save() + + @receiver(models.post_delete, sender=Profile, dispatch_uid="delete_avatar") def delete_avatar(sender, instance, **kwargs): """ diff --git a/nouvelles/static/nouvelles/css/components/form.less b/nouvelles/static/nouvelles/css/components/form.less index 16da3cf..f2e4bb0 100644 --- a/nouvelles/static/nouvelles/css/components/form.less +++ b/nouvelles/static/nouvelles/css/components/form.less @@ -1,3 +1,6 @@ +@import "../../vendor/spectre/src/variables"; +@import "../../vendor/spectre/src/mixins"; + .form-input:not(:placeholder-shown):invalid { // Removes the red border on invalid inputs. border-color: @border-color-dark; @@ -15,4 +18,21 @@ margin: 0; } } -} \ No newline at end of file +} + +.form-input[type="file"] { + overflow: hidden; +} + +.has-error { + .form-checkbox .form-icon, + .form-radio .form-icon { + border-color: @control-color-error; + } + .form-checkbox input:focus + .form-icon, + .form-radio input:focus + .form-icon, + .form-switch input:focus + .form-icon { + border-color: @control-color-error; + .control-shadow(@control-color-error); + } +} diff --git a/nouvelles/static/nouvelles/css/style.css b/nouvelles/static/nouvelles/css/style.css index 7600221..7cecc93 100644 --- a/nouvelles/static/nouvelles/css/style.css +++ b/nouvelles/static/nouvelles/css/style.css @@ -243,6 +243,19 @@ pre code { .form-input-hint ol li { margin: 0; } +.form-input[type="file"] { + overflow: hidden; +} +.has-error .form-checkbox .form-icon, +.has-error .form-radio .form-icon { + border-color: #e85600; +} +.has-error .form-checkbox input:focus + .form-icon, +.has-error .form-radio input:focus + .form-icon, +.has-error .form-switch input:focus + .form-icon { + border-color: #e85600; + box-shadow: 0 0 0 0.2rem rgba(232, 86, 0, 0.15); +} .tile-article { background: #f8f9fa; border-radius: 3px; diff --git a/nouvelles/templates/nouvelles/forms/avatar_input.html b/nouvelles/templates/nouvelles/forms/avatar_input.html new file mode 100644 index 0000000..d3f42c8 --- /dev/null +++ b/nouvelles/templates/nouvelles/forms/avatar_input.html @@ -0,0 +1,9 @@ + +{% if widget.is_initial %} + {% if not widget.required %} + + {% endif %} +{% endif %} diff --git a/nouvelles/templates/registration/details_change_form.html b/nouvelles/templates/registration/details_change_form.html index 519f665..f58ab98 100644 --- a/nouvelles/templates/registration/details_change_form.html +++ b/nouvelles/templates/registration/details_change_form.html @@ -33,6 +33,11 @@
{% csrf_token %} {{ form|as_spectre }} + {{ formset.management_form }} + {% for profile_form in formset %} + {{ profile_form.avatar|as_spectre }} + {{ profile_form.user }} + {% endfor %}