filer.models.filemodels: 166 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/filer/models/filemodels.py

Stats: 0 executed, 147 missed, 19 excluded, 121 ignored

  1. #-*- coding: utf-8 -*-
  2. from django.contrib.auth import models as auth_models
  3. from django.core import urlresolvers
  4. from django.core.files.base import ContentFile
  5. from django.db import models
  6. from django.utils.translation import ugettext_lazy as _
  7. from filer.fields.multistorage_file import MultiStorageFileField
  8. from filer.models import mixins
  9. from filer import settings as filer_settings
  10. from filer.models.foldermodels import Folder
  11. from polymorphic import PolymorphicModel, PolymorphicManager
  12. import hashlib
  13. import os
  14. class FileManager(PolymorphicManager):
  15. def find_all_duplicates(self):
  16. r = {}
  17. for file_obj in self.all():
  18. if file_obj.sha1:
  19. q = self.filter(sha1=file_obj.sha1)
  20. if len(q) > 1:
  21. r[file_obj.sha1] = q
  22. return r
  23. def find_duplicates(self, file_obj):
  24. return [i for i in self.exclude(pk=file_obj.pk).filter(sha1=file_obj.sha1)]
  25. class File(PolymorphicModel, mixins.IconsMixin):
  26. file_type = 'File'
  27. _icon = "file"
  28. folder = models.ForeignKey(Folder, verbose_name=_('folder'), related_name='all_files',
  29. null=True, blank=True)
  30. file = MultiStorageFileField(_('file'), null=True, blank=True, max_length=255)
  31. _file_size = models.IntegerField(_('file size'), null=True, blank=True)
  32. sha1 = models.CharField(_('sha1'), max_length=40, blank=True, default='')
  33. has_all_mandatory_data = models.BooleanField(_('has all mandatory data'), default=False, editable=False)
  34. original_filename = models.CharField(_('original filename'), max_length=255, blank=True, null=True)
  35. name = models.CharField(max_length=255, default="", blank=True,
  36. verbose_name=_('name'))
  37. description = models.TextField(null=True, blank=True,
  38. verbose_name=_('description'))
  39. owner = models.ForeignKey(auth_models.User,
  40. related_name='owned_%(class)ss',
  41. null=True, blank=True, verbose_name=_('owner'))
  42. uploaded_at = models.DateTimeField(_('uploaded at'), auto_now_add=True)
  43. modified_at = models.DateTimeField(_('modified at'), auto_now=True)
  44. is_public = models.BooleanField(
  45. default=filer_settings.FILER_IS_PUBLIC_DEFAULT,
  46. verbose_name=_('Permissions disabled'),
  47. help_text=_('Disable any permission checking for this ' +\
  48. 'file. File will be publicly accessible ' +\
  49. 'to anyone.'))
  50. objects = FileManager()
  51. @classmethod
  52. def matches_file_type(cls, iname, ifile, request):
  53. return True # I match all files...
  54. def __init__(self, *args, **kwargs):
  55. super(File, self).__init__(*args, **kwargs)
  56. self._old_is_public = self.is_public
  57. def _move_file(self):
  58. """
  59. Move the file from src to dst.
  60. """
  61. src_file_name = self.file.name
  62. dst_file_name = self._meta.get_field('file').generate_filename(
  63. self, self.original_filename)
  64. if self.is_public:
  65. src_storage = self.file.storages['private']
  66. dst_storage = self.file.storages['public']
  67. else:
  68. src_storage = self.file.storages['public']
  69. dst_storage = self.file.storages['private']
  70. # delete the thumbnail
  71. # We are toggling the is_public to make sure that easy_thumbnails can
  72. # delete the thumbnails
  73. self.is_public = not self.is_public
  74. self.file.delete_thumbnails()
  75. self.is_public = not self.is_public
  76. # This is needed because most of the remote File Storage backend do not
  77. # open the file.
  78. src_file = src_storage.open(src_file_name)
  79. src_file.open()
  80. self.file = dst_storage.save(dst_file_name,
  81. ContentFile(src_file.read()))
  82. src_storage.delete(src_file_name)
  83. def _copy_file(self, destination, overwrite=False):
  84. """
  85. Copies the file to a destination files and returns it.
  86. """
  87. if overwrite:
  88. # If the destination file already exists default storage backend
  89. # does not overwrite it but generates another filename.
  90. # TODO: Find a way to override this behavior.
  91. raise NotImplementedError
  92. src_file_name = self.file.name
  93. storage = self.file.storages['public' if self.is_public else 'private']
  94. # This is needed because most of the remote File Storage backend do not
  95. # open the file.
  96. src_file = storage.open(src_file_name)
  97. src_file.open()
  98. return storage.save(destination, ContentFile(src_file.read()))
  99. def generate_sha1(self):
  100. sha = hashlib.sha1()
  101. self.file.seek(0)
  102. sha.update(self.file.read())
  103. self.sha1 = sha.hexdigest()
  104. # to make sure later operations can read the whole file
  105. self.file.seek(0)
  106. def save(self, *args, **kwargs):
  107. # check if this is a subclass of "File" or not and set
  108. # _file_type_plugin_name
  109. if self.__class__ == File:
  110. # what should we do now?
  111. # maybe this has a subclass, but is being saved as a File instance
  112. # anyway. do we need to go check all possible subclasses?
  113. pass
  114. elif issubclass(self.__class__, File):
  115. self._file_type_plugin_name = self.__class__.__name__
  116. # cache the file size
  117. # TODO: only do this if needed (depending on the storage backend the whole file will be downloaded)
  118. try:
  119. self._file_size = self.file.size
  120. except:
  121. pass
  122. if self._old_is_public != self.is_public and self.pk:
  123. self._move_file()
  124. self._old_is_public = self.is_public
  125. # generate SHA1 hash
  126. # TODO: only do this if needed (depending on the storage backend the whole file will be downloaded)
  127. try:
  128. self.generate_sha1()
  129. except Exception, e:
  130. pass
  131. super(File, self).save(*args, **kwargs)
  132. save.alters_data = True
  133. def delete(self, *args, **kwargs):
  134. # Delete the model before the file
  135. super(File, self).delete(*args, **kwargs)
  136. # Delete the file if there are no other Files referencing it.
  137. if not File.objects.filter(file=self.file.name, is_public=self.is_public).exists():
  138. self.file.delete(False)
  139. delete.alters_data = True
  140. @property
  141. def label(self):
  142. if self.name in ['', None]:
  143. text = self.original_filename or 'unnamed file'
  144. else:
  145. text = self.name
  146. text = u"%s" % (text,)
  147. return text
  148. def __lt__(self, other):
  149. return cmp(self.label.lower(), other.label.lower()) < 0
  150. def has_edit_permission(self, request):
  151. return self.has_generic_permission(request, 'edit')
  152. def has_read_permission(self, request):
  153. return self.has_generic_permission(request, 'read')
  154. def has_add_children_permission(self, request):
  155. return self.has_generic_permission(request, 'add_children')
  156. def has_generic_permission(self, request, permission_type):
  157. """
  158. Return true if the current user has permission on this
  159. image. Return the string 'ALL' if the user has all rights.
  160. """
  161. user = request.user
  162. if not user.is_authenticated():
  163. return False
  164. elif user.is_superuser:
  165. return True
  166. elif user == self.owner:
  167. return True
  168. elif self.folder:
  169. return self.folder.has_generic_permission(request, permission_type)
  170. else:
  171. return False
  172. def __unicode__(self):
  173. if self.name in ('', None):
  174. text = u"%s" % (self.original_filename,)
  175. else:
  176. text = u"%s" % (self.name,)
  177. return text
  178. def get_admin_url_path(self):
  179. return urlresolvers.reverse(
  180. 'admin:%s_%s_change' % (self._meta.app_label,
  181. self._meta.module_name,),
  182. args=(self.pk,)
  183. )
  184. @property
  185. def file_ptr(self):
  186. """
  187. Evil hack to get around the cascade delete problem with django_polymorphic.
  188. Prevents ``AttributeError: 'File' object has no attribute 'file_ptr'``.
  189. This is only a workaround for one level of subclassing. The hierarchy of
  190. object in the admin delete view is wrong, but at least it works.
  191. """
  192. return self
  193. @property
  194. def url(self):
  195. """
  196. to make the model behave like a file field
  197. """
  198. try:
  199. r = self.file.url
  200. except:
  201. r = ''
  202. return r
  203. @property
  204. def path(self):
  205. try:
  206. return self.file.path
  207. except:
  208. return ""
  209. @property
  210. def size(self):
  211. return self._file_size or 0
  212. @property
  213. def extension(self):
  214. filetype = os.path.splitext(self.file.name)[1].lower()
  215. if len(filetype) > 0:
  216. filetype = filetype[1:]
  217. return filetype
  218. @property
  219. def logical_folder(self):
  220. """
  221. if this file is not in a specific folder return the Special "unfiled"
  222. Folder object
  223. """
  224. if not self.folder:
  225. from filer.models.virtualitems import UnfiledImages
  226. return UnfiledImages()
  227. else:
  228. return self.folder
  229. @property
  230. def logical_path(self):
  231. """
  232. Gets logical path of the folder in the tree structure.
  233. Used to generate breadcrumbs
  234. """
  235. folder_path = []
  236. if self.folder:
  237. folder_path.extend(self.folder.get_ancestors())
  238. folder_path.append(self.logical_folder)
  239. return folder_path
  240. @property
  241. def duplicates(self):
  242. return File.objects.find_duplicates(self)
  243. class Meta:
  244. app_label = 'filer'
  245. verbose_name = _('file')
  246. verbose_name_plural = _('files')