PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/rest_framework/templatetags/rest_framework.py

https://github.com/jxiaodev/django-rest-framework
Python | 254 lines | 223 code | 9 blank | 22 comment | 3 complexity | 78a15bd870526110180e44c4745ca498 MD5 | raw file
  1. from __future__ import unicode_literals, absolute_import
  2. from django import template
  3. from django.core.urlresolvers import reverse, NoReverseMatch
  4. from django.http import QueryDict
  5. from django.utils.html import escape
  6. from django.utils.safestring import SafeData, mark_safe
  7. from rest_framework.compat import urlparse
  8. from rest_framework.compat import force_text
  9. from rest_framework.compat import six
  10. import re
  11. import string
  12. register = template.Library()
  13. # Note we don't use 'load staticfiles', because we need a 1.3 compatible
  14. # version, so instead we include the `static` template tag ourselves.
  15. # When 1.3 becomes unsupported by REST framework, we can instead start to
  16. # use the {% load staticfiles %} tag, remove the following code,
  17. # and add a dependancy that `django.contrib.staticfiles` must be installed.
  18. # Note: We can't put this into the `compat` module because the compat import
  19. # from rest_framework.compat import ...
  20. # conflicts with this rest_framework template tag module.
  21. try: # Django 1.5+
  22. from django.contrib.staticfiles.templatetags.staticfiles import StaticFilesNode
  23. @register.tag('static')
  24. def do_static(parser, token):
  25. return StaticFilesNode.handle_token(parser, token)
  26. except ImportError:
  27. try: # Django 1.4
  28. from django.contrib.staticfiles.storage import staticfiles_storage
  29. @register.simple_tag
  30. def static(path):
  31. """
  32. A template tag that returns the URL to a file
  33. using staticfiles' storage backend
  34. """
  35. return staticfiles_storage.url(path)
  36. except ImportError: # Django 1.3
  37. from urlparse import urljoin
  38. from django import template
  39. from django.templatetags.static import PrefixNode
  40. class StaticNode(template.Node):
  41. def __init__(self, varname=None, path=None):
  42. if path is None:
  43. raise template.TemplateSyntaxError(
  44. "Static template nodes must be given a path to return.")
  45. self.path = path
  46. self.varname = varname
  47. def url(self, context):
  48. path = self.path.resolve(context)
  49. return self.handle_simple(path)
  50. def render(self, context):
  51. url = self.url(context)
  52. if self.varname is None:
  53. return url
  54. context[self.varname] = url
  55. return ''
  56. @classmethod
  57. def handle_simple(cls, path):
  58. return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)
  59. @classmethod
  60. def handle_token(cls, parser, token):
  61. """
  62. Class method to parse prefix node and return a Node.
  63. """
  64. bits = token.split_contents()
  65. if len(bits) < 2:
  66. raise template.TemplateSyntaxError(
  67. "'%s' takes at least one argument (path to file)" % bits[0])
  68. path = parser.compile_filter(bits[1])
  69. if len(bits) >= 2 and bits[-2] == 'as':
  70. varname = bits[3]
  71. else:
  72. varname = None
  73. return cls(varname, path)
  74. @register.tag('static')
  75. def do_static_13(parser, token):
  76. return StaticNode.handle_token(parser, token)
  77. def replace_query_param(url, key, val):
  78. """
  79. Given a URL and a key/val pair, set or replace an item in the query
  80. parameters of the URL, and return the new URL.
  81. """
  82. (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
  83. query_dict = QueryDict(query).copy()
  84. query_dict[key] = val
  85. query = query_dict.urlencode()
  86. return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
  87. # Regex for adding classes to html snippets
  88. class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
  89. # Bunch of stuff cloned from urlize
  90. LEADING_PUNCTUATION = ['(', '<', '&lt;', '"', "'"]
  91. TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '&gt;', '"', "'"]
  92. DOTS = ['&middot;', '*', '\xe2\x80\xa2', '&#149;', '&bull;', '&#8226;']
  93. unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
  94. word_split_re = re.compile(r'(\s+)')
  95. punctuation_re = re.compile('^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % \
  96. ('|'.join([re.escape(x) for x in LEADING_PUNCTUATION]),
  97. '|'.join([re.escape(x) for x in TRAILING_PUNCTUATION])))
  98. simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
  99. link_target_attribute_re = re.compile(r'(<a [^>]*?)target=[^\s>]+')
  100. html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<strong><\/strong>|<\/?smallcaps>|<\/?uppercase>)', re.IGNORECASE)
  101. hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL)
  102. trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\Z')
  103. # And the template tags themselves...
  104. @register.simple_tag
  105. def optional_login(request):
  106. """
  107. Include a login snippet if REST framework's login view is in the URLconf.
  108. """
  109. try:
  110. login_url = reverse('rest_framework:login')
  111. except NoReverseMatch:
  112. return ''
  113. snippet = "<a href='%s?next=%s'>Log in</a>" % (login_url, request.path)
  114. return snippet
  115. @register.simple_tag
  116. def optional_logout(request):
  117. """
  118. Include a logout snippet if REST framework's logout view is in the URLconf.
  119. """
  120. try:
  121. logout_url = reverse('rest_framework:logout')
  122. except NoReverseMatch:
  123. return ''
  124. snippet = "<a href='%s?next=%s'>Log out</a>" % (logout_url, request.path)
  125. return snippet
  126. @register.simple_tag
  127. def add_query_param(request, key, val):
  128. """
  129. Add a query parameter to the current request url, and return the new url.
  130. """
  131. return replace_query_param(request.get_full_path(), key, val)
  132. @register.filter
  133. def add_class(value, css_class):
  134. """
  135. http://stackoverflow.com/questions/4124220/django-adding-css-classes-when-rendering-form-fields-in-a-template
  136. Inserts classes into template variables that contain HTML tags,
  137. useful for modifying forms without needing to change the Form objects.
  138. Usage:
  139. {{ field.label_tag|add_class:"control-label" }}
  140. In the case of REST Framework, the filter is used to add Bootstrap-specific
  141. classes to the forms.
  142. """
  143. html = six.text_type(value)
  144. match = class_re.search(html)
  145. if match:
  146. m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
  147. css_class, css_class),
  148. match.group(1))
  149. if not m:
  150. return mark_safe(class_re.sub(match.group(1) + " " + css_class,
  151. html))
  152. else:
  153. return mark_safe(html.replace('>', ' class="%s">' % css_class, 1))
  154. return value
  155. @register.filter
  156. def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
  157. """
  158. Converts any URLs in text into clickable links.
  159. Works on http://, https://, www. links and links ending in .org, .net or
  160. .com. Links can have trailing punctuation (periods, commas, close-parens)
  161. and leading punctuation (opening parens) and it'll still do the right
  162. thing.
  163. If trim_url_limit is not None, the URLs in link text longer than this limit
  164. will truncated to trim_url_limit-3 characters and appended with an elipsis.
  165. If nofollow is True, the URLs in link text will get a rel="nofollow"
  166. attribute.
  167. If autoescape is True, the link text and URLs will get autoescaped.
  168. """
  169. trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
  170. safe_input = isinstance(text, SafeData)
  171. words = word_split_re.split(force_text(text))
  172. nofollow_attr = nofollow and ' rel="nofollow"' or ''
  173. for i, word in enumerate(words):
  174. match = None
  175. if '.' in word or '@' in word or ':' in word:
  176. match = punctuation_re.match(word)
  177. if match:
  178. lead, middle, trail = match.groups()
  179. # Make URL we want to point to.
  180. url = None
  181. if middle.startswith('http://') or middle.startswith('https://'):
  182. url = middle
  183. elif middle.startswith('www.') or ('@' not in middle and \
  184. middle and middle[0] in string.ascii_letters + string.digits and \
  185. (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
  186. url = 'http://%s' % middle
  187. elif '@' in middle and not ':' in middle and simple_email_re.match(middle):
  188. url = 'mailto:%s' % middle
  189. nofollow_attr = ''
  190. # Make link.
  191. if url:
  192. trimmed = trim_url(middle)
  193. if autoescape and not safe_input:
  194. lead, trail = escape(lead), escape(trail)
  195. url, trimmed = escape(url), escape(trimmed)
  196. middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
  197. words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
  198. else:
  199. if safe_input:
  200. words[i] = mark_safe(word)
  201. elif autoescape:
  202. words[i] = escape(word)
  203. elif safe_input:
  204. words[i] = mark_safe(word)
  205. elif autoescape:
  206. words[i] = escape(word)
  207. return mark_safe(''.join(words))