diff options
Diffstat (limited to 'webapp/django/contrib/admin/widgets.py')
-rw-r--r-- | webapp/django/contrib/admin/widgets.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/webapp/django/contrib/admin/widgets.py b/webapp/django/contrib/admin/widgets.py new file mode 100644 index 0000000000..2bfbc9715d --- /dev/null +++ b/webapp/django/contrib/admin/widgets.py @@ -0,0 +1,238 @@ +""" +Form Widget classes specific to the Django admin site. +""" + +import copy + +from django import forms +from django.forms.widgets import RadioFieldRenderer +from django.forms.util import flatatt +from django.utils.text import truncate_words +from django.utils.translation import ugettext as _ +from django.utils.safestring import mark_safe +from django.utils.encoding import force_unicode +from django.conf import settings + +class FilteredSelectMultiple(forms.SelectMultiple): + """ + A SelectMultiple with a JavaScript filter interface. + + Note that the resulting JavaScript assumes that the SelectFilter2.js + library and its dependencies have been loaded in the HTML page. + """ + def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): + self.verbose_name = verbose_name + self.is_stacked = is_stacked + super(FilteredSelectMultiple, self).__init__(attrs, choices) + + def render(self, name, value, attrs=None, choices=()): + output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)] + output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {') + # TODO: "id_" is hard-coded here. This should instead use the correct + # API to determine the ID dynamically. + output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \ + (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX)) + return mark_safe(u''.join(output)) + +class AdminDateWidget(forms.TextInput): + class Media: + js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", + settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") + + def __init__(self, attrs={}): + super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}) + +class AdminTimeWidget(forms.TextInput): + class Media: + js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", + settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") + + def __init__(self, attrs={}): + super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}) + +class AdminSplitDateTime(forms.SplitDateTimeWidget): + """ + A SplitDateTime Widget that has some admin-specific styling. + """ + def __init__(self, attrs=None): + widgets = [AdminDateWidget, AdminTimeWidget] + # Note that we're calling MultiWidget, not SplitDateTimeWidget, because + # we want to define widgets. + forms.MultiWidget.__init__(self, widgets, attrs) + + def format_output(self, rendered_widgets): + return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \ + (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) + +class AdminRadioFieldRenderer(RadioFieldRenderer): + def render(self): + """Outputs a <ul> for this set of radio fields.""" + return mark_safe(u'<ul%s>\n%s\n</ul>' % ( + flatatt(self.attrs), + u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])) + ) + +class AdminRadioSelect(forms.RadioSelect): + renderer = AdminRadioFieldRenderer + +class AdminFileWidget(forms.FileInput): + """ + A FileField Widget that shows its current value if it has one. + """ + def __init__(self, attrs={}): + super(AdminFileWidget, self).__init__(attrs) + + def render(self, name, value, attrs=None): + output = [] + if value and hasattr(value, "url"): + output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \ + (_('Currently:'), value.url, value, _('Change:'))) + output.append(super(AdminFileWidget, self).render(name, value, attrs)) + return mark_safe(u''.join(output)) + +class ForeignKeyRawIdWidget(forms.TextInput): + """ + A Widget for displaying ForeignKeys in the "raw_id" interface rather than + in a <select> box. + """ + def __init__(self, rel, attrs=None): + self.rel = rel + super(ForeignKeyRawIdWidget, self).__init__(attrs) + + def render(self, name, value, attrs=None): + related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) + if self.rel.limit_choices_to: + url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in self.rel.limit_choices_to.items()]) + else: + url = '' + if not attrs.has_key('class'): + attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook. + output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + # TODO: "id_" is hard-coded here. This should instead use the correct + # API to determine the ID dynamically. + output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ + (related_url, url, name)) + output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % settings.ADMIN_MEDIA_PREFIX) + if value: + output.append(self.label_for_value(value)) + return mark_safe(u''.join(output)) + + def label_for_value(self, value): + return ' <strong>%s</strong>' % \ + truncate_words(self.rel.to.objects.get(pk=value), 14) + +class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): + """ + A Widget for displaying ManyToMany ids in the "raw_id" interface rather than + in a <select multiple> box. + """ + def __init__(self, rel, attrs=None): + super(ManyToManyRawIdWidget, self).__init__(rel, attrs) + + def render(self, name, value, attrs=None): + attrs['class'] = 'vManyToManyRawIdAdminField' + if value: + value = ','.join([str(v) for v in value]) + else: + value = '' + return super(ManyToManyRawIdWidget, self).render(name, value, attrs) + + def label_for_value(self, value): + return '' + + def value_from_datadict(self, data, files, name): + value = data.get(name, None) + if value and ',' in value: + return data[name].split(',') + if value: + return [value] + return None + + def _has_changed(self, initial, data): + if initial is None: + initial = [] + if data is None: + data = [] + if len(initial) != len(data): + return True + for pk1, pk2 in zip(initial, data): + if force_unicode(pk1) != force_unicode(pk2): + return True + return False + +class RelatedFieldWidgetWrapper(forms.Widget): + """ + This class is a wrapper to a given widget to add the add icon for the + admin interface. + """ + def __init__(self, widget, rel, admin_site): + self.is_hidden = widget.is_hidden + self.needs_multipart_form = widget.needs_multipart_form + self.attrs = widget.attrs + self.choices = widget.choices + self.widget = widget + self.rel = rel + # so we can check if the related object is registered with this AdminSite + self.admin_site = admin_site + + def __deepcopy__(self, memo): + obj = copy.copy(self) + obj.widget = copy.deepcopy(self.widget, memo) + obj.attrs = self.widget.attrs + memo[id(self)] = obj + return obj + + def render(self, name, value, *args, **kwargs): + rel_to = self.rel.to + related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower()) + self.widget.choices = self.choices + output = [self.widget.render(name, value, *args, **kwargs)] + if rel_to in self.admin_site._registry: # If the related object has an admin interface: + # TODO: "id_" is hard-coded here. This should instead use the correct + # API to determine the ID dynamically. + output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ + (related_url, name)) + output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>' % settings.ADMIN_MEDIA_PREFIX) + return mark_safe(u''.join(output)) + + def build_attrs(self, extra_attrs=None, **kwargs): + "Helper function for building an attribute dictionary." + self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs) + return self.attrs + + def value_from_datadict(self, data, files, name): + return self.widget.value_from_datadict(data, files, name) + + def _has_changed(self, initial, data): + return self.widget._has_changed(initial, data) + + def id_for_label(self, id_): + return self.widget.id_for_label(id_) + +class AdminTextareaWidget(forms.Textarea): + def __init__(self, attrs=None): + final_attrs = {'class': 'vLargeTextField'} + if attrs is not None: + final_attrs.update(attrs) + super(AdminTextareaWidget, self).__init__(attrs=final_attrs) + +class AdminTextInputWidget(forms.TextInput): + def __init__(self, attrs=None): + final_attrs = {'class': 'vTextField'} + if attrs is not None: + final_attrs.update(attrs) + super(AdminTextInputWidget, self).__init__(attrs=final_attrs) + +class AdminURLFieldWidget(forms.TextInput): + def __init__(self, attrs=None): + final_attrs = {'class': 'vURLField'} + if attrs is not None: + final_attrs.update(attrs) + super(AdminURLFieldWidget, self).__init__(attrs=final_attrs) + +class AdminIntegerFieldWidget(forms.TextInput): + def __init__(self, attrs=None): + final_attrs = {'class': 'vIntegerField'} + if attrs is not None: + final_attrs.update(attrs) + super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs) |