attachmentmodel.py 6.3 KB

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