Initial implementation of node_url templatetag.
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Thu, 15 Jul 2010 01:37:13 +0000 (21:37 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Thu, 15 Jul 2010 02:30:22 +0000 (22:30 -0400)
contrib/penfield/models.py
exceptions.py [new file with mode: 0644]
models/base.py
models/nodes.py
models/pages.py
templatetags/nodes.py [new file with mode: 0644]

index 0d8a374..7da8e08 100644 (file)
@@ -1,7 +1,9 @@
 from django.db import models
 from django.conf import settings
 from philo.models import Tag, Titled, Entity, MultiView, Page, register_value_model
+from philo.exceptions import ViewCanNotProvideSubpath
 from django.conf.urls.defaults import url, patterns
+from django.core.urlresolvers import reverse
 from django.http import Http404, HttpResponse
 from datetime import datetime
 from philo.contrib.penfield.utils import paginate
@@ -59,6 +61,22 @@ class BlogView(MultiView):
        def __unicode__(self):
                return u'BlogView for %s' % self.blog.title
        
+       def get_subpath(self, obj):
+               if isinstance(obj, BlogEntry):
+                       if obj.blog == self.blog:
+                               entry_view_args = {'slug': obj.slug}
+                               if self.entry_permalink_style in 'DMY':
+                                       entry_view_args.update({'year': str(obj.date.year).zfill(4)})
+                                       if self.entry_permalink_style in 'DM':
+                                               entry_view_args.update({'month': str(obj.date.month).zfill(2)})
+                                               if self.entry_permalink_style == 'D':
+                                                       entry_view_args.update({'day': str(obj.date.day).zfill(2)})
+                               return reverse(self.entry_view, urlconf=self, kwargs=entry_view_args)
+               elif isinstance(obj, Tag):
+                       if obj in self.blog.entry_tags:
+                               return reverse(self.tag_view, urlconf=self, kwargs={'tag_slugs': obj.slug})
+               raise ViewCanNotProvideSubpath
+       
        @property
        def urlpatterns(self):
                base_patterns = patterns('',
diff --git a/exceptions.py b/exceptions.py
new file mode 100644 (file)
index 0000000..b40f08a
--- /dev/null
@@ -0,0 +1,7 @@
+class ViewDoesNotProvideSubpaths(Exception):
+       """ Raised by get_subpath when the View does not provide subpaths (the default). """
+       silent_variable_failure = True
+
+class ViewCanNotProvideSubpath(Exception):
+       """ Raised by get_subpath when the View can not provide a subpath for the supplied object. """
+       silent_variable_failure = True
\ No newline at end of file
index 2f5bf93..6f23191 100644 (file)
@@ -159,13 +159,29 @@ class TreeModel(models.Model):
        parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
        slug = models.SlugField()
        
-       def get_path(self, pathsep='/', field='slug'):
-               path = getattr(self, field, '?')
-               parent = self.parent
+       def has_ancestor(self, ancestor):
+               parent = self
                while parent:
-                       path = getattr(parent, field, '?') + pathsep + path
+                       if parent == ancestor:
+                               return True
                        parent = parent.parent
-               return path
+               return False
+       
+       def get_path(self, root=None, pathsep='/', field='slug'):
+               if root is not None and self.has_ancestor(root):
+                       path = ''
+                       parent = self
+                       while parent and parent != root:
+                               path = getattr(parent, field, '?') + pathsep + path
+                               parent = parent.parent
+                       return path
+               else:
+                       path = getattr(self, field, '?')
+                       parent = self.parent
+                       while parent and parent != root:
+                               path = getattr(parent, field, '?') + pathsep + path
+                               parent = parent.parent
+                       return path
        path = property(get_path)
        
        def __unicode__(self):
index 1e7622a..f46ddce 100644 (file)
@@ -5,10 +5,12 @@ from django.contrib.sites.models import Site
 from django.http import HttpResponse, HttpResponseServerError, HttpResponseRedirect
 from django.core.servers.basehttp import FileWrapper
 from django.core.urlresolvers import resolve
+from django.template import add_to_builtins as register_templatetags
 from inspect import getargspec
 from philo.models.base import TreeEntity, Entity, QuerySetMapper
 from philo.utils import ContentTypeSubclassLimiter
 from philo.validators import RedirectValidator
+from philo.exceptions import ViewDoesNotProvideSubpaths
 
 
 _view_content_type_limiter = ContentTypeSubclassLimiter(None)
@@ -21,7 +23,9 @@ class Node(TreeEntity):
        
        @property
        def accepts_subpath(self):
-               return self.view.accepts_subpath
+               if self.view:
+                       return self.view.accepts_subpath
+               return False
        
        def render_to_response(self, request, path=None, subpath=None, extra_context=None):
                return self.view.render_to_response(self, request, path, subpath, extra_context)
@@ -39,6 +43,9 @@ class View(Entity):
        
        accepts_subpath = False
        
+       def get_subpath(self, obj):
+               raise ViewDoesNotProvideSubpaths
+       
        def attributes_with_node(self, node):
                return QuerySetMapper(self.attribute_set, passthrough=node.attributes)
        
@@ -111,4 +118,7 @@ class File(View):
                app_label = 'philo'
        
        def __unicode__(self):
-               return self.file.name
\ No newline at end of file
+               return self.file.name
+
+
+register_templatetags('philo.templatetags.nodes')
\ No newline at end of file
index 5f75494..ff8e876 100644 (file)
@@ -101,7 +101,7 @@ class Page(View):
        def render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
                context = {}
                context.update(extra_context or {})
-               context.update({'page': self, 'attributes': self.attributes_with_node(node), 'relationships': self.relationships_with_node(node)})
+               context.update({'node': node, 'page': self, 'attributes': self.attributes_with_node(node), 'relationships': self.relationships_with_node(node)})
                return HttpResponse(self.template.django_template.render(RequestContext(request, context)), mimetype=self.template.mimetype)
        
        def __unicode__(self):
diff --git a/templatetags/nodes.py b/templatetags/nodes.py
new file mode 100644 (file)
index 0000000..3be8194
--- /dev/null
@@ -0,0 +1,76 @@
+from django import template
+from django.conf import settings
+from django.contrib.sites.models import Site
+
+
+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
+               
+               self.as_var = as_var
+       
+       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:
+                               return settings.TEMPLATE_STRING_IF_INVALID
+                       
+                       if self.as_var:
+                               context[self.as_var] = url
+                               return settings.TEMPLATE_STRING_IF_INVALID
+                       else:
+                               return url
+               except:
+                       return settings.TEMPLATE_STRING_IF_INVALID
+
+
+@register.tag(name='node_url')
+def do_node_url(parser, token):
+       """
+       {% node_url [<node>] [with <obj>] [as <var>] %}
+       """
+       params = token.split_contents()
+       tag = params[0]
+       
+       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