diff options
author | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-10-10 01:05:40 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-10-10 01:05:40 +0200 |
commit | 615457617e65019e0ce39b585f4eeb41b17ba61a (patch) | |
tree | 0057598919dfeec52bae4416274e548025dccd11 | |
parent | 1b173c7353631e40b0f40d5cf7d60ad0b664aa9d (diff) | |
download | Ishtar-615457617e65019e0ce39b585f4eeb41b17ba61a.tar.bz2 Ishtar-615457617e65019e0ce39b585f4eeb41b17ba61a.zip |
Django 1.4 migration - Begining work on FormWizard migration: person creation
-rw-r--r-- | ishtar/ishtar_base/forms.py | 597 | ||||
-rw-r--r-- | ishtar/ishtar_base/forms_common.py | 25 | ||||
-rw-r--r-- | ishtar/ishtar_base/forms_main.py | 8 | ||||
-rw-r--r-- | ishtar/ishtar_base/management/commands/generate_rights.py | 12 | ||||
-rw-r--r-- | ishtar/ishtar_base/urls.py | 159 | ||||
-rw-r--r-- | ishtar/ishtar_base/views.py | 627 | ||||
-rw-r--r-- | ishtar/templates/ishtar/wizard/confirm_wizard.html (renamed from ishtar/templates/confirm_wizard.html) | 5 | ||||
-rw-r--r-- | ishtar/templates/ishtar/wizard/default_wizard.html (renamed from ishtar/templates/default_wizard.html) | 12 | ||||
-rw-r--r-- | ishtar/urls.py | 2 |
9 files changed, 750 insertions, 697 deletions
diff --git a/ishtar/ishtar_base/forms.py b/ishtar/ishtar_base/forms.py index d947b43ae..3d6f3f503 100644 --- a/ishtar/ishtar_base/forms.py +++ b/ishtar/ishtar_base/forms.py @@ -40,7 +40,18 @@ from django.forms.formsets import formset_factory, BaseFormSet, \ from django.contrib.auth.models import User from django.contrib.sites.models import Site -from formwizard.forms import NamedUrlSessionFormWizard +# from formwizard.forms import NamedUrlSessionFormWizard + +class NamedUrlSessionFormWizard(forms.Form): + def __init__(self, form_list, condition_list={}, url_name=''): + self.form_list = dict(form_list) + self.condition_list = condition_list + self.url_name = url_name + super(NamedUrlSessionFormWizard, self).__init__(self) + + + def rindex(self, idx): + return self.url_name.rindex(idx) import models import widgets @@ -90,6 +101,7 @@ class FormSet(BaseFormSet): form.fields[DELETION_FIELD_NAME].label = '' form.fields[DELETION_FIELD_NAME].widget = widgets.DeleteWidget() +''' class SearchWizard(NamedUrlSessionFormWizard): model = None @@ -103,587 +115,6 @@ class SearchWizard(NamedUrlSessionFormWizard): templates = ['search.html'] return templates -class Wizard(NamedUrlSessionFormWizard): - model = None - modification = None # True when the wizard modify an item - - @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 - return check_right - - def __init__(self, *args, **kwargs): - """Check right for each step of the wizard""" - super(Wizard, self).__init__(*args, **kwargs) - 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_wizard_name(self): - """As the class name can interfere when reused, use the url_name""" - return self.url_name - - def get_template(self, request, storage): - templates = ['default_wizard.html'] - current_step = storage.get_current_step() or self.get_first_step( - request, storage) - if current_step == self.get_last_step(request, storage): - templates = ['confirm_wizard.html'] + templates - return templates - - def get_template_context(self, request, storage, form=None): - """Add previous, next and current steps to manage the wizard path""" - context = super(Wizard, self).get_template_context(request, storage, - form) - step = self.get_first_step(request, storage) - current_step = storage.get_current_step() or self.get_first_step( - request, storage) - context.update({'current_step':self.form_list[current_step]}) - if step == current_step: - return context - previous_steps, next_steps, previous_step_counter = [], [], 0 - while step: - if step == current_step: - break - previous_steps.append(self.form_list[step]) - step = self.get_next_step(request, storage, step) - previous_step_counter += 1 - context.update({'previous_steps':previous_steps, - 'previous_step_counter':previous_step_counter}) - # 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(request, storage, 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(request, storage, next_step) - context.update({'next_steps':next_steps}) - # not last step: validation - if step != self.get_last_step(request, storage): - return context - final_form_list = [] - for form_key in self.get_form_list(request, storage).keys(): - form_obj = self.get_form(request, storage, step=form_key, - data=storage.get_step_data(form_key), - files=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""" - 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, request, storage, form_list): - dct['history_modifier'] = request.user - return dct - - def done(self, request, storage, form_list, return_object=False, **kwargs): - """Save to the model""" - dct, m2m, whole_associated_models = {}, [], [] - for form in form_list: - if not form.is_valid(): - return self.render(request, storage, 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, request, - storage, form_list, return_object) - - def get_saved_model(self): - """Permit a distinguo when saved model is not the base selected model""" - return self.model - - def get_current_saved_object(self, request, storage): - """Permit a distinguo when saved model is not the base selected model""" - return self.get_current_object(request, storage) - - def save_model(self, dct, m2m, whole_associated_models, request, storage, - form_list, return_object): - dct = self.get_extra_model(dct, request, storage, form_list) - obj = self.get_current_saved_object(request, storage) - # 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(request, storage, 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'] = 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(request, storage, 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'] = 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(request)) - return return_object and (obj, res) or res - - def get_deleted(self, keys): - """Get the deleted and non-deleted items in formsets""" - 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, request, storage, step=None, data=None, files=None): - """Manage formset""" - if data: - data = data.copy() - if not step: - step = self.determine_step(request, storage) - form = self.get_form_list(request, storage)[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(request, storage, 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(request, storage, step, data, files) - return form - - def render_next_step(self, request, storage, form, **kwargs): - """ - Manage: - - modify or delete button in formset: next step = current step - - validate and end: nextstep = last step - """ - 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(request, storage, form, **kwargs) - elif request.POST.has_key('validate_and_end') \ - and request.POST['validate_and_end']: - last_step = self.get_last_step(request, storage) - new_form = self.get_form(request, storage, last_step, - data=storage.get_step_data(last_step), - files=storage.get_step_files(last_step)) - storage.set_current_step(last_step) - return self.render(request, storage, new_form, **kwargs) - return super(Wizard, self).render_next_step(request, storage, form, - **kwargs) - - def process_post_request(self, request, storage, *args, **kwargs): - """Convert numerical step number to step name""" - post_data = request.POST.copy() - if request.POST.has_key('form_prev_step'): - try: - step_number = int(request.POST['form_prev_step']) - post_data['form_prev_step'] = self.get_form_list(request, - storage).keys()[step_number] - except ValueError: - pass - request.POST = post_data - return super(Wizard, self).process_post_request(request, storage, *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 - """ - 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""" - 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, request, storage): - """Get the current object for an instancied wizard""" - current_obj = None - main_form_key = 'selec-' + self.url_name - try: - idx = int(self.session_get_value(request, storage, main_form_key, - 'pk')) - current_obj = self.model.objects.get(pk=idx) - except(TypeError, ValueError, ObjectDoesNotExist): - pass - return current_obj - - def get_form_initial(self, request, storage, step): - current_obj = self.get_current_object(request, storage) - current_step = storage.get_current_step() or self.get_first_step( - request, storage) - 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) - 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, request, storage, - 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(request, storage, step) - - def get_instanced_init(self, obj, request, storage, step=None): - """Get initial data from an init""" - current_step = step or storage.get_current_step() \ - or self.get_first_step(request, storage) - 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 = storage.prefix.split('_') - if len(prefixes) > 1 and prefixes[-2].startswith(obj_name): - obj_name = prefixes[-2] - request.session[obj_name] = unicode(obj.pk) - initial = {} - if request.POST or (step in request.session[storage.prefix] and\ - request.session[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 - def get_now(): format = formats.get_format('DATE_INPUT_FORMATS')[0] value = datetime.datetime.now().strftime(format) @@ -815,4 +246,4 @@ def get_form_selection(class_name, label, key, model, base_form, get_url, return cleaned_data attrs['clean'] = clean return type(class_name, (forms.Form,), attrs) - +''' diff --git a/ishtar/ishtar_base/forms_common.py b/ishtar/ishtar_base/forms_common.py index 68ab9be5f..7ef1d4070 100644 --- a/ishtar/ishtar_base/forms_common.py +++ b/ishtar/ishtar_base/forms_common.py @@ -38,8 +38,7 @@ from ishtar import settings import models import widgets -from forms import Wizard, SearchWizard, FinalForm, FormSet, reverse_lazy, \ - name_validator +from forms import FinalForm, FormSet, reverse_lazy, name_validator def get_town_field(label=_(u"Town"), required=True): help_text = _(u"<p>Type name, department code and/or postal code of the " @@ -152,9 +151,6 @@ class OrganizationForm(forms.Form): new_item.save() return new_item -class PersonWizard(Wizard): - model = models.Person - class PersonFormSelection(forms.Form): form_label = _(u"Person search") associated_models = {'pk':models.Person} @@ -199,14 +195,11 @@ class PersonForm(forms.Form): new_item.save() return new_item +''' person_search_wizard = SearchWizard([ ('general-person_search', PersonFormSelection)], url_name='person_search',) -person_creation_wizard = PersonWizard([ - ('identity-person_creation', PersonForm), - ('final-person_creation', FinalForm)], - url_name='person_creation',) person_modification_wizard = PersonWizard([ @@ -228,9 +221,9 @@ class AccountWizard(Wizard): return datas def done(self, request, storage, form_list, **kwargs): - ''' + """ Save the account - ''' + """ dct = {} for form in form_list: if not form.is_valid(): @@ -299,7 +292,7 @@ class AccountWizard(Wizard): form.is_hidden = False return form - +''' class AccountForm(forms.Form): form_label = _("Account") associated_models = {'pk':models.Person} @@ -355,13 +348,13 @@ class FinalAccountForm(forms.Form): def __init__(self, *args, **kwargs): self.is_hidden = True return super(FinalAccountForm, self).__init__(*args, **kwargs) - +''' account_management_wizard = AccountWizard([ ('selec-account_management', PersonFormSelection), ('account-account_management', AccountForm), ('final-account_management', FinalAccountForm)], url_name='account_management',) - +''' class TownForm(forms.Form): form_label = _("Towns") base_model = 'town' @@ -432,7 +425,7 @@ ParcelFormSet.form_label = _(u"Parcels") ###################### # Sources management # ###################### - +''' class SourceWizard(Wizard): model = None def get_extra_model(self, dct, request, storage, form_list): @@ -441,7 +434,7 @@ class SourceWizard(Wizard): if 'history_modifier' in dct: dct.pop('history_modifier') return dct - +''' class SourceForm(forms.Form): form_label = _(u"Documentation informations") associated_models = {'source_type':models.SourceType} diff --git a/ishtar/ishtar_base/forms_main.py b/ishtar/ishtar_base/forms_main.py index 723b80d83..29253f284 100644 --- a/ishtar/ishtar_base/forms_main.py +++ b/ishtar/ishtar_base/forms_main.py @@ -18,9 +18,9 @@ # See the file COPYING for details. from forms_common import * -from forms_files import * -from forms_operations import * -from forms_context_records import * -from forms_items import * +#from forms_files import * +#from forms_operations import * +#from forms_context_records import * +#from forms_items import * from forms import * diff --git a/ishtar/ishtar_base/management/commands/generate_rights.py b/ishtar/ishtar_base/management/commands/generate_rights.py index 9bf2776a8..75b1cf807 100644 --- a/ishtar/ishtar_base/management/commands/generate_rights.py +++ b/ishtar/ishtar_base/management/commands/generate_rights.py @@ -17,6 +17,8 @@ # See the file COPYING for details. +import sys + from django.core.management.base import BaseCommand, CommandError from django.core.exceptions import ObjectDoesNotExist @@ -40,7 +42,8 @@ class Command(BaseCommand): except ObjectDoesNotExist: wizard_obj = models.Wizard.objects.create(url_name=url_name) wizard_obj.save() - self.stdout.write('* Wizard "%s" added\n' % url_name) + #self.stdout.write('* Wizard "%s" added\n' % url_name) + sys.stdout.write('* Wizard "%s" added\n' % url_name) wizard_steps[url_name] = [] for idx, step_url_name in enumerate(wizard.form_list.keys()): form = wizard.form_list[step_url_name] @@ -59,7 +62,10 @@ class Command(BaseCommand): 'url_name':step_url_name}) step_obj = models.WizardStep.objects.create(**step_values) step_obj.save() - self.stdout.write('* Wizard step "%s" added\n' \ + #self.stdout.write('* Wizard step "%s" added\n' \ + # % unicode(form.form_label)) + sys.stdout.write('* Wizard step "%s" added\n' \ % unicode(form.form_label)) wizard_steps[url_name].append(step_url_name) - self.stdout.write('Successfully regeneration of wizard rights\n') + #self.stdout.write('Successfully regeneration of wizard rights\n') + sys.stdout.write('Successfully regeneration of wizard rights\n') diff --git a/ishtar/ishtar_base/urls.py b/ishtar/ishtar_base/urls.py index 8f511cf39..a5b1f5efa 100644 --- a/ishtar/ishtar_base/urls.py +++ b/ishtar/ishtar_base/urls.py @@ -19,9 +19,10 @@ from django.conf.urls.defaults import * -from ishtar.urls import BASE_URL from menus import menu -import forms_main as ishtar_forms +#import forms_main as ishtar_forms + +import views urlpatterns, actions = [], [] @@ -29,102 +30,106 @@ urlpatterns, actions = [], [] # forms urlpatterns = patterns('', # General - url(BASE_URL + r'person_creation/(?P<step>.+)$', - ishtar_forms.person_creation_wizard, name='person_creation'), - url(BASE_URL + r'person_modification/(?P<step>.+)$', + url(r'person_creation/(?P<step>.+)$', + views.person_creation_wizard, name='person_creation_step'), + url(r'person_creation/$', + views.person_creation_wizard, name='person_creation'), + ) +""" + url(r'person_modification/(?P<step>.+)$', ishtar_forms.person_modification_wizard, name='person_modification'), - url(BASE_URL + r'account_management/(?P<step>.+)$', + url(r'account_management/(?P<step>.+)$', ishtar_forms.account_management_wizard, name='account_management'), # Archaelogical files - url(BASE_URL + r'file_search/(?P<step>.+)$', + url(r'file_search/(?P<step>.+)$', ishtar_forms.file_search_wizard, name='file_search'), - url(BASE_URL + r'file_creation/(?P<step>.+)$', + url(r'file_creation/(?P<step>.+)$', ishtar_forms.file_creation_wizard, name='file_creation'), - url(BASE_URL + r'file_modification/(?P<step>.+)$', + url(r'file_modification/(?P<step>.+)$', ishtar_forms.file_modification_wizard, name='file_modification'), - url(BASE_URL + r'file_closing/(?P<step>.+)$', + url(r'file_closing/(?P<step>.+)$', ishtar_forms.file_closing_wizard, name='file_closing'), - url(BASE_URL + r'file_deletion/(?P<step>.+)$', + url(r'file_deletion/(?P<step>.+)$', ishtar_forms.file_deletion_wizard, name='file_deletion'), - url(BASE_URL + r'file_administrativeactfile/(?P<step>.+)$', + url(r'file_administrativeactfile/(?P<step>.+)$', ishtar_forms.file_administrativeactfile_wizard, name='file_administrativeactfile'), - url(BASE_URL + r'file_administrativeactfile_modification/(?P<step>.+)$', + url(r'file_administrativeactfile_modification/(?P<step>.+)$', ishtar_forms.file_administrativeactfile_modification_wizard, name='file_administrativeactfile_modification'), - url(BASE_URL + r'file_administrativeactfile_deletion/(?P<step>.+)$', + url(r'file_administrativeactfile_deletion/(?P<step>.+)$', ishtar_forms.file_administrativeactfile_deletion_wizard, name='file_administrativeactfile_deletion'), # Operations - url(BASE_URL + r'operation_search/(?P<step>.+)$', + url(r'operation_search/(?P<step>.+)$', ishtar_forms.operation_search_wizard, name='operation_search'), - url(BASE_URL + r'operation_creation/(?P<step>.+)$', + url(r'operation_creation/(?P<step>.+)$', ishtar_forms.operation_creation_wizard, name='operation_creation'), - url(BASE_URL + r'operation_modification/(?P<step>.+)$', + url(r'operation_modification/(?P<step>.+)$', ishtar_forms.operation_modification_wizard, name='operation_modification'), - url(BASE_URL + r'operation_closing/(?P<step>.+)$', + url(r'operation_closing/(?P<step>.+)$', ishtar_forms.operation_closing_wizard, name='operation_closing'), - url(BASE_URL + r'operation_deletion/(?P<step>.+)$', + url(r'operation_deletion/(?P<step>.+)$', ishtar_forms.operation_deletion_wizard, name='operation_deletion'), - url(BASE_URL + r'operation_administrativeactop/(?P<step>.+)$', + url(r'operation_administrativeactop/(?P<step>.+)$', ishtar_forms.operation_administrativeactop_wizard, name='operation_administrativeactop'), - url(BASE_URL + r'operation_administrativeactop_modification/(?P<step>.+)$', + url(r'operation_administrativeactop_modification/(?P<step>.+)$', ishtar_forms.operation_administrativeactop_modification_wizard, name='operation_administrativeactop_modification'), - url(BASE_URL + r'operation_administrativeactop_deletion/(?P<step>.+)$', + url(r'operation_administrativeactop_deletion/(?P<step>.+)$', ishtar_forms.operation_administrativeactop_deletion_wizard, name='operation_administrativeactop_deletion'), - url(BASE_URL + r'operation_source_creation/(?P<step>.+)$', + url(r'operation_source_creation/(?P<step>.+)$', ishtar_forms.operation_source_creation_wizard, name='operation_source_creation'), - url(BASE_URL + r'operation_source_modification/(?P<step>.+)$', + url(r'operation_source_modification/(?P<step>.+)$', ishtar_forms.operation_source_modification_wizard, name='operation_source_modification'), - url(BASE_URL + r'operation_source_deletion/(?P<step>.+)$', + url(r'operation_source_deletion/(?P<step>.+)$', ishtar_forms.operation_source_deletion_wizard, name='operation_source_deletion'), # Context records - url(BASE_URL + r'record_search/(?P<step>.+)$', + url(r'record_search/(?P<step>.+)$', ishtar_forms.record_search_wizard, name='record_search'), - url(BASE_URL + r'record_creation/(?P<step>.+)$', + url(r'record_creation/(?P<step>.+)$', ishtar_forms.record_creation_wizard, name='record_creation'), - url(BASE_URL + r'record_modification/(?P<step>.+)$', + url(r'record_modification/(?P<step>.+)$', ishtar_forms.record_modification_wizard, name='record_modification'), - url(BASE_URL + r'record_deletion/(?P<step>.+)$', + url(r'record_deletion/(?P<step>.+)$', ishtar_forms.record_deletion_wizard, name='record_deletion'), - url(BASE_URL + r'record_source_creation/(?P<step>.+)$', + url(r'record_source_creation/(?P<step>.+)$', ishtar_forms.record_source_creation_wizard, name='record_source_creation'), - url(BASE_URL + r'record_source_modification/(?P<step>.+)$', + url(r'record_source_modification/(?P<step>.+)$', ishtar_forms.record_source_modification_wizard, name='record_source_modification'), - url(BASE_URL + r'record_source_deletion/(?P<step>.+)$', + url(r'record_source_deletion/(?P<step>.+)$', ishtar_forms.record_source_deletion_wizard, name='record_source_deletion'), # Finds - url(BASE_URL + r'item_search/(?P<step>.+)$', + url(r'item_search/(?P<step>.+)$', ishtar_forms.item_search_wizard, name='item_search'), - url(BASE_URL + r'item_creation/(?P<step>.+)$', + url(r'item_creation/(?P<step>.+)$', ishtar_forms.item_creation_wizard, name='item_creation'), - url(BASE_URL + r'item_modification/(?P<step>.+)$', + url(r'item_modification/(?P<step>.+)$', ishtar_forms.item_modification_wizard, name='item_modification'), - url(BASE_URL + r'item_source_creation/(?P<step>.+)$', + url(r'item_source_creation/(?P<step>.+)$', ishtar_forms.item_source_creation_wizard, name='item_source_creation'), - url(BASE_URL + r'item_source_modification/(?P<step>.+)$', + url(r'item_source_modification/(?P<step>.+)$', ishtar_forms.item_source_modification_wizard, name='item_source_modification'), - url(BASE_URL + r'item_source_deletion/(?P<step>.+)$', + url(r'item_source_deletion/(?P<step>.+)$', ishtar_forms.item_source_deletion_wizard, name='item_source_deletion'), # Treatments - url(BASE_URL + r'treatment_creation/(?P<step>.+)$', + url(r'treatment_creation/(?P<step>.+)$', ishtar_forms.treatment_creation_wizard, name='treatment_creation'), - url(BASE_URL + r'warehouse_packaging/(?P<step>.+)$', + url(r'warehouse_packaging/(?P<step>.+)$', ishtar_forms.warehouse_packaging_wizard, name='warehouse_packaging'), - ) + )""" for section in menu.childs: for menu_item in section.childs: if hasattr(menu_item, 'childs'): @@ -137,82 +142,82 @@ actions = r"|".join(actions) # other views urlpatterns += patterns('ishtar.ishtar_base.views', # General - url(BASE_URL + r'(?P<action_slug>' + actions + r')/$', 'action', + url(r'(?P<action_slug>' + actions + r')/$', 'action', name='action'), - url(BASE_URL + r'new-person/(?P<parent_name>.+)?/$', + url(r'new-person/(?P<parent_name>.+)?/$', 'new_person', name='new-person'), - url(BASE_URL + r'autocomplete-person/([0-9_]+)?$', 'autocomplete_person', + url(r'autocomplete-person/([0-9_]+)?$', 'autocomplete_person', name='autocomplete-person'), - url(BASE_URL + r'autocomplete-town/$', 'autocomplete_town', + url(r'autocomplete-town/$', 'autocomplete_town', name='autocomplete-town'), - url(BASE_URL + r'new-author/(?P<parent_name>.+)?/$', + url(r'new-author/(?P<parent_name>.+)?/$', 'new_author', name='new-author'), - url(BASE_URL + r'autocomplete-author/$', 'autocomplete_author', + url(r'autocomplete-author/$', 'autocomplete_author', name='autocomplete-author'), - url(BASE_URL + r'new-organization/(?P<parent_name>.+)?/$', + url(r'new-organization/(?P<parent_name>.+)?/$', 'new_organization', name='new-organization'), - url(BASE_URL + r'autocomplete-organization/([0-9_]+)?$', + url(r'autocomplete-organization/([0-9_]+)?$', 'autocomplete_organization', name='autocomplete-organization'), - url(BASE_URL + r'new-warehouse/(?P<parent_name>.+)?/$', + url(r'new-warehouse/(?P<parent_name>.+)?/$', 'new_warehouse', name='new-warehouse'), - url(BASE_URL + r'autocomplete-warehouse/$', 'autocomplete_warehouse', + url(r'autocomplete-warehouse/$', 'autocomplete_warehouse', name='autocomplete-warehouse'), # Archaelogical files - url(BASE_URL + r'autocomplete-file/$', 'autocomplete_file', + url(r'autocomplete-file/$', 'autocomplete_file', name='autocomplete-file'), - url(BASE_URL + r'get-file/(?P<type>.+)?$', 'get_file', + url(r'get-file/(?P<type>.+)?$', 'get_file', name='get-file'), - url(BASE_URL + r'get-file-full/(?P<type>.+)?$', 'get_file', + url(r'get-file-full/(?P<type>.+)?$', 'get_file', name='get-file-full', kwargs={'full':True}), - url(BASE_URL + r'get-administrativeactfile/(?P<type>.+)?$', + url(r'get-administrativeactfile/(?P<type>.+)?$', 'get_administrativeactfile', name='get-administrativeactfile'), - url(BASE_URL + r'show-file/(?P<pk>.+)?/(?P<type>.+)?$', 'show_file', + url(r'show-file/(?P<pk>.+)?/(?P<type>.+)?$', 'show_file', name='show-file'), - url(BASE_URL + r'show-historized-file/(?P<pk>.+)?/(?P<date>.+)?$', + url(r'show-historized-file/(?P<pk>.+)?/(?P<date>.+)?$', 'show_file', name='show-historized-file'), - url(BASE_URL + r'revert-file/(?P<pk>.+)/(?P<date>.+)$', + url(r'revert-file/(?P<pk>.+)/(?P<date>.+)$', 'revert_file', name='revert-file'), # Operations - url(BASE_URL + r'autocomplete-operation/$', 'autocomplete_operation', + url(r'autocomplete-operation/$', 'autocomplete_operation', name='autocomplete-operation'), - url(BASE_URL + r'get-operation/(?P<type>.+)?$', 'get_operation', + url(r'get-operation/(?P<type>.+)?$', 'get_operation', name='get-operation'), - url(BASE_URL + r'get-operation-full/(?P<type>.+)?$', 'get_operation', + url(r'get-operation-full/(?P<type>.+)?$', 'get_operation', name='get-operation-full', kwargs={'full':True}), - url(BASE_URL + r'get-available-operation-code/(?P<year>.+)?$', + url(r'get-available-operation-code/(?P<year>.+)?$', 'get_available_operation_code', name='get_available_operation_code'), - url(BASE_URL + r'revert-operation/(?P<pk>.+)/(?P<date>.+)$', + url(r'revert-operation/(?P<pk>.+)/(?P<date>.+)$', 'revert_operation', name='revert-operation'), - url(BASE_URL + r'show-operation/(?P<pk>.+)?/(?P<type>.+)?$', + url(r'show-operation/(?P<pk>.+)?/(?P<type>.+)?$', 'show_operation', name='show-operation'), - url(BASE_URL + r'get-administrativeactop/(?P<type>.+)?$', + url(r'get-administrativeactop/(?P<type>.+)?$', 'get_administrativeactop', name='get-administrativeactop'), - url(BASE_URL + r'get-operationsource/(?P<type>.+)?$', + url(r'get-operationsource/(?P<type>.+)?$', 'get_operationsource', name='get-operationsource'), # Context records - url(BASE_URL + r'show-contextrecord/(?P<pk>.+)?/(?P<type>.+)?$', + url(r'show-contextrecord/(?P<pk>.+)?/(?P<type>.+)?$', 'show_contextrecord', name='show-contextrecord'), - url(BASE_URL + r'get-contextrecord/(?P<type>.+)?$', 'get_contextrecord', + url(r'get-contextrecord/(?P<type>.+)?$', 'get_contextrecord', name='get-contextrecord'), - url(BASE_URL + r'get-contextrecord-full/(?P<type>.+)?$', + url(r'get-contextrecord-full/(?P<type>.+)?$', 'get_contextrecord', name='get-contextrecord-full', kwargs={'full':True}), - url(BASE_URL + r'get-contexrecordsource/(?P<type>.+)?$', + url(r'get-contexrecordsource/(?P<type>.+)?$', 'get_contextrecordsource', name='get-contextrecordsource'), # Finds - url(BASE_URL + r'update-current-item/$', 'update_current_item', + url(r'update-current-item/$', 'update_current_item', name='update-current-item'), - url(BASE_URL + r'get-item/(?P<type>.+)?$', 'get_archaeologicalitem', + url(r'get-item/(?P<type>.+)?$', 'get_archaeologicalitem', name='get-item'), - url(BASE_URL + r'get-item-full/(?P<type>.+)?$', 'get_archaeologicalitem', + url(r'get-item-full/(?P<type>.+)?$', 'get_archaeologicalitem', name='get-item-full', kwargs={'full':True}), - url(BASE_URL + r'get-itemsource/(?P<type>.+)?$', + url(r'get-itemsource/(?P<type>.+)?$', 'get_itemsource', name='get-itemsource'), - url(BASE_URL + r'get-container/$', 'get_container', + url(r'get-container/$', 'get_container', name='get-container'), # Treatments - url(BASE_URL + r'autocomplete-container/?$', + url(r'autocomplete-container/?$', 'autocomplete_container', name='autocomplete-container'), - url(BASE_URL + r'new-container/(?P<parent_name>.+)?/$', + url(r'new-container/(?P<parent_name>.+)?/$', 'new_container', name='new-container'), ) diff --git a/ishtar/ishtar_base/views.py b/ishtar/ishtar_base/views.py index 94a37d46e..4a7f276fb 100644 --- a/ishtar/ishtar_base/views.py +++ b/ishtar/ishtar_base/views.py @@ -17,7 +17,11 @@ # See the file COPYING for details. -import tidy +try: + import tidy +except: + from tidylib import tidy_document as tidy + import re import csv import json @@ -27,15 +31,16 @@ import cStringIO as StringIO from tempfile import NamedTemporaryFile import ho.pisa as pisa +from django.contrib.formtools.wizard.views import NamedUrlWizardView +from django.core import serializers +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse, NoReverseMatch +from django.db.models import Q from django.http import HttpResponse, Http404 +from django.shortcuts import render_to_response, redirect from django.template import RequestContext, loader from django.template.defaultfilters import slugify -from django.shortcuts import render_to_response, redirect from django.utils.translation import ugettext, ugettext_lazy as _ -from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import reverse, NoReverseMatch -from django.db.models import Q -from django.core import serializers from ishtar import settings if settings.XHTML2ODT_PATH: @@ -45,6 +50,9 @@ if settings.XHTML2ODT_PATH: from menus import menu import forms_main as ishtar_forms + +from ishtar.ishtar_base.forms import FinalForm +from ishtar.ishtar_base.forms_common import PersonForm import models CSV_OPTIONS = {'delimiter':';', 'quotechar':'"', 'quoting':csv.QUOTE_ALL} @@ -58,6 +66,613 @@ def index(request): return render_to_response('index.html', dct, context_instance=RequestContext(request)) +class Wizard(NamedUrlWizardView): + model = None + 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 + 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""" + 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_wizard_name(self): + """As the class name can interfere when reused, use the url_name""" + return self.url_name + + def get_template_names(self): + 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""" + context = super(Wizard, self).get_context_data(form) + step = self.steps.first + current_step = self.steps.current + context.update({'current_step':self.form_list[current_step]}) + if step == current_step: + return context + previous_steps, next_steps, previous_step_counter = [], [], 0 + while step: + if step == current_step: + break + previous_steps.append(self.form_list[step]) + step = self.steps.next + previous_step_counter += 1 + context.update({'previous_steps':previous_steps, + 'previous_step_counter':previous_step_counter}) + # 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(request, storage, 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(request, storage, 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""" + 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): + dct['history_modifier'] = self.request.user + return dct + + def done(self, form_list, return_object=False, **kwargs): + """Save to the model""" + 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""" + return self.model + + def get_current_saved_object(self): + """Permit a distinguo when saved model is not the base selected model""" + return self.get_current_object() + + def save_model(self, dct, m2m, whole_associated_models, form_list, + return_object): + 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""" + 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""" + 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(request, storage, 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 + """ + 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""" + 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 + """ + 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""" + 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""" + current_obj = None + main_form_key = 'selec-' + self.url_name + try: + idx = int(self.session_get_value(self.request, self.storage, + main_form_key, 'pk')) + current_obj = self.model.objects.get(pk=idx) + except(TypeError, ValueError, ObjectDoesNotExist): + pass + return current_obj + + def get_form_initial(self, step): + current_obj = self.get_current_object() + current_step = self.steps.current + 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) + 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""" + 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 PersonWizard(Wizard): + model = models.Person + +person_creation_wizard = PersonWizard.as_view([ + ('identity-person_creation', PersonForm), + ('final-person_creation', FinalForm)], + url_name='person_creation_step',) + + def update_current_item(request): if not request.is_ajax() and not request.method == 'POST': raise Http404 diff --git a/ishtar/templates/confirm_wizard.html b/ishtar/templates/ishtar/wizard/confirm_wizard.html index 371de39c3..bd80e3967 100644 --- a/ishtar/templates/confirm_wizard.html +++ b/ishtar/templates/ishtar/wizard/confirm_wizard.html @@ -19,9 +19,10 @@ {% endfor %} </table> {% endfor %} - {%if not form.is_hidden %} +{{wizard.management_form}} + {%if not wizard.form.is_hidden %} <table> - {{ form.as_table }} + {{ wizard.form.as_table }} </table> {%endif%} <p>{%if confirm_end_msg %}{{confirm_end_msg|safe}}{%else%}{% trans "Would you like to save them?" %}{%endif%}</p> diff --git a/ishtar/templates/default_wizard.html b/ishtar/templates/ishtar/wizard/default_wizard.html index a71565b6e..1996f4e46 100644 --- a/ishtar/templates/default_wizard.html +++ b/ishtar/templates/ishtar/wizard/default_wizard.html @@ -18,19 +18,21 @@ </ul> <div class='form'> {% if reminder %}<div class='reminder'>{{ reminder }}</div>{%endif%} -{% if form.forms %} +{{ wizard.form.media }} +{{ wizard.management_form }} +{% if wizard.form.forms %} + {{ wizard.form.management_form }} <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%} + {% for formsetform in wizard.form.forms %} {% table_form formsetform %} {% endfor %} <tr class='modify'><td colspan="2"><button name="formset_modify" value="{{form_step}}">{% trans "Add/Modify" %}</button></td></tr></li> </table> {% else %} <table> -{% table_form form %} +{% table_form wizard.form %} </table> {% endif %} <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> diff --git a/ishtar/urls.py b/ishtar/urls.py index baa906979..2723eb1cf 100644 --- a/ishtar/urls.py +++ b/ishtar/urls.py @@ -3,7 +3,7 @@ from django.conf.urls.defaults import * from django.contrib.auth.models import User from django.contrib import admin admin.autodiscover() -admin.site.unregister(User) +#admin.site.unregister(User) from settings import URL_PATH |