Initial extension of containers to support referencing objects of arbitrary type.
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Wed, 19 May 2010 09:30:23 +0000 (05:30 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Wed, 19 May 2010 09:30:23 +0000 (05:30 -0400)
admin.py
models.py
templatetags/containers.py

index faf4b71..12b9183 100644 (file)
--- a/admin.py
+++ b/admin.py
@@ -82,12 +82,15 @@ class PageAdmin(EntityAdmin):
                if obj: # if no obj, creating a new page, thus no template set, thus no containers
                        page = obj
                        template = page.template
                if obj: # if no obj, creating a new page, thus no template set, thus no containers
                        page = obj
                        template = page.template
-                       containers = template.containers
-                       if len(containers) > 0:
-                               for container in containers:
-                                       fieldsets.append((('Container: %s' % container), {
-                                               'fields': (('container_content_%s' % container), ('container_dynamic_%s' % container))
-                                       }))
+                       contentlet_containers, contentreference_containers = template.containers
+                       for container_name in contentlet_containers:
+                               fieldsets.append((('Container: %s' % container_name), {
+                                       'fields': (('contentlet_container_content_%s' % container_name), ('contentlet_container_dynamic_%s' % container_name))
+                               }))
+                       for container_name, container_content_type in contentreference_containers:
+                               fieldsets.append((('Container: %s' % container_name), {
+                                       'fields': (('contentreference_container_%s' % container_name),)
+                               }))
                return fieldsets
        
        def get_form(self, request, obj=None, **kwargs):
                return fieldsets
        
        def get_form(self, request, obj=None, **kwargs):
@@ -95,34 +98,47 @@ class PageAdmin(EntityAdmin):
                if obj: # if no obj, creating a new page, thus no template set, thus no containers
                        page = obj
                        template = page.template
                if obj: # if no obj, creating a new page, thus no template set, thus no containers
                        page = obj
                        template = page.template
-                       containers = template.containers
-                       for container in containers:
+                       contentlet_containers, contentreference_containers = template.containers
+                       for container_name in contentlet_containers:
                                initial_content = None
                                initial_dynamic = False
                                try:
                                initial_content = None
                                initial_dynamic = False
                                try:
-                                       contentlet = page.contentlets.get(name__exact=container)
+                                       contentlet = page.contentlets.get(name__exact=container_name)
                                        initial_content = contentlet.content
                                        initial_dynamic = contentlet.dynamic
                                except Contentlet.DoesNotExist:
                                        pass
                                        initial_content = contentlet.content
                                        initial_dynamic = contentlet.dynamic
                                except Contentlet.DoesNotExist:
                                        pass
-                               form.base_fields[('container_content_%s' % container)] = forms.CharField(label='Content', widget=forms.Textarea(), initial=initial_content, required=False)
-                               form.base_fields[('container_dynamic_%s' % container)] = forms.BooleanField(label='Dynamic', help_text='Specify whether this content contains dynamic template code', initial=initial_dynamic, required=False)
+                               form.base_fields[('contentlet_container_content_%s' % container_name)] = forms.CharField(label='Content', widget=forms.Textarea(), initial=initial_content, required=False)
+                               form.base_fields[('contentlet_container_dynamic_%s' % container_name)] = forms.BooleanField(label='Dynamic', help_text='Specify whether this content contains dynamic template code', initial=initial_dynamic, required=False)
+                       for container_name, container_content_type in contentreference_containers:
+                               initial_content = None
+                               try:
+                                       initial_content = page.contentreferences.get(name__exact=container_name, content_type=container_content_type)
+                               except ContentReference.DoesNotExist:
+                                       pass
+                               form.base_fields[('contentreference_container_%s' % container_name)] = forms.ModelChoiceField(label='References', initial=initial_content, required=False, queryset=container_content_type.model_class().objects.all())
                return form
        
        def save_model(self, request, page, form, change):
                page.save()
                return form
        
        def save_model(self, request, page, form, change):
                page.save()
-               
                template = page.template
                template = page.template
-               containers = template.containers
-               for container in containers:
-                       if (("container_content_%s" % container) in form.cleaned_data) and (("container_dynamic_%s" % container) in form.cleaned_data):
-                               content = form.cleaned_data[('container_content_%s' % container)]
-                               dynamic = form.cleaned_data[('container_dynamic_%s' % container)]
-                               contentlet, created = page.contentlets.get_or_create(name=container, defaults={'content': content, 'dynamic': dynamic})
+               contentlet_containers, contentreference_containers = template.containers
+               for container_name in contentlet_containers:
+                       if (('contentlet_container_content_%s' % container_name) in form.cleaned_data) and (('contentlet_container_dynamic_%s' % container_name) in form.cleaned_data):
+                               content = form.cleaned_data[('contentlet_container_content_%s' % container_name)]
+                               dynamic = form.cleaned_data[('contentlet_container_dynamic_%s' % container_name)]
+                               contentlet, created = page.contentlets.get_or_create(name=container_name, defaults={'content': content, 'dynamic': dynamic})
                                if not created:
                                        contentlet.content = content
                                        contentlet.dynamic = dynamic
                                        contentlet.save()
                                if not created:
                                        contentlet.content = content
                                        contentlet.dynamic = dynamic
                                        contentlet.save()
+               for container_name, container_content_type in contentreference_containers:
+                       if ('contentreference_container_%s' % container_name) in form.cleaned_data:
+                               content = form.cleaned_data[('contentreference_container_%s' % container_name)]
+                               contentreference, created = page.contentreferences.get_or_create(name=container_name, defaults={'content': content})
+                               if not created:
+                                       contentreference.content = content
+                                       contentreference.save()
 
 
 admin.site.register(Collection, CollectionAdmin)
 
 
 admin.site.register(Collection, CollectionAdmin)
index 1d255c7..41e9a15 100644 (file)
--- a/models.py
+++ b/models.py
@@ -282,37 +282,46 @@ class Template(TreeModel):
        @property
        def containers(self):
                """
        @property
        def containers(self):
                """
-               Returns a list of names of contentlets referenced by containers. 
+               Returns a tuple where the first item is a list of names of contentlets referenced by containers,
+               and the second item is a list of tuples of names and contenttypes of contentreferences referenced by containers.
                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 container_node_names(template):
-                       def nodelist_container_node_names(nodelist):
-                               names = []
+               def container_nodes(template):
+                       def nodelist_container_nodes(nodelist):
+                               nodes = []
                                for node in nodelist:
                                        try:
                                                for nodelist_name in ('nodelist', 'nodelist_loop', 'nodelist_empty', 'nodelist_true', 'nodelist_false'):
                                                        if hasattr(node, nodelist_name):
                                for node in nodelist:
                                        try:
                                                for nodelist_name in ('nodelist', 'nodelist_loop', 'nodelist_empty', 'nodelist_true', 'nodelist_false'):
                                                        if hasattr(node, nodelist_name):
-                                                               names.extend(nodelist_container_node_names(getattr(node, nodelist_name)))
+                                                               nodes.extend(nodelist_container_nodes(getattr(node, nodelist_name)))
                                                if isinstance(node, ContainerNode):
                                                if isinstance(node, ContainerNode):
-                                                       names.append(node.name)
+                                                       nodes.append(node)
                                                elif isinstance(node, ExtendsNode):
                                                        extended_template = node.get_parent(Context())
                                                        if extended_template:
                                                elif isinstance(node, ExtendsNode):
                                                        extended_template = node.get_parent(Context())
                                                        if extended_template:
-                                                               names.extend(container_node_names(extended_template))
+                                                               nodes.extend(container_nodes(extended_template))
                                                elif isinstance(node, ConstantIncludeNode):
                                                        included_template = node.template
                                                        if included_template:
                                                elif isinstance(node, ConstantIncludeNode):
                                                        included_template = node.template
                                                        if included_template:
-                                                               names.extend(container_node_names(included_template))
+                                                               nodes.extend(container_nodes(included_template))
                                                elif isinstance(node, IncludeNode):
                                                        included_template = get_template(node.template_name.resolve(Context()))
                                                        if included_template:
                                                elif isinstance(node, IncludeNode):
                                                        included_template = get_template(node.template_name.resolve(Context()))
                                                        if included_template:
-                                                               names.extend(container_node_names(included_template))
+                                                               nodes.extend(container_nodes(included_template))
                                        except:
                                                pass # fail for this node
                                        except:
                                                pass # fail for this node
-                               return names
-                       return nodelist_container_node_names(template.nodelist)
-               return set(container_node_names(self.django_template))
+                               return nodes
+                       return nodelist_container_nodes(template.nodelist)
+               all_nodes = container_nodes(self.django_template)
+               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
        
        def __unicode__(self):
                return self.get_path(u' › ', 'name')
        
        def __unicode__(self):
                return self.get_path(u' › ', 'name')
@@ -349,6 +358,14 @@ class Contentlet(models.Model):
        dynamic = models.BooleanField(default=False)
 
 
        dynamic = models.BooleanField(default=False)
 
 
+class ContentReference(models.Model):
+       page = models.ForeignKey(Page, related_name='contentreferences')
+       name = models.CharField(max_length=255)
+       content_type = models.ForeignKey(ContentType, verbose_name='Content type')
+       content_id = models.PositiveIntegerField(verbose_name='Content ID')
+       content = generic.GenericForeignKey('content_type', 'content_id')
+
+
 register_templatetags('philo.templatetags.containers')
 
 
 register_templatetags('philo.templatetags.containers')
 
 
index 9bf056f..cc6b313 100644 (file)
@@ -2,51 +2,77 @@ from django import template
 from django.conf import settings
 from django.utils.safestring import SafeUnicode, mark_safe
 from django.core.exceptions import ObjectDoesNotExist
 from django.conf import settings
 from django.utils.safestring import SafeUnicode, mark_safe
 from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.contenttypes.models import ContentType
+
 
 register = template.Library()
 
 
 class ContainerNode(template.Node):
 
 register = template.Library()
 
 
 class ContainerNode(template.Node):
-       def __init__(self, name, as_var=None):
+       def __init__(self, name, references=None, as_var=None):
                self.name = name
                self.as_var = as_var
                self.name = name
                self.as_var = as_var
+               self.references = references
        def render(self, context):
        def render(self, context):
-               page = None
+               content = settings.TEMPLATE_STRING_IF_INVALID
                if 'page' in context:
                        page = context['page']
                if 'page' in context:
                        page = context['page']
-               if page:
-                       contentlet = None
-                       try:
-                               contentlet = page.contentlets.get(name__exact=self.name)
-                       except ObjectDoesNotExist:
-                               pass
-                       if contentlet:
-                               content = contentlet.content
-                               if contentlet.dynamic:
-                                       try:
-                                               content = mark_safe(template.Template(content, name=contentlet.name).render(context))
-                                       except template.TemplateSyntaxError, error:
-                                               content = ''
-                                               if settings.DEBUG:
-                                                       content = ('[Error parsing contentlet \'%s\': %s]' % self.name, error)
-                               if self.as_var:
-                                       context[self.as_var] = content
-                                       content = ''
-                               return content
-               return ''
+                       if self.references:
+                               try:
+                                       contentreference = page.contentreferences.get(name__exact=self.name, content_type=self.references)
+                                       content = contentreference.content
+                               except ObjectDoesNotExist:
+                                       pass
+                       else:
+                               try:
+                                       contentlet = page.contentlets.get(name__exact=self.name)
+                                       if contentlet.dynamic:
+                                               try:
+                                                       content = mark_safe(template.Template(contentlet.content, name=contentlet.name).render(context))
+                                               except template.TemplateSyntaxError, error:
+                                                       if settings.DEBUG:
+                                                               content = ('[Error parsing contentlet \'%s\': %s]' % self.name, error)
+                                       else:
+                                               content = contentlet.content
+                               except ObjectDoesNotExist:
+                                       pass
+               if content and self.as_var:
+                       context[self.as_var] = content
+                       return ''
+               return content
 
 
 def do_container(parser, token):
        """
 
 
 def do_container(parser, token):
        """
-       {% container <name> [as <variable>] %}
+       {% container <name> [[references <type>] as <variable>] %}
        """
        params = token.split_contents()
        """
        params = token.split_contents()
-       if len(params) >= 2: # without as_var
+       if len(params) >= 2:
                name = params[1].strip('"')
                name = params[1].strip('"')
+               references = None
                as_var = None
                as_var = None
-               if len(params) == 4:
-                       as_var = params[3]
-               return ContainerNode(name, as_var)
+               if len(params) > 2:
+                       remaining_tokens = params[2:]
+                       while remaining_tokens:
+                               option_token = remaining_tokens.pop(0)
+                               if option_token == 'references':
+                                       try:
+                                               app_label, model = remaining_tokens.pop(0).strip('"').split('.')
+                                               references = ContentType.objects.get(app_label=app_label, model=model)
+                                       except IndexError:
+                                               raise template.TemplateSyntaxError('"container" template tag option "references" requires an argument specifying a content type')
+                                       except ValueError:
+                                               raise template.TemplateSyntaxError('"container" template tag option "references" requires an argument of the form app_label.model (see django.contrib.contenttypes)')
+                                       except ObjectDoesNotExist:
+                                               raise template.TemplateSyntaxError('"container" template tag option "references" requires an argument of the form app_label.model which refers to an installed content type (see django.contrib.contenttypes)')
+                               elif option_token == 'as':
+                                       try:
+                                               as_var = remaining_tokens.pop(0)
+                                       except IndexError:
+                                               raise template.TemplateSyntaxError('"container" template tag option "as" requires an argument specifying a variable name')
+                       if references and not as_var:
+                               raise template.TemplateSyntaxError('"container" template tags using "references" option require additional use of the "as" option specifying a variable name')
+               return ContainerNode(name, references, as_var)
        else: # error
        else: # error
-               raise template.TemplateSyntaxError('do_container template tag provided with invalid arguments')
-register.tag('container', do_container)
+               raise template.TemplateSyntaxError('"container" template tag provided without arguments (at least one required)')
+register.tag('container', do_container)
\ No newline at end of file