From f5634457ef7882cbc9fcb30d0e12a61d4f13498a Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Mon, 28 May 2018 16:58:42 +0200 Subject: User profile form: duplicate, delete and edit --- ishtar_common/forms_common.py | 72 +++++++++++++++++++--- .../migrations/0054_auto_20180525_1249.py | 24 ++++++++ ishtar_common/models.py | 39 +++++++++++- .../templates/blocks/bs_form_snippet.html | 4 +- ishtar_common/templates/ishtar/forms/profile.html | 51 +++++++++++++++ .../templates/ishtar/wizard/default_wizard.html | 2 +- ishtar_common/templatetags/table_form.py | 4 +- ishtar_common/urls.py | 3 +- ishtar_common/views.py | 36 ++++++++--- ishtar_common/wizards.py | 15 +++-- 10 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 ishtar_common/migrations/0054_auto_20180525_1249.py create mode 100644 ishtar_common/templates/ishtar/forms/profile.html (limited to 'ishtar_common') diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index af16e6deb..edea0cde2 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -771,6 +771,7 @@ class ProfileForm(ManageOldType): profile_type = forms.ChoiceField(label=_(u"Type"), choices=[], required=False) area = widgets.Select2MultipleField(label=_(u"Areas"), required=False) + name = forms.CharField(label=_(u"Name"), required=False) pk = forms.IntegerField(label=" ", widget=forms.HiddenInput, required=False) TYPES = [ @@ -798,37 +799,88 @@ class FinalAccountForm(forms.Form): class ProfilePersonForm(forms.Form): + """ + Edit the current profile + """ current_profile = forms.ChoiceField(label=_(u"Current profile"), choices=[]) + name = forms.CharField(label=_(u"Name"), required=False) + profile_type = forms.ChoiceField(label=_(u"Profile type"), required=False, + disabled=True, choices=[]) + duplicate_profile = forms.BooleanField( + label=_(u"Duplicate this profile"), required=False) + delete_profile = forms.BooleanField( + label=_(u"Delete this profile"), required=False, + ) def __init__(self, *args, **kwargs): self.user = kwargs.pop('user') choices, initial = [], kwargs.get('initial', {}) - for profile in self.user.ishtaruser.person.profiles.all(): + current_profile = None + for profile in self.user.ishtaruser.person.profiles.order_by( + 'name', 'profile_type__label').all(): if profile.current: + current_profile = profile initial['current_profile'] = profile.pk choices.append((profile.pk, unicode(profile))) + if current_profile: + initial['name'] = current_profile.name or \ + current_profile.profile_type + initial['profile_type'] = current_profile.profile_type.pk kwargs['initial'] = initial super(ProfilePersonForm, self).__init__(*args, **kwargs) self.fields['current_profile'].choices = choices - def save(self, session): - q = models.UserProfile.objects.filter( - person__ishtaruser=self.user.ishtaruser, current=True) - for profile in q.all(): - profile.current = False - profile.save() + if not current_profile or \ + not self.user.ishtaruser.person.profiles.filter( + profile_type=current_profile.profile_type).exclude( + pk=current_profile.pk).count(): + # cannot delete the current profile if no profile of this type is + # available + self.fields.pop('delete_profile') + + if not current_profile: + return + self.fields['profile_type'].choices = [ + (current_profile.profile_type.pk, current_profile.profile_type.name) + ] + def save(self, session): q = models.UserProfile.objects.filter( person__ishtaruser=self.user.ishtaruser, - pk=int(self.cleaned_data['current_profile']) - ) + pk=self.cleaned_data['current_profile']) if not q.count(): return profile = q.all()[0] + + # manage deletion + if self.cleaned_data.get('delete_profile', None): + q = self.user.ishtaruser.person.profiles.filter( + profile_type=profile.profile_type).exclude( + pk=profile.pk) + if not q.count(): + # cannot delete the current profile if no profile of this type + # is available + return + new_current = q.all()[0] + new_current.current = True + new_current.save() + profile.delete() + return + + name = self.cleaned_data['name'] + + # manage duplication + if self.cleaned_data.get('duplicate_profile', None): + profile_name = profile.name or profile.profile_type.label + if name == profile_name: + name += unicode(_(u" (duplicate)")) + profile.duplicate(name=name) + return + profile.current = True + profile.name = name profile.save() - clean_session_cache(session) diff --git a/ishtar_common/migrations/0054_auto_20180525_1249.py b/ishtar_common/migrations/0054_auto_20180525_1249.py new file mode 100644 index 000000000..46f63b5c0 --- /dev/null +++ b/ishtar_common/migrations/0054_auto_20180525_1249.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-05-25 12:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ishtar_common', '0053_auto_20180523_1504'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='name', + field=models.CharField(blank=True, default='', max_length=100, verbose_name='Name'), + ), + migrations.AlterUniqueTogether( + name='userprofile', + unique_together=set([('name', 'profile_type', 'person')]), + ), + ] diff --git a/ishtar_common/models.py b/ishtar_common/models.py index a5bf6cf3a..b31338a2d 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -2792,6 +2792,7 @@ post_delete.connect(post_save_cache, sender=ProfileType) class UserProfile(models.Model): + name = models.CharField(_(u"Name"), blank=True, default=u"", max_length=100) profile_type = models.ForeignKey( ProfileType, verbose_name=_(u"Profile type")) areas = models.ManyToManyField("Area", verbose_name=_(u"Areas"), @@ -2803,9 +2804,10 @@ class UserProfile(models.Model): class Meta: verbose_name = _(u"User profile") verbose_name_plural = _(u"User profiles") + unique_together = (('name', 'profile_type', 'person'),) def __unicode__(self): - lbl = unicode(self.profile_type) + lbl = self.name or unicode(self.profile_type) if not self.areas.count(): return lbl return u"{} ({})".format(lbl, u", ".join( @@ -2824,6 +2826,34 @@ class UserProfile(models.Model): def area_labels(self): return u", ".join([unicode(area) for area in self.areas.all()]) + def duplicate(self, **kwargs): + areas = [area for area in self.areas.all()] + new_item = self + new_item.pk = None + for key in kwargs: + setattr(new_item, key, kwargs[key]) + new_item.save() + for area in areas: + new_item.areas.add(area) + return new_item + + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + super(UserProfile, self).save( + force_insert=force_insert, force_update=force_update, using=using, + update_fields=update_fields) + + # only one current profile per user + if not self.current: + return + q = UserProfile.objects.filter( + person=self.person, current=True).exclude(pk=self.pk) + if not q.count(): + return + for p in q.all(): + p.current = False + p.save() + class IshtarUser(FullSearch): TABLE_COLS = ('username', 'person__name', 'person__surname', @@ -2864,8 +2894,8 @@ class IshtarUser(FullSearch): def current_profile_name(self): q = UserProfile.objects.filter(current=True, person__ishtaruser=self) if q.count(): - return q.values('profile_type__label').all()[0][ - 'profile_type__label'] + vals = q.values('profile_type__label', 'name').all()[0] + return vals['name'] or vals['profile_type__label'] profile = self.person.current_profile if not profile: return u"" @@ -2885,6 +2915,9 @@ class IshtarUser(FullSearch): admin, created = ProfileType.objects.get_or_create( txt_idx='administrator') if user.is_superuser: + if UserProfile.objects.filter( + profile_type=admin, person=person).count(): + return UserProfile.objects.get_or_create( profile_type=admin, person=person, defaults={'current': True}) diff --git a/ishtar_common/templates/blocks/bs_form_snippet.html b/ishtar_common/templates/blocks/bs_form_snippet.html index 818c654d1..47779cdc1 100644 --- a/ishtar_common/templates/blocks/bs_form_snippet.html +++ b/ishtar_common/templates/blocks/bs_form_snippet.html @@ -64,9 +64,9 @@ {% if forloop.counter0 %} {% endif %}

{{field.name|from_dict:form.HEADERS|call:'render'}}

-
+
{% elif not search and not forloop.counter0 or search and forloop.counter0 == 1 %} -
+
{% endif %} {% include "blocks/bs_field_snippet.html" %} {% if forloop.last %} diff --git a/ishtar_common/templates/ishtar/forms/profile.html b/ishtar_common/templates/ishtar/forms/profile.html new file mode 100644 index 000000000..02f50be7b --- /dev/null +++ b/ishtar_common/templates/ishtar/forms/profile.html @@ -0,0 +1,51 @@ +{% extends "ishtar/form.html" %} +{% load i18n inline_formset table_form %} +{% block extra_head %} + +{% endblock %} + +{% block content %} +

{{page_name}}

+
+ + {% if form.non_field_errors %} + + {% endif %} + + {% for hidden in form.hidden_fields %} + {{hidden}} + {% if hidden.errors %}
+ {{ hidden.errors }} +
{% endif %} + {% endfor %} + +
+ {% for field in form.visible_fields %} + {% if forloop.counter0 == 0 %} +
+ {{field|safe}} +
+
+
+ {% elif field.name == 'delete_profile' %} +
+
+ {% include "blocks/bs_field_snippet.html" %} + {% else %} + {% include "blocks/bs_field_snippet.html" %} + {% endif %} + {% endfor %} +
+
+{% endblock %} + diff --git a/ishtar_common/templates/ishtar/wizard/default_wizard.html b/ishtar_common/templates/ishtar/wizard/default_wizard.html index a7a705f59..6707124f9 100644 --- a/ishtar_common/templates/ishtar/wizard/default_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/default_wizard.html @@ -25,7 +25,7 @@
{% endif %} {% for formsetform in wizard.form.forms %} - {% bs_form formsetform %} + {% bs_form formsetform forloop.counter0 %} {% endfor %}