WuttaWeb 0.6.0__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/CHANGELOG.md +29 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/PKG-INFO +2 -2
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/pyproject.toml +2 -2
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/app.py +6 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/auth.py +90 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/base.py +82 -23
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/schema.py +104 -3
- wuttaweb-0.8.0/src/wuttaweb/forms/widgets.py +192 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/grids/base.py +115 -4
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/menus.py +2 -3
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/subscribers.py +57 -8
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/base.mako +15 -12
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/checkbox_choice.pt +18 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/permissions.pt +23 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/readonly/notes.pt +7 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/readonly/objectref.pt +1 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/readonly/permissions.pt +18 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/deform/textarea.pt +11 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/forbidden.mako +26 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/form.mako +9 -5
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/grids/vue_template.mako +2 -1
- wuttaweb-0.8.0/src/wuttaweb/templates/notfound.mako +23 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/people/view_profile.mako +12 -0
- wuttaweb-0.8.0/src/wuttaweb/templates/setup.mako +20 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/util.py +13 -8
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/auth.py +13 -8
- wuttaweb-0.8.0/src/wuttaweb/views/common.py +223 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/master.py +281 -51
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/people.py +44 -0
- wuttaweb-0.8.0/src/wuttaweb/views/roles.py +271 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/settings.py +4 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/users.py +86 -2
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/forms/test_base.py +67 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/forms/test_schema.py +58 -2
- wuttaweb-0.8.0/tests/forms/test_widgets.py +115 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/grids/test_base.py +57 -2
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_auth.py +24 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_menus.py +24 -9
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_subscribers.py +132 -1
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_util.py +8 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/util.py +11 -5
- wuttaweb-0.8.0/tests/views/test___init__.py +10 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/test_auth.py +45 -52
- wuttaweb-0.8.0/tests/views/test_common.py +95 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/test_master.py +288 -35
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/test_people.py +34 -0
- wuttaweb-0.8.0/tests/views/test_roles.py +242 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/test_settings.py +4 -0
- wuttaweb-0.8.0/tests/views/test_users.py +188 -0
- wuttaweb-0.6.0/src/wuttaweb/forms/widgets.py +0 -82
- wuttaweb-0.6.0/src/wuttaweb/views/common.py +0 -74
- wuttaweb-0.6.0/src/wuttaweb/views/roles.py +0 -100
- wuttaweb-0.6.0/tests/forms/test_widgets.py +0 -32
- wuttaweb-0.6.0/tests/views/test___init__.py +0 -14
- wuttaweb-0.6.0/tests/views/test_common.py +0 -27
- wuttaweb-0.6.0/tests/views/test_roles.py +0 -57
- wuttaweb-0.6.0/tests/views/test_users.py +0 -57
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/.gitignore +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/COPYING.txt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/README.md +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/Makefile +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/app.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/auth.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/db.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.schema.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.widgets.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/grids.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/grids.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/handler.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/helpers.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/menus.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/static.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/subscribers.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/util.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.auth.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.common.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.essential.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.master.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.people.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.roles.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.settings.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.users.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/conf.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/make.bat +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/docs/narr/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/db.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tasks.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_app.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/test_static.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.8.0}/tox.ini +0 -0
|
@@ -5,6 +5,35 @@ All notable changes to wuttaweb will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v0.8.0 (2024-08-15)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add form/grid label auto-overrides for master view
|
|
13
|
+
|
|
14
|
+
### Fix
|
|
15
|
+
|
|
16
|
+
- add `person` to template context for `PersonView.view_profile()`
|
|
17
|
+
|
|
18
|
+
## v0.7.0 (2024-08-15)
|
|
19
|
+
|
|
20
|
+
### Feat
|
|
21
|
+
|
|
22
|
+
- add sane views for 403 Forbidden and 404 Not Found
|
|
23
|
+
- add permission checks for menus, view routes
|
|
24
|
+
- add first-time setup page to create admin user
|
|
25
|
+
- expose User password for editing in master views
|
|
26
|
+
- expose Role permissions for editing
|
|
27
|
+
- expose User "roles" for editing
|
|
28
|
+
- improve widget, rendering for Role notes
|
|
29
|
+
|
|
30
|
+
### Fix
|
|
31
|
+
|
|
32
|
+
- add stub for `PersonView.make_user()`
|
|
33
|
+
- allow arbitrary kwargs for `Form.render_vue_field()`
|
|
34
|
+
- make some tweaks for better tailbone compatibility
|
|
35
|
+
- prevent delete for built-in roles
|
|
36
|
+
|
|
8
37
|
## v0.6.0 (2024-08-13)
|
|
9
38
|
|
|
10
39
|
### Feat
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Web App for Wutta Framework
|
|
5
5
|
Project-URL: Homepage, https://wuttaproject.org/
|
|
6
6
|
Project-URL: Repository, https://forgejo.wuttaproject.org/wutta/wuttaweb
|
|
@@ -33,7 +33,7 @@ Requires-Dist: pyramid-tm
|
|
|
33
33
|
Requires-Dist: pyramid>=2
|
|
34
34
|
Requires-Dist: waitress
|
|
35
35
|
Requires-Dist: webhelpers2
|
|
36
|
-
Requires-Dist: wuttjamaican[db]>=0.
|
|
36
|
+
Requires-Dist: wuttjamaican[db]>=0.12.0
|
|
37
37
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
38
38
|
Provides-Extra: docs
|
|
39
39
|
Requires-Dist: furo; extra == 'docs'
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.8.0"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
|
@@ -39,7 +39,7 @@ dependencies = [
|
|
|
39
39
|
"pyramid_tm",
|
|
40
40
|
"waitress",
|
|
41
41
|
"WebHelpers2",
|
|
42
|
-
"WuttJamaican[db]>=0.
|
|
42
|
+
"WuttJamaican[db]>=0.12.0",
|
|
43
43
|
"zope.sqlalchemy>=1.5",
|
|
44
44
|
]
|
|
45
45
|
|
|
@@ -135,6 +135,12 @@ def make_pyramid_config(settings):
|
|
|
135
135
|
pyramid_config.include('pyramid_mako')
|
|
136
136
|
pyramid_config.include('pyramid_tm')
|
|
137
137
|
|
|
138
|
+
# add some permissions magic
|
|
139
|
+
pyramid_config.add_directive('add_wutta_permission_group',
|
|
140
|
+
'wuttaweb.auth.add_permission_group')
|
|
141
|
+
pyramid_config.add_directive('add_wutta_permission',
|
|
142
|
+
'wuttaweb.auth.add_permission')
|
|
143
|
+
|
|
138
144
|
return pyramid_config
|
|
139
145
|
|
|
140
146
|
|
|
@@ -148,3 +148,93 @@ class WuttaSecurityPolicy:
|
|
|
148
148
|
auth = app.get_auth_handler()
|
|
149
149
|
user = self.identity(request)
|
|
150
150
|
return auth.has_permission(self.db_session, user, permission)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def add_permission_group(pyramid_config, key, label=None, overwrite=True):
|
|
154
|
+
"""
|
|
155
|
+
Pyramid directive to add a "permission group" to the app's
|
|
156
|
+
awareness.
|
|
157
|
+
|
|
158
|
+
The app must be made aware of all permissions, so they are exposed
|
|
159
|
+
when editing a
|
|
160
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.auth.Role`. The logic
|
|
161
|
+
for discovering permissions is in
|
|
162
|
+
:meth:`~wuttaweb.views.roles.RoleView.get_available_permissions()`.
|
|
163
|
+
|
|
164
|
+
This is usually called from within a master view's
|
|
165
|
+
:meth:`~wuttaweb.views.master.MasterView.defaults()` to establish
|
|
166
|
+
the permission group which applies to the view model.
|
|
167
|
+
|
|
168
|
+
A simple example of usage::
|
|
169
|
+
|
|
170
|
+
pyramid_config.add_permission_group('widgets', label="Widgets")
|
|
171
|
+
|
|
172
|
+
:param key: Unique key for the permission group. In the context
|
|
173
|
+
of a master view, this will be the same as
|
|
174
|
+
:attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
|
|
175
|
+
|
|
176
|
+
:param label: Optional label for the permission group. If not
|
|
177
|
+
specified, it is derived from ``key``.
|
|
178
|
+
|
|
179
|
+
:param overwrite: If the permission group was already established,
|
|
180
|
+
this flag controls whether the group's label should be
|
|
181
|
+
overwritten (with ``label``).
|
|
182
|
+
|
|
183
|
+
See also :func:`add_permission()`.
|
|
184
|
+
"""
|
|
185
|
+
config = pyramid_config.get_settings()['wutta_config']
|
|
186
|
+
app = config.get_app()
|
|
187
|
+
def action():
|
|
188
|
+
perms = pyramid_config.get_settings().get('wutta_permissions', {})
|
|
189
|
+
if overwrite or key not in perms:
|
|
190
|
+
group = perms.setdefault(key, {'key': key})
|
|
191
|
+
group['label'] = label or app.make_title(key)
|
|
192
|
+
pyramid_config.add_settings({'wutta_permissions': perms})
|
|
193
|
+
pyramid_config.action(None, action)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def add_permission(pyramid_config, groupkey, key, label=None):
|
|
197
|
+
"""
|
|
198
|
+
Pyramid directive to add a single "permission" to the app's
|
|
199
|
+
awareness.
|
|
200
|
+
|
|
201
|
+
The app must be made aware of all permissions, so they are exposed
|
|
202
|
+
when editing a
|
|
203
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.auth.Role`. The logic
|
|
204
|
+
for discovering permissions is in
|
|
205
|
+
:meth:`~wuttaweb.views.roles.RoleView.get_available_permissions()`.
|
|
206
|
+
|
|
207
|
+
This is usually called from within a master view's
|
|
208
|
+
:meth:`~wuttaweb.views.master.MasterView.defaults()` to establish
|
|
209
|
+
"known" permissions based on master view feature flags
|
|
210
|
+
(:attr:`~wuttaweb.views.master.MasterView.viewable`,
|
|
211
|
+
:attr:`~wuttaweb.views.master.MasterView.editable`, etc.).
|
|
212
|
+
|
|
213
|
+
A simple example of usage::
|
|
214
|
+
|
|
215
|
+
pyramid_config.add_permission('widgets', 'widgets.polish',
|
|
216
|
+
label="Polish all the widgets")
|
|
217
|
+
|
|
218
|
+
:param key: Unique key for the permission group. In the context
|
|
219
|
+
of a master view, this will be the same as
|
|
220
|
+
:attr:`~wuttaweb.views.master.MasterView.permission_prefix`.
|
|
221
|
+
|
|
222
|
+
:param key: Unique key for the permission. This should be the
|
|
223
|
+
"complete" permission name which includes the permission
|
|
224
|
+
prefix.
|
|
225
|
+
|
|
226
|
+
:param label: Optional label for the permission. If not
|
|
227
|
+
specified, it is derived from ``key``.
|
|
228
|
+
|
|
229
|
+
See also :func:`add_permission_group()`.
|
|
230
|
+
"""
|
|
231
|
+
def action():
|
|
232
|
+
config = pyramid_config.get_settings()['wutta_config']
|
|
233
|
+
app = config.get_app()
|
|
234
|
+
perms = pyramid_config.get_settings().get('wutta_permissions', {})
|
|
235
|
+
group = perms.setdefault(groupkey, {'key': groupkey})
|
|
236
|
+
group.setdefault('label', app.make_title(groupkey))
|
|
237
|
+
perm = group.setdefault('perms', {}).setdefault(key, {'key': key})
|
|
238
|
+
perm['label'] = label or app.make_title(key)
|
|
239
|
+
pyramid_config.add_settings({'wutta_permissions': perms})
|
|
240
|
+
pyramid_config.action(None, action)
|
|
@@ -120,6 +120,13 @@ class Form:
|
|
|
120
120
|
|
|
121
121
|
See also :meth:`set_validator()`.
|
|
122
122
|
|
|
123
|
+
.. attribute:: defaults
|
|
124
|
+
|
|
125
|
+
Dict of default field values, used to construct the form in
|
|
126
|
+
:meth:`get_schema()`.
|
|
127
|
+
|
|
128
|
+
See also :meth:`set_default()`.
|
|
129
|
+
|
|
123
130
|
.. attribute:: readonly
|
|
124
131
|
|
|
125
132
|
Boolean indicating the form does not allow submit. In practice
|
|
@@ -248,6 +255,7 @@ class Form:
|
|
|
248
255
|
nodes={},
|
|
249
256
|
widgets={},
|
|
250
257
|
validators={},
|
|
258
|
+
defaults={},
|
|
251
259
|
readonly=False,
|
|
252
260
|
readonly_fields=[],
|
|
253
261
|
required_fields={},
|
|
@@ -271,6 +279,7 @@ class Form:
|
|
|
271
279
|
self.nodes = nodes or {}
|
|
272
280
|
self.widgets = widgets or {}
|
|
273
281
|
self.validators = validators or {}
|
|
282
|
+
self.defaults = defaults or {}
|
|
274
283
|
self.readonly = readonly
|
|
275
284
|
self.readonly_fields = set(readonly_fields or [])
|
|
276
285
|
self.required_fields = required_fields or {}
|
|
@@ -375,6 +384,23 @@ class Form:
|
|
|
375
384
|
"""
|
|
376
385
|
self.fields = FieldList(fields)
|
|
377
386
|
|
|
387
|
+
def append(self, *keys):
|
|
388
|
+
"""
|
|
389
|
+
Add some fields(s) to the form.
|
|
390
|
+
|
|
391
|
+
This is a convenience to allow adding multiple fields at
|
|
392
|
+
once::
|
|
393
|
+
|
|
394
|
+
form.append('first_field',
|
|
395
|
+
'second_field',
|
|
396
|
+
'third_field')
|
|
397
|
+
|
|
398
|
+
It will add each field to :attr:`fields`.
|
|
399
|
+
"""
|
|
400
|
+
for key in keys:
|
|
401
|
+
if key not in self.fields:
|
|
402
|
+
self.fields.append(key)
|
|
403
|
+
|
|
378
404
|
def remove(self, *keys):
|
|
379
405
|
"""
|
|
380
406
|
Remove some fields(s) from the form.
|
|
@@ -409,16 +435,14 @@ class Form:
|
|
|
409
435
|
|
|
410
436
|
Node overrides are tracked via :attr:`nodes`.
|
|
411
437
|
"""
|
|
438
|
+
from wuttaweb.forms.schema import ObjectNode
|
|
439
|
+
|
|
412
440
|
if isinstance(nodeinfo, colander.SchemaNode):
|
|
413
441
|
# assume nodeinfo is a complete node
|
|
414
442
|
node = nodeinfo
|
|
415
443
|
|
|
416
444
|
else: # assume nodeinfo is a schema type
|
|
417
445
|
kwargs.setdefault('name', key)
|
|
418
|
-
|
|
419
|
-
from wuttaweb.forms.schema import ObjectNode
|
|
420
|
-
|
|
421
|
-
# node = colander.SchemaNode(nodeinfo, **kwargs)
|
|
422
446
|
node = ObjectNode(nodeinfo, **kwargs)
|
|
423
447
|
|
|
424
448
|
self.nodes[key] = node
|
|
@@ -471,6 +495,18 @@ class Form:
|
|
|
471
495
|
if self.schema and key in self.schema:
|
|
472
496
|
self.schema[key].validator = validator
|
|
473
497
|
|
|
498
|
+
def set_default(self, key, value):
|
|
499
|
+
"""
|
|
500
|
+
Set/override the default value for a field.
|
|
501
|
+
|
|
502
|
+
:param key: Name of field.
|
|
503
|
+
|
|
504
|
+
:param validator: Default value for the field.
|
|
505
|
+
|
|
506
|
+
Default value overrides are tracked via :attr:`defaults`.
|
|
507
|
+
"""
|
|
508
|
+
self.defaults[key] = value
|
|
509
|
+
|
|
474
510
|
def set_readonly(self, key, readonly=True):
|
|
475
511
|
"""
|
|
476
512
|
Enable or disable the "readonly" flag for a given field.
|
|
@@ -624,29 +660,22 @@ class Form:
|
|
|
624
660
|
|
|
625
661
|
if self.model_class:
|
|
626
662
|
|
|
627
|
-
#
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
# determine which we want ColanderAlchemy to handle
|
|
632
|
-
auto_includes = []
|
|
633
|
-
for key in includes:
|
|
634
|
-
|
|
635
|
-
# skip if we already have a node defined
|
|
663
|
+
# collect list of field names and/or nodes
|
|
664
|
+
includes = []
|
|
665
|
+
for key in fields:
|
|
636
666
|
if key in self.nodes:
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
auto_includes.append(key)
|
|
667
|
+
includes.append(self.nodes[key])
|
|
668
|
+
else:
|
|
669
|
+
includes.append(key)
|
|
641
670
|
|
|
642
671
|
# make initial schema with ColanderAlchemy magic
|
|
643
672
|
schema = SQLAlchemySchemaNode(self.model_class,
|
|
644
|
-
includes=
|
|
673
|
+
includes=includes)
|
|
645
674
|
|
|
646
|
-
#
|
|
647
|
-
for key in
|
|
648
|
-
if key not in
|
|
649
|
-
node =
|
|
675
|
+
# fill in the blanks if anything got missed
|
|
676
|
+
for key in fields:
|
|
677
|
+
if key not in schema:
|
|
678
|
+
node = colander.SchemaNode(colander.String(), name=key)
|
|
650
679
|
schema.add(node)
|
|
651
680
|
|
|
652
681
|
else:
|
|
@@ -685,6 +714,11 @@ class Form:
|
|
|
685
714
|
elif key in schema: # field-level
|
|
686
715
|
schema[key].validator = validator
|
|
687
716
|
|
|
717
|
+
# apply default value overrides
|
|
718
|
+
for key, value in self.defaults.items():
|
|
719
|
+
if key in schema:
|
|
720
|
+
schema[key].default = value
|
|
721
|
+
|
|
688
722
|
# apply required flags
|
|
689
723
|
for key, required in self.required_fields.items():
|
|
690
724
|
if key in schema:
|
|
@@ -775,7 +809,12 @@ class Form:
|
|
|
775
809
|
output = render(template, context)
|
|
776
810
|
return HTML.literal(output)
|
|
777
811
|
|
|
778
|
-
def render_vue_field(
|
|
812
|
+
def render_vue_field(
|
|
813
|
+
self,
|
|
814
|
+
fieldname,
|
|
815
|
+
readonly=None,
|
|
816
|
+
**kwargs,
|
|
817
|
+
):
|
|
779
818
|
"""
|
|
780
819
|
Render the given field completely, i.e. ``<b-field>`` wrapper
|
|
781
820
|
with label and containing a widget.
|
|
@@ -791,6 +830,12 @@ class Form:
|
|
|
791
830
|
message="something went wrong!">
|
|
792
831
|
<!-- widget element(s) -->
|
|
793
832
|
</b-field>
|
|
833
|
+
|
|
834
|
+
.. warning::
|
|
835
|
+
|
|
836
|
+
Any ``**kwargs`` received from caller are ignored by this
|
|
837
|
+
method. For now they are allowed, for sake of backwawrd
|
|
838
|
+
compatibility. This may change in the future.
|
|
794
839
|
"""
|
|
795
840
|
# readonly comes from: caller, field flag, or form flag
|
|
796
841
|
if readonly is None:
|
|
@@ -903,6 +948,20 @@ class Form:
|
|
|
903
948
|
|
|
904
949
|
return model_data
|
|
905
950
|
|
|
951
|
+
# TODO: for tailbone compat, should document?
|
|
952
|
+
# (ideally should remove this and find a better way)
|
|
953
|
+
def get_vue_field_value(self, key):
|
|
954
|
+
""" """
|
|
955
|
+
if key not in self.fields:
|
|
956
|
+
return
|
|
957
|
+
|
|
958
|
+
dform = self.get_deform()
|
|
959
|
+
if key not in dform:
|
|
960
|
+
return
|
|
961
|
+
|
|
962
|
+
field = dform[key]
|
|
963
|
+
return make_json_safe(field.cstruct)
|
|
964
|
+
|
|
906
965
|
def validate(self):
|
|
907
966
|
"""
|
|
908
967
|
Try to validate the form, using data from the :attr:`request`.
|
|
@@ -59,13 +59,16 @@ class ObjectNode(colander.SchemaNode):
|
|
|
59
59
|
:class:`ObjectRef`.
|
|
60
60
|
|
|
61
61
|
If the node's type does not have a ``dictify()`` method, this
|
|
62
|
-
will
|
|
62
|
+
will just convert the object to a string and return that.
|
|
63
63
|
"""
|
|
64
64
|
if hasattr(self.typ, 'dictify'):
|
|
65
65
|
return self.typ.dictify(obj)
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
# TODO: this is better than raising an error, as it previously
|
|
68
|
+
# did, but seems like troubleshooting problems may often lead
|
|
69
|
+
# one here.. i suspect this needs to do something smarter but
|
|
70
|
+
# not sure what that is yet
|
|
71
|
+
return str(obj)
|
|
69
72
|
|
|
70
73
|
def objectify(self, value):
|
|
71
74
|
"""
|
|
@@ -257,3 +260,101 @@ class PersonRef(ObjectRef):
|
|
|
257
260
|
def sort_query(self, query):
|
|
258
261
|
""" """
|
|
259
262
|
return query.order_by(self.model_class.full_name)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class WuttaSet(colander.Set):
|
|
266
|
+
"""
|
|
267
|
+
Custom schema type for :class:`python:set` fields.
|
|
268
|
+
|
|
269
|
+
This is a subclass of :class:`colander.Set`, but adds
|
|
270
|
+
Wutta-related params to the constructor.
|
|
271
|
+
|
|
272
|
+
:param request: Current :term:`request` object.
|
|
273
|
+
|
|
274
|
+
:param session: Optional :term:`db session` to use instead of
|
|
275
|
+
:class:`wuttaweb.db.Session`.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
def __init__(self, request, session=None):
|
|
279
|
+
super().__init__()
|
|
280
|
+
self.request = request
|
|
281
|
+
self.config = self.request.wutta_config
|
|
282
|
+
self.app = self.config.get_app()
|
|
283
|
+
self.session = session or Session()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class RoleRefs(WuttaSet):
|
|
287
|
+
"""
|
|
288
|
+
Form schema type for the User
|
|
289
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.User.roles`
|
|
290
|
+
association proxy field.
|
|
291
|
+
|
|
292
|
+
This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
|
|
293
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.auth.Role` ``uuid``
|
|
294
|
+
values for underlying data format.
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
def widget_maker(self, **kwargs):
|
|
298
|
+
"""
|
|
299
|
+
Constructs a default widget for the field.
|
|
300
|
+
|
|
301
|
+
:returns: Instance of
|
|
302
|
+
:class:`~wuttaweb.forms.widgets.RoleRefsWidget`.
|
|
303
|
+
"""
|
|
304
|
+
kwargs.setdefault('session', self.session)
|
|
305
|
+
|
|
306
|
+
if 'values' not in kwargs:
|
|
307
|
+
model = self.app.model
|
|
308
|
+
auth = self.app.get_auth_handler()
|
|
309
|
+
avoid = {
|
|
310
|
+
auth.get_role_authenticated(self.session),
|
|
311
|
+
auth.get_role_anonymous(self.session),
|
|
312
|
+
}
|
|
313
|
+
avoid = set([role.uuid for role in avoid])
|
|
314
|
+
roles = self.session.query(model.Role)\
|
|
315
|
+
.filter(~model.Role.uuid.in_(avoid))\
|
|
316
|
+
.order_by(model.Role.name)\
|
|
317
|
+
.all()
|
|
318
|
+
values = [(role.uuid, role.name) for role in roles]
|
|
319
|
+
kwargs['values'] = values
|
|
320
|
+
|
|
321
|
+
return widgets.RoleRefsWidget(self.request, **kwargs)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class Permissions(WuttaSet):
|
|
325
|
+
"""
|
|
326
|
+
Form schema type for the Role
|
|
327
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.permissions`
|
|
328
|
+
association proxy field.
|
|
329
|
+
|
|
330
|
+
This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
|
|
331
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Permission.permission`
|
|
332
|
+
values for underlying data format.
|
|
333
|
+
|
|
334
|
+
:param permissions: Dict with all possible permissions. Should be
|
|
335
|
+
in the same format as returned by
|
|
336
|
+
:meth:`~wuttaweb.views.roles.RoleView.get_available_permissions()`.
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
def __init__(self, request, permissions, *args, **kwargs):
|
|
340
|
+
super().__init__(request, *args, **kwargs)
|
|
341
|
+
self.permissions = permissions
|
|
342
|
+
|
|
343
|
+
def widget_maker(self, **kwargs):
|
|
344
|
+
"""
|
|
345
|
+
Constructs a default widget for the field.
|
|
346
|
+
|
|
347
|
+
:returns: Instance of
|
|
348
|
+
:class:`~wuttaweb.forms.widgets.PermissionsWidget`.
|
|
349
|
+
"""
|
|
350
|
+
kwargs.setdefault('session', self.session)
|
|
351
|
+
kwargs.setdefault('permissions', self.permissions)
|
|
352
|
+
|
|
353
|
+
if 'values' not in kwargs:
|
|
354
|
+
values = []
|
|
355
|
+
for gkey, group in self.permissions.items():
|
|
356
|
+
for pkey, perm in group['perms'].items():
|
|
357
|
+
values.append((pkey, perm['label']))
|
|
358
|
+
kwargs['values'] = values
|
|
359
|
+
|
|
360
|
+
return widgets.PermissionsWidget(self.request, **kwargs)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
Form widgets
|
|
25
|
+
|
|
26
|
+
This module defines some custom widgets for use with WuttaWeb.
|
|
27
|
+
|
|
28
|
+
However for convenience it also makes other Deform widgets available
|
|
29
|
+
in the namespace:
|
|
30
|
+
|
|
31
|
+
* :class:`deform:deform.widget.Widget` (base class)
|
|
32
|
+
* :class:`deform:deform.widget.TextInputWidget`
|
|
33
|
+
* :class:`deform:deform.widget.TextAreaWidget`
|
|
34
|
+
* :class:`deform:deform.widget.PasswordWidget`
|
|
35
|
+
* :class:`deform:deform.widget.CheckedPasswordWidget`
|
|
36
|
+
* :class:`deform:deform.widget.SelectWidget`
|
|
37
|
+
* :class:`deform:deform.widget.CheckboxChoiceWidget`
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
import colander
|
|
41
|
+
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
|
|
42
|
+
PasswordWidget, CheckedPasswordWidget,
|
|
43
|
+
SelectWidget, CheckboxChoiceWidget)
|
|
44
|
+
from webhelpers2.html import HTML
|
|
45
|
+
|
|
46
|
+
from wuttaweb.db import Session
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ObjectRefWidget(SelectWidget):
|
|
50
|
+
"""
|
|
51
|
+
Widget for use with model "object reference" fields, e.g. foreign
|
|
52
|
+
key UUID => TargetModel instance.
|
|
53
|
+
|
|
54
|
+
While you may create instances of this widget directly, it
|
|
55
|
+
normally happens automatically when schema nodes of the
|
|
56
|
+
:class:`~wuttaweb.forms.schema.ObjectRef` (sub)type are part of
|
|
57
|
+
the form schema; via
|
|
58
|
+
:meth:`~wuttaweb.forms.schema.ObjectRef.widget_maker()`.
|
|
59
|
+
|
|
60
|
+
In readonly mode, this renders a ``<span>`` tag around the
|
|
61
|
+
:attr:`model_instance` (converted to string).
|
|
62
|
+
|
|
63
|
+
Otherwise it renders a select (dropdown) element allowing user to
|
|
64
|
+
choose from available records.
|
|
65
|
+
|
|
66
|
+
This is a subclass of :class:`deform:deform.widget.SelectWidget`
|
|
67
|
+
and uses these Deform templates:
|
|
68
|
+
|
|
69
|
+
* ``select``
|
|
70
|
+
* ``readonly/objectref``
|
|
71
|
+
|
|
72
|
+
.. attribute:: model_instance
|
|
73
|
+
|
|
74
|
+
Reference to the model record instance, i.e. the "far side" of
|
|
75
|
+
the foreign key relationship.
|
|
76
|
+
|
|
77
|
+
.. note::
|
|
78
|
+
|
|
79
|
+
You do not need to provide the ``model_instance`` when
|
|
80
|
+
constructing the widget. Rather, it is set automatically
|
|
81
|
+
when the :class:`~wuttaweb.forms.schema.ObjectRef` type
|
|
82
|
+
instance (associated with the node) is serialized.
|
|
83
|
+
"""
|
|
84
|
+
readonly_template = 'readonly/objectref'
|
|
85
|
+
|
|
86
|
+
def __init__(self, request, *args, **kwargs):
|
|
87
|
+
super().__init__(*args, **kwargs)
|
|
88
|
+
self.request = request
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class NotesWidget(TextAreaWidget):
|
|
92
|
+
"""
|
|
93
|
+
Widget for use with "notes" fields.
|
|
94
|
+
|
|
95
|
+
In readonly mode, this shows the notes with a background to make
|
|
96
|
+
them stand out a bit more.
|
|
97
|
+
|
|
98
|
+
Otherwise it effectively shows a ``<textarea>`` input element.
|
|
99
|
+
|
|
100
|
+
This is a subclass of :class:`deform:deform.widget.TextAreaWidget`
|
|
101
|
+
and uses these Deform templates:
|
|
102
|
+
|
|
103
|
+
* ``textarea``
|
|
104
|
+
* ``readonly/notes``
|
|
105
|
+
"""
|
|
106
|
+
readonly_template = 'readonly/notes'
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
|
|
110
|
+
"""
|
|
111
|
+
Custom widget for :class:`python:set` fields.
|
|
112
|
+
|
|
113
|
+
This is a subclass of
|
|
114
|
+
:class:`deform:deform.widget.CheckboxChoiceWidget`, but adds
|
|
115
|
+
Wutta-related params to the constructor.
|
|
116
|
+
|
|
117
|
+
:param request: Current :term:`request` object.
|
|
118
|
+
|
|
119
|
+
:param session: Optional :term:`db session` to use instead of
|
|
120
|
+
:class:`wuttaweb.db.Session`.
|
|
121
|
+
|
|
122
|
+
It uses these Deform templates:
|
|
123
|
+
|
|
124
|
+
* ``checkbox_choice``
|
|
125
|
+
* ``readonly/checkbox_choice``
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, request, session=None, *args, **kwargs):
|
|
129
|
+
super().__init__(*args, **kwargs)
|
|
130
|
+
self.request = request
|
|
131
|
+
self.config = self.request.wutta_config
|
|
132
|
+
self.app = self.config.get_app()
|
|
133
|
+
self.session = session or Session()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class RoleRefsWidget(WuttaCheckboxChoiceWidget):
|
|
137
|
+
"""
|
|
138
|
+
Widget for use with User
|
|
139
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.User.roles` field.
|
|
140
|
+
|
|
141
|
+
This is a subclass of :class:`WuttaCheckboxChoiceWidget`.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def serialize(self, field, cstruct, **kw):
|
|
145
|
+
""" """
|
|
146
|
+
# special logic when field is editable
|
|
147
|
+
readonly = kw.get('readonly', self.readonly)
|
|
148
|
+
if not readonly:
|
|
149
|
+
|
|
150
|
+
# but does not apply if current user is root
|
|
151
|
+
if not self.request.is_root:
|
|
152
|
+
auth = self.app.get_auth_handler()
|
|
153
|
+
admin = auth.get_role_administrator(self.session)
|
|
154
|
+
|
|
155
|
+
# prune admin role from values list; it should not be
|
|
156
|
+
# one of the options since current user is not admin
|
|
157
|
+
values = kw.get('values', self.values)
|
|
158
|
+
values = [val for val in values
|
|
159
|
+
if val[0] != admin.uuid]
|
|
160
|
+
kw['values'] = values
|
|
161
|
+
|
|
162
|
+
# default logic from here
|
|
163
|
+
return super().serialize(field, cstruct, **kw)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class PermissionsWidget(WuttaCheckboxChoiceWidget):
|
|
167
|
+
"""
|
|
168
|
+
Widget for use with Role
|
|
169
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.permissions`
|
|
170
|
+
field.
|
|
171
|
+
|
|
172
|
+
This is a subclass of :class:`WuttaCheckboxChoiceWidget`. It uses
|
|
173
|
+
these Deform templates:
|
|
174
|
+
|
|
175
|
+
* ``permissions``
|
|
176
|
+
* ``readonly/permissions``
|
|
177
|
+
"""
|
|
178
|
+
template = 'permissions'
|
|
179
|
+
readonly_template = 'readonly/permissions'
|
|
180
|
+
|
|
181
|
+
def serialize(self, field, cstruct, **kw):
|
|
182
|
+
""" """
|
|
183
|
+
kw.setdefault('permissions', self.permissions)
|
|
184
|
+
|
|
185
|
+
if 'values' not in kw:
|
|
186
|
+
values = []
|
|
187
|
+
for gkey, group in self.permissions.items():
|
|
188
|
+
for pkey, perm in group['perms'].items():
|
|
189
|
+
values.append((pkey, perm['label']))
|
|
190
|
+
kw['values'] = values
|
|
191
|
+
|
|
192
|
+
return super().serialize(field, cstruct, **kw)
|