Skip to content
This repository has been archived by the owner on May 15, 2019. It is now read-only.

Commit

Permalink
Users who are not logged in via a directory can delete their avatar
Browse files Browse the repository at this point in the history
  • Loading branch information
mpapillon committed Sep 28, 2017
1 parent 5a7e056 commit 4aed204
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 43 deletions.
38 changes: 8 additions & 30 deletions nouvelles/forms.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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'
12 changes: 12 additions & 0 deletions nouvelles/signals.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
"""
Expand Down
22 changes: 21 additions & 1 deletion nouvelles/static/nouvelles/css/components/form.less
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,4 +18,21 @@
margin: 0;
}
}
}
}

.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);
}
}
13 changes: 13 additions & 0 deletions nouvelles/static/nouvelles/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions nouvelles/templates/nouvelles/forms/avatar_input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} />
{% if widget.is_initial %}
{% if not widget.required %}
<label class="form-checkbox" for="{{ widget.checkbox_id }}">
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}"/>
<i class="form-icon"></i> {{ widget.clear_checkbox_label }}
</label>
{% endif %}
{% endif %}
5 changes: 5 additions & 0 deletions nouvelles/templates/registration/details_change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
</nav>
<div class="panel-body">{% csrf_token %}
{{ form|as_spectre }}
{{ formset.management_form }}
{% for profile_form in formset %}
{{ profile_form.avatar|as_spectre }}
{{ profile_form.user }}
{% endfor %}
</div>

<div class="panel-footer">
Expand Down
35 changes: 25 additions & 10 deletions nouvelles/views/profile.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.forms import inlineformset_factory
from django.urls import reverse_lazy
from django.views.generic import FormView
from django.views.generic import UpdateView
from django.views.generic.base import TemplateView

from nouvelles.forms import DetailsChangeForm
from nouvelles.forms import UserChangeForm, AvatarInput
from nouvelles.models import Profile
from nouvelles.views.mixins import ModelFormSetMixin


class DetailsChangeView(LoginRequiredMixin, FormView):
class DetailsChangeView(LoginRequiredMixin, ModelFormSetMixin, UpdateView):
template_name = 'registration/details_change_form.html'
success_url = reverse_lazy('nouvelles:profile:change_done')
form_class = DetailsChangeForm
model = User
form_class = UserChangeForm

def get_form_kwargs(self):
kwargs = super(DetailsChangeView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_formset_class(self, **kwargs):
"""
Returns the form set class to use in this view
"""
return inlineformset_factory(self.model, Profile, fields=('avatar',),
widgets={'avatar': AvatarInput()},
can_delete=False)

def get_object(self, queryset=None):
return self.request.user

def form_valid(self, form):
form.save()
return super(DetailsChangeView, self).form_valid(form)
formset = self.get_formset()
if formset.is_valid():
formset.save()
return super(DetailsChangeView, self).form_valid(form)
else:
return self.form_invalid(form)


class DetailsChangeDoneView(TemplateView):
Expand Down
2 changes: 1 addition & 1 deletion spectre/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_info__ = ('0', '1', '0', 'dev1')
__version_info__ = ('0', '1', '0', 'dev2')
__version__ = ".".join(__version_info__)
2 changes: 1 addition & 1 deletion spectre/templatetags/spectre.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def as_spectre(form_or_field, size=None, layout=None):
'field': form_or_field,
'size': size,
})
elif isinstance(form_or_field, forms.Form):
elif isinstance(form_or_field, forms.BaseForm):
return get_template("spectre/form.html").render({
'form': form_or_field,
'size': size,
Expand Down

0 comments on commit 4aed204

Please sign in to comment.