forked from expo/troggle
[svn] Updates to allow subcave tree with nice admin.
This commit is contained in:
232
feincms/module/page/models.py
Normal file
232
feincms/module/page/models.py
Normal file
@@ -0,0 +1,232 @@
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user