from philo.admin.base import COLLAPSE_CLASSES
from philo.admin.nodes import ViewAdmin
from philo.models.pages import Page, Template, Contentlet, ContentReference
-from philo.forms import TemplateForm
+from philo.forms import TemplateForm, ContentletInlineFormSet, ContentReferenceInlineFormSet
+
+
+class ContentletInline(admin.StackedInline):
+ model = Contentlet
+ extra = 0
+ max_num = 0
+ formset = ContentletInlineFormSet
+ can_delete = False
+ template = 'admin/philo/edit_inline/tabular_container.html'
+
+
+class ContentReferenceInline(admin.StackedInline):
+ model = ContentReference
+ extra = 0
+ max_num = 0
+ formset = ContentReferenceInlineFormSet
+ can_delete = False
+ template = 'admin/philo/edit_inline/tabular_container.html'
class PageAdmin(ViewAdmin):
list_display = ('title', 'template')
list_filter = ('template',)
search_fields = ['title', 'contentlets__content']
-
- def get_fieldsets(self, request, obj=None, **kwargs):
- fieldsets = list(self.fieldsets)
- if obj: # if no obj, creating a new page, thus no template set, thus no containers
- template = obj.template
- if template.documentation:
- fieldsets.append(('Template Documentation', {
- 'description': template.documentation
- }))
- 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)),
- 'classes': ['monospace']
- }))
- 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):
- form = super(PageAdmin, self).get_form(request, obj, **kwargs)
- if obj: # if no obj, creating a new page, thus no template set, thus no containers
- page = obj
- template = page.template
- contentlet_containers, contentreference_containers = template.containers
- for container_name in contentlet_containers:
- initial_content = None
- initial_dynamic = False
- try:
- contentlet = page.contentlets.get(name__exact=container_name)
- initial_content = contentlet.content
- initial_dynamic = contentlet.dynamic
- except Contentlet.DoesNotExist:
- pass
- 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).content.pk
- except (ContentReference.DoesNotExist, AttributeError):
- pass
- form.base_fields[('contentreference_container_%s' % container_name)] = forms.ModelChoiceField(label='References', widget=widgets.ModelLookupWidget(container_content_type), 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()
- template = page.template
- 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()
- 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)]
- try:
- contentreference = page.contentreferences.get(name=container_name)
- except ContentReference.DoesNotExist:
- contentreference = ContentReference(name=container_name, page=page, content_type=container_content_type)
- else:
- if content == None:
- contentreference.delete()
-
- if content is not None:
- contentreference.content_id = content.id
- contentreference.save()
+ inlines = [ContentletInline, ContentReferenceInline] + ViewAdmin.inlines
class TemplateAdmin(admin.ModelAdmin):
-from django.core.exceptions import ValidationError
-from django.forms.models import model_to_dict, fields_for_model, ModelFormMetaclass, ModelForm
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
+from django.forms.models import model_to_dict, fields_for_model, ModelFormMetaclass, ModelForm, BaseInlineFormSet
from django.template import loader, loader_tags, TemplateDoesNotExist, Context, Template as DjangoTemplate
from django.utils.datastructures import SortedDict
from philo.models import Entity, Template
def validate_template(template):
"""
Makes sure that the template and all included or extended templates are valid.
- """
+ """
for node in template.nodelist:
try:
if isinstance(node, loader_tags.ExtendsNode):
return code
class Meta:
- model = Template
\ No newline at end of file
+ model = Template
+
+
+class ContainerInlineFormSet(BaseInlineFormSet):
+ def __init__(self, containers, data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None):
+ # Unfortunately, I need to add some things to BaseInline between its __init__ and its super call, so
+ # a lot of this is repetition.
+
+ # Start cribbed from BaseInline
+ from django.db.models.fields.related import RelatedObject
+ self.save_as_new = save_as_new
+ # is there a better way to get the object descriptor?
+ self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
+ if self.fk.rel.field_name == self.fk.rel.to._meta.pk.name:
+ backlink_value = self.instance
+ else:
+ backlink_value = getattr(self.instance, self.fk.rel.field_name)
+ if queryset is None:
+ queryset = self.model._default_manager
+ qs = queryset.filter(**{self.fk.name: backlink_value}).filter(name__in=containers)
+ # End cribbed from BaseInline
+
+ self.container_instances = []
+ for container in qs:
+ self.container_instances.append(container)
+ containers.remove(container.name)
+ self.extra_containers = containers
+ self.extra = len(self.extra_containers)
+
+ super(BaseInlineFormSet, self).__init__(data, files, prefix, qs)
+
+ def _construct_form(self, i, **kwargs):
+ if i > self.initial_form_count(): # and not kwargs.get('instance'):
+ kwargs['instance'] = self.model(name=self.extra_containers[i - self.initial_form_count() - 1])
+
+ return super(ContainerInlineFormSet, self)._construct_form(i, **kwargs)
+
+
+class ContentletInlineFormSet(ContainerInlineFormSet):
+ def __init__(self, data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None):
+ if instance is None:
+ self.instance = self.fk.rel.to()
+ containers = []
+ else:
+ self.instance = instance
+ containers = list(self.instance.containers[0])
+
+ super(ContentletInlineFormSet, self).__init__(containers, data, files, instance, save_as_new, prefix, queryset)
+
+
+class ContentReferenceInlineFormSet(ContainerInlineFormSet):
+ def __init__(self, data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None):
+ if instance is None:
+ self.instance = self.fk.rel.to()
+ containers = []
+ else:
+ self.instance = instance
+ containers = list(self.instance.containers[1])
+
+ super(ContentReferenceInlineFormSet, self).__init__(containers, data, files, instance, save_as_new, prefix, queryset)
\ No newline at end of file
template = models.ForeignKey(Template, related_name='pages')
title = models.CharField(max_length=255)
+ def get_containers(self):
+ if not hasattr(self, '_containers'):
+ self._containers = self.template.containers
+ return self._containers
+ containers = property(get_containers)
+
def render_to_string(self, node=None, request=None, path=None, subpath=None, extra_context=None):
context = {}
context.update(extra_context or {})
--- /dev/null
+{% load i18n adminmedia %}
+<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
+ <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
+{{ inline_admin_formset.formset.management_form }}
+<fieldset class="module{% if inline_admin_formset.classes %} {{ inline_admin_formset.classes|join:' ' }}{% endif %}">
+ <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+ {{ inline_admin_formset.formset.non_form_errors }}
+ <table>
+ <thead><tr>
+ {% for field in inline_admin_formset.fields %}
+ {% if not field.widget.is_hidden %}
+ <th{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}</th>
+ {% endif %}
+ {% endfor %}
+ {% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
+ </tr></thead>
+
+ <tbody>
+ {% for inline_admin_form in inline_admin_formset %}
+ {% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
+ {{ inline_admin_form.fk_field.field }}
+ {% spaceless %}
+ {% for fieldset in inline_admin_form %}
+ {% for line in fieldset %}
+ {% for field in line %}
+ {% if field.is_hidden %} {{ field.field }} {% endif %}
+ {% endfor %}
+ {% endfor %}
+ {% endfor %}
+ {% endspaceless %}
+ {% if inline_admin_form.form.non_field_errors %}
+ <tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
+ {% endif %}
+ <tr class="{% cycle "row1" "row2" %} {% if forloop.last %} empty-form{% endif %}"
+ id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
+ <th>{{ inline_admin_form.form.name.as_hidden }}{{ inline_admin_form.form.initial.name|capfirst }}</th>
+ {% for fieldset in inline_admin_form %}
+ {% for line in fieldset %}
+ {% for field in line %}
+ {% if field.field.name != 'name' %}
+ <td class="{{ field.field.name }}">
+ {% if field.is_readonly %}
+ <p>{{ field.contents }}</p>
+ {% else %}
+ {{ field.field.errors.as_ul }}
+ {{ field.field }}
+ {% endif %}
+ </td>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ {% endfor %}
+ {% if inline_admin_formset.formset.can_delete %}
+ <td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</fieldset>
+ </div>
+</div>
+
+<script type="text/javascript">
+(function($) {
+ $(document).ready(function($) {
+ var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
+ var alternatingRows = function(row) {
+ $(rows).not(".add-row").removeClass("row1 row2")
+ .filter(":even").addClass("row1").end()
+ .filter(rows + ":odd").addClass("row2");
+ }
+ var reinitDateTimeShortCuts = function() {
+ // Reinitialize the calendar and clock widgets by force
+ if (typeof DateTimeShortcuts != "undefined") {
+ $(".datetimeshortcuts").remove();
+ DateTimeShortcuts.init();
+ }
+ }
+ var updateSelectFilter = function() {
+ // If any SelectFilter widgets are a part of the new form,
+ // instantiate a new SelectFilter instance for it.
+ if (typeof SelectFilter != "undefined"){
+ $(".selectfilter").each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% admin_media_prefix %}");
+ })
+ $(".selectfilterstacked").each(function(index, value){
+ var namearr = value.name.split('-');
+ SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% admin_media_prefix %}");
+ })
+ }
+ }
+ var initPrepopulatedFields = function(row) {
+ row.find('.prepopulated_field').each(function() {
+ var field = $(this);
+ var input = field.find('input, select, textarea');
+ var dependency_list = input.data('dependency_list') || [];
+ var dependencies = row.find(dependency_list.join(',')).find('input, select, textarea');
+ if (dependencies.length) {
+ input.prepopulate(dependencies, input.attr('maxlength'));
+ }
+ });
+ }
+ $(rows).formset({
+ prefix: "{{ inline_admin_formset.formset.prefix }}",
+ addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
+ formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
+ deleteCssClass: "inline-deletelink",
+ deleteText: "{% trans "Remove" %}",
+ emptyCssClass: "empty-form",
+ removed: alternatingRows,
+ added: (function(row) {
+ initPrepopulatedFields(row);
+ reinitDateTimeShortCuts();
+ updateSelectFilter();
+ alternatingRows(row);
+ })
+ });
+ });
+})(django.jQuery);
+</script>