WuttaWeb 0.7.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.7.0 → wuttaweb-0.8.0}/CHANGELOG.md +10 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/PKG-INFO +2 -2
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/pyproject.toml +2 -2
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/base.py +2 -4
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/schema.py +6 -3
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/grids/base.py +36 -2
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/master.py +69 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/people.py +3 -2
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/forms/test_schema.py +3 -2
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/grids/test_base.py +24 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_master.py +39 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/.gitignore +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/COPYING.txt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/README.md +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/Makefile +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/index.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/app.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/auth.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/db.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.base.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.schema.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/forms.widgets.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/grids.base.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/grids.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/handler.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/helpers.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/index.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/menus.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/static.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/subscribers.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/util.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.auth.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.base.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.common.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.essential.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.master.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.people.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.roles.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.settings.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/api/wuttaweb/views.users.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/conf.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/index.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/make.bat +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/docs/narr/index.rst +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/db.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/forms/widgets.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/base.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tasks.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/forms/test_widgets.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_app.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_static.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/test_util.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/util.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tests/views/test_users.py +0 -0
- {wuttaweb-0.7.0 → wuttaweb-0.8.0}/tox.ini +0 -0
|
@@ -5,6 +5,16 @@ 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
|
+
|
|
8
18
|
## v0.7.0 (2024-08-15)
|
|
9
19
|
|
|
10
20
|
### 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
|
|
|
@@ -435,16 +435,14 @@ class Form:
|
|
|
435
435
|
|
|
436
436
|
Node overrides are tracked via :attr:`nodes`.
|
|
437
437
|
"""
|
|
438
|
+
from wuttaweb.forms.schema import ObjectNode
|
|
439
|
+
|
|
438
440
|
if isinstance(nodeinfo, colander.SchemaNode):
|
|
439
441
|
# assume nodeinfo is a complete node
|
|
440
442
|
node = nodeinfo
|
|
441
443
|
|
|
442
444
|
else: # assume nodeinfo is a schema type
|
|
443
445
|
kwargs.setdefault('name', key)
|
|
444
|
-
|
|
445
|
-
from wuttaweb.forms.schema import ObjectNode
|
|
446
|
-
|
|
447
|
-
# node = colander.SchemaNode(nodeinfo, **kwargs)
|
|
448
446
|
node = ObjectNode(nodeinfo, **kwargs)
|
|
449
447
|
|
|
450
448
|
self.nodes[key] = node
|
|
@@ -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
|
"""
|
|
@@ -82,6 +82,12 @@ class Grid:
|
|
|
82
82
|
model records) or else an object capable of producing such a
|
|
83
83
|
list, e.g. SQLAlchemy query.
|
|
84
84
|
|
|
85
|
+
.. attribute:: labels
|
|
86
|
+
|
|
87
|
+
Dict of column label overrides.
|
|
88
|
+
|
|
89
|
+
See also :meth:`get_label()` and :meth:`set_label()`.
|
|
90
|
+
|
|
85
91
|
.. attribute:: renderers
|
|
86
92
|
|
|
87
93
|
Dict of column (cell) value renderer overrides.
|
|
@@ -113,6 +119,7 @@ class Grid:
|
|
|
113
119
|
key=None,
|
|
114
120
|
columns=None,
|
|
115
121
|
data=None,
|
|
122
|
+
labels={},
|
|
116
123
|
renderers={},
|
|
117
124
|
actions=[],
|
|
118
125
|
linked_columns=[],
|
|
@@ -122,6 +129,7 @@ class Grid:
|
|
|
122
129
|
self.model_class = model_class
|
|
123
130
|
self.key = key
|
|
124
131
|
self.data = data
|
|
132
|
+
self.labels = labels or {}
|
|
125
133
|
self.renderers = renderers or {}
|
|
126
134
|
self.actions = actions or []
|
|
127
135
|
self.linked_columns = linked_columns or []
|
|
@@ -220,6 +228,32 @@ class Grid:
|
|
|
220
228
|
if key in self.columns:
|
|
221
229
|
self.columns.remove(key)
|
|
222
230
|
|
|
231
|
+
def set_label(self, key, label):
|
|
232
|
+
"""
|
|
233
|
+
Set/override the label for a column.
|
|
234
|
+
|
|
235
|
+
:param key: Name of column.
|
|
236
|
+
|
|
237
|
+
:param label: New label for the column header.
|
|
238
|
+
|
|
239
|
+
See also :meth:`get_label()`.
|
|
240
|
+
|
|
241
|
+
Label overrides are tracked via :attr:`labels`.
|
|
242
|
+
"""
|
|
243
|
+
self.labels[key] = label
|
|
244
|
+
|
|
245
|
+
def get_label(self, key):
|
|
246
|
+
"""
|
|
247
|
+
Returns the label text for a given column.
|
|
248
|
+
|
|
249
|
+
If no override is defined, the label is derived from ``key``.
|
|
250
|
+
|
|
251
|
+
See also :meth:`set_label()`.
|
|
252
|
+
"""
|
|
253
|
+
if key in self.labels:
|
|
254
|
+
return self.labels[key]
|
|
255
|
+
return self.app.make_title(key)
|
|
256
|
+
|
|
223
257
|
def set_renderer(self, key, renderer, **kwargs):
|
|
224
258
|
"""
|
|
225
259
|
Set/override the value renderer for a column.
|
|
@@ -376,7 +410,7 @@ class Grid:
|
|
|
376
410
|
for name in self.columns:
|
|
377
411
|
columns.append({
|
|
378
412
|
'field': name,
|
|
379
|
-
'label': self.
|
|
413
|
+
'label': self.get_label(name),
|
|
380
414
|
})
|
|
381
415
|
return columns
|
|
382
416
|
|
|
@@ -430,7 +464,7 @@ class Grid:
|
|
|
430
464
|
|
|
431
465
|
# customize value rendering where applicable
|
|
432
466
|
for key in self.renderers:
|
|
433
|
-
value = record
|
|
467
|
+
value = record.get(key, None)
|
|
434
468
|
record[key] = self.renderers[key](original_record, key, value)
|
|
435
469
|
|
|
436
470
|
# add action urls to each record
|
|
@@ -33,6 +33,7 @@ from webhelpers2.html import HTML
|
|
|
33
33
|
from wuttaweb.views import View
|
|
34
34
|
from wuttaweb.util import get_form_data, get_model_fields
|
|
35
35
|
from wuttaweb.db import Session
|
|
36
|
+
from wuttjamaican.util import get_class_hierarchy
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class MasterView(View):
|
|
@@ -803,6 +804,16 @@ class MasterView(View):
|
|
|
803
804
|
# support methods
|
|
804
805
|
##############################
|
|
805
806
|
|
|
807
|
+
def get_class_hierarchy(self, topfirst=True):
|
|
808
|
+
"""
|
|
809
|
+
Convenience to return a list of classes from which the current
|
|
810
|
+
class inherits.
|
|
811
|
+
|
|
812
|
+
This is a wrapper around
|
|
813
|
+
:func:`wuttjamaican.util.get_class_hierarchy()`.
|
|
814
|
+
"""
|
|
815
|
+
return get_class_hierarchy(self.__class__, topfirst=topfirst)
|
|
816
|
+
|
|
806
817
|
def has_perm(self, name):
|
|
807
818
|
"""
|
|
808
819
|
Shortcut to check if current user has the given permission.
|
|
@@ -949,6 +960,60 @@ class MasterView(View):
|
|
|
949
960
|
route_prefix = self.get_route_prefix()
|
|
950
961
|
return self.request.route_url(route_prefix, **kwargs)
|
|
951
962
|
|
|
963
|
+
def set_labels(self, obj):
|
|
964
|
+
"""
|
|
965
|
+
Set label overrides on a form or grid, based on what is
|
|
966
|
+
defined by the view class and its parent class(es).
|
|
967
|
+
|
|
968
|
+
This is called automatically from :meth:`configure_grid()` and
|
|
969
|
+
:meth:`configure_form()`.
|
|
970
|
+
|
|
971
|
+
This calls :meth:`collect_labels()` to find everything, then
|
|
972
|
+
it assigns the labels using one of (based on ``obj`` type):
|
|
973
|
+
|
|
974
|
+
* :func:`wuttaweb.forms.base.Form.set_label()`
|
|
975
|
+
* :func:`wuttaweb.grids.base.Grid.set_label()`
|
|
976
|
+
|
|
977
|
+
:param obj: Either a :class:`~wuttaweb.grids.base.Grid` or a
|
|
978
|
+
:class:`~wuttaweb.forms.base.Form` instance.
|
|
979
|
+
"""
|
|
980
|
+
labels = self.collect_labels()
|
|
981
|
+
for key, label in labels.items():
|
|
982
|
+
obj.set_label(key, label)
|
|
983
|
+
|
|
984
|
+
def collect_labels(self):
|
|
985
|
+
"""
|
|
986
|
+
Collect all labels defined by the view class and/or its parents.
|
|
987
|
+
|
|
988
|
+
A master view can declare labels via class-level attribute,
|
|
989
|
+
like so::
|
|
990
|
+
|
|
991
|
+
from wuttaweb.views import MasterView
|
|
992
|
+
|
|
993
|
+
class WidgetView(MasterView):
|
|
994
|
+
|
|
995
|
+
labels = {
|
|
996
|
+
'id': "Widget ID",
|
|
997
|
+
'serial_no': "Serial Number",
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
All such labels, defined by any class from which the master
|
|
1001
|
+
view inherits, will be returned. However if the same label
|
|
1002
|
+
key is defined by multiple classes, the "subclass" always
|
|
1003
|
+
wins.
|
|
1004
|
+
|
|
1005
|
+
Labels defined in this way will apply to both forms and grids.
|
|
1006
|
+
See also :meth:`set_labels()`.
|
|
1007
|
+
|
|
1008
|
+
:returns: Dict of all labels found.
|
|
1009
|
+
"""
|
|
1010
|
+
labels = {}
|
|
1011
|
+
hierarchy = self.get_class_hierarchy()
|
|
1012
|
+
for cls in hierarchy:
|
|
1013
|
+
if hasattr(cls, 'labels'):
|
|
1014
|
+
labels.update(cls.labels)
|
|
1015
|
+
return labels
|
|
1016
|
+
|
|
952
1017
|
def make_model_grid(self, session=None, **kwargs):
|
|
953
1018
|
"""
|
|
954
1019
|
Create and return a :class:`~wuttaweb.grids.base.Grid`
|
|
@@ -1072,6 +1137,8 @@ class MasterView(View):
|
|
|
1072
1137
|
if 'uuid' in grid.columns:
|
|
1073
1138
|
grid.columns.remove('uuid')
|
|
1074
1139
|
|
|
1140
|
+
self.set_labels(grid)
|
|
1141
|
+
|
|
1075
1142
|
for key in self.get_model_key():
|
|
1076
1143
|
grid.set_link(key)
|
|
1077
1144
|
|
|
@@ -1311,6 +1378,8 @@ class MasterView(View):
|
|
|
1311
1378
|
"""
|
|
1312
1379
|
form.remove('uuid')
|
|
1313
1380
|
|
|
1381
|
+
self.set_labels(form)
|
|
1382
|
+
|
|
1314
1383
|
if self.editing:
|
|
1315
1384
|
for key in self.get_model_key():
|
|
1316
1385
|
form.set_readonly(key)
|
|
@@ -87,9 +87,10 @@ class PersonView(MasterView):
|
|
|
87
87
|
|
|
88
88
|
def view_profile(self, session=None):
|
|
89
89
|
""" """
|
|
90
|
-
|
|
90
|
+
person = self.get_instance(session=session)
|
|
91
91
|
context = {
|
|
92
|
-
'
|
|
92
|
+
'person': person,
|
|
93
|
+
'instance': person,
|
|
93
94
|
}
|
|
94
95
|
return self.render_to_response('view_profile', context)
|
|
95
96
|
|
|
@@ -22,9 +22,10 @@ class TestObjectNode(DataTestCase):
|
|
|
22
22
|
model = self.app.model
|
|
23
23
|
person = model.Person(full_name="Betty Boop")
|
|
24
24
|
|
|
25
|
-
# unsupported type
|
|
25
|
+
# unsupported type is converted to string
|
|
26
26
|
node = mod.ObjectNode(colander.String())
|
|
27
|
-
|
|
27
|
+
value = node.dictify(person)
|
|
28
|
+
self.assertEqual(value, "Betty Boop")
|
|
28
29
|
|
|
29
30
|
# but supported type can dictify
|
|
30
31
|
node = mod.ObjectNode(mod.PersonRef(self.request))
|
|
@@ -81,6 +81,30 @@ class TestGrid(TestCase):
|
|
|
81
81
|
grid.remove('two', 'three')
|
|
82
82
|
self.assertEqual(grid.columns, ['one', 'four'])
|
|
83
83
|
|
|
84
|
+
def test_set_label(self):
|
|
85
|
+
grid = self.make_grid(columns=['foo', 'bar'])
|
|
86
|
+
self.assertEqual(grid.labels, {})
|
|
87
|
+
|
|
88
|
+
# basic
|
|
89
|
+
grid.set_label('foo', "Foo Fighters")
|
|
90
|
+
self.assertEqual(grid.labels['foo'], "Foo Fighters")
|
|
91
|
+
|
|
92
|
+
# can replace label
|
|
93
|
+
grid.set_label('foo', "Different")
|
|
94
|
+
self.assertEqual(grid.labels['foo'], "Different")
|
|
95
|
+
self.assertEqual(grid.get_label('foo'), "Different")
|
|
96
|
+
|
|
97
|
+
def test_get_label(self):
|
|
98
|
+
grid = self.make_grid(columns=['foo', 'bar'])
|
|
99
|
+
self.assertEqual(grid.labels, {})
|
|
100
|
+
|
|
101
|
+
# default derived from key
|
|
102
|
+
self.assertEqual(grid.get_label('foo'), "Foo")
|
|
103
|
+
|
|
104
|
+
# can override
|
|
105
|
+
grid.set_label('foo', "Different")
|
|
106
|
+
self.assertEqual(grid.get_label('foo'), "Different")
|
|
107
|
+
|
|
84
108
|
def test_set_renderer(self):
|
|
85
109
|
grid = self.make_grid(columns=['foo', 'bar'])
|
|
86
110
|
self.assertEqual(grid.renderers, {})
|
|
@@ -10,6 +10,7 @@ from pyramid.httpexceptions import HTTPNotFound
|
|
|
10
10
|
|
|
11
11
|
from wuttjamaican.conf import WuttaConfig
|
|
12
12
|
from wuttaweb.views import master
|
|
13
|
+
from wuttaweb.views import View
|
|
13
14
|
from wuttaweb.subscribers import new_request_set_user
|
|
14
15
|
from tests.util import WebTestCase
|
|
15
16
|
|
|
@@ -331,6 +332,14 @@ class TestMasterView(WebTestCase):
|
|
|
331
332
|
# support methods
|
|
332
333
|
##############################
|
|
333
334
|
|
|
335
|
+
def test_get_class_hierarchy(self):
|
|
336
|
+
class MyView(master.MasterView):
|
|
337
|
+
pass
|
|
338
|
+
|
|
339
|
+
view = MyView(self.request)
|
|
340
|
+
classes = view.get_class_hierarchy()
|
|
341
|
+
self.assertEqual(classes, [View, master.MasterView, MyView])
|
|
342
|
+
|
|
334
343
|
def test_has_perm(self):
|
|
335
344
|
model = self.app.model
|
|
336
345
|
auth = self.app.get_auth_handler()
|
|
@@ -428,6 +437,36 @@ class TestMasterView(WebTestCase):
|
|
|
428
437
|
self.assertEqual(view.get_index_title(), "Wutta Widgets")
|
|
429
438
|
del master.MasterView.model_title_plural
|
|
430
439
|
|
|
440
|
+
def test_collect_labels(self):
|
|
441
|
+
|
|
442
|
+
# no labels by default
|
|
443
|
+
view = self.make_view()
|
|
444
|
+
labels = view.collect_labels()
|
|
445
|
+
self.assertEqual(labels, {})
|
|
446
|
+
|
|
447
|
+
# labels come from all classes; subclass wins
|
|
448
|
+
with patch.object(View, 'labels', new={'foo': "Foo", 'bar': "Bar"}, create=True):
|
|
449
|
+
with patch.object(master.MasterView, 'labels', new={'foo': "FOO FIGHTERS"}, create=True):
|
|
450
|
+
view = self.make_view()
|
|
451
|
+
labels = view.collect_labels()
|
|
452
|
+
self.assertEqual(labels, {'foo': "FOO FIGHTERS", 'bar': "Bar"})
|
|
453
|
+
|
|
454
|
+
def test_set_labels(self):
|
|
455
|
+
model = self.app.model
|
|
456
|
+
with patch.object(master.MasterView, 'model_class', new=model.Setting, create=True):
|
|
457
|
+
|
|
458
|
+
# no labels by default
|
|
459
|
+
view = self.make_view()
|
|
460
|
+
grid = view.make_model_grid(session=self.session)
|
|
461
|
+
view.set_labels(grid)
|
|
462
|
+
self.assertEqual(grid.labels, {})
|
|
463
|
+
|
|
464
|
+
# labels come from all classes; subclass wins
|
|
465
|
+
with patch.object(master.MasterView, 'labels', new={'name': "SETTING NAME"}, create=True):
|
|
466
|
+
view = self.make_view()
|
|
467
|
+
view.set_labels(grid)
|
|
468
|
+
self.assertEqual(grid.labels, {'name': "SETTING NAME"})
|
|
469
|
+
|
|
431
470
|
def test_make_model_grid(self):
|
|
432
471
|
model = self.app.model
|
|
433
472
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|