Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/templatetags/rest_framework.py: 29%
177 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import re
2from collections import OrderedDict
4from django import template
5from django.template import loader
6from django.urls import NoReverseMatch, reverse
7from django.utils.encoding import iri_to_uri
8from django.utils.html import escape, format_html, smart_urlquote
9from django.utils.safestring import mark_safe
11from rest_framework.compat import apply_markdown, pygments_highlight
12from rest_framework.renderers import HTMLFormRenderer
13from rest_framework.utils.urls import replace_query_param
15register = template.Library()
17# Regex for adding classes to html snippets
18class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
21@register.tag(name='code')
22def highlight_code(parser, token):
23 code = token.split_contents()[-1]
24 nodelist = parser.parse(('endcode',))
25 parser.delete_first_token()
26 return CodeNode(code, nodelist)
29class CodeNode(template.Node):
30 style = 'emacs'
32 def __init__(self, lang, code):
33 self.lang = lang
34 self.nodelist = code
36 def render(self, context):
37 text = self.nodelist.render(context)
38 return pygments_highlight(text, self.lang, self.style)
41@register.filter()
42def with_location(fields, location):
43 return [
44 field for field in fields
45 if field.location == location
46 ]
49@register.simple_tag
50def form_for_link(link):
51 import coreschema
52 properties = OrderedDict([
53 (field.name, field.schema or coreschema.String())
54 for field in link.fields
55 ])
56 required = [
57 field.name
58 for field in link.fields
59 if field.required
60 ]
61 schema = coreschema.Object(properties=properties, required=required)
62 return mark_safe(coreschema.render_to_form(schema))
65@register.simple_tag
66def render_markdown(markdown_text):
67 if apply_markdown is None:
68 return markdown_text
69 return mark_safe(apply_markdown(markdown_text))
72@register.simple_tag
73def get_pagination_html(pager):
74 return pager.to_html()
77@register.simple_tag
78def render_form(serializer, template_pack=None):
79 style = {'template_pack': template_pack} if template_pack else {}
80 renderer = HTMLFormRenderer()
81 return renderer.render(serializer.data, None, {'style': style})
84@register.simple_tag
85def render_field(field, style):
86 renderer = style.get('renderer', HTMLFormRenderer())
87 return renderer.render_field(field, style)
90@register.simple_tag
91def optional_login(request):
92 """
93 Include a login snippet if REST framework's login view is in the URLconf.
94 """
95 try:
96 login_url = reverse('rest_framework:login')
97 except NoReverseMatch:
98 return ''
100 snippet = "<li><a href='{href}?next={next}'>Log in</a></li>"
101 snippet = format_html(snippet, href=login_url, next=escape(request.path))
103 return mark_safe(snippet)
106@register.simple_tag
107def optional_docs_login(request):
108 """
109 Include a login snippet if REST framework's login view is in the URLconf.
110 """
111 try:
112 login_url = reverse('rest_framework:login')
113 except NoReverseMatch:
114 return 'log in'
116 snippet = "<a href='{href}?next={next}'>log in</a>"
117 snippet = format_html(snippet, href=login_url, next=escape(request.path))
119 return mark_safe(snippet)
122@register.simple_tag
123def optional_logout(request, user):
124 """
125 Include a logout snippet if REST framework's logout view is in the URLconf.
126 """
127 try:
128 logout_url = reverse('rest_framework:logout')
129 except NoReverseMatch:
130 snippet = format_html('<li class="navbar-text">{user}</li>', user=escape(user))
131 return mark_safe(snippet)
133 snippet = """<li class="dropdown">
134 <a href="#" class="dropdown-toggle" data-toggle="dropdown">
135 {user}
136 <b class="caret"></b>
137 </a>
138 <ul class="dropdown-menu">
139 <li><a href='{href}?next={next}'>Log out</a></li>
140 </ul>
141 </li>"""
142 snippet = format_html(snippet, user=escape(user), href=logout_url, next=escape(request.path))
144 return mark_safe(snippet)
147@register.simple_tag
148def add_query_param(request, key, val):
149 """
150 Add a query parameter to the current request url, and return the new url.
151 """
152 iri = request.get_full_path()
153 uri = iri_to_uri(iri)
154 return escape(replace_query_param(uri, key, val))
157@register.filter
158def as_string(value):
159 if value is None:
160 return ''
161 return '%s' % value
164@register.filter
165def as_list_of_strings(value):
166 return [
167 '' if (item is None) else ('%s' % item)
168 for item in value
169 ]
172@register.filter
173def add_class(value, css_class):
174 """
175 https://stackoverflow.com/questions/4124220/django-adding-css-classes-when-rendering-form-fields-in-a-template
177 Inserts classes into template variables that contain HTML tags,
178 useful for modifying forms without needing to change the Form objects.
180 Usage:
182 {{ field.label_tag|add_class:"control-label" }}
184 In the case of REST Framework, the filter is used to add Bootstrap-specific
185 classes to the forms.
186 """
187 html = str(value)
188 match = class_re.search(html)
189 if match:
190 m = re.search(r'^%s$|^%s\s|\s%s\s|\s%s$' % (css_class, css_class,
191 css_class, css_class),
192 match.group(1))
193 if not m:
194 return mark_safe(class_re.sub(match.group(1) + " " + css_class,
195 html))
196 else:
197 return mark_safe(html.replace('>', ' class="%s">' % css_class, 1))
198 return value
201@register.filter
202def format_value(value):
203 if getattr(value, 'is_hyperlink', False):
204 name = str(value.obj)
205 return mark_safe('<a href=%s>%s</a>' % (value, escape(name)))
206 if value is None or isinstance(value, bool):
207 return mark_safe('<code>%s</code>' % {True: 'true', False: 'false', None: 'null'}[value])
208 elif isinstance(value, list):
209 if any(isinstance(item, (list, dict)) for item in value):
210 template = loader.get_template('rest_framework/admin/list_value.html')
211 else:
212 template = loader.get_template('rest_framework/admin/simple_list_value.html')
213 context = {'value': value}
214 return template.render(context)
215 elif isinstance(value, dict):
216 template = loader.get_template('rest_framework/admin/dict_value.html')
217 context = {'value': value}
218 return template.render(context)
219 elif isinstance(value, str):
220 if (
221 (value.startswith('http:') or value.startswith('https:')) and not
222 re.search(r'\s', value)
223 ):
224 return mark_safe('<a href="{value}">{value}</a>'.format(value=escape(value)))
225 elif '@' in value and not re.search(r'\s', value):
226 return mark_safe('<a href="mailto:{value}">{value}</a>'.format(value=escape(value)))
227 elif '\n' in value:
228 return mark_safe('<pre>%s</pre>' % escape(value))
229 return str(value)
232@register.filter
233def items(value):
234 """
235 Simple filter to return the items of the dict. Useful when the dict may
236 have a key 'items' which is resolved first in Django template dot-notation
237 lookup. See issue #4931
238 Also see: https://stackoverflow.com/questions/15416662/django-template-loop-over-dictionary-items-with-items-as-key
239 """
240 if value is None:
241 # `{% for k, v in value.items %}` doesn't raise when value is None or
242 # not in the context, so neither should `{% for k, v in value|items %}`
243 return []
244 return value.items()
247@register.filter
248def data(value):
249 """
250 Simple filter to access `data` attribute of object,
251 specifically coreapi.Document.
253 As per `items` filter above, allows accessing `document.data` when
254 Document contains Link keyed-at "data".
256 See issue #5395
257 """
258 return value.data
261@register.filter
262def schema_links(section, sec_key=None):
263 """
264 Recursively find every link in a schema, even nested.
265 """
266 NESTED_FORMAT = '%s > %s' # this format is used in docs/js/api.js:normalizeKeys
267 links = section.links
268 if section.data:
269 data = section.data.items()
270 for sub_section_key, sub_section in data:
271 new_links = schema_links(sub_section, sec_key=sub_section_key)
272 links.update(new_links)
274 if sec_key is not None:
275 new_links = OrderedDict()
276 for link_key, link in links.items():
277 new_key = NESTED_FORMAT % (sec_key, link_key)
278 new_links.update({new_key: link})
279 return new_links
281 return links
284@register.filter
285def add_nested_class(value):
286 if isinstance(value, dict):
287 return 'class=nested'
288 if isinstance(value, list) and any(isinstance(item, (list, dict)) for item in value):
289 return 'class=nested'
290 return ''
293# Bunch of stuff cloned from urlize
294TRAILING_PUNCTUATION = ['.', ',', ':', ';', '.)', '"', "']", "'}", "'"]
295WRAPPING_PUNCTUATION = [('(', ')'), ('<', '>'), ('[', ']'), ('<', '>'),
296 ('"', '"'), ("'", "'")]
297word_split_re = re.compile(r'(\s+)')
298simple_url_re = re.compile(r'^https?://\[?\w', re.IGNORECASE)
299simple_url_2_re = re.compile(r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)$', re.IGNORECASE)
300simple_email_re = re.compile(r'^\S+@\S+\.\S+$')
303def smart_urlquote_wrapper(matched_url):
304 """
305 Simple wrapper for smart_urlquote. ValueError("Invalid IPv6 URL") can
306 be raised here, see issue #1386
307 """
308 try:
309 return smart_urlquote(matched_url)
310 except ValueError:
311 return None
314@register.filter
315def break_long_headers(header):
316 """
317 Breaks headers longer than 160 characters (~page length)
318 when possible (are comma separated)
319 """
320 if len(header) > 160 and ',' in header:
321 header = mark_safe('<br> ' + ', <br>'.join(header.split(',')))
322 return header