utils.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import os
  4. import hashlib
  5. import binascii
  6. import base64
  7. import html2text
  8. import re
  9. from datetime import date, timedelta
  10. from contextlib import contextmanager
  11. from django.utils import translation
  12. from django.core.mail import EmailMultiAlternatives
  13. from django.core.files.storage import FileSystemStorage
  14. from django.conf import settings
  15. from django.template.loader import get_template
  16. from django.template import Context, TemplateDoesNotExist
  17. from django.contrib.sites.models import Site
  18. # Stockage des fichiers privés (comme les factures par exemple)
  19. private_files_storage = FileSystemStorage(location=settings.PRIVATE_FILES_ROOT)
  20. # regexp which matches for ex irc://irc.example.tld/#channel
  21. re_chat_url = re.compile(r'(?P<protocol>\w+://)(?P<server>[\w\.]+)/(?P<channel>.*)')
  22. def str_or_none(obj):
  23. return str(obj) if obj else None
  24. def ldap_hash(password):
  25. """Hash a password for use with LDAP. If the password is already hashed,
  26. do nothing."""
  27. if password and not password.startswith('{SSHA}'):
  28. salt = binascii.hexlify(os.urandom(8))
  29. digest = hashlib.sha1(password.encode() + salt).digest()
  30. return '{SSHA}' + base64.b64encode(digest + salt).decode()
  31. else:
  32. return password
  33. def send_templated_email(to, subject_template, body_template, context={}, attachements=[]):
  34. """
  35. Send a multialternative email based on html and optional txt template.
  36. """
  37. # Ensure arrays when needed
  38. if not isinstance(to, list):
  39. to = [to]
  40. if not isinstance(attachements, list):
  41. attachements = [attachements]
  42. # Add domain in context
  43. context['domain'] = Site.objects.get_current()
  44. # If .html/.txt is specified in template name remove it
  45. body_template = body_template.split('.')[0]
  46. subject_template = subject_template.split('.')[0]
  47. # Get html template for body, fail if not exists
  48. template_html = get_template('%s.html' % (body_template,))
  49. html_content = template_html.render(Context(context))
  50. # Get txt template for subject, fail if not exists
  51. subject_template = get_template('%s.txt' % (subject_template,))
  52. subject = subject_template.render(Context(context))
  53. # Get rid of newlines
  54. subject = subject.strip().replace('\n', '')
  55. # Try to get a txt version, convert from html to markdown style
  56. # (using html2text) if fail
  57. try:
  58. template_txt = get_template('%s.txt' % (body_template,))
  59. text_content = template_txt.render_to_string(Context(context))
  60. except TemplateDoesNotExist:
  61. text_content = html2text.html2text(html_content)
  62. # make multipart email default : text, alternative : html
  63. msg = EmailMultiAlternatives(subject=subject, body=text_content, to=to)
  64. msg.attach_alternative(html_content, "text/html")
  65. # Set attachements
  66. for attachement in attachements:
  67. msg.attach_file(attachement)
  68. # Send email
  69. msg.send()
  70. def delete_selected(modeladmin, request, queryset):
  71. """Overrides QuerySet's delete() function to remove objects one by one
  72. so, that they are deleted in the LDAP (Redmine issue #195)."""
  73. for obj in queryset:
  74. obj.delete()
  75. delete_selected.short_description = "Supprimer tous les objets sélectionnés."
  76. # Time-related functions
  77. def in_one_year():
  78. return date.today() + timedelta(365)
  79. def start_of_month():
  80. return date(date.today().year, date.today().month, 1)
  81. def end_of_month():
  82. today = date.today()
  83. if today.month == 12:
  84. return date(today.year + 1, 1, 1) - timedelta(days=1)
  85. else:
  86. return date(today.year, today.month + 1, 1) - timedelta(days=1)
  87. @contextmanager
  88. def respect_language(language):
  89. """Context manager that changes the current translation language for
  90. all code inside the following block.
  91. Can be used like this::
  92. from amorce.utils import respect_language
  93. def my_func(language='fr'):
  94. with respect_language(language):
  95. pass
  96. """
  97. if language:
  98. prev = translation.get_language()
  99. translation.activate(language)
  100. try:
  101. yield
  102. finally:
  103. translation.activate(prev)
  104. else:
  105. yield
  106. def respects_language(fun):
  107. """Associated decorator"""
  108. @wraps(fun)
  109. def _inner(*args, **kwargs):
  110. with respect_language(kwargs.pop('language', None)):
  111. return fun(*args, **kwargs)
  112. return _inner
  113. if __name__ == '__main__':
  114. print(ldap_hash('coin'))