batiste/django-page-cms

View on GitHub
pages/admin/forms.py

Summary

Maintainability
C
1 day
Test Coverage
# -*- coding: utf-8 -*-
"""Page CMS forms"""
from django import forms
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from django.conf import settings as global_settings

from pages import settings
from pages.models import Page, Content

from pages.urlconf_registry import get_choices
from pages.widgets import LanguageChoiceWidget
import collections

error_dict = {
    'another_page_error': _('Another page with this slug already exists'),
    'sibling_position_error': _('A sibling with this slug already exists at the targeted position'),
    'child_error': _('A child with this slug already exists at the targeted position'),
    'sibling_error': _('A sibling with this slug already exists'),
    'sibling_root_error': _('A sibling with this slug already exists at the root level'),
}

def automatic_slug_renaming(slug, is_slug_safe):
    """Helper to add numbers to slugs"""

    if not isinstance(is_slug_safe, collections.Callable):
        raise TypeError('is_slug_safe must be callable')

    if is_slug_safe(slug):
       return slug

    count = 2
    new_slug = slug + "-" + str(count)
    while not is_slug_safe(new_slug):
        count = count + 1
        new_slug = slug + "-" + str(count)
    return new_slug

def unique_slug_required(form, slug):
    """Enforce a unique slug accross all pages and websistes."""

    if hasattr(form, 'instance') and form.instance.id:
        if Content.objects.exclude(page=form.instance).filter(
            body=slug, type="slug").count():
            raise forms.ValidationError(error_dict['another_page_error'])
    elif Content.objects.filter(body=slug, type="slug").count():
        raise forms.ValidationError(error_dict['another_page_error'])
    return slug

def intersect_sites_method(form):
    """Return a method to intersect sites."""
    if settings.PAGE_USE_SITE_ID:
        if settings.PAGE_HIDE_SITES:
            site_ids = [global_settings.SITE_ID]
        else:
            site_ids = [int(x) for x in form.data.getlist('sites')]
        def intersects_sites(sibling):
            return sibling.sites.filter(id__in=site_ids).count() > 0
    else:
        def intersects_sites(sibling):
            return True
    return intersects_sites

def make_form(model_, placeholders):

    # a new form is needed every single time as some
    # initial data are bound
    class PageForm(forms.ModelForm):
        """Form for page creation"""

        def __init__(self, *args, **kwargs):
            super(PageForm, self).__init__(*args, **kwargs)
            for p in placeholders:
                if not self.fields[p.ctype]:
                    self.fields[p.ctype] = forms.TextField()

        target = forms.IntegerField(required=False, widget=forms.HiddenInput)
        position = forms.CharField(required=False, widget=forms.HiddenInput)

        class Meta:
            model = model_
            exclude = ('author', 'last_modification_date', 'parent')

        title = forms.CharField(
            label=_('Title'),
            widget=forms.TextInput(),
        )
        slug = forms.CharField(
            label=_('Slug'),
            widget=forms.TextInput(),
            help_text=_('The slug will be used to create the page URL, it must be unique among the other pages of the same level.')
        )

        language = forms.ChoiceField(
            label=_('Language'),
            choices=settings.PAGE_LANGUAGES,
            widget=LanguageChoiceWidget()
        )
        template = forms.ChoiceField(
            required=False,
            label=_('Template'),
            choices=settings.get_page_templates(),
        )
        delegate_to = forms.ChoiceField(
            required=False,
            label=_('Delegate to application'),
            choices=get_choices(),
        )
        freeze_date = forms.DateTimeField(
            required=False,
            label=_('Freeze'),
            help_text=_("Don't publish any content after this date. Format is 'Y-m-d H:M:S'")
            # those make tests fail miserably
            #widget=widgets.AdminSplitDateTime()
            #widget=widgets.AdminTimeWidget()
        )

        def clean_slug(self):
            """Handle move action on the pages"""

            slug = slugify(self.cleaned_data['slug'])
            target = self.data.get('target', None)
            position = self.data.get('position', None)

            # this enforce a unique slug for every page
            if settings.PAGE_AUTOMATIC_SLUG_RENAMING:
                def is_slug_safe(slug):
                    content = Content.objects.get_content_slug_by_slug(slug)
                    if content is None:
                        return True
                    if self.instance.id:
                        if content.page.id == self.instance.id:
                            return True
                    else:
                        return False

                return automatic_slug_renaming(slug, is_slug_safe)

            if settings.PAGE_UNIQUE_SLUG_REQUIRED:
                # We can return here as not futher checks
                # are necessary
                return unique_slug_required(self, slug)

            intersects_sites = intersect_sites_method(self)

            if not settings.PAGE_UNIQUE_SLUG_REQUIRED:
                if target and position:
                    target = Page.objects.get(pk=target)
                    if position in ['right', 'left']:
                        slugs = [sibling.slug() for sibling in
                                target.get_siblings()
                                if intersects_sites(sibling)]
                        slugs.append(target.slug())
                        if slug in slugs:
                            raise forms.ValidationError(error_dict['sibling_position_error'])
                    if position == 'first-child':
                        if slug in [sibling.slug() for sibling in
                                    target.get_children()
                                    if intersects_sites(sibling)]:
                            raise forms.ValidationError(error_dict['child_error'])
                else:
                    if self.instance.id:
                        if (slug in [sibling.slug() for sibling in
                            self.instance.get_siblings().exclude(
                                id=self.instance.id
                            ) if intersects_sites(sibling)]):
                            raise forms.ValidationError(error_dict['sibling_error'])
                    else:
                        if slug in [sibling.slug() for sibling in
                                    Page.objects.root()
                                    if intersects_sites(sibling)]:
                            raise forms.ValidationError(error_dict['sibling_root_error'])
            return slug

    return PageForm