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
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2010-2017 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# See the file COPYING for details.
"""
Menus
"""
from copy import deepcopy
import datetime
from django.conf import settings
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
_extra_menus = []
# collect menu from INSTALLED_APPS
for app in settings.INSTALLED_APPS:
mod = __import__(app, fromlist=["ishtar_menu"])
if hasattr(mod, "ishtar_menu"):
menu = getattr(mod, "ishtar_menu")
_extra_menus += menu.MENU_SECTIONS
# sort
__section_items = [mnu for order, mnu in sorted(_extra_menus, key=lambda x: x[0])]
# regroup menus
_section_items, __keys = [], []
for section_item in __section_items:
if section_item.idx not in __keys:
__keys.append(section_item.idx)
_section_items.append(section_item)
continue
section_childs = _section_items[__keys.index(section_item.idx)].childs
childs_idx = [child.idx for child in section_childs]
for child in section_item.childs:
if child.idx not in childs_idx:
section_childs.append(child)
class Menu:
ref_childs = _section_items
def __init__(self, user, current_action=None, session=None):
self.user = user
self.initialized = False
self.items = {}
self.childs = []
self.current_action = current_action
self.current_section = None
self.current_url = None
self.current_section = ""
self.current_sections = []
self.current_subsection = ""
self.current_subsections = []
self.current_subsubsection = ""
self.current_subsubsections = []
self.selected_idx = None
self.session = session
self.items_by_idx = {}
def reinit_menu_for_all_user(self):
"""
Force cache deletion and reinitialization of menu for all
"""
lst_cache_key = "{}-{}".format(
settings.PROJECT_SLUG,
"menu_updated_list",
)
lst_ids = cache.get(lst_cache_key)
if not lst_ids:
return
for idx in lst_ids:
self.init(idx, force=True)
def set_menu_updated_key(self, cache_key, user_id):
"""
Stock updated information in cache
"""
lst_cache_key = "{}-{}".format(
settings.PROJECT_SLUG,
"menu_updated_list",
)
lst_ids = cache.get(lst_cache_key)
if not lst_ids:
lst_ids = []
if not lst_ids or user_id not in lst_ids:
lst_ids.append(user_id)
cache.set(lst_cache_key, lst_ids, settings.CACHE_TIMEOUT)
time = str(datetime.datetime.now().isoformat())
cache.set(cache_key, time, settings.CACHE_TIMEOUT)
self.initialized = time
def init(self, user_id=0, force=False):
"""
Get or init menu for a user
"""
if not user_id:
if not self.user:
return
user_id = self.user.pk
else:
try:
self.user = User.objects.get(pk=user_id)
except User.DoesNotExist:
return
cache_key = "{}-{}-{}".format(settings.PROJECT_SLUG, "menu_updated", user_id)
menu_updated = cache.get(cache_key)
if (
not force
and menu_updated
and self.initialized
and self.initialized == menu_updated
):
return
self.set_menu_updated_key(cache_key, user_id)
self.items = {}
self.items_by_idx = {}
childs = deepcopy(self.ref_childs)
for idx, main_menu in enumerate(reversed(childs)):
if not main_menu.can_be_available(self.user, self.session):
childs.pop(len(self.ref_childs) - idx - 1)
continue
self.items_by_idx[main_menu.idx] = main_menu
sub_childs = main_menu.childs[:]
for s_idx, child in enumerate(reversed(main_menu.childs)):
if not child.can_be_available(self.user, self.session):
sub_childs.pop(len(main_menu.childs) - s_idx - 1)
continue
self.items_by_idx[child.idx] = child
if hasattr(child, "childs"):
sub_sub_childs = child.childs[:]
for ss_idx, subchild in enumerate(reversed(child.childs)):
if not subchild.can_be_available(self.user, self.session):
sub_sub_childs.pop(len(child.childs) - ss_idx - 1)
continue
self.items_by_idx[subchild.idx] = subchild
child.childs = sub_sub_childs
main_menu.childs = sub_childs
selected = main_menu.set_items(
self.user, self.items, self.current_action, session=self.session
)
if selected:
self.selected_idx = idx
self.childs = childs
self.initialized = True
def get_current_selection(self, current_path):
# current_section, current_subsection, etc. are current labels
# current_sections, current_subsections, etc. are list of:
# (label, url, has_children)
self.current_section = ""
self.current_sections = []
self.current_subsection = ""
self.current_subsections = []
self.current_subsubsection = ""
self.current_subsubsections = []
self.current_url = None
for section in self.childs:
if not section.available:
continue
section_url = None
subsections = []
if not self.current_section:
# initialize by default with the first section
self.current_section = section.label
selected_section = None
for menu_item in section.childs:
if not menu_item.available:
continue
if not hasattr(menu_item, "childs") or not menu_item.childs:
item_url = reverse("action", args=[menu_item.idx])
if not section_url:
section_url = item_url
subsections.append([menu_item.label, item_url, False])
if item_url in current_path:
self.current_url = item_url
self.current_section = section.label
self.current_subsection = menu_item.label
selected_section = True
continue
subsection_url = None
selected_subsection = None
subsubsections = []
for menu_subitem in menu_item.childs:
if not menu_subitem.available:
continue
item_url = reverse("action", args=[menu_subitem.idx])
if not section_url:
section_url = item_url
if not subsection_url:
subsection_url = item_url
subsections.append([menu_item.label, item_url, True])
subsubsections.append([menu_subitem.label, item_url, False])
if item_url in current_path:
self.current_url = item_url
self.current_section = section.label
self.current_subsection = menu_item.label
self.current_subsubsection = menu_subitem.label
selected_section = True
selected_subsection = True
if selected_subsection:
self.current_subsubsections = subsubsections
if selected_section:
self.current_subsections = subsections
if not section_url:
section_url = "/"
self.current_sections.append(
[section.label, section_url, bool(subsections)]
)
|