1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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)
|