2 from django.db import models
3 from django.contrib.contenttypes.models import ContentType
4 from django.contrib.contenttypes import generic
5 from django.conf import settings
6 from django.template import add_to_builtins as register_templatetags
7 from django.template import Template as DjangoTemplate
8 from django.template import TemplateDoesNotExist
9 from django.template import Context, RequestContext
10 from django.template.loader import get_template
11 from django.template.loader_tags import ExtendsNode, ConstantIncludeNode, IncludeNode
12 from django.http import HttpResponse
13 from philo.models.base import TreeModel, register_value_model
14 from philo.models.nodes import View
15 from philo.utils import fattr
16 from philo.templatetags.containers import ContainerNode
19 class Template(TreeModel):
20 name = models.CharField(max_length=255)
21 documentation = models.TextField(null=True, blank=True)
22 mimetype = models.CharField(max_length=255, null=True, blank=True, help_text='Default: %s' % settings.DEFAULT_CONTENT_TYPE)
23 code = models.TextField(verbose_name='django template code')
27 return 'philo.models.Template: ' + self.path
30 def django_template(self):
31 return DjangoTemplate(self.code)
36 Returns a tuple where the first item is a list of names of contentlets referenced by containers,
37 and the second item is a list of tuples of names and contenttypes of contentreferences referenced by containers.
38 This will break if there is a recursive extends or includes in the template code.
39 Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work.
41 def container_nodes(template):
42 def nodelist_container_nodes(nodelist):
46 if hasattr(node, 'child_nodelists'):
47 for nodelist_name in node.child_nodelists:
48 if hasattr(node, nodelist_name):
49 nodes.extend(nodelist_container_nodes(getattr(node, nodelist_name)))
50 if isinstance(node, ContainerNode):
52 elif isinstance(node, ExtendsNode):
53 extended_template = node.get_parent(Context())
55 nodes.extend(container_nodes(extended_template))
56 elif isinstance(node, ConstantIncludeNode):
57 included_template = node.template
59 nodes.extend(container_nodes(included_template))
60 elif isinstance(node, IncludeNode):
61 included_template = get_template(node.template_name.resolve(Context()))
63 nodes.extend(container_nodes(included_template))
65 raise # fail for this node
67 return nodelist_container_nodes(template.nodelist)
68 all_nodes = container_nodes(self.django_template)
69 contentlet_node_names = set([node.name for node in all_nodes if not node.references])
70 contentreference_node_names = []
71 contentreference_node_specs = []
72 for node in all_nodes:
73 if node.references and node.name not in contentreference_node_names:
74 contentreference_node_specs.append((node.name, node.references))
75 contentreference_node_names.append(node.name)
76 return contentlet_node_names, contentreference_node_specs
78 def __unicode__(self):
79 return self.get_path(pathsep=u' › ', field='name')
82 @fattr(is_usable=True)
83 def loader(template_name, template_dirs=None): # load_template_source
85 template = Template.objects.get_with_path(template_name)
86 except Template.DoesNotExist:
87 raise TemplateDoesNotExist(template_name)
88 return (template.code, template.origin)
96 Represents a page - something which is rendered according to a template. The page will have a number of related Contentlets depending on the template selected - but these will appear only after the page has been saved with that template.
98 template = models.ForeignKey(Template, related_name='pages')
99 title = models.CharField(max_length=255)
101 def get_containers(self):
102 if not hasattr(self, '_containers'):
103 self._containers = self.template.containers
104 return self._containers
105 containers = property(get_containers)
107 def render_to_string(self, node=None, request=None, path=None, subpath=None, extra_context=None):
109 context.update(extra_context or {})
110 context.update({'page': self, 'attributes': self.attributes, 'relationships': self.relationships})
112 context.update({'node': node, 'attributes': self.attributes_with_node(node), 'relationships': self.relationships_with_node(node)})
113 return self.template.django_template.render(RequestContext(request, context))
114 return self.template.django_template.render(Context(context))
116 def render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
117 return HttpResponse(self.render_to_string(node, request, path, subpath, extra_context), mimetype=self.template.mimetype)
119 def __unicode__(self):
126 class Contentlet(models.Model):
127 page = models.ForeignKey(Page, related_name='contentlets')
128 name = models.CharField(max_length=255)
129 content = models.TextField()
130 dynamic = models.BooleanField(default=False)
132 def __unicode__(self):
139 class ContentReference(models.Model):
140 page = models.ForeignKey(Page, related_name='contentreferences')
141 name = models.CharField(max_length=255)
142 content_type = models.ForeignKey(ContentType, verbose_name='Content type')
143 content_id = models.PositiveIntegerField(verbose_name='Content ID', blank=True, null=True)
144 content = generic.GenericForeignKey('content_type', 'content_id')
146 def __unicode__(self):
153 register_templatetags('philo.templatetags.containers')
156 register_value_model(Template)
157 register_value_model(Page)