1# -*- coding: utf-8 -*- 2from __future__ import unicode_literals 3 4import re 5 6from django.forms.utils import flatatt 7from django.template import Variable, VariableDoesNotExist 8from django.template.base import FilterExpression, TemplateSyntaxError, kwarg_re 9from django.template.loader import get_template 10from django.utils import six 11from django.utils.encoding import force_str 12from django.utils.html import format_html 13from django.utils.safestring import mark_safe 14from django.utils.six.moves.urllib.parse import parse_qs, urlencode, urlparse, urlunparse 15 16from .text import text_value 17 18try: 19 from collections.abc import Mapping 20except ImportError: 21 # py27 fallback, remove if support for python 2.7 is dropped 22 from collections import Mapping 23 24 25# RegEx for quoted string 26QUOTED_STRING = re.compile(r'^["\'](?P<noquotes>.+)["\']$') 27 28 29def handle_var(value, context): 30 """ 31 Handle template tag variable 32 """ 33 # Resolve FilterExpression and Variable immediately 34 if isinstance(value, FilterExpression) or isinstance(value, Variable): 35 return value.resolve(context) 36 # Return quoted strings unquoted 37 # http://djangosnippets.org/snippets/886 38 stringval = QUOTED_STRING.search(value) 39 if stringval: 40 return stringval.group("noquotes") 41 # Resolve variable or return string value 42 try: 43 return Variable(value).resolve(context) 44 except VariableDoesNotExist: 45 return value 46 47 48def parse_token_contents(parser, token): 49 """ 50 Parse template tag contents 51 """ 52 bits = token.split_contents() 53 tag = bits.pop(0) 54 args = [] 55 kwargs = {} 56 asvar = None 57 if len(bits) >= 2 and bits[-2] == "as": 58 asvar = bits[-1] 59 bits = bits[:-2] 60 if len(bits): 61 for bit in bits: 62 match = kwarg_re.match(bit) 63 if not match: 64 raise TemplateSyntaxError('Malformed arguments to tag "{}"'.format(tag)) 65 name, value = match.groups() 66 if name: 67 kwargs[name] = parser.compile_filter(value) 68 else: 69 args.append(parser.compile_filter(value)) 70 return {"tag": tag, "args": args, "kwargs": kwargs, "asvar": asvar} 71 72 73def split_css_classes(css_classes): 74 """ 75 Turn string into a list of CSS classes 76 """ 77 classes_list = text_value(css_classes).split(" ") 78 return [c for c in classes_list if c] 79 80 81def add_css_class(css_classes, css_class, prepend=False): 82 """ 83 Add a CSS class to a string of CSS classes 84 """ 85 classes_list = split_css_classes(css_classes) 86 classes_to_add = [c for c in split_css_classes(css_class) if c not in classes_list] 87 if prepend: 88 classes_list = classes_to_add + classes_list 89 else: 90 classes_list += classes_to_add 91 return " ".join(classes_list) 92 93 94def remove_css_class(css_classes, css_class): 95 """ 96 Remove a CSS class from a string of CSS classes 97 """ 98 remove = set(split_css_classes(css_class)) 99 classes_list = [c for c in split_css_classes(css_classes) if c not in remove] 100 return " ".join(classes_list) 101 102 103def render_script_tag(url): 104 """ 105 Build a script tag 106 """ 107 url_dict = sanitize_url_dict(url) 108 url_dict.setdefault("src", url_dict.pop("url", None)) 109 return render_tag("script", url_dict) 110 111 112def render_link_tag(url, rel="stylesheet", media=None): 113 """ 114 Build a link tag 115 """ 116 url_dict = sanitize_url_dict(url, url_attr="href") 117 url_dict.setdefault("href", url_dict.pop("url", None)) 118 url_dict["rel"] = rel 119 if media: 120 url_dict["media"] = media 121 return render_tag("link", attrs=url_dict, close=False) 122 123 124def render_tag(tag, attrs=None, content=None, close=True): 125 """ 126 Render a HTML tag 127 """ 128 builder = "<{tag}{attrs}>{content}" 129 if content or close: 130 builder += "</{tag}>" 131 return format_html( 132 builder, 133 tag=tag, 134 attrs=mark_safe(flatatt(attrs)) if attrs else "", 135 content=text_value(content), 136 ) 137 138 139def render_template_file(template, context=None): 140 """ 141 Render a Template to unicode 142 """ 143 assert isinstance(context, Mapping) 144 template = get_template(template) 145 return template.render(context) 146 147 148def url_replace_param(url, name, value): 149 """ 150 Replace a GET parameter in an URL 151 """ 152 url_components = urlparse(force_str(url)) 153 154 params = parse_qs(url_components.query) 155 156 if value is None: 157 del params[name] 158 else: 159 params[name] = value 160 161 return mark_safe( 162 urlunparse( 163 [ 164 url_components.scheme, 165 url_components.netloc, 166 url_components.path, 167 url_components.params, 168 urlencode(params, doseq=True), 169 url_components.fragment, 170 ] 171 ) 172 ) 173 174 175def sanitize_url_dict(url, url_attr="src"): 176 """ 177 Sanitize url dict as used in django-bootstrap4 settings 178 """ 179 if isinstance(url, six.string_types): 180 return {url_attr: url} 181 return url.copy() 182