#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2011 É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. """ Administrative forms definitions: manage accounts and persons """ import datetime from django import forms from django.template import Context, RequestContext, loader from django.shortcuts import render_to_response from django.core import validators from django.core.mail import send_mail from django.core.exceptions import ObjectDoesNotExist from django.utils.safestring import mark_safe from django.forms.formsets import formset_factory, DELETION_FIELD_NAME from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User from django.contrib.sites.models import Site from ishtar import settings 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"

Type name, department code and/or postal code of the " u"town you would like to select. The search is insensitive to case.

\n" u"

Only the first twenty results are displayed but specifying the " u"department code is generally sufficient to get the appropriate result.

" u"\n

For instance type \"saint denis 93\" for getting " u"the french town Saint-Denis in the Seine-Saint-Denis department.

") # !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")