post.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from django.contrib.postgres.fields import JSONField
  2. from django.core.urlresolvers import reverse
  3. from django.db import models
  4. from django.dispatch import receiver
  5. from django.utils import six, timezone
  6. from misago.conf import settings
  7. from .. import threadtypes
  8. from ..checksums import is_post_valid, update_post_checksum
  9. class Post(models.Model):
  10. category = models.ForeignKey('misago_categories.Category')
  11. thread = models.ForeignKey('misago_threads.Thread')
  12. poster = models.ForeignKey(
  13. settings.AUTH_USER_MODEL,
  14. blank=True,
  15. null=True,
  16. on_delete=models.SET_NULL,
  17. )
  18. poster_name = models.CharField(max_length=255)
  19. poster_ip = models.GenericIPAddressField()
  20. original = models.TextField()
  21. parsed = models.TextField()
  22. checksum = models.CharField(max_length=64, default='-')
  23. mentions = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="mention_set")
  24. has_attachments = models.BooleanField(default=False)
  25. attachments_cache = JSONField(null=True, blank=True)
  26. posted_on = models.DateTimeField()
  27. updated_on = models.DateTimeField()
  28. hidden_on = models.DateTimeField(default=timezone.now)
  29. edits = models.PositiveIntegerField(default=0)
  30. last_editor = models.ForeignKey(
  31. settings.AUTH_USER_MODEL,
  32. blank=True,
  33. null=True,
  34. on_delete=models.SET_NULL,
  35. related_name='+',
  36. )
  37. last_editor_name = models.CharField(max_length=255, null=True, blank=True)
  38. last_editor_slug = models.SlugField(max_length=255, null=True, blank=True)
  39. hidden_by = models.ForeignKey(
  40. settings.AUTH_USER_MODEL,
  41. blank=True,
  42. null=True,
  43. on_delete=models.SET_NULL,
  44. related_name='+',
  45. )
  46. hidden_by_name = models.CharField(max_length=255, null=True, blank=True)
  47. hidden_by_slug = models.SlugField(max_length=255, null=True, blank=True)
  48. has_reports = models.BooleanField(default=False)
  49. has_open_reports = models.BooleanField(default=False)
  50. is_unapproved = models.BooleanField(default=False, db_index=True)
  51. is_hidden = models.BooleanField(default=False)
  52. is_protected = models.BooleanField(default=False)
  53. is_event = models.BooleanField(default=False, db_index=True)
  54. event_type = models.CharField(max_length=255, null=True, blank=True)
  55. event_context = JSONField(null=True, blank=True)
  56. class Meta:
  57. index_together = [
  58. ('is_event', 'is_hidden'),
  59. ('poster', 'posted_on')
  60. ]
  61. def __unicode__(self):
  62. return '%s...' % self.original[10:].strip()
  63. def delete(self, *args, **kwargs):
  64. from ..signals import delete_post
  65. delete_post.send(sender=self)
  66. super(Post, self).delete(*args, **kwargs)
  67. def merge(self, other_post):
  68. if self.thread_id != other_post.thread_id:
  69. raise ValueError("only posts belonging to same thread can be merged")
  70. if self.is_event or other_post.is_event:
  71. raise ValueError("can't merge events")
  72. if self.pk == other_post.pk:
  73. raise ValueError("post can't be merged with itself")
  74. other_post.original = six.text_type('\n\n').join((other_post.original, self.original))
  75. other_post.parsed = six.text_type('\n').join((other_post.parsed, self.parsed))
  76. update_post_checksum(other_post)
  77. from ..signals import merge_post
  78. merge_post.send(sender=self, other_post=other_post)
  79. def move(self, new_thread):
  80. from ..signals import move_post
  81. self.category = new_thread.category
  82. self.thread = new_thread
  83. move_post.send(sender=self)
  84. @property
  85. def thread_type(self):
  86. return self.category.thread_type
  87. def get_api_url(self):
  88. return self.thread_type.get_post_api_url(self)
  89. def get_editor_api_url(self):
  90. return self.thread_type.get_post_editor_api_url(self)
  91. def get_absolute_url(self):
  92. return self.thread_type.get_post_absolute_url(self)
  93. @property
  94. def short(self):
  95. if self.is_valid:
  96. if len(self.original) > 150:
  97. return six.text_type('%s...') % self.original[:150].strip()
  98. else:
  99. return self.original
  100. else:
  101. return ''
  102. @property
  103. def is_valid(self):
  104. return is_post_valid(self)
  105. @property
  106. def is_first_post(self):
  107. return self.pk == self.thread.first_post_id