123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- from __future__ import unicode_literals
- from future.builtins import str
- from future.utils import native
- from io import BytesIO
- import os
- from string import punctuation
- from zipfile import ZipFile
- from chardet import detect as charsetdetect
- from django.core.files.base import ContentFile
- from django.core.files.storage import default_storage
- from django.db import models
- from django.utils.encoding import python_2_unicode_compatible
- from django.utils.encoding import force_text
- from django.utils.translation import ugettext_lazy as _
- from mezzanine.conf import settings
- from mezzanine.core.fields import FileField
- from mezzanine.core.models import Orderable, RichText
- from mezzanine.pages.models import Page
- from mezzanine.utils.importing import import_dotted_path
- from mezzanine.utils.models import upload_to
- # Set the directory where gallery images are uploaded to,
- # either MEDIA_ROOT + 'galleries', or filebrowser's upload
- # directory if being used.
- GALLERIES_UPLOAD_DIR = "galleries"
- if settings.PACKAGE_NAME_FILEBROWSER in settings.INSTALLED_APPS:
- fb_settings = "%s.settings" % settings.PACKAGE_NAME_FILEBROWSER
- try:
- GALLERIES_UPLOAD_DIR = import_dotted_path(fb_settings).DIRECTORY
- except ImportError:
- pass
- class BaseGallery(models.Model):
- """
- Base gallery functionality.
- """
- class Meta:
- abstract = True
- zip_import = models.FileField(verbose_name=_("Zip import"), blank=True,
- upload_to=upload_to("galleries.Gallery.zip_import", "galleries"),
- help_text=_("Upload a zip file containing images, and "
- "they'll be imported into this gallery."))
- def save(self, delete_zip_import=True, *args, **kwargs):
- """
- If a zip file is uploaded, extract any images from it and add
- them to the gallery, before removing the zip file.
- """
- super(BaseGallery, self).save(*args, **kwargs)
- if self.zip_import:
- zip_file = ZipFile(self.zip_import)
- for name in zip_file.namelist():
- data = zip_file.read(name)
- try:
- from PIL import Image
- image = Image.open(BytesIO(data))
- image.load()
- image = Image.open(BytesIO(data))
- image.verify()
- except ImportError:
- pass
- except:
- continue
- name = os.path.split(name)[1]
- # In python3, name is a string. Convert it to bytes.
- if not isinstance(name, bytes):
- try:
- name = name.encode('cp437')
- except UnicodeEncodeError:
- # File name includes characters that aren't in cp437,
- # which isn't supported by most zip tooling. They will
- # not appear correctly.
- tempname = name
- # Decode byte-name.
- if isinstance(name, bytes):
- encoding = charsetdetect(name)['encoding']
- tempname = name.decode(encoding)
- # A gallery with a slug of "/" tries to extract files
- # to / on disk; see os.path.join docs.
- slug = self.slug if self.slug != "/" else ""
- path = os.path.join(GALLERIES_UPLOAD_DIR, slug, tempname)
- try:
- saved_path = default_storage.save(path, ContentFile(data))
- except UnicodeEncodeError:
- from warnings import warn
- warn("A file was saved that contains unicode "
- "characters in its path, but somehow the current "
- "locale does not support utf-8. You may need to set "
- "'LC_ALL' to a correct value, eg: 'en_US.UTF-8'.")
- # The native() call is needed here around str because
- # os.path.join() in Python 2.x (in posixpath.py)
- # mixes byte-strings with unicode strings without
- # explicit conversion, which raises a TypeError as it
- # would on Python 3.
- path = os.path.join(GALLERIES_UPLOAD_DIR, slug,
- native(str(name, errors="ignore")))
- saved_path = default_storage.save(path, ContentFile(data))
- self.images.create(file=saved_path)
- if delete_zip_import:
- zip_file.close()
- self.zip_import.delete(save=True)
- class Gallery(Page, RichText, BaseGallery):
- """
- Page bucket for gallery photos.
- """
- class Meta:
- verbose_name = _("Gallery")
- verbose_name_plural = _("Galleries")
- @python_2_unicode_compatible
- class GalleryImage(Orderable):
- gallery = models.ForeignKey("Gallery", related_name="images")
- file = FileField(_("File"), max_length=200, format="Image",
- upload_to=upload_to("galleries.GalleryImage.file", "galleries"))
- description = models.CharField(_("Description"), max_length=1000,
- blank=True)
- class Meta:
- verbose_name = _("Image")
- verbose_name_plural = _("Images")
- def __str__(self):
- return self.description
- def save(self, *args, **kwargs):
- """
- If no description is given when created, create one from the
- file name.
- """
- if not self.id and not self.description:
- name = force_text(self.file)
- name = name.rsplit("/", 1)[-1].rsplit(".", 1)[0]
- name = name.replace("'", "")
- name = "".join([c if c not in punctuation else " " for c in name])
- # str.title() doesn't deal with unicode very well.
- # http://bugs.python.org/issue6412
- name = "".join([s.upper() if i == 0 or name[i - 1] == " " else s
- for i, s in enumerate(name)])
- self.description = name
- super(GalleryImage, self).save(*args, **kwargs)
|