Changeset 1443
- Timestamp:
- 11/26/05 16:46:31 (4 years ago)
- Files:
-
- django/trunk/django/contrib/admin/templates/admin/change_form.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/change_list.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/edit_inline_stacked.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/edit_inline_tabular.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/field_line.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/filter.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/filters.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/pagination.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/search_form.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/widget/default.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/widget/file.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/widget/foreign.html (modified) (2 diffs)
- django/trunk/django/contrib/admin/templatetags/adminapplist.py (modified) (2 diffs)
- django/trunk/django/contrib/admin/templatetags/admin_list.py (modified) (8 diffs)
- django/trunk/django/contrib/admin/templatetags/adminmedia.py (modified) (2 diffs)
- django/trunk/django/contrib/admin/templatetags/admin_modify.py (modified) (11 diffs)
- django/trunk/django/contrib/admin/templatetags/log.py (modified) (2 diffs)
- django/trunk/django/contrib/admin/views/template.py (modified) (2 diffs)
- django/trunk/django/contrib/comments/templatetags/comments.py (modified) (2 diffs)
- django/trunk/django/contrib/markup/templatetags/markup.py (modified) (5 diffs)
- django/trunk/django/core/template/decorators.py (deleted)
- django/trunk/django/core/template/defaultfilters.py (modified) (18 diffs)
- django/trunk/django/core/template/defaulttags.py (modified) (26 diffs)
- django/trunk/django/core/template/__init__.py (modified) (20 diffs)
- django/trunk/django/core/template/loader.py (modified) (3 diffs)
- django/trunk/django/core/template/loader_tags.py (added)
- django/trunk/django/templatetags/i18n.py (modified) (4 diffs)
- django/trunk/docs/templates_python.txt (modified) (5 diffs)
- django/trunk/docs/templates.txt (modified) (1 diff)
- django/trunk/tests/othertests/defaultfilters.py (modified) (1 diff)
- django/trunk/tests/othertests/markup.py (modified) (3 diffs)
- django/trunk/tests/othertests/templates.py (modified) (3 diffs)
- django/trunk/tests/testapp/templatetags/testtags.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/contrib/admin/templates/admin/change_form.html
r1434 r1443 1 1 {% extends "admin/base_site" %} 2 {% load i18n %} 3 {% load admin_modify %} 4 {% load adminmedia %} 2 {% load i18n admin_modify adminmedia %} 5 3 {% block extrahead %} 6 4 {% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %} django/trunk/django/contrib/admin/templates/admin/change_list.html
r1434 r1443 1 {% load admin_list %} 2 {% load i18n %} 1 {% load adminmedia admin_list i18n %} 3 2 {% extends "admin/base_site" %} 4 3 {% block bodyclass %}change-list{% endblock %} django/trunk/django/contrib/admin/templates/admin/edit_inline_stacked.html
r1434 r1443 1 {% load admin_modify %} 1 2 <fieldset class="module aligned"> 2 3 {% for fcw in bound_related_object.form_field_collection_wrappers %} django/trunk/django/contrib/admin/templates/admin/edit_inline_tabular.html
r1434 r1443 1 {% load admin_modify %} 1 2 <fieldset class="module"> 2 3 <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table> django/trunk/django/contrib/admin/templates/admin/field_line.html
r1434 r1443 1 {% load admin_modify %} 1 2 <div class="{{ class_names }}" > 2 3 {% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %} django/trunk/django/contrib/admin/templates/admin/filter.html
r1434 r1443 1 {% load i18n %} 1 2 <h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3> 2 3 <ul> django/trunk/django/contrib/admin/templates/admin/filters.html
r1434 r1443 1 {% load admin_list %} 1 2 {% if cl.has_filters %}<div id="changelist-filter"> 2 3 <h2>Filter</h2> django/trunk/django/contrib/admin/templates/admin/pagination.html
r1434 r1443 1 {% load admin_list %} 1 2 <p class="paginator"> 2 3 {% if pagination_required %} django/trunk/django/contrib/admin/templates/admin/search_form.html
r1434 r1443 1 {% load adminmedia %} 1 2 {% if cl.lookup_opts.admin.search_fields %} 2 3 <div id="toolbar"><form id="changelist-search" action="" method="get"> django/trunk/django/contrib/admin/templates/widget/default.html
r1434 r1443 1 {% output_all bound_field.form_fields %}1 {% load admin_modify %}{% output_all bound_field.form_fields %} django/trunk/django/contrib/admin/templates/widget/file.html
r1434 r1443 1 {% if bound_field.original_value %}1 {% load admin_modify %}{% if bound_field.original_value %} 2 2 Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br /> 3 3 Change: {% output_all bound_field.form_fields %} django/trunk/django/contrib/admin/templates/widget/foreign.html
r1434 r1443 1 {% load admin_modify adminmedia %} 1 2 {% output_all bound_field.form_fields %} 2 3 {% if bound_field.raw_id_admin %} … … 5 6 {% if bound_field.needs_add_label %} 6 7 <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.element_id}}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a> 7 {% endif %} {% endif %}8 {% endif %}{% endif %} django/trunk/django/contrib/admin/templatetags/adminapplist.py
r948 r1443 1 1 from django.core import template 2 3 register = template.Library() 2 4 3 5 class AdminApplistNode(template.Node): … … 55 57 return AdminApplistNode(tokens[2]) 56 58 57 template.register_tag('get_admin_app_list', get_admin_app_list)59 register.tag('get_admin_app_list', get_admin_app_list) django/trunk/django/contrib/admin/templatetags/admin_list.py
r1434 r1443 4 4 from django.core import meta, template 5 5 from django.core.exceptions import ObjectDoesNotExist 6 from django.core.template.decorators import simple_tag, inclusion_tag7 6 from django.utils import dateformat 8 7 from django.utils.html import strip_tags, escape … … 10 9 from django.utils.translation import get_date_formats 11 10 from django.conf.settings import ADMIN_MEDIA_PREFIX 11 from django.core.template import Library 12 13 register = Library() 12 14 13 15 DOT = '.' 14 16 15 #@ simple_tag17 #@register.simple_tag 16 18 def paginator_number(cl,i): 17 19 if i == DOT: … … 21 23 else: 22 24 return '<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.pages-1 and ' class="end"' or ''), i+1) 23 paginator_number = simple_tag(paginator_number)24 25 #@ inclusion_tag('admin/pagination')25 paginator_number = register.simple_tag(paginator_number) 26 27 #@register.inclusion_tag('admin/pagination') 26 28 def pagination(cl): 27 29 paginator, page_num = cl.paginator, cl.page_num … … 65 67 '1': 1, 66 68 } 67 pagination = inclusion_tag('admin/pagination')(pagination)69 pagination = register.inclusion_tag('admin/pagination')(pagination) 68 70 69 71 def result_headers(cl): … … 178 180 yield list(items_for_result(cl,res)) 179 181 180 #@ inclusion_tag("admin/change_list_results")182 #@register.inclusion_tag("admin/change_list_results") 181 183 def result_list(cl): 182 184 res = list(results(cl)) … … 184 186 'result_headers': list(result_headers(cl)), 185 187 'results': list(results(cl))} 186 result_list = inclusion_tag("admin/change_list_results")(result_list)187 188 #@ inclusion_tag("admin/date_hierarchy")188 result_list = register.inclusion_tag("admin/change_list_results")(result_list) 189 190 #@register.inclusion_tag("admin/date_hierarchy") 189 191 def date_hierarchy(cl): 190 192 lookup_opts, params, lookup_params, lookup_mod = \ … … 257 259 } for year in years ] 258 260 } 259 date_hierarchy = inclusion_tag('admin/date_hierarchy')(date_hierarchy)260 261 #@ inclusion_tag('admin/search_form')261 date_hierarchy = register.inclusion_tag('admin/date_hierarchy')(date_hierarchy) 262 263 #@register.inclusion_tag('admin/search_form') 262 264 def search_form(cl): 263 265 return { … … 266 268 'search_var': SEARCH_VAR 267 269 } 268 search_form = inclusion_tag('admin/search_form')(search_form)269 270 #@ inclusion_tag('admin/filter')270 search_form = register.inclusion_tag('admin/search_form')(search_form) 271 272 #@register.inclusion_tag('admin/filter') 271 273 def filter(cl, spec): 272 274 return {'title': spec.title(), 'choices' : list(spec.choices(cl))} 273 filter = inclusion_tag('admin/filter')(filter)274 275 #@ inclusion_tag('admin/filters')275 filter = register.inclusion_tag('admin/filter')(filter) 276 277 #@register.inclusion_tag('admin/filters') 276 278 def filters(cl): 277 279 return {'cl': cl} 278 filters = inclusion_tag('admin/filters')(filters)280 filters = register.inclusion_tag('admin/filters')(filters) django/trunk/django/contrib/admin/templatetags/adminmedia.py
r1411 r1443 1 from django.core.template.decorators import simple_tag 1 from django.core.template import Library 2 register = Library() 2 3 3 4 def admin_media_prefix(): … … 7 8 return '' 8 9 return ADMIN_MEDIA_PREFIX 9 admin_media_prefix = simple_tag(admin_media_prefix)10 admin_media_prefix = register.simple_tag(admin_media_prefix) django/trunk/django/contrib/admin/templatetags/admin_modify.py
r1434 r1443 3 3 from django.utils.text import capfirst 4 4 from django.utils.functional import curry 5 from django.core.template.decorators import simple_tag, inclusion_tag6 5 from django.contrib.admin.views.main import AdminBoundField 7 6 from django.core.meta.fields import BoundField, Field … … 10 9 import re 11 10 11 register = template.Library() 12 12 13 word_re = re.compile('[A-Z][a-z]+') 13 14 … … 15 16 return '_'.join([s.lower() for s in word_re.findall(name)[:-1]]) 16 17 17 #@ simple_tag18 #@register.simple_tag 18 19 def include_admin_script(script_path): 19 20 return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path) 20 include_admin_script = simple_tag(include_admin_script)21 22 #@ inclusion_tag('admin/submit_line', takes_context=True)21 include_admin_script = register.simple_tag(include_admin_script) 22 23 #@register.inclusion_tag('admin/submit_line', takes_context=True) 23 24 def submit_row(context, bound_manipulator): 24 25 change = context['change'] … … 37 38 'show_save': True 38 39 } 39 submit_row = inclusion_tag('admin/submit_line', takes_context=True)(submit_row)40 41 #@ simple_tag40 submit_row = register.inclusion_tag('admin/submit_line', takes_context=True)(submit_row) 41 42 #@register.simple_tag 42 43 def field_label(bound_field): 43 44 class_names = [] … … 54 55 return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \ 55 56 capfirst(bound_field.field.verbose_name), colon) 56 field_label = simple_tag(field_label)57 field_label = register.simple_tag(field_label) 57 58 58 59 class FieldWidgetNode(template.Node): … … 171 172 return output 172 173 173 #@ simple_tag174 #@register.simple_tag 174 175 def output_all(form_fields): 175 176 return ''.join([str(f) for f in form_fields]) 176 output_all = simple_tag(output_all)177 178 #@ simple_tag177 output_all = register.simple_tag(output_all) 178 179 #@register.simple_tag 179 180 def auto_populated_field_script(auto_pop_fields, change = False): 180 181 for field in auto_pop_fields: … … 192 193 f, field.name, add_values, field.maxlength)) 193 194 return ''.join(t) 194 auto_populated_field_script = simple_tag(auto_populated_field_script)195 196 #@ simple_tag195 auto_populated_field_script = register.simple_tag(auto_populated_field_script) 196 197 #@register.simple_tag 197 198 def filter_interface_script_maybe(bound_field): 198 199 f = bound_field.field … … 203 204 else: 204 205 return '' 205 filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)206 filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe) 206 207 207 208 def do_one_arg_tag(node_factory, parser,token): … … 214 215 tag_name = class_name_to_underscored(node.__name__) 215 216 parse_func = curry(do_one_arg_tag, node) 216 template.register_tag(tag_name, parse_func)217 register.tag(tag_name, parse_func) 217 218 218 219 one_arg_tag_nodes = ( … … 224 225 register_one_arg_tag(node) 225 226 226 #@ inclusion_tag('admin/field_line', takes_context=True)227 #@register.inclusion_tag('admin/field_line', takes_context=True) 227 228 def admin_field_line(context, argument_val): 228 229 if (isinstance(argument_val, BoundField)): … … 250 251 'class_names': " ".join(class_names), 251 252 } 252 admin_field_line = inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)253 254 #@ simple_tag253 admin_field_line = register.inclusion_tag('admin/field_line', takes_context=True)(admin_field_line) 254 255 #@register.simple_tag 255 256 def object_pk(bound_manip, ordered_obj): 256 257 return bound_manip.get_ordered_object_pk(ordered_obj) 257 258 258 object_pk = simple_tag(object_pk)259 object_pk = register.simple_tag(object_pk) django/trunk/django/contrib/admin/templatetags/log.py
r948 r1443 1 1 from django.models.admin import log 2 2 from django.core import template 3 4 register = template.Library() 3 5 4 6 class AdminLogNode(template.Node): … … 49 51 return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None)) 50 52 51 template.register_tag('get_admin_log', DoGetAdminLog('get_admin_log'))53 register.tag('get_admin_log', DoGetAdminLog('get_admin_log')) django/trunk/django/contrib/admin/views/template.py
r948 r1443 51 51 52 52 # so that inheritance works in the site's context, register a new function 53 # for "extends" that uses the site's TEMPLATE_DIR instead53 # for "extends" that uses the site's TEMPLATE_DIRS instead. 54 54 def new_do_extends(parser, token): 55 55 node = loader.do_extends(parser, token) 56 56 node.template_dirs = settings_module.TEMPLATE_DIRS 57 57 return node 58 template.register_tag('extends', new_do_extends) 58 register = template.Library() 59 register.tag('extends', new_do_extends) 60 template.builtins.append(register) 59 61 60 # now validate the template using the new template dirs61 # making sure to reset the extends function in any case 62 # Now validate the template using the new template dirs 63 # making sure to reset the extends function in any case. 62 64 error = None 63 65 try: … … 66 68 except template.TemplateSyntaxError, e: 67 69 error = e 68 template. register_tag('extends', loader.do_extends)70 template.builtins.remove(register) 69 71 if error: 70 72 raise validators.ValidationError, e.args django/trunk/django/contrib/comments/templatetags/comments.py
r1378 r1443 6 6 from django.models.core import contenttypes 7 7 import re 8 9 register = template.Library() 8 10 9 11 COMMENT_FORM = ''' … … 361 363 362 364 # registration comments 363 template.register_tag('get_comment_list', DoGetCommentList(False))364 template.register_tag('comment_form', DoCommentForm(False))365 template.register_tag('get_comment_count', DoCommentCount(False))365 register.tag('get_comment_list', DoGetCommentList(False)) 366 register.tag('comment_form', DoCommentForm(False)) 367 register.tag('get_comment_count', DoCommentCount(False)) 366 368 # free comments 367 template.register_tag('get_free_comment_list', DoGetCommentList(True))368 template.register_tag('free_comment_form', DoCommentForm(True))369 template.register_tag('get_free_comment_count', DoCommentCount(True))369 register.tag('get_free_comment_list', DoGetCommentList(True)) 370 register.tag('free_comment_form', DoCommentForm(True)) 371 register.tag('get_free_comment_count', DoCommentCount(True)) django/trunk/django/contrib/markup/templatetags/markup.py
r467 r1443 5 5 * Textile, which requires the PyTextile library available at 6 6 http://dealmeida.net/projects/textile/ 7 7 8 8 * Markdown, which requires the Python-markdown library from 9 9 http://www.freewisdom.org/projects/python-markdown 10 10 11 11 * ReStructuredText, which requires docutils from http://docutils.sf.net/ 12 12 13 13 In each case, if the required library is not installed, the filter will 14 14 silently fail and return the un-marked-up text. … … 17 17 from django.core import template 18 18 19 def textile(value, _): 19 register = template.Library() 20 21 def textile(value): 20 22 try: 21 23 import textile … … 24 26 else: 25 27 return textile.textile(value) 26 27 def markdown(value , _):28 29 def markdown(value): 28 30 try: 29 31 import markdown … … 32 34 else: 33 35 return markdown.markdown(value) 34 35 def restructuredtext(value , _):36 37 def restructuredtext(value): 36 38 try: 37 39 from docutils.core import publish_parts … … 41 43 parts = publish_parts(source=value, writer_name="html4css1") 42 44 return parts["fragment"] 43 44 template.register_filter("textile", textile, False)45 template.register_filter("markdown", markdown, False)46 template.register_filter("restructuredtext", restructuredtext, False)45 46 register.filter(textile) 47 register.filter(markdown) 48 register.filter(restructuredtext) django/trunk/django/core/template/defaultfilters.py
r1050 r1443 1 1 "Default variable filters" 2 2 3 from django.core.template import register_filter, resolve_variable 3 from django.core.template import resolve_variable, Library 4 from django.conf.settings import DATE_FORMAT, TIME_FORMAT 4 5 import re 5 6 import random as random_module 6 7 8 register = Library() 9 7 10 ################### 8 11 # STRINGS # 9 12 ################### 10 13 11 def addslashes(value, _): 14 15 def addslashes(value): 12 16 "Adds slashes - useful for passing strings to JavaScript, for example." 13 17 return value.replace('"', '\\"').replace("'", "\\'") 14 18 15 def capfirst(value , _):19 def capfirst(value): 16 20 "Capitalizes the first character of the value" 17 21 value = str(value) 18 22 return value and value[0].upper() + value[1:] 19 23 20 def fix_ampersands(value , _):24 def fix_ampersands(value): 21 25 "Replaces ampersands with ``&`` entities" 22 26 from django.utils.html import fix_ampersands 23 27 return fix_ampersands(value) 24 28 25 def floatformat(text , _):29 def floatformat(text): 26 30 """ 27 31 Displays a floating point number as 34.2 (with one decimal place) -- but … … 38 42 return '%d' % int(f) 39 43 40 def linenumbers(value , _):44 def linenumbers(value): 41 45 "Displays text with line numbers" 42 46 from django.utils.html import escape … … 48 52 return '\n'.join(lines) 49 53 50 def lower(value , _):54 def lower(value): 51 55 "Converts a string into all lowercase" 52 56 return value.lower() 53 57 54 def make_list(value , _):58 def make_list(value): 55 59 """ 56 60 Returns the value turned into a list. For an integer, it's a list of … … 59 63 return list(str(value)) 60 64 61 def slugify(value , _):65 def slugify(value): 62 66 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 63 67 value = re.sub('[^\w\s-]', '', value).strip().lower() … … 78 82 return "" 79 83 80 def title(value , _):84 def title(value): 81 85 "Converts a string into titlecase" 82 86 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) … … 97 101 return truncate_words(value, length) 98 102 99 def upper(value , _):103 def upper(value): 100 104 "Converts a string into all uppercase" 101 105 return value.upper() 102 106 103 def urlencode(value , _):107 def urlencode(value): 104 108 "Escapes a value for use in a URL" 105 109 import urllib 106 110 return urllib.quote(value) 107 111 108 def urlize(value , _):112 def urlize(value): 109 113 "Converts URLs in plain text into clickable links" 110 114 from django.utils.html import urlize … … 120 124 return urlize(value, trim_url_limit=int(limit), nofollow=True) 121 125 122 def wordcount(value , _):126 def wordcount(value): 123 127 "Returns the number of words" 124 128 return len(value.split()) … … 161 165 ################### 162 166 163 def escape(value , _):167 def escape(value): 164 168 "Escapes a string's HTML" 165 169 from django.utils.html import escape 166 170 return escape(value) 167 171 168 def linebreaks(value , _):172 def linebreaks(value): 169 173 "Converts newlines into <p> and <br />s" 170 174 from django.utils.html import linebreaks 171 175 return linebreaks(value) 172 176 173 def linebreaksbr(value , _):177 def linebreaksbr(value): 174 178 "Converts newlines into <br />s" 175 179 return value.replace('\n', '<br />') … … 185 189 return value 186 190 187 def striptags(value , _):191 def striptags(value): 188 192 "Strips all [X]HTML tags" 189 193 from django.utils.html import strip_tags … … 215 219 return [item[1] for item in decorated] 216 220 217 def first(value , _):221 def first(value): 218 222 "Returns the first item in a list" 219 223 try: … … 229 233 return value 230 234 231 def length(value , _):235 def length(value): 232 236 "Returns the length of the value - useful for lists" 233 237 return len(value) … … 237 241 return len(value) == int(arg) 238 242 239 def random(value , _):243 def random(value): 240 244 "Returns a random item from the list" 241 245 return random_module.choice(value) … … 254 258 return value # Fail silently. 255 259 256 def unordered_list(value , _):260 def unordered_list(value): 257 261 """ 258 262 Recursively takes a self-nested list and returns an HTML unordered list -- … … 315 319 ################### 316 320 317 def date(value, arg ):321 def date(value, arg=DATE_FORMAT): 318 322 "Formats a date according to the given format" 319 323 from django.utils.dateformat import format 320 324 return format(value, arg) 321 325 322 def time(value, arg ):326 def time(value, arg=TIME_FORMAT): 323 327 "Formats a time according to the given format" 324 328 from django.utils.dateformat import time_format 325 329 return time_format(value, arg) 326 330 327 def timesince(value , _):331 def timesince(value): 328 332 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' 329 333 from django.utils.timesince import timesince … … 348 352 return int(value) % int(arg) == 0 349 353 350 def yesno(value, arg ):354 def yesno(value, arg=_("yes,no,maybe")): 351 355 """ 352 356 Given a string mapping values for true, false and (optionally) None, … … 380 384 ################### 381 385 382 def filesizeformat(bytes , _):386 def filesizeformat(bytes): 383 387 """ 384 388 Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102 … … 394 398 return "%.1f GB" % (bytes / (1024 * 1024 * 1024)) 395 399 396 def pluralize(value , _):400 def pluralize(value): 397 401 "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'" 398 402 try: … … 409 413 return '' 410 414 411 def phone2numeric(value , _):415 def phone2numeric(value): 412 416 "Takes a phone number and converts it in to its numerical equivalent" 413 417 from django.utils.text import phone2numeric 414 418 return phone2numeric(value) 415 419 416 def pprint(value , _):420 def pprint(value): 417 421 "A wrapper around pprint.pprint -- for debugging, really" 418 422 from pprint import pformat 419 423 return pformat(value) 420 424 421 # Syntax: register _filter(name of filter, callback, has_argument)422 register _filter('add', add, True)423 register _filter('addslashes', addslashes, False)424 register _filter('capfirst', capfirst, False)425 register _filter('center', center, True)426 register _filter('cut', cut, True)427 register _filter('date', date, True)428 register _filter('default', default, True)429 register _filter('default_if_none', default_if_none, True)430 register _filter('dictsort', dictsort, True)431 register _filter('dictsortreversed', dictsortreversed, True)432 register _filter('divisibleby', divisibleby, True)433 register _filter('escape', escape, False)434 register _filter('filesizeformat', filesizeformat, False)435 register _filter('first', first, False)436 register _filter('fix_ampersands', fix_ampersands, False)437 register _filter('floatformat', floatformat, False)438 register _filter('get_digit', get_digit, True)439 register _filter('join', join, True)440 register _filter('length', length, False)441 register _filter('length_is', length_is, True)442 register _filter('linebreaks', linebreaks, False)443 register _filter('linebreaksbr', linebreaksbr, False)444 register _filter('linenumbers', linenumbers, False)445 register _filter('ljust', ljust, True)446 register _filter('lower', lower, False)447 register _filter('make_list', make_list, False)448 register _filter('phone2numeric', phone2numeric, False)449 register _filter('pluralize', pluralize, False)450 register _filter('pprint', pprint, False)451 register _filter('removetags', removetags, True)452 register _filter('random', random, False)453 register _filter('rjust', rjust, True)454 register _filter('slice', slice_, True)455 register _filter('slugify', slugify, False)456 register _filter('stringformat', stringformat, True)457 register _filter('striptags', striptags, False)458 register _filter('time', time, True)459 register _filter('timesince', timesince, False)460 register _filter('title', title, False)461 register _filter('truncatewords', truncatewords, True)462 register _filter('unordered_list', unordered_list, False)463 register _filter('upper', upper, False)464 register _filter('urlencode', urlencode, False)465 register _filter('urlize', urlize, False)466 register _filter('urlizetrunc', urlizetrunc, True)467 register _filter('wordcount', wordcount, False)468 register _filter('wordwrap', wordwrap, True)469 register _filter('yesno', yesno, True)425 # Syntax: register.filter(name of filter, callback) 426 register.filter(add) 427 register.filter(addslashes) 428 register.filter(capfirst) 429 register.filter(center) 430 register.filter(cut) 431 register.filter(date) 432 register.filter(default) 433 register.filter(default_if_none) 434 register.filter(dictsort) 435 register.filter(dictsortreversed) 436 register.filter(divisibleby) 437 register.filter(escape) 438 register.filter(filesizeformat) 439 register.filter(first) 440 register.filter(fix_ampersands) 441 register.filter(floatformat) 442 register.filter(get_digit) 443 register.filter(join) 444 register.filter(length) 445 register.filter(length_is) 446 register.filter(linebreaks) 447 register.filter(linebreaksbr) 448 register.filter(linenumbers) 449 register.filter(ljust) 450 register.filter(lower) 451 register.filter(make_list) 452 register.filter(phone2numeric) 453 register.filter(pluralize) 454 register.filter(pprint) 455 register.filter(removetags) 456 register.filter(random) 457 register.filter(rjust) 458 register.filter(slice_) 459 register.filter(slugify) 460 register.filter(stringformat) 461 register.filter(striptags) 462 register.filter(time) 463 register.filter(timesince) 464 register.filter(title) 465 register.filter(truncatewords) 466 register.filter(unordered_list) 467 register.filter(upper) 468 register.filter(urlencode) 469 register.filter(urlize) 470 register.filter(urlizetrunc) 471 register.filter(wordcount) 472 register.filter(wordwrap) 473 register.filter(yesno) django/trunk/django/core/template/defaulttags.py
r1379 r1443 1 1 "Default tags used by the template system, available to all templates." 2 2 3 from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, get_filters_from_token, registered_filters 4 from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, register_tag 3 from django.core.template import Node, NodeList, Template, Context, resolve_variable 4 from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END 5 from django.core.template import get_library, Library, InvalidTemplateLibrary 5 6 import sys 7 8 register = Library() 6 9 7 10 class CommentNode(Node): … … 28 31 29 32 class FilterNode(Node): 30 def __init__(self, filter s, nodelist):31 self.filter s, self.nodelist = filters, nodelist33 def __init__(self, filter_expr, nodelist): 34 self.filter_expr, self.nodelist = filter_expr, nodelist 32 35 33 36 def render(self, context): 34 37 output = self.nodelist.render(context) 35 38 # apply filters 36 for f in self.filters: 37 output = registered_filters[f[0]][0](output, f[1]) 38 return output 39 return self.filter_expr.resolve(Context({'var': output})) 39 40 40 41 class FirstOfNode(Node): … … 82 83 context.push() 83 84 try: 84 values = resolve_variable_with_filters(self.sequence,context)85 values = self.sequence.resolve(context) 85 86 except VariableDoesNotExist: 86 87 values = [] … … 148 149 149 150 class IfNode(Node): 150 def __init__(self, bool vars, nodelist_true, nodelist_false):151 self.bool vars = boolvars151 def __init__(self, bool_exprs, nodelist_true, nodelist_false): 152 self.bool_exprs = bool_exprs 152 153 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 153 154 … … 170 171 171 172 def render(self, context): 172 for ifnot, bool var in self.boolvars:173 for ifnot, bool_expr in self.bool_exprs: 173 174 try: 174 value = resolve_variable_with_filters(boolvar,context)175 value = bool_expr.resolve(context) 175 176 except VariableDoesNotExist: 176 177 value = None … … 180 181 181 182 class RegroupNode(Node): 182 def __init__(self, target _var, expression, var_name):183 self.target _var, self.expression = target_var, expression183 def __init__(self, target, expression, var_name): 184 self.target, self.expression = target, expression 184 185 self.var_name = var_name 185 186 186 187 def render(self, context): 187 obj_list = resolve_variable_with_filters(self.target_var,context)188 obj_list = self.target.resolve(context) 188 189 if obj_list == '': # target_var wasn't found in context; fail silently 189 190 context[self.var_name] = [] … … 191 192 output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 192 193 for obj in obj_list: 193 grouper = resolve_variable_with_filters('var.%s' % self.expression, \ 194 Context({'var': obj})) 194 grouper = self.expression.resolve(Context({'var': obj})) 195 195 # TODO: Is this a sensible way to determine equality? 196 196 if output and repr(output[-1]['grouper']) == repr(grouper): … … 237 237 238 238 class LoadNode(Node): 239 def __init__(self, taglib): 240 self.taglib = taglib 241 242 def load_taglib(taglib): 243 mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', ['']) 244 reload(mod) 245 return mod 246 load_taglib = staticmethod(load_taglib) 247 248 def render(self, context): 249 "Import the relevant module" 250 try: 251 self.__class__.load_taglib(self.taglib) 252 except ImportError: 253 pass # Fail silently for invalid loads. 239 def render(self, context): 254 240 return '' 255 241 … … 277 263 278 264 class WidthRatioNode(Node): 279 def __init__(self, val_ var, max_var, max_width):280 self.val_ var = val_var281 self.max_ var = max_var265 def __init__(self, val_expr, max_expr, max_width): 266 self.val_expr = val_expr 267 self.max_expr = max_expr 282 268 self.max_width = max_width 283 269 284 270 def render(self, context): 285 271 try: 286 value = resolve_variable_with_filters(self.val_var,context)287 maxvalue = resolve_variable_with_filters(self.max_var,context)272 value = self.val_expr.resolve(context) 273 maxvalue = self.max_expr.resolve(context) 288 274 except VariableDoesNotExist: 289 275 return '' … … 296 282 return str(int(round(ratio))) 297 283 298 def do_comment(parser, token): 284 #@register.tag 285 def comment(parser, token): 299 286 """ 300 287 Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` … … 303 290 parser.delete_first_token() 304 291 return CommentNode() 305 306 def do_cycle(parser, token): 292 comment = register.tag(comment) 293 294 #@register.tag 295 def cycle(parser, token): 307 296 """ 308 297 Cycle among the given strings each time this tag is encountered … … 370 359 else: 371 360 raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args) 372 373 def do_debug(parser, token): 374 "Print a whole load of debugging information, including the context and imported modules" 375 return DebugNode() 376 361 cycle = register.tag(cycle) 362 363 #@register.tag(name="filter") 377 364 def do_filter(parser, token): 378 365 """ … … 389 376 """ 390 377 _, rest = token.contents.split(None, 1) 391 _, filters = get_filters_from_token('var|%s' % rest)378 filter_expr = parser.compile_filter("var|%s" % (rest)) 392 379 nodelist = parser.parse(('endfilter',)) 393 380 parser.delete_first_token() 394 return FilterNode(filters, nodelist) 395 396 def do_firstof(parser, token): 381 return FilterNode(filter_expr, nodelist) 382 filter = register.tag("filter", do_filter) 383 384 #@register.tag 385 def firstof(parser, token): 397 386 """ 398 387 Outputs the first variable passed that is not False. … … 420 409 raise TemplateSyntaxError, "'firstof' statement requires at least one argument" 421 410 return FirstOfNode(bits) 422 423 411 firstof = register.tag(firstof) 412 413 #@register.tag(name="for") 424 414 def do_for(parser, token): 425 415 """ … … 463 453 raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents 464 454 loopvar = bits[1] 465 sequence = bits[3]455 sequence = parser.compile_filter(bits[3]) 466 456 reversed = (len(bits) == 5) 467 457 nodelist_loop = parser.parse(('endfor',)) 468 458 parser.delete_first_token() 469 459 return ForNode(loopvar, sequence, reversed, nodelist_loop) 460 do_for = register.tag("for", do_for) 470 461 471 462 def do_ifequal(parser, token, negate): … … 498 489 return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate) 499 490 491 #@register.tag 492 def ifequal(parser, token): 493 return do_ifequal(parser, token, False) 494 ifequal = register.tag(ifequal) 495 496 #@register.tag 497 def ifnotequal(parser, token): 498 return do_ifequal(parser, token, True) 499 ifnotequal = register.tag(ifnotequal) 500 501 #@register.tag(name="if") 500 502 def do_if(parser, token): 501 503 """ … … 555 557 if not_ != 'not': 556 558 raise TemplateSyntaxError, "Expected 'not' in if statement" 557 boolvars.append((True, boolvar))559 boolvars.append((True, parser.compile_filter(boolvar))) 558 560 else: 559 boolvars.append((False, boolpair))561 boolvars.append((False, parser.compile_filter(boolpair))) 560 562 nodelist_true = parser.parse(('else', 'endif')) 561 563 token = parser.next_token() … … 566 568 nodelist_false = NodeList() 567 569 return IfNode(boolvars, nodelist_true, nodelist_false) 568 569 def do_ifchanged(parser, token): 570 do_if = register.tag("if", do_if) 571 572 #@register.tag 573 def ifchanged(parser, token): 570 574 """ 571 575 Check if a value has changed from the last iteration of a loop. … … 588 592 parser.delete_first_token() 589 593 return IfChangedNode(nodelist) 590 591 def do_ssi(parser, token): 594 ifchanged = register.tag(ifchanged) 595 596 #@register.tag 597 def ssi(parser, token): 592 598 """ 593 599 Output the contents of a given file into the page. … … 614 620 raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0] 615 621 return SsiNode(bits[1], parsed) 616 617 def do_load(parser, token): 622 ssi = register.tag(ssi) 623 624 #@register.tag 625 def load(parser, token): 618 626 """ 619 627 Load a custom template tag set. … … 624 632 """ 625 633 bits = token.contents.split() 626 if len(bits) != 2: 627 raise TemplateSyntaxError, "'load' statement takes one argument" 628 taglib = bits[1] 629 # check at compile time that the module can be imported 630 try: 631 LoadNode.load_taglib(taglib) 632 except ImportError, e: 633 raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e) 634 return LoadNode(taglib) 635 636 def do_now(parser, token): 634 for taglib in bits[1:]: 635 # add the library to the parser 636 try: 637 lib = get_library("django.templatetags.%s" % taglib.split('.')[-1]) 638 parser.add_library(lib) 639 except InvalidTemplateLibrary, e: 640 raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e) 641 return LoadNode() 642 load = register.tag(load) 643 644 #@register.tag 645 def now(parser, token): 637 646 """ 638 647 Display the date, formatted according to the given string. … … 650 659 format_string = bits[1] 651 660 return NowNode(format_string) 652 653 def do_regroup(parser, token): 661 now = register.tag(now) 662 663 #@register.tag 664 def regroup(parser, token): 654 665 """ 655 666 Regroup a list of alike objects by a common attribute. … … 700 711 if len(firstbits) != 4: 701 712 raise TemplateSyntaxError, "'regroup' tag takes five arguments" 702 target _var = firstbits[1]713 target = parser.compile_filter(firstbits[1]) 703 714 if firstbits[2] != 'by': 704 715 raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'" … … 706 717 if lastbits_reversed[1][::-1] != 'as': 707 718 raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'" 708 expression = lastbits_reversed[2][::-1] 719 720 expression = parser.compile_filters('var.%s' % lastbits_reversed[2][::-1]) 721 709 722 var_name = lastbits_reversed[0][::-1] 710 return RegroupNode(target_var, expression, var_name) 711 712 def do_templatetag(parser, token): 723 return RegroupNode(target, expression, var_name) 724 regroup = register.tag(regroup) 725 726 #@register.tag 727 def templatetag(parser, token): 713 728 """ 714 729 Output one of the bits used to compose template tags. … … 736 751 (tag, TemplateTagNode.mapping.keys()) 737 752 return TemplateTagNode(tag) 738 739 def do_widthratio(parser, token): 753 templatetag = register.tag(templatetag) 754 755 @register.tag 756 def widthratio(parser, token): 740 757 """ 741 758 For creating bar charts and such, this tag calculates the ratio of a given … … 753 770 if len(bits) != 4: 754 771 raise TemplateSyntaxError("widthratio takes three arguments") 755 tag, this_value_ var, max_value_var, max_width = bits772 tag, this_value_expr, max_value_expr, max_width = bits 756 773 try: 757 774 max_width = int(max_width) 758 775 except ValueError: 759 776 raise TemplateSyntaxError("widthratio final argument must be an integer") 760 return WidthRatioNode(this_value_var, max_value_var, max_width) 761 762 register_tag('comment', do_comment) 763 register_tag('cycle', do_cycle) 764 register_tag('debug', do_debug) 765 register_tag('filter', do_filter) 766 register_tag('firstof', do_firstof) 767 register_tag('for', do_for) 768 register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False)) 769 register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True)) 770 register_tag('if', do_if) 771 register_tag('ifchanged', do_ifchanged) 772 register_tag('regroup', do_regroup) 773 register_tag('ssi', do_ssi) 774 register_tag('load', do_load) 775 register_tag('now', do_now) 776 register_tag('templatetag', do_templatetag) 777 register_tag('widthratio', do_widthratio) 777 return WidthRatioNode(parser.compile_filter(this_value_expr), 778 parser.compile_filter(max_value_expr), max_width) 779 widthratio = register.tag(widthratio) django/trunk/django/core/template/__init__.py
r1379 r1443 4 4 How it works: 5 5 6 The tokenize() function converts a template string (i.e., a string containing6 The Lexer.tokenize() function converts a template string (i.e., a string containing 7 7 markup with custom template tags) to tokens, which can be either plain text 8 8 (TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK). … … 56 56 """ 57 57 import re 58 from inspect import getargspec 59 from django.utils.functional import curry 58 60 from django.conf.settings import DEFAULT_CHARSET, TEMPLATE_DEBUG 59 61 … … 83 85 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) 84 86 85 # global dict used by register_tag; maps custom tags to callback functions 86 registered_tags = {} 87 88 # global dict used by register_filter; maps custom filters to callback functions 89 registered_filters = {} 87 # global dictionary of libraries that have been loaded using get_library 88 libraries = {} 89 # global list of libraries to load by default for a new parser 90 builtins = [] 90 91 91 92 class TemplateSyntaxError(Exception): … … 106 107 pass 107 108 109 class InvalidTemplateLibrary(Exception): 110 pass 111 108 112 class Origin(object): 109 113 def __init__(self, name): … … 111 115 112 116 def reload(self): 113 raise NotImplementedE xception117 raise NotImplementedError 114 118 115 119 def __str__(self): … … 265 269 def __init__(self, tokens): 266 270 self.tokens = tokens 271 self.tags = {} 272 self.filters = {} 273 for lib in builtins: 274 self.add_library(lib) 267 275 268 276 def parse(self, parse_until=[]): … … 275 283 if not token.contents: 276 284 self.empty_variable(token) 277 var_node = self.create_variable_node(token.contents) 285 filter_expression = self.compile_filter(token.contents) 286 var_node = self.create_variable_node(filter_expression) 278 287 self.extend_nodelist(nodelist, var_node,token) 279 288 elif token.token_type == TOKEN_BLOCK: … … 289 298 self.enter_command(command, token) 290 299 try: 291 compile_func = registered_tags[command]300 compile_func = self.tags[command] 292 301 except KeyError: 293 302 self.invalid_block_tag(token, command) … … 303 312 return nodelist 304 313 305 def create_variable_node(self, contents):306 return VariableNode( contents)314 def create_variable_node(self, filter_expression): 315 return VariableNode(filter_expression) 307 316 308 317 def create_nodelist(self): … … 344 353 def delete_first_token(self): 345 354 del self.tokens[0] 355 356 def add_library(self, lib): 357 self.tags.update(lib.tags) 358 self.filters.update(lib.filters) 359 360 def compile_filter(self,token): 361 "Convenient wrapper for FilterExpression" 362 return FilterExpression(token, self) 363 364 def find_filter(self, filter_name): 365 if self.filters.has_key(filter_name): 366 return self.filters[filter_name] 367 else: 368 raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name 346 369 347 370 class DebugParser(Parser): … … 484 507 (?: 485 508 %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s| 486 "(?P<arg>%(str)s)" 509 "(?P<constant_arg>%(str)s)"| 510 (?P<var_arg>[%(var_chars)s]+) 487 511 ) 488 512 )? … … 499 523 filter_re = re.compile(filter_raw_string) 500 524 501 class Filter Parser(object):525 class FilterExpression(object): 502 526 """ 503 527 Parses a variable token and its optional filters (all as a single string), … … 514 538 get_filters_from_token helper function. 515 539 """ 516 def __init__(self, token): 540 def __init__(self, token, parser): 541 self.token = token 517 542 matches = filter_re.finditer(token) 518 543 var = None … … 537 562 else: 538 563 filter_name = match.group("filter_name") 539 arg, i18n_arg = match.group("arg","i18n_arg") 564 args = [] 565 constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg") 540 566 if i18n_arg: 541 arg =_(i18n_arg.replace('\\', '')) 542 if arg: 543 arg = arg.replace('\\', '') 544 if not registered_filters.has_key(filter_name): 545 raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name 546 if registered_filters[filter_name][1] == True and arg is None: 547 raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name 548 if registered_filters[filter_name][1] == False and arg is not None: 549 raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg) 550 filters.append( (filter_name,arg) ) 567 args.append((False, _(i18n_arg.replace('\\', '')))) 568 elif constant_arg: 569 args.append((False, constant_arg.replace('\\', ''))) 570 elif var_arg: 571 args.append((True, var_arg)) 572 filter_func = parser.find_filter(filter_name) 573 self.args_check(filter_name,filter_func, args) 574 filters.append( (filter_func,args)) 551 575 upto = match.end() 552 576 if upto != len(token): … … 554 578 self.var , self.filters = var, filters 555 579 556 def get_filters_from_token(token): 557 "Convenient wrapper for FilterParser" 558 p = FilterParser(token) 559 return (p.var, p.filters) 580 def resolve(self, context): 581 try: 582 obj = resolve_variable(self.var, context) 583 except VariableDoesNotExist: 584 obj = '' 585 for func, args in self.filters: 586 arg_vals = [] 587 for lookup, arg in args: 588 if not lookup: 589 arg_vals.append(arg) 590 else: 591 arg_vals.append(resolve_variable(arg, context)) 592 obj = func(obj, *arg_vals) 593 return obj 594 595 def args_check(name, func, provided): 596 provided = list(provided) 597 plen = len(provided) 598 (args, varargs, varkw, defaults) = getargspec(func) 599 # First argument is filter input. 600 args.pop(0) 601 if defaults: 602 nondefs = args[:-len(defaults)] 603 else: 604 nondefs = args 605 # Args without defaults must be provided. 606 try: 607 for arg in nondefs: 608 provided.pop(0) 609 except IndexError: 610 # Not enough 611 raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen) 612 613 # Defaults can be overridden. 614 defaults = defaults and list(defaults) or [] 615 try: 616 for parg in provided: 617 defaults.pop(0) 618 except IndexError: 619 # Too many. 620 raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen) 621 622 return True 623 args_check = staticmethod(args_check) 624 625 def __str__(self): 626 return self.token 560 627 561 628 def resolve_variable(path, context): … … 608 675 return current 609 676 610 def resolve_variable_with_filters(var_string, context):611 """612 var_string is a full variable expression with optional filters, like:613 a.b.c|lower|date:"y/m/d"614 This function resolves the variable in the context, applies all filters and615 returns the object.616 """617 var, filters = get_filters_from_token(var_string)618 try:619 obj = resolve_variable(var, context)620 except VariableDoesNotExist:621 obj = ''622 for name, arg in filters:623 obj = registered_filters[name][0](obj, arg)624 return obj625 626 677 class Node: 627 678 def render(self, context): … … 688 739 689 740 class VariableNode(Node): 690 def __init__(self, var_string):691 self. var_string = var_string741 def __init__(self, filter_expression): 742 self.filter_expression = filter_expression 692 743 693 744 def __repr__(self): 694 return "<Variable Node: %s>" % self. var_string745 return "<Variable Node: %s>" % self.filter_expression 695 746 696 747 def encode_output(self, output): … … 704 755 705 756 def render(self, context): 706 output = resolve_variable_with_filters(self.var_string,context)757 output = self.filter_expression.resolve(context) 707 758 return self.encode_output(output) 708 759 … … 710 761 def render(self, context): 711 762 try: 712 output = resolve_variable_with_filters(self.var_string,context)763 output = self.filter_expression.resolve(context) 713 764 except TemplateSyntaxError, e: 714 765 if not hasattr(e, 'source'): … … 717 768 return self.encode_output(output) 718 769 719 def register_tag(token_command, callback_function): 720 registered_tags[token_command] = callback_function 721 722 def unregister_tag(token_command): 723 del registered_tags[token_command] 724 725 def register_filter(filter_name, callback_function, has_arg): 726 registered_filters[filter_name] = (callback_function, has_arg) 727 728 def unregister_filter(filter_name): 729 del registered_filters[filter_name] 730 731 import defaulttags 732 import defaultfilters 770 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 771 "Returns a template.Node subclass." 772 bits = token.contents.split()[1:] 773 bmax = len(params) 774 def_len = defaults and len(defaults) or 0 775 bmin = bmax - def_len 776 if(len(bits) < bmin or len(bits) > bmax): 777 if bmin == bmax: 778 message = "%s takes %s arguments" % (name, bmin) 779 else: 780 message = "%s takes between %s and %s arguments" % (name, bmin, bmax) 781 raise TemplateSyntaxError, message 782 return node_class(bits) 783 784 class Library(object): 785 def __init__(self): 786 self.filters = {} 787 self.tags = {} 788 789 def tag(self, name = None, compile_function = None): 790 if name == None and compile_function == None: 791 # @register.tag() 792 return self.tag_function 793 elif name != None and compile_function == None: 794 if(callable(name)): 795 # @register.tag 796 return self.tag_function(name) 797 else: 798 # @register.tag('somename') or @register.tag(name='somename') 799 def dec(func): 800 return self.tag(name, func) 801 return dec 802 elif name != None and compile_function != None: 803 # register.tag('somename', somefunc) 804 self.tags[name] = compile_function 805 return compile_function 806 else: 807 raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function) 808 809 def tag_function(self,func): 810 self.tags[func.__name__] = func 811 return func 812 813 def filter(self, name = None, filter_func = None): 814 if name == None and filter_func == None: 815 # @register.filter() 816 return self.filter_function 817 elif filter_func == None: 818 if(callable(name)): 819 # @register.filter 820 return self.filter_function(name) 821 else: 822 # @register.filter('somename') or @register.filter(name='somename') 823 def dec(func): 824 return self.filter(name, func) 825 return dec 826 elif name != None and filter_func != None: 827 # register.filter('somename', somefunc) 828 self.filters[name] = filter_func 829 else: 830 raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r, %r)", (name, compile_function, has_arg) 831 832 def filter_function(self, func): 833 self.filters[func.__name__] = func 834 return func 835 836 def simple_tag(self,func): 837 (params, xx, xxx, defaults) = getargspec(func) 838 839 class SimpleNode(Node): 840 def __init__(self, vars_to_resolve): 841 self.vars_to_resolve = vars_to_resolve 842 843 def render(self, context): 844 resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] 845 return func(*resolved_vars) 846 847 compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode) 848 compile_func.__doc__ = func.__doc__ 849 self.tag(func.__name__, compile_func) 850 return func 851 852 def inclusion_tag(self, file_name, context_class=Context, takes_context=False): 853 def dec(func): 854 (params, xx, xxx, defaults) = getargspec(func) 855 if takes_context: 856 if params[0] == 'context': 857 params = params[1:] 858 else: 859 raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'" 860 861 class InclusionNode(Node): 862 def __init__(self, vars_to_resolve): 863 self.vars_to_resolve = vars_to_resolve 864 865 def render(self, context): 866 resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] 867 if takes_context: 868 args = [context] + resolved_vars 869 else: 870 args = resolved_vars 871 872 dict = func(*args) 873 874 if not getattr(self, 'nodelist', False): 875 from django.core.template_loader import get_template 876 t = get_template(file_name) 877 self.nodelist = t.nodelist 878 return self.nodelist.render(context_class(dict)) 879 880 compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode) 881 compile_func.__doc__ = func.__doc__ 882 self.tag(func.__name__, compile_func) 883 return func 884 return dec 885 886 def get_library(module_name): 887 lib = libraries.get(module_name, None) 888 if not lib: 889 try: 890 mod = __import__(module_name, '', '', ['']) 891 except ImportError, e: 892 raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e) 893 for k, v in mod.__dict__.items(): 894 if isinstance(v, Library): 895 lib = v 896 libraries[module_name] = lib 897 break 898 if not lib: 899 raise InvalidTemplateLibrary, "Template library %s does not have a Library member" % module_name 900 return lib 901 902 def add_to_builtins(module_name): 903 builtins.append(get_library(module_name)) 904 905 add_to_builtins('django.core.template.defaulttags') 906 add_to_builtins('django.core.template.defaultfilters') django/trunk/django/core/template/loader.py
r1399 r1443 22 22 23 23 from django.core.exceptions import ImproperlyConfigured 24 from django.core.template import Origin, StringOrigin, Template, Context, Node, TemplateDoesNotExist, TemplateSyntaxError, resolve_variable_with_filters, resolve_variable, register_tag24 from django.core.template import Origin, StringOrigin, Template, TemplateDoesNotExist, add_to_builtins 25 25 from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG 26 26 … … 69 69 find_template_source(name, dirs)[0] 70 70 71 class ExtendsError(Exception):72 pass73 74 71 def get_template(template_name): 75 72 """ … … 114 111 raise TemplateDoesNotExist, ', '.join(template_name_list) 115 112 116 class BlockNode(Node): 117 def __init__(self, name, nodelist, parent=None): 118 self.name, self.nodelist, self.parent = name, nodelist, parent 119 120 def __repr__(self): 121 return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) 122 123 def render(self, context): 124 context.push() 125 # Save context in case of block.super(). 126 self.context = context 127 context['block'] = self 128 result = self.nodelist.render(context) 129 context.pop() 130 return result 131 132 def super(self): 133 if self.parent: 134 return self.parent.render(self.context) 135 return '' 136 137 def add_parent(self, nodelist): 138 if self.parent: 139 self.parent.add_parent(nodelist) 140 else: 141 self.parent = BlockNode(self.name, nodelist) 142 143 class ExtendsNode(Node): 144 def __init__(self, nodelist, parent_name, parent_name_var, template_dirs=None): 145 self.nodelist = nodelist 146 self.parent_name, self.parent_name_var = parent_name, parent_name_var 147 self.template_dirs = template_dirs 148 149 def get_parent(self, context): 150 if self.parent_name_var: 151 self.parent_name = resolve_variable_with_filters(self.parent_name_var, context) 152 parent = self.parent_name 153 if not parent: 154 error_msg = "Invalid template name in 'extends' tag: %r." % parent 155 if self.parent_name_var: 156 error_msg += " Got this from the %r variable." % self.parent_name_var 157 raise TemplateSyntaxError, error_msg 158 try: 159 return get_template_from_string(*find_template_source(parent, self.template_dirs)) 160 except TemplateDoesNotExist: 161 raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent 162 163 def render(self, context): 164 compiled_parent = self.get_parent(context) 165 parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode) 166 parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) 167 for block_node in self.nodelist.get_nodes_by_type(BlockNode): 168 # Check for a BlockNode with this node's name, and replace it if found. 169 try: 170 parent_block = parent_blocks[block_node.name] 171 except KeyError: 172 # This BlockNode wasn't found in the parent template, but the 173 # parent block might be defined in the parent's *parent*, so we 174 # add this BlockNode to the parent's ExtendsNode nodelist, so 175 # it'll be checked when the parent node's render() is called. 176 if parent_is_child: 177 compiled_parent.nodelist[0].nodelist.append(block_node) 178 else: 179 # Keep any existing parents and add a new one. Used by BlockNode. 180 parent_block.parent = block_node.parent 181 parent_block.add_parent(parent_block.nodelist) 182 parent_block.nodelist = block_node.nodelist 183 return compiled_parent.render(context) 184 185 class ConstantIncludeNode(Node): 186 def __init__(self, template_path): 187 try: 188 t = get_template(template_path) 189 self.template = t 190 except Exception, e: 191 if TEMPLATE_DEBUG: 192 raise 193 self.template = None 194 195 def render(self, context): 196 if self.template: 197 return self.template.render(context) 198 else: 199 return '' 200 201 class IncludeNode(Node): 202 def __init__(self, template_name): 203 self.template_name = template_name 204 205 def render(self, context): 206 try: 207 template_name = resolve_variable(self.template_name, context) 208 t = get_template(template_name) 209 return t.render(context) 210 except TemplateSyntaxError, e: 211 if TEMPLATE_DEBUG: 212 raise 213 return '' 214 except: 215 return '' # Fail silently for invalid included templates. 216 217 def do_block(parser, token): 218 """ 219 Define a block that can be overridden by child templates. 220 """ 221 bits = token.contents.split() 222 if len(bits) != 2: 223 raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0] 224 block_name = bits[1] 225 # Keep track of the names of BlockNodes found in this template, so we can 226 # check for duplication. 227 try: 228 if block_name in parser.__loaded_blocks: 229 raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name) 230 parser.__loaded_blocks.append(block_name) 231 except AttributeError: # parser._loaded_blocks isn't a list yet 232 parser.__loaded_blocks = [block_name] 233 nodelist = parser.parse(('endblock',)) 234 parser.delete_first_token() 235 return BlockNode(block_name, nodelist) 236 237 def do_extends(parser, token): 238 """ 239 Signal that this template extends a parent template. 240 241 This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) 242 uses the literal value "base" as the name of the parent template to extend, 243 or ``{% extends variable %}`` uses the value of ``variable`` as the name 244 of the parent template to extend. 245 """ 246 bits = token.contents.split() 247 if len(bits) != 2: 248 raise TemplateSyntaxError, "'%s' takes one argument" % bits[0] 249 parent_name, parent_name_var = None, None 250 if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]: 251 parent_name = bits[1][1:-1] 252 else: 253 parent_name_var = bits[1] 254 nodelist = parser.parse() 255 if nodelist.get_nodes_by_type(ExtendsNode): 256 raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0] 257 return ExtendsNode(nodelist, parent_name, parent_name_var) 258 259 def do_include(parser, token): 260 """ 261 Loads a template and renders it with the current context. 262 263 Example:: 264 265 {% include "foo/some_include" %} 266 """ 267 268 bits = token.contents.split() 269 if len(bits) != 2: 270 raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0] 271 path = bits[1] 272 if path[0] in ('"', "'") and path[-1] == path[0]: 273 return ConstantIncludeNode(path[1:-1]) 274 return IncludeNode(bits[1]) 275 276 register_tag('block', do_block) 277 register_tag('extends', do_extends) 278 register_tag('include', do_include) 113 add_to_builtins('django.core.template.loader_tags') django/trunk/django/templatetags/i18n.py
r1412 r1443 1 from django.core.template import Node, NodeList, Template, Context, resolve_variable , resolve_variable_with_filters, registered_filters2 from django.core.template import TemplateSyntaxError, register_tag, TokenParser1 from django.core.template import Node, NodeList, Template, Context, resolve_variable 2 from django.core.template import TemplateSyntaxError, TokenParser, Library 3 3 from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR 4 4 from django.utils import translation 5 5 import re, sys 6 7 register = Library() 6 8 7 9 class GetAvailableLanguagesNode(Node): … … 54 56 context.push() 55 57 for var,val in self.extra_context.items(): 56 context[var] = resolve_variable_with_filters(val,context)58 context[var] = val.resolve(context) 57 59 singular = self.render_token_list(self.singular) 58 60 if self.plural and self.countervar and self.counter: 59 count = resolve_variable_with_filters(self.counter,context)61 count = self.counter.resolve(context) 60 62 context[self.countervar] = count 61 63 plural = self.render_token_list(self.plural) … … 180 182 if self.tag() != 'as': 181 183 raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'" 182 extra_context[self.tag()] = value184 extra_context[self.tag()] = parser.compile_filter(value) 183 185 elif tag == 'count': 184 counter = self.value()186 counter = parser.compile_filter(self.value()) 185 187 if self.tag() != 'as': 186 188 raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'" … … 214 216 return BlockTranslateNode(extra_context, singular, plural, countervar, counter) 215 217 216 register _tag('get_available_languages', do_get_available_languages)217 register _tag('get_current_language', do_get_current_language)218 register _tag('trans', do_translate)219 register _tag('blocktrans', do_block_translate)218 register.tag('get_available_languages', do_get_available_languages) 219 register.tag('get_current_language', do_get_current_language) 220 register.tag('trans', do_translate) 221 register.tag('blocktrans', do_block_translate) django/trunk/docs/templates_python.txt
r1261 r1443 445 445 Python code, depending on whether you're writing filters or tags. 446 446 447 To be a valid tag library, the module contain a module-level variable that is a 448 ``template.Library`` instance, in which all the tags and filters are 449 registered. So, near the top of your module, put the following:: 450 451 from django.core import template 452 register = template.Library() 453 454 Convention is to call this instance ``register``. 455 447 456 .. admonition:: Behind the scenes 448 457 … … 454 463 ------------------------------- 455 464 456 Custom filters are just Python functions that take two arguments: 457 458 * The value of the variable (input) -- not necessarily a string 459 * The value of the argument -- always a string 465 **This section applies to the Django development version.** 466 467 Custom filters are just Python functions that take one or two arguments: 468 469 * The value of the variable (input) -- not necessarily a string. 470 * The value of the argument -- this can have a default value, or be left 471 out altogether. 472 473 For example, in the filter ``{{ var|foo:"bar" }}``, the filter ``foo`` would be 474 passed the variable ``var`` and the argument ``"bar"``. 460 475 461 476 Filter functions should always return something. They shouldn't raise … … 469 484 return value.replace(arg, '') 470 485 471 Most filters don't take arguments. For filters that don't take arguments, the 472 convention is to use a single underscore as the second argument to the filter 473 definition. Example:: 474 475 def lower(value, _): 486 And here's an example of how that filter would be used:: 487 488 {{ somevariable|cut:"0" }} 489 490 Most filters don't take arguments. In this case, just leave the argument out of 491 your function. Example:: 492 493 def lower(value): # Only one argument. 476 494 "Converts a string into all lowercase" 477 495 return value.lower() 478 496 479 When you've written your filter definition, you need to register it, to make it 480 available to Django's template language:: 481 482 from django.core import template 483 template.register_filter('cut', cut, True) 484 template.register_filter('lower', lower, False) 485 486 ``register_filter`` takes three arguments: 497 When you've written your filter definition, you need to register it with 498 your ``Library`` instance, to make it available to Django's template language:: 499 500 register.filter('cut', cut) 501 register.filter('lower', lower) 502 503 The ``Library.filter()`` method takes two arguments: 487 504 488 505 1. The name of the filter -- a string. 489 506 2. The compilation function -- a Python function (not the name of the 490 507 function as a string). 491 3. A boolean, designating whether the filter requires an argument. This 492 tells Django's template parser whether to throw ``TemplateSyntaxError`` 493 when filter arguments are given (or missing). 494 495 The convention is to put all ``register_filter`` calls at the bottom of your 496 template-library module. 508 509 If you're using Python 2.4 or above, you can use ``register.filter()`` as a 510 decorator instead:: 511 512 @register.filter(name='cut') 513 def cut(value, arg): 514 return value.replace(arg, '') 515 516 @register.filter 517 def lower(value): 518 return value.lower() 519 520 If you leave off the ``name`` argument, as in the second example above, Django 521 will use the function's name as the filter name. 497 522 498 523 Writing custom template tags 499 524 ---------------------------- 525 526 **This section applies to the Django development version.** 500 527 501 528 Tags are more complex than filters, because tags can do anything. … … 525 552 function with the tag contents and the parser object itself. This function is 526 553 responsible for returning a ``Node`` instance based on the contents of the tag. 527 528 By convention, the name of each compilation function should start with ``do_``.529 554 530 555 For example, let's write a template tag, ``{% current_time %}``, that displays … … 613 638 ~~~~~~~~~~~~~~~~~~~ 614 639 615 Finally, use a ``register_tag`` call, as in ``register_filter`` above. Example:: 616 617 from django.core import template 618 template.register_tag('current_time', do_current_time) 619 620 ``register_tag`` takes two arguments: 621 622 1. The name of the template tag -- a string. 640 Finally, register the tag with your module's ``Library`` instance, as explained 641 in "Writing custom template filters" above. Example:: 642 643 register.tag('current_time', do_current_time) 644 645 The ``tag()`` method takes two arguments: 646 647 1. The name of the template tag -- a string. If this is left out, the 648 name of the compilation function will be used. 623 649 2. The compilation function -- a Python function (not the name of the 624 650 function as a string). 651 652 As with filter registration, it is also possible to use this as a decorator, in 653 Python 2.4 and above: 654 655 @register.tag(name="current_time") 656 def do_current_time(parser, token): 657 # ... 658 659 @register.tag 660 def shout(parser, token): 661 # ... 662 663 If you leave off the ``name`` argument, as in the second example above, Django 664 will use the function's name as the tag name. 625 665 626 666 Setting a variable in the context django/trunk/docs/templates.txt
r1349 r1443 278 278 makes the ``comment_form`` tag available for use. Consult the documentation 279 279 area in your admin to find the list of custom libraries in your installation. 280 281 **New in Django development version:** The ``{% load %}`` tag can take multiple 282 library names, separated by spaces. Example:: 283 284 {% load comments i18n %} 280 285 281 286 Built-in tag and filter reference django/trunk/tests/othertests/defaultfilters.py
r1007 r1443 1 1 """ 2 >>> floatformat(7.7 , None)2 >>> floatformat(7.7) 3 3 '7.7' 4 >>> floatformat(7.0 , None)4 >>> floatformat(7.0) 5 5 '7' 6 >>> floatformat(0.7 , None)6 >>> floatformat(0.7) 7 7 '0.7' 8 >>> floatformat(0.07 , None)8 >>> floatformat(0.07) 9 9 '0.1' 10 >>> floatformat(0.007 , None)10 >>> floatformat(0.007) 11 11 '0.0' 12 >>> floatformat(0.0 , None)12 >>> floatformat(0.0) 13 13 '0' 14 14 """ django/trunk/tests/othertests/markup.py
r483 r1443 1 1 # Quick tests for the markup templatetags (django.contrib.markup) 2 2 3 from django.core.template import Template, Context 4 import django.contrib.markup.templatetags.markup # this registers the filters 3 from django.core.template import Template, Context, add_to_builtins 4 5 add_to_builtins('django.contrib.markup.templatetags.markup') 5 6 6 7 # find out if markup modules are installed and tailor the test appropriately … … 9 10 except ImportError: 10 11 textile = None 11 12 12 13 try: 13 14 import markdown 14 15 except ImportError: 15 16 markdown = None 16 17 17 18 try: 18 19 import docutils … … 37 38 else: 38 39 assert rendered == textile_content 39 40 40 41 ### test markdown 41 42 django/trunk/tests/othertests/templates.py
r1416 r1443 1 import traceback 1 from django.conf import settings 2 3 # Turn TEMPLATE_DEBUG off, because tests assume that. 4 settings.TEMPLATE_DEBUG = False 2 5 3 6 from django.core import template 4 7 from django.core.template import loader 5 8 from django.utils.translation import activate, deactivate, install 9 import traceback 6 10 7 11 # Helper objects for template tests … … 100 104 'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"), 101 105 102 #Escaped string as argument 103 'basic-syntax30': (r"""{{ var|default_if_none:" endquote\" hah" }}""", {"var": None}, ' endquote" hah'), 106 # Escaped string as argument 107 'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), 108 109 # Variable as argument 110 'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), 111 112 # Default argument testing 113 'basic-syntax32' : (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), 104 114 105 115 ### IF TAG ################################################################ … … 290 300 "A custom template loader that loads the unit-test templates." 291 301 try: 292 return ( TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)302 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 293 303 except KeyError: 294 304 raise template.TemplateDoesNotExist, template_name django/trunk/tests/testapp/templatetags/testtags.py
r367 r1443 2 2 3 3 from django.core import template 4 5 register = template.Library() 4 6 5 7 class EchoNode(template.Node): … … 12 14 def do_echo(parser, token): 13 15 return EchoNode(token.contents.split()[1:]) 14 15 template.register_tag("echo", do_echo)16 17 register.tag("echo", do_echo)
