Work in progress: django-imagekit but for all types of media files (movies, PDFs etc.). + private media
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.
 

110 lines
3.9 KiB

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Erik Stein <code@classlibrary.net>, 2016
import os
from django import forms
from django.contrib.admin.widgets import AdminFileWidget
from django.db import models
from django.db.models.fields.files import FileDescriptor
from django.utils.html import conditional_escape
from django.utils.text import slugify
from ..storages import ProtectedMediaAssetStorage
# TODO Define the central storage somewhere more central
ORIGINALS_STORAGE = ProtectedMediaAssetStorage()
# TODO Move filename max length to storage
FILENAME_MAX_LENGTH = 255
# TOD Move get_upload_path to utils
def get_upload_path(instance, filename):
"""
Returns /<uuid_hex>/original/<slugified_filename.ext>
where
- uuid is taken from instance,
- filename is slugified and shortened to a max length including the extension.
"""
name, ext = os.path.splitext(filename)
name = slugify(name)
name = name[:(FILENAME_MAX_LENGTH - len(ext))]
filename = "%s%s" % (name, ext)
return os.path.join(
instance.get_uuid(),
instance.STORAGE.ORIGINAL_FILE_PREFIX,
filename
)
DEFAULT_UPLOAD_TO = get_upload_path
class MediaAssetFileWidget(AdminFileWidget):
"""
Widget which understands ProtectedMediaAssetStorage
(knows that the url property is not relevant), also
does not provide a link to the original file.
# TODO Add admin access to the original file, if permissions apply
# TODO Add permission "allowed to view original file"
"""
template_with_initial = (
'%(initial_text)s: %(initial)s '
'%(clear_template)s<br />%(input_text)s: %(input)s'
)
def is_initial(self, value):
# Checks for 'name' instead of 'url' property
return bool(value and hasattr(value, 'name'))
def get_template_substitution_values(self, value):
# Does not use value.url
return {
'initial': conditional_escape(value),
}
class MediaAssetFileDescriptor(FileDescriptor):
# TODO Assign metadata fields like width/height
def __set__(self, instance, value):
previous_file = instance.__dict__.get(self.field.name)
super(MediaAssetFileDescriptor, self).__set__(instance, value)
# TODO Check if previous_file is actually None in our case; remove comment
# To prevent recalculating image dimensions when we are instantiating
# an object from the database (bug #11084), only update dimensions if
# the field had a value before this assignment. Since the default
# value for FileField subclasses is an instance of field.attr_class,
# previous_file will only be None when we are called from
# Model.__init__(). The ImageField.update_dimension_fields method
# hooked up to the post_init signal handles the Model.__init__() cases.
# Assignment happening outside of Model.__init__() will trigger the
# update right here.
if previous_file is not None:
pass # self.field.update_dimension_fields(instance, force=True)
class MediaAssetFormField(forms.FileField):
widget = MediaAssetFileWidget
class MediaAssetField(models.FileField):
# The descriptor to use for accessing the attribute off of the class.
description_class = MediaAssetFileDescriptor
def _init__(self, verbose_name=None, name=None, upload_to='', storage=None, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', FILENAME_MAX_LENGTH)
kwargs['upload_to'] = kwargs.get('upload_to', DEFAULT_UPLOAD_TO)
kwargs['storage'] = kwargs.get('storage', ORIGINALS_STORAGE)
super(MediaAssetField, self).__init__(verbose_name, name, **kwargs)
def formfield(self, **kwargs):
defaults = {
'form_class': MediaAssetFormField,
'max_length': self.max_length
}
defaults.update(kwargs)
return super(MediaAssetField, self).formfield(**defaults)