#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2012 Étienne Loks # 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 . # See the file COPYING for details. try: import tidy except: from tidylib import tidy_document as tidy import re import csv import json import datetime import optparse import cStringIO as StringIO from tempfile import NamedTemporaryFile import ho.pisa as pisa from django.conf import settings from django.contrib.auth import logout 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.utils.translation import ugettext, ugettext_lazy as _ if settings.XHTML2ODT_PATH: import sys sys.path.append(settings.XHTML2ODT_PATH) from xhtml2odt import xhtml2odt from menus import menu import forms_main as ishtar_forms from ishtar_common.forms import FinalForm from ishtar_common.forms_common import PersonForm, PersonFormSelection,\ AccountForm, FinalAccountForm import models CSV_OPTIONS = {'delimiter':';', 'quotechar':'"', 'quoting':csv.QUOTE_ALL} ENCODING = settings.ENCODING or 'utf-8' def index(request): """ Main page """ dct = {} try: return render_to_response('index.html', dct, context_instance=RequestContext(request)) except NoReverseMatch: # probably rights exception (rights revoked) logout(request) return render_to_response('index.html', dct, context_instance=RequestContext(request)) 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(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 """ 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 PersonWizard(Wizard): model = models.Person person_creation_wizard = PersonWizard.as_view([ ('identity-person_creation', PersonForm), ('final-person_creation', FinalForm)], label=_(u"Person creation"), url_name='person_creation') class PersonModifWizard(PersonWizard): modification = True person_modification_wizard = PersonModifWizard.as_view([ ('selec-person_modification', PersonFormSelection), ('identity-person_modification', PersonForm), ('final-person_modification', FinalForm)], label=_(u"Person modification"), url_name='person_modification') 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 account_management_wizard = AccountWizard.as_view([ ('selec-account_management', PersonFormSelection), ('account-account_management', AccountForm), ('final-account_management', FinalAccountForm)], label=_(u"Account management"), url_name='account_management',) def update_current_item(request): if not request.is_ajax() and not request.method == 'POST': raise Http404 if 'value' in request.POST and 'item' in request.POST: request.session[request.POST['item']] = request.POST['value'] return HttpResponse('ok') def check_permission(request, action_slug, obj_id=None): if action_slug not in menu.items: #! TODO return True if obj_id: return menu.items[action_slug].is_available(request.user, obj_id) return menu.items[action_slug].can_be_available(request.user) def autocomplete_person(request, person_type=None): person_types = request.user.ishtaruser.person.person_type if (not request.user.has_perm('ishtar_common.view_person', models.Person) and not request.user.has_perm('ishtar_common.view_own_person', models.Person) and not person_types.rights.filter(wizard__url_name='person_search' ).count()): return HttpResponse(mimetype='text/plain') if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') q = request.GET.get('term') limit = request.GET.get('limit', 20) try: limit = int(limit) except ValueError: return HttpResponseBadRequest() query = Q() for q in q.split(' '): query = query & (Q(name__icontains=q) | Q(surname__icontains=q) | \ Q(email__icontains=q)) if person_type: try: typs = [int(tp) for tp in person_type.split('_') if tp] typ = models.PersonType.objects.filter(pk__in=typs).all() query = query & Q(person_type__in=typ) except (ValueError, ObjectDoesNotExist): pass limit = 20 persons = models.Person.objects.filter(query)[:limit] data = json.dumps([{'id':person.pk, 'value':unicode(person)} for person in persons if person]) return HttpResponse(data, mimetype='text/plain') def autocomplete_town(request): if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') q = request.GET.get('term') query = Q() for q in q.split(' '): extra = Q(name__icontains=q) if settings.COUNTRY == 'fr': extra = (extra | Q(numero_insee__istartswith=q) | \ Q(departement__label__istartswith=q)) query = query & extra limit = 20 towns = models.Town.objects.filter(query)[:limit] data = json.dumps([{'id':town.pk, 'value':unicode(town)} for town in towns]) return HttpResponse(data, mimetype='text/plain') from types import NoneType def format_val(val): if type(val) == NoneType: return u"" if type(val) == bool: if val: return unicode(_(u"True")) else: return unicode(_(u"False")) return unicode(val) HIERARCHIC_LEVELS = 5 HIERARCHIC_FIELDS = ['period', 'unit', 'material_type'] PRIVATE_FIELDS = ('id', 'history_modifier', 'order') def get_item(model, func_name, default_name, extra_request_keys=[], base_request={}, bool_fields=[]): """ Generic treatment of tables """ def func(request, data_type='json', full=False, **dct): if 'type' in dct: data_type = dct.pop('type') if not data_type: data_type = 'json' fields = [model._meta.get_field_by_name(k)[0] for k in model._meta.get_all_field_names()] request_keys = dict([(field.name, field.name + (hasattr(field, 'rel') and field.rel and '__pk' or '')) for field in fields]) request_keys.update(extra_request_keys) request_items = request.method == 'POST' and request.POST or request.GET dct = base_request.copy() try: old = 'old' in request_items and int(request_items['old']) except ValueError: return HttpResponse(None, mimetype='text/plain') for k in request_keys: q = request_items.get(k) if not q: continue dct[request_keys[k]] = q if not dct and 'submited' not in request_items: if default_name in request.session and \ request.session[default_name]: dct = {"pk":request.session[default_name]} if (not dct or data_type == 'csv') and func_name in request.session: dct = request.session[func_name] else: request.session[func_name] = dct for k in bool_fields: if k in dct: if dct[k] == u"1": dct.pop(k) else: dct[k] = dct[k] == u"2" and True or False # manage hierarchic conditions or_reqs = [] for req in dct.copy(): for k_hr in HIERARCHIC_FIELDS: if req.endswith(k_hr + '__pk'): val = dct.pop(req) reqs = Q(**{req:val}) req = req[:-2] + '__' for idx in xrange(HIERARCHIC_LEVELS): req = req[:-2] + 'parent__pk' q = Q(**{req:val}) reqs = reqs | q or_reqs.append(reqs) break query = Q(**dct) for or_req in or_reqs: query = query & or_req items = model.objects.filter(query) q = request_items.get('sidx') # manage sort tables if q and q in request_keys: ks = request_keys[q] if type(ks) not in (list, tuple): ks = [ks] orders = [] for k in ks: if k.endswith("__pk"): k = k[:-len("__pk")] + "__label" q = request_items.get('sord') sign = q and q == u'desc' and "-" or '' if '__' in k: k = k.split('__')[0] orders.append(sign+k) items = items.order_by(*orders) # pager management start, end = 0, None page_nb = 1 try: row_nb = int(request_items.get('rows')) except (ValueError, TypeError): row_nb = None if row_nb: try: page_nb = int(request_items.get('page')) assert page_nb >= 1 except (ValueError, AssertionError): pass start = (page_nb-1)*row_nb end = page_nb*row_nb items_nb = items.count() items = items[start:end] datas = [] if old: items = [item.get_previous(old) for item in items] table_cols = full and [field.name for field in model._meta.fields if field.name not in PRIVATE_FIELDS] \ or model.TABLE_COLS for item in items: data = [item.pk] for k in table_cols: vals = [item] for ky in k.split('.'): new_vals = [] for val in vals: if hasattr(val, 'all'): # manage related objects val = list(val.all()) for v in val: new_vals.append(getattr(v, ky)) elif val: new_vals.append(getattr(val, ky)) vals = new_vals if vals and hasattr(vals[0], 'all'): # manage last related objects new_vals = [] for val in vals: new_vals += list(val.all()) vals = new_vals data.append(", ".join([format_val(v) for v in vals]) or u"") datas.append(data) link_template = "%s" % \ (unicode(_("Details"))) if data_type == "json": rows = [] for data in datas: try: lnk = link_template % reverse('show-'+default_name, args=[data[0], '']) except NoReverseMatch: lnk = '' res = {'id':data[0], 'link':lnk} for idx, value in enumerate(data[1:]): if value: res[table_cols[idx].split('.')[-1]] = value rows.append(res) data = json.dumps({ "records":items_nb, "rows":rows, "page":page_nb, "total":items_nb/row_nb + 1, }) return HttpResponse(data, mimetype='text/plain') elif data_type == "csv": response = HttpResponse(mimetype='text/csv') n = datetime.datetime.now() filename = u'%s_%s.csv' % (default_name, n.strftime('%Y%m%d-%H%M%S')) response['Content-Disposition'] = 'attachment; filename=%s'%filename writer = csv.writer(response, **CSV_OPTIONS) col_names = [] for field_name in table_cols: try: field = model._meta.get_field(field_name) except: col_names.append(u"".encode(ENCODING)) continue col_names.append(unicode(field.verbose_name).encode(ENCODING)) writer.writerow(col_names) for data in datas: writer.writerow([val.encode(ENCODING) for val in data[1:]]) return response return HttpResponse(None, mimetype='text/plain') return func def show_item(model, name): def func(request, pk, **dct): try: item = model.objects.get(pk=pk) except ObjectDoesNotExist: return HttpResponse(None) doc_type = 'type' in dct and dct.pop('type') date = 'date' in dct and dct.pop('date') dct['window_id'] = "%s-%d-%s" % (name, item.pk, datetime.datetime.now().strftime('%M%s')) if date: try: date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') item = item.get_previous(date=date) assert item != None except (ValueError, AssertionError): return HttpResponse(None, mimetype='text/plain') dct['previous'] = item._previous dct['next'] = item._next else: historized = item.history.all() if historized: item.history_date = historized[0].history_date if len(historized) > 1: dct['previous'] = historized[1].history_date dct['item'], dct['item_name'] = item, name context_instance = RequestContext(request) context_instance.update(dct) n = datetime.datetime.now() filename = u'%s_%s_%s' % (name, slugify(unicode(item)), n.strftime('%Y%m%d-%H%M%S')) if doc_type == "odt" and settings.XHTML2ODT_PATH and \ settings.ODT_TEMPLATE: tpl = loader.get_template('sheet_%s.html' % name) content = tpl.render(context_instance) try: tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0, output_encoding='utf8', doctype='auto', wrap=0, char_encoding='utf8') html = str(tidy.parseString(content.encode('utf-8'), **tidy_options)) html = html.replace(" ", " ") html = re.sub(']*)>\n', '', html) odt = NamedTemporaryFile() options = optparse.Values() options.with_network = True for k, v in (('input', ''), ('output', odt.name), ('template', settings.ODT_TEMPLATE), ('with_network', True), ('top_header_level', 1), ('img_width', '8cm'), ('img_height', '6cm'), ('verbose', False), ('replace_keyword', 'ODT-INSERT'), ('cut_start', 'ODT-CUT-START'), ('htmlid', None), ('url', "#")): setattr(options, k, v) odtfile = xhtml2odt.ODTFile(options) odtfile.open() odtfile.import_xhtml(html) odtfile = odtfile.save() except xhtml2odt.ODTExportError, ex: return HttpResponse(content, content_type="application/xhtml") response = HttpResponse( mimetype='application/vnd.oasis.opendocument.text') response['Content-Disposition'] = 'attachment; filename=%s.odt' % \ filename response.write(odtfile) return response elif doc_type == 'pdf': tpl = loader.get_template('sheet_%s_pdf.html' % name) content = tpl.render(context_instance) result = StringIO.StringIO() html = content.encode('utf-8') html = html.replace("