WuttaWeb 0.12.1__tar.gz → 0.13.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.12.1 → wuttaweb-0.13.0}/CHANGELOG.md +26 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/PKG-INFO +3 -2
- wuttaweb-0.13.0/docs/api/wuttaweb/grids.filters.rst +6 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/index.rst +4 -0
- wuttaweb-0.13.0/docs/api/wuttaweb/progress.rst +6 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.people.rst +1 -1
- wuttaweb-0.13.0/docs/api/wuttaweb/views.progress.rst +6 -0
- wuttaweb-0.13.0/docs/api/wuttaweb/views.upgrades.rst +6 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/conf.py +1 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/pyproject.toml +3 -2
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/app.py +7 -5
- wuttaweb-0.13.0/src/wuttaweb/email/templates/feedback.html.mako +40 -0
- wuttaweb-0.13.0/src/wuttaweb/email/templates/feedback.txt.mako +23 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/forms/base.py +50 -24
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/forms/schema.py +123 -18
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/forms/widgets.py +70 -3
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/grids/base.py +180 -225
- wuttaweb-0.13.0/src/wuttaweb/grids/filters.py +444 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/menus.py +5 -0
- wuttaweb-0.13.0/src/wuttaweb/progress.py +165 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/appinfo/configure.mako +61 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/appinfo/index.mako +4 -1
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/auth/login.mako +2 -2
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/base.mako +199 -28
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/configure.mako +5 -1
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/checkbox.pt +1 -1
- wuttaweb-0.13.0/src/wuttaweb/templates/deform/moneyinput.pt +7 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/deform/readonly/checkbox.pt +4 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/deform/readonly/filedownload.pt +14 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/form.mako +6 -2
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/forms/vue_template.mako +3 -3
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/grids/table_element.mako +1 -1
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/grids/vue_template.mako +37 -5
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/home.mako +2 -2
- wuttaweb-0.13.0/src/wuttaweb/templates/master/index.mako +66 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/progress.mako +127 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/upgrade.mako +64 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/upgrades/configure.mako +20 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/upgrades/view.mako +37 -0
- wuttaweb-0.13.0/src/wuttaweb/templates/wutta-components.mako +342 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/util.py +26 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/base.py +41 -1
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/common.py +79 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/essential.py +4 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/master.py +656 -44
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/people.py +6 -0
- wuttaweb-0.13.0/src/wuttaweb/views/progress.py +75 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/roles.py +1 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/settings.py +15 -4
- wuttaweb-0.13.0/src/wuttaweb/views/upgrades.py +364 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/users.py +28 -3
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/forms/test_base.py +44 -30
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/forms/test_schema.py +70 -6
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/forms/test_widgets.py +51 -2
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/grids/test_base.py +128 -94
- wuttaweb-0.13.0/tests/grids/test_filters.py +385 -0
- wuttaweb-0.13.0/tests/test_progress.py +62 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_util.py +99 -91
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/util.py +4 -2
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_base.py +20 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_common.py +76 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_master.py +390 -0
- wuttaweb-0.13.0/tests/views/test_progress.py +62 -0
- wuttaweb-0.13.0/tests/views/test_upgrades.py +364 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_users.py +11 -0
- wuttaweb-0.12.1/src/wuttaweb/templates/master/index.mako +0 -31
- wuttaweb-0.12.1/src/wuttaweb/templates/wutta-components.mako +0 -167
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/.gitignore +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/COPYING.txt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/README.md +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/Makefile +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/index.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/app.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/auth.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/db.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/forms.base.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/forms.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/forms.schema.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/forms.widgets.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/grids.base.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/grids.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/handler.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/helpers.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/menus.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/static.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/subscribers.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/util.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.auth.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.base.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.common.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.essential.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.master.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.roles.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.settings.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/api/wuttaweb/views.users.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/index.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/make.bat +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/narr/index.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/db.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tasks.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_app.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_static.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.12.1 → wuttaweb-0.13.0}/tox.ini +0 -0
|
@@ -5,6 +5,32 @@ 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.13.0 (2024-08-26)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- use native wuttjamaican app to send feedback email
|
|
13
|
+
- add basic user feedback email mechanism
|
|
14
|
+
- add "progress" page for executing upgrades
|
|
15
|
+
- add basic support for execute upgrades, download stdout/stderr
|
|
16
|
+
- add basic progress page/indicator support
|
|
17
|
+
- add basic "delete results" grid tool
|
|
18
|
+
- add initial views for upgrades
|
|
19
|
+
- allow app db to be rattail-native instead of wutta-native
|
|
20
|
+
- add per-row css class support for grids
|
|
21
|
+
- improve grid filter API a bit, support string/bool filters
|
|
22
|
+
|
|
23
|
+
### Fix
|
|
24
|
+
|
|
25
|
+
- tweak max image size for full logo on home, login pages
|
|
26
|
+
- improve handling of boolean form fields
|
|
27
|
+
- misc. improvements for display of grids, form errors
|
|
28
|
+
- use autocomplete for grid filter verb choices
|
|
29
|
+
- small cleanup for grid filters template
|
|
30
|
+
- add once-button action for grid Reset View
|
|
31
|
+
- set sort defaults for users, roles
|
|
32
|
+
- add override hook for base form template
|
|
33
|
+
|
|
8
34
|
## v0.12.1 (2024-08-22)
|
|
9
35
|
|
|
10
36
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.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
|
|
@@ -25,6 +25,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
25
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
26
|
Requires-Python: >=3.8
|
|
27
27
|
Requires-Dist: colanderalchemy
|
|
28
|
+
Requires-Dist: humanize
|
|
28
29
|
Requires-Dist: paginate
|
|
29
30
|
Requires-Dist: paginate-sqlalchemy
|
|
30
31
|
Requires-Dist: pyramid-beaker
|
|
@@ -35,7 +36,7 @@ Requires-Dist: pyramid-tm
|
|
|
35
36
|
Requires-Dist: pyramid>=2
|
|
36
37
|
Requires-Dist: waitress
|
|
37
38
|
Requires-Dist: webhelpers2
|
|
38
|
-
Requires-Dist: wuttjamaican[db]>=0.
|
|
39
|
+
Requires-Dist: wuttjamaican[db,email]>=0.13.0
|
|
39
40
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
40
41
|
Provides-Extra: docs
|
|
41
42
|
Requires-Dist: furo; extra == 'docs'
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
forms.widgets
|
|
17
17
|
grids
|
|
18
18
|
grids.base
|
|
19
|
+
grids.filters
|
|
19
20
|
handler
|
|
20
21
|
helpers
|
|
21
22
|
menus
|
|
23
|
+
progress
|
|
22
24
|
static
|
|
23
25
|
subscribers
|
|
24
26
|
util
|
|
@@ -29,6 +31,8 @@
|
|
|
29
31
|
views.essential
|
|
30
32
|
views.master
|
|
31
33
|
views.people
|
|
34
|
+
views.progress
|
|
32
35
|
views.roles
|
|
33
36
|
views.settings
|
|
37
|
+
views.upgrades
|
|
34
38
|
views.users
|
|
@@ -31,6 +31,7 @@ intersphinx_mapping = {
|
|
|
31
31
|
'deform': ('https://docs.pylonsproject.org/projects/deform/en/latest/', None),
|
|
32
32
|
'pyramid': ('https://docs.pylonsproject.org/projects/pyramid/en/latest/', None),
|
|
33
33
|
'python': ('https://docs.python.org/3/', None),
|
|
34
|
+
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
|
34
35
|
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
|
|
35
36
|
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
|
36
37
|
}
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.13.0"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
|
@@ -31,6 +31,7 @@ classifiers = [
|
|
|
31
31
|
requires-python = ">= 3.8"
|
|
32
32
|
dependencies = [
|
|
33
33
|
"ColanderAlchemy",
|
|
34
|
+
"humanize",
|
|
34
35
|
"paginate",
|
|
35
36
|
"paginate_sqlalchemy",
|
|
36
37
|
"pyramid>=2",
|
|
@@ -41,7 +42,7 @@ dependencies = [
|
|
|
41
42
|
"pyramid_tm",
|
|
42
43
|
"waitress",
|
|
43
44
|
"WebHelpers2",
|
|
44
|
-
"WuttJamaican[db]>=0.
|
|
45
|
+
"WuttJamaican[db,email]>=0.13.0",
|
|
45
46
|
"zope.sqlalchemy>=1.5",
|
|
46
47
|
]
|
|
47
48
|
|
|
@@ -37,9 +37,10 @@ from wuttaweb.auth import WuttaSecurityPolicy
|
|
|
37
37
|
|
|
38
38
|
class WebAppProvider(AppProvider):
|
|
39
39
|
"""
|
|
40
|
-
The :term:`app provider` for WuttaWeb. This adds some methods
|
|
41
|
-
specific to web apps.
|
|
40
|
+
The :term:`app provider` for WuttaWeb. This adds some methods to
|
|
41
|
+
the :term:`app handler`, which are specific to web apps.
|
|
42
42
|
"""
|
|
43
|
+
email_templates = 'wuttaweb:email/templates'
|
|
43
44
|
|
|
44
45
|
def get_web_handler(self, **kwargs):
|
|
45
46
|
"""
|
|
@@ -61,7 +62,7 @@ class WebAppProvider(AppProvider):
|
|
|
61
62
|
return self.web_handler
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
def make_wutta_config(settings):
|
|
65
|
+
def make_wutta_config(settings, config_maker=None, **kwargs):
|
|
65
66
|
"""
|
|
66
67
|
Make a WuttaConfig object from the given settings.
|
|
67
68
|
|
|
@@ -93,8 +94,9 @@ def make_wutta_config(settings):
|
|
|
93
94
|
"section of config to the path of your "
|
|
94
95
|
"config file. Lame, but necessary.")
|
|
95
96
|
|
|
96
|
-
# make config
|
|
97
|
-
|
|
97
|
+
# make config, add to settings
|
|
98
|
+
config_maker = config_maker or make_config
|
|
99
|
+
wutta_config = config_maker(path, **kwargs)
|
|
98
100
|
settings['wutta_config'] = wutta_config
|
|
99
101
|
|
|
100
102
|
# configure database sessions
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
## -*- coding: utf-8 -*-
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<style type="text/css">
|
|
5
|
+
label {
|
|
6
|
+
display: block;
|
|
7
|
+
font-weight: bold;
|
|
8
|
+
margin-top: 1em;
|
|
9
|
+
}
|
|
10
|
+
p {
|
|
11
|
+
margin: 1em 0 1em 1.5em;
|
|
12
|
+
}
|
|
13
|
+
p.msg {
|
|
14
|
+
white-space: pre-wrap;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<h1>User feedback from website</h1>
|
|
20
|
+
|
|
21
|
+
<label>User Name</label>
|
|
22
|
+
<p>
|
|
23
|
+
% if user:
|
|
24
|
+
<a href="${user_url}">${user}</a>
|
|
25
|
+
% else:
|
|
26
|
+
${user_name}
|
|
27
|
+
% endif
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<label>Referring URL</label>
|
|
31
|
+
<p><a href="${referrer}">${referrer}</a></p>
|
|
32
|
+
|
|
33
|
+
<label>Client IP</label>
|
|
34
|
+
<p>${client_ip}</p>
|
|
35
|
+
|
|
36
|
+
<label>Message</label>
|
|
37
|
+
<p class="msg">${message}</p>
|
|
38
|
+
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## -*- coding: utf-8; -*-
|
|
2
|
+
|
|
3
|
+
# User feedback from website
|
|
4
|
+
|
|
5
|
+
**User Name**
|
|
6
|
+
|
|
7
|
+
% if user:
|
|
8
|
+
${user}
|
|
9
|
+
% else:
|
|
10
|
+
${user_name}
|
|
11
|
+
% endif
|
|
12
|
+
|
|
13
|
+
**Referring URL**
|
|
14
|
+
|
|
15
|
+
${referrer}
|
|
16
|
+
|
|
17
|
+
**Client IP**
|
|
18
|
+
|
|
19
|
+
${client_ip}
|
|
20
|
+
|
|
21
|
+
**Message**
|
|
22
|
+
|
|
23
|
+
${message}
|
|
@@ -313,7 +313,7 @@ class Form:
|
|
|
313
313
|
self.set_fields(fields or self.get_fields())
|
|
314
314
|
|
|
315
315
|
# nb. this tracks grid JSON data for inclusion in page template
|
|
316
|
-
self.
|
|
316
|
+
self.grid_vue_context = OrderedDict()
|
|
317
317
|
|
|
318
318
|
def __contains__(self, name):
|
|
319
319
|
"""
|
|
@@ -746,17 +746,39 @@ class Form:
|
|
|
746
746
|
kwargs = {}
|
|
747
747
|
|
|
748
748
|
if self.model_instance:
|
|
749
|
-
|
|
750
|
-
#
|
|
751
|
-
|
|
749
|
+
|
|
750
|
+
# TODO: i keep finding problems with this, not sure
|
|
751
|
+
# what needs to happen. some forms will have a simple
|
|
752
|
+
# dict for model_instance, others will have a proper
|
|
753
|
+
# SQLAlchemy object. and in the latter case, it may
|
|
754
|
+
# not be "wutta-native" but from another DB.
|
|
755
|
+
|
|
756
|
+
# so the problem is, how to detect whether we should
|
|
757
|
+
# use the model_instance as-is or if we should convert
|
|
758
|
+
# to a dict. some options include:
|
|
759
|
+
|
|
760
|
+
# - check if instance has dictify() method
|
|
761
|
+
# i *think* this was tried and didn't work? but do not recall
|
|
762
|
+
|
|
763
|
+
# - check if is instance of model.Base
|
|
764
|
+
# this is unreliable since model.Base is wutta-native
|
|
765
|
+
|
|
766
|
+
# - check if form has a model_class
|
|
767
|
+
# has not been tried yet
|
|
768
|
+
|
|
769
|
+
# - check if schema is from colanderalchemy
|
|
770
|
+
# this is what we are trying currently...
|
|
771
|
+
|
|
772
|
+
if isinstance(schema, SQLAlchemySchemaNode):
|
|
752
773
|
kwargs['appstruct'] = schema.dictify(self.model_instance)
|
|
753
774
|
else:
|
|
754
775
|
kwargs['appstruct'] = self.model_instance
|
|
755
776
|
|
|
756
|
-
|
|
777
|
+
# create the Deform instance
|
|
757
778
|
# nb. must give a reference back to wutta form; this is
|
|
758
779
|
# for sake of field schema nodes and widgets, e.g. to
|
|
759
780
|
# access the main model instance
|
|
781
|
+
form = deform.Form(schema, **kwargs)
|
|
760
782
|
form.wutta_form = self
|
|
761
783
|
self.deform_form = form
|
|
762
784
|
|
|
@@ -826,16 +848,16 @@ class Form:
|
|
|
826
848
|
output = render(template, context)
|
|
827
849
|
return HTML.literal(output)
|
|
828
850
|
|
|
829
|
-
def
|
|
851
|
+
def add_grid_vue_context(self, grid):
|
|
830
852
|
""" """
|
|
831
853
|
if not grid.key:
|
|
832
854
|
raise ValueError("grid must have a key!")
|
|
833
855
|
|
|
834
|
-
if grid.key in self.
|
|
856
|
+
if grid.key in self.grid_vue_context:
|
|
835
857
|
log.warning("grid data with key '%s' already registered, "
|
|
836
858
|
"but will be replaced", grid.key)
|
|
837
859
|
|
|
838
|
-
self.
|
|
860
|
+
self.grid_vue_context[grid.key] = grid.get_vue_context()
|
|
839
861
|
|
|
840
862
|
def render_vue_field(
|
|
841
863
|
self,
|
|
@@ -922,18 +944,13 @@ class Form:
|
|
|
922
944
|
if field_type:
|
|
923
945
|
attrs['type'] = field_type
|
|
924
946
|
if messages:
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
# else:
|
|
933
|
-
# # nb. must pass an array as JSON string
|
|
934
|
-
# attrs[':message'] = '[{}]'.format(', '.join([
|
|
935
|
-
# "'{}'".format(msg.replace("'", r"\'"))
|
|
936
|
-
# for msg in messages]))
|
|
947
|
+
cls = 'is-size-7'
|
|
948
|
+
if field_type == 'is-danger':
|
|
949
|
+
cls += ' has-text-danger'
|
|
950
|
+
messages = [HTML.tag('p', c=[msg], class_=cls)
|
|
951
|
+
for msg in messages]
|
|
952
|
+
slot = HTML.tag('slot', name='messages', c=messages)
|
|
953
|
+
html = HTML.tag('div', c=[html, slot])
|
|
937
954
|
|
|
938
955
|
return HTML.tag('b-field', c=[html], **attrs)
|
|
939
956
|
|
|
@@ -978,7 +995,16 @@ class Form:
|
|
|
978
995
|
model_data = {}
|
|
979
996
|
|
|
980
997
|
def assign(field):
|
|
981
|
-
|
|
998
|
+
value = field.cstruct
|
|
999
|
+
|
|
1000
|
+
# TODO: we need a proper true/false on the Vue side,
|
|
1001
|
+
# but deform/colander want 'true' and 'false' ..so
|
|
1002
|
+
# for now we explicitly translate here, ugh. also
|
|
1003
|
+
# note this does not yet allow for null values.. :(
|
|
1004
|
+
if isinstance(field.typ, colander.Boolean):
|
|
1005
|
+
value = True if field.typ.true_val else False
|
|
1006
|
+
|
|
1007
|
+
model_data[field.oid] = make_json_safe(value)
|
|
982
1008
|
|
|
983
1009
|
for key in self.fields:
|
|
984
1010
|
|
|
@@ -1076,7 +1102,7 @@ class Form:
|
|
|
1076
1102
|
"""
|
|
1077
1103
|
dform = self.get_deform()
|
|
1078
1104
|
if field in dform:
|
|
1079
|
-
|
|
1080
|
-
if error:
|
|
1081
|
-
return
|
|
1105
|
+
field = dform[field]
|
|
1106
|
+
if field.error:
|
|
1107
|
+
return field.error.messages()
|
|
1082
1108
|
return []
|
|
@@ -92,6 +92,53 @@ class ObjectNode(colander.SchemaNode):
|
|
|
92
92
|
raise NotImplementedError(f"you must define {class_name}.objectify()")
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
class WuttaEnum(colander.Enum):
|
|
96
|
+
"""
|
|
97
|
+
Custom schema type for enum fields.
|
|
98
|
+
|
|
99
|
+
This is a subclass of :class:`colander.Enum`, but adds a
|
|
100
|
+
default widget (``SelectWidget``) with enum choices.
|
|
101
|
+
|
|
102
|
+
:param request: Current :term:`request` object.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(self, request, *args, **kwargs):
|
|
106
|
+
super().__init__(*args, **kwargs)
|
|
107
|
+
self.request = request
|
|
108
|
+
self.config = self.request.wutta_config
|
|
109
|
+
self.app = self.config.get_app()
|
|
110
|
+
|
|
111
|
+
def widget_maker(self, **kwargs):
|
|
112
|
+
""" """
|
|
113
|
+
|
|
114
|
+
if 'values' not in kwargs:
|
|
115
|
+
kwargs['values'] = [(getattr(e, self.attr), getattr(e, self.attr))
|
|
116
|
+
for e in self.enum_cls]
|
|
117
|
+
|
|
118
|
+
return widgets.SelectWidget(**kwargs)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class WuttaSet(colander.Set):
|
|
122
|
+
"""
|
|
123
|
+
Custom schema type for :class:`python:set` fields.
|
|
124
|
+
|
|
125
|
+
This is a subclass of :class:`colander.Set`, but adds
|
|
126
|
+
Wutta-related params to the constructor.
|
|
127
|
+
|
|
128
|
+
:param request: Current :term:`request` object.
|
|
129
|
+
|
|
130
|
+
:param session: Optional :term:`db session` to use instead of
|
|
131
|
+
:class:`wuttaweb.db.Session`.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def __init__(self, request, session=None):
|
|
135
|
+
super().__init__()
|
|
136
|
+
self.request = request
|
|
137
|
+
self.config = self.request.wutta_config
|
|
138
|
+
self.app = self.config.get_app()
|
|
139
|
+
self.session = session or Session()
|
|
140
|
+
|
|
141
|
+
|
|
95
142
|
class ObjectRef(colander.SchemaType):
|
|
96
143
|
"""
|
|
97
144
|
Custom schema type for a model class reference field.
|
|
@@ -199,7 +246,7 @@ class ObjectRef(colander.SchemaType):
|
|
|
199
246
|
|
|
200
247
|
# fetch object from DB
|
|
201
248
|
model = self.app.model
|
|
202
|
-
obj = self.session.
|
|
249
|
+
obj = self.session.get(self.model_class, value)
|
|
203
250
|
|
|
204
251
|
# raise error if not found
|
|
205
252
|
if not obj:
|
|
@@ -247,43 +294,69 @@ class ObjectRef(colander.SchemaType):
|
|
|
247
294
|
kwargs['values'] = values
|
|
248
295
|
|
|
249
296
|
if 'url' not in kwargs:
|
|
250
|
-
kwargs['url'] =
|
|
297
|
+
kwargs['url'] = self.get_object_url
|
|
251
298
|
|
|
252
299
|
return widgets.ObjectRefWidget(self.request, **kwargs)
|
|
253
300
|
|
|
301
|
+
def get_object_url(self, obj):
|
|
302
|
+
"""
|
|
303
|
+
Returns the "view" URL for the given object, if applicable.
|
|
304
|
+
|
|
305
|
+
This is used when rendering the field readonly. If this
|
|
306
|
+
method returns a URL then the field text will be wrapped with
|
|
307
|
+
a hyperlink, otherwise it will be shown as-is.
|
|
308
|
+
|
|
309
|
+
Default logic always returns ``None``; subclass should
|
|
310
|
+
override as needed.
|
|
311
|
+
"""
|
|
312
|
+
|
|
254
313
|
|
|
255
314
|
class PersonRef(ObjectRef):
|
|
256
315
|
"""
|
|
257
|
-
Custom schema type for a
|
|
316
|
+
Custom schema type for a
|
|
317
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.base.Person` reference
|
|
318
|
+
field.
|
|
258
319
|
|
|
259
320
|
This is a subclass of :class:`ObjectRef`.
|
|
260
321
|
"""
|
|
261
|
-
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def model_class(self):
|
|
325
|
+
""" """
|
|
326
|
+
model = self.app.model
|
|
327
|
+
return model.Person
|
|
262
328
|
|
|
263
329
|
def sort_query(self, query):
|
|
264
330
|
""" """
|
|
265
331
|
return query.order_by(self.model_class.full_name)
|
|
266
332
|
|
|
333
|
+
def get_object_url(self, person):
|
|
334
|
+
""" """
|
|
335
|
+
return self.request.route_url('people.view', uuid=person.uuid)
|
|
267
336
|
|
|
268
|
-
|
|
337
|
+
|
|
338
|
+
class UserRef(ObjectRef):
|
|
269
339
|
"""
|
|
270
|
-
Custom schema type for
|
|
340
|
+
Custom schema type for a
|
|
341
|
+
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` reference
|
|
342
|
+
field.
|
|
271
343
|
|
|
272
|
-
This is a subclass of :class:`
|
|
273
|
-
|
|
344
|
+
This is a subclass of :class:`ObjectRef`.
|
|
345
|
+
"""
|
|
274
346
|
|
|
275
|
-
|
|
347
|
+
@property
|
|
348
|
+
def model_class(self):
|
|
349
|
+
""" """
|
|
350
|
+
model = self.app.model
|
|
351
|
+
return model.User
|
|
276
352
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
353
|
+
def sort_query(self, query):
|
|
354
|
+
""" """
|
|
355
|
+
return query.order_by(self.model_class.username)
|
|
280
356
|
|
|
281
|
-
def
|
|
282
|
-
|
|
283
|
-
self.request =
|
|
284
|
-
self.config = self.request.wutta_config
|
|
285
|
-
self.app = self.config.get_app()
|
|
286
|
-
self.session = session or Session()
|
|
357
|
+
def get_object_url(self, user):
|
|
358
|
+
""" """
|
|
359
|
+
return self.request.route_url('users.view', uuid=user.uuid)
|
|
287
360
|
|
|
288
361
|
|
|
289
362
|
class RoleRefs(WuttaSet):
|
|
@@ -383,3 +456,35 @@ class Permissions(WuttaSet):
|
|
|
383
456
|
kwargs['values'] = values
|
|
384
457
|
|
|
385
458
|
return widgets.PermissionsWidget(self.request, **kwargs)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
class FileDownload(colander.String):
|
|
462
|
+
"""
|
|
463
|
+
Custom schema type for a file download field.
|
|
464
|
+
|
|
465
|
+
This field is only meant for readonly use, it does not handle file
|
|
466
|
+
uploads.
|
|
467
|
+
|
|
468
|
+
It expects the incoming ``appstruct`` to be the path to a file on
|
|
469
|
+
disk (or null).
|
|
470
|
+
|
|
471
|
+
Uses the :class:`~wuttaweb.forms.widgets.FileDownloadWidget` by
|
|
472
|
+
default.
|
|
473
|
+
|
|
474
|
+
:param request: Current :term:`request` object.
|
|
475
|
+
|
|
476
|
+
:param url: Optional URL for hyperlink. If not specified, file
|
|
477
|
+
name/size is shown with no hyperlink.
|
|
478
|
+
"""
|
|
479
|
+
|
|
480
|
+
def __init__(self, request, *args, **kwargs):
|
|
481
|
+
self.url = kwargs.pop('url', None)
|
|
482
|
+
super().__init__(*args, **kwargs)
|
|
483
|
+
self.request = request
|
|
484
|
+
self.config = self.request.wutta_config
|
|
485
|
+
self.app = self.config.get_app()
|
|
486
|
+
|
|
487
|
+
def widget_maker(self, **kwargs):
|
|
488
|
+
""" """
|
|
489
|
+
kwargs.setdefault('url', self.url)
|
|
490
|
+
return widgets.FileDownloadWidget(self.request, **kwargs)
|
|
@@ -33,14 +33,20 @@ in the namespace:
|
|
|
33
33
|
* :class:`deform:deform.widget.TextAreaWidget`
|
|
34
34
|
* :class:`deform:deform.widget.PasswordWidget`
|
|
35
35
|
* :class:`deform:deform.widget.CheckedPasswordWidget`
|
|
36
|
+
* :class:`deform:deform.widget.CheckboxWidget`
|
|
36
37
|
* :class:`deform:deform.widget.SelectWidget`
|
|
37
38
|
* :class:`deform:deform.widget.CheckboxChoiceWidget`
|
|
39
|
+
* :class:`deform:deform.widget.MoneyInputWidget`
|
|
38
40
|
"""
|
|
39
41
|
|
|
42
|
+
import os
|
|
43
|
+
|
|
40
44
|
import colander
|
|
45
|
+
import humanize
|
|
41
46
|
from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
|
|
42
47
|
PasswordWidget, CheckedPasswordWidget,
|
|
43
|
-
SelectWidget, CheckboxChoiceWidget
|
|
48
|
+
CheckboxWidget, SelectWidget, CheckboxChoiceWidget,
|
|
49
|
+
MoneyInputWidget)
|
|
44
50
|
from webhelpers2.html import HTML
|
|
45
51
|
|
|
46
52
|
from wuttaweb.db import Session
|
|
@@ -144,6 +150,63 @@ class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
|
|
|
144
150
|
self.session = session or Session()
|
|
145
151
|
|
|
146
152
|
|
|
153
|
+
class FileDownloadWidget(Widget):
|
|
154
|
+
"""
|
|
155
|
+
Widget for use with :class:`~wuttaweb.forms.schema.FileDownload`
|
|
156
|
+
fields.
|
|
157
|
+
|
|
158
|
+
This only supports readonly, and shows a hyperlink to download the
|
|
159
|
+
file. Link text is the filename plus file size.
|
|
160
|
+
|
|
161
|
+
This is a subclass of :class:`deform:deform.widget.Widget` and
|
|
162
|
+
uses these Deform templates:
|
|
163
|
+
|
|
164
|
+
* ``readonly/filedownload``
|
|
165
|
+
|
|
166
|
+
:param request: Current :term:`request` object.
|
|
167
|
+
|
|
168
|
+
:param url: Optional URL for hyperlink. If not specified, file
|
|
169
|
+
name/size is shown with no hyperlink.
|
|
170
|
+
"""
|
|
171
|
+
readonly_template = 'readonly/filedownload'
|
|
172
|
+
|
|
173
|
+
def __init__(self, request, *args, **kwargs):
|
|
174
|
+
self.url = kwargs.pop('url', None)
|
|
175
|
+
super().__init__(*args, **kwargs)
|
|
176
|
+
self.request = request
|
|
177
|
+
self.config = self.request.wutta_config
|
|
178
|
+
self.app = self.config.get_app()
|
|
179
|
+
|
|
180
|
+
def serialize(self, field, cstruct, **kw):
|
|
181
|
+
""" """
|
|
182
|
+
# nb. readonly is the only way this rolls
|
|
183
|
+
kw['readonly'] = True
|
|
184
|
+
template = self.readonly_template
|
|
185
|
+
|
|
186
|
+
path = cstruct or None
|
|
187
|
+
if path:
|
|
188
|
+
kw.setdefault('filename', os.path.basename(path))
|
|
189
|
+
kw.setdefault('filesize', self.readable_size(path))
|
|
190
|
+
if self.url:
|
|
191
|
+
kw.setdefault('url', self.url)
|
|
192
|
+
|
|
193
|
+
else:
|
|
194
|
+
kw.setdefault('filename', None)
|
|
195
|
+
kw.setdefault('filesize', None)
|
|
196
|
+
|
|
197
|
+
kw.setdefault('url', None)
|
|
198
|
+
values = self.get_template_values(field, cstruct, kw)
|
|
199
|
+
return field.renderer(template, **values)
|
|
200
|
+
|
|
201
|
+
def readable_size(self, path):
|
|
202
|
+
""" """
|
|
203
|
+
try:
|
|
204
|
+
size = os.path.getsize(path)
|
|
205
|
+
except os.error:
|
|
206
|
+
size = 0
|
|
207
|
+
return humanize.naturalsize(size)
|
|
208
|
+
|
|
209
|
+
|
|
147
210
|
class RoleRefsWidget(WuttaCheckboxChoiceWidget):
|
|
148
211
|
"""
|
|
149
212
|
Widget for use with User
|
|
@@ -181,7 +244,7 @@ class RoleRefsWidget(WuttaCheckboxChoiceWidget):
|
|
|
181
244
|
roles = []
|
|
182
245
|
if cstruct:
|
|
183
246
|
for uuid in cstruct:
|
|
184
|
-
role = self.session.
|
|
247
|
+
role = self.session.get(model.Role, uuid)
|
|
185
248
|
if role:
|
|
186
249
|
roles.append(role)
|
|
187
250
|
kw['roles'] = roles
|
|
@@ -220,11 +283,15 @@ class UserRefsWidget(WuttaCheckboxChoiceWidget):
|
|
|
220
283
|
users = []
|
|
221
284
|
if cstruct:
|
|
222
285
|
for uuid in cstruct:
|
|
223
|
-
user = self.session.
|
|
286
|
+
user = self.session.get(model.User, uuid)
|
|
224
287
|
if user:
|
|
225
288
|
users.append(dict([(key, getattr(user, key))
|
|
226
289
|
for key in columns + ['uuid']]))
|
|
227
290
|
|
|
291
|
+
# do not render if no data
|
|
292
|
+
if not users:
|
|
293
|
+
return HTML.tag('span')
|
|
294
|
+
|
|
228
295
|
# grid
|
|
229
296
|
grid = Grid(self.request, key='roles.view.users',
|
|
230
297
|
columns=columns, data=users)
|