diff options
18 files changed, 352 insertions, 299 deletions
diff --git a/archaeological_files_pdl/forms.py b/archaeological_files_pdl/forms.py index 7ade0b3f0..7ada27be8 100644 --- a/archaeological_files_pdl/forms.py +++ b/archaeological_files_pdl/forms.py @@ -26,11 +26,14 @@ from ishtar_common.utils import ugettext_lazy as _  from ishtar_common.models import Person, Town, Department, valid_id, \      person_type_pk_lazy, person_type_pks_lazy, organization_type_pks_lazy, \ -    organization_type_pk_lazy +    organization_type_pk_lazy, get_sra_agent_label, \ +    get_orga_general_contractor_label, get_general_contractor_label, \ +    get_orga_planning_service_label, get_responsible_planning_service_label +  from archaeological_files import models  from ishtar_common.forms import get_now, reverse_lazy, ManageOldType, \ -    CustomForm, FieldType, IshtarForm +    CustomForm, FieldType, IshtarForm, FormHeader  from ishtar_common import widgets  from bootstrap_datepicker.widgets import DatePicker @@ -84,18 +87,22 @@ class FileFormPreventiveType(CustomForm, ManageOldType, forms.Form):          self.fields['permit_type'].help_text = models.PermitType.get_help() -class FileFormPlanning(CustomForm, forms.Form): +class FileFormPlanning(CustomForm, ManageOldType):      form_label = _(u"Planning")      form_admin_name = _(u"Archaeological file - 017 - Preventive - Planning")      form_slug = "file-017-preventiveplanning"      base_models = ['town', 'department']      associated_models = {'town': Town, 'department': Department} +    HEADERS = {} +    HEADERS['town'] = FormHeader(_("Localisation"))      name = forms.CharField(label=_(u"Planning name"), required=False,                             max_length=100)      town = widgets.Select2MultipleField(          model=Town, label=_("Towns"), required=False, remote=True)      department = widgets.Select2MultipleField( -        model=Department, label=_("Departments"), required=False) +        model=Department, label=_("Departments"), required=False, +        help_text=_("Only relevant when no town is provided.") +    )      locality = forms.CharField(label=_(u"Locality"), max_length=100,                                 required=False)      address = forms.CharField( @@ -104,6 +111,7 @@ class FileFormPlanning(CustomForm, forms.Form):          required=False)      postal_code = forms.CharField(label=_(u"Postal code"), max_length=10,                                    required=False) +    HEADERS['total_surface'] = FormHeader(_("Surfaces"))      total_surface = forms.FloatField(          required=False,          widget=widgets.AreaWidget, @@ -236,33 +244,36 @@ class PersonOrgaForm(forms.Form):                  validators=[valid_id(models.Organization)]) -class FileFormGeneralContractor(CustomForm, PersonOrgaForm): +class FileFormGeneralContractor(CustomForm, ManageOldType):      form_label = _(u"General contractor")      form_admin_name = _("Archaeological file - 030 - General contractor")      form_slug = "file-030-generalcontractor" +    extra_form_modals = ["person", "organization"]      associated_models = {'general_contractor': models.Person,                           'corporation_general_contractor': models.Organization} +      corporation_general_contractor = forms.IntegerField(          label=_("General contractor"), -        required=False, -        widget=widgets.JQueryPersonOrganization( -            reverse_lazy('autocomplete-organization', -                         args=[ -                             organization_type_pks_lazy(['general_contractor'])] -                         ), -            reverse_lazy('organization_create'), -            model=models.Organization, +        widget=widgets.JQueryAutoComplete( +            reverse_lazy( +                'autocomplete-organization', +                args=[ +                    organization_type_pks_lazy(['general_contractor']), +                ]),              limit={                  'organization_type': [                      organization_type_pk_lazy('general_contractor') -                ]}, -            js_template='ishtar/blocks/JQueryCorporationPerson.js', -            new=True), -        validators=[valid_id(models.Organization)] -    ) +                ] +            }, +            tips=get_orga_general_contractor_label, +            associated_model=models.Organization, new=True, +            detail=True, +            modify=True +        ), +        validators=[valid_id(models.Organization)])      general_contractor = forms.IntegerField( -        label=_(u"In charge"), +        label=_("In charge"),          required=False,          widget=widgets.JQueryAutoComplete(              reverse_lazy('autocomplete-person', @@ -271,118 +282,56 @@ class FileFormGeneralContractor(CustomForm, PersonOrgaForm):                               ]),              associated_model=Person,              limit={'person_types': [ -                person_type_pk_lazy(['general_contractor']) +                person_type_pk_lazy('general_contractor')                  ]}, -            dynamic_limit=['general_contractor'], -            url_new='new-person-noorga', +            tips=get_general_contractor_label, +            detail=True, +            modify=True,              new=True),          validators=[valid_id(Person)]      ) -    PERSON_FIELD = 'general_contractor' -    PERSON_TYPE_PK = person_type_pk_lazy('general_contractor') -    PERSON_LABEL = _(u"General contractor") -    ORGA_FIELD = 'corporation_general_contractor' -    ORGA_TYPE_PK = organization_type_pk_lazy('general_contractor') -    ORGA_LABEL = _(u"General contractor") - -    def __init__(self, *args, **kwargs): -        # get the status: natural person or corporation -        DEFAULT_STATUS = 'natural' -        current_status = '' -        if 'data' in kwargs: -            # the order is important: PERSON can have an ORGA -            for field in [self.PERSON_FIELD, self.ORGA_FIELD]: -                current_item_key = ( -                    (kwargs['prefix'] + '-') -                    if kwargs.get('prefix') else '') + field -                if kwargs['data'] and kwargs['data'].get(current_item_key): -                    model = self.associated_models[field] -                    try: -                        model.objects.get( -                            pk=kwargs['data'][current_item_key]) -                        current_status = 'natural' \ -                            if field == self.PERSON_FIELD else 'corporation' -                    except (model.DoesNotExist, ValueError): -                        pass -        initial = kwargs.get("initial", {}) -        if not current_status: -            # the order is important: PERSON can have an ORGA -            for field in [self.ORGA_FIELD, self.PERSON_FIELD]: -                value = initial.get(field) -                model = self.associated_models[field] -                try: -                    model.objects.get(pk=value) -                    current_status = 'natural' if field == self.PERSON_FIELD \ -                        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 - -        self.status = status - -        if status not in ('natural', 'corporation'): -            status = DEFAULT_STATUS - -        user = None -        if 'user' in kwargs: -            user = kwargs.pop('user') - -        super(PersonOrgaForm, self).__init__(*args, **kwargs) - -        # distinct widget for natural and corporation -        if status == 'natural': -            self.fields[self.PERSON_FIELD] = forms.IntegerField( -                label=self.PERSON_LABEL, -                required=False, -                initial=initial.get(self.PERSON_FIELD, None), -                widget=widgets.JQueryPersonOrganization( -                    reverse_lazy('autocomplete-person', -                                 args=[self.PERSON_TYPE_PK]), -                    reverse_lazy('person_create'), -                    model=Person, -                    limit={'person_types': [self.PERSON_TYPE_PK], -                           'attached_to__isnull': True}, -                    js_template='ishtar/blocks/JQueryNaturalPerson.js', -                    new=True), -                validators=[valid_id(Person)]) -            self.fields.pop(self.ORGA_FIELD) - - -class FileFormPlanningService(CustomForm, forms.Form): -    form_label = _(u"Planning service") +    def clean(self): +        general_contractor = self.cleaned_data["general_contractor"] +        corporation_general_contractor = self.cleaned_data[ +            "corporation_general_contractor"] +        if general_contractor: +            try: +                person = models.Person.objects.get(pk=general_contractor) +            except models.Person.DoesNotExist: +                raise forms.ValidationError(_("Non existing person.")) +            if person.attached_to.pk != corporation_general_contractor: +                raise forms.ValidationError(_( +                    "The organization of the person in charge differs from the " +                    "general contractor.")) +        return self.cleaned_data + + +class FileFormPlanningService(CustomForm, IshtarForm): +    form_label = _("Planning service")      form_admin_name = _("Archaeological file - 040 - Planning service")      form_slug = "file-040-planningservice" +    extra_form_modals = ["person", "organization"]      associated_models = {'responsible_town_planning_service': models.Person,                           'planning_service': models.Organization} -    permit_reference = forms.CharField(label=_(u"File reference"), -                                       required=False, max_length=200)      planning_service = forms.IntegerField(          label=_("Planning service"),          required=False, -        widget=widgets.JQueryPersonOrganization( +        widget=widgets.JQueryAutoComplete(              reverse_lazy(                  'autocomplete-organization',                  args=[organization_type_pks_lazy(['planning_service'])]), -            reverse_lazy('organization_create'), -            model=models.Organization, +            associated_model=models.Organization,              limit={                  'organization_type':                  [organization_type_pk_lazy(['planning_service'])],              }, -            js_template='ishtar/blocks/JQueryCorporationPerson.js', -            new=True), +            tips=get_orga_planning_service_label, +            new=True, +            detail=True, +            modify=True, +        ),          validators=[valid_id(models.Organization)]      )      responsible_town_planning_service = forms.IntegerField( @@ -398,21 +347,39 @@ class FileFormPlanningService(CustomForm, forms.Form):                  person_type_pk_lazy('responsible_planning_service')              ]},              dynamic_limit=['planning_service'], -            url_new='new-person-noorga', +            tips=get_responsible_planning_service_label, +            detail=True, +            modify=True,              new=True),          validators=[valid_id(Person)]      ) +    permit_reference = forms.CharField(label=_(u"File reference"), +                                       required=False, max_length=200) + +    def clean(self): +        responsible = self.cleaned_data["responsible_town_planning_service"] +        orga = self.cleaned_data["planning_service"] +        if responsible: +            try: +                person = models.Person.objects.get(pk=responsible) +            except models.Person.DoesNotExist: +                raise forms.ValidationError(_("Non existing person.")) +            if person.attached_to.pk != orga: +                raise forms.ValidationError(_( +                    "The organization of the person in charge differs from the " +                    "planning service.")) +        return self.cleaned_data  class FileFormInstruction(CustomForm, IshtarForm): -    form_label = u"Instruction SRA" +    form_label = _("Instruction")      form_admin_name = _("Archaeological file - 050 - Instruction")      form_slug = "file-050-instruction"      extra_form_modals = ["person", "organization"]      associated_models = {'in_charge': models.Person,                           'related_file': models.File}      in_charge = forms.IntegerField( -        label=_("Person in charge"), +        label=_("File managed by"),          widget=widgets.JQueryAutoComplete(              reverse_lazy(                  'autocomplete-person', @@ -421,7 +388,9 @@ class FileFormInstruction(CustomForm, IshtarForm):                  'person_types': [                      person_type_pk_lazy('sra_agent')]              }, -            associated_model=Person, new=True), +            tips=get_sra_agent_label, +            associated_model=Person, new=True, +        ),          validators=[valid_id(Person)])      related_file = forms.IntegerField(          label=_("Related file"), required=False, diff --git a/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html b/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html deleted file mode 100644 index 169b2757e..000000000 --- a/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "ishtar/wizard/wizard_person_orga.html" %} -{% load replace_underscore %} - -{% block corporation %} -  <tr class='required'> -    <th>{{ wizard.form.corporation_general_contractor.label_tag }}</th> -    <td> {{ wizard.form.corporation_general_contractor.errors }}{{wizard.form.corporation_general_contractor|safe}}</td> -  </tr> -{% endblock %} - -{% block natural %} -  <tr class='required'> -    <th>{{ wizard.form.general_contractor.label_tag }}</th> -    <td> {{ wizard.form.general_contractor.errors }}{{wizard.form.general_contractor|safe}}</td> -  </tr> -{% endblock %} -{% block extra_head_po %} -<script type='text/javascript'> -$(function() { -var corp_id = '#id_{{wizard.form.prefix}}-corporation_general_contractor'; -var pers_id = "#id_select_{{wizard.form.prefix}}-general_contractor" ; -var pers_select_id = "#id_select_{{wizard.form.prefix}}-general_contractor" ; -$(corp_id).change( -  function(){ -    $(pers_select_id).autocomplete( -        "option", "source", -        source_{{wizard.form.prefix|replace_underscore}}_general_contractor -            + $(corp_id).val() + '/'); -    $(pers_select_id).val(""); -    $(pers_id).val(""); -    if ($(corp_id).val()){ -        $(pers_select_id).prop("disabled", false); -    } else { -        $(pers_select_id).prop("disabled", true); -    } -}); -}); -</script> -{% endblock %} diff --git a/archaeological_files_pdl/templates/ishtar/wizard/wizard_instruction.html b/archaeological_files_pdl/templates/ishtar/wizard/wizard_instruction.html index 72dc1d35d..34db944aa 100644 --- a/archaeological_files_pdl/templates/ishtar/wizard/wizard_instruction.html +++ b/archaeological_files_pdl/templates/ishtar/wizard/wizard_instruction.html @@ -1,76 +1,48 @@  {% extends "ishtar/wizard/default_wizard.html" %}  {% load i18n range table_form %} -{% block wizard_form %} -<form action="." method="post" name='wizard'{% if wizard.form.file_upload %} enctype="multipart/form-data"{% endif %}>{% csrf_token %} -<div class='form'> -{{ wizard.form.media }} -{{ wizard.management_form }} -<table> +{% block form_detail %} +  <div class="form-row"> +    {% with wizard.form.in_charge as field %} +    {% include "blocks/bs_field_snippet.html" %} +    {% endwith %} +    {% with wizard.form.related_file as field %} +    {% include "blocks/bs_field_snippet.html" %} +    {% endwith %} +    {% with wizard.form.comment as field %} +    {% include "blocks/bs_field_snippet.html" %} +    {% endwith %} -<tr class='required'> -  <th><label for="id_instruction-{{CURRENT_ACTION}}-in_charge">Dossier suivi par</label></th> -</tr> -<tr class='required'> -  <td>{{wizard.form.in_charge.errors}}{{wizard.form.in_charge|safe}}</td> -</tr> - -<tr> -  <th><label for="id_instruction-{{CURRENT_ACTION}}-related_file">Dossier lié à</label></th> -</tr> -<tr> -  <td>{{wizard.form.related_file|safe}}</td> -</tr> - -<tr> -  <th><label for="id_instruction-{{CURRENT_ACTION}}-comment">Commentaire</label></th> -</tr> -<tr> -  <td>{{wizard.form.comment|safe}}</td> -</tr> - -<tr class='required'> -  <th><label>État du dossier</label></th> -</tr> -<tr> -  <td><input type='radio' name='state' value='open' id='state-open'/> <label for='state-open'>Dossier actif</label></td> -</tr> -<tr> -  <td>{{wizard.form.end_date.errors}}<input type='radio' name='state' value='closed' id='state-closed'/> <label for='state-closed'>Dossier clos / date de clôture</label> : {{wizard.form.end_date|safe}}</td> -</tr> - -<tr class='required'> -  <th><label for="id_instruction-{{CURRENT_ACTION}}-instruction_deadline">Date limite d'instruction</label></th> -</tr> -{% if saisine_type %}<tr> -  <th><em>{{ saisine_type }}{% if saisine_type_delay %} : délai de {{saisine_type_delay}} jours{% endif %}</em></th> -</tr>{% endif %} -<tr class='required'> -  <td>{{wizard.form.instruction_deadline.errors}}{{wizard.form.instruction_deadline|safe}}</td> -</tr> - -<tr class='required'> -  <th><label for="id_instruction-{{CURRENT_ACTION}}-year">{{wizard.form.numeric_reference.label}}</label></th> -</tr> -<tr> -  <td>{{wizard.form.numeric_reference.errors}}SRA <span class='small'>{{wizard.form.year|safe}}</span> - <span class='small'>{{wizard.form.numeric_reference|safe}}</span></td> -</tr> -</table> - -<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> -{{ previous_fields|safe }} -  {% block "footer" %} -  <div id="footer"> -    {% block "validation_bar" %} -    {% include 'ishtar/wizard/validation_bar.html' %} -    {% endblock %} -    {% include 'ishtar/blocks/footer.html' %} +    <div class="form-group col-lg-6"> +      <span class="required"> +        <label class="required">{% trans "State of the file:" %}</label> +      </span> +        <div class="form-group col-lg-12 mb-0"> +          <span><input type='radio' name='state' value='open' id='state-open'/> +          <label for='state-open'>Dossier actif</label></span> +        </div> +        <div class="form-group col-lg-12"> +          <span>{{wizard.form.end_date.errors}}<input type='radio' name='state' value='closed' id='state-closed'/> +            <label for='state-closed'>Dossier clos / date de clôture</label> : {{wizard.form.end_date|safe}}</span> +        </div> +    </div> +    {% with wizard.form.instruction_deadline as field %}{% with saisine_type_message as extra_field_label %} +    {% include "blocks/bs_field_snippet.html" %} +    {% endwith %}{% endwith %} +    <div class="form-group col-lg-6"> +      <span class="required"> +        <label>{{wizard.form.numeric_reference.label}}{% trans ":" %}</label> +      </span> +      <div class="form-inline"> +        {{wizard.form.numeric_reference.errors}} +        {% if FILE_PREFIX %}<span class="pr-2">{{FILE_PREFIX}}</span>{% endif %} +        <span class="pr-2">{{wizard.form.year|safe}}</span> - +        <span class="pl-2">{{wizard.form.numeric_reference|safe}}</span> +      </div> +    </div>    </div> -  {% endblock %} -</div> -</form> -<script type='text/javascript'> -$(function(){ +{% endblock %} +{% block "js_extra_ready" %}      if ($('#id_instruction-{{CURRENT_ACTION}}-end_date').val()){          $("#state-closed").prop('checked', true);      } else { @@ -96,18 +68,16 @@ $(function(){          var state = $("input[name=state]:checked").val();          if (state == 'closed'){              if (!$('#id_instruction-{{CURRENT_ACTION}}-end_date').val()){ -                alert("Vous devez sélectionner une date de clôture.") +                alert("{% trans 'You must select a closing date.' %}")                  return false;              }              return true;          } else if (state == 'open'){              return true;          } else { -            alert("Vous devez choisir un état pour ce dossier.") +            alert("{% trans 'You must select a state for this file.' %}")              return false;          }          return true;      }); -}); -</script> -{% endblock %} +    {% endblock %} diff --git a/archaeological_files_pdl/templates/ishtar/wizard/wizard_planningservice.html b/archaeological_files_pdl/templates/ishtar/wizard/wizard_planningservice.html deleted file mode 100644 index 5eb7c014b..000000000 --- a/archaeological_files_pdl/templates/ishtar/wizard/wizard_planningservice.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends "ishtar/wizard/default_wizard.html" %} -{% load i18n range table_form %} -{% block wizard_form %} -<form action="." method="post" name='wizard'{% if wizard.form.file_upload %} enctype="multipart/form-data"{% endif %}>{% csrf_token %} -<div class='form'> -{% if wizard.form.media %}{{ wizard.form.media }}{% endif %} -{{ wizard.management_form }} - -<table> -  {% if wizard.form.non_field_errors %}<tr class='errors'> -    <td colspan='3'>{{wizard.form.non_field_errors}}</td> -  </tr>{%endif%} - -  <tr> -    <th>{{ wizard.form.planning_service.label_tag }}</th> -    <td> {{ wizard.form.planning_service.errors }}{{wizard.form.planning_service|safe}}</td> -  </tr> -  <tr> -    <th>{{ wizard.form.responsible_town_planning_service.label_tag }}</th> -    <td> {{ wizard.form.responsible_town_planning_service.errors }}{{wizard.form.responsible_town_planning_service|safe}}</td> -  </tr> -</table> - - -<div> -<table> -  {% if permit_type %} -  <tr> -    <th colspan='3'>{{permit_type}}</th> -  </tr>{% endif %} -  <tr> -    <th>{{ wizard.form.permit_reference.label_tag }}{% if permit_type_code %} [{{permit_type_code}}]{% endif %}</th> -    <td> {{ wizard.form.permit_reference.errors }}{{wizard.form.permit_reference|safe}}</td> -  </tr> -</table> -</div> - -<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> -{{ previous_fields|safe }} -  {% block "footer" %} -  <div id="footer"> -    {% block "validation_bar" %} -    {% include 'ishtar/wizard/validation_bar.html' %} -    {% endblock %} -    {% include 'ishtar/blocks/footer.html' %} -  </div> -  {% endblock %} -</div> -</form> -{% endblock %} - - - diff --git a/archaeological_files_pdl/wizards.py b/archaeological_files_pdl/wizards.py index d13811eea..988303937 100644 --- a/archaeological_files_pdl/wizards.py +++ b/archaeological_files_pdl/wizards.py @@ -17,9 +17,13 @@  # See the file COPYING for details. +from django.conf import settings +  from archaeological_files.wizards import FileWizard as BaseFileWizard  from archaeological_files import models +from ishtar_common.utils import ugettext_lazy as _ +  class FileWizard(BaseFileWizard):      parcel_step_key = 'parcelspdl-' @@ -28,8 +32,8 @@ class FileWizard(BaseFileWizard):      towns_formset = False      multi_towns = True      wizard_templates = { -        'generalcontractor-%(url_name)s': -            'ishtar/wizard/wizard_generalcontractor.html', +        #'generalcontractor-%(url_name)s': +        #    'ishtar/wizard/wizard_generalcontractor.html',          'planningservice-%(url_name)s':              'ishtar/wizard/wizard_planningservice.html',          'instruction-%(url_name)s': @@ -82,9 +86,13 @@ class FileWizard(BaseFileWizard):                  pass          elif self.steps.current == forminstruction:              saisine_type = self.get_saisine_type() +            context['FILE_PREFIX'] = settings.ISHTAR_FILE_PREFIX              if saisine_type:                  context['saisine_type'] = str(saisine_type) -                context['saisine_type_delay'] = saisine_type.delay or 0 +                context['saisine_type_message'] = str(saisine_type) +                if saisine_type.delay: +                    context['saisine_type_message'] += str(_( +                        ": delay of {} days")).format(saisine_type.delay)          elif self.steps.current == formfinal:              if self.steps.current.endswith('creation'):  # creation only                  parcels = [] diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 90805e656..843d082fe 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -55,7 +55,7 @@ from bootstrap_datepicker.widgets import DatePicker  from ishtar_common import widgets  from ishtar_common.forms import CustomForm, CustomFormSearch, FormSet, \      FloatField, reverse_lazy, TableSelect, get_now, FinalForm, \ -    ManageOldType, FieldType, IshtarForm, FormHeader, QAForm, HistorySelect, \ +    ManageOldType, FieldType, IshtarForm, FormHeader, QAForm, \      MultiSearchForm, LockForm, DocumentItemSelect  from ishtar_common.forms_common import get_town_field  from ishtar_common.models import valid_id, valid_ids, get_current_profile, \ diff --git a/example_project/settings.py b/example_project/settings.py index a5d1d3b3e..24c3bee35 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -254,6 +254,8 @@ ISHTAR_MAP_MAX_ITEMS = 50000  ISHTAR_QRCODE_VERSION = 6  # density of the QR code  ISHTAR_QRCODE_SCALE = 2  # scale of the QR code +ISHTAR_FILE_PREFIX = "" +  SRID = 4326  # WGS84 - World  SURFACE_SRID = 4326  # WGS84 - World  ENCODING = 'windows-1252' diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 2b52f0919..2df825594 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -315,7 +315,7 @@ class TargetKeyFormset(BaseModelFormSet):  class OrganizationForm(ManageOldType, NewItemForm):      prefix = 'organization' -    form_label = _(u"Organization") +    form_label = _("Organization")      associated_models = {'organization_type': models.OrganizationType,                           "precise_town": models.Town}      name = forms.CharField( @@ -348,7 +348,7 @@ class OrganizationForm(ManageOldType, NewItemForm):              models.OrganizationType.get_help()          self.limit_fields() -    def save(self, user): +    def save(self, user, item=None):          dct = self.cleaned_data          dct['history_modifier'] = user          dct['organization_type'] = models.OrganizationType.objects.get( @@ -359,7 +359,12 @@ class OrganizationForm(ManageOldType, NewItemForm):                      pk=dct["precise_town"])              except models.Town.DoesNotExist:                  dct.pop("precise_town") -        new_item = models.Organization(**dct) +        if not item: +            new_item = models.Organization(**dct) +        else: +            new_item = item +            for k in dct: +                setattr(new_item, k, dct[k])          new_item.save()          return new_item @@ -792,7 +797,7 @@ class PersonForm(SimplePersonForm):          self.fields['person_types'].help_text = models.PersonType.get_help()          self.limit_fields() -    def save(self, user): +    def save(self, user, item=None):          dct = self.cleaned_data          dct['history_modifier'] = user          for key in self.associated_models.keys(): @@ -806,7 +811,14 @@ class PersonForm(SimplePersonForm):                      except model.DoesNotExist:                          dct.pop(key)          person_types = dct.pop('person_types') -        new_item = models.Person.objects.create(**dct) +        if not item: +            new_item = models.Person.objects.create(**dct) +        else: +            for k in dct: +                setattr(item, k, dct[k]) +            item.save() +            item.person_types.clear() +            new_item = item          for pt in person_types:              new_item.person_types.add(pt)          return new_item diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 5fa668faf..9b446ff9e 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -1046,6 +1046,13 @@ class GeneralType(Cached, models.Model):              item.generate_key() +def get_general_type_label(model, slug): +    obj = model.get_cache(slug) +    if not obj: +        return "" +    return str(obj) + +  class HierarchicalType(GeneralType):      parent = models.ForeignKey('self', blank=True, null=True,                                 on_delete=models.SET_NULL, @@ -3971,6 +3978,46 @@ class Address(BaseHistorizedItem):      class Meta:          abstract = True +    def get_short_html_items(self): +        items = [] +        if self.address: +            items.append( +                """<span class="subadress">{}</span>""".format(self.address)) +        if self.address_complement: +            items.append( +                """<span class="subadress-complement">{}</span>""".format( +                    self.address_complement)) +        if self.postal_code: +            items.append( +                """<span class="postal-code">{}</span>""".format( +                    self.postal_code)) +        if self.precise_town: +            items.append( +                """<span class="town">{}</span>""".format( +                    self.precise_town.name)) +        elif self.town: +            items.append( +                """<span class="town">{}</span>""".format( +                    self.town)) +        if self.country: +            items.append( +                """<span class="country">{}</span>""".format( +                    self.country)) +        return items + +    def get_short_html_detail(self): +        html = """<div class="address">""" +        items = self.get_short_html_items() +        if not items: +            items = [ +                "<span class='no-address'>{}</span>".format( +                    _("No associated address") +                ) +            ] +        html += "".join(items) +        html += """</div>""" +        return html +      def get_town_centroid(self):          if self.precise_town:              return self.precise_town.center, self._meta.verbose_name @@ -4138,6 +4185,20 @@ class OrganizationType(GeneralType):          ordering = ('label',) +def get_orga_planning_service_label(): +    lbl = get_general_type_label(OrganizationType, "planning_service") +    if lbl: +        return lbl +    return _("Error: planning_service type is missing") + + +def get_orga_general_contractor_label(): +    lbl = get_general_type_label(OrganizationType, "general_contractor") +    if lbl: +        return lbl +    return _("Error: general_contractor type is missing") + +  post_save.connect(post_save_cache, sender=OrganizationType)  post_delete.connect(post_save_cache, sender=OrganizationType) @@ -4259,6 +4320,27 @@ person_type_pk_lazy = lazy(PersonType.get_or_create_pk, str)  person_type_pks_lazy = lazy(PersonType.get_or_create_pks, str) +def get_sra_agent_label(): +    lbl = get_general_type_label(PersonType, "sra_agent") +    if lbl: +        return lbl +    return _("Error: sra_agent type is missing") + + +def get_general_contractor_label(): +    lbl = get_general_type_label(PersonType, "general_contractor") +    if lbl: +        return lbl +    return _("Error: general_contractor type is missing") + + +def get_responsible_planning_service_label(): +    lbl = get_general_type_label(PersonType, "responsible_planning_service") +    if lbl: +        return lbl +    return _("Error: responsible_planning_service type is missing") + +  class TitleType(GeneralType):      class Meta:          verbose_name = _("Title type") @@ -4407,6 +4489,19 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem):          profile.save()          return profile +    def get_short_html_items(self): +        items = super(Person, self).get_short_html_items() +        if items or not self.attached_to: +            return items +        orga_address = self.attached_to.get_short_html_items() +        if not orga_address: +            return [] +        items.append( +            """<span class="organization">{}</span>""".format( +                self.attached_to.name)) +        items += orga_address +        return items +      def simple_lbl(self):          values = [str(getattr(self, attr)) for attr in ('surname', 'name')                    if getattr(self, attr)] diff --git a/ishtar_common/static/js/ishtar.js b/ishtar_common/static/js/ishtar.js index b8a9bf9ce..60ebb0bc9 100644 --- a/ishtar_common/static/js/ishtar.js +++ b/ishtar_common/static/js/ishtar.js @@ -706,15 +706,21 @@ function save_and_close_window_data(main_page, name_label,                                      name_pk, item_name, item_pk){      var cur_item;      var cur_item_pk; +    var id_item;      if (main_page){ -        cur_item = $(main_page).find("#"+name_label); -        cur_item_pk = $(main_page).find("#"+name_pk); +        cur_item = $(main_page).find("#" + name_label); +        cur_item_pk = $(main_page).find("#" + name_pk); +        id_item = $(main_page).find("#id_" + name_label);      } else { -        cur_item = $("#"+name_label); -        cur_item_pk = $("#"+name_pk); +        cur_item = $("#" + name_label); +        cur_item_pk = $("#" + name_pk); +        id_item = $("#id_" + name_label);      }      cur_item.val(item_name); +    cur_item.change();      cur_item_pk.val(item_pk); +    cur_item_pk.change(); +    id_item.change();  }  function save_and_close_window_many(name_label, name_pk, item_name, item_pk){ diff --git a/ishtar_common/templates/blocks/JQueryAutocomplete.js b/ishtar_common/templates/blocks/JQueryAutocomplete.js index 7cd0e2a60..aaf493db9 100644 --- a/ishtar_common/templates/blocks/JQueryAutocomplete.js +++ b/ishtar_common/templates/blocks/JQueryAutocomplete.js @@ -52,7 +52,7 @@ $(function() {  	{{safe_field_id}}_modify = function(){          var current_val = $('#id_{{field_id}}').val();          if (current_val){ -			dt_qa_open('/modify-{{model_name}}/' + current_val + "/", +			dt_qa_open('/modify-{{model_name}}/{{field_id}}/' + current_val + "/",  					   'modal-dynamic-form-{{model_name}}');  	    }  	} diff --git a/ishtar_common/templates/blocks/bs_field_snippet.html b/ishtar_common/templates/blocks/bs_field_snippet.html index 644f7f433..dcee87f7f 100644 --- a/ishtar_common/templates/blocks/bs_field_snippet.html +++ b/ishtar_common/templates/blocks/bs_field_snippet.html @@ -2,6 +2,7 @@      <div id="main_div-{{field.auto_id}}" class="form-group{% if not field.label %} no-label{% endif %} {% if field.field.widget.attrs.cols or force_large_col %}col-lg-12{% else %}col-lg-6{% endif %}{% if field.errors %} is-invalid{% endif %}{% if field.field.required %} required{% endif %}{% if force_large_col %} full-width{% endif %}"           data-alt-name="{{field.field.alt_name}}">          {% if field.label %}{{ field.label_tag }}{% endif %} +        {% if extra_field_label %}<label><em>{{extra_field_label}}</em></label>{% endif %}          {% if show_field_number or form.show_field_number %}          {% if field.field.order_number %}<span class="badge badge-pill badge-success field-tip">              {{field.field.order_number}} diff --git a/ishtar_common/templates/ishtar/forms/qa_new_item.html b/ishtar_common/templates/ishtar/forms/qa_new_item.html index ae57adfc2..435531594 100644 --- a/ishtar_common/templates/ishtar/forms/qa_new_item.html +++ b/ishtar_common/templates/ishtar/forms/qa_new_item.html @@ -3,7 +3,8 @@  {% block main_form %}  {% if new_item_pk %} -<p>{% if new_item_label %}{{new_item_label}}{% else %}{% trans "Item" %}{% endif %} {% trans "created." %}</p> +<p>{% if new_item_label %}{{new_item_label}}{% else %}{% trans "Item" %}{% endif %} +    {% if modify %}{% trans "modified." %}{% else %}{% trans "created." %}{% endif %}</p>  {% else %}      {% bs_compact_form form %}  {% endif %} diff --git a/ishtar_common/templates/ishtar/wizard/default_wizard.html b/ishtar_common/templates/ishtar/wizard/default_wizard.html index b5ea4207c..cfd17e58f 100644 --- a/ishtar_common/templates/ishtar/wizard/default_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/default_wizard.html @@ -62,6 +62,8 @@ $(document).ready(function(){          return false;      });      {% if open_url %}load_window("{{open_url}}");{% endif %} +    {% block "js_extra_ready" %} +    {% endblock %}  });  </script>  {% endblock %} diff --git a/ishtar_common/templates/ishtar/wizard/parcels_wizard.html b/ishtar_common/templates/ishtar/wizard/parcels_wizard.html index 2028d8792..d8c902c72 100644 --- a/ishtar_common/templates/ishtar/wizard/parcels_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/parcels_wizard.html @@ -22,13 +22,16 @@    {% bs_form wizard.form.selection_form %}  <table class='inline-table' id='parcel-table'> -  <tr>{% for field in wizard.form.forms.0 %}<th{% if not forloop.last %} rowspan='2'{% endif %}>{{ field.label_tag }}</th>{% endfor %}</tr> +  <tr>{% for field in wizard.form.forms.0 %} +    {% if field.required %}<th{%else%}<td{% endif %}{% if not forloop.last %} rowspan='2'{% endif %}> +    {{ field.label_tag }}{% if field.required %}</th>{%else%}</td>{% endif %}{% endfor %} +  </tr>    <tr><td>({% trans "all"%} <input type='checkbox' name='check-all' class='check-all'/>)</td></tr>    {% inline_formset 'Parcels' wizard.form.forms False %}  </table>  {% if add_all %}<p><input type='checkbox' name='add_all_parcels' id='add_all_parcels'> <label for='add_all_parcels'>{% trans "Add all parcels from the archaeological file" %}</label></p>{% endif %} -  <div class="text-center"> +  <div class="mt-3 text-center">        <button class="btn btn-success" name="formset_modify" value="{{wizard.steps.current}}">          {% trans "Add/Modify" %}        </button> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index cc8d3eed4..8b506c55c 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -206,6 +206,14 @@ urlpatterns += [          views.get_by_importer, name='get-by-importer'),      url(r'new-person/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$',          views.new_person, name='new-person'), +    url(r'modify-person/(?:(?P<parent_name>[^/]+)/)?(?P<pk>[\d+]+)/$', +        views.modify_person, name='modify-person'), +    url(r'detail-person/(?P<pk>[\d+]+)/$', +        views.detail_person, name='detail-person'), +    url(r'modify-organization/(?:(?P<parent_name>[^/]+)/)?(?P<pk>[\d+]+)/$', +        views.modify_organization, name='modify-organization'), +    url(r'detail-organization/(?P<pk>[\d+]+)/$', +        views.detail_organization, name='detail-organization'),      url(r'new-person-noorga/'          r'(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$',          views.new_person_noorga, name='new-person-noorga'), diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 4724d61b7..b662c1a4d 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -70,7 +70,8 @@ from ishtar_common.widgets import JQueryAutoComplete  from ishtar_common import tasks  from .views_item import CURRENT_ITEM_KEYS, CURRENT_ITEM_KEYS_DICT, \ -    check_permission, display_item, get_item, show_item, new_qa_item +    check_permission, display_item, get_item, show_item, new_qa_item, \ +    modify_qa_item, get_short_html_detail  logger = logging.getLogger(__name__) @@ -791,11 +792,15 @@ def autocomplete_author(request):  new_person = new_qa_item(models.Person, forms.PersonForm) +modify_person = modify_qa_item(models.Person, forms.PersonForm) +detail_person = get_short_html_detail(models.Person)  new_person_noorga = new_qa_item(models.Person, forms.NoOrgaPersonForm)  new_organization = new_qa_item(models.Organization, forms.OrganizationForm)  show_organization = show_item(models.Organization, 'organization')  get_organization = get_item(models.Organization, 'get_organization',                              'organization') +modify_organization = modify_qa_item(models.Organization, forms.OrganizationForm) +detail_organization = get_short_html_detail(models.Organization)  new_author = new_qa_item(models.Author, forms.AuthorForm)  show_person = show_item(models.Person, 'person') diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 8cd903a00..aa9a39965 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -22,6 +22,7 @@ from django.core.urlresolvers import reverse, NoReverseMatch  from django.db.models import Q, Count, Sum, ImageField, Func, \      ExpressionWrapper, FloatField, FileField  from django.db.models.fields import FieldDoesNotExist +from django.forms.models import model_to_dict  from django.http import HttpResponse  from django.shortcuts import render  from django.template import loader @@ -146,7 +147,7 @@ def new_qa_item(model, frm, many=False):          template = "ishtar/forms/qa_new_item.html"          model_name = model._meta.object_name          if not check_permission(request, 'add_' + model_name.lower()): -            not_permitted_msg = ugettext(u"Operation not permitted.") +            not_permitted_msg = ugettext("Operation not permitted.")              return HttpResponse(not_permitted_msg)          slug = model.SLUG          if model.SLUG == "site": @@ -177,6 +178,68 @@ def new_qa_item(model, frm, many=False):      return func +def get_short_html_detail(model): +    def func(request, pk): +        model_name = model._meta.object_name +        not_permitted_msg = ugettext("Operation not permitted.") +        if not check_permission(request, 'view_' + model_name.lower(), +                                pk): +            return HttpResponse(not_permitted_msg) +        try: +            item = model.objects.get(pk=pk) +        except model.DoesNotExist: +            return HttpResponse(not_permitted_msg) +        html = item.get_short_html_detail() +        return HttpResponse(html) +    return func + + +def modify_qa_item(model, frm): +    def func(request, parent_name="", pk=None): +        template = "ishtar/forms/qa_new_item.html" +        model_name = model._meta.object_name +        not_permitted_msg = ugettext("Operation not permitted.") +        if not check_permission(request, 'change_' + model_name.lower(), +                                pk): +            return HttpResponse(not_permitted_msg) +        slug = model.SLUG +        if model.SLUG == "site": +            slug = "archaeologicalsite" +        try: +            item = model.objects.get(pk=pk) +        except model.DoesNotExist: +            return HttpResponse(not_permitted_msg) +        url_slug = "modify-" + slug +        dct = {'page_name': str(_('Modify a %s' % model_name.lower())), +               'url': reverse(url_slug, args=[parent_name, pk]), +               'slug': slug, +               "modify": True, +               'parent_name': parent_name} +        if request.method == 'POST': +            dct['form'] = frm(request.POST) +            if dct['form'].is_valid(): +                new_item = dct['form'].save(request.user, item) +                lbl = str(new_item) +                if not lbl and hasattr(new_item, "_generate_cached_label"): +                    lbl = new_item._generate_cached_label() +                dct['new_item_label'] = lbl +                dct['new_item_pk'] = new_item.pk +                dct['parent_pk'] = parent_name +                if dct['parent_pk'] and '_select_' in dct['parent_pk']: +                    parents = dct['parent_pk'].split('_') +                    dct['parent_pk'] = "_".join([parents[0]] + parents[2:]) +                return render(request, template, dct) +        else: +            data = model_to_dict(item) +            for k in list(data.keys()): +                if data[k] and isinstance(data[k], list) and hasattr( +                        data[k][0], "pk"): +                    data[k] = [i.pk for i in data[k]] +            dct['form'] = frm(initial=data) +        return render(request, template, dct) +    return func + +  def display_item(model, extra_dct=None, show_url=None):      def func(request, pk, **dct):          if show_url:  | 
