forms.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from io import BytesIO
  2. from PIL import Image
  3. from django import forms
  4. from django.core.files.base import ContentFile
  5. from django.utils.translation import gettext_lazy as _
  6. from ...core.utils import get_file_hash
  7. from ...core.validators import validate_image_square
  8. from ..models import Icon
  9. FAVICON_MIN_SIZE = 48
  10. FAVICON_SIZES = ((16, 16), (32, 32), (48, 48))
  11. APPLE_TOUCH_MIN_SIZE = 180
  12. VALID_MIME = ("image/gif", "image/jpeg", "image/png")
  13. class IconsForm(forms.Form):
  14. favicon = forms.ImageField(
  15. label=_("Upload image"),
  16. help_text=_("Uploaded image should be a square that is 48px wide and tall."),
  17. required=False,
  18. )
  19. favicon_delete = forms.BooleanField(label=_("Delete custom icon"), required=False)
  20. apple_touch_icon = forms.ImageField(
  21. label=_("Upload image"),
  22. help_text=_("Uploaded image should be square at least 180px wide and tall."),
  23. required=False,
  24. )
  25. apple_touch_icon_delete = forms.BooleanField(
  26. label=_("Delete custom icon"), required=False
  27. )
  28. def clean_favicon(self):
  29. upload = self.cleaned_data.get("favicon")
  30. if not upload or upload == self.initial.get("favicon"):
  31. return None
  32. validate_image_square(upload.image)
  33. validate_image_dimensions(upload.image, FAVICON_MIN_SIZE)
  34. validate_image_mime_type(upload)
  35. return upload
  36. def clean_apple_touch_icon(self):
  37. upload = self.cleaned_data.get("apple_touch_icon")
  38. if not upload or upload == self.initial.get("apple_touch_icon"):
  39. return None
  40. validate_image_square(upload.image)
  41. validate_image_dimensions(upload.image, APPLE_TOUCH_MIN_SIZE)
  42. validate_image_mime_type(upload)
  43. return upload
  44. def save(self):
  45. if self.cleaned_data.get("favicon"):
  46. self.save_favicon(self.cleaned_data["favicon"])
  47. elif self.cleaned_data.get("favicon_delete"):
  48. self.delete_icons(Icon.FAVICON_TYPES)
  49. if self.cleaned_data.get("apple_touch_icon"):
  50. self.save_apple_touch_icon(self.cleaned_data["apple_touch_icon"])
  51. elif self.cleaned_data.get("apple_touch_icon_delete"):
  52. self.delete_icons([Icon.TYPE_APPLE_TOUCH_ICON])
  53. def save_favicon(self, image):
  54. self.delete_icons(Icon.FAVICON_TYPES)
  55. save_favicon(image)
  56. save_icon(image, (32, 32), Icon.TYPE_FAVICON_32)
  57. save_icon(image, (16, 16), Icon.TYPE_FAVICON_16)
  58. def save_apple_touch_icon(self, image):
  59. self.delete_icons([Icon.TYPE_APPLE_TOUCH_ICON])
  60. save_icon(image, (180, 180), Icon.TYPE_APPLE_TOUCH_ICON)
  61. def delete_icons(self, icon_types):
  62. for icon in Icon.objects.filter(type__in=icon_types):
  63. icon.delete()
  64. def save_favicon(image):
  65. icon = Image.open(image)
  66. buffer = BytesIO()
  67. icon.save(buffer, "ICO", sizes=FAVICON_SIZES)
  68. buffer.seek(0)
  69. icon_file = ContentFile(buffer.read())
  70. icon_file.name = "%s.%s.ico" % ("favicon", get_file_hash(icon_file))
  71. Icon.objects.create(type=Icon.TYPE_FAVICON, image=icon_file, size=icon_file.size)
  72. def save_icon(image, size, icon_type):
  73. icon = Image.open(image)
  74. icon.thumbnail(size)
  75. buffer = BytesIO()
  76. icon.save(buffer, "PNG")
  77. buffer.seek(0)
  78. icon_file = ContentFile(buffer.read())
  79. icon_file.name = "%s.%s.png" % (
  80. icon_type.replace("_", "-"),
  81. get_file_hash(icon_file),
  82. )
  83. Icon.objects.create(type=icon_type, image=icon_file, size=icon_file.size)
  84. def validate_image_dimensions(image, size):
  85. if image.width < size:
  86. raise forms.ValidationError(
  87. _("Uploaded image's edge should be at least %(size)s pixels long.")
  88. % {"size": size}
  89. )
  90. def validate_image_mime_type(upload):
  91. if upload.content_type not in VALID_MIME:
  92. raise forms.ValidationError(_("Uploaded image was not gif, jpeg or png."))