diff --git a/django/conf/admin_templates/admin_change_form.html b/django/conf/admin_templates/admin_change_form.html index 2beac80272..692de223af 100644 --- a/django/conf/admin_templates/admin_change_form.html +++ b/django/conf/admin_templates/admin_change_form.html @@ -87,7 +87,7 @@ {% if auto_populated_fields %} {% endif %} diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 449d25d01e..c5e560c9e1 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -110,7 +110,7 @@ IGNORABLE_404_ENDS = ('mail.pl', 'mailform.pl', 'mail.cgi', 'mailform.cgi', 'fav SECRET_KEY = '' # Path to the "jing" executable -- needed to validate XMLFields -JING_PATH = "/usr/bin/jng" +JING_PATH = "/usr/bin/jing" ############## # MIDDLEWARE # diff --git a/django/core/defaulttags.py b/django/core/defaulttags.py index d5bfec7cf0..41595fcee0 100644 --- a/django/core/defaulttags.py +++ b/django/core/defaulttags.py @@ -227,6 +227,15 @@ class SsiNode(template.Node): return '' # Fail silently for invalid included templates. return output + +class ConstantIncludeNode(template.Node): + def __init__(self, template_path): + t = template_loader.get_template(template_path) + self.nodelist = t.nodelist + + def render(self, context): + return self.nodelist.render(context) + class IncludeNode(template.Node): def __init__(self, template_path_var): self.template_path_var = template_path_var @@ -631,6 +640,10 @@ def do_include(parser, token): parsed = False if len(bits) != 2: raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included" + + path = bits[1] + if path[0] in ('"', "'") and path[-1] == path[0]: + return ConstantIncludeNode(path[1:-1]) return IncludeNode(bits[1]) def do_load(parser, token): @@ -648,8 +661,8 @@ def do_load(parser, token): # check at compile time that the module can be imported try: LoadNode.load_taglib(taglib) - except ImportError: - raise template.TemplateSyntaxError, "'%s' is not a valid tag library" % taglib + except ImportError, e: + raise template.TemplateSyntaxError, "'%s' is not a valid tag library, %s" % (taglib, e) return LoadNode(taglib) def do_now(parser, token): diff --git a/django/core/template_decorators.py b/django/core/template_decorators.py new file mode 100644 index 0000000000..3b32e694e7 --- /dev/null +++ b/django/core/template_decorators.py @@ -0,0 +1,70 @@ +from inspect import getargspec +from django.core import template +from django.core.template_loader import render_to_string, get_template +from django.utils.functional import curry + +def gen_compile_func(params, defaults, name, node_class, parser, token): + #look in tags for + 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 template.TemplateSyntaxError(message) + return node_class(bits) + + +def simple_tag(func): + (params,_, _, defaults) = getargspec(func) + class TNode(template.Node): + def __init__(self, vars_to_resolve): + #get the vars to resolve + self.vars_to_resolve = vars_to_resolve + + def render(self, context): + resolved_vars = [template.resolve_variable(var, context) + for var in self.vars_to_resolve] + return func(*resolved_vars) + compile_func = curry(gen_compile_func, params, defaults, func.__name__, TNode) + compile_func.__doc__ = func.__doc__ + template.register_tag(func.__name__, compile_func) + return func + + +def inclusion_tag(file_name, context_class=template.Context, takes_context=False): + def dec(func): + (params,_, _, defaults) = getargspec(func) + if takes_context: + if params[0] == 'context': + params = params[1:] + else: + raise template.TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'" ) + class TNode(template.Node): + def __init__(self, vars_to_resolve): + self.vars_to_resolve = vars_to_resolve + + def render(self, context): + resolved_vars = [template.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(gen_compile_func, params, defaults, func.__name__, TNode) + compile_func.__doc__ = func.__doc__ + template.register_tag(func.__name__, compile_func) + return func + return dec + diff --git a/django/templatetags/admin_modify.py b/django/templatetags/admin_modify.py index c52505e044..2938f47a58 100644 --- a/django/templatetags/admin_modify.py +++ b/django/templatetags/admin_modify.py @@ -5,6 +5,8 @@ from django.utils.text import capfirst from django.utils.html import escape from django.utils.functional import curry +from django.core.template_decorators import simple_tag, inclusion_tag + from django.views.admin.main import AdminBoundField import re @@ -13,21 +15,15 @@ word_re = re.compile('[A-Z][a-z]+') def class_name_to_underscored(name): return '_'.join([ s.lower() for s in word_re.findall(name)[:-1] ]) - -class IncludeAdminScriptNode(template.Node): - def __init__(self, var): - self.var = var - - def render(self, context): - resolved = template.resolve_variable(self.var, context) +#@simple_tag +def include_admin_script(script_path): return '' % \ - (ADMIN_MEDIA_PREFIX, resolved) - -class SubmitRowNode(template.Node): - def __init__(self): - pass + (ADMIN_MEDIA_PREFIX, script_path) +include_admin_script = simple_tag(include_admin_script) - def render(self, context): + +#@inclusion_tag('admin_submit_line', takes_context=True) +def submit_row(context): change = context['change'] add = context['add'] show_delete = context['show_delete'] @@ -36,8 +32,7 @@ class SubmitRowNode(template.Node): has_delete_permission = context['has_delete_permission'] is_popup = context['is_popup'] - - output = render_to_string('admin_submit_line', { + return { 'onclick_attrib' : (ordered_objects and change and 'onclick="submitOrderForm();"' or ''), 'show_delete_link' : (not is_popup and has_delete_permission @@ -46,50 +41,35 @@ class SubmitRowNode(template.Node): 'show_save_and_add_another': not is_popup and (not save_as or add), 'show_save_and_continue': not is_popup, 'show_save': True - }, context); - context.pop() - return output; + } -class AdminFieldBoundNode(template.Node): - def __init__(self, argument): - self.argument = argument +srdec = inclusion_tag('admin_submit_line', takes_context=True) +submit_row = srdec(submit_row) + +#@simple_tag +def field_label(bound_field): + class_names = [] + if isinstance(bound_field.field, meta.BooleanField): + class_names.append("vCheckboxLabel") + else: + if not bound_field.field.blank: + class_names.append('required') + if not bound_field.first: + class_names.append('inline') - def render(self, context): - argument_val = template.resolve_variable(self.argument, context) - if (isinstance(argument_val, list)): - bound_fields = argument_val - else: - bound_fields = [argument_val] - add = context['add'] - change = context['change'] - - context.push() - context['bound_fields'] = bound_fields - context['class_names'] = " ".join(self.get_class_names(bound_fields)) - t = template_loader.get_template("admin_field") - output = t.render(context) - context.pop() - - return output + class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' + return ' ' % \ + (bound_field.element_id, class_str, + capfirst(bound_field.field.verbose_name) ) +field_label = simple_tag(field_label) - def get_class_names(self, bound_fields): - class_names = ['form-row'] - for bound_field in bound_fields: - for f in bound_field.form_fields: - if f.errors(): - class_names.append('errors') - break - - # Assumes BooleanFields won't be stacked next to each other! - if isinstance(bound_fields[0].field, meta.BooleanField): - class_names.append('checkbox-row') - - return class_names - class FieldWidgetNode(template.Node): def __init__(self, bound_field_var): self.bound_field_var = bound_field_var + self.nodelists = {} + t = template_loader.get_template("widget/default") + self.default = t.nodelist def render(self, context): @@ -100,22 +80,26 @@ class FieldWidgetNode(template.Node): context.push() context['bound_field'] = bound_field klass = bound_field.field.__class__ - t = None - while klass: - try: - field_class_name = klass.__name__ - template_name = "widget/%s" % \ - class_name_to_underscored(field_class_name) - - t = template_loader.get_template(template_name) - break - except template.TemplateDoesNotExist: - klass = (len(klass.__bases__) > 0) and klass.__bases__[0] or None - - if t == None: - t = template_loader.get_template("widget/default") - - output = t.render(context) + if not self.nodelists.has_key(klass): + t = None + while klass: + try: + field_class_name = klass.__name__ + template_name = "widget/%s" % \ + class_name_to_underscored(field_class_name) + t = template_loader.get_template(template_name) + break + except template.TemplateDoesNotExist: + klass = bool(klass.__bases__) and klass.__bases__[0] or None + + if t == None: + nodelist = self.default + else: + nodelist = t.nodelist + + self.nodelists[klass] = nodelist + + output = self.nodelists[klass].render(context) context.pop() return output @@ -177,71 +161,38 @@ class EditInlineNode(template.Node): context['num_headers'] = len(field_wrapper_list) context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) # context['name_prefix'] = "%s." % (var_name,) - -class FieldLabelNode(template.Node): - def __init__(self, bound_field_var): - self.bound_field_var = bound_field_var - - def render(self, context): - bound_field = template.resolve_variable(self.bound_field_var, context) - class_names = [] - if isinstance(bound_field.field, meta.BooleanField): - class_names.append("vCheckboxLabel") - else: - if not bound_field.field.blank: - class_names.append('required') - if not bound_field.first: - class_names.append('inline') - - class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' - return ' ' % (bound_field.element_id, class_str, capfirst(bound_field.field.verbose_name) ) -class OutputAllNode(template.Node): - def __init__(self, form_fields_var): - self.form_fields_var = form_fields_var - - def render(self, context): - form_fields = template.resolve_variable(self.form_fields_var, context) - return ''.join([str(f) for f in form_fields]) -class AutoPopulatedFieldScriptNode(template.Node): - def __init__(self, auto_pop_var): - self.auto_pop_var = auto_pop_var +#@simple_tag +def output_all(form_fields): + return ''.join([str(f) for f in form_fields]) +output_all = simple_tag(output_all) - def render(self,context): - auto_pop_fields = template.resolve_variable(self.auto_pop_var, context) - change = context['change'] - for field in auto_pop_fields: - t = [] - if change: - t.append('document.getElementById("id_%s")._changed = true;' % field.name ) - else: - t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) - add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) - for f in field.prepopulate_from: - t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if(e._changed) { e.value = URLify(%s, %s);} } ' % (f, field.name, add_values, field.maxlength) ) - - return ''.join(t) - -class FilterInterfaceScriptMaybeNode(template.Node): - def __init__(self, bound_field_var): - self.bound_field_var = bound_field_var - - def render(self, context): - bound_field = template.resolve_variable(self.bound_field_var, context) - f = bound_field.field - if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface: - return '\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX) +#@simple_tag +def auto_populated_field_script(auto_pop_fields, change = False): + for field in auto_pop_fields: + t = [] + if change: + t.append('document.getElementById("id_%s")._changed = true;' % field.name ) else: - return '' + t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) - + add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) + for f in field.prepopulate_from: + t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if(e._changed) { e.value = URLify(%s, %s);} } ' % (f, field.name, add_values, field.maxlength) ) + return ''.join(t) +auto_populated_field_script = simple_tag(auto_populated_field_script) -def do_submit_row(parser, token): - return SubmitRowNode() - +#@simple_tag +def filter_interface_script_maybe(bound_field): + f = bound_field.field + if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface: + return '\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX) + else: + return '' +filter_interface_script_maybe = simple_tag(filter_interface_script_maybe) def do_one_arg_tag(node_factory, parser,token): tokens = token.contents.split() @@ -251,14 +202,8 @@ def do_one_arg_tag(node_factory, parser,token): one_arg_tag_nodes = [ - IncludeAdminScriptNode, - AdminFieldBoundNode, - FieldLabelNode, FieldWidgetNode, - OutputAllNode, EditInlineNode, - AutoPopulatedFieldScriptNode, - FilterInterfaceScriptMaybeNode, ] @@ -267,9 +212,39 @@ def register_one_arg_tag(node): parse_func = curry(do_one_arg_tag, node) template.register_tag(tag_name, parse_func) - - for node in one_arg_tag_nodes: register_one_arg_tag(node) -template.register_tag('submit_row', do_submit_row ) + +#@inclusion_tag('admin_field', takes_context=True) +def admin_field_bound(context, argument_val): + if (isinstance(argument_val, list)): + bound_fields = argument_val + else: + bound_fields = [argument_val] + add = context['add'] + change = context['change'] + + class_names = ['form-row'] + for bound_field in bound_fields: + for f in bound_field.form_fields: + if f.errors(): + class_names.append('errors') + break + + # Assumes BooleanFields won't be stacked next to each other! + if isinstance(bound_fields[0].field, meta.BooleanField): + class_names.append('checkbox-row') + + return { + 'add' : context['add'], + 'change' : context['change'], + 'bound_fields' : bound_fields, + 'class_names' : " ".join(class_names) + } + + +afbdec = inclusion_tag('admin_field', takes_context=True) +admin_field_bound = afbdec(admin_field_bound) + +