WuttaWeb 0.11.0__tar.gz → 0.12.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.
Files changed (154) hide show
  1. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/CHANGELOG.md +24 -0
  2. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/PKG-INFO +2 -2
  3. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/pyproject.toml +2 -2
  4. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/forms/base.py +19 -0
  5. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/forms/schema.py +25 -0
  6. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/forms/widgets.py +85 -1
  7. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/grids/base.py +669 -5
  8. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/appinfo/configure.mako +54 -5
  9. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/appinfo/index.mako +44 -0
  10. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/auth/login.mako +1 -8
  11. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/base.mako +75 -32
  12. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/base_meta.mako +3 -3
  13. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/configure.mako +27 -5
  14. wuttaweb-0.12.0/src/wuttaweb/templates/deform/readonly/objectref.pt +9 -0
  15. wuttaweb-0.12.0/src/wuttaweb/templates/deform/readonly/rolerefs.pt +7 -0
  16. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/form.mako +4 -4
  17. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/forms/vue_template.mako +14 -1
  18. wuttaweb-0.12.0/src/wuttaweb/templates/grids/table_element.mako +49 -0
  19. wuttaweb-0.12.0/src/wuttaweb/templates/grids/vue_template.mako +685 -0
  20. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/home.mako +1 -8
  21. wuttaweb-0.12.0/src/wuttaweb/templates/wutta-components.mako +167 -0
  22. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/common.py +5 -0
  23. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/master.py +133 -3
  24. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/people.py +29 -8
  25. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/roles.py +12 -1
  26. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/settings.py +103 -40
  27. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/users.py +4 -0
  28. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/forms/test_base.py +24 -0
  29. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/forms/test_schema.py +14 -0
  30. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/forms/test_widgets.py +59 -2
  31. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/grids/test_base.py +521 -16
  32. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_common.py +11 -0
  33. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_master.py +253 -204
  34. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_people.py +44 -5
  35. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_settings.py +21 -0
  36. wuttaweb-0.11.0/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -1
  37. wuttaweb-0.11.0/src/wuttaweb/templates/grids/vue_template.mako +0 -349
  38. wuttaweb-0.11.0/src/wuttaweb/templates/wutta-components.mako +0 -71
  39. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/.gitignore +0 -0
  40. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/COPYING.txt +0 -0
  41. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/README.md +0 -0
  42. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/Makefile +0 -0
  43. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/_static/.keepme +0 -0
  44. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/index.rst +0 -0
  45. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/app.rst +0 -0
  46. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/auth.rst +0 -0
  47. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/db.rst +0 -0
  48. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/forms.base.rst +0 -0
  49. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/forms.rst +0 -0
  50. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/forms.schema.rst +0 -0
  51. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/forms.widgets.rst +0 -0
  52. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/grids.base.rst +0 -0
  53. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/grids.rst +0 -0
  54. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/handler.rst +0 -0
  55. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/helpers.rst +0 -0
  56. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/index.rst +0 -0
  57. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/menus.rst +0 -0
  58. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/static.rst +0 -0
  59. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/subscribers.rst +0 -0
  60. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/util.rst +0 -0
  61. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.auth.rst +0 -0
  62. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.base.rst +0 -0
  63. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.common.rst +0 -0
  64. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.essential.rst +0 -0
  65. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.master.rst +0 -0
  66. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.people.rst +0 -0
  67. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.roles.rst +0 -0
  68. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.rst +0 -0
  69. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.settings.rst +0 -0
  70. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/api/wuttaweb/views.users.rst +0 -0
  71. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/conf.py +0 -0
  72. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/glossary.rst +0 -0
  73. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/index.rst +0 -0
  74. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/make.bat +0 -0
  75. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/narr/index.rst +0 -0
  76. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/narr/templates/base.rst +0 -0
  77. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/narr/templates/index.rst +0 -0
  78. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/narr/templates/lookup.rst +0 -0
  79. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/docs/narr/templates/overview.rst +0 -0
  80. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/__init__.py +0 -0
  81. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/_version.py +0 -0
  82. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/app.py +0 -0
  83. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/auth.py +0 -0
  84. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/db.py +0 -0
  85. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/forms/__init__.py +0 -0
  86. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/grids/__init__.py +0 -0
  87. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/handler.py +0 -0
  88. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/helpers.py +0 -0
  89. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/menus.py +0 -0
  90. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/static/__init__.py +0 -0
  91. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/static/img/favicon.ico +0 -0
  92. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/static/img/logo.png +0 -0
  93. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/static/img/testing.png +0 -0
  94. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/subscribers.py +0 -0
  95. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  96. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  97. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  98. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  99. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/password.pt +0 -0
  100. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  101. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  102. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  103. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/select.pt +0 -0
  104. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  105. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  106. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/forbidden.mako +0 -0
  107. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/configure.mako +0 -0
  108. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/create.mako +0 -0
  109. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/delete.mako +0 -0
  110. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/edit.mako +0 -0
  111. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/form.mako +0 -0
  112. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/index.mako +0 -0
  113. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/master/view.mako +0 -0
  114. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/notfound.mako +0 -0
  115. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/page.mako +0 -0
  116. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  117. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/templates/setup.mako +0 -0
  118. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/util.py +0 -0
  119. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/__init__.py +0 -0
  120. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/auth.py +0 -0
  121. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/base.py +0 -0
  122. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/src/wuttaweb/views/essential.py +0 -0
  123. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tasks.py +0 -0
  124. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/__init__.py +0 -0
  125. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/forms/__init__.py +0 -0
  126. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/grids/__init__.py +0 -0
  127. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  128. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  129. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_oruga.js +0 -0
  130. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_oruga_bulma.css +0 -0
  131. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_oruga_bulma.js +0 -0
  132. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_vue.js +0 -0
  133. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
  134. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/buefy.css +0 -0
  135. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/buefy.js +0 -0
  136. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/fontawesome.js +0 -0
  137. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/vue.js +0 -0
  138. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/libcache/vue_resource.js +0 -0
  139. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_app.py +0 -0
  140. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_auth.py +0 -0
  141. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_handler.py +0 -0
  142. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_helpers.py +0 -0
  143. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_menus.py +0 -0
  144. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_static.py +0 -0
  145. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_subscribers.py +0 -0
  146. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/test_util.py +0 -0
  147. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/util.py +0 -0
  148. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/__init__.py +0 -0
  149. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test___init__.py +0 -0
  150. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_auth.py +0 -0
  151. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_base.py +0 -0
  152. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_roles.py +0 -0
  153. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tests/views/test_users.py +0 -0
  154. {wuttaweb-0.11.0 → wuttaweb-0.12.0}/tox.ini +0 -0
@@ -5,6 +5,30 @@ 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.12.0 (2024-08-22)
9
+
10
+ ### Feat
11
+
12
+ - add "copy link" button for sharing a grid view
13
+ - add initial support for proper grid filters
14
+ - add initial filtering logic to grid class
15
+ - add "searchable" column support for grids
16
+ - improve page linkage between role/user/person
17
+ - add basic autocomplete support, for Person
18
+
19
+ ### Fix
20
+
21
+ - cleanup templates for home, login pages
22
+ - cleanup logic for appinfo/configure
23
+ - expose settings for app node title, type
24
+ - show installed python packages on appinfo page
25
+ - tweak login form to stop extending size of background card
26
+ - add setting to auto-redirect anon users to login, from home page
27
+ - add form padding, validators for /configure pages
28
+ - add padding around main form, via wrapper css
29
+ - show CRUD buttons in header only if relevant and user has access
30
+ - tweak style config for home link app title in main menu
31
+
8
32
  ## v0.11.0 (2024-08-20)
9
33
 
10
34
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: WuttaWeb
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -35,7 +35,7 @@ Requires-Dist: pyramid-tm
35
35
  Requires-Dist: pyramid>=2
36
36
  Requires-Dist: waitress
37
37
  Requires-Dist: webhelpers2
38
- Requires-Dist: wuttjamaican[db]>=0.12.0
38
+ Requires-Dist: wuttjamaican[db]>=0.12.1
39
39
  Requires-Dist: zope-sqlalchemy>=1.5
40
40
  Provides-Extra: docs
41
41
  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.11.0"
9
+ version = "0.12.0"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
@@ -41,7 +41,7 @@ dependencies = [
41
41
  "pyramid_tm",
42
42
  "waitress",
43
43
  "WebHelpers2",
44
- "WuttJamaican[db]>=0.12.0",
44
+ "WuttJamaican[db]>=0.12.1",
45
45
  "zope.sqlalchemy>=1.5",
46
46
  ]
47
47
 
@@ -25,6 +25,7 @@ Base form classes
25
25
  """
26
26
 
27
27
  import logging
28
+ from collections import OrderedDict
28
29
 
29
30
  import colander
30
31
  import deform
@@ -311,6 +312,9 @@ class Form:
311
312
 
312
313
  self.set_fields(fields or self.get_fields())
313
314
 
315
+ # nb. this tracks grid JSON data for inclusion in page template
316
+ self.grid_vue_data = OrderedDict()
317
+
314
318
  def __contains__(self, name):
315
319
  """
316
320
  Custom logic for the ``in`` operator, to allow easily checking
@@ -750,6 +754,10 @@ class Form:
750
754
  kwargs['appstruct'] = self.model_instance
751
755
 
752
756
  form = deform.Form(schema, **kwargs)
757
+ # nb. must give a reference back to wutta form; this is
758
+ # for sake of field schema nodes and widgets, e.g. to
759
+ # access the main model instance
760
+ form.wutta_form = self
753
761
  self.deform_form = form
754
762
 
755
763
  return self.deform_form
@@ -818,6 +826,17 @@ class Form:
818
826
  output = render(template, context)
819
827
  return HTML.literal(output)
820
828
 
829
+ def add_grid_vue_data(self, grid):
830
+ """ """
831
+ if not grid.key:
832
+ raise ValueError("grid must have a key!")
833
+
834
+ if grid.key in self.grid_vue_data:
835
+ log.warning("grid data with key '%s' already registered, "
836
+ "but will be replaced", grid.key)
837
+
838
+ self.grid_vue_data[grid.key] = grid.get_vue_data()
839
+
821
840
  def render_vue_field(
822
841
  self,
823
842
  fieldname,
@@ -246,6 +246,9 @@ class ObjectRef(colander.SchemaType):
246
246
  values.insert(0, self.empty_option)
247
247
  kwargs['values'] = values
248
248
 
249
+ if 'url' not in kwargs:
250
+ kwargs['url'] = lambda person: self.request.route_url('people.view', uuid=person.uuid)
251
+
249
252
  return widgets.ObjectRefWidget(self.request, **kwargs)
250
253
 
251
254
 
@@ -321,6 +324,28 @@ class RoleRefs(WuttaSet):
321
324
  return widgets.RoleRefsWidget(self.request, **kwargs)
322
325
 
323
326
 
327
+ class UserRefs(WuttaSet):
328
+ """
329
+ Form schema type for the Role
330
+ :attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users`
331
+ association proxy field.
332
+
333
+ This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
334
+ :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` ``uuid``
335
+ values for underlying data format.
336
+ """
337
+
338
+ def widget_maker(self, **kwargs):
339
+ """
340
+ Constructs a default widget for the field.
341
+
342
+ :returns: Instance of
343
+ :class:`~wuttaweb.forms.widgets.UserRefsWidget`.
344
+ """
345
+ kwargs.setdefault('session', self.session)
346
+ return widgets.UserRefsWidget(self.request, **kwargs)
347
+
348
+
324
349
  class Permissions(WuttaSet):
325
350
  """
326
351
  Form schema type for the Role
@@ -44,6 +44,7 @@ from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
44
44
  from webhelpers2.html import HTML
45
45
 
46
46
  from wuttaweb.db import Session
47
+ from wuttaweb.grids import Grid
47
48
 
48
49
 
49
50
  class ObjectRefWidget(SelectWidget):
@@ -83,9 +84,19 @@ class ObjectRefWidget(SelectWidget):
83
84
  """
84
85
  readonly_template = 'readonly/objectref'
85
86
 
86
- def __init__(self, request, *args, **kwargs):
87
+ def __init__(self, request, url=None, *args, **kwargs):
87
88
  super().__init__(*args, **kwargs)
88
89
  self.request = request
90
+ self.url = url
91
+
92
+ def get_template_values(self, field, cstruct, kw):
93
+ """ """
94
+ values = super().get_template_values(field, cstruct, kw)
95
+
96
+ if 'url' not in values and self.url and field.schema.model_instance:
97
+ values['url'] = self.url(field.schema.model_instance)
98
+
99
+ return values
89
100
 
90
101
 
91
102
  class NotesWidget(TextAreaWidget):
@@ -137,12 +148,17 @@ class RoleRefsWidget(WuttaCheckboxChoiceWidget):
137
148
  """
138
149
  Widget for use with User
139
150
  :attr:`~wuttjamaican:wuttjamaican.db.model.auth.User.roles` field.
151
+ This is the default widget for the
152
+ :class:`~wuttaweb.forms.schema.RoleRefs` type.
140
153
 
141
154
  This is a subclass of :class:`WuttaCheckboxChoiceWidget`.
142
155
  """
156
+ readonly_template = 'readonly/rolerefs'
143
157
 
144
158
  def serialize(self, field, cstruct, **kw):
145
159
  """ """
160
+ model = self.app.model
161
+
146
162
  # special logic when field is editable
147
163
  readonly = kw.get('readonly', self.readonly)
148
164
  if not readonly:
@@ -159,10 +175,78 @@ class RoleRefsWidget(WuttaCheckboxChoiceWidget):
159
175
  if val[0] != admin.uuid]
160
176
  kw['values'] = values
161
177
 
178
+ else: # readonly
179
+
180
+ # roles
181
+ roles = []
182
+ if cstruct:
183
+ for uuid in cstruct:
184
+ role = self.session.query(model.Role).get(uuid)
185
+ if role:
186
+ roles.append(role)
187
+ kw['roles'] = roles
188
+
189
+ # url
190
+ url = lambda role: self.request.route_url('roles.view', uuid=role.uuid)
191
+ kw['url'] = url
192
+
162
193
  # default logic from here
163
194
  return super().serialize(field, cstruct, **kw)
164
195
 
165
196
 
197
+ class UserRefsWidget(WuttaCheckboxChoiceWidget):
198
+ """
199
+ Widget for use with Role
200
+ :attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users` field.
201
+ This is the default widget for the
202
+ :class:`~wuttaweb.forms.schema.UserRefs` type.
203
+
204
+ This is a subclass of :class:`WuttaCheckboxChoiceWidget`; however
205
+ it only supports readonly mode and does not use a template.
206
+ Rather, it generates and renders a
207
+ :class:`~wuttaweb.grids.base.Grid` showing the users list.
208
+ """
209
+
210
+ def serialize(self, field, cstruct, **kw):
211
+ """ """
212
+ readonly = kw.get('readonly', self.readonly)
213
+ if not readonly:
214
+ raise NotImplementedError("edit not allowed for this widget")
215
+
216
+ model = self.app.model
217
+ columns = ['person', 'username', 'active']
218
+
219
+ # generate data set for users
220
+ users = []
221
+ if cstruct:
222
+ for uuid in cstruct:
223
+ user = self.session.query(model.User).get(uuid)
224
+ if user:
225
+ users.append(dict([(key, getattr(user, key))
226
+ for key in columns + ['uuid']]))
227
+
228
+ # grid
229
+ grid = Grid(self.request, key='roles.view.users',
230
+ columns=columns, data=users)
231
+
232
+ # view action
233
+ if self.request.has_perm('users.view'):
234
+ url = lambda user, i: self.request.route_url('users.view', uuid=user['uuid'])
235
+ grid.add_action('view', icon='eye', url=url)
236
+ grid.set_link('person')
237
+ grid.set_link('username')
238
+
239
+ # edit action
240
+ if self.request.has_perm('users.edit'):
241
+ url = lambda user, i: self.request.route_url('users.edit', uuid=user['uuid'])
242
+ grid.add_action('edit', url=url)
243
+
244
+ # render as simple <b-table>
245
+ # nb. must indicate we are a part of this form
246
+ form = getattr(field.parent, 'wutta_form', None)
247
+ return grid.render_table_element(form)
248
+
249
+
166
250
  class PermissionsWidget(WuttaCheckboxChoiceWidget):
167
251
  """
168
252
  Widget for use with Role