WuttaWeb 0.20.5__tar.gz → 0.21.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 (206) hide show
  1. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/CHANGELOG.md +22 -0
  2. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/PKG-INFO +2 -2
  3. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/pyproject.toml +2 -2
  4. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/cli/webapp.py +1 -0
  5. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/forms/schema.py +0 -22
  6. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/forms/widgets.py +0 -58
  7. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/grids/base.py +34 -7
  8. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/menus.py +3 -1
  9. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/base.mako +10 -6
  10. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/common.py +13 -0
  11. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/people.py +54 -13
  12. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/roles.py +35 -4
  13. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/users.py +52 -16
  14. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/cli/test_webapp.py +4 -2
  15. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/forms/test_schema.py +0 -14
  16. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/forms/test_widgets.py +1 -46
  17. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/grids/test_base.py +21 -0
  18. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_people.py +49 -15
  19. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_roles.py +22 -0
  20. wuttaweb-0.21.0/tests/views/test_users.py +319 -0
  21. wuttaweb-0.20.5/tests/views/test_users.py +0 -219
  22. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/.gitignore +0 -0
  23. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/COPYING.txt +0 -0
  24. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/README.md +0 -0
  25. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/Makefile +0 -0
  26. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/_static/.keepme +0 -0
  27. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.app.rst +0 -0
  28. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.auth.rst +0 -0
  29. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.cli.rst +0 -0
  30. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  31. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.conf.rst +0 -0
  32. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
  33. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.db.rst +0 -0
  34. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.db.sess.rst +0 -0
  35. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.emails.rst +0 -0
  36. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.forms.base.rst +0 -0
  37. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.forms.rst +0 -0
  38. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
  39. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  40. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.grids.base.rst +0 -0
  41. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
  42. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.grids.rst +0 -0
  43. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.handler.rst +0 -0
  44. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.helpers.rst +0 -0
  45. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.menus.rst +0 -0
  46. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.progress.rst +0 -0
  47. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.rst +0 -0
  48. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.static.rst +0 -0
  49. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.subscribers.rst +0 -0
  50. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.util.rst +0 -0
  51. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.auth.rst +0 -0
  52. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.base.rst +0 -0
  53. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.batch.rst +0 -0
  54. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.common.rst +0 -0
  55. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.email.rst +0 -0
  56. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.essential.rst +0 -0
  57. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.master.rst +0 -0
  58. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.people.rst +0 -0
  59. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.progress.rst +0 -0
  60. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.reports.rst +0 -0
  61. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.roles.rst +0 -0
  62. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.rst +0 -0
  63. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.settings.rst +0 -0
  64. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  65. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/api/wuttaweb.views.users.rst +0 -0
  66. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/conf.py +0 -0
  67. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/glossary.rst +0 -0
  68. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/index.rst +0 -0
  69. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/make.bat +0 -0
  70. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/cli/builtin.rst +0 -0
  71. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/cli/index.rst +0 -0
  72. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/templates/base.rst +0 -0
  73. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/templates/index.rst +0 -0
  74. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/templates/lookup.rst +0 -0
  75. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/docs/narr/templates/overview.rst +0 -0
  76. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/__init__.py +0 -0
  77. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/_version.py +0 -0
  78. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/app.py +0 -0
  79. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/auth.py +0 -0
  80. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/cli/__init__.py +0 -0
  81. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/conf.py +0 -0
  82. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/db/__init__.py +0 -0
  83. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/db/continuum.py +0 -0
  84. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/db/sess.py +0 -0
  85. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  86. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  87. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/emails.py +0 -0
  88. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/forms/__init__.py +0 -0
  89. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/forms/base.py +0 -0
  90. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/grids/__init__.py +0 -0
  91. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/grids/filters.py +0 -0
  92. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/handler.py +0 -0
  93. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/helpers.py +0 -0
  94. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/progress.py +0 -0
  95. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/static/__init__.py +0 -0
  96. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/static/img/favicon.ico +0 -0
  97. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/static/img/logo.png +0 -0
  98. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/static/img/testing.png +0 -0
  99. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/subscribers.py +0 -0
  100. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  101. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  102. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  103. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/auth/login.mako +0 -0
  104. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/base_meta.mako +0 -0
  105. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/batch/view.mako +0 -0
  106. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/configure.mako +0 -0
  107. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  108. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  109. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  110. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  111. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  112. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  113. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/password.pt +0 -0
  114. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  115. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  116. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  117. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  118. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  119. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  120. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  121. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  122. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/select.pt +0 -0
  123. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  124. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  125. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  126. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/forbidden.mako +0 -0
  127. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/form.mako +0 -0
  128. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  129. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  130. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  131. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/home.mako +0 -0
  132. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/configure.mako +0 -0
  133. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/create.mako +0 -0
  134. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/delete.mako +0 -0
  135. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/edit.mako +0 -0
  136. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/form.mako +0 -0
  137. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/index.mako +0 -0
  138. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/master/view.mako +0 -0
  139. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/notfound.mako +0 -0
  140. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/page.mako +0 -0
  141. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  142. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/progress.mako +0 -0
  143. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/reports/view.mako +0 -0
  144. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/setup.mako +0 -0
  145. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/upgrade.mako +0 -0
  146. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  147. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  148. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
  149. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/testing.py +0 -0
  150. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/util.py +0 -0
  151. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/__init__.py +0 -0
  152. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/auth.py +0 -0
  153. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/base.py +0 -0
  154. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/batch.py +0 -0
  155. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/email.py +0 -0
  156. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/essential.py +0 -0
  157. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/master.py +0 -0
  158. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/progress.py +0 -0
  159. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/reports.py +0 -0
  160. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/settings.py +0 -0
  161. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/src/wuttaweb/views/upgrades.py +0 -0
  162. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tasks.py +0 -0
  163. {wuttaweb-0.20.5/tests/views → wuttaweb-0.21.0/tests}/__init__.py +0 -0
  164. {wuttaweb-0.20.5/tests/grids → wuttaweb-0.21.0/tests/cli}/__init__.py +0 -0
  165. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/db/__init__.py +0 -0
  166. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/db/test_continuum.py +0 -0
  167. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/forms/test_base.py +0 -0
  168. {wuttaweb-0.20.5/tests/cli → wuttaweb-0.21.0/tests/grids}/__init__.py +0 -0
  169. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/grids/test_filters.py +0 -0
  170. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  171. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  172. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_oruga.js +0 -0
  173. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_oruga_bulma.css +0 -0
  174. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_oruga_bulma.js +0 -0
  175. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_vue.js +0 -0
  176. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
  177. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/buefy.css +0 -0
  178. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/buefy.js +0 -0
  179. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/fontawesome.js +0 -0
  180. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/vue.js +0 -0
  181. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/libcache/vue_resource.js +0 -0
  182. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_app.py +0 -0
  183. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_auth.py +0 -0
  184. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_emails.py +0 -0
  185. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_handler.py +0 -0
  186. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_helpers.py +0 -0
  187. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_menus.py +0 -0
  188. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_progress.py +0 -0
  189. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_static.py +0 -0
  190. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_subscribers.py +0 -0
  191. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/test_util.py +0 -0
  192. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/util.py +0 -0
  193. {wuttaweb-0.20.5/tests → wuttaweb-0.21.0/tests/views}/__init__.py +0 -0
  194. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test___init__.py +0 -0
  195. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_auth.py +0 -0
  196. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_base.py +0 -0
  197. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_batch.py +0 -0
  198. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_common.py +0 -0
  199. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_email.py +0 -0
  200. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_essential.py +0 -0
  201. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_master.py +0 -0
  202. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_progress.py +0 -0
  203. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_reports.py +0 -0
  204. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_settings.py +0 -0
  205. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tests/views/test_upgrades.py +0 -0
  206. {wuttaweb-0.20.5 → wuttaweb-0.21.0}/tox.ini +0 -0
@@ -5,6 +5,28 @@ 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.21.0 (2025-02-01)
9
+
10
+ ### Feat
11
+
12
+ - overhaul some User/Person form fields etc.
13
+
14
+ ### Fix
15
+
16
+ - do not auto-create grid filters for uuid columns
17
+
18
+ ## v0.20.6 (2025-01-26)
19
+
20
+ ### Fix
21
+
22
+ - add `setup_enhance_admin_user()` method for initial setup
23
+ - add `render_percent()` method for Grid
24
+ - allow override for Admin menu title
25
+ - add `index_title_controls()` def block for base template
26
+ - add `make_users_grid()` method for RoleView
27
+ - fallback to empty string for uvicorn `root_path`
28
+ - add `root_path` config setting for running webapp via uvicorn
29
+
8
30
  ## v0.20.5 (2025-01-23)
9
31
 
10
32
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.20.5
3
+ Version: 0.21.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
@@ -39,7 +39,7 @@ Requires-Dist: pyramid-tm
39
39
  Requires-Dist: pyramid>=2
40
40
  Requires-Dist: waitress
41
41
  Requires-Dist: webhelpers2
42
- Requires-Dist: wuttjamaican[db]>=0.20.2
42
+ Requires-Dist: wuttjamaican[db]>=0.20.4
43
43
  Requires-Dist: zope-sqlalchemy>=1.5
44
44
  Provides-Extra: continuum
45
45
  Requires-Dist: wutta-continuum; extra == 'continuum'
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "WuttaWeb"
9
- version = "0.20.5"
9
+ version = "0.21.0"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -44,7 +44,7 @@ dependencies = [
44
44
  "pyramid_tm",
45
45
  "waitress",
46
46
  "WebHelpers2",
47
- "WuttJamaican[db]>=0.20.2",
47
+ "WuttJamaican[db]>=0.20.4",
48
48
  "zope.sqlalchemy>=1.5",
49
49
  ]
50
50
 
@@ -74,6 +74,7 @@ def webapp(
74
74
  'reload_dirs': config.get_list(f'{config.appname}.web.app.reload_dirs'),
75
75
  'factory': config.get_bool(f'{config.appname}.web.app.factory', default=False),
76
76
  'interface': config.get(f'{config.appname}.web.app.interface', default='auto'),
77
+ 'root_path': config.get(f'{config.appname}.web.app.root_path', default=''),
77
78
  }
78
79
 
79
80
  # also must inject our config files to env, since there is no
@@ -572,28 +572,6 @@ class RoleRefs(WuttaSet):
572
572
  return widgets.RoleRefsWidget(self.request, **kwargs)
573
573
 
574
574
 
575
- class UserRefs(WuttaSet):
576
- """
577
- Form schema type for the Role
578
- :attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users`
579
- association proxy field.
580
-
581
- This is a subclass of :class:`WuttaSet`. It uses a ``set`` of
582
- :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` ``uuid``
583
- values for underlying data format.
584
- """
585
-
586
- def widget_maker(self, **kwargs):
587
- """
588
- Constructs a default widget for the field.
589
-
590
- :returns: Instance of
591
- :class:`~wuttaweb.forms.widgets.UserRefsWidget`.
592
- """
593
- kwargs.setdefault('session', Session())
594
- return widgets.UserRefsWidget(self.request, **kwargs)
595
-
596
-
597
575
  class Permissions(WuttaSet):
598
576
  """
599
577
  Form schema type for the Role
@@ -56,7 +56,6 @@ from webhelpers2.html import HTML
56
56
  from wuttjamaican.conf import parse_list
57
57
 
58
58
  from wuttaweb.db import Session
59
- from wuttaweb.grids import Grid
60
59
 
61
60
 
62
61
  class ObjectRefWidget(SelectWidget):
@@ -414,63 +413,6 @@ class RoleRefsWidget(WuttaCheckboxChoiceWidget):
414
413
  return super().serialize(field, cstruct, **kw)
415
414
 
416
415
 
417
- class UserRefsWidget(WuttaCheckboxChoiceWidget):
418
- """
419
- Widget for use with Role
420
- :attr:`~wuttjamaican:wuttjamaican.db.model.auth.Role.users` field.
421
- This is the default widget for the
422
- :class:`~wuttaweb.forms.schema.UserRefs` type.
423
-
424
- This is a subclass of :class:`WuttaCheckboxChoiceWidget`; however
425
- it only supports readonly mode and does not use a template.
426
- Rather, it generates and renders a
427
- :class:`~wuttaweb.grids.base.Grid` showing the users list.
428
- """
429
-
430
- def serialize(self, field, cstruct, **kw):
431
- """ """
432
- readonly = kw.get('readonly', self.readonly)
433
- if not readonly:
434
- raise NotImplementedError("edit not allowed for this widget")
435
-
436
- model = self.app.model
437
- columns = ['username', 'active']
438
-
439
- # generate data set for users
440
- users = []
441
- if cstruct:
442
- for uuid in cstruct:
443
- user = self.session.get(model.User, uuid)
444
- if user:
445
- users.append(dict([(key, getattr(user, key))
446
- for key in columns + ['uuid']]))
447
-
448
- # do not render if no data
449
- if not users:
450
- return HTML.tag('span')
451
-
452
- # grid
453
- grid = Grid(self.request, key='roles.view.users',
454
- columns=columns, data=users)
455
-
456
- # view action
457
- if self.request.has_perm('users.view'):
458
- url = lambda user, i: self.request.route_url('users.view', uuid=user['uuid'])
459
- grid.add_action('view', icon='eye', url=url)
460
- grid.set_link('person')
461
- grid.set_link('username')
462
-
463
- # edit action
464
- if self.request.has_perm('users.edit'):
465
- url = lambda user, i: self.request.route_url('users.edit', uuid=user['uuid'])
466
- grid.add_action('edit', url=url)
467
-
468
- # render as simple <b-table>
469
- # nb. must indicate we are a part of this form
470
- form = getattr(field.parent, 'wutta_form', None)
471
- return grid.render_table_element(form)
472
-
473
-
474
416
  class PermissionsWidget(WuttaCheckboxChoiceWidget):
475
417
  """
476
418
  Widget for use with Role
@@ -41,6 +41,7 @@ from webhelpers2.html import HTML
41
41
  from wuttaweb.db import Session
42
42
  from wuttaweb.util import FieldList, get_model_fields, make_json_safe
43
43
  from wuttjamaican.util import UNSPECIFIED
44
+ from wuttjamaican.db.util import UUID
44
45
  from wuttaweb.grids.filters import default_sqlalchemy_filters, VerbNotSupported
45
46
 
46
47
 
@@ -612,6 +613,7 @@ class Grid:
612
613
  * ``'date'`` -> :meth:`render_date()`
613
614
  * ``'datetime'`` -> :meth:`render_datetime()`
614
615
  * ``'quantity'`` -> :meth:`render_quantity()`
616
+ * ``'percent'`` -> :meth:`render_percent()`
615
617
 
616
618
  Renderer overrides are tracked via :attr:`renderers`.
617
619
  """
@@ -622,6 +624,7 @@ class Grid:
622
624
  'date': self.render_date,
623
625
  'datetime': self.render_datetime,
624
626
  'quantity': self.render_quantity,
627
+ 'percent': self.render_percent,
625
628
  }
626
629
 
627
630
  if renderer in builtins:
@@ -1134,14 +1137,15 @@ class Grid:
1134
1137
 
1135
1138
  def make_backend_filters(self, filters=None):
1136
1139
  """
1137
- Make backend filters for all columns in the grid.
1140
+ Make "automatic" backend filters for the grid.
1138
1141
 
1139
1142
  This is called by the constructor, if :attr:`filterable` is
1140
1143
  true.
1141
1144
 
1142
- For each column in the grid, this checks the provided
1143
- ``filters`` and if the column is not yet in there, will call
1144
- :meth:`make_filter()` to add it.
1145
+ For each "column" in the model class, this will call
1146
+ :meth:`make_filter()` to add an automatic filter. However it
1147
+ first checks the provided ``filters`` and will not override
1148
+ any of those.
1145
1149
 
1146
1150
  .. note::
1147
1151
 
@@ -1179,9 +1183,18 @@ class Grid:
1179
1183
 
1180
1184
  inspector = sa.inspect(self.model_class)
1181
1185
  for prop in inspector.column_attrs:
1182
- if prop.key not in filters:
1183
- attr = getattr(self.model_class, prop.key)
1184
- filters[prop.key] = self.make_filter(attr)
1186
+
1187
+ # do not overwrite existing filters
1188
+ if prop.key in filters:
1189
+ continue
1190
+
1191
+ # do not create filter for UUID field
1192
+ if (len(prop.columns) == 1
1193
+ and isinstance(prop.columns[0].type, UUID)):
1194
+ continue
1195
+
1196
+ attr = getattr(self.model_class, prop.key)
1197
+ filters[prop.key] = self.make_filter(attr)
1185
1198
 
1186
1199
  return filters
1187
1200
 
@@ -1884,6 +1897,20 @@ class Grid:
1884
1897
  dt = getattr(obj, key)
1885
1898
  return self.app.render_datetime(dt)
1886
1899
 
1900
+ def render_percent(self, obj, key, value, **kwargs):
1901
+ """
1902
+ Column renderer for percentage values.
1903
+
1904
+ This calls
1905
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_percent()`
1906
+ for the return value.
1907
+
1908
+ This is not used automatically but you can use it explicitly::
1909
+
1910
+ grid.set_renderer('foo', 'percent')
1911
+ """
1912
+ return self.app.render_percent(value, **kwargs)
1913
+
1887
1914
  def render_quantity(self, obj, key, value):
1888
1915
  """
1889
1916
  Column renderer for quantity values.
@@ -143,6 +143,8 @@ class MenuHandler(GenericHandler):
143
143
  which will ultimately be one element of the final list of
144
144
  dicts as described in :class:`MenuHandler`.
145
145
 
146
+ :param title: Override the menu title; default is "Admin".
147
+
146
148
  :param include_people: You can pass this flag to indicate the
147
149
  admin menu should contain an entry for the "People" view.
148
150
  """
@@ -198,7 +200,7 @@ class MenuHandler(GenericHandler):
198
200
  ])
199
201
 
200
202
  return {
201
- 'title': "Admin",
203
+ 'title': kwargs.get('title', "Admin"),
202
204
  'type': 'menu',
203
205
  'items': items,
204
206
  }
@@ -248,12 +248,7 @@
248
248
  % else:
249
249
  <h1 class="title">${index_title}</h1>
250
250
  % endif
251
- % if master and master.creatable and not master.creating and master.has_perm('create'):
252
- <wutta-button once type="is-primary"
253
- tag="a" href="${url(f'{route_prefix}.create')}"
254
- icon-left="plus"
255
- label="Create New" />
256
- % endif
251
+ ${self.index_title_controls()}
257
252
  % endif
258
253
  </div>
259
254
  </div>
@@ -349,6 +344,15 @@
349
344
  </footer>
350
345
  </%def>
351
346
 
347
+ <%def name="index_title_controls()">
348
+ % if master and master.creatable and not master.creating and master.has_perm('create'):
349
+ <wutta-button once type="is-primary"
350
+ tag="a" href="${url(f'{route_prefix}.create')}"
351
+ icon-left="plus"
352
+ label="Create New" />
353
+ % endif
354
+ </%def>
355
+
352
356
  <%def name="render_vue_template_whole_page()">
353
357
  <script type="text/x-template" id="whole-page-template">
354
358
 
@@ -245,6 +245,8 @@ class CommonView(View):
245
245
  session.add(person)
246
246
  user.person = person
247
247
 
248
+ self.setup_enhance_admin_user(user)
249
+
248
250
  # send user to /login
249
251
  self.request.session.flash("Account created! Please login below.")
250
252
  return self.redirect(self.request.route_url('login'))
@@ -254,6 +256,17 @@ class CommonView(View):
254
256
  'form': form,
255
257
  }
256
258
 
259
+ def setup_enhance_admin_user(self, user):
260
+ """
261
+ Further "enhance" the initial admin user when it is first created.
262
+
263
+ This does nothing by default; subclass can override if needed.
264
+
265
+ :param user: New admin
266
+ :class:`~wuttjamaican:wuttjamaican.db.model.auth.User`
267
+ which was just created as part of initial setup.
268
+ """
269
+
257
270
  @classmethod
258
271
  def defaults(cls, config):
259
272
  cls._defaults(config)
@@ -28,7 +28,6 @@ import sqlalchemy as sa
28
28
 
29
29
  from wuttjamaican.db.model import Person
30
30
  from wuttaweb.views import MasterView
31
- from wuttaweb.forms.schema import UserRefs
32
31
 
33
32
 
34
33
  class PersonView(MasterView):
@@ -62,6 +61,14 @@ class PersonView(MasterView):
62
61
  'full_name': {'active': True},
63
62
  }
64
63
 
64
+ form_fields = [
65
+ 'full_name',
66
+ 'first_name',
67
+ 'middle_name',
68
+ 'last_name',
69
+ 'users',
70
+ ]
71
+
65
72
  def configure_grid(self, g):
66
73
  """ """
67
74
  super().configure_grid(g)
@@ -80,20 +87,54 @@ class PersonView(MasterView):
80
87
  super().configure_form(f)
81
88
  person = f.model_instance
82
89
 
83
- # TODO: master should handle these? (nullable column)
84
- f.set_required('first_name', False)
85
- f.set_required('middle_name', False)
86
- f.set_required('last_name', False)
90
+ # full_name
91
+ if self.creating or self.editing:
92
+ f.remove('full_name')
87
93
 
88
94
  # users
89
- # nb. colanderalchemy wants to do some magic for the true
90
- # 'users' relationship, so we use a different field name
91
- f.remove('users')
92
- if not (self.creating or self.editing):
93
- f.append('_users')
94
- f.set_readonly('_users')
95
- f.set_node('_users', UserRefs(self.request))
96
- f.set_default('_users', [u.uuid for u in person.users])
95
+ if self.viewing:
96
+ f.set_grid('users', self.make_users_grid(person))
97
+
98
+ def make_users_grid(self, person):
99
+ """
100
+ Make and return the grid for the Users field.
101
+
102
+ This grid is shown for the Users field when viewing a Person.
103
+
104
+ :returns: Fully configured :class:`~wuttaweb.grids.base.Grid`
105
+ instance.
106
+ """
107
+ model = self.app.model
108
+ route_prefix = self.get_route_prefix()
109
+
110
+ grid = self.make_grid(key=f'{route_prefix}.view.users',
111
+ model_class=model.User,
112
+ data=person.users,
113
+ columns=[
114
+ 'username',
115
+ 'active',
116
+ ])
117
+
118
+ if self.request.has_perm('users.view'):
119
+ url = lambda user, i: self.request.route_url('users.view', uuid=user.uuid)
120
+ grid.add_action('view', icon='eye', url=url)
121
+ grid.set_link('username')
122
+
123
+ if self.request.has_perm('users.edit'):
124
+ url = lambda user, i: self.request.route_url('users.edit', uuid=user.uuid)
125
+ grid.add_action('edit', url=url)
126
+
127
+ return grid
128
+
129
+ def objectify(self, form):
130
+ """ """
131
+ person = super().objectify(form)
132
+
133
+ # full_name
134
+ person.full_name = self.app.make_full_name(person.first_name,
135
+ person.last_name)
136
+
137
+ return person
97
138
 
98
139
  def autocomplete_query(self, term):
99
140
  """ """
@@ -28,7 +28,7 @@ from wuttjamaican.db.model import Role, Permission
28
28
  from wuttaweb.views import MasterView
29
29
  from wuttaweb.db import Session
30
30
  from wuttaweb.forms import widgets
31
- from wuttaweb.forms.schema import UserRefs, Permissions, RoleRef
31
+ from wuttaweb.forms.schema import Permissions, RoleRef
32
32
 
33
33
 
34
34
  class RoleView(MasterView):
@@ -123,9 +123,7 @@ class RoleView(MasterView):
123
123
  # users
124
124
  if not (self.creating or self.editing):
125
125
  f.append('users')
126
- f.set_readonly('users')
127
- f.set_node('users', UserRefs(self.request))
128
- f.set_default('users', [u.uuid for u in role.users])
126
+ f.set_grid('users', self.make_users_grid(role))
129
127
 
130
128
  # permissions
131
129
  f.append('permissions')
@@ -134,6 +132,39 @@ class RoleView(MasterView):
134
132
  if not self.creating:
135
133
  f.set_default('permissions', list(role.permissions))
136
134
 
135
+ def make_users_grid(self, role):
136
+ """
137
+ Make and return the grid for the Users field.
138
+
139
+ This grid is shown for the Users field when viewing a Role.
140
+
141
+ :returns: Fully configured :class:`~wuttaweb.grids.base.Grid`
142
+ instance.
143
+ """
144
+ model = self.app.model
145
+ route_prefix = self.get_route_prefix()
146
+
147
+ grid = self.make_grid(key=f'{route_prefix}.view.users',
148
+ model_class=model.User,
149
+ data=role.users,
150
+ columns=[
151
+ 'username',
152
+ 'person',
153
+ 'active',
154
+ ])
155
+
156
+ if self.request.has_perm('users.view'):
157
+ url = lambda user, i: self.request.route_url('users.view', uuid=user.uuid)
158
+ grid.add_action('view', icon='eye', url=url)
159
+ grid.set_link('person')
160
+ grid.set_link('username')
161
+
162
+ if self.request.has_perm('users.edit'):
163
+ url = lambda user, i: self.request.route_url('users.edit', uuid=user.uuid)
164
+ grid.add_action('edit', url=url)
165
+
166
+ return grid
167
+
137
168
  def unique_name(self, node, value):
138
169
  """ """
139
170
  model = self.app.model
@@ -2,7 +2,7 @@
2
2
  ################################################################################
3
3
  #
4
4
  # wuttaweb -- Web App for Wutta Framework
5
- # Copyright © 2024 Lance Edgar
5
+ # Copyright © 2024-2025 Lance Edgar
6
6
  #
7
7
  # This file is part of Wutta Framework.
8
8
  #
@@ -30,7 +30,6 @@ from wuttjamaican.db.model import User
30
30
  from wuttaweb.views import MasterView
31
31
  from wuttaweb.forms import widgets
32
32
  from wuttaweb.forms.schema import PersonRef, RoleRefs
33
- from wuttaweb.db import Session
34
33
 
35
34
 
36
35
  class UserView(MasterView):
@@ -61,6 +60,14 @@ class UserView(MasterView):
61
60
  }
62
61
  sort_defaults = 'username'
63
62
 
63
+ form_fields = [
64
+ 'username',
65
+ 'person',
66
+ 'active',
67
+ 'prevent_edit',
68
+ 'roles',
69
+ ]
70
+
64
71
  def get_query(self, session=None):
65
72
  """ """
66
73
  query = super().get_query(session=session)
@@ -109,17 +116,24 @@ class UserView(MasterView):
109
116
  super().configure_form(f)
110
117
  user = f.model_instance
111
118
 
112
- # never show these
113
- f.remove('person_uuid',
114
- 'role_refs')
115
-
116
- # person
117
- f.set_node('person', PersonRef(self.request, empty_option=True))
118
- f.set_required('person', False)
119
-
120
119
  # username
121
120
  f.set_validator('username', self.unique_username)
122
121
 
122
+ # person
123
+ if self.creating or self.editing:
124
+ f.fields.insert_after('person', 'first_name')
125
+ f.set_required('first_name', False)
126
+ f.fields.insert_after('first_name', 'last_name')
127
+ f.set_required('last_name', False)
128
+ f.remove('person')
129
+ if self.editing:
130
+ person = user.person
131
+ if person:
132
+ f.set_default('first_name', person.first_name)
133
+ f.set_default('last_name', person.last_name)
134
+ else:
135
+ f.set_node('person', PersonRef(self.request))
136
+
123
137
  # password
124
138
  # nb. we must avoid 'password' as field name since
125
139
  # ColanderAlchemy wants to handle the raw/hashed value
@@ -140,7 +154,7 @@ class UserView(MasterView):
140
154
  def unique_username(self, node, value):
141
155
  """ """
142
156
  model = self.app.model
143
- session = Session()
157
+ session = self.Session()
144
158
 
145
159
  query = session.query(model.User)\
146
160
  .filter(model.User.username == value)
@@ -152,26 +166,48 @@ class UserView(MasterView):
152
166
  if query.count():
153
167
  node.raise_invalid("Username must be unique")
154
168
 
155
- def objectify(self, form, session=None):
169
+ def objectify(self, form):
156
170
  """ """
171
+ model = self.app.model
172
+ auth = self.app.get_auth_handler()
157
173
  data = form.validated
158
174
 
159
175
  # normal logic first
160
176
  user = super().objectify(form)
161
177
 
178
+ # maybe update person name
179
+ if 'first_name' in form or 'last_name' in form:
180
+ first_name = data.get('first_name')
181
+ last_name = data.get('last_name')
182
+ if self.creating and (first_name or last_name):
183
+ user.person = auth.make_person(first_name=first_name, last_name=last_name)
184
+ elif self.editing:
185
+ if first_name or last_name:
186
+ if user.person:
187
+ person = user.person
188
+ if 'first_name' in form:
189
+ person.first_name = first_name
190
+ if 'last_name' in form:
191
+ person.last_name = last_name
192
+ person.full_name = self.app.make_full_name(person.first_name,
193
+ person.last_name)
194
+ else:
195
+ user.person = auth.make_person(first_name=first_name, last_name=last_name)
196
+ elif user.person:
197
+ user.person = None
198
+
162
199
  # maybe set user password
163
200
  if 'set_password' in form and data.get('set_password'):
164
- auth = self.app.get_auth_handler()
165
201
  auth.set_user_password(user, data['set_password'])
166
202
 
167
203
  # update roles for user
168
204
  # TODO
169
205
  # if self.has_perm('edit_roles'):
170
- self.update_roles(user, form, session=session)
206
+ self.update_roles(user, form)
171
207
 
172
208
  return user
173
209
 
174
- def update_roles(self, user, form, session=None):
210
+ def update_roles(self, user, form):
175
211
  """ """
176
212
  # TODO
177
213
  # if not self.has_perm('edit_roles'):
@@ -181,7 +217,7 @@ class UserView(MasterView):
181
217
  return
182
218
 
183
219
  model = self.app.model
184
- session = session or Session()
220
+ session = self.Session()
185
221
  auth = self.app.get_auth_handler()
186
222
 
187
223
  old_roles = set([role.uuid for role in user.roles])
@@ -88,7 +88,8 @@ app.spec = wuttaweb.app:make_wsgi_app
88
88
  reload=False,
89
89
  reload_dirs=None,
90
90
  factory=False,
91
- interface='auto')
91
+ interface='auto',
92
+ root_path='')
92
93
 
93
94
  # with reload
94
95
  uvicorn.run.reset_mock()
@@ -101,4 +102,5 @@ app.spec = wuttaweb.app:make_wsgi_app
101
102
  reload=True,
102
103
  reload_dirs=None,
103
104
  factory=False,
104
- interface='auto')
105
+ interface='auto',
106
+ root_path='')
@@ -377,20 +377,6 @@ class TestUserRef(WebTestCase):
377
377
  self.assertIn(f'/users/{user.uuid}', url)
378
378
 
379
379
 
380
- class TestUserRefs(DataTestCase):
381
-
382
- def setUp(self):
383
- self.setup_db()
384
- self.request = testing.DummyRequest(wutta_config=self.config)
385
-
386
- def test_widget_maker(self):
387
- model = self.app.model
388
- with patch.object(mod, 'Session', return_value=self.session):
389
- typ = mod.UserRefs(self.request)
390
- widget = typ.widget_maker()
391
- self.assertIsInstance(widget, widgets.UserRefsWidget)
392
-
393
-
394
380
  class TestRoleRefs(DataTestCase):
395
381
 
396
382
  def setUp(self):