From 086cb36acde4343f5e4dc8dc18a70cdbd8512c33 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Sun, 28 Dec 2014 01:15:07 +0100 Subject: Work on dynamic (too much of ajax and JS...) person/organization widget --- archaeological_files/models.py | 2 +- archaeological_files_pdl/forms.py | 89 +++++++++++++- .../ishtar/blocks/JQueryCorporationPerson.js | 2 + .../templates/ishtar/blocks/JQueryNaturalPerson.js | 2 + .../templates/ishtar/blocks/JQueryPersonOrga.js | 65 ++++++++++ .../ishtar/wizard/wizard_generalcontractor.html | 32 +++++ archaeological_files_pdl/views.py | 8 +- archaeological_files_pdl/wizards.py | 9 ++ ishtar_common/forms_common.py | 40 +++++++ ishtar_common/models.py | 12 +- ishtar_common/static/media/style.css | 9 ++ .../templates/blocks/JQueryPersonOrganization.js | 50 ++++++++ .../templates/blocks/PersonOrganization.html | 9 ++ .../templates/ishtar/organization_form.html | 30 +++++ .../templates/ishtar/organization_person_form.html | 131 +++++++++++++++++++++ ishtar_common/templates/ishtar/person_form.html | 113 ++++++++++++++++++ ishtar_common/urls.py | 14 +++ ishtar_common/views.py | 58 ++++++++- ishtar_common/widgets.py | 103 +++++++++++++++- 19 files changed, 765 insertions(+), 13 deletions(-) create mode 100644 archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js create mode 100644 archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js create mode 100644 archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js create mode 100644 archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html create mode 100644 ishtar_common/templates/blocks/JQueryPersonOrganization.js create mode 100644 ishtar_common/templates/blocks/PersonOrganization.html create mode 100644 ishtar_common/templates/ishtar/organization_form.html create mode 100644 ishtar_common/templates/ishtar/organization_person_form.html create mode 100644 ishtar_common/templates/ishtar/person_form.html diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 7dc10d49c..289aa4166 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -46,7 +46,7 @@ class FileType(GeneralType): try: preventive = FileType.objects.get(txt_idx=key).pk return file_type_id == preventive - except ObjectDoesNotExist: + except FileType.DoesNotExist: return False class PermitType(GeneralType): diff --git a/archaeological_files_pdl/forms.py b/archaeological_files_pdl/forms.py index aedf60ba1..fc4efff73 100644 --- a/archaeological_files_pdl/forms.py +++ b/archaeological_files_pdl/forms.py @@ -23,11 +23,13 @@ from django import forms from django.core import validators from django.utils.translation import ugettext_lazy as _ -from ishtar_common.models import Person +from ishtar_common.models import Person, valid_id from archaeological_files import models -from ishtar_common.forms import get_now +from ishtar_common.forms import get_now, reverse_lazy from ishtar_common.forms_common import get_town_field +from archaeological_files.forms import GENERAL_CONTRACTOR, \ + RESPONSIBLE_PLANNING_SERVICE from ishtar_common import widgets @@ -65,7 +67,6 @@ class FileFormPreventiveType(forms.Form): default='NP') self.fields['permit_type'].help_text = models.PermitType.get_help() - class FileFormPlanning(forms.Form): form_label = _(u"Planning") associated_models = {'town':models.Town} @@ -87,3 +88,85 @@ class FileFormPlanning(forms.Form): label=_(u"Total developed surface (m²)"), required=False, validators=[validators.MinValueValidator(0), validators.MaxValueValidator(999999999)]) + +class FileFormGeneralContractor(forms.Form): + form_label = _(u"General contractor") + associated_models = {'general_contractor':models.Person} + + def __init__(self, *args, **kwargs): + + # get the status: natural person or corporation + DEFAULT_STATUS = 'natural' + current_status = '' + if 'data' in kwargs: + current_item_key = ((kwargs['prefix'] + '-') if kwargs.get('prefix')\ + else '') + 'general_contractor' + if kwargs['data'] and kwargs['data'].get(current_item_key): + model = self.associated_models['general_contractor'] + try: + item = model.objects.get(pk=kwargs['data'][current_item_key]) + current_status = 'natural' if item.is_natural() \ + else 'corporation' + except (model.DoesNotExist, ValueError): + pass + + status = '' + if 'status' in kwargs: + status = kwargs.pop('status') + if current_status != status: + if kwargs.get('data'): + # status is different from the existing - clear fields + kwargs.pop('data') + elif current_status: + status = current_status + else: + status = DEFAULT_STATUS + + if status not in ('natural', 'corporation'): + status = DEFAULT_STATUS + super(FileFormGeneralContractor, self).__init__(*args, **kwargs) + + # distinct widget for natural and corporation + if status == 'natural': + self.fields['general_contractor'] = forms.IntegerField( + label=_(u"General contractor"), + widget=widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-person', + args=[GENERAL_CONTRACTOR.pk]), + reverse_lazy('person_create'), + model=Person, + limit={'person_types':[GENERAL_CONTRACTOR.pk], + 'attached_to__isnull':True}, + js_template='ishtar/blocks/JQueryNaturalPerson.js', + new=True), + validators=[valid_id(Person)]) + else: + self.fields['general_contractor'] = forms.IntegerField( + label=_(u"General contractor"), + widget=widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-person', + args=[GENERAL_CONTRACTOR.pk]), + reverse_lazy('organization_person_create'), + model=Person, + limit={'person_types':[GENERAL_CONTRACTOR.pk]}, + js_template='ishtar/blocks/JQueryCorporationPerson.js', + new=True), + validators=[valid_id(Person)]) + +class FileFormPlanningService(forms.Form): + form_label = _(u"Town planning service") + associated_models = {'responsible_planning_service':models.Person} + + def __init__(self, *args, **kwargs): + super(FileFormPlanningService, self).__init__(*args, **kwargs) + self.fields['responsible_planning_service'] = forms.IntegerField( + label=_(u"Responsible town planning service"), + widget=widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-person', + args=[RESPONSIBLE_PLANNING_SERVICE.pk]), + reverse_lazy('person_create'), + model=Person, + limit={'person_types':[RESPONSIBLE_PLANNING_SERVICE.pk]}, + new=True), + validators=[valid_id(Person)]) + diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js new file mode 100644 index 000000000..3eb375167 --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js @@ -0,0 +1,2 @@ +var current_status = 'corporation'; +{% include "ishtar/blocks/JQueryPersonOrga.js" %} diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js new file mode 100644 index 000000000..fc4b9a90c --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js @@ -0,0 +1,2 @@ +var current_status = 'natural'; +{% include "ishtar/blocks/JQueryPersonOrga.js" %} diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js new file mode 100644 index 000000000..c151a5e4d --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js @@ -0,0 +1,65 @@ +person_save_callback = function(item_id, lbl){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(lbl); + if (item_id){ + url = {{edit_source}}+item_id; + $('#id_{{field_id}}').val(item_id); + } + $("#id_select_{{field_id}}").trigger('autocompletechange'); + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); +}; + +edit_url = {{edit_source}}; +parent_id = "{{field_id}}"; + +person_new_callback = function(){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); +} + +$(function() { + var $radios = $('input:radio[name=person_type]'); + if($radios.is(':checked') === false) { + $radios.filter('[value='+ current_status +']').prop('checked', true); + } + + $radios.change(function(){ + var loc = window.location; + window.location = loc.protocol + '//' + loc.host + loc.pathname + "?status=" + $('input:radio[name=person_type]:checked').val(); + }); + + $("#id_select_{{field_id}}").autocomplete({ + source: {{source}}, + select: function( event, ui ) { + var url = {{edit_source}}; + if(ui.item){ + url = {{edit_source}}+ui.item.id; + $('#id_{{field_id}}').val(ui.item.id); + } else { + $('#id_{{field_id}}').val(null); + } + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); + }, + minLength: 2{% if options %}, + {{options}} + {% endif %} + }); + + $.get( {{edit_source}}{% if selected %}+'{{selected}}'{% endif %}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); + + $('#id_select_{{field_id}}').live('click', function(){ + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); + $.get( {{edit_source}}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); + }); +}); diff --git a/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html b/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html new file mode 100644 index 000000000..4788a41fc --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html @@ -0,0 +1,32 @@ +{% extends "ishtar/wizard/default_wizard.html" %} +{% load i18n range table_form %} +{% block wizard_form %} +
{% csrf_token %} +
+{{ wizard.form.media }} +{{ wizard.management_form }} + + + + + + + + + + + +
Statut
+ + +{% table_form wizard.form %} +
+ +{{ previous_fields|safe }} +
+ + {% if next_steps %}{% endif %} +
+
+
+{% endblock %} diff --git a/archaeological_files_pdl/views.py b/archaeological_files_pdl/views.py index 1dd69b50c..139da223d 100644 --- a/archaeological_files_pdl/views.py +++ b/archaeological_files_pdl/views.py @@ -33,7 +33,10 @@ file_creation_wizard = FileWizard.as_view([ ('preventivetype-file_creation', forms.FileFormPreventiveType), ('preventiveplanning-file_creation', forms.FileFormPlanning), ('parcelspdl-file_creation', ParcelFormSet), - ('preventive-file_creation', ref_forms.FileFormPreventive), + ('generalcontractor-file_creation', + forms.FileFormGeneralContractor), + ('planningservice-file_creation', + forms.FileFormPlanningService), ('research-file_creation', ref_forms.FileFormResearch), ('final-file_creation', ref_forms.FinalForm)], label=_(u"New file"), @@ -41,9 +44,6 @@ file_creation_wizard = FileWizard.as_view([ 'preventivetype-file_creation':\ is_preventive('general-file_creation', models.FileType, type_key='file_type'), - 'preventive-file_creation':\ - is_preventive('general-file_creation', models.FileType, - type_key='file_type'), 'research-file_creation':\ is_not_preventive('general-file_creation', models.FileType, type_key='file_type'), diff --git a/archaeological_files_pdl/wizards.py b/archaeological_files_pdl/wizards.py index 679b0823f..4abe5f0d5 100644 --- a/archaeological_files_pdl/wizards.py +++ b/archaeological_files_pdl/wizards.py @@ -24,4 +24,13 @@ class FileWizard(BaseFileWizard): town_step_key = 'preventiveplanning-' town_input_id = 'main_town' multi_towns = False + wizard_templates = { + 'generalcontractor-file_creation':\ + 'ishtar/wizard/wizard_generalcontractor.html',} + def get_form_kwargs(self, *args, **kwargs): + returned = super(FileWizard, self).get_form_kwargs(*args, **kwargs) + if args and args[0].startswith('generalcontractor-file_creation'): + if 'status' in self.request.GET: + returned['status'] = self.request.GET['status'] + return returned diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index d8740b68c..e1a0e9063 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -191,6 +191,13 @@ class OrganizationFormSelection(forms.Form): OrganizationSelect, models.Organization), validators=[models.valid_id(models.Organization)]) +class BaseOrganizationForm(forms.ModelForm): + form_prefix = "orga" + class Meta: + model = models.Organization + fields = ['name', 'organization_type', 'address', 'address_complement', + 'town', 'postal_code',] + class PersonSelect(TableSelect): name = forms.CharField(label=_(u"Name"), max_length=30) surname = forms.CharField(label=_(u"Surname"), max_length=20) @@ -242,6 +249,39 @@ class SimplePersonForm(NewItemForm): associated_model=models.Organization, new=True), validators=[models.valid_id(models.Organization)], required=False) +class BasePersonForm(forms.ModelForm): + class Meta: + model = models.Person + fields = ['title', 'name', 'surname', 'address', 'address_complement', + 'town', 'postal_code'] + +class BaseOrganizationPersonForm(forms.ModelForm): + class Meta: + model = models.Person + fields = ['attached_to', 'title', 'name', 'surname',] + widgets = {'attached_to':widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-organization'), + reverse_lazy('organization_create'), + model=models.Organization, + attrs={'hidden':True}, + new=True), + } + + def __init__(self, *args, **kwargs): + super(BaseOrganizationPersonForm, self).__init__(*args, **kwargs) + + def save(self, *args, **kwargs): + person = super(BaseOrganizationPersonForm, self).save(*args, **kwargs) + instance = person.attached_to + form = BaseOrganizationForm(self.data, instance=instance, + prefix=BaseOrganizationForm.form_prefix) + if form.is_valid(): + orga = form.save() + if not person.attached_to: + person.attached_to = orga + person.save() + return person + class PersonForm(SimplePersonForm): person_types = forms.MultipleChoiceField(label=_("Person type"), choices=[], widget=forms.CheckboxSelectMultiple) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 23c2f3ac7..fe907f014 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -1540,10 +1540,13 @@ class Person(Address, Merge, OwnPerms, ValueGetter) : values = [unicode(getattr(self, attr)) for attr in ('surname', 'name') if getattr(self, attr)] - if not values: - values = [self.raw_name or ""] + if not values and self.raw_name: + values = [self.raw_name] if self.attached_to: - values.append(u"- " + unicode(self.attached_to)) + attached_to = unicode(self.attached_to) + if values: + values.append(u'-') + values.append(attached_to) return u" ".join(values) def get_values(self, prefix=''): @@ -1577,6 +1580,9 @@ class Person(Address, Merge, OwnPerms, ValueGetter) : self.merge_key += "-" + self.attached_to.merge_key self.merge_key = self.merge_key[:300] + def is_natural(self): + return not self.attached_to + def has_right(self, right_name): if '.' in right_name: right_name = right_name.split('.')[-1] diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 5168cba6e..55bb95e30 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -120,6 +120,15 @@ td{ text-align:left; } +div.selected{ + border:1px solid #922; +} + +textarea:disabled, +input[type="text"]:disabled{ + background-color: #eee; +} + button{ background-color:#f1f2f6; border:1px solid #AAA; diff --git a/ishtar_common/templates/blocks/JQueryPersonOrganization.js b/ishtar_common/templates/blocks/JQueryPersonOrganization.js new file mode 100644 index 000000000..b13a2c28e --- /dev/null +++ b/ishtar_common/templates/blocks/JQueryPersonOrganization.js @@ -0,0 +1,50 @@ + +$("#id_select_{{field_id}}").autocomplete({ + source: {{source}}, + select: function( event, ui ) { + var url = {{edit_source}}; + if(ui.item){ + url = {{edit_source}}+ui.item.id; + $('#id_{{field_id}}').val(ui.item.id); + } else { + $('#id_{{field_id}}').val(null); + } + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); + }, + minLength: 2{% if options %}, + {{options}} + {% endif %} +}); + +$.get( {{edit_source}}{% if selected %}+'{{selected}}'{% endif %}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); +}); + +$('#id_select_{{field_id}}').live('click', function(){ + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); + $.get( {{edit_source}}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); +}); + +person_save_callback = function(item_id, lbl){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(lbl); + if (item_id){ + url = {{edit_source}}+item_id; + $('#id_{{field_id}}').val(item_id); + } + $("#id_select_{{field_id}}").trigger('autocompletechange'); + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); +}; +person_new_callback = function(){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); +} diff --git a/ishtar_common/templates/blocks/PersonOrganization.html b/ishtar_common/templates/blocks/PersonOrganization.html new file mode 100644 index 000000000..6e7264c8e --- /dev/null +++ b/ishtar_common/templates/blocks/PersonOrganization.html @@ -0,0 +1,9 @@ +{% load i18n %} + + +
+ + + diff --git a/ishtar_common/templates/ishtar/organization_form.html b/ishtar_common/templates/ishtar/organization_form.html new file mode 100644 index 000000000..207116c21 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_form.html @@ -0,0 +1,30 @@ +{% load i18n %} + + + + +
{{form.name}}
+{% for hidden in form.hidden_fields %}{{hidden}}{% endfor %} + + + + + + + + + + + + + + + + + + + + + +
{{form.organization_type}}
{{form.address}}
{{form.address_complement}}
{{form.postal_code}}
{{form.town}}
+ diff --git a/ishtar_common/templates/ishtar/organization_person_form.html b/ishtar_common/templates/ishtar/organization_person_form.html new file mode 100644 index 000000000..ccc442197 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_person_form.html @@ -0,0 +1,131 @@ +{% load i18n %}{% load url from future %} +
+
+{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} + + + + + + + + +
{% trans "Identification" %}
 {{form.attached_to}}
+ + + + + + + + + + + + + + + +
{% trans "Corporation manager" %}
{{form.title}}
{{form.name}}
{{form.surname}}
+
+ + + + +
+
+
+ + diff --git a/ishtar_common/templates/ishtar/person_form.html b/ishtar_common/templates/ishtar/person_form.html new file mode 100644 index 000000000..555aa1a5f --- /dev/null +++ b/ishtar_common/templates/ishtar/person_form.html @@ -0,0 +1,113 @@ +{% load i18n %}{% load url from future %} +
+
+{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} + + + + + + + + + + + + + + + + +
{% trans "Identification" %}
{{form.title}}
{{form.name}}
{{form.surname}}
+ + + + + + + + + + + + + + + + + + +
{% trans "Contact informations" %}
{{form.address}}
{{form.address_complement}}
{{form.postal_code}}
{{form.town}}
+
+ + + + +
+
+
+ diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 97fe4f81a..d31d0d359 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -39,6 +39,10 @@ urlpatterns = patterns('', views.person_modification_wizard, name='person_modification'), url(r'person_deletion/(?P.+)?$', views.person_deletion_wizard, name='person_deletion'), + url(r'^person-edit/$', + views.PersonCreate.as_view(), name='person_create'), + url(r'^person-edit/(?P\d+)$', + views.PersonEdit.as_view(), name='person_edit'), url(r'organization_creation/(?P.+)?$', views.organization_creation_wizard, name='organization_creation'), url(r'organization_modification/(?P.+)?$', @@ -46,6 +50,16 @@ urlpatterns = patterns('', name='organization_modification'), url(r'organization_deletion/(?P.+)?$', views.organization_deletion_wizard, name='organization_deletion'), + url(r'organization-edit/$', + views.OrganizationCreate.as_view(), name='organization_create'), + url(r'organization-edit/(?P\d+)$', + views.OrganizationEdit.as_view(), name='organization_edit'), + url(r'organization-person-edit/$', + views.OrganizationPersonCreate.as_view(), + name='organization_person_create'), + url(r'organization-person-edit/(?P\d+)$', + views.OrganizationPersonEdit.as_view(), + name='organization_person_edit'), url(r'account_management/(?P.+)?$', views.account_management_wizard, name='account_management'), url(r'import/$', views.NewImportView.as_view(), name='new_import'), diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 1b27bcf43..3c6b796db 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -44,7 +44,7 @@ from django.shortcuts import render_to_response, redirect from django.template import RequestContext, loader from django.utils.decorators import method_decorator from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView +from django.views.generic import ListView, UpdateView, CreateView from django.views.generic.edit import CreateView, DeleteView from xhtml2odt import xhtml2odt @@ -943,3 +943,59 @@ class ImportDeleteView(IshtarMixin, LoginRequiredMixin, DeleteView): def get_success_url(self): return reverse('current_imports') + +class PersonCreate(LoginRequiredMixin, CreateView): + model = models.Person + form_class = forms.BasePersonForm + template_name = 'ishtar/person_form.html' + + def get_success_url(self): + return reverse('person_edit', args=[self.object.pk]) + +class PersonEdit(LoginRequiredMixin, UpdateView): + model = models.Person + form_class = forms.BasePersonForm + template_name = 'ishtar/person_form.html' + + def get_success_url(self): + return reverse('person_edit', args=[self.object.pk]) + +class OrganizationCreate(LoginRequiredMixin, CreateView): + model = models.Organization + form_class = forms.BaseOrganizationForm + template_name = 'ishtar/organization_form.html' + form_prefix = "orga" + + def get_form_kwargs(self): + kwargs = super(OrganizationCreate, self).get_form_kwargs() + if hasattr(self.form_class, 'form_prefix'): + kwargs.update({'prefix': self.form_class.form_prefix}) + return kwargs + +class OrganizationEdit(LoginRequiredMixin, UpdateView): + model = models.Organization + form_class = forms.BaseOrganizationForm + template_name = 'ishtar/organization_form.html' + + def get_form_kwargs(self): + kwargs = super(OrganizationEdit, self).get_form_kwargs() + if hasattr(self.form_class, 'form_prefix'): + kwargs.update({'prefix': self.form_class.form_prefix}) + return kwargs + +class OrganizationPersonCreate(LoginRequiredMixin, CreateView): + model = models.Person + form_class = forms.BaseOrganizationPersonForm + template_name = 'ishtar/organization_person_form.html' + + def get_success_url(self): + return reverse('organization_person_edit', args=[self.object.pk]) + +class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): + model = models.Person + form_class = forms.BaseOrganizationPersonForm + template_name = 'ishtar/organization_person_form.html' + + def get_success_url(self): + return reverse('organization_person_edit', args=[self.object.pk]) + diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index 0ba4061a8..91594edbd 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -26,6 +26,7 @@ from django.db.models import fields from django.forms import ClearableFileInput from django.forms.widgets import flatatt from django.template import Context, loader +from django.template.defaultfilters import slugify from django.utils.encoding import smart_unicode from django.utils.functional import lazy from django.utils.html import escape @@ -203,7 +204,7 @@ class JQueryAutoComplete(forms.TextInput): except: raise ValueError('source type is not valid') dct = {'source':mark_safe(source), - 'field_id':field_id} + 'field_id':field_id,} if self.options: dct['options'] = mark_safe('%s' % self.options) @@ -218,6 +219,7 @@ class JQueryAutoComplete(forms.TextInput): def render(self, name, value=None, attrs=None): attrs_hidden = self.build_attrs(attrs, name=name) attrs_select = self.build_attrs(attrs) + attrs_select['placeholder'] = _(u"Search...") if value: hiddens = [] selects = [] @@ -281,6 +283,105 @@ class JQueryAutoComplete(forms.TextInput): } return html +class JQueryPersonOrganization(forms.TextInput): + """ + Complex widget which manage: + * link between person and organization + * display addresses of the person and of the organization + * create new person and new organization + """ + + def __init__(self, source, edit_source, model, options={}, + attrs={}, new=False, limit={}, + html_template = 'blocks/PersonOrganization.html', + js_template='blocks/JQueryPersonOrganization.js'): + self.options = None + self.attrs = {} + self.model = model + self.source = source + self.edit_source = edit_source + if len(options) > 0: + self.options = JSONEncoder().encode(options) + self.attrs.update(attrs) + self.new = new + self.limit = limit + self.js_template = js_template + self.html_template = html_template + + @classmethod + def encode_source(cls, source): + encoded_src = '' + if isinstance(source, list): + encoded_src = JSONEncoder().encode(source) + elif isinstance(source, str) \ + or isinstance(source, unicode): + encoded_src = "'%s'" % escape(source) + else: + try: + encoded_src = "'" + unicode(source) + "'" + except: + raise ValueError('source type is not valid') + return encoded_src + + def render_js(self, field_id, selected=''): + source = self.encode_source(self.source) + edit_source = self.encode_source(self.edit_source) + dct = {'source':mark_safe(source), + 'edit_source':mark_safe(edit_source), + 'selected':selected, + 'safe_field_id':slugify(field_id).replace('-', '_'), + 'field_id':field_id} + if self.options: + dct['options'] = mark_safe('%s' % self.options) + js = loader.get_template(self.js_template).render(Context(dct)) + return js + + def render(self, name, value=None, attrs=None): + attrs_hidden = self.build_attrs(attrs, name=name) + attrs_select = self.build_attrs(attrs) + attrs_select['placeholder'] = _(u"Search...") + selected = '' + if value: + hiddens = [] + selects = [] + if type(value) not in (list, tuple): + values = unicode(escape(smart_unicode(value))) + values = values.replace('[', '').replace(']', '') + values = values.split(',') + else: + values = [] + for v in value: + values += v.split(',') + for v in values: + if not v: + continue + hiddens.append(v) + selects.append(v) + if self.model: + try: + item = self.model.objects.get(pk=v) + selects[-1] = unicode(item) + selected = item.pk + except (self.model.DoesNotExist, ValueError): + selects.pop() + hiddens.pop() + if hiddens and selects: + attrs_hidden['value'] = hiddens[0] + attrs_select['value'] = selects[0] + if not self.attrs.has_key('id'): + attrs_hidden['id'] = 'id_%s' % name + attrs_select['id'] = 'id_select_%s' % name + if 'class' not in attrs_select: + attrs_select['class'] = 'autocomplete' + new = '' + dct = {'attrs_select':mark_safe(flatatt(attrs_select)), + 'attrs_hidden':mark_safe(flatatt(attrs_hidden)), + 'name':name, + 'js':self.render_js(name, selected), + 'new':mark_safe(new)} + html = loader.get_template(self.html_template).render(Context(dct)) + return html + class JQueryJqGrid(forms.RadioSelect): COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" class Media: -- cgit v1.2.3