troggle-unchained/feincms/module/page/models.py

233 lines
7.1 KiB
Python
Raw Normal View History

from django.conf import settings
from django.db import models
from django.db.models import Q
from django.http import Http404
from django.utils import translation
from django.utils.translation import ugettext_lazy as _
import mptt
from feincms.models import TypeRegistryMetaClass, Region, Template,\
Base, ContentProxy
def get_object(path, fail_silently=False):
dot = path.rindex('.')
try:
return getattr(__import__(path[:dot], {}, {}, ['']), path[dot+1:])
except ImportError:
if not fail_silently:
raise
return None
class PagePretender(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def get_absolute_url(self):
return self.url
class NavigationExtension(object):
__metaclass__ = TypeRegistryMetaClass
name = _('navigation extension')
def children(self, page, **kwargs):
raise NotImplementedError
class PageManager(models.Manager):
def active(self):
return self.filter(active=True)
def page_for_path(self, path, raise404=False):
"""
Return a page for a path.
Example:
Page.objects.page_for_path(request.path)
"""
stripped = path.strip('/')
try:
return self.active().filter(override_url='/%s/' % stripped)[0]
except IndexError:
pass
tokens = stripped.split('/')
count = len(tokens)
filters = {'%sisnull' % ('parent__' * count): True}
for n, token in enumerate(tokens):
filters['%sslug' % ('parent__' * (count-n-1))] = token
try:
return self.active().filter(**filters)[0]
except IndexError:
if raise404:
raise Http404
raise self.model.DoesNotExist
def page_for_path_or_404(self, path):
"""
Wrapper for page_for_path which raises a Http404 if no page
has been found for the passed path.
"""
return self.page_for_path(path, raise404=True)
def best_match_for_path(self, path, raise404=False):
"""
Return the best match for a path.
"""
tokens = path.strip('/').split('/')
for count in range(len(tokens), -1, -1):
try:
return self.page_for_path('/'.join(tokens[:count]))
except self.model.DoesNotExist:
pass
if raise404:
raise Http404
return None
def in_navigation(self):
return self.active().filter(in_navigation=True)
def toplevel_navigation(self):
return self.in_navigation().filter(parent__isnull=True)
def for_request(self, request, raise404=False):
page = self.page_for_path(request.path, raise404)
page.setup_request(request)
return page
def for_request_or_404(self, request):
return self.page_for_path_or_404(request.path, raise404=True)
def best_match_for_request(self, request, raise404=False):
page = self.best_match_for_path(request.path, raise404)
page.setup_request(request)
return page
def from_request(self, request):
if hasattr(request, '_feincms_page'):
return request._feincms_page
return self.for_request(request)
class Page(Base):
active = models.BooleanField(_('active'), default=False)
# structure and navigation
title = models.CharField(_('title'), max_length=100,
help_text=_('This is used for the generated navigation too.'))
slug = models.SlugField()
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
in_navigation = models.BooleanField(_('in navigation'), default=True)
override_url = models.CharField(_('override URL'), max_length=200, blank=True,
help_text=_('Override the target URL for the navigation.'))
redirect_to = models.CharField(_('redirect to'), max_length=200, blank=True,
help_text=_('Target URL for automatic redirects.'))
_cached_url = models.CharField(_('Cached URL'), max_length=200, blank=True,
editable=False, default='')
# navigation extensions
NE_CHOICES = [(
'%s.%s' % (cls.__module__, cls.__name__), cls.name) for cls in NavigationExtension.types]
navigation_extension = models.CharField(_('navigation extension'),
choices=NE_CHOICES, blank=True, max_length=50,
help_text=_('Select the module providing subpages for this page if you need to customize the navigation.'))
# content
_content_title = models.TextField(_('content title'), blank=True,
help_text=_('The first line is the main title, the following lines are subtitles.'))
# meta stuff TODO keywords and description?
_page_title = models.CharField(_('page title'), max_length=100, blank=True,
help_text=_('Page title for browser window. Same as title by default.'))
meta_keywords = models.TextField(_('meta keywords'), blank=True,
help_text=_('This will be prepended to the default keyword list.'))
meta_description = models.TextField(_('meta description'), blank=True,
help_text=_('This will be prepended to the default description.'))
# language
language = models.CharField(_('language'), max_length=10,
choices=settings.LANGUAGES)
translations = models.ManyToManyField('self', blank=True)
class Meta:
ordering = ['tree_id', 'lft']
verbose_name = _('page')
verbose_name_plural = _('pages')
objects = PageManager()
def __unicode__(self):
return u'%s (%s)' % (self.title, self.get_absolute_url())
def save(self, *args, **kwargs):
super(Page, self).save(*args, **kwargs)
pages = self.get_descendants(include_self=True)
for page in pages:
page._generate_cached_url()
def _generate_cached_url(self):
if self.override_url:
self._cached_url = self.override_url
if self.is_root_node():
self._cached_url = u'/%s/' % (self.slug)
else:
self._cached_url = u'/%s/%s/' % ('/'.join([page.slug for page in self.get_ancestors()]), self.slug)
super(Page, self).save()
def get_absolute_url(self):
return self._cached_url
@property
def page_title(self):
if self._page_title:
return self._page_title
return self.content_title
@property
def content_title(self):
if not self._content_title:
return self.title
try:
return self._content_title.splitlines()[0]
except IndexError:
return u''
@property
def content_subtitle(self):
return u'\n'.join(self._content_title.splitlines()[1:])
def setup_request(self, request):
translation.activate(self.language)
request.LANGUAGE_CODE = translation.get_language()
request._feincms_page = self
def extended_navigation(self):
if not self.navigation_extension:
return []
cls = get_object(self.navigation_extension, fail_silently=True)
if not cls:
return []
return cls().children(self)
mptt.register(Page)