You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

widgets.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import re
  2. from django import forms
  3. from django.forms.util import flatatt
  4. from django.forms.widgets import FileInput
  5. from django.template import Context
  6. from django.template.loader import render_to_string
  7. from django.utils.encoding import force_text, force_unicode
  8. from django.utils.safestring import mark_safe
  9. try:
  10. from django.utils.html import format_html
  11. except ImportError:
  12. # Django 1.4 compatibility
  13. from oscar.core.compat import format_html
  14. class ImageInput(FileInput):
  15. """
  16. Widget providing a input element for file uploads based on the
  17. Django ``FileInput`` element. It hides the actual browser-specific
  18. input element and shows the available image for images that have
  19. been previously uploaded. Selecting the image will open the file
  20. dialog and allow for selecting a new or replacing image file.
  21. """
  22. template_name = 'partials/image_input_widget.html'
  23. attrs = {'accept': 'image/*'}
  24. def render(self, name, value, attrs=None):
  25. """
  26. Render the ``input`` field based on the defined ``template_name``. The
  27. image URL is take from *value* and is provided to the template as
  28. ``image_url`` context variable relative to ``MEDIA_URL``. Further
  29. attributes for the ``input`` element are provide in ``input_attrs`` and
  30. contain parameters specified in *attrs* and *name*.
  31. If *value* contains no valid image URL an empty string will be provided
  32. in the context.
  33. """
  34. if value is None:
  35. value = ''
  36. final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
  37. if value != '':
  38. # Only add the 'value' attribute if a value is non-empty.
  39. final_attrs['value'] = force_unicode(self._format_value(value))
  40. image_url = final_attrs.get('value', '')
  41. return render_to_string(self.template_name, Context({
  42. 'input_attrs': flatatt(final_attrs),
  43. 'image_url': image_url,
  44. 'image_id': "%s-image" % final_attrs['id'],
  45. }))
  46. class WYSIWYGTextArea(forms.Textarea):
  47. def __init__(self, *args, **kwargs):
  48. kwargs.setdefault('attrs', {})
  49. kwargs['attrs'].setdefault('class', '')
  50. kwargs['attrs']['class'] += ' wysiwyg'
  51. super(WYSIWYGTextArea, self).__init__(*args, **kwargs)
  52. def datetime_format_to_js_date_format(format):
  53. """
  54. Convert a Python datetime format to a date format suitable for use with JS
  55. date pickers
  56. """
  57. converted = format
  58. replacements = {
  59. '%Y': 'yy',
  60. '%m': 'mm',
  61. '%d': 'dd',
  62. '%H:%M': '',
  63. }
  64. for search, replace in replacements.iteritems():
  65. converted = converted.replace(search, replace)
  66. return converted.strip()
  67. def datetime_format_to_js_time_format(format):
  68. """
  69. Convert a Python datetime format to a time format suitable for use with JS
  70. date pickers
  71. """
  72. converted = format
  73. replacements = {
  74. '%Y': '',
  75. '%m': '',
  76. '%d': '',
  77. '%H': 'HH',
  78. '%M': 'mm',
  79. }
  80. for search, replace in replacements.iteritems():
  81. converted = converted.replace(search, replace)
  82. converted = re.sub('[-/][^%]', '', converted)
  83. return converted.strip()
  84. def add_js_formats(widget):
  85. """
  86. Set data attributes for date and time format on a widget
  87. """
  88. attrs = {
  89. 'data-dateFormat': datetime_format_to_js_date_format(
  90. widget.format),
  91. 'data-timeFormat': datetime_format_to_js_time_format(
  92. widget.format)
  93. }
  94. widget.attrs.update(attrs)
  95. class DatePickerInput(forms.DateInput):
  96. """
  97. DatePicker input that uses the jQuery UI datepicker. Data attributes are
  98. used to pass the date format to the JS
  99. """
  100. def __init__(self, *args, **kwargs):
  101. super(DatePickerInput, self).__init__(*args, **kwargs)
  102. add_js_formats(self)
  103. class DateTimePickerInput(forms.DateTimeInput):
  104. # Build a widget which uses the locale datetime format but without seconds.
  105. # We also use data attributes to pass these formats to the JS datepicker.
  106. def __init__(self, *args, **kwargs):
  107. include_seconds = kwargs.pop('include_seconds', False)
  108. super(DateTimePickerInput, self).__init__(*args, **kwargs)
  109. if not include_seconds:
  110. self.format = re.sub(':?%S', '', self.format)
  111. add_js_formats(self)
  112. class AdvancedSelect(forms.Select):
  113. """
  114. Customised Select widget that allows a list of disabled values to be passed
  115. to the constructor. Django's default Select widget doesn't allow this so
  116. we have to override the render_option method and add a section that checks
  117. for whether the widget is disabled.
  118. """
  119. def __init__(self, attrs=None, choices=(), disabled_values=()):
  120. self.disabled_values = set(force_text(v) for v in disabled_values)
  121. super(AdvancedSelect, self).__init__(attrs, choices)
  122. def render_option(self, selected_choices, option_value, option_label):
  123. option_value = force_text(option_value)
  124. if option_value in self.disabled_values:
  125. selected_html = mark_safe(' disabled="disabled"')
  126. elif option_value in selected_choices:
  127. selected_html = mark_safe(' selected="selected"')
  128. if not self.allow_multiple_selected:
  129. # Only allow for a single selection.
  130. selected_choices.remove(option_value)
  131. else:
  132. selected_html = ''
  133. return format_html(u'<option value="{0}"{1}>{2}</option>',
  134. option_value,
  135. selected_html,
  136. force_text(option_label))