# -*- coding: utf-8 -*- from __future__ import unicode_literals # Erik Stein , 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 //original/ 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
%(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)