123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- # -*- coding: utf-8 -*-
- from __future__ import unicode_literals
- import os
- import hashlib
- import binascii
- import base64
- import html2text
- import re
- import sys
- from datetime import date, timedelta
- from contextlib import contextmanager
- from functools import wraps
- from django.utils import translation
- from django.core.mail import EmailMultiAlternatives
- from django.core.files.storage import FileSystemStorage
- from django.conf import settings
- from django.template.loader import get_template
- from django.template import Context, TemplateDoesNotExist
- from django.contrib.sites.models import Site
- # Stockage des fichiers privés (comme les factures par exemple)
- private_files_storage = FileSystemStorage(location=settings.PRIVATE_FILES_ROOT)
- # regexp which matches for ex irc://irc.example.tld/#channel
- re_chat_url = re.compile(r'(?P<protocol>\w+://)(?P<server>[\w\.]+)/(?P<channel>.*)')
- def str_or_none(obj):
- return str(obj) if obj else None
- def rstrip_str(s, suffix):
- """Return a copy of the string [s] with the string [suffix] removed from
- the end (if [s] ends with [suffix], otherwise return s)."""
- if s.endswith(suffix):
- return s[:-len(suffix)]
- else:
- return s
- def ldap_hash(password):
- """Hash a password for use with LDAP. If the password is already hashed,
- do nothing.
- Implementation details: Django provides us with a unicode object, so
- we have to encode/decode it as needed to switch between unicode and
- bytes. The code should work fine with both python2 and python3.
- """
- if password and not password.startswith('{SSHA}'):
- salt = binascii.hexlify(os.urandom(8))
- digest = hashlib.sha1(password.encode("utf-8") + salt).digest()
- return '{SSHA}' + base64.b64encode(digest + salt).decode("utf-8")
- else:
- return password
- def send_templated_email(to, subject_template, body_template, context={}, attachements=[], **kwargs):
- """
- Send a multialternative email based on html and optional txt template.
- :param **kwargs: extra-args pased as-is to EmailMultiAlternatives()
- """
- # Ensure arrays when needed
- if not isinstance(to, list):
- to = [to]
- if not isinstance(attachements, list):
- attachements = [attachements]
- # Add domain in context
- context['domain'] = Site.objects.get_current()
- # If .html/.txt is specified in template name remove it
- body_template = body_template.split('.')[0]
- subject_template = subject_template.split('.')[0]
- # Get html template for body, fail if not exists
- template_html = get_template('%s.html' % (body_template,))
- html_content = template_html.render(Context(context))
- # Get txt template for subject, fail if not exists
- subject_template = get_template('%s.txt' % (subject_template,))
- subject = subject_template.render(Context(context))
- # Get rid of newlines
- subject = subject.strip().replace('\n', '')
- # Try to get a txt version, convert from html to markdown style
- # (using html2text) if fail
- try:
- template_txt = get_template('%s.txt' % (body_template,))
- text_content = template_txt.render_to_string(Context(context))
- except TemplateDoesNotExist:
- text_content = html2text.html2text(html_content)
- # make multipart email default : text, alternative : html
- msg = EmailMultiAlternatives(subject=subject, body=text_content, to=to, **kwargs)
- msg.attach_alternative(html_content, "text/html")
- # Set attachements
- for attachement in attachements:
- msg.attach_file(attachement)
- # Send email
- msg.send()
- def delete_selected(modeladmin, request, queryset):
- """Overrides QuerySet's delete() function to remove objects one by one
- so, that they are deleted in the LDAP (Redmine issue #195)."""
- for obj in queryset:
- obj.delete()
- delete_selected.short_description = "Supprimer tous les objets sélectionnés."
- # Time-related functions
- def in_one_year():
- return date.today() + timedelta(365)
- def start_of_month():
- return date(date.today().year, date.today().month, 1)
- def end_of_month():
- today = date.today()
- if today.month == 12:
- return date(today.year + 1, 1, 1) - timedelta(days=1)
- else:
- return date(today.year, today.month + 1, 1) - timedelta(days=1)
- @contextmanager
- def respect_language(language):
- """Context manager that changes the current translation language for
- all code inside the following block.
- Can be used like this::
- from amorce.utils import respect_language
- def my_func(language='fr'):
- with respect_language(language):
- pass
- """
- if language:
- prev = translation.get_language()
- translation.activate(language)
- try:
- yield
- finally:
- translation.activate(prev)
- else:
- yield
- def respects_language(fun):
- """Associated decorator"""
- @wraps(fun)
- def _inner(*args, **kwargs):
- with respect_language(kwargs.pop('language', None)):
- return fun(*args, **kwargs)
- return _inner
- def disable_for_loaddata(signal_handler):
- """Decorator for post_save events that disables them when loading
- data from fixtures."""
- @wraps(signal_handler)
- def wrapper(*args, **kwargs):
- if kwargs['raw']:
- return
- signal_handler(*args, **kwargs)
- return wrapper
- if __name__ == '__main__':
- # ldap_hash expects an unicode string
- print(ldap_hash(sys.argv[1].decode("utf-8")))
|