Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/modules/script.py: 0%
82 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-16 22:16 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-16 22:16 +0000
1import re
2import typing as t
3from viur.core.bones import *
4from viur.core.prototypes.tree import Tree, TreeSkel, SkelType
5from viur.core import db, conf, current, skeleton, tasks, errors
6from viur.core.decorators import exposed
7from viur.core.i18n import translate
9# pre-compile patterns for vfuncs
10DIRECTORY_PATTERN = re.compile(r'^[a-zA-Z0-9äöüÄÖÜ_-]*$')
11FILE_PATTERN = re.compile(r'^[a-zA-Z0-9äöüÄÖÜ_-]+?.py$')
14class BaseScriptAbstractSkel(TreeSkel):
16 path = StringBone(
17 descr="Path",
18 readOnly=True,
19 unique=UniqueValue(UniqueLockMethod.SameValue, True, "This path name is already taken!")
20 )
22 @classmethod
23 def fromClient(cls, skel, data, *args, **kwargs):
24 # Set script name when provided, so that the path can be regenerated
25 if name := data.get("name"):
26 skel["name"] = name
27 conf.main_app.vi.script.update_path(skel)
29 ret = super().fromClient(skel, data, *args, **kwargs)
31 if not ret:
32 # in case the path failed because the unique value is already taken, rewrite the error for name field
33 for error in skel.errors:
34 if error.severity == skeleton.ReadFromClientErrorSeverity.Invalid and error.fieldPath == ["path"]:
35 error.fieldPath = ["name"]
36 break
38 return ret
41class ScriptNodeSkel(BaseScriptAbstractSkel):
42 kindName = "viur-script-node"
44 rootNode = BooleanBone(
45 descr="Is root node?",
46 defaultValue=False,
47 )
49 plugin = BooleanBone(
50 descr="Is plugin?",
51 defaultValue=False
52 )
54 name = StringBone(
55 descr="Folder",
56 required=True,
57 vfunc=lambda value: not DIRECTORY_PATTERN.match(value)
58 )
61class ScriptLeafSkel(BaseScriptAbstractSkel):
62 kindName = "viur-script-leaf"
64 name = StringBone(
65 descr="Filename",
66 vfunc=lambda value: not FILE_PATTERN.match(value),
67 )
69 script = RawBone(
70 descr="Code",
71 indexed=False
72 )
74 access = SelectBone(
75 descr="Required access rights to run this Script",
76 values=lambda: {
77 right: translate("server.modules.user.accessright.%s" % right, defaultText=right)
78 for right in sorted(conf.user.access_rights)
79 },
80 multiple=True,
81 )
84class Script(Tree):
85 leafSkelCls = ScriptLeafSkel
86 nodeSkelCls = ScriptNodeSkel
88 roles = {
89 "admin": "*",
90 }
92 def adminInfo(self):
93 return conf.script_admin_info or {}
95 def getAvailableRootNodes(self):
96 if not current.user.get():
97 return []
99 return [{"name": "Scripts", "key": self.ensureOwnModuleRootNode().key}]
101 @exposed
102 def view(self, skelType: SkelType, key: db.Key | int | str, *args, **kwargs) -> t.Any:
103 try:
104 return super().view(skelType, key, *args, **kwargs)
105 except errors.NotFound:
106 # When key is not found, try to interpret key as path
107 if skel := self.viewSkel(skelType).all().mergeExternalFilter({"path": key}).getSkel():
108 return super().view(skelType, skel["key"], *args, **kwargs)
110 raise
112 def onEdit(self, skelType, skel):
113 self.update_path(skel)
114 super().onEdit(skelType, skel)
116 def onEdited(self, skelType, skel):
117 if skelType == "node":
118 self.update_path_recursive("node", skel["path"], skel["key"])
119 self.update_path_recursive("leaf", skel["path"], skel["key"])
121 super().onEdited(skelType, skel)
123 @tasks.CallDeferred
124 def update_path_recursive(self, skel_type, path, parent_key, cursor=None):
125 """
126 Recursively updates all items under a given parent key.
127 """
128 query = self.editSkel(skel_type).all().filter("parententry", parent_key)
129 query.setCursor(cursor)
131 for skel in query.fetch(99):
132 new_path = path + "/" + skel["name"]
134 # only update when path changed
135 if new_path != skel["path"]:
136 skel["path"] = new_path # self.onEdit() is NOT required, as it resolves the path again.
137 skel.toDB()
138 self.onEdited(skel_type, skel) # triggers this recursion for nodes, again.
140 if cursor := query.getCursor():
141 self.update_path_recursive(skel_type, path, parent_key, cursor)
143 def update_path(self, skel):
144 """
145 Updates the path-value of a either a folder or a script file, by resolving the repository's root node.
146 """
147 path = [skel["name"]]
149 key = skel["parententry"]
150 while key:
151 parent_skel = self.viewSkel("node")
152 if not parent_skel.fromDB(key) or parent_skel["key"] == skel["parentrepo"]:
153 break
155 path.insert(0, parent_skel["name"])
156 key = parent_skel["parententry"]
158 skel["path"] = "/".join(path)
161Script.json = True