forked from django-cms/django-cms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmenu_pool.py
163 lines (148 loc) · 5.94 KB
/
menu_pool.py
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
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.cache import cache
from django.utils.importlib import import_module
from django.utils.translation import get_language
from menus import settings as menus_settings
from menus.exceptions import NamespaceAllreadyRegistered
import copy
def lex_cache_key(key):
"""
Returns the language and site ID a cache key is related to.
"""
return key.rsplit('_', 2)[1:]
class MenuPool(object):
def __init__(self):
self.menus = {}
self.modifiers = []
self.discovered = False
self.cache_keys = set()
def discover_menus(self):
if self.discovered:
return
for app in settings.INSTALLED_APPS:
try:
import_module('.menu', app)
except ImportError:
pass
from menus.modifiers import register
register()
self.discovered = True
def clear(self, site_id=None, language=None):
def relevance_test(keylang, keysite):
sok = not site_id
lok = not language
if site_id and (site_id == keysite or site_id == int(keysite)):
sok = True
if language and language == keylang:
lok = True
return lok and sok
to_be_deleted = []
for key in self.cache_keys:
keylang, keysite = lex_cache_key(key)
if relevance_test(keylang, keysite):
to_be_deleted.append(key)
cache.delete_many(to_be_deleted)
self.cache_keys.difference_update(to_be_deleted)
def register_menu(self, menu):
from menus.base import Menu
assert issubclass(menu, Menu)
if menu.__name__ in self.menus.keys():
raise NamespaceAllreadyRegistered, "[%s] a menu with this name is already registered" % menu.__name__
self.menus[menu.__name__] = menu()
def register_modifier(self, modifier_class):
from menus.base import Modifier
assert issubclass(modifier_class, Modifier)
if not modifier_class in self.modifiers:
self.modifiers.append(modifier_class)
def _build_nodes(self, request, site_id):
lang = get_language()
prefix = getattr(settings, "CMS_CACHE_PREFIX", "menu_cache_")
key = "%smenu_nodes_%s_%s" % (prefix, lang, site_id)
self.cache_keys.add(key)
cached_nodes = cache.get(key, None)
if cached_nodes:
return cached_nodes
final_nodes = []
for ns in self.menus:
nodes = self.menus[ns].get_nodes(request)
last = None
for node in nodes:
if not node.namespace:
node.namespace = ns
if node.parent_id:
if not node.parent_namespace:
node.parent_namespace = ns
found = False
if last:
n = last
while n:
if n.namespace == node.namespace and n.id == node.parent_id:
node.parent = n
found = True
n = None
elif n.parent:
n = n.parent
else:
n = None
if not found:
for n in nodes:
if n.namespace == node.namespace and n.id == node.parent_id:
node.parent = n
found = True
if found:
node.parent.children.append(node)
else:
continue
final_nodes.append(node)
last = node
duration = getattr(settings, "MENU_CACHE_DURATION", 60*60)
cache.set(key, final_nodes, duration)
return final_nodes
def apply_modifiers(self, nodes, request, namespace=None, root_id=None, post_cut=False, breadcrumb=False):
if not post_cut:
nodes = self._mark_selected(request, nodes)
for cls in self.modifiers:
inst = cls()
nodes = inst.modify(request, nodes, namespace, root_id, post_cut, breadcrumb)
return nodes
def get_nodes(self, request, namespace=None, root_id=None, site_id=None, breadcrumb=False):
self.discover_menus()
if not site_id:
site_id = Site.objects.get_current().pk
nodes = self._build_nodes(request, site_id)
nodes = copy.deepcopy(nodes)
nodes = self.apply_modifiers(nodes, request, namespace, root_id, post_cut=False, breadcrumb=breadcrumb)
return nodes
def _mark_selected(self, request, nodes):
sel = None
for node in nodes:
node.sibling = False
node.ancestor = False
node.descendant = False
node.selected = False
if node.get_absolute_url() == request.path[:len(node.get_absolute_url())]:
if sel:
if len(node.get_absolute_url()) > len(sel.get_absolute_url()):
sel = node
else:
sel = node
else:
node.selected = False
if sel:
sel.selected = True
return nodes
def get_menus_by_attribute(self, name, value):
self.discover_menus()
found = []
for menu in self.menus.items():
if hasattr(menu[1], name) and getattr(menu[1], name, None) == value:
found.append((menu[0], menu[1].name))
return found
def get_nodes_by_attribute(self, nodes, name, value):
found = []
for node in nodes:
if node.attr.get(name, None) == value:
found.append(node)
return found
menu_pool = MenuPool()