diff options
| -rw-r--r-- | archaeological_context_records/models.py | 7 | ||||
| -rw-r--r-- | archaeological_context_records/wizards.py | 3 | ||||
| -rw-r--r-- | archaeological_files/models.py | 2 | ||||
| -rw-r--r-- | archaeological_files/views.py | 4 | ||||
| -rw-r--r-- | archaeological_files/wizards.py | 51 | ||||
| -rw-r--r-- | archaeological_finds/models.py | 8 | ||||
| -rw-r--r-- | archaeological_operations/forms.py | 1 | ||||
| -rw-r--r-- | archaeological_operations/models.py | 10 | ||||
| -rw-r--r-- | archaeological_operations/wizards.py | 12 | ||||
| -rw-r--r-- | ishtar_common/models.py | 15 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/wizard/search.html (renamed from ishtar_common/templates/search.html) | 13 | ||||
| -rw-r--r-- | ishtar_common/urls.py | 2 | ||||
| -rw-r--r-- | ishtar_common/wizards.py | 878 | 
13 files changed, 946 insertions, 60 deletions
| diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index 75653e78e..64772627b 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -143,7 +143,7 @@ class ContextRecord(BaseHistorizedItem, OwnPerms):          return self.short_label()      def short_label(self): -        return JOINT.join([unicode(item) for item in [self.parcel, +        return settings.JOINT.join([unicode(item) for item in [self.parcel,                                                     self.label] if item])      def full_label(self): @@ -154,13 +154,14 @@ class ContextRecord(BaseHistorizedItem, OwnPerms):      def _real_label(self):          if not self.parcel.operation.code_patriarche:              return -        return JOINT.join((self.parcel.operation.code_patriarche, +        return settings.JOINT.join((self.parcel.operation.code_patriarche,                             self.label))      def _temp_label(self):          if self.parcel.operation.code_patriarche:              return -        return JOINT.join([unicode(lbl) for lbl in [self.parcel.operation.year, +        return settings.JOINT.join([unicode(lbl) for lbl in [ +                                           self.parcel.operation.year,                                             self.parcel.operation.operation_code,                                             self.label] if lbl]) diff --git a/archaeological_context_records/wizards.py b/archaeological_context_records/wizards.py index b14272e16..1fd657bcc 100644 --- a/archaeological_context_records/wizards.py +++ b/archaeological_context_records/wizards.py @@ -73,7 +73,8 @@ class RecordWizard(Wizard):          else:              data = {}          if not step: -            step = self.determine_step(request, storage) +            step = self.steps.current +            #step = self.determine_step(request, storage)          form = self.get_form_list(request, storage)[step]          general_form_key = 'general-' + self.url_name diff --git a/archaeological_files/models.py b/archaeological_files/models.py index c3d950d3e..0bc9a9566 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -131,7 +131,7 @@ class File(BaseHistorizedItem, OwnPerms):                                 unicode(self.numeric_reference))))          items += [unicode(getattr(self, k))[:36]                    for k in ['internal_reference',] if getattr(self, k)] -        return JOINT.join(items) +        return settings.JOINT.join(items)      @classmethod      def get_query_owns(cls, user): diff --git a/archaeological_files/views.py b/archaeological_files/views.py index 0f71b24d8..84a42b95a 100644 --- a/archaeological_files/views.py +++ b/archaeological_files/views.py @@ -78,8 +78,8 @@ def dashboard_file(request, dct, obj_id=None, *args, **kwargs):                                context_instance=RequestContext(request))  file_search_wizard = SearchWizard.as_view( -                                [('general-file_search', FileFormSelection)], -                                url_name='file_search',) +                            [('general-file_search', FileFormSelection)], +                            url_name='file_search',)  file_creation_wizard = FileWizard.as_view([                              ('general-file_creation', FileFormGeneral),                              ('address-file_creation', FileFormAddress), diff --git a/archaeological_files/wizards.py b/archaeological_files/wizards.py index b161deae0..e98248965 100644 --- a/archaeological_files/wizards.py +++ b/archaeological_files/wizards.py @@ -19,6 +19,7 @@  from django.conf import settings  from django.core.exceptions import ObjectDoesNotExist +from django.db.models import Max  from django.shortcuts import render_to_response  from django.template import RequestContext  from django.utils.translation import ugettext_lazy as _ @@ -26,14 +27,15 @@ from django.utils.translation import ugettext_lazy as _  from ishtar_common.wizards import Wizard, ClosingWizard  from archaeological_operations.wizards import OperationAdministrativeActWizard,\                                                AdministrativeActDeletionWizard -from archaeological_operations.models import AdministrativeAct +from ishtar_common.models import Town +from archaeological_operations.models import AdministrativeAct, Parcel  import models  class FileWizard(Wizard):      model = models.File      object_parcel_type = 'associated_file' -    def get_form(self, request, storage, step=None, data=None, files=None): +    def get_form(self, step=None, data=None, files=None):          """          Manage towns          """ @@ -43,40 +45,40 @@ class FileWizard(Wizard):              data = {}          # manage the dynamic choice of towns          if not step: -            step = self.determine_step(request, storage) -        form = self.get_form_list(request, storage)[step] +            # step = self.determine_step(request, storage) +            step = self.steps.current +        form = self.get_form_list()[step]          town_form_key = 'towns-' + self.url_name          if step.startswith('parcels-') and hasattr(form, 'management_form') \ -           and self.session_has_key(request, storage, town_form_key): +           and self.session_has_key(self.request, self.storage, town_form_key):              towns = [] -            qdict = request.session[storage.prefix]['step_data'][town_form_key] +            qdict = self.request.session[self.storage.prefix]['step_data']\ +                                        [town_form_key]              for k in qdict.keys():                  if k.endswith("town") and qdict[k]:                      try: -                        town = Town.objects.get(pk=int(qdict[k])) +                        town = Town.objects.get(pk=int(qdict[k][0]))                          towns.append((town.pk, unicode(town)))                      except (ObjectDoesNotExist, ValueError):                          pass              data['TOWNS'] = sorted(towns, key=lambda x:x[1]) -        form = super(FileWizard, self).get_form(request, storage, step, data, -                                                files) +        form = super(FileWizard, self).get_form(step, data, files)          return form -    def get_extra_model(self, dct, request, storage, form_list): -        dct = super(FileWizard, self).get_extra_model(dct, request, storage, -                                                      form_list) +    def get_extra_model(self, dct, form_list): +        dct = super(FileWizard, self).get_extra_model(dct, form_list)          if not dct['numeric_reference']:              current_ref = models.File.objects.filter(year=dct['year']                  ).aggregate(Max('numeric_reference'))["numeric_reference__max"]              dct['numeric_reference'] = current_ref and current_ref + 1 or 1          return dct -    def done(self, request, storage, form_list, **kwargs): +    def done(self, form_list, **kwargs):          '''          Save parcels          ''' -        r = super(FileWizard, self).done(request, storage, form_list, -                                         return_object=True, **kwargs) +        r = super(FileWizard, self).done(form_list, return_object=True, +                                         **kwargs)          if type(r) not in (list, tuple) or len(r) != 2:              return r          obj, res = r @@ -92,7 +94,7 @@ class FileWizard(Wizard):                  dct = frm.cleaned_data.copy()                  if 'parcel' in dct:                      try: -                        parcel = models.Parcel.objects.get(pk=dct['parcel']) +                        parcel = Parcel.objects.get(pk=dct['parcel'])                          setattr(parcel, self.object_parcel_type, obj)                          parcel.save()                      except (ValueError, ObjectDoesNotExist): @@ -106,10 +108,10 @@ class FileWizard(Wizard):                  dct[self.object_parcel_type] = obj                  if 'DELETE' in dct:                      dct.pop('DELETE') -                parcel = models.Parcel.objects.filter(**dct).count() +                parcel = Parcel.objects.filter(**dct).count()                  if not parcel: -                    dct['history_modifier'] = request.user -                    parcel = models.Parcel(**dct) +                    dct['history_modifier'] = self.request.user +                    parcel = Parcel(**dct)                      parcel.save()          return res @@ -125,6 +127,7 @@ class FileClosingWizard(ClosingWizard):      if settings.COUNTRY == 'fr':          fields += ['saisine_type', 'reference_number']      fields += ['towns'] +  class FileDeletionWizard(FileClosingWizard):      def get_formated_datas(self, forms):          datas = super(FileDeletionWizard, self).get_formated_datas(forms) @@ -135,14 +138,14 @@ class FileDeletionWizard(FileClosingWizard):                  datas[-1][1].append(('', unicode(operation)))          return datas -    def done(self, request, storage, form_list, **kwargs): -        obj = self.get_current_object(request, storage) +    def done(self, form_list, **kwargs): +        obj = self.get_current_object()          for operation in models.Operation.objects.filter(                                                     associated_file=obj).all():              operation.delete()          obj.delete()          return render_to_response('wizard_done.html', {}, -                                  context_instance=RequestContext(request)) +                                  context_instance=RequestContext(self.request))  class FileAdministrativeActWizard(OperationAdministrativeActWizard): @@ -151,5 +154,5 @@ class FileAdministrativeActWizard(OperationAdministrativeActWizard):  class FileEditAdministrativeActWizard(FileAdministrativeActWizard):      model = AdministrativeAct      edit = True -    def get_associated_item(self, request, storage, dct): -        return self.get_current_object(request, storage).associated_file +    def get_associated_item(self, dct): +        return self.get_current_object().associated_file diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index eeb293934..cb305a93a 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -79,14 +79,14 @@ class BaseFind(BaseHistorizedItem, OwnPerms):          finds += [ope.code_patriarche or \                    (unicode(ope.year) + "-" + unicode(ope.operation_code))]          finds += [self.context_record.label, unicode(self.material_index)] -        return JOINT.join(finds) +        return settings.JOINT.join(finds)      def _real_label(self):          if not self.context_record.parcel.operation.code_patriarche:              return          find = self.get_last_find()          lbl = find.label or self.label -        return JOINT.join([unicode(it) for it in ( +        return settings.JOINT.join([unicode(it) for it in (                             self.context_record.parcel.operation.code_patriarche,                             self.context_record.label,                             lbl) if it]) @@ -96,7 +96,7 @@ class BaseFind(BaseHistorizedItem, OwnPerms):              return          find = self.get_last_find()          lbl = find.label or self.label -        return JOINT.join([unicode(it) for it in ( +        return settings.JOINT.join([unicode(it) for it in (                             self.context_record.parcel.year,                             self.index,                             self.context_record.label, @@ -278,5 +278,5 @@ class Property(LightHistorizedItem):          verbose_name_plural = _(u"Properties")      def __unicode__(self): -        return self.person + JOINT + self.find +        return self.person + settings.JOINT + self.find diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py index d87e72423..dc342f477 100644 --- a/archaeological_operations/forms.py +++ b/archaeological_operations/forms.py @@ -29,6 +29,7 @@ from django.template import RequestContext  from django.core import validators  from django.core.exceptions import ObjectDoesNotExist  from django.db.models import Max +from django.forms.formsets import DELETION_FIELD_NAME  from django.utils.translation import ugettext_lazy as _  from ishtar_common.models import valid_id, PersonType, Person, Town diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 2c008ef9b..60de936d2 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -129,7 +129,7 @@ class Operation(BaseHistorizedItem, OwnPerms):              items[0] = unicode(self.towns.all()[0])          items.append("-".join((unicode(self.year),                                 unicode(self.operation_code)))) -        return JOINT.join(items) +        return settings.JOINT.join(items)      @classmethod      def get_available_operation_code(cls, year=None): @@ -301,7 +301,7 @@ related_name='+', verbose_name=_(u"Person in charge of the scientific part"))          )      def __unicode__(self): -        return JOINT.join([unicode(item) +        return settings.JOINT.join([unicode(item)            for item in [self.operation, self.associated_file, self.act_object]            if item]) @@ -322,7 +322,7 @@ class Parcel(LightHistorizedItem):          verbose_name_plural = _(u"Parcels")      def short_label(self): -        return JOINT.join([unicode(item) for item in [self.section, +        return settings.JOINT.join([unicode(item) for item in [self.section,                                                     self.parcel_number] if item])      def __unicode__(self): @@ -332,7 +332,7 @@ class Parcel(LightHistorizedItem):          items = [unicode(self.operation or self.associated_file)]          items += [unicode(item) for item in [self.section, self.parcel_number]                                            if item] -        return JOINT.join(items) +        return settings.JOINT.join(items)  class ParcelOwner(LightHistorizedItem):      owner = models.ForeignKey(Person, verbose_name=_(u"Owner")) @@ -345,7 +345,7 @@ class ParcelOwner(LightHistorizedItem):          verbose_name_plural = _(u"Parcel owners")      def __unicode__(self): -        return self.owner + JOINT + self.parcel +        return self.owner + settings.JOINT + self.parcel  class OperationDashboard:      def __init__(self): diff --git a/archaeological_operations/wizards.py b/archaeological_operations/wizards.py index df785fe6e..542d0118b 100644 --- a/archaeological_operations/wizards.py +++ b/archaeological_operations/wizards.py @@ -45,7 +45,8 @@ class OperationWizard(Wizard):          """          context = super(OperationWizard, self).get_extra_context(request,                                                                   storage) -        step = self.determine_step(request, storage) +        #step = self.determine_step(request, storage) +        step = self.steps.current          if not step.startswith('towns-'):              return context          context['TOWNS'] = self.get_towns(request, storage) @@ -79,7 +80,8 @@ class OperationWizard(Wizard):          else:              data = {}          if not step: -            step = self.determine_step(request, storage) +            #step = self.determine_step(request, storage) +            step = self.steps.current          form = self.get_form_list(request, storage)[step]          general_form_key = 'general-' + self.url_name          # manage the dynamic choice of towns @@ -229,7 +231,9 @@ class AdministrativeActDeletionWizard(ClosingWizard):                                    context_instance=RequestContext(request))  def is_preventive(form_name, model, type_key='operation_type', key=''): -    def func(self, request, storage): +    def func(self): +        request = self.request +        storage = self.storage          if storage.prefix not in request.session or \             'step_data' not in request.session[storage.prefix] or \             form_name not in request.session[storage.prefix]['step_data'] or\ @@ -238,7 +242,7 @@ def is_preventive(form_name, model, type_key='operation_type', key=''):              return False          try:              typ = int(request.session[storage.prefix]['step_data']\ -                                     [form_name][form_name+'-'+type_key]) +                                     [form_name][form_name+'-'+type_key][0])              return model.is_preventive(typ, key)          except ValueError:              return False diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 9b6f94907..3ca830aa3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -38,8 +38,6 @@ from django.contrib import admin  from simple_history.models import HistoricalRecords as BaseHistoricalRecords -JOINT = u" - " -  def post_save_user(sender, **kwargs):      user = kwargs['instance']      ishtaruser = None @@ -499,7 +497,7 @@ class Department(models.Model):          ordering = ['number']      def __unicode__(self): -        return unicode(self.number) + JOINT + self.label +        return unicode(self.number) + settings.JOINT + self.label  class Address(BaseHistorizedItem):      address = models.TextField(_(u"Address"), null=True, blank=True) @@ -574,11 +572,10 @@ class Person(Address, OwnPerms) :      def __unicode__(self):          lbl = u"%s %s" % (self.name, self.surname) -        lbl += JOINT          if self.attached_to: -            lbl += unicode(self.attached_to) +            lbl += settings.JOINT + unicode(self.attached_to)          elif self.email: -            lbl += self.email +            lbl += settings.JOINT + self.email          return lbl      def full_label(self): @@ -624,7 +621,7 @@ class Author(models.Model):          verbose_name_plural = _(u"Authors")      def __unicode__(self): -        return unicode(self.person) + JOINT + unicode(self.author_type) +        return unicode(self.person) + settings.JOINT + unicode(self.author_type)  class SourceType(GeneralType):      class Meta: @@ -655,14 +652,14 @@ if settings.COUNTRY == 'fr':          department = models.ForeignKey(Department, verbose_name=u"Département")          def __unicode__(self): -            return JOINT.join((self.name, unicode(self.department))) +            return settings.JOINT.join((self.name, unicode(self.department)))      class Canton(models.Model):          name = models.CharField(u"Nom", max_length=30)          arrondissement = models.ForeignKey(Arrondissement,                                             verbose_name=u"Arrondissement")          def __unicode__(self): -            return JOINT.join((self.name, unicode(self.arrondissement))) +            return settings.JOINT.join((self.name, unicode(self.arrondissement)))  class Town(models.Model):      name = models.CharField(_(u"Name"), max_length=100) diff --git a/ishtar_common/templates/search.html b/ishtar_common/templates/ishtar/wizard/search.html index 1b5829820..7ed214a48 100644 --- a/ishtar_common/templates/search.html +++ b/ishtar_common/templates/ishtar/wizard/search.html @@ -2,26 +2,27 @@  {% load i18n %}  {% load range %}  {% block extra_head %} -{{form.media}} +{{wizard.form.media}}  {% endblock %}  {% block content %} +<h2>{{wizard_label}}</h2>  <ul id='form_path'>    <li class='current'>» <a href='#'>{{form.form_label}}</a></li>  </ul>  <div class='form'> -{% if form.forms %} +{% if wizard.form.forms %}  <div class='top_button'><input type="submit" id="submit_form" value="{% trans "Validate" %}"/></div>  <table class='formset'> -  {%if form.non_form_errors%}<tr class='error'><th colspan='2'>{{form.non_form_errors}}</th></tr>{%endif%} -  {{ form.management_form }} -  {% for formsetform in form.forms %} +  {%if wizard.form.non_form_errors%}<tr class='error'><th colspan='2'>{{wizard.form.non_form_errors}}</th></tr>{%endif%} +  {{ wizard.form.management_form }} +  {% for formsetform in wizard.form.forms %}      {{ formsetform.as_table }}    {% endfor %}    <tr class='modify'><td colspan="2"><button name="formset_modify" value="{{form_step}}">{% trans "Add/Modify" %}</button></td></tr></li>  </table>  {% else %}  <table> -  {{ form.as_table }} +  {{ wizard.form.as_table }}  </table>  {% endif %}  </div> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 40ef4a1d2..7f82ade2f 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -57,7 +57,7 @@ urlpatterns += patterns('ishtar_common.views',             'new_person', name='new-person'),       url(r'autocomplete-person/([0-9_]+)?$', 'autocomplete_person',             name='autocomplete-person'), -     url(r'autocomplete-town/$', 'autocomplete_town', +     url(r'autocomplete-town/?$', 'autocomplete_town',             name='autocomplete-town'),       url(r'new-author/(?P<parent_name>.+)?/$',             'new_author', name='new-author'), diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py new file mode 100644 index 000000000..7895d98e3 --- /dev/null +++ b/ishtar_common/wizards.py @@ -0,0 +1,878 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012  Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet> + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +import datetime + +from django.conf import settings +from django.contrib.formtools.wizard.views import NamedUrlWizardView +from django.core.exceptions import ObjectDoesNotExist +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ + +import models + +class Wizard(NamedUrlWizardView): +    model = None +    label = '' +    modification = None # True when the wizard modify an item +    storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + +    @staticmethod +    def _check_right(step, condition=True): +        '''Return a method to check the right for a specific step''' +        """ +        def check_right(self, request, storage): +            cond = condition +            if callable(condition): +                cond = condition(self, request, storage) +            if not cond: +                return False +            person_type = request.user.ishtaruser.person.person_type +            if person_type.txt_idx == 'administrator': +                return True +            if person_type.rights.filter(url_name=step).count(): +                return True""" +        def check_right(self): +            cond = condition +            if callable(condition): +                cond = condition(self) +            if not cond: +                return False +            if not hasattr(self.request.user, 'ishtaruser'): +                return False +            person_type = self.request.user.ishtaruser.person.person_type +            if person_type.txt_idx == 'administrator': +                return True +            if person_type.rights.filter(url_name=step).count(): +                return True +        return check_right + +    def __init__(self, *args, **kwargs): +        """Check right for each step of the wizard""" +        print "2" +        super(Wizard, self).__init__(*args, **kwargs) +        for form_key in self.form_list.keys()[:-1]: +            condition = True +            if form_key in self.condition_dict: +                condition = self.condition_dict.get(form_key, True) +            cond = self._check_right(form_key, condition) +            self.condition_dict[form_key] = cond +        """ +        for form_key in self.form_list.keys()[:-1]: +            condition = True +            if form_key in self.condition_list: +                condition = self.condition_list.get(form_key, True) +            cond = self._check_right(form_key, condition) +            self.condition_list[form_key] = cond""" + +    def get_prefix(self, *args, **kwargs): +        """As the class name can interfere when reused prefix with the url_name +        """ +        print "3" +        return self.url_name + super(Wizard, self).get_prefix(*args, +                                                              **kwargs) + +    def get_wizard_name(self): +        """As the class name can interfere when reused, use the url_name""" +        print "4" +        return self.url_name + +    def get_template_names(self): +        print "5" +        templates = ['ishtar/wizard/default_wizard.html'] +        current_step = self.steps.current +        if current_step == self.steps.last: +            templates = ['ishtar/wizard/confirm_wizard.html'] + templates +        return templates + +    def get_context_data(self, form, **kwargs): +        """Add previous, next and current steps to manage the wizard path""" +        print "6" +        context = super(Wizard, self).get_context_data(form) +        self.request.session['CURRENT_ACTION'] = self.get_wizard_name() +        step = self.steps.first +        current_step = self.steps.current +        context.update({'current_step':self.form_list[current_step], +                        'wizard_label':self.label}) +        if step == current_step: +            return context +        previous_steps, next_steps, previous_step_counter = [], [], 0 +        while step: +            if step == current_step \ +               or (previous_steps and +                   previous_steps[-1] == self.form_list[step]): +                break +            previous_steps.append(self.form_list[step]) +            previous_step_counter += 1 +            if previous_step_counter >= len(self.steps): +                break +            step = self.steps.all[previous_step_counter] +        context.update({'previous_steps':previous_steps, +                        'previous_step_counter':previous_step_counter}) +        storage = self.storage +        # if modification: show the next steps +        if self.modification: +            next_step = step +            while next_step: +                # check if the form is initialized otherwise initialize it +                if not storage.get_step_data(next_step): +                    values = self.get_form_initial(next_step) +                    prefixed_values = {} +                    if not isinstance(values, list): +                        for key in values: +                            form_key = next_step + '-' + key +                            prefixed_values[form_key] = values[key] +                    else: +                        for formset_idx, v in enumerate(values): +                            prefix = u"-%d-" % formset_idx +                            for key in v: +                                form_key = next_step + prefix + key +                                prefixed_values[form_key] = v[key] +                    storage.set_step_data(next_step, prefixed_values) +                if step != next_step: # if not current step +                    next_steps.append(self.form_list[next_step]) +                next_step = self.get_next_step(next_step) +        context.update({'next_steps':next_steps}) +        # not last step: validation +        if current_step != self.steps.last: +            return context +        final_form_list = [] +        for form_key in self.get_form_list().keys(): +            form_obj = self.get_form(step=form_key, +                    data=self.storage.get_step_data(form_key), +                    files=self.storage.get_step_files(form_key)) +            form_obj.is_valid() +            final_form_list.append(form_obj) +        last_form = final_form_list[-1] +        context.update({'datas':self.get_formated_datas(final_form_list)}) +        if hasattr(last_form, 'confirm_msg'): +            context.update({'confirm_msg':last_form.confirm_msg}) +        if hasattr(last_form, 'confirm_end_msg'): +            context.update({'confirm_end_msg':last_form.confirm_end_msg}) +        return context + +    def get_formated_datas(self, forms): +        """Get the data to present in the last page""" +        print "7" +        datas = [] +        for form in forms: +            form_datas = [] +            base_form = hasattr(form, 'forms') and form.forms[0] or form +            associated_models = hasattr(base_form, 'associated_models') and \ +                                base_form.associated_models or {} +            if not hasattr(form, 'cleaned_data') and hasattr(form, 'forms'): +                cleaned_datas = [frm.cleaned_data for frm in form.forms +                                                  if frm.is_valid()] +                if not cleaned_datas: +                    continue +            elif not hasattr(form, 'cleaned_data'): +                continue +            else: +                cleaned_datas = type(form.cleaned_data) == list and \ +                                              form.cleaned_data \ +                            or [form.cleaned_data] +            for cleaned_data in cleaned_datas: +                if not cleaned_data: +                    continue +                if form_datas: +                    form_datas.append(("", "", "spacer")) +                items = hasattr(base_form, 'fields') and \ +                        base_form.fields.keyOrder or cleaned_data.keys() +                for key in items: +                    lbl = None +                    if key.startswith('hidden_'): +                        continue +                    if hasattr(base_form, 'fields') and key in base_form.fields: +                        lbl = base_form.fields[key].label +                        if hasattr(base_form, 'associated_labels') \ +                           and key in base_form.associated_labels: +                            lbl = base_form.associated_labels[key] +                    if not lbl: +                        continue +                    value = cleaned_data[key] +                    if not value and value != False: +                        continue +                    if type(value) == bool: +                        if value == True: +                            value = _(u"Yes") +                        elif value == False: +                            value = _(u"No") +                    elif key in associated_models: +                        values = [] +                        if "," in unicode(value): +                            values = unicode(value).split(",") +                        else: +                            values = [value] +                        rendered_values = [] +                        for val in values: +                            item = associated_models[key].objects.get(pk=val) +                            if hasattr(item, 'short_label'): +                                value = item.short_label() +                            else: +                                value = unicode(item) +                            rendered_values.append(value) +                        value = u" ; ".join(rendered_values) +                    form_datas.append((lbl, value, '')) +            if form_datas: +                datas.append((form.form_label, form_datas)) +        return datas + +    def get_extra_model(self, dct, form_list): +        print "8" +        dct['history_modifier'] = self.request.user +        return dct + +    def done(self, form_list, return_object=False, **kwargs): +        """Save to the model""" +        print "9" +        dct, m2m, whole_associated_models = {}, [], [] +        for form in form_list: +            if not form.is_valid(): +                return self.render(form) +            base_form = hasattr(form, 'forms') and form.forms[0] or form +            associated_models = hasattr(base_form, 'associated_models') and \ +                                base_form.associated_models or {} +            if hasattr(form, 'forms'): +                multi = False +                if form.forms: +                    frm = form.forms[0] +                    if hasattr(frm, 'base_model') and frm.base_model: +                        whole_associated_models.append(frm.base_model) +                    else: +                        whole_associated_models += associated_models.keys() +                    fields = frm.fields.copy() +                    if 'DELETE' in fields: +                        fields.pop('DELETE') +                    multi = len(fields) > 1 +                    if multi: +                        assert hasattr(frm, 'base_model'), \ +                       u"Must define a base_model for " + unicode(frm.__class__) +                for frm in form.forms: +                    if not frm.is_valid(): +                        continue +                    vals = {} +                    if "DELETE" in frm.cleaned_data: +                        if frm.cleaned_data["DELETE"]: +                            continue +                        frm.cleaned_data.pop('DELETE') +                    for key in frm.cleaned_data: +                        value = frm.cleaned_data[key] +                        if not value and value != False: +                            continue +                        if key in associated_models: +                            value = associated_models[key].objects.get(pk=value) +                        if multi: +                            vals[key] = value +                        else: +                            m2m.append((key, value)) +                    if multi and vals: +                        m2m.append((frm.base_model, vals)) +            elif type(form.cleaned_data) == dict: +                for key in form.cleaned_data: +                    if key.startswith('hidden_'): +                        continue +                    value = form.cleaned_data[key] +                    if key in associated_models: +                        if value: +                            model = associated_models[key] +                            if isinstance(value, unicode) \ +                             or isinstance(value, str) and "," in value: +                                value = value.split(",") +                            if isinstance(value, list) \ +                             or isinstance(value, tuple): +                                value = [model.objects.get(pk=val) +                                         for val in value if val] +                                if len(value) == 1: +                                    value = value[0] +                            else: +                                value = model.objects.get(pk=value) +                        else: +                            value = None +                    dct[key] = value +        return self.save_model(dct, m2m, whole_associated_models, form_list, +                               return_object) + +    def get_saved_model(self): +        """Permit a distinguo when saved model is not the base selected model""" +        print "10" +        return self.model + +    def get_current_saved_object(self): +        """Permit a distinguo when saved model is not the base selected model""" +        print "11" +        return self.get_current_object() + +    def save_model(self, dct, m2m, whole_associated_models, form_list, +                   return_object): +        print "12" +        dct = self.get_extra_model(dct, form_list) +        obj = self.get_current_saved_object() +        # manage dependant items +        other_objs = {} +        for k in dct.keys(): +            if '__' not in k: +                continue +            vals = k.split('__') +            assert len(vals) == 2, "Only one level of dependant item is managed" +            dependant_item, key = vals +            if dependant_item not in other_objs: +                other_objs[dependant_item] = {} +            other_objs[dependant_item][key] = dct.pop(k) +        if obj: +            for k in dct: +                if k.startswith('pk'): +                    continue +                setattr(obj, k, dct[k]) +            try: +                obj.full_clean() +            except forms.ValidationError, msg: +                return self.render(form_list[-1]) +            for dependant_item in other_objs: +                c_item = getattr(obj, dependant_item) +                # manage ManyToMany if only one associated +                if hasattr(c_item, "all"): +                    c_items = c_item.all() +                    if len(c_items) != 1: +                        continue +                    c_item = c_items[0] +                if c_item: +                    # to check # +                    for k in other_objs[dependant_item]: +                        setattr(c_item, k, other_objs[dependant_item][k]) +                    c_item.save() +                else: +                    m = getattr(self.model, dependant_item) +                    if hasattr(m, 'related'): +                        c_item = m.related.model(**other_objs[dependant_item]) +                        setattr(obj, dependant_item, c_item) +                    obj.save() +            obj.save() +        else: +            adds = {} +            for dependant_item in other_objs: +                m = getattr(self.model, dependant_item) +                model = m.field.rel.to +                c_dct = other_objs[dependant_item].copy() +                if issubclass(model, models.BaseHistorizedItem): +                    c_dct['history_modifier'] = self.request.user +                c_item = model(**c_dct) +                c_item.save() +                if hasattr(m, 'through'): +                    adds[dependant_item] = c_item +                elif hasattr(m, 'field'): +                    dct[dependant_item] = c_item +            if 'pk' in dct: +                dct.pop('pk') +            obj = self.get_saved_model()(**dct) +            try: +                obj.full_clean() +            except forms.ValidationError, msg: +                return self.render(form_list[-1]) +            obj.save() +            for k in adds: +                getattr(obj, k).add(adds[k]) +            # necessary to manage interaction between models like +            # material_index management for baseitems +            obj.save() +        m2m_items = {} +        for model in whole_associated_models: +            getattr(obj, model+'s').clear() +        for key, value in m2m: +            if key not in m2m_items: +                if type(key) == dict: +                    vals = [] +                    for item in getattr(obj, key+'s').all(): +                        v = {} +                        for k in value.keys(): +                            v[k] = getattr(item, k) +                        vals.append(v) +                    m2m_items[key] = vals +                else: +                    m2m_items[key] = getattr(obj, key+'s').all() +            if value not in m2m_items[key]: +                if type(value) == dict: +                    model = getattr(obj, key+'s').model +                    if issubclass(model, models.BaseHistorizedItem): +                        value['history_modifier'] = self.request.user +                    value = model.objects.create(**value) +                    value.save() +                getattr(obj, key+'s').add(value) +                # necessary to manage interaction between models like +                # material_index management for baseitems +                obj.save() +        res = render_to_response('wizard_done.html', {}, +                                  context_instance=RequestContext(self.request)) +        return return_object and (obj, res) or res + +    def get_deleted(self, keys): +        """Get the deleted and non-deleted items in formsets""" +        print "13" +        not_to_delete, to_delete = set(), set() +        for key in keys: +            items = key.split('-') +            if len(items) < 2 or items[-2] in to_delete: +                continue +            idx = items[-2] +            try: +                int(idx) +            except: +                continue +            if items[-1] == u'DELETE': +                to_delete.add(idx) +                if idx in not_to_delete: +                    not_to_delete.remove(idx) +            elif idx not in not_to_delete: +                not_to_delete.add(idx) +        return (to_delete, not_to_delete) + +    def get_form(self, step=None, data=None, files=None): +        """Manage formset""" +        print "14" +        request = self.request +        storage = self.storage +        if data: +            data = data.copy() +            if not step: +                step = self.steps.current +            form = self.get_form_list()[step] +            if hasattr(form, 'management_form'): +                # manage deletion +                to_delete, not_to_delete = self.get_deleted(data.keys()) +                # raz deleted fields +                for key in data.keys(): +                    items = key.split('-') +                    if len(items) < 2 or items[-2] not in to_delete: +                        continue +                    data.pop(key) +                if to_delete: +                    # reorganize +                    for idx, number in enumerate(sorted(not_to_delete)): +                        idx = unicode(idx) +                        if idx == number: +                            continue +                        for key in data.keys(): +                            items = key.split('-') +                            if len(items) > 2 and number == items[-2]: +                                items[-2] = unicode(idx) +                                k = u'-'.join(items) +                                data[k] = data.pop(key)[0] +                # get a form key +                base_key = form.form.base_fields.keys()[0] +                init = self.get_form_initial(step) +                total_field = len([key for key in data.keys() +                                        if base_key in key.split('-') +                                           and data[key]]) +                if init and not to_delete: +                    total_field = max((total_field, len(init))) +                data[step + u'-INITIAL_FORMS'] = unicode(total_field) +                data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1) +        data = data or None +        form = super(Wizard, self).get_form(step, data, files) +        return form + +    def render_next_step(self, form, **kwargs): +        """ +        Manage: +         - modify or delete button in formset: next step = current step +         - validate and end: nextstep = last step +        """ +        print "15" +        request = self.request +        if request.POST.has_key('formset_modify') \ +           and request.POST['formset_modify'] \ +           or [key for key in request.POST.keys() +               if key.endswith('DELETE') and request.POST[key]]: +            return self.render(form) +        elif request.POST.has_key('validate_and_end') \ +           and request.POST['validate_and_end']: +            last_step = self.steps.last +            new_form = self.get_form(last_step, +                data=self.storage.get_step_data(last_step), +                files=self.storage.get_step_files(last_step)) +            self.storage.current_step = last_step +            return self.render(new_form) +        return super(Wizard, self).render_next_step(form, **kwargs) + +    def post(self, *args, **kwargs): +        """Convert numerical step number to step name""" +        print "16" +        request = self.request +        post_data = request.POST.copy() +        if request.POST.has_key('form_prev_step'): +            try: +                step_number = int(request.POST['form_prev_step']) +                post_data['wizard_goto_step'] = self.get_form_list().keys( +                                                                 )[step_number] +            except ValueError: +                pass +        request.POST = post_data +        return super(Wizard, self).post(*args, **kwargs) + +    @classmethod +    def session_has_key(cls, request, storage, form_key, key=None, multi=None): +        """Check if the session has value of a specific form and (if provided) +        of a key +        """ +        print "17" +        test = storage.prefix in request.session \ +          and 'step_data' in request.session[storage.prefix] \ +          and form_key in request.session[storage.prefix]['step_data'] +        if not key or not test: +            return test +        key = key.startswith(form_key) and key or \ +              not multi and form_key + '-' + key or \ +              form_key + '-0-' + key #only check if the first field is available +        return key in request.session[storage.prefix]['step_data'][form_key] + +    @classmethod +    def session_get_value(cls, request, storage, form_key, key, multi=False): +        """Get the value of a specific form""" +        print "18" +        if not cls.session_has_key(request, storage, form_key, key, multi): +            return +        if not multi: +            key = key.startswith(form_key) and key or form_key + '-' + key +            return request.session[storage.prefix]['step_data'][form_key][key] +        vals = [] +        for k in request.session[storage.prefix]['step_data'][form_key]: +            if k.startswith(form_key) and k.endswith(key) and \ +               request.session[storage.prefix]['step_data'][form_key][k]: +                vals.append(request.session[storage.prefix]['step_data']\ +                                           [form_key][k]) +        return vals + +    def get_current_object(self): +        """Get the current object for an instancied wizard""" +        print "19" +        current_obj = None +        main_form_key = 'selec-' + self.url_name +        try: +            idx = self.session_get_value(self.request, self.storage, +                                             main_form_key, 'pk') +            if type(idx) in (tuple, list): +                idx = idx[0] +            idx = int(idx) +            current_obj = self.model.objects.get(pk=idx) +        except(TypeError, ValueError, ObjectDoesNotExist): +            pass +        return current_obj + +    def get_form_initial(self, step): +        print "20" +        current_obj = self.get_current_object() +        current_step = self.steps.current +        request = self.request +        if step.startswith('selec-') and step in self.form_list \ +           and 'pk' in self.form_list[step].associated_models: +            model_name = self.form_list[step].associated_models['pk' +                                              ].__name__.lower() +            if step == current_step: +                #self.reset_wizard(request, storage) +                self.storage.reset() +            val = model_name in request.session and request.session[model_name] +            if val: +                return {'pk':val} +        elif current_obj: +            return self.get_instanced_init(current_obj, step) +        current_form = self.form_list[current_step] +        if hasattr(current_form, 'currents'): +            initial = {} +            for key in current_form.currents: +                model_name = current_form.currents[key].__name__.lower() +                val = model_name in request.session and \ +                      request.session[model_name] +                if val: +                    initial[key] = val +            if initial: +                return initial +        return super(Wizard, self).get_form_initial(step) + +    def get_instanced_init(self, obj, step=None): +        """Get initial data from an init""" +        print "21" +        current_step = step or self.steps.current +        c_form = self.form_list[current_step] +        # make the current object the default item for the session +        obj_name = obj.__class__.__name__.lower() +        # prefer a specialized name if available +        prefixes = self.storage.prefix.split('_') +        if len(prefixes) > 1 and prefixes[-2].startswith(obj_name): +            obj_name = prefixes[-2] +        self.request.session[obj_name] = unicode(obj.pk) +        initial = {} +        if self.request.POST or \ +          (step in self.request.session[self.storage.prefix] and\ +           self.request.session[self.storage.prefix]['step_data'][step]): +            return {} +        if hasattr(c_form, 'base_fields'): +            for base_field in c_form.base_fields.keys(): +                fields = base_field.split('__') +                value = obj +                for field in fields: +                    if not hasattr(value, field) or \ +                       getattr(value, field) == None: +                        value = obj +                        break +                    value = getattr(value, field) +                if value == obj: +                    continue +                if hasattr(value, 'pk'): +                    value = value.pk +                if value in (True, False): +                    initial[base_field] = value +                elif value != None: +                    initial[base_field] = unicode(value) +        elif hasattr(c_form, 'management_form'): +            initial = [] +            if hasattr(c_form.form, 'base_model'): +                key = c_form.form.base_model + 's' +            else: +                key = current_step.split('-')[0] +            if not hasattr(obj, key): +                return initial +            keys = c_form.form.base_fields.keys() +            for child_obj in getattr(obj, key).order_by('pk').all(): +                if not keys: +                    break +                vals = {} +                if len(keys) == 1: +                    # only one field: must be the id of the object +                    vals[keys[0]] = unicode(child_obj.pk) +                else: +                    for field in keys: +                        if hasattr(child_obj, field): +                            value = getattr(child_obj, field) +                            if hasattr(value, 'pk'): +                                value = value.pk +                            if value != None: +                                vals[field] = unicode(value) +                if vals: +                    initial.append(vals) +        return initial + +class SearchWizard(NamedUrlWizardView): +    model = None +    label = '' +    modification = None # True when the wizard modify an item +    storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage' + +    def get_wizard_name(self): +        """ +        As the class name can interfere when reused, use the url_name +        """ +        return self.url_name + +    def get_prefix(self, *args, **kwargs): +        """As the class name can interfere when reused prefix with the url_name +        """ +        return self.url_name + super(SearchWizard, self).get_prefix(*args, +                                                              **kwargs) + +    def get_template_names(self): +        templates = ['ishtar/wizard/search.html'] +        return templates + +class DeletionWizard(Wizard): +    def get_formated_datas(self, forms): +        datas = super(DeletionWizard, self).get_formated_datas(forms) +        self.current_obj = None +        for form in forms: +            if not hasattr(form, "cleaned_data"): +                continue +            for key in form.cleaned_data: +                if key == 'pk': +                    model = form.associated_models['pk'] +                    self.current_obj = model.objects.get(pk=form.cleaned_data['pk']) +        if not self.current_obj: +            return datas +        res = {} +        for field in self.model._meta.fields + self.model._meta.many_to_many: +            if field.name not in self.fields: +                continue +            value = getattr(self.current_obj, field.name) +            if not value: +                continue +            if hasattr(value, 'all'): +                value = ", ".join([unicode(item) for item in value.all()]) +                if not value: +                    continue +            else: +                value = unicode(value) +            res[field.name] = (field.verbose_name, value, '') +        if not datas and self.fields: +            datas = [['', []]] +        for field in self.fields: +            if field in res: +                datas[0][1].append(res[field]) +        return datas + +    def done(self, request, storage, form_list, **kwargs): +        obj = self.get_current_object(request, storage) +        obj.delete() +        return render_to_response('wizard_delete_done.html', {}, +                                  context_instance=RequestContext(request)) + +class ClosingWizard(Wizard): +    # "close" an item +    # to be define in the overloaded class +    model = None +    fields = [] + +    def get_formated_datas(self, forms): +        datas = super(ClosingWizard, self).get_formated_datas(forms) +        self.current_obj = None +        for form in forms: +            if not hasattr(form, "cleaned_data"): +                continue +            for key in form.cleaned_data: +                if key == 'pk': +                    model = form.associated_models['pk'] +                    self.current_obj = model.objects.get( +                                                  pk=form.cleaned_data['pk']) +        if not self.current_obj: +            return datas +        res = {} +        for field in self.model._meta.fields + self.model._meta.many_to_many: +            if field.name not in self.fields: +                continue +            value = getattr(self.current_obj, field.name) +            if not value: +                continue +            if hasattr(value, 'all'): +                value = ", ".join([unicode(item) for item in value.all()]) +                if not value: +                    continue +            else: +                value = unicode(value) +            res[field.name] = (field.verbose_name, value, '') +        if not datas and self.fields: +            datas = [['', []]] +        for field in self.fields: +            if field in res: +                datas[0][1].append(res[field]) +        return datas + +    def done(self, request, storage, form_list, **kwargs): +        obj = self.get_current_object(request, storage) +        for form in form_list: +            if form.is_valid(): +                if 'end_date' in form.cleaned_data and hasattr(obj, 'end_date'): +                    obj.end_date = form.cleaned_data['end_date'] +                    obj.save() +        return render_to_response('wizard_closing_done.html', {}, +                                  context_instance=RequestContext(request)) + +class PersonWizard(Wizard): +    model = models.Person + +class PersonModifWizard(PersonWizard): +    modification = True + +class AccountWizard(Wizard): +    model = models.Person +    def get_formated_datas(self, forms): +        datas = super(AccountWizard, self).get_formated_datas(forms) +        for form in forms: +            if not hasattr(form, "cleaned_data"): +                continue +            for key in form.cleaned_data: +                if key == 'hidden_password' and form.cleaned_data[key]: +                    datas[-1][1].append((_("New password"), "*"*8)) +        return datas + +    def done(self, form_list, **kwargs): +        """ +        Save the account +        """ +        dct = {} +        for form in form_list: +            if not form.is_valid(): +                return self.render(form) +            associated_models = hasattr(form, 'associated_models') and \ +                                form.associated_models or {} +            if type(form.cleaned_data) == dict: +                for key in form.cleaned_data: +                    if key == 'pk': +                        continue +                    value = form.cleaned_data[key] +                    if key in associated_models and value: +                        value = associated_models[key].objects.get(pk=value) +                    dct[key] = value +        person = self.get_current_object() +        if not person: +            return self.render(form) +        for key in dct.keys(): +            if key.startswith('hidden_password'): +                dct['password'] = dct.pop(key) +        try: +            account = models.IshtarUser.objects.get(person=person) +            account.username = dct['username'] +            account.email = dct['email'] +        except ObjectDoesNotExist: +            now = datetime.datetime.now() +            account = models.IshtarUser(person=person, username=dct['username'], +                    email=dct['email'], first_name=person.surname, +                    last_name=person.name, is_staff=False, is_active=True, +                    is_superuser=False, last_login=now, date_joined=now) +        if dct['password']: +            account.set_password(dct['password']) +        account.save() + +        if 'send_password' in dct and dct['send_password'] and \ +           settings.ADMINS: +            site = Site.objects.get_current() + +            app_name = site and ("Ishtar - " + site.name) \ +                       or "Ishtar" +            context = Context({'login':dct['username'], +                               'password':dct['password'], +                               'app_name':app_name, +                               'site': site and site.domain or "" +                              }) +            t = loader.get_template('account_activation_email.txt') +            msg = t.render(context) +            subject = _(u"[%(app_name)s] Account creation/modification") % { +                                                           "app_name":app_name} +            send_mail(subject, msg, settings.ADMINS[0][1], +                      [dct['email']], fail_silently=True) +        res = render_to_response('wizard_done.html', {}, +                                  context_instance=RequestContext(self.request)) +        return res + +    def get_form(self, step=None, data=None, files=None): +        """ +        Display the "Send email" field if necessary +        """ +        form = super(AccountWizard, self).get_form(step, data, files) +        if not hasattr(form, 'is_hidden'): +            return form +        if self.session_get_value(self.request, self.storage, +                               'account-account_management', 'hidden_password'): +            form.is_hidden = False +        return form + +class SourceWizard(Wizard): +    model = None +    def get_extra_model(self, dct, request, storage, form_list): +        dct = super(SourceWizard, self).get_extra_model(dct, request, storage, +                                                        form_list) +        if 'history_modifier' in dct: +            dct.pop('history_modifier') +        return dct | 
