Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/prototypes/singleton.py: 0%
97 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 logging
2import typing as t
3from viur.core import db, current, utils, errors
4from viur.core.decorators import *
5from viur.core.cache import flushCache
6from viur.core.skeleton import SkeletonInstance
7from .skelmodule import SkelModule
10class Singleton(SkelModule):
11 """
12 Singleton module prototype.
14 It is used to store one single data entity, and needs to be sub-classed for individual modules.
15 """
16 handler = "singleton"
17 accessRights = ("edit", "view", "manage")
19 def getKey(self) -> str:
20 """
21 Returns the DB-Key for the current context.
23 This implementation provides one module-global key.
24 It *must* return *exactly one* key at any given time in any given context.
26 :returns: Current context DB-key
27 """
28 return f"{self.editSkel().kindName}-modulekey"
30 def viewSkel(self, *args, **kwargs) -> SkeletonInstance:
31 """
32 Retrieve a new instance of a :class:`viur.core.skeleton.Skeleton` that is used by the application
33 for viewing the existing entry.
35 The default is a Skeleton instance returned by :func:`~baseSkel`.
37 .. seealso:: :func:`addSkel`, :func:`editSkel`, :func:`~baseSkel`
39 :return: Returns a Skeleton instance for viewing the singleton entry.
40 """
41 return self.baseSkel(*args, **kwargs)
43 def editSkel(self, *args, **kwargs) -> SkeletonInstance:
44 """
45 Retrieve a new instance of a :class:`viur.core.skeleton.Skeleton` that is used by the application
46 for editing the existing entry.
48 The default is a Skeleton instance returned by :func:`~baseSkel`.
50 .. seealso:: :func:`viewSkel`, :func:`editSkel`, :func:`~baseSkel`
52 :return: Returns a Skeleton instance for editing the entry.
53 """
54 return self.baseSkel(*args, **kwargs)
56 ## External exposed functions
58 @exposed
59 @skey
60 def preview(self, *args, **kwargs) -> t.Any:
61 """
62 Renders data for the entry, without reading it from the database.
63 This function allows to preview the entry without writing it to the database.
65 Any entity values are provided via *kwargs*.
67 The function uses the viewTemplate of the application.
69 :returns: The rendered representation of the supplied data.
70 """
71 if not self.canPreview():
72 raise errors.Unauthorized()
74 skel = self.viewSkel()
75 skel.fromClient(kwargs)
77 return self.render.view(skel)
79 @exposed
80 def structure(self, *args, **kwargs) -> t.Any:
81 """
82 :returns: Returns the structure of our skeleton as used in list/view. Values are the defaultValues set
83 in each bone.
85 :raises: :exc:`viur.core.errors.Unauthorized`, if the current user does not have the required permissions.
86 """
87 skel = self.viewSkel()
88 if not self.canView():
89 raise errors.Unauthorized()
90 return self.render.view(skel)
92 @exposed
93 def view(self, *args, **kwargs) -> t.Any:
94 """
95 Prepares and renders the singleton entry for viewing.
97 The function performs several access control checks on the requested entity before it is rendered.
99 .. seealso:: :func:`viewSkel`, :func:`canView`, :func:`onView`
101 :returns: The rendered representation of the entity.
103 :raises: :exc:`viur.core.errors.NotFound`, if there is no singleton entry existing, yet.
104 :raises: :exc:`viur.core.errors.Unauthorized`, if the current user does not have the required permissions.
105 """
107 skel = self.viewSkel()
108 if not self.canView():
109 raise errors.Unauthorized()
111 key = db.Key(self.editSkel().kindName, self.getKey())
113 if not skel.fromDB(key):
114 raise errors.NotFound()
116 self.onView(skel)
117 return self.render.view(skel)
119 @exposed
120 @force_ssl
121 @skey(allow_empty=True)
122 def edit(self, *args, **kwargs) -> t.Any:
123 """
124 Modify the existing entry, and render the entry, eventually with error notes on incorrect data.
126 The entry is fetched by its entity key, which either is provided via *kwargs["key"]*,
127 or as the first parameter in *args*. The function performs several access control checks
128 on the singleton's entity before it is modified.
130 .. seealso:: :func:`editSkel`, :func:`onEdited`, :func:`canEdit`
132 :returns: The rendered, edited object of the entry, eventually with error hints.
134 :raises: :exc:`viur.core.errors.Unauthorized`, if the current user does not have the required permissions.
135 :raises: :exc:`viur.core.errors.PreconditionFailed`, if the *skey* could not be verified.
136 """
137 if not self.canEdit():
138 raise errors.Unauthorized()
140 key = db.Key(self.editSkel().kindName, self.getKey())
141 skel = self.editSkel()
142 if not skel.fromDB(key): # Its not there yet; we need to set the key again
143 skel["key"] = key
145 if (
146 not kwargs # no data supplied
147 or not current.request.get().isPostRequest # failure if not using POST-method
148 or not skel.fromClient(kwargs, amend=True) # failure on reading into the bones
149 or utils.parse.bool(kwargs.get("bounce")) # review before changing
150 ):
151 return self.render.edit(skel)
153 self.onEdit(skel)
154 skel.toDB()
155 self.onEdited(skel)
156 return self.render.editSuccess(skel)
158 def getContents(self) -> SkeletonInstance | None:
159 """
160 Returns the entity of this singleton application as :class:`viur.core.skeleton.Skeleton` object.
162 :returns: The content as Skeleton provided by :func:`viewSkel`.
163 """
164 skel = self.viewSkel()
165 key = db.Key(self.viewSkel().kindName, self.getKey())
167 if not skel.fromDB(key):
168 return None
170 return skel
172 def canPreview(self) -> bool:
173 """
174 Access control function for preview permission.
176 Checks if the current user has the permission to preview the singletons entry.
178 The default behavior is:
179 - If no user is logged in, previewing is generally refused.
180 - If the user has "root" access, previewing is generally allowed.
181 - If the user has the modules "edit" permission (module-edit) enabled, \
182 previewing is allowed.
184 It should be overridden for a module-specific behavior.
186 .. seealso:: :func:`preview`
188 :returns: True, if previewing entries is allowed, False otherwise.
189 """
190 if not (user := current.user.get()):
191 return False
193 if user["access"] and "root" in user["access"]:
194 return True
196 if user["access"] and f"{self.moduleName}-edit" in user["access"]:
197 return True
199 return False
201 def canEdit(self) -> bool:
202 """
203 Access control function for modification permission.
205 Checks if the current user has the permission to edit the singletons entry.
207 The default behavior is:
208 - If no user is logged in, editing is generally refused.
209 - If the user has "root" access, editing is generally allowed.
210 - If the user has the modules "edit" permission (module-edit) enabled, editing is allowed.
212 It should be overridden for a module-specific behavior.
214 .. seealso:: :func:`edit`
216 :returns: True, if editing is allowed, False otherwise.
217 """
218 if not (user := current.user.get()):
219 return False
221 if user["access"] and "root" in user["access"]:
222 return True
224 if user["access"] and f"{self.moduleName}-edit" in user["access"]:
225 return True
227 return False
229 def canView(self) -> bool:
230 """
231 Access control function for viewing permission.
233 Checks if the current user has the permission to view the singletons entry.
235 The default behavior is:
236 - If no user is logged in, viewing is generally refused.
237 - If the user has "root" access, viewing is generally allowed.
238 - If the user has the modules "view" permission (module-view) enabled, viewing is allowed.
240 It should be overridden for a module-specific behavior.
242 .. seealso:: :func:`view`
244 :returns: True, if viewing is allowed, False otherwise.
245 """
246 if not (user := current.user.get()):
247 return False
248 if user["access"] and "root" in user["access"]:
249 return True
250 if user["access"] and f"{self.moduleName}-view" in user["access"]:
251 return True
252 return False
254 def onEdit(self, skel: SkeletonInstance):
255 """
256 Hook function that is called before editing an entry.
258 It can be overridden for a module-specific behavior.
260 :param skel: The Skeleton that is going to be edited.
262 .. seealso:: :func:`edit`, :func:`onEdited`
263 """
264 pass
266 def onEdited(self, skel: SkeletonInstance):
267 """
268 Hook function that is called after modifying the entry.
270 It should be overridden for a module-specific behavior.
271 The default is writing a log entry.
273 :param skel: The Skeleton that has been modified.
275 .. seealso:: :func:`edit`, :func:`onEdit`
276 """
277 logging.info(f"""Entry changed: {skel["key"]!r}""")
278 flushCache(key=skel["key"])
279 if user := current.user.get():
280 logging.info(f"""User: {user["name"]!r} ({user["key"]!r})""")
282 def onView(self, skel: SkeletonInstance):
283 """
284 Hook function that is called when viewing an entry.
286 It should be overridden for a module-specific behavior.
287 The default is doing nothing.
289 :param skel: The Skeleton that is being viewed.
291 .. seealso:: :func:`view`
292 """
293 pass
296Singleton.admin = True
297Singleton.vi = True