Exemple #1
0
    def render(self, context):
        photo = self.photo.resolve(context)

        # We can't crop and constrain, so we need
        # to pick one if both are passed
        if self.crop and self.constrain:
            self.constrain = False

        # return empty unicode string
        if not photo.pk:
            return "%s%s" % (getattr(settings, 'STATIC_URL'),
                             getattr(settings, 'DEFAULT_IMAGE_URL'))

        cache_key = generate_image_cache_key(file=str(photo.pk),
                                             size=self.size,
                                             pre_key="photo",
                                             crop=self.crop,
                                             unique_key=str(photo.pk),
                                             quality=self.quality,
                                             constrain=self.constrain)
        cached_image_url = cache.get(cache_key)
        if cached_image_url:
            return cached_image_url

        args = [photo.pk, self.size]
        if self.crop:
            args.append("crop")
        if self.constrain:
            args.append("constrain")
        if self.quality:
            args.append(self.quality)
        url = reverse('photo.size', args=args)
        return url
Exemple #2
0
    def render(self, context):
        file = self.file.resolve(context)

        if file and file.pk:

            cache_key = generate_image_cache_key(file=str(file.id),
                                                 size=self.size,
                                                 pre_key=FILE_IMAGE_PRE_KEY,
                                                 crop=self.crop,
                                                 unique_key=str(file.id),
                                                 quality=self.quality,
                                                 constrain=self.constrain)
            cached_image_url = cache.get(cache_key)
            if cached_image_url:
                return cached_image_url

            args = [file.pk]
            if self.size:
                try:
                    size = Variable(self.size)
                    size = size.resolve(context)
                except:
                    size = self.size
                args.append(size)
            if self.crop:
                args.append("crop")
            if self.constrain:
                args.append("constrain")
            if self.quality:
                args.append(self.quality)
            url = reverse('file', args=args)
            return url

        # return the default image url
        return static(settings.DEFAULT_IMAGE_URL)
Exemple #3
0
    def render(self, context):
        file = self.file.resolve(context)

        if file and file.pk:

            cache_key = generate_image_cache_key(file=str(file.id), size=self.size, pre_key=FILE_IMAGE_PRE_KEY, crop=self.crop, unique_key=str(file.id), quality=self.quality, constrain=self.constrain)
            cached_image_url = cache.get(cache_key)
            if cached_image_url:
                return cached_image_url

            args = [file.pk]
            if self.size:
                try:
                    size = Variable(self.size)
                    size = size.resolve(context)
                except:
                    size = self.size
                args.append(size)
            if self.crop:
                args.append("crop")
            if self.constrain:
                args.append("constrain")
            if self.quality:
                args.append(self.quality)
            url = reverse('file', args=args)
            return url

        # return the default image url
        return "%s%s" % (getattr(settings, 'STATIC_URL'), getattr(settings, 'DEFAULT_IMAGE_URL'))
Exemple #4
0
    def render(self, context):
        photo = self.photo.resolve(context)

        # We can't crop and constrain, so we need
        # to pick one if both are passed
        if self.crop and self.constrain:
            self.constrain = False

        # return empty unicode string
        if not photo.pk:
            return "%s%s" % (getattr(settings, 'STATIC_URL'), getattr(settings, 'DEFAULT_IMAGE_URL'))

        cache_key = generate_image_cache_key(file=str(photo.pk), size=self.size, pre_key="photo", crop=self.crop, unique_key=str(photo.pk), quality=self.quality, constrain=self.constrain)
        cached_image_url = cache.get(cache_key)
        if cached_image_url:
            return cached_image_url

        args = [photo.pk, self.size]
        if self.crop:
            args.append("crop")
        if self.constrain:
            args.append("constrain")
        if self.quality:
            args.append(self.quality)
        url = reverse('photo.size', args=args)
        return url
Exemple #5
0
def photo_size(request, id, size, crop=False, quality=90, download=False, constrain=False):
    """
    Renders image and returns response
    Does not use template
    Saves resized image within cache system
    Returns 404 if if image rendering fails
    """

    if isinstance(quality, unicode) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=id, size=size, pre_key=PHOTO_PRE_KEY, crop=crop, unique_key=id, quality=quality, constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return redirect(cached_image)

    photo = get_object_or_404(Image, id=id)
    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # check permissions
    if not has_perm(request.user, 'photos.view_image', photo):
        raise Http403

    attachment = ''
    if download:
        attachment = 'attachment;'


    if not photo.image or not default_storage.exists(photo.image.name):
        raise Http404

    # gets resized image from cache or rebuild
    image = get_image(photo.image, size, PHOTO_PRE_KEY, crop=crop, quality=quality, unique_key=str(photo.pk), constrain=constrain)

    # if image not rendered; quit
    if not image:
        raise Http404

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = '%s filename=%s' % (attachment, photo.image.file.name)
    image.save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        file_name = photo.image_filename()
        file_path = 'cached%s%s' % (request.path, file_name)
        default_storage.delete(file_path)
        default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

    return response
Exemple #6
0
def photo_size(request, id, size, crop=False, quality=90, download=False, constrain=False):
    """
    Renders image and returns response
    Does not use template
    Saves resized image within cache system
    Returns 404 if if image rendering fails
    """

    if isinstance(quality, unicode) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=id, size=size, pre_key=PHOTO_PRE_KEY, crop=crop, unique_key=id, quality=quality, constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return redirect(cached_image)

    photo = get_object_or_404(Image, id=id)
    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # check permissions
    if not has_perm(request.user, 'photos.view_image', photo):
        raise Http403

    attachment = ''
    if download:
        attachment = 'attachment;'


    if not photo.image or not default_storage.exists(photo.image.name):
        raise Http404

    # gets resized image from cache or rebuild
    image = get_image(photo.image, size, PHOTO_PRE_KEY, crop=crop, quality=quality, unique_key=str(photo.pk), constrain=constrain)

    # if image not rendered; quit
    if not image:
        raise Http404

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = '%s filename=%s' % (attachment, photo.image_filename())
    image.save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        file_name = photo.image_filename()
        file_path = 'cached%s%s' % (request.path, file_name)
        default_storage.delete(file_path)
        default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

    return response
Exemple #7
0
def cache_photo_size(id, size, crop=False, quality=90, download=False, constrain=False):
    """
    """
    if isinstance(quality, unicode) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=str(id), size=size, pre_key=PHOTO_PRE_KEY, crop=crop, unique_key=str(id), quality=quality, constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return cached_image

    try:
        photo = Image.objects.get(id=id)
    except:
        return ""

    args = [id, size]
    if crop:
        args.append("crop")
    if constrain:
        args.append("constrain")
    if quality:
        args.append(quality)
    request_path = reverse('photo.size', args=args)

    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # gets resized image from cache or rebuild
    image = get_image(photo.image, size, PHOTO_PRE_KEY, crop=crop, quality=quality, unique_key=str(photo.pk), constrain=constrain)

    # if image not rendered; quit
    if not image:
        return request_path

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = ' filename="%s"' % photo.image_filename()
    image.save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        file_name = photo.image_filename()
        file_path = 'cached%s%s' % (request_path, file_name)
        default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

        return full_file_path
    return request_path
Exemple #8
0
    def render(self, context):
        file = self.file.resolve(context)

        if file and file.pk:

            cache_key = generate_image_cache_key(file=str(file.id),
                                                 size=self.size,
                                                 pre_key=FILE_IMAGE_PRE_KEY,
                                                 crop=self.crop,
                                                 unique_key=str(file.id),
                                                 quality=self.quality,
                                                 constrain=self.constrain)
            cached_image_url = cache.get(cache_key)
            if cached_image_url:
                return cached_image_url

            args = [file.pk]
            if self.size:
                try:
                    size = Variable(self.size)
                    size = size.resolve(context)
                except:
                    size = self.size
                args.append(size)
            if self.crop:
                args.append("crop")
            if self.constrain:
                args.append("constrain")
            if self.quality:
                args.append(self.quality)
            url = reverse('file', args=args)
            if hasattr(settings, 'USE_S3_STORAGE') and settings.USE_S3_STORAGE:
                url = '%s%s' % (settings.MEDIA_URL, file.file)

            return url

        # return the default image url
        return "%s%s" % (getattr(
            settings, 'STATIC_URL'), getattr(settings, 'DEFAULT_IMAGE_URL'))
Exemple #9
0
def details(request, id, size=None, crop=False, quality=90, download=False, constrain=False, template_name="files/details.html"):
    """
    Return an image response after paramters have been applied.
    """
    cache_key = generate_image_cache_key(
        file=id,
        size=size,
        pre_key=FILE_IMAGE_PRE_KEY,
        crop=crop,
        unique_key=id,
        quality=quality,
        constrain=constrain)

    cached_image = cache.get(cache_key)
    
    if cached_image:
        return redirect('%s%s' % (get_setting('site', 'global', 'siteurl'), cached_image))

    file = get_object_or_404(File, pk=id)

    # basic permissions
    if not has_view_perm(request.user, 'files.view_file', file):
        raise Http403

    # extra permission
    if not file.is_public:
        if not request.user.is_authenticated():
            raise Http403

    # if string and digit convert to integer
    if isinstance(quality, basestring) and quality.isdigit():
        quality = int(quality)

    # get image binary
    try:
        data = file.file.read()
        file.file.close()
    except IOError:  # no such file or directory
        raise Http404

    if download:  # log download
        attachment = u'attachment;'
        EventLog.objects.log(**{
            'event_id': 185000,
            'event_data': '%s %s (%d) dowloaded by %s' % (file.type(), file._meta.object_name, file.pk, request.user),
            'description': '%s downloaded' % file._meta.object_name,
            'user': request.user,
            'request': request,
            'instance': file,
        })
    else:  # log view
        attachment = u''
        if file.type() != 'image':
            EventLog.objects.log(**{
                'event_id': 186000,
                'event_data': '%s %s (%d) viewed by %s' % (file.type(), file._meta.object_name, file.pk, request.user),
                'description': '%s viewed' % file._meta.object_name,
                'user': request.user,
                'request': request,
                'instance': file,
            })

    # if image size specified
    if file.type() == 'image' and size:  # if size specified

        if file.ext() in ('.tif', '.tiff'):
            raise Http404  # tifs cannot (currently) be viewed via browsers

        size = [int(s) if s.isdigit() else 0 for s in size.split('x')]
        size = aspect_ratio(file.image_dimensions(), size, constrain)

        # check for dimensions
        # greater than zero
        if not all(size):
            raise Http404

        # gets resized image from cache or rebuilds
        image = get_image(file.file, size, FILE_IMAGE_PRE_KEY, cache=True, crop=crop, quality=quality, unique_key=None)
        response = HttpResponse(content_type=file.mime_type())
        response['Content-Disposition'] = '%s filename=%s' % (attachment, file.get_name())

        params = {'quality': quality}
        if image.format == 'GIF':
            params['transparency'] = 0

        image.save(response, image.format, **params)

        if file.is_public_file():
            file_name = "%s%s" % (file.get_name(), ".jpg")
            file_path = 'cached%s%s' % (request.path, file_name)
            default_storage.delete(file_path)
            default_storage.save(file_path, ContentFile(response.content))
            full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
            cache.set(cache_key, full_file_path)
            cache_group_key = "files_cache_set.%s" % file.pk
            cache_group_list = cache.get(cache_group_key)

            if cache_group_list is None:
                cache.set(cache_group_key, [cache_key])
            else:
                cache_group_list += [cache_key]
                cache.set(cache_group_key, cache_group_list)

        return response

    if file.is_public_file():
        cache.set(cache_key, file.get_file_public_url())
        set_s3_file_permission(file.file, public=True)
        cache_group_key = "files_cache_set.%s" % file.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += cache_key
            cache.set(cache_group_key, cache_group_list)

    # set mimetype
    if file.mime_type():
        response = HttpResponse(data, content_type=file.mime_type())
    else:
        raise Http404

    # return response
    if file.get_name().endswith(file.ext()):
        response['Content-Disposition'] = '%s filename=%s' % (attachment, file.get_name())
    else:
        response['Content-Disposition'] = '%s filename=%s' % (attachment, file.get_name_ext())
    return response
Exemple #10
0
def details(request,
            id,
            size=None,
            crop=False,
            quality=90,
            download=False,
            constrain=False,
            template_name="files/details.html"):
    """
    Return an image response after paramters have been applied.
    """
    file = get_object_or_404(File, pk=id)

    cache_key = generate_image_cache_key(file=id,
                                         size=size,
                                         pre_key=FILE_IMAGE_PRE_KEY,
                                         crop=crop,
                                         unique_key=id,
                                         quality=quality,
                                         constrain=constrain)

    cached_image = cache.get(cache_key)

    if cached_image:
        if file.type() != 'image':
            # log an event
            EventLog.objects.log(instance=file)
        return redirect(
            '%s%s' % (get_setting('site', 'global', 'siteurl'), cached_image))

    # basic permissions
    if not has_view_perm(request.user, 'files.view_file', file):
        raise Http403

    # extra permission
    if not file.is_public:
        if not request.user.is_authenticated():
            raise Http403

    # if string and digit convert to integer
    if isinstance(quality, basestring) and quality.isdigit():
        quality = int(quality)

    # get image binary
    try:
        data = file.file.read()
        file.file.close()
    except IOError:  # no such file or directory
        raise Http404

    if download:  # log download
        attachment = u'attachment;'
        EventLog.objects.log(
            **{
                'event_id':
                185000,
                'event_data':
                '%s %s (%d) dowloaded by %s' %
                (file.type(), file._meta.object_name, file.pk, request.user),
                'description':
                '%s downloaded' % file._meta.object_name,
                'user':
                request.user,
                'request':
                request,
                'instance':
                file,
            })
    else:  # log view
        attachment = u''
        if file.type() != 'image':
            EventLog.objects.log(
                **{
                    'event_id':
                    186000,
                    'event_data':
                    '%s %s (%d) viewed by %s' %
                    (file.type(), file._meta.object_name, file.pk,
                     request.user),
                    'description':
                    '%s viewed' % file._meta.object_name,
                    'user':
                    request.user,
                    'request':
                    request,
                    'instance':
                    file,
                })

    # if image size specified
    if file.type() == 'image' and size:  # if size specified

        if file.ext() in ('.tif', '.tiff'):
            raise Http404  # tifs cannot (currently) be viewed via browsers

        size = [int(s) if s.isdigit() else 0 for s in size.split('x')]
        size = aspect_ratio(file.image_dimensions(), size, constrain)

        # check for dimensions
        # greater than zero
        if not all(size):
            raise Http404

        # gets resized image from cache or rebuilds
        image = get_image(file.file,
                          size,
                          FILE_IMAGE_PRE_KEY,
                          cache=True,
                          crop=crop,
                          quality=quality,
                          unique_key=None)
        response = HttpResponse(content_type=file.mime_type())
        response['Content-Disposition'] = '%s filename="%s"' % (
            attachment, file.get_name())

        params = {'quality': quality}
        if image.format == 'GIF':
            params['transparency'] = 0

        image.save(response, image.format, **params)

        if file.is_public_file():
            file_name = "%s%s" % (file.get_name(), ".jpg")
            file_path = 'cached%s%s' % (request.path, file_name)
            default_storage.delete(file_path)
            default_storage.save(file_path, ContentFile(response.content))
            full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
            cache.set(cache_key, full_file_path)
            cache_group_key = "files_cache_set.%s" % file.pk
            cache_group_list = cache.get(cache_group_key)

            if cache_group_list is None:
                cache.set(cache_group_key, [cache_key])
            else:
                cache_group_list += [cache_key]
                cache.set(cache_group_key, cache_group_list)

        return response

    if file.is_public_file():
        cache.set(cache_key, file.get_file_public_url())
        set_s3_file_permission(file.file, public=True)
        cache_group_key = "files_cache_set.%s" % file.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += cache_key
            cache.set(cache_group_key, cache_group_list)

    # set mimetype
    if file.mime_type():
        response = HttpResponse(data, content_type=file.mime_type())
    else:
        raise Http404

    # return response
    if file.get_name().endswith(file.ext()):
        response['Content-Disposition'] = '%s filename="%s"' % (
            attachment, file.get_name())
    else:
        response['Content-Disposition'] = '%s filename="%s"' % (
            attachment, file.get_name_ext())
    return response
Exemple #11
0
def photo_size(request,
               id,
               size,
               crop=False,
               quality=90,
               download=False,
               constrain=False):
    """
    Renders image and returns response
    Does not use template
    Saves resized image within cache system
    Returns 404 if if image rendering fails
    """

    if isinstance(quality, str) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=id,
                                         size=size,
                                         pre_key=PHOTO_PRE_KEY,
                                         crop=crop,
                                         unique_key=id,
                                         quality=quality,
                                         constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return redirect('{0}{1}'.format(
            get_setting('site', 'global', 'siteurl'), cached_image))

    photo = get_object_or_404(Image, id=id)
    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # check permissions
    if not has_perm(request.user, 'photos.view_image', photo):
        raise Http403

    attachment = ''
    if download:
        attachment = 'attachment;'

    if not photo.image or not default_storage.exists(photo.image.name):
        raise Http404

    # At this point, we didn't get the image from the cache.
    # Check if this particular thumbnail already exists on file system.
    # If it's there, no need to rebuild it from the original image!
    file_name = photo.image_filename()
    file_path = 'cached%s%s' % (request.path, file_name)
    if default_storage.exists(file_path):
        image = get_image_from_path(
            os.path.join(settings.MEDIA_ROOT, file_path))
    else:
        # gets resized image from cache or rebuild
        image = get_image(photo.image,
                          size,
                          PHOTO_PRE_KEY,
                          crop=crop,
                          quality=quality,
                          unique_key=str(photo.pk),
                          constrain=constrain)

    # if image not rendered; quit
    if not image:
        raise Http404

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = '%s filename="%s"' % (
        attachment, photo.image_filename())
    image.convert('RGB').save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        if not default_storage.exists(file_path):
            default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

    return response
Exemple #12
0
def cache_photo_size(id,
                     size,
                     crop=False,
                     quality=90,
                     download=False,
                     constrain=False):
    """
    """
    if isinstance(quality, str) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=str(id),
                                         size=size,
                                         pre_key=PHOTO_PRE_KEY,
                                         crop=crop,
                                         unique_key=str(id),
                                         quality=quality,
                                         constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return cached_image

    try:
        photo = Image.objects.get(id=id)
    except:
        return ""

    args = [id, size]
    if crop:
        args.append("crop")
    if constrain:
        args.append("constrain")
    if quality:
        args.append(quality)
    request_path = reverse('photo.size', args=args)

    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # gets resized image from cache or rebuild
    image = get_image(photo.image,
                      size,
                      PHOTO_PRE_KEY,
                      crop=crop,
                      quality=quality,
                      unique_key=str(photo.pk),
                      constrain=constrain)

    # if image not rendered; quit
    if not image:
        return request_path

    if image.mode in ("RGBA", "P"):
        image = image.convert('RGB')

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = ' filename="%s"' % photo.image_filename()
    image.save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        file_name = photo.image_filename()
        file_path = 'cached%s%s' % (request_path, file_name)
        default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

        return full_file_path
    return request_path
Exemple #13
0
def photo_size(request, id, size, crop=False, quality=90, download=False, constrain=False):
    """
    Renders image and returns response
    Does not use template
    Saves resized image within cache system
    Returns 404 if if image rendering fails
    """

    if isinstance(quality, unicode) and quality.isdigit():
        quality = int(quality)

    cache_key = generate_image_cache_key(file=id, size=size, pre_key=PHOTO_PRE_KEY, crop=crop, unique_key=id, quality=quality, constrain=constrain)
    cached_image = cache.get(cache_key)
    if cached_image:
        return redirect('{0}{1}'.format(get_setting('site', 'global', 'siteurl'), cached_image))

    photo = get_object_or_404(Image, id=id)
    size = [int(s) for s in size.split('x')]
    size = aspect_ratio(photo.image_dimensions(), size, constrain)

    # check permissions
    if not has_perm(request.user, 'photos.view_image', photo):
        raise Http403

    attachment = ''
    if download:
        attachment = 'attachment;'

    if not photo.image or not default_storage.exists(photo.image.name):
        raise Http404
    
    # At this point, we didn't get the image from the cache.
    # Check if this particular thumbnail already exists on file system.
    # If it's there, no need to rebuild it from the original image!
    file_name = photo.image_filename()
    file_path = 'cached%s%s' % (request.path, file_name)
    if default_storage.exists(file_path):
        image = get_image_from_path(os.path.join(settings.MEDIA_ROOT, file_path))
    else:
        # gets resized image from cache or rebuild
        image = get_image(photo.image, size, PHOTO_PRE_KEY, crop=crop, quality=quality, unique_key=str(photo.pk), constrain=constrain)

    # if image not rendered; quit
    if not image:
        raise Http404

    response = HttpResponse(content_type='image/jpeg')
    response['Content-Disposition'] = '%s filename="%s"' % (attachment, photo.image_filename())
    image.convert('RGB').save(response, "JPEG", quality=quality)

    if photo.is_public_photo() and photo.is_public_photoset():
        if not default_storage.exists(file_path):
            default_storage.save(file_path, ContentFile(response.content))
        full_file_path = "%s%s" % (settings.MEDIA_URL, file_path)
        cache.set(cache_key, full_file_path)
        cache_group_key = "photos_cache_set.%s" % photo.pk
        cache_group_list = cache.get(cache_group_key)

        if cache_group_list is None:
            cache.set(cache_group_key, [cache_key])
        else:
            cache_group_list += [cache_key]
            cache.set(cache_group_key, cache_group_list)

    return response