WuttaWeb 0.6.0__tar.gz → 0.7.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.7.0}/CHANGELOG.md +19 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/PKG-INFO +2 -2
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/pyproject.toml +2 -2
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/app.py +6 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/auth.py +90 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/forms/base.py +80 -19
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/forms/schema.py +98 -0
- wuttaweb-0.7.0/src/wuttaweb/forms/widgets.py +192 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/grids/base.py +80 -3
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/menus.py +2 -3
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/subscribers.py +57 -8
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/base.mako +15 -12
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/checkbox_choice.pt +18 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/permissions.pt +23 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/readonly/notes.pt +7 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/readonly/objectref.pt +1 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/readonly/permissions.pt +18 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/deform/textarea.pt +11 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/forbidden.mako +26 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/form.mako +9 -5
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/grids/vue_template.mako +2 -1
- wuttaweb-0.7.0/src/wuttaweb/templates/notfound.mako +23 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/people/view_profile.mako +12 -0
- wuttaweb-0.7.0/src/wuttaweb/templates/setup.mako +20 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/util.py +13 -8
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/auth.py +13 -8
- wuttaweb-0.7.0/src/wuttaweb/views/common.py +223 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/master.py +212 -51
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/people.py +43 -0
- wuttaweb-0.7.0/src/wuttaweb/views/roles.py +271 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/settings.py +4 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/users.py +86 -2
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/forms/test_base.py +67 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/forms/test_schema.py +55 -0
- wuttaweb-0.7.0/tests/forms/test_widgets.py +115 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/grids/test_base.py +33 -2
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_auth.py +24 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_menus.py +24 -9
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_subscribers.py +132 -1
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_util.py +8 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/util.py +11 -5
- wuttaweb-0.7.0/tests/views/test___init__.py +10 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/test_auth.py +45 -52
- wuttaweb-0.7.0/tests/views/test_common.py +95 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/test_master.py +249 -35
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/test_people.py +34 -0
- wuttaweb-0.7.0/tests/views/test_roles.py +242 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/test_settings.py +4 -0
- wuttaweb-0.7.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.7.0}/.gitignore +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/COPYING.txt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/README.md +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/Makefile +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/app.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/auth.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/db.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/forms.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/forms.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/forms.schema.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/forms.widgets.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/grids.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/grids.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/handler.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/helpers.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/menus.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/static.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/subscribers.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/util.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.auth.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.base.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.common.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.essential.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.master.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.people.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.roles.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.settings.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/api/wuttaweb/views.users.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/conf.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/make.bat +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/docs/narr/index.rst +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/db.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tasks.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_app.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/test_static.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.6.0 → wuttaweb-0.7.0}/tox.ini +0 -0
|
@@ -5,6 +5,25 @@ 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.7.0 (2024-08-15)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add sane views for 403 Forbidden and 404 Not Found
|
|
13
|
+
- add permission checks for menus, view routes
|
|
14
|
+
- add first-time setup page to create admin user
|
|
15
|
+
- expose User password for editing in master views
|
|
16
|
+
- expose Role permissions for editing
|
|
17
|
+
- expose User "roles" for editing
|
|
18
|
+
- improve widget, rendering for Role notes
|
|
19
|
+
|
|
20
|
+
### Fix
|
|
21
|
+
|
|
22
|
+
- add stub for `PersonView.make_user()`
|
|
23
|
+
- allow arbitrary kwargs for `Form.render_vue_field()`
|
|
24
|
+
- make some tweaks for better tailbone compatibility
|
|
25
|
+
- prevent delete for built-in roles
|
|
26
|
+
|
|
8
27
|
## v0.6.0 (2024-08-13)
|
|
9
28
|
|
|
10
29
|
### Feat
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.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.11.
|
|
36
|
+
Requires-Dist: wuttjamaican[db]>=0.11.1
|
|
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.7.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.11.
|
|
42
|
+
"WuttJamaican[db]>=0.11.1",
|
|
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.
|
|
@@ -471,6 +497,18 @@ class Form:
|
|
|
471
497
|
if self.schema and key in self.schema:
|
|
472
498
|
self.schema[key].validator = validator
|
|
473
499
|
|
|
500
|
+
def set_default(self, key, value):
|
|
501
|
+
"""
|
|
502
|
+
Set/override the default value for a field.
|
|
503
|
+
|
|
504
|
+
:param key: Name of field.
|
|
505
|
+
|
|
506
|
+
:param validator: Default value for the field.
|
|
507
|
+
|
|
508
|
+
Default value overrides are tracked via :attr:`defaults`.
|
|
509
|
+
"""
|
|
510
|
+
self.defaults[key] = value
|
|
511
|
+
|
|
474
512
|
def set_readonly(self, key, readonly=True):
|
|
475
513
|
"""
|
|
476
514
|
Enable or disable the "readonly" flag for a given field.
|
|
@@ -624,29 +662,22 @@ class Form:
|
|
|
624
662
|
|
|
625
663
|
if self.model_class:
|
|
626
664
|
|
|
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
|
|
665
|
+
# collect list of field names and/or nodes
|
|
666
|
+
includes = []
|
|
667
|
+
for key in fields:
|
|
636
668
|
if key in self.nodes:
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
auto_includes.append(key)
|
|
669
|
+
includes.append(self.nodes[key])
|
|
670
|
+
else:
|
|
671
|
+
includes.append(key)
|
|
641
672
|
|
|
642
673
|
# make initial schema with ColanderAlchemy magic
|
|
643
674
|
schema = SQLAlchemySchemaNode(self.model_class,
|
|
644
|
-
includes=
|
|
675
|
+
includes=includes)
|
|
645
676
|
|
|
646
|
-
#
|
|
647
|
-
for key in
|
|
648
|
-
if key not in
|
|
649
|
-
node =
|
|
677
|
+
# fill in the blanks if anything got missed
|
|
678
|
+
for key in fields:
|
|
679
|
+
if key not in schema:
|
|
680
|
+
node = colander.SchemaNode(colander.String(), name=key)
|
|
650
681
|
schema.add(node)
|
|
651
682
|
|
|
652
683
|
else:
|
|
@@ -685,6 +716,11 @@ class Form:
|
|
|
685
716
|
elif key in schema: # field-level
|
|
686
717
|
schema[key].validator = validator
|
|
687
718
|
|
|
719
|
+
# apply default value overrides
|
|
720
|
+
for key, value in self.defaults.items():
|
|
721
|
+
if key in schema:
|
|
722
|
+
schema[key].default = value
|
|
723
|
+
|
|
688
724
|
# apply required flags
|
|
689
725
|
for key, required in self.required_fields.items():
|
|
690
726
|
if key in schema:
|
|
@@ -775,7 +811,12 @@ class Form:
|
|
|
775
811
|
output = render(template, context)
|
|
776
812
|
return HTML.literal(output)
|
|
777
813
|
|
|
778
|
-
def render_vue_field(
|
|
814
|
+
def render_vue_field(
|
|
815
|
+
self,
|
|
816
|
+
fieldname,
|
|
817
|
+
readonly=None,
|
|
818
|
+
**kwargs,
|
|
819
|
+
):
|
|
779
820
|
"""
|
|
780
821
|
Render the given field completely, i.e. ``<b-field>`` wrapper
|
|
781
822
|
with label and containing a widget.
|
|
@@ -791,6 +832,12 @@ class Form:
|
|
|
791
832
|
message="something went wrong!">
|
|
792
833
|
<!-- widget element(s) -->
|
|
793
834
|
</b-field>
|
|
835
|
+
|
|
836
|
+
.. warning::
|
|
837
|
+
|
|
838
|
+
Any ``**kwargs`` received from caller are ignored by this
|
|
839
|
+
method. For now they are allowed, for sake of backwawrd
|
|
840
|
+
compatibility. This may change in the future.
|
|
794
841
|
"""
|
|
795
842
|
# readonly comes from: caller, field flag, or form flag
|
|
796
843
|
if readonly is None:
|
|
@@ -903,6 +950,20 @@ class Form:
|
|
|
903
950
|
|
|
904
951
|
return model_data
|
|
905
952
|
|
|
953
|
+
# TODO: for tailbone compat, should document?
|
|
954
|
+
# (ideally should remove this and find a better way)
|
|
955
|
+
def get_vue_field_value(self, key):
|
|
956
|
+
""" """
|
|
957
|
+
if key not in self.fields:
|
|
958
|
+
return
|
|
959
|
+
|
|
960
|
+
dform = self.get_deform()
|
|
961
|
+
if key not in dform:
|
|
962
|
+
return
|
|
963
|
+
|
|
964
|
+
field = dform[key]
|
|
965
|
+
return make_json_safe(field.cstruct)
|
|
966
|
+
|
|
906
967
|
def validate(self):
|
|
907
968
|
"""
|
|
908
969
|
Try to validate the form, using data from the :attr:`request`.
|
|
@@ -257,3 +257,101 @@ class PersonRef(ObjectRef):
|
|
|
257
257
|
def sort_query(self, query):
|
|
258
258
|
""" """
|
|
259
259
|
return query.order_by(self.model_class.full_name)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class WuttaSet(colander.Set):
|
|
263
|
+
"""
|
|
264
|
+
Custom schema type for :class:`python:set` fields.
|
|
265
|
+
|
|
266
|
+
This is a subclass of :class:`colander.Set`, but adds
|
|
267
|
+
Wutta-related params to the constructor.
|
|
268
|
+
|
|
269
|
+
:param request: Current :term:`request` object.
|
|
270
|
+
|
|
271
|
+
:param session: Optional :term:`db session` to use instead of
|
|
272
|
+
:class:`wuttaweb.db.Session`.
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def __init__(self, request, session=None):
|
|
276
|
+
super().__init__()
|
|
277
|
+
self.request = request
|
|
278
|
+
self.config = self.request.wutta_config
|
|
279
|
+
self.app = self.config.get_app()
|
|
280
|
+
self.session = session or Session()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class RoleRefs(WuttaSet):
|
|
284
|
+
"""
|
|
285
|
+
Form schema type for the User
|
|
286
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.User.roles`
|
|
287
|
+
association proxy field.
|
|
288
|
+
|
|
289
|
+
This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
|
|
290
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.auth.Role` ``uuid``
|
|
291
|
+
values for underlying data format.
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
def widget_maker(self, **kwargs):
|
|
295
|
+
"""
|
|
296
|
+
Constructs a default widget for the field.
|
|
297
|
+
|
|
298
|
+
:returns: Instance of
|
|
299
|
+
:class:`~wuttaweb.forms.widgets.RoleRefsWidget`.
|
|
300
|
+
"""
|
|
301
|
+
kwargs.setdefault('session', self.session)
|
|
302
|
+
|
|
303
|
+
if 'values' not in kwargs:
|
|
304
|
+
model = self.app.model
|
|
305
|
+
auth = self.app.get_auth_handler()
|
|
306
|
+
avoid = {
|
|
307
|
+
auth.get_role_authenticated(self.session),
|
|
308
|
+
auth.get_role_anonymous(self.session),
|
|
309
|
+
}
|
|
310
|
+
avoid = set([role.uuid for role in avoid])
|
|
311
|
+
roles = self.session.query(model.Role)\
|
|
312
|
+
.filter(~model.Role.uuid.in_(avoid))\
|
|
313
|
+
.order_by(model.Role.name)\
|
|
314
|
+
.all()
|
|
315
|
+
values = [(role.uuid, role.name) for role in roles]
|
|
316
|
+
kwargs['values'] = values
|
|
317
|
+
|
|
318
|
+
return widgets.RoleRefsWidget(self.request, **kwargs)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class Permissions(WuttaSet):
|
|
322
|
+
"""
|
|
323
|
+
Form schema type for the Role
|
|
324
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.permissions`
|
|
325
|
+
association proxy field.
|
|
326
|
+
|
|
327
|
+
This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
|
|
328
|
+
:attr:`~wuttjamaican:wuttjamaican.db.model.auth.Permission.permission`
|
|
329
|
+
values for underlying data format.
|
|
330
|
+
|
|
331
|
+
:param permissions: Dict with all possible permissions. Should be
|
|
332
|
+
in the same format as returned by
|
|
333
|
+
:meth:`~wuttaweb.views.roles.RoleView.get_available_permissions()`.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
def __init__(self, request, permissions, *args, **kwargs):
|
|
337
|
+
super().__init__(request, *args, **kwargs)
|
|
338
|
+
self.permissions = permissions
|
|
339
|
+
|
|
340
|
+
def widget_maker(self, **kwargs):
|
|
341
|
+
"""
|
|
342
|
+
Constructs a default widget for the field.
|
|
343
|
+
|
|
344
|
+
:returns: Instance of
|
|
345
|
+
:class:`~wuttaweb.forms.widgets.PermissionsWidget`.
|
|
346
|
+
"""
|
|
347
|
+
kwargs.setdefault('session', self.session)
|
|
348
|
+
kwargs.setdefault('permissions', self.permissions)
|
|
349
|
+
|
|
350
|
+
if 'values' not in kwargs:
|
|
351
|
+
values = []
|
|
352
|
+
for gkey, group in self.permissions.items():
|
|
353
|
+
for pkey, perm in group['perms'].items():
|
|
354
|
+
values.append((pkey, perm['label']))
|
|
355
|
+
kwargs['values'] = values
|
|
356
|
+
|
|
357
|
+
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)
|