from django.contrib import admin
-from philo.admin.base import EntityAdmin, TreeEntityAdmin
-from philo.models import Node, Redirect, File
+from philo.admin.base import EntityAdmin, TreeEntityAdmin, COLLAPSE_CLASSES
+from philo.models import Node, Redirect, File, NodeNavigationOverride
+from philo.forms import NodeWithOverrideForm
+
+
+class ChildNavigationOverrideInline(admin.StackedInline):
+ fk_name = 'parent'
+ model = NodeNavigationOverride
+ sortable_field_name = 'order'
+ verbose_name = 'child'
+ verbose_name_plural = 'children'
+ extra = 0
+ max_num = 0
class NodeAdmin(TreeEntityAdmin):
- pass
+ form = NodeWithOverrideForm
+ fieldsets = (
+ (None, {
+ 'fields': ('parent', 'slug', 'view_content_type', 'view_object_id'),
+ }),
+ ('Navigation Overrides', {
+ 'fields': ('title', 'url', 'child_navigation'),
+ 'classes': COLLAPSE_CLASSES
+ })
+ )
+ inlines = [ChildNavigationOverrideInline] + TreeEntityAdmin.inlines
class ViewAdmin(EntityAdmin):
from django.template import loader, loader_tags, TemplateDoesNotExist, Context, Template as DjangoTemplate
from django.utils.datastructures import SortedDict
from philo.admin.widgets import ModelLookupWidget
-from philo.models import Entity, Template, Contentlet, ContentReference, Attribute
+from philo.models import Entity, Template, Contentlet, ContentReference, Attribute, Node, NodeNavigationOverride
from philo.utils import fattr
name, content_type = self.extra_containers[i - self.initial_form_count() - 1]
kwargs['instance'] = self.model(name=name, content_type=content_type)
- return super(ContentReferenceInlineFormSet, self)._construct_form(i, **kwargs)
\ No newline at end of file
+ return super(ContentReferenceInlineFormSet, self)._construct_form(i, **kwargs)
+
+
+class NodeWithOverrideForm(forms.ModelForm):
+ title = NodeNavigationOverride._meta.get_field('title').formfield()
+ url = NodeNavigationOverride._meta.get_field('url').formfield()
+ child_navigation = NodeNavigationOverride._meta.get_field('child_navigation').formfield(required=False)
+
+ def __init__(self, *args, **kwargs):
+ super(NodeWithOverrideForm, self).__init__(*args, **kwargs)
+ if self.instance.pk:
+ self._override = override = self.get_override(self.instance)
+ self.initial.update({
+ 'title': override.title,
+ 'url': override.url,
+ 'child_navigation': override.child_navigation_json
+ })
+
+ def get_override(self, instance):
+ try:
+ return NodeNavigationOverride.objects.get(parent=self.instance.parent, child=self.instance)
+ except NodeNavigationOverride.DoesNotExist:
+ override = NodeNavigationOverride(parent=self.instance.parent, child=self.instance)
+ override.child_navigation = None
+ return override
+
+ def save(self, commit=True):
+ obj = super(NodeWithOverrideForm, self).save(commit)
+ cleaned_data = self.cleaned_data
+ override = self.get_override(obj)
+
+ # Override information should only be set if there was no previous override or if the
+ # information was just manually set - i.e. was not equal to the data on the cached override.
+ if not override.pk or cleaned_data['title'] != self._override.title or cleaned_data['url'] != self._override.url or cleaned_data['child_navigation'] != self._override.child_navigation:
+ override.title = self.cleaned_data['title']
+ override.url = self.cleaned_data['url']
+ override.child_navigation = self.cleaned_data['child_navigation']
+ override.save()
+ return obj
+
+ class Meta:
+ model = Node
\ No newline at end of file
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'NodeNavigationOverride'
+ db.create_table('philo_nodenavigationoverride', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name='child_navigation_overrides', to=orm['philo.Node'])),
+ ('child', self.gf('django.db.models.fields.related.ForeignKey')(related_name='navigation_overrides', to=orm['philo.Node'])),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+ ('url', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
+ ('order', self.gf('django.db.models.fields.PositiveSmallIntegerField')(null=True, blank=True)),
+ ('child_navigation', self.gf('philo.models.fields.JSONField')(default=None)),
+ ('hide', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)),
+ ))
+ db.send_create_signal('philo', ['NodeNavigationOverride'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'NodeNavigationOverride'
+ db.delete_table('philo_nodenavigationoverride')
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.foreignkeyvalue': {
+ 'Meta': {'object_name': 'ForeignKeyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.jsonvalue': {
+ 'Meta': {'object_name': 'JSONValue'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.manytomanyvalue': {
+ 'Meta': {'object_name': 'ManyToManyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'values': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['philo.ForeignKeyValue']", 'null': 'True', 'blank': 'True'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.nodenavigationoverride': {
+ 'Meta': {'unique_together': "(('parent', 'child'),)", 'object_name': 'NodeNavigationOverride'},
+ 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'navigation_overrides'", 'to': "orm['philo.Node']"}),
+ 'child_navigation': ('philo.models.fields.JSONField', [], {'default': 'None'}),
+ 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'child_navigation_overrides'", 'to': "orm['philo.Node']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'NodeNavigationOverride.parent'
+ db.alter_column('philo_nodenavigationoverride', 'parent_id', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, null=True, to=orm['philo.Node']))
+
+
+ def backwards(self, orm):
+
+ # Changing field 'NodeNavigationOverride.parent'
+ db.alter_column('philo_nodenavigationoverride', 'parent_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['philo.Node']))
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.foreignkeyvalue': {
+ 'Meta': {'object_name': 'ForeignKeyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.jsonvalue': {
+ 'Meta': {'object_name': 'JSONValue'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.manytomanyvalue': {
+ 'Meta': {'object_name': 'ManyToManyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'values': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['philo.ForeignKeyValue']", 'null': 'True', 'blank': 'True'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.nodenavigationoverride': {
+ 'Meta': {'unique_together': "(('parent', 'child'),)", 'object_name': 'NodeNavigationOverride'},
+ 'child': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'navigation_overrides'", 'to': "orm['philo.Node']"}),
+ 'child_navigation': ('philo.models.fields.JSONField', [], {'default': 'None'}),
+ 'hide': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'child_navigation_overrides'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
from philo.validators import RedirectValidator
from philo.exceptions import ViewCanNotProvideSubpath, ViewDoesNotProvideSubpaths, AncestorDoesNotExist
from philo.signals import view_about_to_render, view_finished_rendering
+from mptt.templatetags.mptt_tags import cache_tree_children
_view_content_type_limiter = ContentTypeSubclassLimiter(None)
except AncestorDoesNotExist, ViewDoesNotExist:
return None
- def get_navigation(self, depth=DEFAULT_NAVIGATION_DEPTH, current_depth=0, found_node_pks=None):
- navigation = self.view.get_navigation(self, depth, current_depth)
+ def get_navigation(self, depth=DEFAULT_NAVIGATION_DEPTH):
+ max_depth = depth + self.get_level()
+ tree = cache_tree_children(self.get_descendants(include_self=True).filter(level__lte=max_depth))
- if depth == current_depth:
- return navigation
- import pdb
- pdb.set_trace()
- found_node_pks = found_node_pks or [self.pk]
- ordered_child_pks = NodeNavigationOverride.objects.filter(parent=self, child__parent=self).values_list('child__pk', flat=True)
-
- children = self.children.exclude(pk__in=found_node_pks)
- ordered_children = children.filter(pk__in=ordered_child_pks)
- unordered_children = children.exclude(pk__in=ordered_child_pks)
-
- children = list(ordered_children) + list(unordered_children)
-
- if children:
- child_navigation = []
- for child in children:
- found_node_pks.append(child.pk)
- try:
- child_navigation.append(child.get_navigation(depth, current_depth + 1, found_node_pks))
- except NotImplementedError:
- pass
+ def get_nav(parent, nodes):
+ node_overrides = dict([(override.child.pk, override) for override in NodeNavigationOverride.objects.filter(parent=parent, child__in=nodes).select_related('child')])
- if child_navigation:
- if 'children' in navigation:
- navigation['children'] += child_navigation
+ navigation_list = []
+
+ for node in nodes:
+ node._override = node_overrides.get(node.pk, None)
+
+ if node._override:
+ if node._override.hide:
+ continue
+ navigation = node._override.get_navigation(node, max_depth)
else:
- navigation['children'] = child_navigation
+ navigation = node.view.get_navigation(node, max_depth)
+
+ if not node.is_leaf_node() and node.get_level() < max_depth:
+ children = navigation.get('children', [])
+ children += get_nav(node, node.get_children())
+ navigation['children'] = children
+
+ if 'children' in navigation:
+ navigation['children'].sort(cmp=lambda x,y: cmp(x['order'], y['order']))
+
+ navigation_list.append(navigation)
+
+ return navigation_list
- return navigation
+ return get_nav(self.parent, tree)
def save(self):
super(Node, self).save()
class NodeNavigationOverride(Entity):
- parent = models.ForeignKey(Node, related_name="navigation_override_child_set")
- child = models.OneToOneField(Node, related_name="navigation_override")
- url = models.CharField(max_length=200, validators=[RedirectValidator()], blank=True)
+ parent = models.ForeignKey(Node, related_name="child_navigation_overrides", blank=True, null=True)
+ child = models.ForeignKey(Node, related_name="navigation_overrides")
+
title = models.CharField(max_length=100, blank=True)
+ url = models.CharField(max_length=200, validators=[RedirectValidator()], blank=True)
order = models.PositiveSmallIntegerField(blank=True, null=True)
child_navigation = JSONField()
+ hide = models.BooleanField()
def get_navigation(self, node, depth, current_depth):
if self.child_navigation:
depth = current_depth
- default = node.get_navigation(depth, current_depth)
+ default = node.view.get_navigation(depth, current_depth)
if self.url:
default['url'] = self.url
if self.title:
default['title'] = self.title
- if self.child_navigation:
+ if self.order:
+ default['order'] = self.order
+ if isinstance(self.child_navigation, list):
if 'children' in default:
default['children'] += self.child_navigation
else:
default['children'] = self.child_navigation
+ return default
class Meta:
ordering = ['order']
+ unique_together = ('parent', 'child',)
+ app_label = 'philo'
class View(Entity):
def actually_render_to_response(self, request, extra_context=None):
raise NotImplementedError('View subclasses must implement render_to_response.')
- def get_navigation(self, node, depth, current_depth):
+ def get_navigation(self, node, max_depth):
+ """
+ Subclasses should implement get_navigation to support auto-generated navigation.
+ max_depth is the deepest `level` that should be generated; node is the node that
+ is asking for the navigation. This method should return a dictionary of the form:
+ {
+ 'url': url,
+ 'title': title,
+ 'order': order, # None for no ordering.
+ 'children': [ # Optional
+ <similar child navigation dictionaries>
+ ]
+ }
+ """
raise NotImplementedError('View subclasses must implement get_navigation.')
class Meta:
if errors:
raise ValidationError(errors)
- def get_navigation(self, node, depth, current_depth):
+ def get_navigation(self, node, max_depth):
return {
'url': node.get_absolute_url(),
- 'title': self.title
+ 'title': self.title,
+ 'order': None,
}
class Meta: