168 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
import mimetypes
 | 
						|
from os.path import splitext
 | 
						|
 | 
						|
from django.core import validators
 | 
						|
from django.core.exceptions import ValidationError
 | 
						|
from django.core.files.images import get_image_dimensions
 | 
						|
from django.template.defaultfilters import filesizeformat
 | 
						|
from django.utils.deconstruct import deconstructible
 | 
						|
from django.utils.translation import ugettext_lazy as _
 | 
						|
 | 
						|
 | 
						|
@deconstructible
 | 
						|
class ImageDimensionsValidator:
 | 
						|
    """
 | 
						|
    ImageField dimensions validator.
 | 
						|
 | 
						|
    from https://gist.github.com/emilio-rst/4f81ea2718736a6aaf9bdb64d5f2ea6c
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        width=None,
 | 
						|
        height=None,
 | 
						|
        min_width=None,
 | 
						|
        max_width=None,
 | 
						|
        min_height=None,
 | 
						|
        max_height=None,
 | 
						|
    ):
 | 
						|
        """
 | 
						|
        Constructor
 | 
						|
 | 
						|
        Args:
 | 
						|
            width (int): exact width
 | 
						|
            height (int): exact height
 | 
						|
            min_width (int): minimum width
 | 
						|
            min_height (int): minimum height
 | 
						|
            max_width (int): maximum width
 | 
						|
            max_height (int): maximum height
 | 
						|
        """
 | 
						|
 | 
						|
        self.width = width
 | 
						|
        self.height = height
 | 
						|
        self.min_width = min_width
 | 
						|
        self.max_width = max_width
 | 
						|
        self.min_height = min_height
 | 
						|
        self.max_height = max_height
 | 
						|
 | 
						|
    def __call__(self, image):
 | 
						|
        w, h = get_image_dimensions(image)
 | 
						|
 | 
						|
        if self.width is not None and w != self.width:
 | 
						|
            raise ValidationError(_("Width must be %dpx.") % (self.width,))
 | 
						|
 | 
						|
        if self.height is not None and h != self.height:
 | 
						|
            raise ValidationError(_("Height must be %dpx.") % (self.height,))
 | 
						|
 | 
						|
        if self.min_width is not None and w < self.min_width:
 | 
						|
            raise ValidationError(_("Minimum width must be %dpx.") % (self.min_width,))
 | 
						|
 | 
						|
        if self.min_height is not None and h < self.min_height:
 | 
						|
            raise ValidationError(
 | 
						|
                _("Minimum height must be %dpx.") % (self.min_height,)
 | 
						|
            )
 | 
						|
 | 
						|
        if self.max_width is not None and w > self.max_width:
 | 
						|
            raise ValidationError(_("Maximum width must be %dpx.") % (self.max_width,))
 | 
						|
 | 
						|
        if self.max_height is not None and h > self.max_height:
 | 
						|
            raise ValidationError(
 | 
						|
                _("Maximum height must be %dpx.") % (self.max_height,)
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
@deconstructible
 | 
						|
class FileValidator(object):
 | 
						|
    """
 | 
						|
    Taken from https://gist.github.com/jrosebr1/2140738
 | 
						|
    Validator for files, checking the size, extension and mimetype.
 | 
						|
    Initialization parameters:
 | 
						|
        allowed_extensions: iterable with allowed file extensions
 | 
						|
            ie. ('txt', 'doc')
 | 
						|
        allowd_mimetypes: iterable with allowed mimetypes
 | 
						|
            ie. ('image/png', )
 | 
						|
        min_size: minimum number of bytes allowed
 | 
						|
            ie. 100
 | 
						|
        max_size: maximum number of bytes allowed
 | 
						|
            ie. 24*1024*1024 for 24 MB
 | 
						|
    Usage example::
 | 
						|
        MyModel(models.Model):
 | 
						|
            myfile = FileField(validators=FileValidator(max_size=24*1024*1024), ...)
 | 
						|
    """
 | 
						|
 | 
						|
    extension_message = _(
 | 
						|
        "Extension '%(extension)s' not allowed. Allowed extensions are: '%(allowed_extensions)s.'"
 | 
						|
    )
 | 
						|
    mime_message = _(
 | 
						|
        "MIME type '%(mimetype)s' is not valid. Allowed types are: %(allowed_mimetypes)s."
 | 
						|
    )
 | 
						|
    min_size_message = _(
 | 
						|
        "The current file %(size)s, which is too small. The minumum file size is %(allowed_size)s."
 | 
						|
    )
 | 
						|
    max_size_message = _(
 | 
						|
        "The current file %(size)s, which is too large. The maximum file size is %(allowed_size)s."
 | 
						|
    )
 | 
						|
 | 
						|
    def __init__(self, *args, **kwargs):
 | 
						|
        self.allowed_extensions = kwargs.pop("allowed_extensions", None)
 | 
						|
        self.allowed_mimetypes = kwargs.pop("allowed_mimetypes", None)
 | 
						|
        self.min_size = kwargs.pop("min_size", 0)
 | 
						|
        self.max_size = kwargs.pop("max_size", None)
 | 
						|
 | 
						|
    def __call__(self, value):
 | 
						|
        """
 | 
						|
        Check the extension, content type and file size.
 | 
						|
        """
 | 
						|
 | 
						|
        # Check the extension
 | 
						|
        ext = splitext(value.name)[1][1:].lower()
 | 
						|
        if self.allowed_extensions and ext not in self.allowed_extensions:
 | 
						|
            message = self.extension_message % {
 | 
						|
                "extension": ext,
 | 
						|
                "allowed_extensions": ", ".join(self.allowed_extensions),
 | 
						|
            }
 | 
						|
 | 
						|
            raise ValidationError(message)
 | 
						|
 | 
						|
        # Check the content type
 | 
						|
        mimetype = mimetypes.guess_type(value.name)[0]
 | 
						|
        if self.allowed_mimetypes and mimetype not in self.allowed_mimetypes:
 | 
						|
            message = self.mime_message % {
 | 
						|
                "mimetype": mimetype,
 | 
						|
                "allowed_mimetypes": ", ".join(self.allowed_mimetypes),
 | 
						|
            }
 | 
						|
 | 
						|
            raise ValidationError(message)
 | 
						|
 | 
						|
        # Check the file size
 | 
						|
        filesize = len(value)
 | 
						|
        if self.max_size and filesize > self.max_size:
 | 
						|
            message = self.max_size_message % {
 | 
						|
                "size": filesizeformat(filesize),
 | 
						|
                "allowed_size": filesizeformat(self.max_size),
 | 
						|
            }
 | 
						|
 | 
						|
            raise ValidationError(message)
 | 
						|
 | 
						|
        elif filesize < self.min_size:
 | 
						|
            message = self.min_size_message % {
 | 
						|
                "size": filesizeformat(filesize),
 | 
						|
                "allowed_size": filesizeformat(self.min_size),
 | 
						|
            }
 | 
						|
 | 
						|
            raise ValidationError(message)
 | 
						|
 | 
						|
 | 
						|
class DomainValidator(validators.URLValidator):
 | 
						|
    message = "Enter a valid domain name."
 | 
						|
 | 
						|
    def __call__(self, value):
 | 
						|
        """
 | 
						|
        This is a bit hackish but since we don't have any built-in domain validator,
 | 
						|
        we use the url one, and prepend http:// in front of it.
 | 
						|
 | 
						|
        If it fails, we know the domain is not valid.
 | 
						|
        """
 | 
						|
        super().__call__("http://{}".format(value))
 | 
						|
        return value
 |