Fixed #911 -- Made template system scoped to the parser instead of the template module. Also changed the way tags/filters are registered and added support for multiple arguments to {% load %} tag. Thanks, rjwittams. This is a backwards-incompatible change for people who've created custom template tags or filters. See http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges for upgrade instructions.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1443 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5676d5b068
commit
3ede006fc9
|
@ -1,7 +1,5 @@
|
||||||
{% extends "admin/base_site" %}
|
{% extends "admin/base_site" %}
|
||||||
{% load i18n %}
|
{% load i18n admin_modify adminmedia %}
|
||||||
{% load admin_modify %}
|
|
||||||
{% load adminmedia %}
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
|
{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{% load admin_list %}
|
{% load adminmedia admin_list i18n %}
|
||||||
{% load i18n %}
|
|
||||||
{% extends "admin/base_site" %}
|
{% extends "admin/base_site" %}
|
||||||
{% block bodyclass %}change-list{% endblock %}
|
{% block bodyclass %}change-list{% endblock %}
|
||||||
{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
|
{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load admin_modify %}
|
||||||
<fieldset class="module aligned">
|
<fieldset class="module aligned">
|
||||||
{% for fcw in bound_related_object.form_field_collection_wrappers %}
|
{% for fcw in bound_related_object.form_field_collection_wrappers %}
|
||||||
<h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }} #{{ forloop.counter }}</h2>
|
<h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }} #{{ forloop.counter }}</h2>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load admin_modify %}
|
||||||
<fieldset class="module">
|
<fieldset class="module">
|
||||||
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
|
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load admin_modify %}
|
||||||
<div class="{{ class_names }}" >
|
<div class="{{ class_names }}" >
|
||||||
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
|
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
|
||||||
{% for bound_field in bound_fields %}
|
{% for bound_field in bound_fields %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load i18n %}
|
||||||
<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
|
<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{% for choice in choices %}
|
{% for choice in choices %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load admin_list %}
|
||||||
{% if cl.has_filters %}<div id="changelist-filter">
|
{% if cl.has_filters %}<div id="changelist-filter">
|
||||||
<h2>Filter</h2>
|
<h2>Filter</h2>
|
||||||
{% for spec in cl.filter_specs %}
|
{% for spec in cl.filter_specs %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load admin_list %}
|
||||||
<p class="paginator">
|
<p class="paginator">
|
||||||
{% if pagination_required %}
|
{% if pagination_required %}
|
||||||
{% for i in page_range %}
|
{% for i in page_range %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load adminmedia %}
|
||||||
{% if cl.lookup_opts.admin.search_fields %}
|
{% if cl.lookup_opts.admin.search_fields %}
|
||||||
<div id="toolbar"><form id="changelist-search" action="" method="get">
|
<div id="toolbar"><form id="changelist-search" action="" method="get">
|
||||||
<label><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" /></label>
|
<label><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" /></label>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{% output_all bound_field.form_fields %}
|
{% load admin_modify %}{% output_all bound_field.form_fields %}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{% if bound_field.original_value %}
|
{% load admin_modify %}{% if bound_field.original_value %}
|
||||||
Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
|
Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
|
||||||
Change: {% output_all bound_field.form_fields %}
|
Change: {% output_all bound_field.form_fields %}
|
||||||
{% else %} {% output_all bound_field.form_fields %} {% endif %}
|
{% else %} {% output_all bound_field.form_fields %} {% endif %}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
{% load admin_modify adminmedia %}
|
||||||
{% output_all bound_field.form_fields %}
|
{% output_all bound_field.form_fields %}
|
||||||
{% if bound_field.raw_id_admin %}
|
{% if bound_field.raw_id_admin %}
|
||||||
<a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
|
<a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if bound_field.needs_add_label %}
|
{% if bound_field.needs_add_label %}
|
||||||
<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>
|
<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>
|
||||||
{% endif %} {% endif %}
|
{% endif %}{% endif %}
|
||||||
|
|
|
@ -3,16 +3,18 @@ from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR,
|
||||||
from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
|
from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
|
||||||
from django.core import meta, template
|
from django.core import meta, template
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.template.decorators import simple_tag, inclusion_tag
|
|
||||||
from django.utils import dateformat
|
from django.utils import dateformat
|
||||||
from django.utils.html import strip_tags, escape
|
from django.utils.html import strip_tags, escape
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import get_date_formats
|
from django.utils.translation import get_date_formats
|
||||||
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
||||||
|
from django.core.template import Library
|
||||||
|
|
||||||
|
register = Library()
|
||||||
|
|
||||||
DOT = '.'
|
DOT = '.'
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def paginator_number(cl,i):
|
def paginator_number(cl,i):
|
||||||
if i == DOT:
|
if i == DOT:
|
||||||
return '... '
|
return '... '
|
||||||
|
@ -20,9 +22,9 @@ def paginator_number(cl,i):
|
||||||
return '<span class="this-page">%d</span> ' % (i+1)
|
return '<span class="this-page">%d</span> ' % (i+1)
|
||||||
else:
|
else:
|
||||||
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)
|
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)
|
||||||
paginator_number = simple_tag(paginator_number)
|
paginator_number = register.simple_tag(paginator_number)
|
||||||
|
|
||||||
#@inclusion_tag('admin/pagination')
|
#@register.inclusion_tag('admin/pagination')
|
||||||
def pagination(cl):
|
def pagination(cl):
|
||||||
paginator, page_num = cl.paginator, cl.page_num
|
paginator, page_num = cl.paginator, cl.page_num
|
||||||
|
|
||||||
|
@ -64,7 +66,7 @@ def pagination(cl):
|
||||||
'ALL_VAR': ALL_VAR,
|
'ALL_VAR': ALL_VAR,
|
||||||
'1': 1,
|
'1': 1,
|
||||||
}
|
}
|
||||||
pagination = inclusion_tag('admin/pagination')(pagination)
|
pagination = register.inclusion_tag('admin/pagination')(pagination)
|
||||||
|
|
||||||
def result_headers(cl):
|
def result_headers(cl):
|
||||||
lookup_opts = cl.lookup_opts
|
lookup_opts = cl.lookup_opts
|
||||||
|
@ -177,15 +179,15 @@ def results(cl):
|
||||||
for res in cl.result_list:
|
for res in cl.result_list:
|
||||||
yield list(items_for_result(cl,res))
|
yield list(items_for_result(cl,res))
|
||||||
|
|
||||||
#@inclusion_tag("admin/change_list_results")
|
#@register.inclusion_tag("admin/change_list_results")
|
||||||
def result_list(cl):
|
def result_list(cl):
|
||||||
res = list(results(cl))
|
res = list(results(cl))
|
||||||
return {'cl': cl,
|
return {'cl': cl,
|
||||||
'result_headers': list(result_headers(cl)),
|
'result_headers': list(result_headers(cl)),
|
||||||
'results': list(results(cl))}
|
'results': list(results(cl))}
|
||||||
result_list = inclusion_tag("admin/change_list_results")(result_list)
|
result_list = register.inclusion_tag("admin/change_list_results")(result_list)
|
||||||
|
|
||||||
#@inclusion_tag("admin/date_hierarchy")
|
#@register.inclusion_tag("admin/date_hierarchy")
|
||||||
def date_hierarchy(cl):
|
def date_hierarchy(cl):
|
||||||
lookup_opts, params, lookup_params, lookup_mod = \
|
lookup_opts, params, lookup_params, lookup_mod = \
|
||||||
cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
|
cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
|
||||||
|
@ -256,23 +258,23 @@ def date_hierarchy(cl):
|
||||||
'title': year.year
|
'title': year.year
|
||||||
} for year in years ]
|
} for year in years ]
|
||||||
}
|
}
|
||||||
date_hierarchy = inclusion_tag('admin/date_hierarchy')(date_hierarchy)
|
date_hierarchy = register.inclusion_tag('admin/date_hierarchy')(date_hierarchy)
|
||||||
|
|
||||||
#@inclusion_tag('admin/search_form')
|
#@register.inclusion_tag('admin/search_form')
|
||||||
def search_form(cl):
|
def search_form(cl):
|
||||||
return {
|
return {
|
||||||
'cl': cl,
|
'cl': cl,
|
||||||
'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
|
'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
|
||||||
'search_var': SEARCH_VAR
|
'search_var': SEARCH_VAR
|
||||||
}
|
}
|
||||||
search_form = inclusion_tag('admin/search_form')(search_form)
|
search_form = register.inclusion_tag('admin/search_form')(search_form)
|
||||||
|
|
||||||
#@inclusion_tag('admin/filter')
|
#@register.inclusion_tag('admin/filter')
|
||||||
def filter(cl, spec):
|
def filter(cl, spec):
|
||||||
return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
|
return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
|
||||||
filter = inclusion_tag('admin/filter')(filter)
|
filter = register.inclusion_tag('admin/filter')(filter)
|
||||||
|
|
||||||
#@inclusion_tag('admin/filters')
|
#@register.inclusion_tag('admin/filters')
|
||||||
def filters(cl):
|
def filters(cl):
|
||||||
return {'cl': cl}
|
return {'cl': cl}
|
||||||
filters = inclusion_tag('admin/filters')(filters)
|
filters = register.inclusion_tag('admin/filters')(filters)
|
||||||
|
|
|
@ -2,24 +2,25 @@ from django.core import template, template_loader, meta
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.core.template.decorators import simple_tag, inclusion_tag
|
|
||||||
from django.contrib.admin.views.main import AdminBoundField
|
from django.contrib.admin.views.main import AdminBoundField
|
||||||
from django.core.meta.fields import BoundField, Field
|
from django.core.meta.fields import BoundField, Field
|
||||||
from django.core.meta import BoundRelatedObject, TABULAR, STACKED
|
from django.core.meta import BoundRelatedObject, TABULAR, STACKED
|
||||||
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
word_re = re.compile('[A-Z][a-z]+')
|
word_re = re.compile('[A-Z][a-z]+')
|
||||||
|
|
||||||
def class_name_to_underscored(name):
|
def class_name_to_underscored(name):
|
||||||
return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
|
return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def include_admin_script(script_path):
|
def include_admin_script(script_path):
|
||||||
return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
|
return '<script type="text/javascript" src="%s%s"></script>' % (ADMIN_MEDIA_PREFIX, script_path)
|
||||||
include_admin_script = simple_tag(include_admin_script)
|
include_admin_script = register.simple_tag(include_admin_script)
|
||||||
|
|
||||||
#@inclusion_tag('admin/submit_line', takes_context=True)
|
#@register.inclusion_tag('admin/submit_line', takes_context=True)
|
||||||
def submit_row(context, bound_manipulator):
|
def submit_row(context, bound_manipulator):
|
||||||
change = context['change']
|
change = context['change']
|
||||||
add = context['add']
|
add = context['add']
|
||||||
|
@ -36,9 +37,9 @@ def submit_row(context, bound_manipulator):
|
||||||
'show_save_and_continue': not is_popup,
|
'show_save_and_continue': not is_popup,
|
||||||
'show_save': True
|
'show_save': True
|
||||||
}
|
}
|
||||||
submit_row = inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
|
submit_row = register.inclusion_tag('admin/submit_line', takes_context=True)(submit_row)
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def field_label(bound_field):
|
def field_label(bound_field):
|
||||||
class_names = []
|
class_names = []
|
||||||
if isinstance(bound_field.field, meta.BooleanField):
|
if isinstance(bound_field.field, meta.BooleanField):
|
||||||
|
@ -53,7 +54,7 @@ def field_label(bound_field):
|
||||||
class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
|
class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
|
||||||
return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \
|
return '<label for="%s"%s>%s%s</label> ' % (bound_field.element_id, class_str, \
|
||||||
capfirst(bound_field.field.verbose_name), colon)
|
capfirst(bound_field.field.verbose_name), colon)
|
||||||
field_label = simple_tag(field_label)
|
field_label = register.simple_tag(field_label)
|
||||||
|
|
||||||
class FieldWidgetNode(template.Node):
|
class FieldWidgetNode(template.Node):
|
||||||
nodelists = {}
|
nodelists = {}
|
||||||
|
@ -170,12 +171,12 @@ class EditInlineNode(template.Node):
|
||||||
context.pop()
|
context.pop()
|
||||||
return output
|
return output
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def output_all(form_fields):
|
def output_all(form_fields):
|
||||||
return ''.join([str(f) for f in form_fields])
|
return ''.join([str(f) for f in form_fields])
|
||||||
output_all = simple_tag(output_all)
|
output_all = register.simple_tag(output_all)
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def auto_populated_field_script(auto_pop_fields, change = False):
|
def auto_populated_field_script(auto_pop_fields, change = False):
|
||||||
for field in auto_pop_fields:
|
for field in auto_pop_fields:
|
||||||
t = []
|
t = []
|
||||||
|
@ -191,9 +192,9 @@ def auto_populated_field_script(auto_pop_fields, change = False):
|
||||||
' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
|
' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
|
||||||
f, field.name, add_values, field.maxlength))
|
f, field.name, add_values, field.maxlength))
|
||||||
return ''.join(t)
|
return ''.join(t)
|
||||||
auto_populated_field_script = simple_tag(auto_populated_field_script)
|
auto_populated_field_script = register.simple_tag(auto_populated_field_script)
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def filter_interface_script_maybe(bound_field):
|
def filter_interface_script_maybe(bound_field):
|
||||||
f = bound_field.field
|
f = bound_field.field
|
||||||
if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
|
if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
|
||||||
|
@ -202,7 +203,7 @@ def filter_interface_script_maybe(bound_field):
|
||||||
f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
|
f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)
|
filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
|
||||||
|
|
||||||
def do_one_arg_tag(node_factory, parser,token):
|
def do_one_arg_tag(node_factory, parser,token):
|
||||||
tokens = token.contents.split()
|
tokens = token.contents.split()
|
||||||
|
@ -213,7 +214,7 @@ def do_one_arg_tag(node_factory, parser,token):
|
||||||
def register_one_arg_tag(node):
|
def register_one_arg_tag(node):
|
||||||
tag_name = class_name_to_underscored(node.__name__)
|
tag_name = class_name_to_underscored(node.__name__)
|
||||||
parse_func = curry(do_one_arg_tag, node)
|
parse_func = curry(do_one_arg_tag, node)
|
||||||
template.register_tag(tag_name, parse_func)
|
register.tag(tag_name, parse_func)
|
||||||
|
|
||||||
one_arg_tag_nodes = (
|
one_arg_tag_nodes = (
|
||||||
FieldWidgetNode,
|
FieldWidgetNode,
|
||||||
|
@ -223,7 +224,7 @@ one_arg_tag_nodes = (
|
||||||
for node in one_arg_tag_nodes:
|
for node in one_arg_tag_nodes:
|
||||||
register_one_arg_tag(node)
|
register_one_arg_tag(node)
|
||||||
|
|
||||||
#@inclusion_tag('admin/field_line', takes_context=True)
|
#@register.inclusion_tag('admin/field_line', takes_context=True)
|
||||||
def admin_field_line(context, argument_val):
|
def admin_field_line(context, argument_val):
|
||||||
if (isinstance(argument_val, BoundField)):
|
if (isinstance(argument_val, BoundField)):
|
||||||
bound_fields = [argument_val]
|
bound_fields = [argument_val]
|
||||||
|
@ -249,10 +250,10 @@ def admin_field_line(context, argument_val):
|
||||||
'bound_fields': bound_fields,
|
'bound_fields': bound_fields,
|
||||||
'class_names': " ".join(class_names),
|
'class_names': " ".join(class_names),
|
||||||
}
|
}
|
||||||
admin_field_line = inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
|
admin_field_line = register.inclusion_tag('admin/field_line', takes_context=True)(admin_field_line)
|
||||||
|
|
||||||
#@simple_tag
|
#@register.simple_tag
|
||||||
def object_pk(bound_manip, ordered_obj):
|
def object_pk(bound_manip, ordered_obj):
|
||||||
return bound_manip.get_ordered_object_pk(ordered_obj)
|
return bound_manip.get_ordered_object_pk(ordered_obj)
|
||||||
|
|
||||||
object_pk = simple_tag(object_pk)
|
object_pk = register.simple_tag(object_pk)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.core import template
|
from django.core import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
class AdminApplistNode(template.Node):
|
class AdminApplistNode(template.Node):
|
||||||
def __init__(self, varname):
|
def __init__(self, varname):
|
||||||
self.varname = varname
|
self.varname = varname
|
||||||
|
@ -54,4 +56,4 @@ def get_admin_app_list(parser, token):
|
||||||
raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
|
raise template.TemplateSyntaxError, "First argument to '%s' tag must be 'as'" % tokens[0]
|
||||||
return AdminApplistNode(tokens[2])
|
return AdminApplistNode(tokens[2])
|
||||||
|
|
||||||
template.register_tag('get_admin_app_list', get_admin_app_list)
|
register.tag('get_admin_app_list', get_admin_app_list)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.core.template.decorators import simple_tag
|
from django.core.template import Library
|
||||||
|
register = Library()
|
||||||
|
|
||||||
def admin_media_prefix():
|
def admin_media_prefix():
|
||||||
try:
|
try:
|
||||||
|
@ -6,4 +7,4 @@ def admin_media_prefix():
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return ''
|
return ''
|
||||||
return ADMIN_MEDIA_PREFIX
|
return ADMIN_MEDIA_PREFIX
|
||||||
admin_media_prefix = simple_tag(admin_media_prefix)
|
admin_media_prefix = register.simple_tag(admin_media_prefix)
|
|
@ -1,6 +1,8 @@
|
||||||
from django.models.admin import log
|
from django.models.admin import log
|
||||||
from django.core import template
|
from django.core import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
class AdminLogNode(template.Node):
|
class AdminLogNode(template.Node):
|
||||||
def __init__(self, limit, varname, user):
|
def __init__(self, limit, varname, user):
|
||||||
self.limit, self.varname, self.user = limit, varname, user
|
self.limit, self.varname, self.user = limit, varname, user
|
||||||
|
@ -48,4 +50,4 @@ class DoGetAdminLog:
|
||||||
raise template.TemplateSyntaxError, "Fourth argument in '%s' must be 'for_user'" % self.tag_name
|
raise template.TemplateSyntaxError, "Fourth argument in '%s' must be 'for_user'" % self.tag_name
|
||||||
return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
|
return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(len(tokens) > 5 and tokens[5] or None))
|
||||||
|
|
||||||
template.register_tag('get_admin_log', DoGetAdminLog('get_admin_log'))
|
register.tag('get_admin_log', DoGetAdminLog('get_admin_log'))
|
||||||
|
|
|
@ -50,21 +50,23 @@ class TemplateValidator(formfields.Manipulator):
|
||||||
return
|
return
|
||||||
|
|
||||||
# so that inheritance works in the site's context, register a new function
|
# so that inheritance works in the site's context, register a new function
|
||||||
# for "extends" that uses the site's TEMPLATE_DIR instead
|
# for "extends" that uses the site's TEMPLATE_DIRS instead.
|
||||||
def new_do_extends(parser, token):
|
def new_do_extends(parser, token):
|
||||||
node = loader.do_extends(parser, token)
|
node = loader.do_extends(parser, token)
|
||||||
node.template_dirs = settings_module.TEMPLATE_DIRS
|
node.template_dirs = settings_module.TEMPLATE_DIRS
|
||||||
return node
|
return node
|
||||||
template.register_tag('extends', new_do_extends)
|
register = template.Library()
|
||||||
|
register.tag('extends', new_do_extends)
|
||||||
|
template.builtins.append(register)
|
||||||
|
|
||||||
# now validate the template using the new template dirs
|
# Now validate the template using the new template dirs
|
||||||
# making sure to reset the extends function in any case
|
# making sure to reset the extends function in any case.
|
||||||
error = None
|
error = None
|
||||||
try:
|
try:
|
||||||
tmpl = loader.get_template_from_string(field_data)
|
tmpl = loader.get_template_from_string(field_data)
|
||||||
tmpl.render(template.Context({}))
|
tmpl.render(template.Context({}))
|
||||||
except template.TemplateSyntaxError, e:
|
except template.TemplateSyntaxError, e:
|
||||||
error = e
|
error = e
|
||||||
template.register_tag('extends', loader.do_extends)
|
template.builtins.remove(register)
|
||||||
if error:
|
if error:
|
||||||
raise validators.ValidationError, e.args
|
raise validators.ValidationError, e.args
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.models.comments import comments, freecomments
|
||||||
from django.models.core import contenttypes
|
from django.models.core import contenttypes
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
COMMENT_FORM = '''
|
COMMENT_FORM = '''
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% if display_form %}
|
{% if display_form %}
|
||||||
|
@ -360,10 +362,10 @@ class DoGetCommentList:
|
||||||
return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering)
|
return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering)
|
||||||
|
|
||||||
# registration comments
|
# registration comments
|
||||||
template.register_tag('get_comment_list', DoGetCommentList(False))
|
register.tag('get_comment_list', DoGetCommentList(False))
|
||||||
template.register_tag('comment_form', DoCommentForm(False))
|
register.tag('comment_form', DoCommentForm(False))
|
||||||
template.register_tag('get_comment_count', DoCommentCount(False))
|
register.tag('get_comment_count', DoCommentCount(False))
|
||||||
# free comments
|
# free comments
|
||||||
template.register_tag('get_free_comment_list', DoGetCommentList(True))
|
register.tag('get_free_comment_list', DoGetCommentList(True))
|
||||||
template.register_tag('free_comment_form', DoCommentForm(True))
|
register.tag('free_comment_form', DoCommentForm(True))
|
||||||
template.register_tag('get_free_comment_count', DoCommentCount(True))
|
register.tag('get_free_comment_count', DoCommentCount(True))
|
||||||
|
|
|
@ -4,35 +4,37 @@ markup syntaxes to HTML; currently there is support for:
|
||||||
|
|
||||||
* Textile, which requires the PyTextile library available at
|
* Textile, which requires the PyTextile library available at
|
||||||
http://dealmeida.net/projects/textile/
|
http://dealmeida.net/projects/textile/
|
||||||
|
|
||||||
* Markdown, which requires the Python-markdown library from
|
* Markdown, which requires the Python-markdown library from
|
||||||
http://www.freewisdom.org/projects/python-markdown
|
http://www.freewisdom.org/projects/python-markdown
|
||||||
|
|
||||||
* ReStructuredText, which requires docutils from http://docutils.sf.net/
|
* ReStructuredText, which requires docutils from http://docutils.sf.net/
|
||||||
|
|
||||||
In each case, if the required library is not installed, the filter will
|
In each case, if the required library is not installed, the filter will
|
||||||
silently fail and return the un-marked-up text.
|
silently fail and return the un-marked-up text.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core import template
|
from django.core import template
|
||||||
|
|
||||||
def textile(value, _):
|
register = template.Library()
|
||||||
|
|
||||||
|
def textile(value):
|
||||||
try:
|
try:
|
||||||
import textile
|
import textile
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return textile.textile(value)
|
return textile.textile(value)
|
||||||
|
|
||||||
def markdown(value, _):
|
def markdown(value):
|
||||||
try:
|
try:
|
||||||
import markdown
|
import markdown
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return markdown.markdown(value)
|
return markdown.markdown(value)
|
||||||
|
|
||||||
def restructuredtext(value, _):
|
def restructuredtext(value):
|
||||||
try:
|
try:
|
||||||
from docutils.core import publish_parts
|
from docutils.core import publish_parts
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -40,7 +42,7 @@ def restructuredtext(value, _):
|
||||||
else:
|
else:
|
||||||
parts = publish_parts(source=value, writer_name="html4css1")
|
parts = publish_parts(source=value, writer_name="html4css1")
|
||||||
return parts["fragment"]
|
return parts["fragment"]
|
||||||
|
|
||||||
template.register_filter("textile", textile, False)
|
register.filter(textile)
|
||||||
template.register_filter("markdown", markdown, False)
|
register.filter(markdown)
|
||||||
template.register_filter("restructuredtext", restructuredtext, False)
|
register.filter(restructuredtext)
|
||||||
|
|
|
@ -3,7 +3,7 @@ This is the Django template system.
|
||||||
|
|
||||||
How it works:
|
How it works:
|
||||||
|
|
||||||
The tokenize() function converts a template string (i.e., a string containing
|
The Lexer.tokenize() function converts a template string (i.e., a string containing
|
||||||
markup with custom template tags) to tokens, which can be either plain text
|
markup with custom template tags) to tokens, which can be either plain text
|
||||||
(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
|
(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ times with multiple contexts)
|
||||||
'\n<html>\n\n</html>\n'
|
'\n<html>\n\n</html>\n'
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
from inspect import getargspec
|
||||||
|
from django.utils.functional import curry
|
||||||
from django.conf.settings import DEFAULT_CHARSET, TEMPLATE_DEBUG
|
from django.conf.settings import DEFAULT_CHARSET, TEMPLATE_DEBUG
|
||||||
|
|
||||||
__all__ = ('Template','Context','compile_string')
|
__all__ = ('Template','Context','compile_string')
|
||||||
|
@ -82,11 +84,10 @@ UNKNOWN_SOURCE="<unknown source>"
|
||||||
tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
|
tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
|
||||||
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
|
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
|
||||||
|
|
||||||
# global dict used by register_tag; maps custom tags to callback functions
|
# global dictionary of libraries that have been loaded using get_library
|
||||||
registered_tags = {}
|
libraries = {}
|
||||||
|
# global list of libraries to load by default for a new parser
|
||||||
# global dict used by register_filter; maps custom filters to callback functions
|
builtins = []
|
||||||
registered_filters = {}
|
|
||||||
|
|
||||||
class TemplateSyntaxError(Exception):
|
class TemplateSyntaxError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -105,12 +106,15 @@ class SilentVariableFailure(Exception):
|
||||||
"Any function raising this exception will be ignored by resolve_variable"
|
"Any function raising this exception will be ignored by resolve_variable"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class InvalidTemplateLibrary(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class Origin(object):
|
class Origin(object):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
raise NotImplementedException
|
raise NotImplementedError
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -264,6 +268,10 @@ class DebugLexer(Lexer):
|
||||||
class Parser(object):
|
class Parser(object):
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
|
self.tags = {}
|
||||||
|
self.filters = {}
|
||||||
|
for lib in builtins:
|
||||||
|
self.add_library(lib)
|
||||||
|
|
||||||
def parse(self, parse_until=[]):
|
def parse(self, parse_until=[]):
|
||||||
nodelist = self.create_nodelist()
|
nodelist = self.create_nodelist()
|
||||||
|
@ -274,7 +282,8 @@ class Parser(object):
|
||||||
elif token.token_type == TOKEN_VAR:
|
elif token.token_type == TOKEN_VAR:
|
||||||
if not token.contents:
|
if not token.contents:
|
||||||
self.empty_variable(token)
|
self.empty_variable(token)
|
||||||
var_node = self.create_variable_node(token.contents)
|
filter_expression = self.compile_filter(token.contents)
|
||||||
|
var_node = self.create_variable_node(filter_expression)
|
||||||
self.extend_nodelist(nodelist, var_node,token)
|
self.extend_nodelist(nodelist, var_node,token)
|
||||||
elif token.token_type == TOKEN_BLOCK:
|
elif token.token_type == TOKEN_BLOCK:
|
||||||
if token.contents in parse_until:
|
if token.contents in parse_until:
|
||||||
|
@ -288,7 +297,7 @@ class Parser(object):
|
||||||
# execute callback function for this tag and append resulting node
|
# execute callback function for this tag and append resulting node
|
||||||
self.enter_command(command, token)
|
self.enter_command(command, token)
|
||||||
try:
|
try:
|
||||||
compile_func = registered_tags[command]
|
compile_func = self.tags[command]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.invalid_block_tag(token, command)
|
self.invalid_block_tag(token, command)
|
||||||
try:
|
try:
|
||||||
|
@ -302,8 +311,8 @@ class Parser(object):
|
||||||
self.unclosed_block_tag(parse_until)
|
self.unclosed_block_tag(parse_until)
|
||||||
return nodelist
|
return nodelist
|
||||||
|
|
||||||
def create_variable_node(self, contents):
|
def create_variable_node(self, filter_expression):
|
||||||
return VariableNode(contents)
|
return VariableNode(filter_expression)
|
||||||
|
|
||||||
def create_nodelist(self):
|
def create_nodelist(self):
|
||||||
return NodeList()
|
return NodeList()
|
||||||
|
@ -344,6 +353,20 @@ class Parser(object):
|
||||||
def delete_first_token(self):
|
def delete_first_token(self):
|
||||||
del self.tokens[0]
|
del self.tokens[0]
|
||||||
|
|
||||||
|
def add_library(self, lib):
|
||||||
|
self.tags.update(lib.tags)
|
||||||
|
self.filters.update(lib.filters)
|
||||||
|
|
||||||
|
def compile_filter(self,token):
|
||||||
|
"Convenient wrapper for FilterExpression"
|
||||||
|
return FilterExpression(token, self)
|
||||||
|
|
||||||
|
def find_filter(self, filter_name):
|
||||||
|
if self.filters.has_key(filter_name):
|
||||||
|
return self.filters[filter_name]
|
||||||
|
else:
|
||||||
|
raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
|
||||||
|
|
||||||
class DebugParser(Parser):
|
class DebugParser(Parser):
|
||||||
def __init__(self, lexer):
|
def __init__(self, lexer):
|
||||||
super(DebugParser, self).__init__(lexer)
|
super(DebugParser, self).__init__(lexer)
|
||||||
|
@ -483,7 +506,8 @@ filter_raw_string = r"""
|
||||||
(?:%(arg_sep)s
|
(?:%(arg_sep)s
|
||||||
(?:
|
(?:
|
||||||
%(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
|
%(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
|
||||||
"(?P<arg>%(str)s)"
|
"(?P<constant_arg>%(str)s)"|
|
||||||
|
(?P<var_arg>[%(var_chars)s]+)
|
||||||
)
|
)
|
||||||
)?
|
)?
|
||||||
)""" % {
|
)""" % {
|
||||||
|
@ -498,7 +522,7 @@ filter_raw_string = r"""
|
||||||
filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
|
filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
|
||||||
filter_re = re.compile(filter_raw_string)
|
filter_re = re.compile(filter_raw_string)
|
||||||
|
|
||||||
class FilterParser(object):
|
class FilterExpression(object):
|
||||||
"""
|
"""
|
||||||
Parses a variable token and its optional filters (all as a single string),
|
Parses a variable token and its optional filters (all as a single string),
|
||||||
and return a list of tuples of the filter name and arguments.
|
and return a list of tuples of the filter name and arguments.
|
||||||
|
@ -513,7 +537,8 @@ class FilterParser(object):
|
||||||
This class should never be instantiated outside of the
|
This class should never be instantiated outside of the
|
||||||
get_filters_from_token helper function.
|
get_filters_from_token helper function.
|
||||||
"""
|
"""
|
||||||
def __init__(self, token):
|
def __init__(self, token, parser):
|
||||||
|
self.token = token
|
||||||
matches = filter_re.finditer(token)
|
matches = filter_re.finditer(token)
|
||||||
var = None
|
var = None
|
||||||
filters = []
|
filters = []
|
||||||
|
@ -536,27 +561,69 @@ class FilterParser(object):
|
||||||
raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
|
raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
|
||||||
else:
|
else:
|
||||||
filter_name = match.group("filter_name")
|
filter_name = match.group("filter_name")
|
||||||
arg, i18n_arg = match.group("arg","i18n_arg")
|
args = []
|
||||||
|
constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
|
||||||
if i18n_arg:
|
if i18n_arg:
|
||||||
arg =_(i18n_arg.replace('\\', ''))
|
args.append((False, _(i18n_arg.replace('\\', ''))))
|
||||||
if arg:
|
elif constant_arg:
|
||||||
arg = arg.replace('\\', '')
|
args.append((False, constant_arg.replace('\\', '')))
|
||||||
if not registered_filters.has_key(filter_name):
|
elif var_arg:
|
||||||
raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
|
args.append((True, var_arg))
|
||||||
if registered_filters[filter_name][1] == True and arg is None:
|
filter_func = parser.find_filter(filter_name)
|
||||||
raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name
|
self.args_check(filter_name,filter_func, args)
|
||||||
if registered_filters[filter_name][1] == False and arg is not None:
|
filters.append( (filter_func,args))
|
||||||
raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)
|
|
||||||
filters.append( (filter_name,arg) )
|
|
||||||
upto = match.end()
|
upto = match.end()
|
||||||
if upto != len(token):
|
if upto != len(token):
|
||||||
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
|
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
|
||||||
self.var , self.filters = var, filters
|
self.var , self.filters = var, filters
|
||||||
|
|
||||||
def get_filters_from_token(token):
|
def resolve(self, context):
|
||||||
"Convenient wrapper for FilterParser"
|
try:
|
||||||
p = FilterParser(token)
|
obj = resolve_variable(self.var, context)
|
||||||
return (p.var, p.filters)
|
except VariableDoesNotExist:
|
||||||
|
obj = ''
|
||||||
|
for func, args in self.filters:
|
||||||
|
arg_vals = []
|
||||||
|
for lookup, arg in args:
|
||||||
|
if not lookup:
|
||||||
|
arg_vals.append(arg)
|
||||||
|
else:
|
||||||
|
arg_vals.append(resolve_variable(arg, context))
|
||||||
|
obj = func(obj, *arg_vals)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def args_check(name, func, provided):
|
||||||
|
provided = list(provided)
|
||||||
|
plen = len(provided)
|
||||||
|
(args, varargs, varkw, defaults) = getargspec(func)
|
||||||
|
# First argument is filter input.
|
||||||
|
args.pop(0)
|
||||||
|
if defaults:
|
||||||
|
nondefs = args[:-len(defaults)]
|
||||||
|
else:
|
||||||
|
nondefs = args
|
||||||
|
# Args without defaults must be provided.
|
||||||
|
try:
|
||||||
|
for arg in nondefs:
|
||||||
|
provided.pop(0)
|
||||||
|
except IndexError:
|
||||||
|
# Not enough
|
||||||
|
raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
|
||||||
|
|
||||||
|
# Defaults can be overridden.
|
||||||
|
defaults = defaults and list(defaults) or []
|
||||||
|
try:
|
||||||
|
for parg in provided:
|
||||||
|
defaults.pop(0)
|
||||||
|
except IndexError:
|
||||||
|
# Too many.
|
||||||
|
raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
|
||||||
|
|
||||||
|
return True
|
||||||
|
args_check = staticmethod(args_check)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.token
|
||||||
|
|
||||||
def resolve_variable(path, context):
|
def resolve_variable(path, context):
|
||||||
"""
|
"""
|
||||||
|
@ -607,22 +674,6 @@ def resolve_variable(path, context):
|
||||||
del bits[0]
|
del bits[0]
|
||||||
return current
|
return current
|
||||||
|
|
||||||
def resolve_variable_with_filters(var_string, context):
|
|
||||||
"""
|
|
||||||
var_string is a full variable expression with optional filters, like:
|
|
||||||
a.b.c|lower|date:"y/m/d"
|
|
||||||
This function resolves the variable in the context, applies all filters and
|
|
||||||
returns the object.
|
|
||||||
"""
|
|
||||||
var, filters = get_filters_from_token(var_string)
|
|
||||||
try:
|
|
||||||
obj = resolve_variable(var, context)
|
|
||||||
except VariableDoesNotExist:
|
|
||||||
obj = ''
|
|
||||||
for name, arg in filters:
|
|
||||||
obj = registered_filters[name][0](obj, arg)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
"Return the node rendered as a string"
|
"Return the node rendered as a string"
|
||||||
|
@ -687,11 +738,11 @@ class TextNode(Node):
|
||||||
return self.s
|
return self.s
|
||||||
|
|
||||||
class VariableNode(Node):
|
class VariableNode(Node):
|
||||||
def __init__(self, var_string):
|
def __init__(self, filter_expression):
|
||||||
self.var_string = var_string
|
self.filter_expression = filter_expression
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Variable Node: %s>" % self.var_string
|
return "<Variable Node: %s>" % self.filter_expression
|
||||||
|
|
||||||
def encode_output(self, output):
|
def encode_output(self, output):
|
||||||
# Check type so that we don't run str() on a Unicode object
|
# Check type so that we don't run str() on a Unicode object
|
||||||
|
@ -703,30 +754,153 @@ class VariableNode(Node):
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
output = resolve_variable_with_filters(self.var_string, context)
|
output = self.filter_expression.resolve(context)
|
||||||
return self.encode_output(output)
|
return self.encode_output(output)
|
||||||
|
|
||||||
class DebugVariableNode(VariableNode):
|
class DebugVariableNode(VariableNode):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
output = resolve_variable_with_filters(self.var_string, context)
|
output = self.filter_expression.resolve(context)
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError, e:
|
||||||
if not hasattr(e, 'source'):
|
if not hasattr(e, 'source'):
|
||||||
e.source = self.source
|
e.source = self.source
|
||||||
raise
|
raise
|
||||||
return self.encode_output(output)
|
return self.encode_output(output)
|
||||||
|
|
||||||
def register_tag(token_command, callback_function):
|
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
|
||||||
registered_tags[token_command] = callback_function
|
"Returns a template.Node subclass."
|
||||||
|
bits = token.contents.split()[1:]
|
||||||
|
bmax = len(params)
|
||||||
|
def_len = defaults and len(defaults) or 0
|
||||||
|
bmin = bmax - def_len
|
||||||
|
if(len(bits) < bmin or len(bits) > bmax):
|
||||||
|
if bmin == bmax:
|
||||||
|
message = "%s takes %s arguments" % (name, bmin)
|
||||||
|
else:
|
||||||
|
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
|
||||||
|
raise TemplateSyntaxError, message
|
||||||
|
return node_class(bits)
|
||||||
|
|
||||||
def unregister_tag(token_command):
|
class Library(object):
|
||||||
del registered_tags[token_command]
|
def __init__(self):
|
||||||
|
self.filters = {}
|
||||||
|
self.tags = {}
|
||||||
|
|
||||||
def register_filter(filter_name, callback_function, has_arg):
|
def tag(self, name = None, compile_function = None):
|
||||||
registered_filters[filter_name] = (callback_function, has_arg)
|
if name == None and compile_function == None:
|
||||||
|
# @register.tag()
|
||||||
|
return self.tag_function
|
||||||
|
elif name != None and compile_function == None:
|
||||||
|
if(callable(name)):
|
||||||
|
# @register.tag
|
||||||
|
return self.tag_function(name)
|
||||||
|
else:
|
||||||
|
# @register.tag('somename') or @register.tag(name='somename')
|
||||||
|
def dec(func):
|
||||||
|
return self.tag(name, func)
|
||||||
|
return dec
|
||||||
|
elif name != None and compile_function != None:
|
||||||
|
# register.tag('somename', somefunc)
|
||||||
|
self.tags[name] = compile_function
|
||||||
|
return compile_function
|
||||||
|
else:
|
||||||
|
raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
|
||||||
|
|
||||||
def unregister_filter(filter_name):
|
def tag_function(self,func):
|
||||||
del registered_filters[filter_name]
|
self.tags[func.__name__] = func
|
||||||
|
return func
|
||||||
|
|
||||||
import defaulttags
|
def filter(self, name = None, filter_func = None):
|
||||||
import defaultfilters
|
if name == None and filter_func == None:
|
||||||
|
# @register.filter()
|
||||||
|
return self.filter_function
|
||||||
|
elif filter_func == None:
|
||||||
|
if(callable(name)):
|
||||||
|
# @register.filter
|
||||||
|
return self.filter_function(name)
|
||||||
|
else:
|
||||||
|
# @register.filter('somename') or @register.filter(name='somename')
|
||||||
|
def dec(func):
|
||||||
|
return self.filter(name, func)
|
||||||
|
return dec
|
||||||
|
elif name != None and filter_func != None:
|
||||||
|
# register.filter('somename', somefunc)
|
||||||
|
self.filters[name] = filter_func
|
||||||
|
else:
|
||||||
|
raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r, %r)", (name, compile_function, has_arg)
|
||||||
|
|
||||||
|
def filter_function(self, func):
|
||||||
|
self.filters[func.__name__] = func
|
||||||
|
return func
|
||||||
|
|
||||||
|
def simple_tag(self,func):
|
||||||
|
(params, xx, xxx, defaults) = getargspec(func)
|
||||||
|
|
||||||
|
class SimpleNode(Node):
|
||||||
|
def __init__(self, vars_to_resolve):
|
||||||
|
self.vars_to_resolve = vars_to_resolve
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
||||||
|
return func(*resolved_vars)
|
||||||
|
|
||||||
|
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
|
||||||
|
compile_func.__doc__ = func.__doc__
|
||||||
|
self.tag(func.__name__, compile_func)
|
||||||
|
return func
|
||||||
|
|
||||||
|
def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
|
||||||
|
def dec(func):
|
||||||
|
(params, xx, xxx, defaults) = getargspec(func)
|
||||||
|
if takes_context:
|
||||||
|
if params[0] == 'context':
|
||||||
|
params = params[1:]
|
||||||
|
else:
|
||||||
|
raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
|
||||||
|
|
||||||
|
class InclusionNode(Node):
|
||||||
|
def __init__(self, vars_to_resolve):
|
||||||
|
self.vars_to_resolve = vars_to_resolve
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
||||||
|
if takes_context:
|
||||||
|
args = [context] + resolved_vars
|
||||||
|
else:
|
||||||
|
args = resolved_vars
|
||||||
|
|
||||||
|
dict = func(*args)
|
||||||
|
|
||||||
|
if not getattr(self, 'nodelist', False):
|
||||||
|
from django.core.template_loader import get_template
|
||||||
|
t = get_template(file_name)
|
||||||
|
self.nodelist = t.nodelist
|
||||||
|
return self.nodelist.render(context_class(dict))
|
||||||
|
|
||||||
|
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
|
||||||
|
compile_func.__doc__ = func.__doc__
|
||||||
|
self.tag(func.__name__, compile_func)
|
||||||
|
return func
|
||||||
|
return dec
|
||||||
|
|
||||||
|
def get_library(module_name):
|
||||||
|
lib = libraries.get(module_name, None)
|
||||||
|
if not lib:
|
||||||
|
try:
|
||||||
|
mod = __import__(module_name, '', '', [''])
|
||||||
|
except ImportError, e:
|
||||||
|
raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
|
||||||
|
for k, v in mod.__dict__.items():
|
||||||
|
if isinstance(v, Library):
|
||||||
|
lib = v
|
||||||
|
libraries[module_name] = lib
|
||||||
|
break
|
||||||
|
if not lib:
|
||||||
|
raise InvalidTemplateLibrary, "Template library %s does not have a Library member" % module_name
|
||||||
|
return lib
|
||||||
|
|
||||||
|
def add_to_builtins(module_name):
|
||||||
|
builtins.append(get_library(module_name))
|
||||||
|
|
||||||
|
add_to_builtins('django.core.template.defaulttags')
|
||||||
|
add_to_builtins('django.core.template.defaultfilters')
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
from django.core.template import Context, Node, TemplateSyntaxError, register_tag, resolve_variable
|
|
||||||
from django.core.template_loader import get_template
|
|
||||||
from django.utils.functional import curry
|
|
||||||
from inspect import getargspec
|
|
||||||
|
|
||||||
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
|
|
||||||
"Returns a template.Node subclass."
|
|
||||||
bits = token.contents.split()[1:]
|
|
||||||
bmax = len(params)
|
|
||||||
def_len = defaults and len(defaults) or 0
|
|
||||||
bmin = bmax - def_len
|
|
||||||
if(len(bits) < bmin or len(bits) > bmax):
|
|
||||||
if bmin == bmax:
|
|
||||||
message = "%s takes %s arguments" % (name, bmin)
|
|
||||||
else:
|
|
||||||
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
|
|
||||||
raise TemplateSyntaxError, message
|
|
||||||
return node_class(bits)
|
|
||||||
|
|
||||||
def simple_tag(func):
|
|
||||||
(params, xx, xxx, defaults) = getargspec(func)
|
|
||||||
|
|
||||||
class SimpleNode(Node):
|
|
||||||
def __init__(self, vars_to_resolve):
|
|
||||||
self.vars_to_resolve = vars_to_resolve
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
|
||||||
return func(*resolved_vars)
|
|
||||||
|
|
||||||
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
|
|
||||||
compile_func.__doc__ = func.__doc__
|
|
||||||
register_tag(func.__name__, compile_func)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def inclusion_tag(file_name, context_class=Context, takes_context=False):
|
|
||||||
def dec(func):
|
|
||||||
(params, xx, xxx, defaults) = getargspec(func)
|
|
||||||
if takes_context:
|
|
||||||
if params[0] == 'context':
|
|
||||||
params = params[1:]
|
|
||||||
else:
|
|
||||||
raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
|
|
||||||
|
|
||||||
class InclusionNode(Node):
|
|
||||||
def __init__(self, vars_to_resolve):
|
|
||||||
self.vars_to_resolve = vars_to_resolve
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
|
||||||
if takes_context:
|
|
||||||
args = [context] + resolved_vars
|
|
||||||
else:
|
|
||||||
args = resolved_vars
|
|
||||||
|
|
||||||
dict = func(*args)
|
|
||||||
|
|
||||||
if not getattr(self, 'nodelist', False):
|
|
||||||
t = get_template(file_name)
|
|
||||||
self.nodelist = t.nodelist
|
|
||||||
return self.nodelist.render(context_class(dict))
|
|
||||||
|
|
||||||
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
|
|
||||||
compile_func.__doc__ = func.__doc__
|
|
||||||
register_tag(func.__name__, compile_func)
|
|
||||||
return func
|
|
||||||
return dec
|
|
|
@ -1,28 +1,32 @@
|
||||||
"Default variable filters"
|
"Default variable filters"
|
||||||
|
|
||||||
from django.core.template import register_filter, resolve_variable
|
from django.core.template import resolve_variable, Library
|
||||||
|
from django.conf.settings import DATE_FORMAT, TIME_FORMAT
|
||||||
import re
|
import re
|
||||||
import random as random_module
|
import random as random_module
|
||||||
|
|
||||||
|
register = Library()
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# STRINGS #
|
# STRINGS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
def addslashes(value, _):
|
|
||||||
|
def addslashes(value):
|
||||||
"Adds slashes - useful for passing strings to JavaScript, for example."
|
"Adds slashes - useful for passing strings to JavaScript, for example."
|
||||||
return value.replace('"', '\\"').replace("'", "\\'")
|
return value.replace('"', '\\"').replace("'", "\\'")
|
||||||
|
|
||||||
def capfirst(value, _):
|
def capfirst(value):
|
||||||
"Capitalizes the first character of the value"
|
"Capitalizes the first character of the value"
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return value and value[0].upper() + value[1:]
|
return value and value[0].upper() + value[1:]
|
||||||
|
|
||||||
def fix_ampersands(value, _):
|
def fix_ampersands(value):
|
||||||
"Replaces ampersands with ``&`` entities"
|
"Replaces ampersands with ``&`` entities"
|
||||||
from django.utils.html import fix_ampersands
|
from django.utils.html import fix_ampersands
|
||||||
return fix_ampersands(value)
|
return fix_ampersands(value)
|
||||||
|
|
||||||
def floatformat(text, _):
|
def floatformat(text):
|
||||||
"""
|
"""
|
||||||
Displays a floating point number as 34.2 (with one decimal place) -- but
|
Displays a floating point number as 34.2 (with one decimal place) -- but
|
||||||
only if there's a point to be displayed
|
only if there's a point to be displayed
|
||||||
|
@ -37,7 +41,7 @@ def floatformat(text, _):
|
||||||
else:
|
else:
|
||||||
return '%d' % int(f)
|
return '%d' % int(f)
|
||||||
|
|
||||||
def linenumbers(value, _):
|
def linenumbers(value):
|
||||||
"Displays text with line numbers"
|
"Displays text with line numbers"
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
lines = value.split('\n')
|
lines = value.split('\n')
|
||||||
|
@ -47,18 +51,18 @@ def linenumbers(value, _):
|
||||||
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
|
||||||
def lower(value, _):
|
def lower(value):
|
||||||
"Converts a string into all lowercase"
|
"Converts a string into all lowercase"
|
||||||
return value.lower()
|
return value.lower()
|
||||||
|
|
||||||
def make_list(value, _):
|
def make_list(value):
|
||||||
"""
|
"""
|
||||||
Returns the value turned into a list. For an integer, it's a list of
|
Returns the value turned into a list. For an integer, it's a list of
|
||||||
digits. For a string, it's a list of characters.
|
digits. For a string, it's a list of characters.
|
||||||
"""
|
"""
|
||||||
return list(str(value))
|
return list(str(value))
|
||||||
|
|
||||||
def slugify(value, _):
|
def slugify(value):
|
||||||
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
|
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
|
||||||
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
||||||
return re.sub('\s+', '-', value)
|
return re.sub('\s+', '-', value)
|
||||||
|
@ -77,7 +81,7 @@ def stringformat(value, arg):
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def title(value, _):
|
def title(value):
|
||||||
"Converts a string into titlecase"
|
"Converts a string into titlecase"
|
||||||
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
||||||
|
|
||||||
|
@ -96,16 +100,16 @@ def truncatewords(value, arg):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return truncate_words(value, length)
|
return truncate_words(value, length)
|
||||||
|
|
||||||
def upper(value, _):
|
def upper(value):
|
||||||
"Converts a string into all uppercase"
|
"Converts a string into all uppercase"
|
||||||
return value.upper()
|
return value.upper()
|
||||||
|
|
||||||
def urlencode(value, _):
|
def urlencode(value):
|
||||||
"Escapes a value for use in a URL"
|
"Escapes a value for use in a URL"
|
||||||
import urllib
|
import urllib
|
||||||
return urllib.quote(value)
|
return urllib.quote(value)
|
||||||
|
|
||||||
def urlize(value, _):
|
def urlize(value):
|
||||||
"Converts URLs in plain text into clickable links"
|
"Converts URLs in plain text into clickable links"
|
||||||
from django.utils.html import urlize
|
from django.utils.html import urlize
|
||||||
return urlize(value, nofollow=True)
|
return urlize(value, nofollow=True)
|
||||||
|
@ -119,7 +123,7 @@ def urlizetrunc(value, limit):
|
||||||
from django.utils.html import urlize
|
from django.utils.html import urlize
|
||||||
return urlize(value, trim_url_limit=int(limit), nofollow=True)
|
return urlize(value, trim_url_limit=int(limit), nofollow=True)
|
||||||
|
|
||||||
def wordcount(value, _):
|
def wordcount(value):
|
||||||
"Returns the number of words"
|
"Returns the number of words"
|
||||||
return len(value.split())
|
return len(value.split())
|
||||||
|
|
||||||
|
@ -160,17 +164,17 @@ def cut(value, arg):
|
||||||
# HTML STRINGS #
|
# HTML STRINGS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
def escape(value, _):
|
def escape(value):
|
||||||
"Escapes a string's HTML"
|
"Escapes a string's HTML"
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
return escape(value)
|
return escape(value)
|
||||||
|
|
||||||
def linebreaks(value, _):
|
def linebreaks(value):
|
||||||
"Converts newlines into <p> and <br />s"
|
"Converts newlines into <p> and <br />s"
|
||||||
from django.utils.html import linebreaks
|
from django.utils.html import linebreaks
|
||||||
return linebreaks(value)
|
return linebreaks(value)
|
||||||
|
|
||||||
def linebreaksbr(value, _):
|
def linebreaksbr(value):
|
||||||
"Converts newlines into <br />s"
|
"Converts newlines into <br />s"
|
||||||
return value.replace('\n', '<br />')
|
return value.replace('\n', '<br />')
|
||||||
|
|
||||||
|
@ -184,7 +188,7 @@ def removetags(value, tags):
|
||||||
value = endtag_re.sub('', value)
|
value = endtag_re.sub('', value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def striptags(value, _):
|
def striptags(value):
|
||||||
"Strips all [X]HTML tags"
|
"Strips all [X]HTML tags"
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
|
@ -214,7 +218,7 @@ def dictsortreversed(value, arg):
|
||||||
decorated.reverse()
|
decorated.reverse()
|
||||||
return [item[1] for item in decorated]
|
return [item[1] for item in decorated]
|
||||||
|
|
||||||
def first(value, _):
|
def first(value):
|
||||||
"Returns the first item in a list"
|
"Returns the first item in a list"
|
||||||
try:
|
try:
|
||||||
return value[0]
|
return value[0]
|
||||||
|
@ -228,7 +232,7 @@ def join(value, arg):
|
||||||
except AttributeError: # fail silently but nicely
|
except AttributeError: # fail silently but nicely
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def length(value, _):
|
def length(value):
|
||||||
"Returns the length of the value - useful for lists"
|
"Returns the length of the value - useful for lists"
|
||||||
return len(value)
|
return len(value)
|
||||||
|
|
||||||
|
@ -236,7 +240,7 @@ def length_is(value, arg):
|
||||||
"Returns a boolean of whether the value's length is the argument"
|
"Returns a boolean of whether the value's length is the argument"
|
||||||
return len(value) == int(arg)
|
return len(value) == int(arg)
|
||||||
|
|
||||||
def random(value, _):
|
def random(value):
|
||||||
"Returns a random item from the list"
|
"Returns a random item from the list"
|
||||||
return random_module.choice(value)
|
return random_module.choice(value)
|
||||||
|
|
||||||
|
@ -253,7 +257,7 @@ def slice_(value, arg):
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return value # Fail silently.
|
return value # Fail silently.
|
||||||
|
|
||||||
def unordered_list(value, _):
|
def unordered_list(value):
|
||||||
"""
|
"""
|
||||||
Recursively takes a self-nested list and returns an HTML unordered list --
|
Recursively takes a self-nested list and returns an HTML unordered list --
|
||||||
WITHOUT opening and closing <ul> tags.
|
WITHOUT opening and closing <ul> tags.
|
||||||
|
@ -314,17 +318,17 @@ def get_digit(value, arg):
|
||||||
# DATES #
|
# DATES #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
def date(value, arg):
|
def date(value, arg=DATE_FORMAT):
|
||||||
"Formats a date according to the given format"
|
"Formats a date according to the given format"
|
||||||
from django.utils.dateformat import format
|
from django.utils.dateformat import format
|
||||||
return format(value, arg)
|
return format(value, arg)
|
||||||
|
|
||||||
def time(value, arg):
|
def time(value, arg=TIME_FORMAT):
|
||||||
"Formats a time according to the given format"
|
"Formats a time according to the given format"
|
||||||
from django.utils.dateformat import time_format
|
from django.utils.dateformat import time_format
|
||||||
return time_format(value, arg)
|
return time_format(value, arg)
|
||||||
|
|
||||||
def timesince(value, _):
|
def timesince(value):
|
||||||
'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
|
'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
|
||||||
from django.utils.timesince import timesince
|
from django.utils.timesince import timesince
|
||||||
return timesince(value)
|
return timesince(value)
|
||||||
|
@ -347,7 +351,7 @@ def divisibleby(value, arg):
|
||||||
"Returns true if the value is devisible by the argument"
|
"Returns true if the value is devisible by the argument"
|
||||||
return int(value) % int(arg) == 0
|
return int(value) % int(arg) == 0
|
||||||
|
|
||||||
def yesno(value, arg):
|
def yesno(value, arg=_("yes,no,maybe")):
|
||||||
"""
|
"""
|
||||||
Given a string mapping values for true, false and (optionally) None,
|
Given a string mapping values for true, false and (optionally) None,
|
||||||
returns one of those strings accoding to the value:
|
returns one of those strings accoding to the value:
|
||||||
|
@ -379,7 +383,7 @@ def yesno(value, arg):
|
||||||
# MISC #
|
# MISC #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
def filesizeformat(bytes, _):
|
def filesizeformat(bytes):
|
||||||
"""
|
"""
|
||||||
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
|
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
|
||||||
bytes, etc).
|
bytes, etc).
|
||||||
|
@ -393,7 +397,7 @@ def filesizeformat(bytes, _):
|
||||||
return "%.1f MB" % (bytes / (1024 * 1024))
|
return "%.1f MB" % (bytes / (1024 * 1024))
|
||||||
return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
|
return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
|
||||||
|
|
||||||
def pluralize(value, _):
|
def pluralize(value):
|
||||||
"Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
|
"Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
|
||||||
try:
|
try:
|
||||||
if int(value) != 1:
|
if int(value) != 1:
|
||||||
|
@ -408,62 +412,62 @@ def pluralize(value, _):
|
||||||
pass
|
pass
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def phone2numeric(value, _):
|
def phone2numeric(value):
|
||||||
"Takes a phone number and converts it in to its numerical equivalent"
|
"Takes a phone number and converts it in to its numerical equivalent"
|
||||||
from django.utils.text import phone2numeric
|
from django.utils.text import phone2numeric
|
||||||
return phone2numeric(value)
|
return phone2numeric(value)
|
||||||
|
|
||||||
def pprint(value, _):
|
def pprint(value):
|
||||||
"A wrapper around pprint.pprint -- for debugging, really"
|
"A wrapper around pprint.pprint -- for debugging, really"
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
return pformat(value)
|
return pformat(value)
|
||||||
|
|
||||||
# Syntax: register_filter(name of filter, callback, has_argument)
|
# Syntax: register.filter(name of filter, callback)
|
||||||
register_filter('add', add, True)
|
register.filter(add)
|
||||||
register_filter('addslashes', addslashes, False)
|
register.filter(addslashes)
|
||||||
register_filter('capfirst', capfirst, False)
|
register.filter(capfirst)
|
||||||
register_filter('center', center, True)
|
register.filter(center)
|
||||||
register_filter('cut', cut, True)
|
register.filter(cut)
|
||||||
register_filter('date', date, True)
|
register.filter(date)
|
||||||
register_filter('default', default, True)
|
register.filter(default)
|
||||||
register_filter('default_if_none', default_if_none, True)
|
register.filter(default_if_none)
|
||||||
register_filter('dictsort', dictsort, True)
|
register.filter(dictsort)
|
||||||
register_filter('dictsortreversed', dictsortreversed, True)
|
register.filter(dictsortreversed)
|
||||||
register_filter('divisibleby', divisibleby, True)
|
register.filter(divisibleby)
|
||||||
register_filter('escape', escape, False)
|
register.filter(escape)
|
||||||
register_filter('filesizeformat', filesizeformat, False)
|
register.filter(filesizeformat)
|
||||||
register_filter('first', first, False)
|
register.filter(first)
|
||||||
register_filter('fix_ampersands', fix_ampersands, False)
|
register.filter(fix_ampersands)
|
||||||
register_filter('floatformat', floatformat, False)
|
register.filter(floatformat)
|
||||||
register_filter('get_digit', get_digit, True)
|
register.filter(get_digit)
|
||||||
register_filter('join', join, True)
|
register.filter(join)
|
||||||
register_filter('length', length, False)
|
register.filter(length)
|
||||||
register_filter('length_is', length_is, True)
|
register.filter(length_is)
|
||||||
register_filter('linebreaks', linebreaks, False)
|
register.filter(linebreaks)
|
||||||
register_filter('linebreaksbr', linebreaksbr, False)
|
register.filter(linebreaksbr)
|
||||||
register_filter('linenumbers', linenumbers, False)
|
register.filter(linenumbers)
|
||||||
register_filter('ljust', ljust, True)
|
register.filter(ljust)
|
||||||
register_filter('lower', lower, False)
|
register.filter(lower)
|
||||||
register_filter('make_list', make_list, False)
|
register.filter(make_list)
|
||||||
register_filter('phone2numeric', phone2numeric, False)
|
register.filter(phone2numeric)
|
||||||
register_filter('pluralize', pluralize, False)
|
register.filter(pluralize)
|
||||||
register_filter('pprint', pprint, False)
|
register.filter(pprint)
|
||||||
register_filter('removetags', removetags, True)
|
register.filter(removetags)
|
||||||
register_filter('random', random, False)
|
register.filter(random)
|
||||||
register_filter('rjust', rjust, True)
|
register.filter(rjust)
|
||||||
register_filter('slice', slice_, True)
|
register.filter(slice_)
|
||||||
register_filter('slugify', slugify, False)
|
register.filter(slugify)
|
||||||
register_filter('stringformat', stringformat, True)
|
register.filter(stringformat)
|
||||||
register_filter('striptags', striptags, False)
|
register.filter(striptags)
|
||||||
register_filter('time', time, True)
|
register.filter(time)
|
||||||
register_filter('timesince', timesince, False)
|
register.filter(timesince)
|
||||||
register_filter('title', title, False)
|
register.filter(title)
|
||||||
register_filter('truncatewords', truncatewords, True)
|
register.filter(truncatewords)
|
||||||
register_filter('unordered_list', unordered_list, False)
|
register.filter(unordered_list)
|
||||||
register_filter('upper', upper, False)
|
register.filter(upper)
|
||||||
register_filter('urlencode', urlencode, False)
|
register.filter(urlencode)
|
||||||
register_filter('urlize', urlize, False)
|
register.filter(urlize)
|
||||||
register_filter('urlizetrunc', urlizetrunc, True)
|
register.filter(urlizetrunc)
|
||||||
register_filter('wordcount', wordcount, False)
|
register.filter(wordcount)
|
||||||
register_filter('wordwrap', wordwrap, True)
|
register.filter(wordwrap)
|
||||||
register_filter('yesno', yesno, True)
|
register.filter(yesno)
|
|
@ -1,9 +1,12 @@
|
||||||
"Default tags used by the template system, available to all templates."
|
"Default tags used by the template system, available to all templates."
|
||||||
|
|
||||||
from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, get_filters_from_token, registered_filters
|
from django.core.template import Node, NodeList, Template, Context, resolve_variable
|
||||||
from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, register_tag
|
from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
|
||||||
|
from django.core.template import get_library, Library, InvalidTemplateLibrary
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
register = Library()
|
||||||
|
|
||||||
class CommentNode(Node):
|
class CommentNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
return ''
|
return ''
|
||||||
|
@ -27,15 +30,13 @@ class DebugNode(Node):
|
||||||
return ''.join(output)
|
return ''.join(output)
|
||||||
|
|
||||||
class FilterNode(Node):
|
class FilterNode(Node):
|
||||||
def __init__(self, filters, nodelist):
|
def __init__(self, filter_expr, nodelist):
|
||||||
self.filters, self.nodelist = filters, nodelist
|
self.filter_expr, self.nodelist = filter_expr, nodelist
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
output = self.nodelist.render(context)
|
output = self.nodelist.render(context)
|
||||||
# apply filters
|
# apply filters
|
||||||
for f in self.filters:
|
return self.filter_expr.resolve(Context({'var': output}))
|
||||||
output = registered_filters[f[0]][0](output, f[1])
|
|
||||||
return output
|
|
||||||
|
|
||||||
class FirstOfNode(Node):
|
class FirstOfNode(Node):
|
||||||
def __init__(self, vars):
|
def __init__(self, vars):
|
||||||
|
@ -81,7 +82,7 @@ class ForNode(Node):
|
||||||
parentloop = {}
|
parentloop = {}
|
||||||
context.push()
|
context.push()
|
||||||
try:
|
try:
|
||||||
values = resolve_variable_with_filters(self.sequence, context)
|
values = self.sequence.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
values = []
|
values = []
|
||||||
if values is None:
|
if values is None:
|
||||||
|
@ -147,8 +148,8 @@ class IfEqualNode(Node):
|
||||||
return self.nodelist_false.render(context)
|
return self.nodelist_false.render(context)
|
||||||
|
|
||||||
class IfNode(Node):
|
class IfNode(Node):
|
||||||
def __init__(self, boolvars, nodelist_true, nodelist_false):
|
def __init__(self, bool_exprs, nodelist_true, nodelist_false):
|
||||||
self.boolvars = boolvars
|
self.bool_exprs = bool_exprs
|
||||||
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
|
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -169,9 +170,9 @@ class IfNode(Node):
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
for ifnot, boolvar in self.boolvars:
|
for ifnot, bool_expr in self.bool_exprs:
|
||||||
try:
|
try:
|
||||||
value = resolve_variable_with_filters(boolvar, context)
|
value = bool_expr.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
value = None
|
value = None
|
||||||
if (value and not ifnot) or (ifnot and not value):
|
if (value and not ifnot) or (ifnot and not value):
|
||||||
|
@ -179,19 +180,18 @@ class IfNode(Node):
|
||||||
return self.nodelist_false.render(context)
|
return self.nodelist_false.render(context)
|
||||||
|
|
||||||
class RegroupNode(Node):
|
class RegroupNode(Node):
|
||||||
def __init__(self, target_var, expression, var_name):
|
def __init__(self, target, expression, var_name):
|
||||||
self.target_var, self.expression = target_var, expression
|
self.target, self.expression = target, expression
|
||||||
self.var_name = var_name
|
self.var_name = var_name
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
obj_list = resolve_variable_with_filters(self.target_var, context)
|
obj_list = self.target.resolve(context)
|
||||||
if obj_list == '': # target_var wasn't found in context; fail silently
|
if obj_list == '': # target_var wasn't found in context; fail silently
|
||||||
context[self.var_name] = []
|
context[self.var_name] = []
|
||||||
return ''
|
return ''
|
||||||
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
|
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
|
||||||
for obj in obj_list:
|
for obj in obj_list:
|
||||||
grouper = resolve_variable_with_filters('var.%s' % self.expression, \
|
grouper = self.expression.resolve(Context({'var': obj}))
|
||||||
Context({'var': obj}))
|
|
||||||
# TODO: Is this a sensible way to determine equality?
|
# TODO: Is this a sensible way to determine equality?
|
||||||
if output and repr(output[-1]['grouper']) == repr(grouper):
|
if output and repr(output[-1]['grouper']) == repr(grouper):
|
||||||
output[-1]['list'].append(obj)
|
output[-1]['list'].append(obj)
|
||||||
|
@ -236,21 +236,7 @@ class SsiNode(Node):
|
||||||
return output
|
return output
|
||||||
|
|
||||||
class LoadNode(Node):
|
class LoadNode(Node):
|
||||||
def __init__(self, taglib):
|
|
||||||
self.taglib = taglib
|
|
||||||
|
|
||||||
def load_taglib(taglib):
|
|
||||||
mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', [''])
|
|
||||||
reload(mod)
|
|
||||||
return mod
|
|
||||||
load_taglib = staticmethod(load_taglib)
|
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
"Import the relevant module"
|
|
||||||
try:
|
|
||||||
self.__class__.load_taglib(self.taglib)
|
|
||||||
except ImportError:
|
|
||||||
pass # Fail silently for invalid loads.
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
class NowNode(Node):
|
class NowNode(Node):
|
||||||
|
@ -276,15 +262,15 @@ class TemplateTagNode(Node):
|
||||||
return self.mapping.get(self.tagtype, '')
|
return self.mapping.get(self.tagtype, '')
|
||||||
|
|
||||||
class WidthRatioNode(Node):
|
class WidthRatioNode(Node):
|
||||||
def __init__(self, val_var, max_var, max_width):
|
def __init__(self, val_expr, max_expr, max_width):
|
||||||
self.val_var = val_var
|
self.val_expr = val_expr
|
||||||
self.max_var = max_var
|
self.max_expr = max_expr
|
||||||
self.max_width = max_width
|
self.max_width = max_width
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
value = resolve_variable_with_filters(self.val_var, context)
|
value = self.val_expr.resolve(context)
|
||||||
maxvalue = resolve_variable_with_filters(self.max_var, context)
|
maxvalue = self.max_expr.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
return ''
|
return ''
|
||||||
try:
|
try:
|
||||||
|
@ -295,15 +281,18 @@ class WidthRatioNode(Node):
|
||||||
return ''
|
return ''
|
||||||
return str(int(round(ratio)))
|
return str(int(round(ratio)))
|
||||||
|
|
||||||
def do_comment(parser, token):
|
#@register.tag
|
||||||
|
def comment(parser, token):
|
||||||
"""
|
"""
|
||||||
Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
|
Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
|
||||||
"""
|
"""
|
||||||
nodelist = parser.parse(('endcomment',))
|
nodelist = parser.parse(('endcomment',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
return CommentNode()
|
return CommentNode()
|
||||||
|
comment = register.tag(comment)
|
||||||
|
|
||||||
def do_cycle(parser, token):
|
#@register.tag
|
||||||
|
def cycle(parser, token):
|
||||||
"""
|
"""
|
||||||
Cycle among the given strings each time this tag is encountered
|
Cycle among the given strings each time this tag is encountered
|
||||||
|
|
||||||
|
@ -369,11 +358,9 @@ def do_cycle(parser, token):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
|
raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
|
||||||
|
cycle = register.tag(cycle)
|
||||||
|
|
||||||
def do_debug(parser, token):
|
#@register.tag(name="filter")
|
||||||
"Print a whole load of debugging information, including the context and imported modules"
|
|
||||||
return DebugNode()
|
|
||||||
|
|
||||||
def do_filter(parser, token):
|
def do_filter(parser, token):
|
||||||
"""
|
"""
|
||||||
Filter the contents of the blog through variable filters.
|
Filter the contents of the blog through variable filters.
|
||||||
|
@ -388,12 +375,14 @@ def do_filter(parser, token):
|
||||||
{% endfilter %}
|
{% endfilter %}
|
||||||
"""
|
"""
|
||||||
_, rest = token.contents.split(None, 1)
|
_, rest = token.contents.split(None, 1)
|
||||||
_, filters = get_filters_from_token('var|%s' % rest)
|
filter_expr = parser.compile_filter("var|%s" % (rest))
|
||||||
nodelist = parser.parse(('endfilter',))
|
nodelist = parser.parse(('endfilter',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
return FilterNode(filters, nodelist)
|
return FilterNode(filter_expr, nodelist)
|
||||||
|
filter = register.tag("filter", do_filter)
|
||||||
|
|
||||||
def do_firstof(parser, token):
|
#@register.tag
|
||||||
|
def firstof(parser, token):
|
||||||
"""
|
"""
|
||||||
Outputs the first variable passed that is not False.
|
Outputs the first variable passed that is not False.
|
||||||
|
|
||||||
|
@ -419,8 +408,9 @@ def do_firstof(parser, token):
|
||||||
if len(bits) < 1:
|
if len(bits) < 1:
|
||||||
raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
|
raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
|
||||||
return FirstOfNode(bits)
|
return FirstOfNode(bits)
|
||||||
|
firstof = register.tag(firstof)
|
||||||
|
|
||||||
|
#@register.tag(name="for")
|
||||||
def do_for(parser, token):
|
def do_for(parser, token):
|
||||||
"""
|
"""
|
||||||
Loop over each item in an array.
|
Loop over each item in an array.
|
||||||
|
@ -462,11 +452,12 @@ def do_for(parser, token):
|
||||||
if bits[2] != 'in':
|
if bits[2] != 'in':
|
||||||
raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
|
raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
|
||||||
loopvar = bits[1]
|
loopvar = bits[1]
|
||||||
sequence = bits[3]
|
sequence = parser.compile_filter(bits[3])
|
||||||
reversed = (len(bits) == 5)
|
reversed = (len(bits) == 5)
|
||||||
nodelist_loop = parser.parse(('endfor',))
|
nodelist_loop = parser.parse(('endfor',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
return ForNode(loopvar, sequence, reversed, nodelist_loop)
|
return ForNode(loopvar, sequence, reversed, nodelist_loop)
|
||||||
|
do_for = register.tag("for", do_for)
|
||||||
|
|
||||||
def do_ifequal(parser, token, negate):
|
def do_ifequal(parser, token, negate):
|
||||||
"""
|
"""
|
||||||
|
@ -497,6 +488,17 @@ def do_ifequal(parser, token, negate):
|
||||||
nodelist_false = NodeList()
|
nodelist_false = NodeList()
|
||||||
return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
|
return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
|
||||||
|
|
||||||
|
#@register.tag
|
||||||
|
def ifequal(parser, token):
|
||||||
|
return do_ifequal(parser, token, False)
|
||||||
|
ifequal = register.tag(ifequal)
|
||||||
|
|
||||||
|
#@register.tag
|
||||||
|
def ifnotequal(parser, token):
|
||||||
|
return do_ifequal(parser, token, True)
|
||||||
|
ifnotequal = register.tag(ifnotequal)
|
||||||
|
|
||||||
|
#@register.tag(name="if")
|
||||||
def do_if(parser, token):
|
def do_if(parser, token):
|
||||||
"""
|
"""
|
||||||
The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
|
The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
|
||||||
|
@ -554,9 +556,9 @@ def do_if(parser, token):
|
||||||
not_, boolvar = boolpair.split()
|
not_, boolvar = boolpair.split()
|
||||||
if not_ != 'not':
|
if not_ != 'not':
|
||||||
raise TemplateSyntaxError, "Expected 'not' in if statement"
|
raise TemplateSyntaxError, "Expected 'not' in if statement"
|
||||||
boolvars.append((True, boolvar))
|
boolvars.append((True, parser.compile_filter(boolvar)))
|
||||||
else:
|
else:
|
||||||
boolvars.append((False, boolpair))
|
boolvars.append((False, parser.compile_filter(boolpair)))
|
||||||
nodelist_true = parser.parse(('else', 'endif'))
|
nodelist_true = parser.parse(('else', 'endif'))
|
||||||
token = parser.next_token()
|
token = parser.next_token()
|
||||||
if token.contents == 'else':
|
if token.contents == 'else':
|
||||||
|
@ -565,8 +567,10 @@ def do_if(parser, token):
|
||||||
else:
|
else:
|
||||||
nodelist_false = NodeList()
|
nodelist_false = NodeList()
|
||||||
return IfNode(boolvars, nodelist_true, nodelist_false)
|
return IfNode(boolvars, nodelist_true, nodelist_false)
|
||||||
|
do_if = register.tag("if", do_if)
|
||||||
|
|
||||||
def do_ifchanged(parser, token):
|
#@register.tag
|
||||||
|
def ifchanged(parser, token):
|
||||||
"""
|
"""
|
||||||
Check if a value has changed from the last iteration of a loop.
|
Check if a value has changed from the last iteration of a loop.
|
||||||
|
|
||||||
|
@ -587,8 +591,10 @@ def do_ifchanged(parser, token):
|
||||||
nodelist = parser.parse(('endifchanged',))
|
nodelist = parser.parse(('endifchanged',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
return IfChangedNode(nodelist)
|
return IfChangedNode(nodelist)
|
||||||
|
ifchanged = register.tag(ifchanged)
|
||||||
|
|
||||||
def do_ssi(parser, token):
|
#@register.tag
|
||||||
|
def ssi(parser, token):
|
||||||
"""
|
"""
|
||||||
Output the contents of a given file into the page.
|
Output the contents of a given file into the page.
|
||||||
|
|
||||||
|
@ -613,8 +619,10 @@ def do_ssi(parser, token):
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
|
raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
|
||||||
return SsiNode(bits[1], parsed)
|
return SsiNode(bits[1], parsed)
|
||||||
|
ssi = register.tag(ssi)
|
||||||
|
|
||||||
def do_load(parser, token):
|
#@register.tag
|
||||||
|
def load(parser, token):
|
||||||
"""
|
"""
|
||||||
Load a custom template tag set.
|
Load a custom template tag set.
|
||||||
|
|
||||||
|
@ -623,17 +631,18 @@ def do_load(parser, token):
|
||||||
{% load news.photos %}
|
{% load news.photos %}
|
||||||
"""
|
"""
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
if len(bits) != 2:
|
for taglib in bits[1:]:
|
||||||
raise TemplateSyntaxError, "'load' statement takes one argument"
|
# add the library to the parser
|
||||||
taglib = bits[1]
|
try:
|
||||||
# check at compile time that the module can be imported
|
lib = get_library("django.templatetags.%s" % taglib.split('.')[-1])
|
||||||
try:
|
parser.add_library(lib)
|
||||||
LoadNode.load_taglib(taglib)
|
except InvalidTemplateLibrary, e:
|
||||||
except ImportError, e:
|
raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
|
||||||
raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
|
return LoadNode()
|
||||||
return LoadNode(taglib)
|
load = register.tag(load)
|
||||||
|
|
||||||
def do_now(parser, token):
|
#@register.tag
|
||||||
|
def now(parser, token):
|
||||||
"""
|
"""
|
||||||
Display the date, formatted according to the given string.
|
Display the date, formatted according to the given string.
|
||||||
|
|
||||||
|
@ -649,8 +658,10 @@ def do_now(parser, token):
|
||||||
raise TemplateSyntaxError, "'now' statement takes one argument"
|
raise TemplateSyntaxError, "'now' statement takes one argument"
|
||||||
format_string = bits[1]
|
format_string = bits[1]
|
||||||
return NowNode(format_string)
|
return NowNode(format_string)
|
||||||
|
now = register.tag(now)
|
||||||
|
|
||||||
def do_regroup(parser, token):
|
#@register.tag
|
||||||
|
def regroup(parser, token):
|
||||||
"""
|
"""
|
||||||
Regroup a list of alike objects by a common attribute.
|
Regroup a list of alike objects by a common attribute.
|
||||||
|
|
||||||
|
@ -699,17 +710,21 @@ def do_regroup(parser, token):
|
||||||
firstbits = token.contents.split(None, 3)
|
firstbits = token.contents.split(None, 3)
|
||||||
if len(firstbits) != 4:
|
if len(firstbits) != 4:
|
||||||
raise TemplateSyntaxError, "'regroup' tag takes five arguments"
|
raise TemplateSyntaxError, "'regroup' tag takes five arguments"
|
||||||
target_var = firstbits[1]
|
target = parser.compile_filter(firstbits[1])
|
||||||
if firstbits[2] != 'by':
|
if firstbits[2] != 'by':
|
||||||
raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"
|
raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"
|
||||||
lastbits_reversed = firstbits[3][::-1].split(None, 2)
|
lastbits_reversed = firstbits[3][::-1].split(None, 2)
|
||||||
if lastbits_reversed[1][::-1] != 'as':
|
if lastbits_reversed[1][::-1] != 'as':
|
||||||
raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
|
raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
|
||||||
expression = lastbits_reversed[2][::-1]
|
|
||||||
var_name = lastbits_reversed[0][::-1]
|
|
||||||
return RegroupNode(target_var, expression, var_name)
|
|
||||||
|
|
||||||
def do_templatetag(parser, token):
|
expression = parser.compile_filters('var.%s' % lastbits_reversed[2][::-1])
|
||||||
|
|
||||||
|
var_name = lastbits_reversed[0][::-1]
|
||||||
|
return RegroupNode(target, expression, var_name)
|
||||||
|
regroup = register.tag(regroup)
|
||||||
|
|
||||||
|
#@register.tag
|
||||||
|
def templatetag(parser, token):
|
||||||
"""
|
"""
|
||||||
Output one of the bits used to compose template tags.
|
Output one of the bits used to compose template tags.
|
||||||
|
|
||||||
|
@ -735,8 +750,10 @@ def do_templatetag(parser, token):
|
||||||
raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
|
raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
|
||||||
(tag, TemplateTagNode.mapping.keys())
|
(tag, TemplateTagNode.mapping.keys())
|
||||||
return TemplateTagNode(tag)
|
return TemplateTagNode(tag)
|
||||||
|
templatetag = register.tag(templatetag)
|
||||||
|
|
||||||
def do_widthratio(parser, token):
|
@register.tag
|
||||||
|
def widthratio(parser, token):
|
||||||
"""
|
"""
|
||||||
For creating bar charts and such, this tag calculates the ratio of a given
|
For creating bar charts and such, this tag calculates the ratio of a given
|
||||||
value to a maximum value, and then applies that ratio to a constant.
|
value to a maximum value, and then applies that ratio to a constant.
|
||||||
|
@ -752,26 +769,11 @@ def do_widthratio(parser, token):
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
if len(bits) != 4:
|
if len(bits) != 4:
|
||||||
raise TemplateSyntaxError("widthratio takes three arguments")
|
raise TemplateSyntaxError("widthratio takes three arguments")
|
||||||
tag, this_value_var, max_value_var, max_width = bits
|
tag, this_value_expr, max_value_expr, max_width = bits
|
||||||
try:
|
try:
|
||||||
max_width = int(max_width)
|
max_width = int(max_width)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise TemplateSyntaxError("widthratio final argument must be an integer")
|
raise TemplateSyntaxError("widthratio final argument must be an integer")
|
||||||
return WidthRatioNode(this_value_var, max_value_var, max_width)
|
return WidthRatioNode(parser.compile_filter(this_value_expr),
|
||||||
|
parser.compile_filter(max_value_expr), max_width)
|
||||||
register_tag('comment', do_comment)
|
widthratio = register.tag(widthratio)
|
||||||
register_tag('cycle', do_cycle)
|
|
||||||
register_tag('debug', do_debug)
|
|
||||||
register_tag('filter', do_filter)
|
|
||||||
register_tag('firstof', do_firstof)
|
|
||||||
register_tag('for', do_for)
|
|
||||||
register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False))
|
|
||||||
register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True))
|
|
||||||
register_tag('if', do_if)
|
|
||||||
register_tag('ifchanged', do_ifchanged)
|
|
||||||
register_tag('regroup', do_regroup)
|
|
||||||
register_tag('ssi', do_ssi)
|
|
||||||
register_tag('load', do_load)
|
|
||||||
register_tag('now', do_now)
|
|
||||||
register_tag('templatetag', do_templatetag)
|
|
||||||
register_tag('widthratio', do_widthratio)
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
# installed, because pkg_resources is necessary to read eggs.
|
# installed, because pkg_resources is necessary to read eggs.
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.template import Origin, StringOrigin, Template, Context, Node, TemplateDoesNotExist, TemplateSyntaxError, resolve_variable_with_filters, resolve_variable, register_tag
|
from django.core.template import Origin, StringOrigin, Template, TemplateDoesNotExist, add_to_builtins
|
||||||
from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG
|
from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG
|
||||||
|
|
||||||
template_source_loaders = []
|
template_source_loaders = []
|
||||||
|
@ -68,9 +68,6 @@ def find_template_source(name, dirs=None):
|
||||||
def load_template_source(name, dirs=None):
|
def load_template_source(name, dirs=None):
|
||||||
find_template_source(name, dirs)[0]
|
find_template_source(name, dirs)[0]
|
||||||
|
|
||||||
class ExtendsError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_template(template_name):
|
def get_template(template_name):
|
||||||
"""
|
"""
|
||||||
Returns a compiled Template object for the given template name,
|
Returns a compiled Template object for the given template name,
|
||||||
|
@ -113,166 +110,4 @@ def select_template(template_name_list):
|
||||||
# If we get here, none of the templates could be loaded
|
# If we get here, none of the templates could be loaded
|
||||||
raise TemplateDoesNotExist, ', '.join(template_name_list)
|
raise TemplateDoesNotExist, ', '.join(template_name_list)
|
||||||
|
|
||||||
class BlockNode(Node):
|
add_to_builtins('django.core.template.loader_tags')
|
||||||
def __init__(self, name, nodelist, parent=None):
|
|
||||||
self.name, self.nodelist, self.parent = name, nodelist, parent
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
context.push()
|
|
||||||
# Save context in case of block.super().
|
|
||||||
self.context = context
|
|
||||||
context['block'] = self
|
|
||||||
result = self.nodelist.render(context)
|
|
||||||
context.pop()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def super(self):
|
|
||||||
if self.parent:
|
|
||||||
return self.parent.render(self.context)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def add_parent(self, nodelist):
|
|
||||||
if self.parent:
|
|
||||||
self.parent.add_parent(nodelist)
|
|
||||||
else:
|
|
||||||
self.parent = BlockNode(self.name, nodelist)
|
|
||||||
|
|
||||||
class ExtendsNode(Node):
|
|
||||||
def __init__(self, nodelist, parent_name, parent_name_var, template_dirs=None):
|
|
||||||
self.nodelist = nodelist
|
|
||||||
self.parent_name, self.parent_name_var = parent_name, parent_name_var
|
|
||||||
self.template_dirs = template_dirs
|
|
||||||
|
|
||||||
def get_parent(self, context):
|
|
||||||
if self.parent_name_var:
|
|
||||||
self.parent_name = resolve_variable_with_filters(self.parent_name_var, context)
|
|
||||||
parent = self.parent_name
|
|
||||||
if not parent:
|
|
||||||
error_msg = "Invalid template name in 'extends' tag: %r." % parent
|
|
||||||
if self.parent_name_var:
|
|
||||||
error_msg += " Got this from the %r variable." % self.parent_name_var
|
|
||||||
raise TemplateSyntaxError, error_msg
|
|
||||||
try:
|
|
||||||
return get_template_from_string(*find_template_source(parent, self.template_dirs))
|
|
||||||
except TemplateDoesNotExist:
|
|
||||||
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
compiled_parent = self.get_parent(context)
|
|
||||||
parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
|
|
||||||
parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
|
|
||||||
for block_node in self.nodelist.get_nodes_by_type(BlockNode):
|
|
||||||
# Check for a BlockNode with this node's name, and replace it if found.
|
|
||||||
try:
|
|
||||||
parent_block = parent_blocks[block_node.name]
|
|
||||||
except KeyError:
|
|
||||||
# This BlockNode wasn't found in the parent template, but the
|
|
||||||
# parent block might be defined in the parent's *parent*, so we
|
|
||||||
# add this BlockNode to the parent's ExtendsNode nodelist, so
|
|
||||||
# it'll be checked when the parent node's render() is called.
|
|
||||||
if parent_is_child:
|
|
||||||
compiled_parent.nodelist[0].nodelist.append(block_node)
|
|
||||||
else:
|
|
||||||
# Keep any existing parents and add a new one. Used by BlockNode.
|
|
||||||
parent_block.parent = block_node.parent
|
|
||||||
parent_block.add_parent(parent_block.nodelist)
|
|
||||||
parent_block.nodelist = block_node.nodelist
|
|
||||||
return compiled_parent.render(context)
|
|
||||||
|
|
||||||
class ConstantIncludeNode(Node):
|
|
||||||
def __init__(self, template_path):
|
|
||||||
try:
|
|
||||||
t = get_template(template_path)
|
|
||||||
self.template = t
|
|
||||||
except Exception, e:
|
|
||||||
if TEMPLATE_DEBUG:
|
|
||||||
raise
|
|
||||||
self.template = None
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
if self.template:
|
|
||||||
return self.template.render(context)
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
class IncludeNode(Node):
|
|
||||||
def __init__(self, template_name):
|
|
||||||
self.template_name = template_name
|
|
||||||
|
|
||||||
def render(self, context):
|
|
||||||
try:
|
|
||||||
template_name = resolve_variable(self.template_name, context)
|
|
||||||
t = get_template(template_name)
|
|
||||||
return t.render(context)
|
|
||||||
except TemplateSyntaxError, e:
|
|
||||||
if TEMPLATE_DEBUG:
|
|
||||||
raise
|
|
||||||
return ''
|
|
||||||
except:
|
|
||||||
return '' # Fail silently for invalid included templates.
|
|
||||||
|
|
||||||
def do_block(parser, token):
|
|
||||||
"""
|
|
||||||
Define a block that can be overridden by child templates.
|
|
||||||
"""
|
|
||||||
bits = token.contents.split()
|
|
||||||
if len(bits) != 2:
|
|
||||||
raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
|
|
||||||
block_name = bits[1]
|
|
||||||
# Keep track of the names of BlockNodes found in this template, so we can
|
|
||||||
# check for duplication.
|
|
||||||
try:
|
|
||||||
if block_name in parser.__loaded_blocks:
|
|
||||||
raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
|
|
||||||
parser.__loaded_blocks.append(block_name)
|
|
||||||
except AttributeError: # parser._loaded_blocks isn't a list yet
|
|
||||||
parser.__loaded_blocks = [block_name]
|
|
||||||
nodelist = parser.parse(('endblock',))
|
|
||||||
parser.delete_first_token()
|
|
||||||
return BlockNode(block_name, nodelist)
|
|
||||||
|
|
||||||
def do_extends(parser, token):
|
|
||||||
"""
|
|
||||||
Signal that this template extends a parent template.
|
|
||||||
|
|
||||||
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
|
|
||||||
uses the literal value "base" as the name of the parent template to extend,
|
|
||||||
or ``{% extends variable %}`` uses the value of ``variable`` as the name
|
|
||||||
of the parent template to extend.
|
|
||||||
"""
|
|
||||||
bits = token.contents.split()
|
|
||||||
if len(bits) != 2:
|
|
||||||
raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
|
|
||||||
parent_name, parent_name_var = None, None
|
|
||||||
if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
|
|
||||||
parent_name = bits[1][1:-1]
|
|
||||||
else:
|
|
||||||
parent_name_var = bits[1]
|
|
||||||
nodelist = parser.parse()
|
|
||||||
if nodelist.get_nodes_by_type(ExtendsNode):
|
|
||||||
raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
|
|
||||||
return ExtendsNode(nodelist, parent_name, parent_name_var)
|
|
||||||
|
|
||||||
def do_include(parser, token):
|
|
||||||
"""
|
|
||||||
Loads a template and renders it with the current context.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
{% include "foo/some_include" %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
bits = token.contents.split()
|
|
||||||
if len(bits) != 2:
|
|
||||||
raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
|
|
||||||
path = bits[1]
|
|
||||||
if path[0] in ('"', "'") and path[-1] == path[0]:
|
|
||||||
return ConstantIncludeNode(path[1:-1])
|
|
||||||
return IncludeNode(bits[1])
|
|
||||||
|
|
||||||
register_tag('block', do_block)
|
|
||||||
register_tag('extends', do_extends)
|
|
||||||
register_tag('include', do_include)
|
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
from django.core.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
|
||||||
|
from django.core.template import Library, Context, Node
|
||||||
|
from django.core.template.loader import get_template, get_template_from_string, find_template_source
|
||||||
|
from django.conf.settings import TEMPLATE_DEBUG
|
||||||
|
register = Library()
|
||||||
|
|
||||||
|
class ExtendsError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BlockNode(Node):
|
||||||
|
def __init__(self, name, nodelist, parent=None):
|
||||||
|
self.name, self.nodelist, self.parent = name, nodelist, parent
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
context.push()
|
||||||
|
# Save context in case of block.super().
|
||||||
|
self.context = context
|
||||||
|
context['block'] = self
|
||||||
|
result = self.nodelist.render(context)
|
||||||
|
context.pop()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def super(self):
|
||||||
|
if self.parent:
|
||||||
|
return self.parent.render(self.context)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def add_parent(self, nodelist):
|
||||||
|
if self.parent:
|
||||||
|
self.parent.add_parent(nodelist)
|
||||||
|
else:
|
||||||
|
self.parent = BlockNode(self.name, nodelist)
|
||||||
|
|
||||||
|
class ExtendsNode(Node):
|
||||||
|
def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
|
||||||
|
self.nodelist = nodelist
|
||||||
|
self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
|
||||||
|
self.template_dirs = template_dirs
|
||||||
|
|
||||||
|
def get_parent(self, context):
|
||||||
|
if self.parent_name_expr:
|
||||||
|
self.parent_name = self.parent_name_expr.resolve(context)
|
||||||
|
parent = self.parent_name
|
||||||
|
if not parent:
|
||||||
|
error_msg = "Invalid template name in 'extends' tag: %r." % parent
|
||||||
|
if self.parent_name_expr:
|
||||||
|
error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
|
||||||
|
raise TemplateSyntaxError, error_msg
|
||||||
|
try:
|
||||||
|
return get_template_from_string(*find_template_source(parent, self.template_dirs))
|
||||||
|
except TemplateDoesNotExist:
|
||||||
|
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
compiled_parent = self.get_parent(context)
|
||||||
|
parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
|
||||||
|
parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
|
||||||
|
for block_node in self.nodelist.get_nodes_by_type(BlockNode):
|
||||||
|
# Check for a BlockNode with this node's name, and replace it if found.
|
||||||
|
try:
|
||||||
|
parent_block = parent_blocks[block_node.name]
|
||||||
|
except KeyError:
|
||||||
|
# This BlockNode wasn't found in the parent template, but the
|
||||||
|
# parent block might be defined in the parent's *parent*, so we
|
||||||
|
# add this BlockNode to the parent's ExtendsNode nodelist, so
|
||||||
|
# it'll be checked when the parent node's render() is called.
|
||||||
|
if parent_is_child:
|
||||||
|
compiled_parent.nodelist[0].nodelist.append(block_node)
|
||||||
|
else:
|
||||||
|
# Keep any existing parents and add a new one. Used by BlockNode.
|
||||||
|
parent_block.parent = block_node.parent
|
||||||
|
parent_block.add_parent(parent_block.nodelist)
|
||||||
|
parent_block.nodelist = block_node.nodelist
|
||||||
|
return compiled_parent.render(context)
|
||||||
|
|
||||||
|
class ConstantIncludeNode(Node):
|
||||||
|
def __init__(self, template_path):
|
||||||
|
try:
|
||||||
|
t = get_template(template_path)
|
||||||
|
self.template = t
|
||||||
|
except:
|
||||||
|
if TEMPLATE_DEBUG:
|
||||||
|
pass
|
||||||
|
# raise
|
||||||
|
self.template = None
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
if self.template:
|
||||||
|
return self.template.render(context)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
class IncludeNode(Node):
|
||||||
|
def __init__(self, template_name):
|
||||||
|
self.template_name = template_name
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
try:
|
||||||
|
template_name = resolve_variable(self.template_name, context)
|
||||||
|
t = get_template(template_name)
|
||||||
|
return t.render(context)
|
||||||
|
except TemplateSyntaxError, e:
|
||||||
|
if TEMPLATE_DEBUG:
|
||||||
|
raise
|
||||||
|
return ''
|
||||||
|
except:
|
||||||
|
return '' # Fail silently for invalid included templates.
|
||||||
|
|
||||||
|
def do_block(parser, token):
|
||||||
|
"""
|
||||||
|
Define a block that can be overridden by child templates.
|
||||||
|
"""
|
||||||
|
bits = token.contents.split()
|
||||||
|
if len(bits) != 2:
|
||||||
|
raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
|
||||||
|
block_name = bits[1]
|
||||||
|
# Keep track of the names of BlockNodes found in this template, so we can
|
||||||
|
# check for duplication.
|
||||||
|
try:
|
||||||
|
if block_name in parser.__loaded_blocks:
|
||||||
|
raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
|
||||||
|
parser.__loaded_blocks.append(block_name)
|
||||||
|
except AttributeError: # parser._loaded_blocks isn't a list yet
|
||||||
|
parser.__loaded_blocks = [block_name]
|
||||||
|
nodelist = parser.parse(('endblock',))
|
||||||
|
parser.delete_first_token()
|
||||||
|
return BlockNode(block_name, nodelist)
|
||||||
|
|
||||||
|
def do_extends(parser, token):
|
||||||
|
"""
|
||||||
|
Signal that this template extends a parent template.
|
||||||
|
|
||||||
|
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
|
||||||
|
uses the literal value "base" as the name of the parent template to extend,
|
||||||
|
or ``{% extends variable %}`` uses the value of ``variable`` as the name
|
||||||
|
of the parent template to extend.
|
||||||
|
"""
|
||||||
|
bits = token.contents.split()
|
||||||
|
if len(bits) != 2:
|
||||||
|
raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
|
||||||
|
parent_name, parent_name_expr = None, None
|
||||||
|
if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
|
||||||
|
parent_name = bits[1][1:-1]
|
||||||
|
else:
|
||||||
|
parent_name_expr = parser.compile_filter(bits[1])
|
||||||
|
nodelist = parser.parse()
|
||||||
|
if nodelist.get_nodes_by_type(ExtendsNode):
|
||||||
|
raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
|
||||||
|
return ExtendsNode(nodelist, parent_name, parent_name_expr)
|
||||||
|
|
||||||
|
def do_include(parser, token):
|
||||||
|
"""
|
||||||
|
Loads a template and renders it with the current context.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
{% include "foo/some_include" %}
|
||||||
|
"""
|
||||||
|
bits = token.contents.split()
|
||||||
|
if len(bits) != 2:
|
||||||
|
raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
|
||||||
|
path = bits[1]
|
||||||
|
if path[0] in ('"', "'") and path[-1] == path[0]:
|
||||||
|
return ConstantIncludeNode(path[1:-1])
|
||||||
|
return IncludeNode(bits[1])
|
||||||
|
|
||||||
|
register.tag('block', do_block)
|
||||||
|
register.tag('extends', do_extends)
|
||||||
|
register.tag('include', do_include)
|
|
@ -1,9 +1,11 @@
|
||||||
from django.core.template import Node, NodeList, Template, Context, resolve_variable, resolve_variable_with_filters, registered_filters
|
from django.core.template import Node, NodeList, Template, Context, resolve_variable
|
||||||
from django.core.template import TemplateSyntaxError, register_tag, TokenParser
|
from django.core.template import TemplateSyntaxError, TokenParser, Library
|
||||||
from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
|
from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
import re, sys
|
import re, sys
|
||||||
|
|
||||||
|
register = Library()
|
||||||
|
|
||||||
class GetAvailableLanguagesNode(Node):
|
class GetAvailableLanguagesNode(Node):
|
||||||
def __init__(self, variable):
|
def __init__(self, variable):
|
||||||
self.variable = variable
|
self.variable = variable
|
||||||
|
@ -53,10 +55,10 @@ class BlockTranslateNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
context.push()
|
context.push()
|
||||||
for var,val in self.extra_context.items():
|
for var,val in self.extra_context.items():
|
||||||
context[var] = resolve_variable_with_filters(val, context)
|
context[var] = val.resolve(context)
|
||||||
singular = self.render_token_list(self.singular)
|
singular = self.render_token_list(self.singular)
|
||||||
if self.plural and self.countervar and self.counter:
|
if self.plural and self.countervar and self.counter:
|
||||||
count = resolve_variable_with_filters(self.counter, context)
|
count = self.counter.resolve(context)
|
||||||
context[self.countervar] = count
|
context[self.countervar] = count
|
||||||
plural = self.render_token_list(self.plural)
|
plural = self.render_token_list(self.plural)
|
||||||
result = translation.ngettext(singular, plural, count) % context
|
result = translation.ngettext(singular, plural, count) % context
|
||||||
|
@ -179,9 +181,9 @@ def do_block_translate(parser, token):
|
||||||
value = self.value()
|
value = self.value()
|
||||||
if self.tag() != 'as':
|
if self.tag() != 'as':
|
||||||
raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
|
raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
|
||||||
extra_context[self.tag()] = value
|
extra_context[self.tag()] = parser.compile_filter(value)
|
||||||
elif tag == 'count':
|
elif tag == 'count':
|
||||||
counter = self.value()
|
counter = parser.compile_filter(self.value())
|
||||||
if self.tag() != 'as':
|
if self.tag() != 'as':
|
||||||
raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
|
raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
|
||||||
countervar = self.tag()
|
countervar = self.tag()
|
||||||
|
@ -213,7 +215,7 @@ def do_block_translate(parser, token):
|
||||||
|
|
||||||
return BlockTranslateNode(extra_context, singular, plural, countervar, counter)
|
return BlockTranslateNode(extra_context, singular, plural, countervar, counter)
|
||||||
|
|
||||||
register_tag('get_available_languages', do_get_available_languages)
|
register.tag('get_available_languages', do_get_available_languages)
|
||||||
register_tag('get_current_language', do_get_current_language)
|
register.tag('get_current_language', do_get_current_language)
|
||||||
register_tag('trans', do_translate)
|
register.tag('trans', do_translate)
|
||||||
register_tag('blocktrans', do_block_translate)
|
register.tag('blocktrans', do_block_translate)
|
||||||
|
|
|
@ -278,6 +278,11 @@ In the above, the ``load`` tag loads the ``comments`` tag library, which then
|
||||||
makes the ``comment_form`` tag available for use. Consult the documentation
|
makes the ``comment_form`` tag available for use. Consult the documentation
|
||||||
area in your admin to find the list of custom libraries in your installation.
|
area in your admin to find the list of custom libraries in your installation.
|
||||||
|
|
||||||
|
**New in Django development version:** The ``{% load %}`` tag can take multiple
|
||||||
|
library names, separated by spaces. Example::
|
||||||
|
|
||||||
|
{% load comments i18n %}
|
||||||
|
|
||||||
Built-in tag and filter reference
|
Built-in tag and filter reference
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
|
|
@ -444,6 +444,15 @@ the given Python module name, not the name of the app.
|
||||||
Once you've created that Python module, you'll just have to write a bit of
|
Once you've created that Python module, you'll just have to write a bit of
|
||||||
Python code, depending on whether you're writing filters or tags.
|
Python code, depending on whether you're writing filters or tags.
|
||||||
|
|
||||||
|
To be a valid tag library, the module contain a module-level variable that is a
|
||||||
|
``template.Library`` instance, in which all the tags and filters are
|
||||||
|
registered. So, near the top of your module, put the following::
|
||||||
|
|
||||||
|
from django.core import template
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
Convention is to call this instance ``register``.
|
||||||
|
|
||||||
.. admonition:: Behind the scenes
|
.. admonition:: Behind the scenes
|
||||||
|
|
||||||
For a ton of examples, read the source code for Django's default filters
|
For a ton of examples, read the source code for Django's default filters
|
||||||
|
@ -453,10 +462,16 @@ Python code, depending on whether you're writing filters or tags.
|
||||||
Writing custom template filters
|
Writing custom template filters
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Custom filters are just Python functions that take two arguments:
|
**This section applies to the Django development version.**
|
||||||
|
|
||||||
* The value of the variable (input) -- not necessarily a string
|
Custom filters are just Python functions that take one or two arguments:
|
||||||
* The value of the argument -- always a string
|
|
||||||
|
* The value of the variable (input) -- not necessarily a string.
|
||||||
|
* The value of the argument -- this can have a default value, or be left
|
||||||
|
out altogether.
|
||||||
|
|
||||||
|
For example, in the filter ``{{ var|foo:"bar" }}``, the filter ``foo`` would be
|
||||||
|
passed the variable ``var`` and the argument ``"bar"``.
|
||||||
|
|
||||||
Filter functions should always return something. They shouldn't raise
|
Filter functions should always return something. They shouldn't raise
|
||||||
exceptions. They should fail silently. In case of error, they should return
|
exceptions. They should fail silently. In case of error, they should return
|
||||||
|
@ -468,36 +483,48 @@ Here's an example filter definition::
|
||||||
"Removes all values of arg from the given string"
|
"Removes all values of arg from the given string"
|
||||||
return value.replace(arg, '')
|
return value.replace(arg, '')
|
||||||
|
|
||||||
Most filters don't take arguments. For filters that don't take arguments, the
|
And here's an example of how that filter would be used::
|
||||||
convention is to use a single underscore as the second argument to the filter
|
|
||||||
definition. Example::
|
|
||||||
|
|
||||||
def lower(value, _):
|
{{ somevariable|cut:"0" }}
|
||||||
|
|
||||||
|
Most filters don't take arguments. In this case, just leave the argument out of
|
||||||
|
your function. Example::
|
||||||
|
|
||||||
|
def lower(value): # Only one argument.
|
||||||
"Converts a string into all lowercase"
|
"Converts a string into all lowercase"
|
||||||
return value.lower()
|
return value.lower()
|
||||||
|
|
||||||
When you've written your filter definition, you need to register it, to make it
|
When you've written your filter definition, you need to register it with
|
||||||
available to Django's template language::
|
your ``Library`` instance, to make it available to Django's template language::
|
||||||
|
|
||||||
from django.core import template
|
register.filter('cut', cut)
|
||||||
template.register_filter('cut', cut, True)
|
register.filter('lower', lower)
|
||||||
template.register_filter('lower', lower, False)
|
|
||||||
|
|
||||||
``register_filter`` takes three arguments:
|
The ``Library.filter()`` method takes two arguments:
|
||||||
|
|
||||||
1. The name of the filter -- a string.
|
1. The name of the filter -- a string.
|
||||||
2. The compilation function -- a Python function (not the name of the
|
2. The compilation function -- a Python function (not the name of the
|
||||||
function as a string).
|
function as a string).
|
||||||
3. A boolean, designating whether the filter requires an argument. This
|
|
||||||
tells Django's template parser whether to throw ``TemplateSyntaxError``
|
|
||||||
when filter arguments are given (or missing).
|
|
||||||
|
|
||||||
The convention is to put all ``register_filter`` calls at the bottom of your
|
If you're using Python 2.4 or above, you can use ``register.filter()`` as a
|
||||||
template-library module.
|
decorator instead::
|
||||||
|
|
||||||
|
@register.filter(name='cut')
|
||||||
|
def cut(value, arg):
|
||||||
|
return value.replace(arg, '')
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def lower(value):
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
|
If you leave off the ``name`` argument, as in the second example above, Django
|
||||||
|
will use the function's name as the filter name.
|
||||||
|
|
||||||
Writing custom template tags
|
Writing custom template tags
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
**This section applies to the Django development version.**
|
||||||
|
|
||||||
Tags are more complex than filters, because tags can do anything.
|
Tags are more complex than filters, because tags can do anything.
|
||||||
|
|
||||||
A quick overview
|
A quick overview
|
||||||
|
@ -525,8 +552,6 @@ For each template tag the template parser encounters, it calls a Python
|
||||||
function with the tag contents and the parser object itself. This function is
|
function with the tag contents and the parser object itself. This function is
|
||||||
responsible for returning a ``Node`` instance based on the contents of the tag.
|
responsible for returning a ``Node`` instance based on the contents of the tag.
|
||||||
|
|
||||||
By convention, the name of each compilation function should start with ``do_``.
|
|
||||||
|
|
||||||
For example, let's write a template tag, ``{% current_time %}``, that displays
|
For example, let's write a template tag, ``{% current_time %}``, that displays
|
||||||
the current date/time, formatted according to a parameter given in the tag, in
|
the current date/time, formatted according to a parameter given in the tag, in
|
||||||
`strftime syntax`_. It's a good idea to decide the tag syntax before anything
|
`strftime syntax`_. It's a good idea to decide the tag syntax before anything
|
||||||
|
@ -612,17 +637,32 @@ without having to be parsed multiple times.
|
||||||
Registering the tag
|
Registering the tag
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Finally, use a ``register_tag`` call, as in ``register_filter`` above. Example::
|
Finally, register the tag with your module's ``Library`` instance, as explained
|
||||||
|
in "Writing custom template filters" above. Example::
|
||||||
|
|
||||||
from django.core import template
|
register.tag('current_time', do_current_time)
|
||||||
template.register_tag('current_time', do_current_time)
|
|
||||||
|
|
||||||
``register_tag`` takes two arguments:
|
The ``tag()`` method takes two arguments:
|
||||||
|
|
||||||
1. The name of the template tag -- a string.
|
1. The name of the template tag -- a string. If this is left out, the
|
||||||
|
name of the compilation function will be used.
|
||||||
2. The compilation function -- a Python function (not the name of the
|
2. The compilation function -- a Python function (not the name of the
|
||||||
function as a string).
|
function as a string).
|
||||||
|
|
||||||
|
As with filter registration, it is also possible to use this as a decorator, in
|
||||||
|
Python 2.4 and above:
|
||||||
|
|
||||||
|
@register.tag(name="current_time")
|
||||||
|
def do_current_time(parser, token):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
@register.tag
|
||||||
|
def shout(parser, token):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
If you leave off the ``name`` argument, as in the second example above, Django
|
||||||
|
will use the function's name as the tag name.
|
||||||
|
|
||||||
Setting a variable in the context
|
Setting a variable in the context
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
"""
|
"""
|
||||||
>>> floatformat(7.7, None)
|
>>> floatformat(7.7)
|
||||||
'7.7'
|
'7.7'
|
||||||
>>> floatformat(7.0, None)
|
>>> floatformat(7.0)
|
||||||
'7'
|
'7'
|
||||||
>>> floatformat(0.7, None)
|
>>> floatformat(0.7)
|
||||||
'0.7'
|
'0.7'
|
||||||
>>> floatformat(0.07, None)
|
>>> floatformat(0.07)
|
||||||
'0.1'
|
'0.1'
|
||||||
>>> floatformat(0.007, None)
|
>>> floatformat(0.007)
|
||||||
'0.0'
|
'0.0'
|
||||||
>>> floatformat(0.0, None)
|
>>> floatformat(0.0)
|
||||||
'0'
|
'0'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
# Quick tests for the markup templatetags (django.contrib.markup)
|
# Quick tests for the markup templatetags (django.contrib.markup)
|
||||||
|
|
||||||
from django.core.template import Template, Context
|
from django.core.template import Template, Context, add_to_builtins
|
||||||
import django.contrib.markup.templatetags.markup # this registers the filters
|
|
||||||
|
add_to_builtins('django.contrib.markup.templatetags.markup')
|
||||||
|
|
||||||
# find out if markup modules are installed and tailor the test appropriately
|
# find out if markup modules are installed and tailor the test appropriately
|
||||||
try:
|
try:
|
||||||
import textile
|
import textile
|
||||||
except ImportError:
|
except ImportError:
|
||||||
textile = None
|
textile = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import markdown
|
import markdown
|
||||||
except ImportError:
|
except ImportError:
|
||||||
markdown = None
|
markdown = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import docutils
|
import docutils
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -36,7 +37,7 @@ if textile:
|
||||||
<p>Paragraph 2 with “quotes” and <code>code</code></p>"""
|
<p>Paragraph 2 with “quotes” and <code>code</code></p>"""
|
||||||
else:
|
else:
|
||||||
assert rendered == textile_content
|
assert rendered == textile_content
|
||||||
|
|
||||||
### test markdown
|
### test markdown
|
||||||
|
|
||||||
markdown_content = """Paragraph 1
|
markdown_content = """Paragraph 1
|
||||||
|
@ -64,4 +65,4 @@ if docutils:
|
||||||
assert rendered =="""<p>Paragraph 1</p>
|
assert rendered =="""<p>Paragraph 1</p>
|
||||||
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>"""
|
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>"""
|
||||||
else:
|
else:
|
||||||
assert rendered == rest_content
|
assert rendered == rest_content
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import traceback
|
from django.conf import settings
|
||||||
|
|
||||||
|
# Turn TEMPLATE_DEBUG off, because tests assume that.
|
||||||
|
settings.TEMPLATE_DEBUG = False
|
||||||
|
|
||||||
from django.core import template
|
from django.core import template
|
||||||
from django.core.template import loader
|
from django.core.template import loader
|
||||||
from django.utils.translation import activate, deactivate, install
|
from django.utils.translation import activate, deactivate, install
|
||||||
|
import traceback
|
||||||
|
|
||||||
# Helper objects for template tests
|
# Helper objects for template tests
|
||||||
class SomeClass:
|
class SomeClass:
|
||||||
|
@ -99,8 +103,14 @@ TEMPLATE_TESTS = {
|
||||||
# Chained filters, with an argument to the first one
|
# Chained filters, with an argument to the first one
|
||||||
'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
|
'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
|
||||||
|
|
||||||
#Escaped string as argument
|
# Escaped string as argument
|
||||||
'basic-syntax30': (r"""{{ var|default_if_none:" endquote\" hah" }}""", {"var": None}, ' endquote" hah'),
|
'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'),
|
||||||
|
|
||||||
|
# Variable as argument
|
||||||
|
'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'),
|
||||||
|
|
||||||
|
# Default argument testing
|
||||||
|
'basic-syntax32' : (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
|
||||||
|
|
||||||
### IF TAG ################################################################
|
### IF TAG ################################################################
|
||||||
'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
|
'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
|
||||||
|
@ -289,7 +299,7 @@ TEMPLATE_TESTS = {
|
||||||
def test_template_loader(template_name, template_dirs=None):
|
def test_template_loader(template_name, template_dirs=None):
|
||||||
"A custom template loader that loads the unit-test templates."
|
"A custom template loader that loads the unit-test templates."
|
||||||
try:
|
try:
|
||||||
return ( TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name )
|
return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise template.TemplateDoesNotExist, template_name
|
raise template.TemplateDoesNotExist, template_name
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
from django.core import template
|
from django.core import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
class EchoNode(template.Node):
|
class EchoNode(template.Node):
|
||||||
def __init__(self, contents):
|
def __init__(self, contents):
|
||||||
self.contents = contents
|
self.contents = contents
|
||||||
|
@ -11,5 +13,5 @@ class EchoNode(template.Node):
|
||||||
|
|
||||||
def do_echo(parser, token):
|
def do_echo(parser, token):
|
||||||
return EchoNode(token.contents.split()[1:])
|
return EchoNode(token.contents.split()[1:])
|
||||||
|
|
||||||
template.register_tag("echo", do_echo)
|
register.tag("echo", do_echo)
|
Loading…
Reference in New Issue