forked from feincms/feincms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnavigation.py
More file actions
179 lines (134 loc) · 5.17 KB
/
Copy pathnavigation.py
File metadata and controls
179 lines (134 loc) · 5.17 KB
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
"""
Extend or modify the navigation with custom entries.
This extension allows the website administrator to select an extension
which processes, modifies or adds subnavigation entries. The bundled
``feincms_nav`` template tag knows how to collect navigation entries,
be they real Page instances or extended navigation entries.
"""
import types
from collections import OrderedDict
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from feincms import extensions
from feincms._internal import monkeypatch_method
from feincms.utils import get_object, shorten_string
class TypeRegistryMetaClass(type):
"""
You can access the list of subclasses as <BaseClass>.types
TODO use NavigationExtension.__subclasses__() instead?
"""
def __init__(cls, name, bases, attrs):
if not hasattr(cls, "types"):
cls.types = []
else:
cls.types.append(cls)
class PagePretender:
"""
A PagePretender pretends to be a page, but in reality is just a shim layer
that implements enough functionality to inject fake pages eg. into the
navigation tree.
For use as fake navigation page, you should at least define the following
parameters on creation: title, url, level. If using the translation
extension, also add language.
"""
pk = None
# emulate mptt properties to get the template tags working
class _mptt_meta:
level_attr = "level"
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def get_absolute_url(self):
return self.url
def get_navigation_url(self):
return self.get_absolute_url()
def get_level(self):
return self.level
def get_children(self):
"""overwrite this if you want nested extensions using recursetree"""
return []
def available_translations(self):
return ()
def get_original_translation(self, page):
return page
def short_title(self):
return shorten_string(self.title)
class NavigationExtension(metaclass=TypeRegistryMetaClass):
"""
Base class for all navigation extensions.
The name attribute is shown to the website administrator.
"""
name = _("navigation extension")
def children(self, page, **kwargs):
"""
This is the method which must be overridden in every navigation
extension.
It receives the page the extension is attached to, the depth up to
which the navigation should be resolved, and the current request object
if it is available.
"""
raise NotImplementedError
def navigation_extension_choices():
for ext in NavigationExtension.types:
if issubclass(ext, NavigationExtension) and ext is not NavigationExtension:
yield (f"{ext.__module__}.{ext.__name__}", ext.name)
def get_extension_class(extension):
extension = get_object(extension)
if isinstance(extension, types.ModuleType):
return getattr(extension, "Extension")
return extension
class Extension(extensions.Extension):
ident = "navigation" # TODO actually use this
navigation_extensions = None
@cached_property
def _extensions(self):
if self.navigation_extensions is None:
return OrderedDict(
(f"{ext.__module__}.{ext.__name__}", ext)
for ext in NavigationExtension.types
if (
issubclass(ext, NavigationExtension)
and ext is not NavigationExtension
)
)
else:
return OrderedDict(
(f"{ext.__module__}.{ext.__name__}", ext)
for ext in map(get_extension_class, self.navigation_extensions)
)
def handle_model(self):
choices = [(path, ext.name) for path, ext in self._extensions.items()]
self.model.add_to_class(
"navigation_extension",
models.CharField(
_("navigation extension"),
choices=choices,
blank=True,
null=True,
max_length=200,
help_text=_(
"Select the module providing subpages for this page if"
" you need to customize the navigation."
),
),
)
extension = self
@monkeypatch_method(self.model)
def extended_navigation(self, **kwargs):
if not self.navigation_extension:
return self.children.in_navigation()
cls = None
try:
cls = extension._extensions[self.navigation_extension]
except KeyError:
cls = get_object(self.navigation_extension, fail_silently=True)
extension._extensions[self.navigation_extension] = cls
if cls:
return cls().children(self, **kwargs)
return self.children.in_navigation()
def handle_modeladmin(self, modeladmin):
modeladmin.add_extension_options(
_("Navigation extension"),
{"fields": ("navigation_extension",), "classes": ("collapse",)},
)