Genericized nodelist_crawl to just pass each node to a callback function. Overloaded...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Wed, 23 Feb 2011 15:48:27 +0000 (10:48 -0500)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Wed, 23 Feb 2011 15:48:27 +0000 (10:48 -0500)
models/pages.py
utils.py

index ef68b5f..1600768 100644 (file)
@@ -5,7 +5,8 @@ from django.contrib.contenttypes import generic
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.http import HttpResponse
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.http import HttpResponse
-from django.template import TemplateDoesNotExist, Context, RequestContext, Template as DjangoTemplate, add_to_builtins as register_templatetags
+from django.template import TemplateDoesNotExist, Context, RequestContext, Template as DjangoTemplate, add_to_builtins as register_templatetags, TextNode, VariableNode
+from django.template.loader_tags import BlockNode, ExtendsNode, BlockContext
 from philo.models.base import TreeModel, register_value_model
 from philo.models.fields import TemplateField
 from philo.models.nodes import View
 from philo.models.base import TreeModel, register_value_model
 from philo.models.fields import TemplateField
 from philo.models.nodes import View
@@ -29,19 +30,57 @@ class Template(TreeModel):
                This will break if there is a recursive extends or includes in the template code.
                Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work.
                """
                This will break if there is a recursive extends or includes in the template code.
                Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work.
                """
-               def process_node(node, nodes):
+               def process_node(node, contentlet_specs, contentreference_specs, contentreference_names, block_context=None):
                        if isinstance(node, ContainerNode):
                        if isinstance(node, ContainerNode):
-                               nodes.append(node)
+                               if not node.references:
+                                       if node.name not in contentlet_specs:
+                                               contentlet_specs.append(node.name)
+                               else:
+                                       if node.name not in contentreference_names:
+                                               contentreference_specs.append((node.name, node.references))
+                                               contentreference_names.add(node.name)
+                       if isinstance(node, ExtendsNode) and block_context is not None:
+                               block_context.add_blocks(node.blocks)
+                               parent = getattr(node, LOADED_TEMPLATE_ATTR)
+                               for node in parent.nodelist:
+                                       if not isinstance(node, TextNode):
+                                               if not isinstance(node, ExtendsNode):
+                                                       blocks = dict([(n.name, n) for n in parent.nodelist.get_nodes_by_type(BlockNode)])
+                                                       block_context.add_blocks(blocks)
+                                               break
+                       
+                       if hasattr(node, 'child_nodelists') and not isinstance(node, BlockNode):
+                               for nodelist_name in node.child_nodelists:
+                                       if hasattr(node, nodelist_name):
+                                               nodelist_crawl(getattr(node, nodelist_name), process_node, contentlet_specs, contentreference_specs, contentreference_names, block_context)
+                       
+                       # LOADED_TEMPLATE_ATTR contains the name of an attribute philo uses to declare a
+                       # node as rendering an additional template. Philo monkeypatches the attribute onto
+                       # the relevant default nodes and declares it on any native nodes.
+                       if hasattr(node, LOADED_TEMPLATE_ATTR):
+                               loaded_template = getattr(node, LOADED_TEMPLATE_ATTR)
+                               if loaded_template:
+                                       nodelist_crawl(loaded_template.nodelist, process_node, contentlet_specs, contentreference_specs, contentreference_names, block_context)
                
                
-               all_nodes = nodelist_crawl(DjangoTemplate(self.code).nodelist, process_node)
-               contentlet_node_names = set([node.name for node in all_nodes if not node.references])
-               contentreference_node_names = []
-               contentreference_node_specs = []
-               for node in all_nodes:
-                       if node.references and node.name not in contentreference_node_names:
-                               contentreference_node_specs.append((node.name, node.references))
-                               contentreference_node_names.append(node.name)
-               return contentlet_node_names, contentreference_node_specs
+               contentreference_names = set()
+               contentlet_specs = []
+               contentreference_specs = []
+               block_context = BlockContext()
+               nodelist_crawl(DjangoTemplate(self.code).nodelist, process_node, contentlet_specs, contentreference_specs, contentreference_names, block_context)
+               
+               def process_block_node(node, contentlet_specs, contentreference_specs, contentreference_names, block_super):
+                       if isinstance(node, VariableNode) and node.filter_expression.var.lookups == (u'block', u'super'):
+                               block_super.append(node)
+                       process_node(node, contentlet_specs, contentreference_specs, contentreference_names, block_context=None)
+               
+               for block_list in block_context.blocks.values():
+                       for block in block_list[::-1]:
+                               block_super = []
+                               nodelist_crawl(block.nodelist, process_block_node, contentlet_specs, contentreference_specs, contentreference_names, block_super)
+                               if not block_super:
+                                       break
+               
+               return contentlet_specs, contentreference_specs
        
        def __unicode__(self):
                return self.get_path(pathsep=u' › ', field='name')
        
        def __unicode__(self):
                return self.get_path(pathsep=u' › ', field='name')
index deb009c..a968ac8 100644 (file)
--- a/utils.py
+++ b/utils.py
@@ -124,27 +124,31 @@ setattr(ExtendsNode, LOADED_TEMPLATE_ATTR, property(get_extended))
 setattr(ConstantIncludeNode, LOADED_TEMPLATE_ATTR, property(get_included))
 
 
 setattr(ConstantIncludeNode, LOADED_TEMPLATE_ATTR, property(get_included))
 
 
-def nodelist_crawl(nodelist, callback):
-       """This function crawls through a template's nodelist and the nodelists of any included or extended
-       templates, as determined by the presence and value of <LOADED_TEMPLATE_ATTR> on a node. Each node
-       will also be passed to a callback function for additional processing."""
-       results = []
+def default_callback(node, result_list):
+       result_list.append(node)
+       
+       if hasattr(node, 'child_nodelists'):
+               for nodelist_name in node.child_nodelists:
+                       if hasattr(node, nodelist_name):
+                               nodelist_crawl(getattr(node, nodelist_name), default_callback)
+       
+       # LOADED_TEMPLATE_ATTR contains the name of an attribute philo uses to declare a
+       # node as rendering an additional template. Philo monkeypatches the attribute onto
+       # the relevant default nodes and declares it on any native nodes.
+       if hasattr(node, LOADED_TEMPLATE_ATTR):
+               loaded_template = getattr(node, LOADED_TEMPLATE_ATTR)
+               if loaded_template:
+                       nodelist_crawl(loaded_template.nodelist, default_callback)
+
+
+def nodelist_crawl(nodelist, callback=default_callback, *args, **kwargs):
+       """This function crawls through a template's nodelist and the
+       nodelists of any included or extended templates, as determined by
+       the presence and value of <LOADED_TEMPLATE_ATTR> on a node. Each
+       node will also be passed to a callback function for additional
+       processing, along with any additional args and kwargs."""
        for node in nodelist:
                try:
        for node in nodelist:
                try:
-                       if hasattr(node, 'child_nodelists'):
-                               for nodelist_name in node.child_nodelists:
-                                       if hasattr(node, nodelist_name):
-                                               results.extend(nodelist_crawl(getattr(node, nodelist_name), callback))
-                       
-                       # LOADED_TEMPLATE_ATTR contains the name of an attribute philo uses to declare a
-                       # node as rendering an additional template. Philo monkeypatches the attribute onto
-                       # the relevant default nodes and declares it on any native nodes.
-                       if hasattr(node, LOADED_TEMPLATE_ATTR):
-                               loaded_template = getattr(node, LOADED_TEMPLATE_ATTR)
-                               if loaded_template:
-                                       results.extend(nodelist_crawl(loaded_template.nodelist, callback))
-                       
-                       callback(node, results)
+                       callback(node, *args, **kwargs)
                except:
                except:
-                       raise # fail for this node
-       return results
\ No newline at end of file
+                       raise # fail for this node
\ No newline at end of file