utils.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. from datetime import datetime, timedelta
  2. from importlib import import_module
  3. from django.conf import settings
  4. from django.http import Http404
  5. from django.urls import resolve, reverse
  6. from django.utils import html, timezone
  7. from django.utils.encoding import force_text
  8. from django.utils.translation import ugettext_lazy as _
  9. from django.utils.translation import ungettext_lazy
  10. MISAGO_SLUGIFY = getattr(settings, 'MISAGO_SLUGIFY', 'misago.core.slugify.default')
  11. def slugify(string):
  12. string = six.text_type(string)
  13. string = unidecode(string)
  14. return django_slugify(string.replace('_', ' ').strip())
  15. def resolve_slugify(path):
  16. path_bits = path.split('.')
  17. module, name = '.'.join(path_bits[:-1]), path_bits[-1]
  18. try:
  19. return getattr(import_module(module), name)
  20. except AttributeError:
  21. raise ImportError("name {} not found in {} module".format(name, module))
  22. except ImportError:
  23. raise ImportError("module {} does not exist".format(module))
  24. slugify = resolve_slugify(MISAGO_SLUGIFY)
  25. def format_plaintext_for_html(string):
  26. return html.linebreaks(html.urlize(html.escape(string)))
  27. def encode_json_html(string):
  28. return string.replace('<', r'\u003C')
  29. """
  30. Turn ISO 8601 string into datetime object
  31. """
  32. ISO8601_FORMATS = (
  33. "%Y-%m-%dT%H:%M:%S",
  34. "%Y-%m-%dT%H:%M:%S.%f",
  35. )
  36. def parse_iso8601_string(value):
  37. value = force_text(value, strings_only=True).rstrip('Z')
  38. for format in ISO8601_FORMATS:
  39. try:
  40. parsed_value = datetime.strptime(value, format)
  41. break
  42. except ValueError:
  43. try:
  44. parsed_value = datetime.strptime(value[:-6], format)
  45. break
  46. except ValueError:
  47. pass
  48. else:
  49. raise ValueError('failed to hydrate the %s timestamp' % value)
  50. offset_str = value[-6:]
  51. if offset_str and offset_str[0] in ('-', '+'):
  52. tz_offset = timedelta(hours=int(offset_str[1:3]), minutes=int(offset_str[4:6]))
  53. tz_offset = tz_offset.seconds // 60
  54. if offset_str[0] == '-':
  55. tz_offset *= -1
  56. else:
  57. tz_offset = 0
  58. tz_correction = timezone.get_fixed_timezone(tz_offset)
  59. return timezone.make_aware(parsed_value, tz_correction)
  60. """
  61. Mark request as having sensitive parameters
  62. We can't use decorator because of DRF uses custom HttpRequest
  63. that is incompatibile with Django's decorator
  64. """
  65. def hide_post_parameters(request):
  66. request.sensitive_post_parameters = '__ALL__'
  67. """
  68. Return path utility
  69. """
  70. def clean_return_path(request):
  71. if request.method == 'POST' and 'return_path' in request.POST:
  72. return _get_return_path_from_post(request)
  73. else:
  74. return _get_return_path_from_referer(request)
  75. def _get_return_path_from_post(request):
  76. return_path = request.POST.get('return_path')
  77. try:
  78. if not return_path:
  79. raise ValueError()
  80. if not return_path.startswith('/'):
  81. raise ValueError()
  82. resolve(return_path)
  83. return return_path
  84. except (Http404, ValueError):
  85. return None
  86. def _get_return_path_from_referer(request):
  87. referer = request.META.get('HTTP_REFERER')
  88. try:
  89. if not referer:
  90. raise ValueError()
  91. if not referer.startswith(request.scheme):
  92. raise ValueError()
  93. referer = referer[len(request.scheme) + 3:]
  94. if not referer.startswith(request.META['HTTP_HOST']):
  95. raise ValueError()
  96. referer = referer[len(request.META['HTTP_HOST'].rstrip('/')):]
  97. if not referer.startswith('/'):
  98. raise ValueError()
  99. resolve(referer)
  100. return referer
  101. except (Http404, KeyError, ValueError):
  102. return None
  103. """
  104. Utils for resolving requests destination
  105. """
  106. def _is_request_path_under_misago(request):
  107. # We are assuming that forum_index link is root of all Misago links
  108. forum_index = reverse('misago:index')
  109. path_info = request.path_info
  110. if len(forum_index) > len(path_info):
  111. return False
  112. return path_info[:len(forum_index)] == forum_index
  113. def is_request_to_misago(request):
  114. try:
  115. return request._request_to_misago
  116. except AttributeError:
  117. request._request_to_misago = _is_request_path_under_misago(request)
  118. return request._request_to_misago
  119. def is_referer_local(request):
  120. referer = request.META.get('HTTP_REFERER')
  121. if not referer:
  122. return False
  123. if not referer.startswith(request.scheme):
  124. return False
  125. referer = referer[len(request.scheme) + 3:]
  126. if not referer.startswith(request.META['HTTP_HOST']):
  127. return False
  128. referer = referer[len(request.META['HTTP_HOST'].rstrip('/')):]
  129. if not referer.startswith('/'):
  130. return False
  131. return True