#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2013 Étienne Loks # Copyright (C) 2007 skam # (http://djangosnippets.org/snippets/233/) # 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. from django import forms from django.conf import settings from django.core.urlresolvers import resolve, reverse from django.db.models import fields from django.forms import ClearableFileInput from django.forms.widgets import flatatt from django.template import Context, loader from django.utils.encoding import smart_unicode from django.utils.functional import lazy from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.simplejson import JSONEncoder from django.utils.translation import ugettext_lazy as _ import models reverse_lazy = lazy(reverse, unicode) class MultipleAutocompleteField(forms.MultipleChoiceField): def __init__(self, *args, **kwargs): model = None if 'model' in kwargs: model = kwargs.pop('model') if 'choices' not in kwargs and model: kwargs['choices'] = [(i.pk, unicode(i))for i in model.objects.all()] new = kwargs.pop('new') if 'new' in kwargs else None if 'widget' not in kwargs and model: kwargs['widget'] = JQueryAutoComplete(reverse_lazy( 'autocomplete-'+model.__name__.lower()), associated_model=model, new=new, multiple=True) super(MultipleAutocompleteField, self).__init__(*args, **kwargs) def clean(self, value): if value: # clean JS messup with values try: if type(value) not in (list, tuple): value = [value] else: val = value value = [] for v in val: v = unicode(v).strip('[').strip(']' ).strip('u').strip("'").strip('"') value += [int(v.strip()) for v in list(set(v.split(','))) if v.strip()] except (TypeError, ValueError): value = [] else: value = [] return super(MultipleAutocompleteField, self).clean(value) class DeleteWidget(forms.CheckboxInput): def render(self, name, value, attrs=None): final_attrs = flatatt(self.build_attrs(attrs, name=name, value='1')) output = [''] output.append(u"%s" % (final_attrs, _("Delete"))) output.append('') return mark_safe('\n'.join(output)) class ImageFileInput(ClearableFileInput): template_with_initial = u'%(initial)s'\ u' %(clear_template)s
%(input_text)s: %(input)s' class SquareMeterWidget(forms.TextInput): def render(self, name, value, attrs=None): if not value: value = u"" final_attrs = flatatt(self.build_attrs(attrs, name=name, value=value)) dct = {'final_attrs':final_attrs, 'unit':settings.SURFACE_UNIT_LABEL, 'id':attrs['id'], "safe_id":attrs['id'].replace('-', '_')} t = loader.get_template('blocks/SquareMeterWidget.html') rendered = t.render(Context(dct)) return mark_safe(rendered) AreaWidget = forms.TextInput if settings.SURFACE_UNIT == 'square-metre': global AreaWidget AreaWidget = SquareMeterWidget class JQueryDate(forms.TextInput): def __init__(self, *args, **kwargs): super(JQueryDate, self).__init__(*args, **kwargs) if 'class' not in self.attrs: self.attrs['class'] = '' self.attrs['class'] = 'date-pickup' def render(self, name, value=None, attrs=None): rendered = super(JQueryDate, self).render(name, value, attrs) # use window.onload to be sure that datepicker don't interfere # with autocomplete fields rendered += """ """ % {"name":name, "country":settings.COUNTRY} return rendered class JQueryAutoComplete(forms.TextInput): def __init__(self, source, associated_model=None, options={}, attrs={}, new=False, multiple=False): """ Source can be a list containing the autocomplete values or a string containing the url used for the request. """ self.options = None self.attrs = {} self.source = source self.associated_model = associated_model if len(options) > 0: self.options = JSONEncoder().encode(options) self.attrs.update(attrs) self.new = new self.multiple = multiple def value_from_datadict(self, data, files, name): if self.multiple: return data.getlist(name, None) else: return data.get(name, None) def render_js(self, field_id): if isinstance(self.source, list): source = JSONEncoder().encode(self.source) elif isinstance(self.source, str) or isinstance(self.source, unicode): source = "'%s'" % escape(self.source) else: try: source = "'" + unicode(self.source) + "'" except: raise ValueError('source type is not valid') dct = {'source':mark_safe(source), 'field_id':field_id} if self.options: dct['options'] = mark_safe('%s' % self.options) js = "" tpl = 'blocks/JQueryAutocomplete.js' if self.multiple: tpl = 'blocks/JQueryAutocompleteMultiple.js' t = loader.get_template(tpl) js = t.render(Context(dct)) return js def render(self, name, value=None, attrs=None): attrs_hidden = self.build_attrs(attrs, name=name) attrs_select = self.build_attrs(attrs) if value: hiddens = [] selects = [] values = value if type(value) not in (list, tuple): values = unicode(escape(smart_unicode(value))) values = values.replace('[', '').replace(']', '') values = values.split(',') else: values = [] for v in value: values += v.split(',') for v in values: if not v: continue hiddens.append(v) selects.append(v) if self.associated_model: try: selects[-1] = unicode( self.associated_model.objects.get(pk=v)) except (self.associated_model.DoesNotExist, ValueError): selects.pop() hiddens.pop() if self.multiple: attrs_hidden['value'] = ", ".join(hiddens) if selects: selects.append("") attrs_select['value'] = ", ".join(selects) else: if hiddens and selects: attrs_hidden['value'] = hiddens[0] attrs_select['value'] = selects[0] if not self.attrs.has_key('id'): attrs_hidden['id'] = 'id_%s' % name attrs_select['id'] = 'id_select_%s' % name if 'class' not in attrs_select: attrs_select['class'] = 'autocomplete' new = '' if self.new: model_name = self.associated_model._meta.object_name.lower() url_new = reverse('new-' + model_name, args=[attrs_select['id']]) new = u' +' % url_new html = u'''%(new)s\ \ ''' % { 'attrs_select' : flatatt(attrs_select), 'attrs_hidden' : flatatt(attrs_hidden), 'js' : self.render_js(name), 'new':new } return html class JQueryJqGrid(forms.RadioSelect): COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" class Media: js = ['%s/js/i18n/grid.locale-%s.js' % (settings.STATIC_URL, settings.COUNTRY), '%s/js/jquery.jqGrid.min.js' % settings.STATIC_URL, ] css = {'all':['%s/media/ui.jqgrid.css' % settings.STATIC_URL, ]} def __init__(self, source, form, associated_model, attrs={}, table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], new=False, new_message="", source_full=None): self.source = source self.form = form self.attrs = attrs self.associated_model = associated_model self.table_cols = table_cols self.multiple = multiple self.multiple_cols = multiple_cols self.new, self.new_message = new, new_message self.source_full = source_full def render(self, name, value=None, attrs=None): t = loader.get_template('blocks/form_snippet.html') form = self.form() rendered = t.render(Context({'form':form})) dct = {} if self.new: model_name = self.associated_model._meta.object_name.lower() dct['url_new'] = reverse('new-' + model_name, args=['0']) dct['new_message'] = self.new_message extra_cols = [] col_names, col_idx = [], [] for k in form.get_input_ids(): #field = form.fields[k] col_idx.append(u'"%s"' % k) for field_name in getattr(self.associated_model, self.table_cols): field = self.associated_model keys = field_name.split('.') field_verbose_name = "" for key in keys: if hasattr(field, 'rel'): field = field.rel.to try: field = field._meta.get_field(key) field_verbose_name = field.verbose_name field_name = field.name except fields.FieldDoesNotExist: if hasattr(field, key + '_lbl'): field_name = key field_verbose_name = getattr(field, key + '_lbl') else: continue col_names.append(u'"%s"' % field_verbose_name) extra_cols.append(self.COL_TPL % {'idx':field_name}) col_names = col_names and ", ".join(col_names) or "" col_idx = col_idx and ", ".join(col_idx) or "" extra_cols = extra_cols and ", ".join(extra_cols) or "" dct['encoding'] = settings.ENCODING or 'utf-8' dct['source'] = unicode(self.source) if unicode(self.source_full) and unicode(self.source_full) != 'None': dct['source_full'] = unicode(self.source_full) dct.update({'name':name, 'col_names':col_names, 'extra_cols':extra_cols, 'source':unicode(self.source), 'col_idx':col_idx, 'no_result':unicode(_("No results")), 'loading':unicode(_("Loading...")), 'remove':unicode(_(u"Remove")), 'sname':name.replace('-', ''), 'multiple':self.multiple, 'multi_cols': ",".join((u'"%d"' % col \ for col in self.multiple_cols)) }) t = loader.get_template('blocks/JQueryJqGrid.html') rendered += t.render(Context(dct)) return mark_safe(rendered)