Permalink
Browse files

Fixed #14389, #9666 -- Started the migration path to make the first a…

…rgument to url and ssi template tags syntactically consistent with other tags. Thanks to Sean Brant for the draft patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14643 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 591ad8a commit 7ff5580d95f6bb40f12a0b90f51570e23b014a2d @freakboy3742 freakboy3742 committed Nov 20, 2010
@@ -1,7 +1,8 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_modify adminmedia %}
+{% load url from future %}
{% block extrahead %}{{ block.super }}
-{% url admin:jsi18n as jsi18nurl %}
+{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
@@ -1,4 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+{% load url from future %}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ LANGUAGE_CODE|default:"en-us" }}" xml:lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
@@ -28,18 +28,18 @@
{% trans 'Welcome,' %}
<strong>{% filter force_escape %}{% firstof user.first_name user.username %}{% endfilter %}</strong>.
{% block userlinks %}
- {% url django-admindocs-docroot as docsroot %}
+ {% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
{% endif %}
- {% url admin:password_change as password_change_url %}
+ {% url 'admin:password_change' as password_change_url %}
{% if password_change_url %}
<a href="{{ password_change_url }}">
{% else %}
<a href="{{ root_path }}password_change/">
{% endif %}
{% trans 'Change password' %}</a> /
- {% url admin:logout as logout_url %}
+ {% url 'admin:logout' as logout_url %}
{% if logout_url %}
<a href="{{ logout_url }}">
{% else %}
@@ -1,14 +1,14 @@
{% extends "admin/base_site.html" %}
{% load adminmedia admin_list i18n %}
-
+{% load url from future %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/changelists.css" />
{% if cl.formset %}
<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />
{% endif %}
{% if cl.formset or action_form %}
- {% url admin:jsi18n as jsi18nurl %}
+ {% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
{% endif %}
{{ media.css }}
@@ -41,11 +41,11 @@
<a href="../../">
{% trans "Home" %}
</a>
- &rsaquo;
+ &rsaquo;
<a href="../">
{{ app_label|capfirst }}
</a>
- &rsaquo;
+ &rsaquo;
{{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
@@ -84,7 +84,7 @@
</div>
{% endif %}
{% endblock %}
-
+
<form id="changelist-form" action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>{% csrf_token %}
{% if cl.formset %}
{{ cl.formset.management_form }}
@@ -1,6 +1,7 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}{% trans 'Change password' %} / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% load url from future %}
+{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}{% trans 'Change password' %} / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
{% block title %}{% trans 'Password change successful' %}{% endblock %}
@@ -1,7 +1,8 @@
{% extends "admin/base_site.html" %}
{% load i18n adminmedia %}
+{% load url from future %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
-{% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
{% block title %}{% trans 'Password change' %}{% endblock %}
@@ -1,10 +1,10 @@
-{% load i18n %}{% autoescape off %}
+{% load i18n %}{% load url from future %}{% autoescape off %}
{% trans "You're receiving this e-mail because you requested a password reset" %}
{% blocktrans %}for your user account at {{ site_name }}{% endblocktrans %}.
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
-{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid token=token %}
+{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
@@ -290,24 +290,30 @@ def include_is_allowed(filepath):
return False
class SsiNode(Node):
- def __init__(self, filepath, parsed):
- self.filepath, self.parsed = filepath, parsed
+ def __init__(self, filepath, parsed, legacy_filepath=True):
+ self.filepath = filepath
+ self.parsed = parsed
+ self.legacy_filepath = legacy_filepath
def render(self, context):
- if not include_is_allowed(self.filepath):
+ filepath = self.filepath
+ if not self.legacy_filepath:
+ filepath = filepath.resolve(context)
+
+ if not include_is_allowed(filepath):
if settings.DEBUG:
return "[Didn't have permission to include file]"
else:
return '' # Fail silently for invalid includes.
try:
- fp = open(self.filepath, 'r')
+ fp = open(filepath, 'r')
output = fp.read()
fp.close()
except IOError:
output = ''
if self.parsed:
try:
- t = Template(output, name=self.filepath)
+ t = Template(output, name=filepath)
return t.render(context)
except TemplateSyntaxError, e:
if settings.DEBUG:
@@ -356,31 +362,37 @@ def render(self, context):
return self.mapping.get(self.tagtype, '')
class URLNode(Node):
- def __init__(self, view_name, args, kwargs, asvar):
+ def __init__(self, view_name, args, kwargs, asvar, legacy_view_name=True):
self.view_name = view_name
+ self.legacy_view_name = legacy_view_name
self.args = args
self.kwargs = kwargs
self.asvar = asvar
def render(self, context):
from django.core.urlresolvers import reverse, NoReverseMatch
args = [arg.resolve(context) for arg in self.args]
- kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
+ kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context))
for k, v in self.kwargs.items()])
+ view_name = self.view_name
+ if not self.legacy_view_name:
+ view_name = view_name.resolve(context)
+
# Try to look up the URL twice: once given the view name, and again
# relative to what we guess is the "main" app. If they both fail,
# re-raise the NoReverseMatch unless we're using the
# {% url ... as var %} construct in which cause return nothing.
url = ''
try:
- url = reverse(self.view_name, args=args, kwargs=kwargs, current_app=context.current_app)
+ url = reverse(view_name, args=args, kwargs=kwargs, current_app=context.current_app)
except NoReverseMatch, e:
if settings.SETTINGS_MODULE:
project_name = settings.SETTINGS_MODULE.split('.')[0]
try:
- url = reverse(project_name + '.' + self.view_name,
- args=args, kwargs=kwargs, current_app=context.current_app)
+ url = reverse(project_name + '.' + view_name,
+ args=args, kwargs=kwargs,
+ current_app=context.current_app)
except NoReverseMatch:
if self.asvar is None:
# Re-raise the original exception, not the one with
@@ -922,6 +934,11 @@ def ssi(parser, token):
{% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
"""
+
+ import warnings
+ warnings.warn('The syntax for the ssi template tag is changing. Load the `ssi` tag from the `future` tag library to start using the new behavior.',
+ category=PendingDeprecationWarning)
+
bits = token.contents.split()
parsed = False
if len(bits) not in (2, 3):
@@ -933,7 +950,7 @@ def ssi(parser, token):
else:
raise TemplateSyntaxError("Second (optional) argument to %s tag"
" must be 'parsed'" % bits[0])
- return SsiNode(bits[1], parsed)
+ return SsiNode(bits[1], parsed, legacy_filepath=True)
ssi = register.tag(ssi)
#@register.tag
@@ -945,16 +962,44 @@ def load(parser, token):
``django/templatetags/news/photos.py``::
{% load news.photos %}
+
+ Can also be used to load an individual tag/filter from
+ a library::
+
+ {% load byline from news %}
+
"""
bits = token.contents.split()
- for taglib in bits[1:]:
- # add the library to the parser
+ if len(bits) >= 4 and bits[-2] == "from":
try:
+ taglib = bits[-1]
lib = get_library(taglib)
- parser.add_library(lib)
except InvalidTemplateLibrary, e:
raise TemplateSyntaxError("'%s' is not a valid tag library: %s" %
(taglib, e))
+ else:
+ temp_lib = Library()
+ for name in bits[1:-2]:
+ if name in lib.tags:
+ temp_lib.tags[name] = lib.tags[name]
+ # a name could be a tag *and* a filter, so check for both
+ if name in lib.filters:
+ temp_lib.filters[name] = lib.filters[name]
+ elif name in lib.filters:
+ temp_lib.filters[name] = lib.filters[name]
+ else:
+ raise TemplateSyntaxError("'%s' is not a valid tag or filter in tag library '%s'" %
+ (name, taglib))
+ parser.add_library(temp_lib)
+ else:
+ for taglib in bits[1:]:
+ # add the library to the parser
+ try:
+ lib = get_library(taglib)
+ parser.add_library(lib)
+ except InvalidTemplateLibrary, e:
+ raise TemplateSyntaxError("'%s' is not a valid tag library: %s" %
+ (taglib, e))
return LoadNode()
load = register.tag(load)
@@ -1140,6 +1185,11 @@ def url(parser, token):
The URL will look like ``/clients/client/123/``.
"""
+
+ import warnings
+ warnings.warn('The syntax for the url template tag is changing. Load the `url` tag from the `future` tag library to start using the new behavior.',
+ category=PendingDeprecationWarning)
+
bits = token.split_contents()
if len(bits) < 2:
raise TemplateSyntaxError("'%s' takes at least one argument"
@@ -1196,7 +1246,7 @@ def url(parser, token):
else:
args.append(parser.compile_filter(value))
- return URLNode(viewname, args, kwargs, asvar)
+ return URLNode(viewname, args, kwargs, asvar, legacy_view_name=True)
url = register.tag(url)
#@register.tag
@@ -0,0 +1,99 @@
+from django.conf import settings
+from django.template import Library, Node, Template, TemplateSyntaxError
+from django.template.defaulttags import kwarg_re, include_is_allowed, SsiNode, URLNode
+from django.utils.encoding import smart_str
+
+
+register = Library()
+
+@register.tag
+def ssi(parser, token):
+ """
+ Outputs the contents of a given file into the page.
+
+ Like a simple "include" tag, the ``ssi`` tag includes the contents
+ of another file -- which must be specified using an absolute path --
+ in the current page::
+
+ {% ssi "/home/html/ljworld.com/includes/right_generic.html" %}
+
+ If the optional "parsed" parameter is given, the contents of the included
+ file are evaluated as template code, with the current context::
+
+ {% ssi "/home/html/ljworld.com/includes/right_generic.html" parsed %}
+ """
+ bits = token.contents.split()
+ parsed = False
+ if len(bits) not in (2, 3):
+ raise TemplateSyntaxError("'ssi' tag takes one argument: the path to"
+ " the file to be included")
+ if len(bits) == 3:
+ if bits[2] == 'parsed':
+ parsed = True
+ else:
+ raise TemplateSyntaxError("Second (optional) argument to %s tag"
+ " must be 'parsed'" % bits[0])
+ filepath = parser.compile_filter(bits[1])
+ return SsiNode(filepath, parsed, legacy_filepath=False)
+
+@register.tag
+def url(parser, token):
+ """
+ Returns an absolute URL matching given view with its parameters.
+
+ This is a way to define links that aren't tied to a particular URL
+ configuration::
+
+ {% url "path.to.some_view" arg1 arg2 %}
+
+ or
+
+ {% url "path.to.some_view" name1=value1 name2=value2 %}
+
+ The first argument is a path to a view. It can be an absolute python path
+ or just ``app_name.view_name`` without the project name if the view is
+ located inside the project. Other arguments are comma-separated values
+ that will be filled in place of positional and keyword arguments in the
+ URL. All arguments for the URL should be present.
+
+ For example if you have a view ``app_name.client`` taking client's id and
+ the corresponding line in a URLconf looks like this::
+
+ ('^client/(\d+)/$', 'app_name.client')
+
+ and this app's URLconf is included into the project's URLconf under some
+ path::
+
+ ('^clients/', include('project_name.app_name.urls'))
+
+ then in a template you can create a link for a certain client like this::
+
+ {% url "app_name.client" client.id %}
+
+ The URL will look like ``/clients/client/123/``.
+ """
+ bits = token.split_contents()
+ if len(bits) < 2:
+ raise TemplateSyntaxError("'%s' takes at least one argument"
+ " (path to a view)" % bits[0])
+ viewname = parser.compile_filter(bits[1])
+ args = []
+ kwargs = {}
+ asvar = None
+ bits = bits[2:]
+ if len(bits) >= 2 and bits[-2] == 'as':
+ asvar = bits[-1]
+ bits = bits[:-2]
+
+ if len(bits):
+ for bit in bits:
+ match = kwarg_re.match(bit)
+ if not match:
+ raise TemplateSyntaxError("Malformed arguments to url tag")
+ name, value = match.groups()
+ if name:
+ kwargs[name] = parser.compile_filter(value)
+ else:
+ args.append(parser.compile_filter(value))
+
+ return URLNode(viewname, args, kwargs, asvar, legacy_view_name=False)
@@ -131,6 +131,11 @@ their deprecation, as per the :ref:`Django deprecation policy
been deprecated in favor of the
:class:`~django.contrib.staticfiles.handlers.StaticFilesHandler`.
+ * The :ttag:`url` and :ttag:`ssi` template tags will be
+ modified so that the first argument to each tag is a
+ template variable, not an implied string. The new-style
+ behavior is provided in the ``future`` template tag library.
+
* 2.0
* ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the
Oops, something went wrong.

0 comments on commit 7ff5580

Please sign in to comment.