attachmentmodel.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. from datetime import date
  2. from time import time
  3. import hashlib
  4. import uuid
  5. from path import path
  6. from PIL import Image
  7. from django.conf import settings
  8. from django.db import models
  9. from django.utils import timezone
  10. from django.utils.translation import ugettext_lazy as _
  11. from floppyforms import ValidationError
  12. from misago.signals import (delete_user_content, merge_post, merge_thread,
  13. move_forum_content, move_post, move_thread,
  14. rename_user, sync_user_profile)
  15. class AttachmentManager(models.Manager):
  16. def allow_more_orphans(self):
  17. if Attachment.objects.filter(post__isnull=True).count() > settings.ORPHAN_ATTACHMENTS_LIMIT:
  18. raise ValidationError(_("Too many users are currently uploading files. Please try agian later."))
  19. class Attachment(models.Model):
  20. hash_id = models.CharField(max_length=8, db_index=True)
  21. filetype = models.ForeignKey('AttachmentType')
  22. forum = models.ForeignKey('Forum', null=True, blank=True, on_delete=models.SET_NULL)
  23. thread = models.ForeignKey('Thread', null=True, blank=True, on_delete=models.SET_NULL)
  24. post = models.ForeignKey('Post', null=True, blank=True, on_delete=models.SET_NULL)
  25. user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
  26. user_name = models.CharField(max_length=255)
  27. user_name_slug = models.CharField(max_length=255)
  28. ip = models.GenericIPAddressField()
  29. agent = models.CharField(max_length=255)
  30. date = models.DateTimeField(default=timezone.now)
  31. session = models.CharField(max_length=255, db_index=True)
  32. name = models.CharField(max_length=255)
  33. content_type = models.CharField(max_length=255)
  34. path = models.CharField(max_length=255)
  35. size = models.PositiveIntegerField(max_length=255)
  36. objects = AttachmentManager()
  37. class Meta:
  38. app_label = 'misago'
  39. def delete(self, *args, **kwargs):
  40. try:
  41. file_path = self.file_path
  42. if file_path.exists() and not file_path.isdir():
  43. file_path.unlink()
  44. except Exception:
  45. pass
  46. try:
  47. file_path = self.thumb_path
  48. if thumb_path.exists() and not thumb_path.isdir():
  49. thumb_path.unlink()
  50. except Exception:
  51. pass
  52. super(Attachment, self).delete(*args, **kwargs)
  53. def delete_from_post(self):
  54. if self.post_id:
  55. self.post.attachments = [attachment
  56. for attachment in self.post.attachments
  57. if attachment.pk != self.pk]
  58. self.post.save(force_update=True)
  59. @property
  60. def is_image(self):
  61. IMAGES_EXTENSIONS = ('.png', '.gif', '.jpg', '.jpeg')
  62. name = self.name.lower()
  63. for extension in IMAGES_EXTENSIONS:
  64. if name[len(extension) * -1:] == extension:
  65. return extension[1:]
  66. return False
  67. @property
  68. def file_path(self):
  69. return path(settings.ATTACHMENTS_ROOT + self.path)
  70. @property
  71. def thumb_path(self):
  72. return path(unicode(self.file_path).replace('.', '_thumb.'))
  73. def use_file(self, uploaded_file):
  74. self.name = self.clean_name(uploaded_file.name)
  75. self.content_type = uploaded_file.content_type
  76. self.size = uploaded_file.size
  77. self.store_file(uploaded_file)
  78. def clean_name(self, filename):
  79. for char in '=[]()<>\\/"\'':
  80. filename = filename.replace(char, '')
  81. if len(filename) > 100:
  82. filename = filename[-100:]
  83. return filename
  84. def store_file(self, uploaded_file):
  85. datenow = date.today()
  86. current_dir = '%s-%s-%s' % (datenow.month, datenow.day, datenow.year)
  87. full_dir = path(settings.ATTACHMENTS_ROOT + current_dir)
  88. full_dir.mkdir_p()
  89. filename = hashlib.md5('%s:%s:%s' % (self.user.pk, int(time()), settings.SECRET_KEY)).hexdigest()
  90. if self.is_image:
  91. filename += '.%s' % self.is_image
  92. self.path = '%s/%s' % (current_dir, filename)
  93. with open('%s/%s' % (full_dir, filename), 'wb+') as destination:
  94. for chunk in uploaded_file.chunks():
  95. destination.write(chunk)
  96. if self.is_image:
  97. self.make_thumb()
  98. def make_thumb(self):
  99. try:
  100. image = Image.open(self.file_path)
  101. image.thumbnail((800, 600), Image.ANTIALIAS)
  102. image.save(self.thumb_path)
  103. except IOError:
  104. pass
  105. def generate_hash_id(self, seed):
  106. hash_seed = '%s:%s' % (uuid.uuid4(), seed)
  107. unique_hash = hashlib.sha256(hash_seed).hexdigest()
  108. self.hash_id = unique_hash[:8]
  109. def rename_user_handler(sender, **kwargs):
  110. sender.attachment_set.update(
  111. user_name=sender.username,
  112. user_name_slug=sender.username_slug,
  113. )
  114. rename_user.connect(rename_user_handler, dispatch_uid="rename_user_attachments")
  115. def delete_user_content_handler(sender, **kwargs):
  116. for attachment in sender.attachment_set.iterator():
  117. if attachment.post_id:
  118. attachment.delete_from_post()
  119. attachment.delete()
  120. delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_attachments")
  121. def move_forum_content_handler(sender, **kwargs):
  122. sender.attachment_set.update(forum=kwargs['move_to'])
  123. move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_attachments")
  124. def move_thread_handler(sender, **kwargs):
  125. sender.attachment_set.update(forum=kwargs['move_to'])
  126. move_thread.connect(move_thread_handler, dispatch_uid="move_thread_attachments")
  127. def move_post_handler(sender, **kwargs):
  128. sender.attachment_set.update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
  129. move_post.connect(move_thread_handler, dispatch_uid="move_post_attachments")
  130. def merge_thread_handler(sender, **kwargs):
  131. sender.attachment_set.update(thread=kwargs['new_thread'])
  132. merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_attachments")
  133. def merge_post_handler(sender, **kwargs):
  134. sender.attachment_set.update(post=kwargs['new_post'], session=('attachments_%s' % kwargs['new_post'].pk))
  135. merge_thread.connect(merge_thread_handler, dispatch_uid="merge_posts_attachments")