forked from viur-framework/viur-vi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathadmin.py
More file actions
454 lines (339 loc) · 12.3 KB
/
admin.py
File metadata and controls
454 lines (339 loc) · 12.3 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
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# -*- coding: utf-8 -*-
from vi import html5
from vi.framework.event import EventDispatcher
from .config import conf
from .widgets import TopBarWidget
from .widgets.userlogoutmsg import UserLogoutMsg
from .network import NetworkService, DeferredCall
from .priorityqueue import HandlerClassSelector, initialHashHandler, startupQueue
from .log import Log
from .pane import Pane, GroupPane
from .screen import Screen
# BELOW IMPORTS MUST REMAIN AND ARE QUEUED!!
from . import handler, bones, actions
from . import i18n
class AdminScreen(Screen):
def __init__(self, *args, **kwargs):
super(AdminScreen, self).__init__(*args, **kwargs)
self.sinkEvent("onClick")
self["id"] = "CoreWindow"
conf["mainWindow"] = self
self.topBar = TopBarWidget()
self.appendChild(self.topBar)
self.mainFrame = html5.Div()
self.mainFrame["class"] = "vi-main-frame"
self.appendChild(self.mainFrame)
self.moduleMgr = html5.Div()
self.moduleMgr["class"] = "vi-manager-frame"
self.mainFrame.appendChild(self.moduleMgr)
self.moduleViMgr = html5.Div()
self.moduleViMgr[ "class" ] = "vi-manager"
self.moduleMgr.appendChild( self.moduleViMgr )
self.moduleList = html5.Nav()
self.moduleList["class"] = "vi-modulelist"
self.moduleViMgr.appendChild(self.moduleList)
self.modulePipe = html5.Div()
self.modulePipe.addClass("vi-modulepipe")
self.moduleMgr.appendChild(self.modulePipe)
self.viewport = html5.Div()
self.viewport["class"] = "vi-viewer-frame"
self.mainFrame.appendChild(self.viewport)
self.logWdg = None
self.currentPane = None
self.nextPane = None # Which pane gains focus once the deferred call fires
self.panes = [] # List of known panes. The ordering represents the order in which the user visited them.
self.userLoggedOutMsg = None
# Register the error-handling for this iframe
try:
html5.window.onerror = html5.window.logError
html5.window.onerror = html5.window.logError
except:
print("logError is disabled")
def onClick(self, event):
if html5.utils.doesEventHitWidgetOrChildren(event, self.modulePipe):
conf["mainWindow"].switchFullscreen(not conf["mainWindow"].isFullscreen())
def reset(self):
self.moduleList.removeAllChildren()
self.viewport.removeAllChildren()
if self.logWdg:
self.logWdg.reset()
self.currentPane = None
self.nextPane = None
self.panes = []
if self.userLoggedOutMsg:
self.userLoggedOutMsg.stopInterval()
self.userLoggedOutMsg = None
def invoke(self):
self.show()
self.lock()
self.reset()
# Run queue
startupQueue.setFinalElem(self.startup)
startupQueue.run()
def getCurrentUser(self):
NetworkService.request("user", "view/self",
successHandler=self.getCurrentUserSuccess,
failureHandler=self.getCurrentUserFailure)
def getCurrentUserSuccess(self, req):
answ = NetworkService.decode(req)
conf["currentUser"] = answ["values"]
self.startup()
def getCurrentUserFailure(self, req, code):
conf["theApp"].login()
def startup(self):
config = conf["mainConfig"]
assert config
if not conf["currentUser"]:
self.getCurrentUser()
return
conf["server"] = config.get("configuration", {})
if "vi.name" in conf["server"]:
conf["vi.name"] = str(conf["server"]["vi.name"])
self.userLoggedOutMsg = UserLogoutMsg()
self.topBar.invoke()
moduleGroups = []
# Save module groups
if ("configuration" in config.keys()
and isinstance(config["configuration"], dict)):
if ("moduleGroups" in config["configuration"].keys()
and isinstance(config["configuration"]["moduleGroups"], list)):
moduleGroups = config["configuration"]["moduleGroups"]
# Modules
groupPanes = {}
panes = []
userAccess = conf["currentUser"].get("access", [])
predefinedFilterCounter = 1
groupedModules = {}
# First create group panes, if configured
for group in moduleGroups:
groupedModules[group["prefix"]]=[]
groupPanes[group["prefix"]] = GroupPane(group["name"], iconURL=group.get("icon"))
groupPanes[group["prefix"]].groupPrefix = group["prefix"]
panes.append((group["name"], group.get("sortIndex"), groupPanes[group["prefix"]]))
# read Hash to register startup module
currentActiveGroup = None
path = None
urlHash = html5.window.location.hash
if urlHash:
if "?" in urlHash:
hashStr = urlHash[1:urlHash.find("?")]
else:
hashStr = urlHash[1:]
path = [x for x in hashStr.split("/") if x]
sortedModules = []
topLevelModules = {}
for module, info in config["modules"].items():
if not "root" in userAccess and not any([x.startswith(module) for x in userAccess]):
# Skip this module, as the user couldn't interact with it anyway
continue
sortedModules.append((module, info))
groupName = info["name"].split(":")[0] + ": "
if groupName not in groupPanes.keys():
topLevelModules.update({module: info})
else:
if path and module == path[0]:
currentActiveGroup = groupName
groupedModules[groupName].append((module, info))
conf["vi.groupedModules"] = groupedModules
sortedModules.sort(key=lambda entry: "%d-%010d-%s" % (1 if entry[1].get("sortIndex") is None else 0, entry[1].get("sortIndex", 0), entry[1].get("name")))
# When create module panes
for module, info in sortedModules:
if "views" in info.keys() and info["views"]:
for v in info["views"]: # Work-a-round for PyJS not supporting id()
v["__id"] = predefinedFilterCounter
v["handler"] = info["handler"]
predefinedFilterCounter += 1
if currentActiveGroup or module in topLevelModules:
handlerCls = HandlerClassSelector.select(module, info)
assert handlerCls is not None, "No handler available for module '%s'" % module
info["visibleName"] = info["name"]
handler = None
if info["name"] and currentActiveGroup and info["name"].startswith(currentActiveGroup):
info["visibleName"] = info["name"].replace(currentActiveGroup, "")
handler = handlerCls(module, info)
groupPanes[currentActiveGroup].addChildPane(handler)
groupPanes[currentActiveGroup].expand()
if not handler and module in topLevelModules:
handler = handlerCls(module, info)
panes.append((info["visibleName"], info.get("sortIndex"), handler))
info["_handler"] = handler
conf["modules"][module] = info
# Sorting top level entries
panes.sort(key=lambda entry: "%d-%010d-%s" % (1 if entry[1] is None else 0, 0 if entry[1] is None else int(entry[1]), entry[0]))
# Add panes in the created order
for name, idx, pane in panes:
self.addPane(pane)
if not currentActiveGroup:
for group in moduleGroups:
groupPanes[ group[ "prefix" ] ].DeferredLoadChildren( 0 )
# Finalizing!
viInitializedEvent.fire()
DeferredCall(self.checkInitialHash)
self.unlock()
def log(self, type, msg, icon=None,modul=None,action=None,key=None,data=None):
self.logWdg.log(type, msg, icon,modul,action,key,data)
def checkInitialHash(self, *args, **kwargs):
urlHash = html5.window.location.hash
if not urlHash:
return
if "?" in urlHash:
hashStr = urlHash[1:urlHash.find("?")]
paramsStr = urlHash[urlHash.find("?") + 1:]
else:
hashStr = urlHash[1:]
paramsStr = ""
self.execCall(hashStr, paramsStr)
def execCall(self, path, params=None):
"""
Performs an execution call.
:param path: Path to the module and action
:param params: Parameters passed to the module
"""
path = [x for x in path.split("/") if x]
param = {}
if params:
if isinstance(params, dict):
param = params
else:
for pair in params.split("&"):
if not "=" in pair:
continue
key = pair[:pair.find("=")]
value = pair[pair.find("=") + 1:]
if not (key and value):
continue
if key in param.keys():
if not isinstance(param[key], list):
param[key] = [params[key]]
param[key].append(value)
else:
param[key] = value
print("execCall", path, param)
gen = initialHashHandler.select(path, param)
if gen:
gen(path, param)
def switchFullscreen(self, fullscreen=True):
if fullscreen:
self.moduleMgr.addClass("is-collapsed")
self.viewport.addClass("is-fullscreen")
else:
self.moduleMgr.removeClass("is-collapsed")
self.viewport.removeClass("is-fullscreen")
def isFullscreen(self):
return "is-fullscreen" in self.viewport["class"]
def onError(self, req, code):
print("ONERROR")
def _registerChildPanes(self, pane):
for childPane in pane.childPanes:
self.panes.append(childPane)
self.viewport.appendChild(childPane.widgetsDomElm)
childPane.widgetsDomElm.removeClass("is-active")
self._registerChildPanes(childPane)
def addPane(self, pane, parentPane=None):
# paneHandle = "pane_%s" % self.paneIdx
# self.paneIdx += 1
if len(pane.childPanes) > 0:
self._registerChildPanes(pane)
self.panes.append(pane)
if parentPane:
parentPane.addChildPane(pane)
else:
self.moduleList.appendChild(pane)
self.viewport.appendChild(pane.widgetsDomElm)
pane.widgetsDomElm.removeClass("is-active")
def insertPane(self, pane, insertAt):
if len(pane.childPanes) > 0:
self._registerChildPanes(pane)
assert insertAt in self.panes
self.panes.append(pane)
self.moduleList.insertBefore(pane, insertAt)
self.viewport.appendChild(pane.widgetsDomElm)
pane.widgetsDomElm.removeClass("is-active")
def stackPane(self, pane, focus=False):
assert self.currentPane is not None, "Cannot stack a pane. There's no current one."
self.addPane(pane, parentPane=self.currentPane)
if focus and not self.nextPane:
# We defer the call to focus, as some widgets stack more than one pane at once.
# If we focus directly, they will stack on each other, instead of the pane that
# currently has focus
self.nextPane = pane
DeferredCall(self.focusNextPane)
def focusNextPane(self, *args, **kwargs):
"""
The deferred call just fired. Focus that pane.
"""
if not self.nextPane:
return
nextPane = self.nextPane
self.nextPane = None
self.focusPane(nextPane)
def focusPane(self, pane):
assert pane in self.panes, "Cannot focus unknown pane!"
if not pane.focusable:
self.topBar.setCurrentModulDescr()
return
# Click on the same pane?
if pane == self.currentPane:
if self.currentPane.collapseable:
if self.currentPane.isExpanded:
self.currentPane.collapse()
else:
self.currentPane.expand()
return
self.panes.remove(pane) # Move the pane to the end of the list
self.panes.append(pane)
# Close current Pane
if self.currentPane is not None:
self.currentPane.item.removeClass("is-active")
self.currentPane.widgetsDomElm.removeClass("is-active")
# Focus wanted Pane
self.topBar.setCurrentModulDescr(pane.descr, pane.iconURL, pane.iconClasses, path=pane.path)
self.currentPane = pane
self.currentPane.widgetsDomElm.addClass("is-active")
self.currentPane.item.addClass("is-active")
if self.currentPane.collapseable:
if self.currentPane.isExpanded:
self.currentPane.collapse()
else:
self.currentPane.expand()
# Also open parent panes, if not already done
pane = self.currentPane.parentPane
while isinstance(pane, Pane):
if pane.childDomElem["style"].get("display", "none") == "none":
pane.childDomElem["style"]["display"] = "block"
pane = pane.parentPane
def removePane(self, pane):
assert pane in self.panes, "Cannot remove unknown pane!"
self.panes.remove(pane)
if pane == self.currentPane:
if self.panes:
self.focusPane(self.panes[-1])
else:
self.currentPane = None
if pane == self.nextPane:
if self.panes:
self.nextPane = self.panes[-1]
else:
self.nextPane = None
if not pane.parentPane or pane.parentPane is self:
self.moduleList.removeChild(pane)
else:
pane.parentPane.removeChildPane(pane)
self.viewport.removeChild(pane.widgetsDomElm)
def addWidget(self, widget, pane,disableOtherWidgets=True):
pane.addWidget(widget,disableOtherWidgets)
def stackWidget(self, widget,disableOtherWidgets=True):
assert self.currentPane is not None, "Cannot stack a widget while no pane is active"
self.addWidget(widget, self.currentPane,disableOtherWidgets)
def removeWidget(self, widget):
for pane in self.panes:
if pane.containsWidget(widget):
pane.removeWidget(widget)
return
raise AssertionError("Tried to remove unknown widget %s" % str(widget))
def containsWidget(self, widget):
for pane in self.panes:
if pane.containsWidget(widget):
return pane
return None
viInitializedEvent = EventDispatcher("viInitialized")