diff options
Diffstat (limited to 'ishtar_common/forms_common.py')
-rw-r--r-- | ishtar_common/forms_common.py | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py new file mode 100644 index 000000000..d034f6ddf --- /dev/null +++ b/ishtar_common/forms_common.py @@ -0,0 +1,518 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +""" +Administrative forms definitions: manage accounts and persons +""" +import datetime + +from django import forms +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core import validators +from django.core.mail import send_mail +from django.core.exceptions import ObjectDoesNotExist +from django.forms.formsets import formset_factory, DELETION_FIELD_NAME +from django.template import Context, RequestContext, loader +from django.shortcuts import render_to_response +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +import models +import widgets +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 " + u"town you would like to select. The search is insensitive to case.</p>\n" + u"<p>Only the first twenty results are displayed but specifying the " + u"department code is generally sufficient to get the appropriate result.</p>" + u"\n<p class='example'>For instance type \"saint denis 93\" for getting " + u"the french town Saint-Denis in the Seine-Saint-Denis department.</p>") + # !FIXME hard_link, reverse_lazy doen't seem to work with formsets + return forms.IntegerField( + widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ + 'autocomplete-town', associated_model=models.Town), + validators=[models.valid_id(models.Town)], label=label, + help_text=mark_safe(help_text), required=required) + +def get_person_field(label=_(u"Person"), required=True, person_type=None): + # !FIXME hard_link, reverse_lazy doen't seem to work with formsets + widget = None + url = "/" + settings.URL_PATH + 'autocomplete-person' + if person_type: + if isinstance(person_type, unicode) or isinstance(person_type, str): + person_type = models.PersonType.objects.get(txt_idx=person_type) + url += u"/" + unicode(person_type.pk) + widget = widgets.JQueryAutoComplete(url, associated_model=models.Person) + return forms.IntegerField(widget=widget, label=label, required=required, + validators=[models.valid_id(models.Person)]) + +def get_warehouse_field(label=_(u"Warehouse"), required=True): + # !FIXME hard_link, reverse_lazy doen't seem to work with formsets + url = "/" + settings.URL_PATH + 'autocomplete-warehouse' + widget = widgets.JQueryAutoComplete(url, associated_model=models.Warehouse) + return forms.IntegerField(widget=widget, label=label, required=required, + validators=[models.valid_id(models.Warehouse)]) + +class WarehouseForm(forms.Form): + name = forms.CharField(label=_(u"Name"), max_length=40, + validators=[name_validator]) + warehouse_type = forms.ChoiceField(label=_(u"Warehouse type"), + choices=[]) + person_in_charge = forms.IntegerField(label=_(u"Person in charge"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-person'), associated_model=models.Person), + validators=[models.valid_id(models.Person)], + required=False) + comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, + required=False) + address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, + required=False) + address_complement = forms.CharField(label=_(u"Address complement"), + widget=forms.Textarea, required=False) + postal_code = forms.CharField(label=_(u"Postal code"), max_length=10, + required=False) + town = forms.CharField(label=_(u"Town"), max_length=30, required=False) + country = forms.CharField(label=_(u"Country"), max_length=30, + required=False) + phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) + mobile_phone = forms.CharField(label=_(u"Town"), max_length=18, + required=False) + + def __init__(self, *args, **kwargs): + super(WarehouseForm, self).__init__(*args, **kwargs) + self.fields['warehouse_type'].choices = \ + models.WarehouseType.get_types() + self.fields['warehouse_type'].help_text = \ + models.WarehouseType.get_help() + + def save(self, user): + dct = self.cleaned_data + dct['history_modifier'] = user + dct['warehouse_type'] = models.WarehouseType.objects.get( + pk=dct['warehouse_type']) + if 'person_in_charge' in dct and dct['person_in_charge']: + dct['person_in_charge'] = models.Person.objects.get( + pk=dct['person_in_charge']) + new_item = models.Warehouse(**dct) + new_item.save() + return new_item + +class OrganizationForm(forms.Form): + name = forms.CharField(label=_(u"Name"), max_length=40, + validators=[name_validator]) + organization_type = forms.ChoiceField(label=_(u"Organization type"), + choices=[]) + address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, + required=False) + address_complement = forms.CharField(label=_(u"Address complement"), + widget=forms.Textarea, required=False) + postal_code = forms.CharField(label=_(u"Postal code"), max_length=10, + required=False) + town = forms.CharField(label=_(u"Town"), max_length=30, required=False) + country = forms.CharField(label=_(u"Country"), max_length=30, + required=False) + phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) + mobile_phone = forms.CharField(label=_(u"Town"), max_length=18, + required=False) + + def __init__(self, *args, **kwargs): + super(OrganizationForm, self).__init__(*args, **kwargs) + self.fields['organization_type'].choices = \ + models.OrganizationType.get_types() + self.fields['organization_type'].help_text = \ + models.OrganizationType.get_help() + + def save(self, user): + dct = self.cleaned_data + dct['history_modifier'] = user + dct['organization_type'] = models.OrganizationType.objects.get( + pk=dct['organization_type']) + new_item = models.Organization(**dct) + new_item.save() + return new_item + +class PersonFormSelection(forms.Form): + form_label = _(u"Person search") + associated_models = {'pk':models.Person} + currents = {'pk':models.Person} + pk = forms.IntegerField(label=_("Person"), + widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-person'), + associated_model=models.Person), + validators=[models.valid_id(models.Person)]) + +class PersonForm(forms.Form): + form_label = _("Identity") + associated_models = {'attached_to':models.Organization, + 'person_type':models.PersonType} + title = forms.ChoiceField(label=_("Title"), choices=models.Person.TYPE) + surname = forms.CharField(label=_(u"Surname"), max_length=20, + validators=[name_validator]) + name = forms.CharField(label=_(u"Name"), max_length=30, + validators=[name_validator]) + email = forms.CharField(label=_(u"Email"), max_length=40, required=False, + validators=[validators.validate_email]) + person_type = forms.ChoiceField(label=_("Person type"), + choices=[]) + attached_to = forms.IntegerField(label=_("Current organization"), + widget=widgets.JQueryAutoComplete(reverse_lazy('autocomplete-organization'), + associated_model=models.Organization, new=True), + validators=[models.valid_id(models.Organization)], required=False) + + def __init__(self, *args, **kwargs): + super(PersonForm, self).__init__(*args, **kwargs) + self.fields['person_type'].choices = models.PersonType.get_types() + self.fields['person_type'].help_text = models.PersonType.get_help() + + def save(self, user): + dct = self.cleaned_data + dct['history_modifier'] = user + dct['person_type'] = models.PersonType.objects.get( + pk=dct['person_type']) + if 'attached_to' in dct and dct['attached_to']: + dct['attached_to'] = models.Organization.objects.get( + pk=dct['attached_to']) + new_item = models.Person(**dct) + new_item.save() + return new_item + +''' +person_search_wizard = SearchWizard([ + ('general-person_search', PersonFormSelection)], + url_name='person_search',) + + + +person_modification_wizard = PersonWizard([ + ('selec-person_modification', PersonFormSelection), + ('identity-person_modification', PersonForm), + ('final-person_modification', FinalForm)], + 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, request, storage, form_list, **kwargs): + """ + Save the account + """ + dct = {} + for form in form_list: + if not form.is_valid(): + return self.render(request, storage, 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(request, storage) + if not person: + return self.render(request, storage, 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(request)) + return res + + def get_form(self, request, storage, step=None, data=None, files=None): + """ + Display the "Send email" field if necessary + """ + form = super(AccountWizard, self).get_form(request, storage, step, data, + files) + if not hasattr(form, 'is_hidden'): + return form + if self.session_get_value(request, storage, + 'account-account_management', 'hidden_password'): + form.is_hidden = False + return form + +''' +class AccountForm(forms.Form): + form_label = _("Account") + associated_models = {'pk':models.Person} + currents = {'pk':models.Person} + pk = forms.IntegerField(label=u"", widget=forms.HiddenInput, required=False) + username = forms.CharField(label=_(u"Account"), max_length=30) + email = forms.CharField(label=_(u"Email"), max_length=75, + validators=[validators.validate_email]) + hidden_password = forms.CharField(label=_(u"New password"), max_length=128, + widget=forms.PasswordInput, required=False, + validators=[validators.MinLengthValidator(4)]) + hidden_password_confirm = forms.CharField( + label=_(u"New password (confirmation)"), max_length=128, + widget=forms.PasswordInput, required=False) + + def __init__(self, *args, **kwargs): + if 'initial' in kwargs and 'pk' in kwargs['initial']: + try: + person = models.Person.objects.get(pk=kwargs['initial']['pk']) + account = models.IshtarUser.objects.get(person=person) + kwargs['initial'].update({'username':account.username, + 'email':account.email}) + except ObjectDoesNotExist: + pass + return super(AccountForm, self).__init__(*args, **kwargs) + + def clean(self): + cleaned_data = self.cleaned_data + password = cleaned_data.get("hidden_password") + if password and password != cleaned_data.get("hidden_password_confirm"): + raise forms.ValidationError(_(u"Your password and confirmation " + u"password do not match.")) + if not cleaned_data.get("pk"): + models.is_unique(User, 'username')(cleaned_data.get("username")) + if not password: + raise forms.ValidationError(_(u"You must provide a correct \ +password.")) + # check username unicity + usernames = models.IshtarUser.objects.filter(username= + cleaned_data.get('username')) + if cleaned_data.get('pk'): + usernames.exclude(pk=cleaned_data.get('pk')) + if usernames.count(): + raise forms.ValidationError(_(u"This username already exists.")) + return cleaned_data + +class FinalAccountForm(forms.Form): + final = True + form_label = _("Confirm") + send_password = forms.BooleanField(label=_(u"Send the new password by " + u"email?"), required=False) + + 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' + associated_models = {'town':models.Town} + town = get_town_field(required=False) + +class TownFormSet(FormSet): + def clean(self): + """Checks that no towns are duplicated.""" + return self.check_duplicate(('town',), + _("There are identical towns.")) + +TownFormset = formset_factory(TownForm, can_delete=True, formset=TownFormSet) +TownFormset.form_label = _("Towns") + +class ParcelForm(forms.Form): + form_label = _("Parcels") + base_model = 'parcel' + associated_models = {'parcel':models.Parcel, 'town':models.Town} + town = forms.ChoiceField(label=_("Town"), choices=(), required=False, + validators=[models.valid_id(models.Town)]) + section = forms.CharField(label=_(u"Section"), required=False, + validators=[validators.MaxLengthValidator(4)]) + parcel_number = forms.CharField(label=_(u"Parcel number"), required=False, + validators=[validators.MaxLengthValidator(6)]) + year = forms.IntegerField(label=_("Year"), required=False, + validators=[validators.MinValueValidator(1900), + validators.MaxValueValidator(2100)]) + def __init__(self, *args, **kwargs): + towns = None + if 'data' in kwargs and 'TOWNS' in kwargs['data']: + towns = kwargs['data']['TOWNS'] + # clean data if not "real" data + prefix_value = kwargs['prefix'] + '-town' + if not [k for k in kwargs['data'].keys() + if k.startswith(prefix_value) and kwargs['data'][k]]: + kwargs['data'] = None + if 'files' in kwargs: + kwargs.pop('files') + super(ParcelForm, self).__init__(*args, **kwargs) + if towns: + self.fields['town'].choices = [('', '--')] + towns + + def clean(self): + """Check required fields""" + if any(self.errors): + return + if not self.cleaned_data or DELETION_FIELD_NAME in self.cleaned_data \ + and self.cleaned_data[DELETION_FIELD_NAME]: + return + for key in ('town', 'parcel_number', 'section'): + if not key in self.cleaned_data or not self.cleaned_data[key]: + raise forms.ValidationError(_(u"Town section and parcel number " + u"fields are required.")) + return self.cleaned_data + + +class ParcelFormSet(FormSet): + def clean(self): + """Checks that no parcels are duplicated.""" + return self.check_duplicate(('town', 'parcel_number', 'year'), + _(u"There are identical parcels.")) + +ParcelFormSet = formset_factory(ParcelForm, can_delete=True, + formset=ParcelFormSet) +ParcelFormSet.form_label = _(u"Parcels") + +###################### +# Sources management # +###################### +''' +class SourceWizard(Wizard): + model = None + def get_extra_model(self, dct, request, storage, form_list): + dct = super(SourceWizard, self).get_extra_model(dct, request, storage, + form_list) + if 'history_modifier' in dct: + dct.pop('history_modifier') + return dct +''' +class SourceForm(forms.Form): + form_label = _(u"Documentation informations") + associated_models = {'source_type':models.SourceType} + title = forms.CharField(label=_(u"Title"), + validators=[validators.MaxLengthValidator(200)]) + source_type = forms.ChoiceField(label=_(u"Source type"), choices=[]) + associated_url = forms.URLField(required=False, + label=_(u"Numerical ressource (web address)")) + receipt_date = forms.DateField(label=_(u"Receipt date"), required=False, + widget=widgets.JQueryDate) + creation_date = forms.DateField(label=_(u"Creation date"), required=False, + widget=widgets.JQueryDate) + + def __init__(self, *args, **kwargs): + super(SourceForm, self).__init__(*args, **kwargs) + self.fields['source_type'].choices = models.SourceType.get_types() + +class SourceSelect(forms.Form): + authors = forms.IntegerField( + widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ + 'autocomplete-author', associated_model=models.Author), + validators=[models.valid_id(models.Author)], label=_(u"Author"), + required=False) + + source_type = forms.ChoiceField(label=_("Source type"), choices=[]) + + def __init__(self, *args, **kwargs): + super(SourceSelect, self).__init__(*args, **kwargs) + self.fields['source_type'].choices = models.SourceType.get_types() + self.fields['source_type'].help_text = models.SourceType.get_help() + +class SourceDeletionForm(FinalForm): + confirm_msg = " " + confirm_end_msg = _(u"Would you like to delete this documentation?") + +###################### +# Authors management # +###################### + +class AuthorForm(forms.Form): + form_label = _(u"Author") + associated_models = {'person':models.Person, + 'author_type':models.AuthorType} + person = forms.IntegerField( + widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ + 'autocomplete-person', associated_model=models.Person, new=True), + validators=[models.valid_id(models.Person)], label=_(u"Person")) + author_type = forms.ChoiceField(label=_(u"Author type"), choices=[]) + + def __init__(self, *args, **kwargs): + super(AuthorForm, self).__init__(*args, **kwargs) + self.fields['author_type'].choices = models.AuthorType.get_types() + + def save(self, user): + dct = self.cleaned_data + dct['author_type'] = models.AuthorType.objects.get( + pk=dct['author_type']) + dct['person'] = models.Person.objects.get(pk=dct['person']) + new_item = models.Author(**dct) + new_item.save() + return new_item + + +class AuthorFormSelection(forms.Form): + form_label = _(u"Author selection") + base_model = 'author' + associated_models = {'author':models.Author} + author = forms.IntegerField( + widget=widgets.JQueryAutoComplete("/" + settings.URL_PATH + \ + 'autocomplete-author', associated_model=models.Author, new=True), + validators=[models.valid_id(models.Author)], label=_(u"Author")) + +class AuthorFormSet(FormSet): + def clean(self): + """Checks that no author are duplicated.""" + return self.check_duplicate(('author',), + _("There are identical authors.")) + +AuthorFormset = formset_factory(AuthorFormSelection, can_delete=True, + formset=AuthorFormSet) +AuthorFormset.form_label = _("Authors") + |