Commits

David Bennett  committed 07d03c9

Fleshed out basic functionality.

  • Participants
  • Parent commits 5bcc273

Comments (0)

Files changed (5)

File docs/settings.rst

     :default: ``'/_private/'``
 
     The URL prefix for Nginx internal location.
+
+.. attribute:: PRIVATE_MEDIA_ENCODE_URL
+
+    :default: ``True``
+
+    Whether or not to base64 encode part of the URL.

File private_media/conf.py

 class PrivateMediaConf(AppConf):
     # The URL prefix for Nginx internal location
     URL = '/_private/'
+    # Whether or not to base64 encode part of the URL
+    ENCODE_URL = True
 
     class Meta:
         prefix = 'private_media'

File private_media/fields.py

-from django.db.models.fields.files import FileField
+from base64 import urlsafe_b64encode
+from django.contrib.contenttypes.models import ContentType
+from django.core.urlresolvers import reverse
+from django.db.models.fields import files
+from private_media.conf import settings
 
 
-class PrivateFileField(FileField):
+class PrivateFieldFile(files.FieldFile):
+    def _get_condition(self):
+        return self.field.condition
+    condition = property(_get_condition)
+
+    def _get_disposition(self):
+        return self.field.disposition
+    disposition = property(_get_disposition)
+
+    def _get_url(self):
+        self._require_file()
+        content_type = ContentType.objects.get_for_model(self.instance)
+        if settings.PRIVATE_MEDIA_ENCODE_URL:
+            key = urlsafe_b64encode('%s/%s/%s' % (
+                content_type.pk, self.instance.pk, self.field.name)).strip('=')
+            return reverse('private_media-get_file_b64', args=[key, self.name])
+        else:
+            return reverse('private_media-get_file', args=[
+                content_type.pk, self.instance.pk, self.field.name, self.name])
+    url = property(_get_url)
+
+
+def user_is_authenticated(request, instance):
+    return request.user.is_authenticated()
+
+
+class PrivateFileField(files.FileField):
+    attr_class = PrivateFieldFile
+
+    def __init__(self, verbose_name=None, name=None, upload_to='',
+                 storage=None, condition=None, disposition=None, **kwargs):
+        self.condition = condition or user_is_authenticated
+        self.disposition = disposition
+        super(PrivateFileField, self).__init__(
+            verbose_name=verbose_name, name=name, upload_to=upload_to,
+            storage=storage, **kwargs)
+
+
+class PrivateImageFieldFile(files.ImageFieldFile, PrivateFieldFile):
     pass
+
+
+class PrivateImageField(files.ImageField, PrivateFileField):
+    attr_class = PrivateImageFieldFile
+
+    def __init__(self, verbose_name=None, name=None, width_field=None,
+                 height_field=None, condition=None, disposition=None,
+                 **kwargs):
+        self.condition = condition or user_is_authenticated
+        self.disposition = disposition
+        super(PrivateImageField, self).__init__(
+            verbose_name=verbose_name, name=name, width_field=width_field,
+            height_field=height_field, **kwargs)

File private_media/urls.py

+from django.conf.urls.defaults import patterns, url
+
+
+urlpatterns = patterns('private_media.views',
+    url(r'^(\d+)/(\d+)/([a-zA-Z_][0-9a-zA-Z_]*)/(.*)$', 'get_file',
+        name='private_media-get_file'),
+    url(r'^([0-9a-zA-Z_-]+)/(.*)$', 'get_file_b64',
+        name='private_media-get_file_b64'),
+)

File private_media/views.py

+import os
+from base64 import urlsafe_b64decode
+from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied
+from django.http import Http404, HttpResponse
+from django.views.static import serve
+from private_media.conf import settings
+
+
+def get_file(request, content_type_id=None, object_id=None, field_name=None,
+             file_name=None):
+    try:
+        content_type = ContentType.objects.get_for_id(content_type_id)
+        instance = content_type.get_object_for_this_type(**{
+            'pk': object_id,
+            field_name: file_name,
+        })
+    except Exception as e:
+        raise Http404(e if settings.DEBUG else 'File not found.')
+    file = getattr(instance, field_name)
+    if not file.condition(request, instance):
+        raise PermissionDenied
+    response = serve(request, file_name, document_root=settings.MEDIA_ROOT)
+    if file.disposition is not None:
+        response['Content-Disposition'] = '%s; filename="%s"' % (
+            file.disposition, os.path.basename(file.name))
+    return response
+
+
+def get_file_b64(request, b64_key=None, file_name=None):
+    try:
+        key = urlsafe_b64decode(str(b64_key) + '==')
+        content_type_id, object_id, field_name = key.split('/', 2)
+    except Exception as e:
+        raise Http404(e if settings.DEBUG else 'File not found.')
+    return get_file(request, content_type_id, object_id, field_name, file_name)