utils.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import email.utils
  2. import logging
  3. import os
  4. import time
  5. from django.conf import settings
  6. from django.contrib.sites.models import Site
  7. from django.core import mail
  8. from django.template.loader import render_to_string
  9. from tickets.defaults import defaults
  10. from tickets.models import Attachment, Comment, Task
  11. log = logging.getLogger(__name__)
  12. def staff_check(user):
  13. """If tickets_STAFF_ONLY is set to True, limit view access to staff users only.
  14. # FIXME: More granular access control needed - see
  15. https://github.com/shacker/django-todo/issues/50
  16. """
  17. if defaults("TICKETS_STAFF_ONLY"):
  18. return user.is_staff
  19. else:
  20. # If unset or False, allow all logged in users
  21. return True
  22. def user_can_read_task(task, user):
  23. return task.task_list.group in user.groups.all() or user.is_superuser
  24. def tickets_get_backend(task):
  25. """Returns a mail backend for some task"""
  26. mail_backends = getattr(settings, "TICKETS_MAIL_BACKENDS", None)
  27. print(mail_backends)
  28. if mail_backends is None:
  29. return None
  30. #task_backend = mail_backends[task.task_list.slug]
  31. task_backend = mail_backends['mail-queue']
  32. if task_backend is None:
  33. return None
  34. return task_backend
  35. def tickets_get_mailer(user, task):
  36. """A mailer is a (from_address, backend) pair"""
  37. task_backend = tickets_get_backend(task)
  38. if task_backend is None:
  39. return (None, mail.get_connection)
  40. from_address = getattr(task_backend, "from_address")
  41. from_address = email.utils.formataddr((user.username, from_address))
  42. return (from_address, task_backend)
  43. def tickets_send_mail(user, task, subject, body, recip_list):
  44. """Send an email attached to task, triggered by user"""
  45. if settings.IS_SEND_EMAIL:
  46. references = Comment.objects.filter(task=task).only("email_message_id")
  47. references = (ref.email_message_id for ref in references)
  48. references = " ".join(filter(bool, references))
  49. from_address, backend = tickets_get_mailer(user, task)
  50. message_hash = hash((subject, body, from_address, frozenset(recip_list), references))
  51. message_id = (
  52. # the task_id enables attaching back notification answers
  53. "<notif-{task_id}."
  54. # the message hash / epoch pair enables deduplication
  55. "{message_hash:x}."
  56. "{epoch}@django-tickets>"
  57. ).format(
  58. task_id=task.pk,
  59. # avoid the -hexstring case (hashes can be negative)
  60. message_hash=abs(message_hash),
  61. epoch=int(time.time()),
  62. )
  63. # the thread message id is used as a common denominator between all
  64. # notifications for some task. This message doesn't actually exist,
  65. # it's just there to make threading possible
  66. thread_message_id = "<thread-{}@django-tickets>".format(task.pk)
  67. references = "{} {}".format(references, thread_message_id)
  68. with backend() as connection:
  69. message = mail.EmailMessage(
  70. subject,
  71. body,
  72. from_address,
  73. recip_list,
  74. [], # Bcc
  75. headers={
  76. **getattr(backend, "headers", {}),
  77. "Message-ID": message_id,
  78. "References": references,
  79. "In-reply-to": thread_message_id,
  80. },
  81. connection=connection,
  82. )
  83. message.send()
  84. def send_notify_mail(new_task):
  85. """
  86. Send email to assignee if task is assigned to someone other than submittor.
  87. Unassigned tasks should not try to notify.
  88. """
  89. if new_task.assigned_to == new_task.created_by:
  90. return
  91. current_site = Site.objects.get_current()
  92. subject = render_to_string("tickets/email/assigned_subject.txt", {"task": new_task})
  93. body = render_to_string(
  94. "tickets/email/assigned_body.txt", {"task": new_task, "site": current_site}
  95. )
  96. recip_list = [new_task.assigned_to.email]
  97. tickets_send_mail(new_task.created_by, new_task, subject, body, recip_list)
  98. def send_notify_change_mail(new_task, user):
  99. """
  100. Send email to assignee if task is assigned to someone other than submittor.
  101. Unassigned tasks should not try to notify.
  102. """
  103. if new_task.assigned_to == new_task.created_by:
  104. return
  105. current_site = Site.objects.get_current()
  106. subject = render_to_string("tickets/email/assigned_subject.txt", {"task": new_task})
  107. body = render_to_string(
  108. "tickets/email/changetask_body.txt", {"task": new_task, "site": current_site}
  109. )
  110. recip_list = [new_task.assigned_to.email]
  111. if user == new_task.created_by:
  112. tickets_send_mail(new_task.created_by, new_task, subject, body, recip_list)
  113. else:
  114. tickets_send_mail(user, new_task, subject, body, recip_list)
  115. tickets_send_mail(user, new_task, subject, body, [new_task.created_by.email])
  116. def send_email_to_thread_participants(task, msg_body, user, subject=None):
  117. """Notify all previous commentors on a Task about a new comment."""
  118. current_site = Site.objects.get_current()
  119. email_subject = subject
  120. if not subject:
  121. subject = render_to_string("tickets/email/assigned_subject.txt", {"task": task})
  122. email_body = render_to_string(
  123. "tickets/email/newcomment_body.txt",
  124. {"task": task, "body": msg_body, "site": current_site, "user": user},
  125. )
  126. # Get all thread participants
  127. commenters = Comment.objects.filter(task=task)
  128. recip_list = set(ca.author.email for ca in commenters if ca.author is not None)
  129. for related_user in (task.created_by, task.assigned_to):
  130. if related_user is not None:
  131. recip_list.add(related_user.email)
  132. recip_list = list(m for m in recip_list if m)
  133. if user == task.created_by:
  134. tickets_send_mail(user, task, email_subject, email_body, recip_list)
  135. else:
  136. tickets_send_mail(user, task, email_subject, email_body, recip_list)
  137. tickets_send_mail(user, task, email_subject, email_body, [task.created_by.email])
  138. def change_task_status(task_id: int, new_status: str) -> bool:
  139. """Change task status"""
  140. try:
  141. task = Task.objects.get(id=task_id)
  142. task.status = new_status
  143. task.save()
  144. return True
  145. except Task.DoesNotExist:
  146. log.info(f"Task {task_id} not found.")
  147. return False
  148. def remove_attachment_file(attachment_id: int) -> bool:
  149. """Delete an Attachment object and its corresponding file from the filesystem."""
  150. try:
  151. attachment = Attachment.objects.get(id=attachment_id)
  152. if attachment.file:
  153. if os.path.isfile(attachment.file.path):
  154. os.remove(attachment.file.path)
  155. attachment.delete()
  156. return True
  157. except Attachment.DoesNotExist:
  158. log.info(f"Attachment {attachment_id} not found.")
  159. return False