X-Git-Url: http://git.ithinksw.org/philo.git/blobdiff_plain/2380185d894e5a62a20b91a0c5a3a48497fb3cfb..64e4cdefe89f642d349b5908a3bbaec76333e3b2:/templatetags/nodes.py diff --git a/templatetags/nodes.py b/templatetags/nodes.py index 3be8194..c2dcd9a 100644 --- a/templatetags/nodes.py +++ b/templatetags/nodes.py @@ -1,76 +1,197 @@ from django import template from django.conf import settings from django.contrib.sites.models import Site +from django.core.urlresolvers import reverse, NoReverseMatch +from django.template.defaulttags import kwarg_re +from django.utils.encoding import smart_str +from philo.exceptions import ViewCanNotProvideSubpath register = template.Library() class NodeURLNode(template.Node): - def __init__(self, node, with_obj, as_var): - if node is not None: - self.node = template.Variable(node) - else: - self.node = None - - if with_obj is not None: - self.with_obj = template.Variable(with_obj) - else: - self.with_obj = None - + def __init__(self, node, as_var, with_obj=None, view_name=None, args=None, kwargs=None): self.as_var = as_var + self.view_name = view_name + + # Because the following variables have already been compiled as filters if they exist, they don't need to be re-scanned as template variables. + self.node = node + self.with_obj = with_obj + self.args = args + self.kwargs = kwargs def render(self, context): - try: - if self.node: - node = self.node.resolve(context) - else: - node = context['node'] - current_site = Site.objects.get_current() - if node.has_ancestor(current_site.root_node): - url = node.get_path(root=current_site.root_node) - if self.with_obj: - with_obj = self.with_obj.resolve(context) - url += node.view.get_subpath(with_obj) - else: + if self.node: + node = self.node.resolve(context) + else: + node = context.get('node', None) + + if not node: + return settings.TEMPLATE_STRING_IF_INVALID + + if self.with_obj is None and self.view_name is None: + url = node.get_absolute_url() + else: + if not node.view.accepts_subpath: return settings.TEMPLATE_STRING_IF_INVALID - if self.as_var: - context[self.as_var] = url - return settings.TEMPLATE_STRING_IF_INVALID + if self.with_obj is not None: + try: + view_name, args, kwargs = node.view.get_reverse_params(self.with_obj.resolve(context)) + except ViewCanNotProvideSubpath: + return settings.TEMPLATE_STRING_IF_INVALID + else: # self.view_name is not None + view_name = self.view_name + args = [arg.resolve(context) for arg in self.args] + kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context)) for k, v in self.kwargs.items()]) + + url = '' + try: + subpath = reverse(view_name, urlconf=node.view, args=args, kwargs=kwargs) + except NoReverseMatch: + if self.as_var is None: + if settings.TEMPLATE_DEBUG: + raise + return settings.TEMPLATE_STRING_IF_INVALID else: - return url - except: - return settings.TEMPLATE_STRING_IF_INVALID + if subpath[0] == '/': + subpath = subpath[1:] + + url = node.get_absolute_url() + subpath + + if self.as_var: + context[self.as_var] = url + return '' + else: + return url @register.tag(name='node_url') def do_node_url(parser, token): """ - {% node_url [] [with ] [as ] %} + {% node_url [for ] [as ] %} + {% node_url with [for ] [as ] %} + {% node_url [ [ ...] ] [for ] [as ] %} + {% node_url [= [= ...] ] [for ] [as ]%} """ params = token.split_contents() tag = params[0] + as_var = None + with_obj = None + node = None + params = params[1:] - if len(params) <= 6: - node = None - with_obj = None - as_var = None - remaining_tokens = params[1:] - while remaining_tokens: - option_token = remaining_tokens.pop(0) - if option_token == 'with': - try: - with_obj = remaining_tokens.pop(0) - except IndexError: - raise template.TemplateSyntaxError('"%s" template tag option "with" requires an argument specifying an object handled by the view on the node' % tag) - elif option_token == 'as': - try: - as_var = remaining_tokens.pop(0) - except IndexError: - raise template.TemplateSyntaxError('"%s" template tag option "as" requires an argument specifying a variable name' % tag) - else: # node - node = option_token - return NodeURLNode(node=node, with_obj=with_obj, as_var=as_var) - else: - raise template.TemplateSyntaxError('"%s" template tag cannot accept more than five arguments' % tag) \ No newline at end of file + if len(params) >= 2 and params[-2] == 'as': + as_var = params[-1] + params = params[:-2] + + if len(params) >= 2 and params[-2] == 'for': + node = parser.compile_filter(params[-1]) + params = params[:-2] + + if len(params) >= 2 and params[-2] == 'with': + with_obj = parser.compile_filter(params[-1]) + params = params[:-2] + + if with_obj is not None: + if params: + raise template.TemplateSyntaxError('`%s` template tag accepts no arguments or keyword arguments if with is specified.' % tag) + return NodeURLNode(with_obj=with_obj, node=node, as_var=as_var) + + if params: + args = [] + kwargs = {} + view_name = params.pop(0) + for param in params: + match = kwarg_re.match(param) + if not match: + raise TemplateSyntaxError("Malformed arguments to `%s` tag" % tag) + name, value = match.groups() + if name: + kwargs[name] = parser.compile_filter(value) + else: + args.append(parser.compile_filter(value)) + return NodeURLNode(view_name=view_name, args=args, kwargs=kwargs, node=node, as_var=as_var) + + return NodeURLNode(node=node, as_var=as_var) + + +class NavigationNode(template.Node): + def __init__(self, node=None, as_var=None): + self.as_var = as_var + self.node = node + + def render(self, context): + if 'request' not in context: + return settings.TEMPLATE_STRING_IF_INVALID + + if self.node: + node = self.node.resolve(context) + else: + node = context.get('node', None) + + if not node: + return settings.TEMPLATE_STRING_IF_INVALID + + try: + nav_root = node.attributes['navigation_root'] + except KeyError: + if settings.TEMPLATE_DEBUG: + raise + return settings.TEMPLATE_STRING_IF_INVALID + + # Should I get its override and check for a max depth override there? + navigation = nav_root.get_navigation() + + if self.as_var: + context[self.as_var] = navigation + return '' + + return self.compile(navigation, context['request'].path, nav_root.get_absolute_url(), nav_root.get_level(), nav_root.get_level() + 3) + + def compile(self, navigation, active_path, root_url, current_depth, max_depth): + compiled = "" + for item in navigation: + if item['url'] in active_path and (item['url'] != root_url or root_url == active_path): + compiled += "
  • " + else: + compiled += "
  • " + + if item['url']: + compiled += "" % item['url'] + + compiled += item['title'] + + if item['url']: + compiled += "" + + if 'children' in item and current_depth < max_depth: + compiled += "
      %s
    " % self.compile(item['children'], active_path, root_url, current_depth + 1, max_depth) + + compiled += "
  • " + return compiled + + +@register.tag(name='navigation') +def do_navigation(parser, token): + """ + {% navigation [for ] [as ] %} + """ + bits = token.split_contents() + tag = bits[0] + bits = bits[1:] + node = None + as_var = None + + if len(bits) >= 2 and bits[-2] == 'as': + as_var = bits[-1] + bits = bits[:-2] + + if len(bits) >= 2 and bits[-2] == 'for': + node = parser.compile_filter(bits[-1]) + bits = bits[-2] + + if bits: + raise template.TemplateSyntaxError('`%s` template tag expects the syntax {%% %s [for ] [as ] %}' % (tag, tag)) + return NavigationNode(node, as_var)