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

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 

8 

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$') 

12 

13 

14class BaseScriptAbstractSkel(TreeSkel): 

15 

16 path = StringBone( 

17 descr="Path", 

18 readOnly=True, 

19 unique=UniqueValue(UniqueLockMethod.SameValue, True, "This path name is already taken!") 

20 ) 

21 

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) 

28 

29 ret = super().fromClient(skel, data, *args, **kwargs) 

30 

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 

37 

38 return ret 

39 

40 

41class ScriptNodeSkel(BaseScriptAbstractSkel): 

42 kindName = "viur-script-node" 

43 

44 rootNode = BooleanBone( 

45 descr="Is root node?", 

46 defaultValue=False, 

47 ) 

48 

49 plugin = BooleanBone( 

50 descr="Is plugin?", 

51 defaultValue=False 

52 ) 

53 

54 name = StringBone( 

55 descr="Folder", 

56 required=True, 

57 vfunc=lambda value: not DIRECTORY_PATTERN.match(value) 

58 ) 

59 

60 

61class ScriptLeafSkel(BaseScriptAbstractSkel): 

62 kindName = "viur-script-leaf" 

63 

64 name = StringBone( 

65 descr="Filename", 

66 vfunc=lambda value: not FILE_PATTERN.match(value), 

67 ) 

68 

69 script = RawBone( 

70 descr="Code", 

71 indexed=False 

72 ) 

73 

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 ) 

82 

83 

84class Script(Tree): 

85 leafSkelCls = ScriptLeafSkel 

86 nodeSkelCls = ScriptNodeSkel 

87 

88 roles = { 

89 "admin": "*", 

90 } 

91 

92 def adminInfo(self): 

93 return conf.script_admin_info or {} 

94 

95 def getAvailableRootNodes(self): 

96 if not current.user.get(): 

97 return [] 

98 

99 return [{"name": "Scripts", "key": self.ensureOwnModuleRootNode().key}] 

100 

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) 

109 

110 raise 

111 

112 def onEdit(self, skelType, skel): 

113 self.update_path(skel) 

114 super().onEdit(skelType, skel) 

115 

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"]) 

120 

121 super().onEdited(skelType, skel) 

122 

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) 

130 

131 for skel in query.fetch(99): 

132 new_path = path + "/" + skel["name"] 

133 

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. 

139 

140 if cursor := query.getCursor(): 

141 self.update_path_recursive(skel_type, path, parent_key, cursor) 

142 

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"]] 

148 

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 

154 

155 path.insert(0, parent_skel["name"]) 

156 key = parent_skel["parententry"] 

157 

158 skel["path"] = "/".join(path) 

159 

160 

161Script.json = True