WuttaWeb 0.14.2__tar.gz → 0.16.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 (178) hide show
  1. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/CHANGELOG.md +24 -0
  2. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/PKG-INFO +4 -3
  3. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/pyproject.toml +4 -3
  4. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/forms/base.py +1 -1
  5. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/grids/base.py +10 -1
  6. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/menus.py +44 -28
  7. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/appinfo/index.mako +2 -2
  8. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/base.mako +7 -5
  9. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/auth.py +3 -0
  10. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/master.py +29 -2
  11. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/upgrades.py +1 -1
  12. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/users.py +9 -0
  13. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/grids/test_base.py +19 -0
  14. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_menus.py +11 -2
  15. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_auth.py +9 -2
  16. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_master.py +35 -0
  17. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_upgrades.py +2 -2
  18. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_users.py +20 -0
  19. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/.gitignore +0 -0
  20. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/COPYING.txt +0 -0
  21. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/README.md +0 -0
  22. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/Makefile +0 -0
  23. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/_static/.keepme +0 -0
  24. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.app.rst +0 -0
  25. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.auth.rst +0 -0
  26. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.conf.rst +0 -0
  27. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
  28. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.db.rst +0 -0
  29. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.db.sess.rst +0 -0
  30. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.forms.base.rst +0 -0
  31. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.forms.rst +0 -0
  32. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
  33. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  34. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.grids.base.rst +0 -0
  35. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
  36. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.grids.rst +0 -0
  37. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.handler.rst +0 -0
  38. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.helpers.rst +0 -0
  39. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.menus.rst +0 -0
  40. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.progress.rst +0 -0
  41. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.rst +0 -0
  42. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.static.rst +0 -0
  43. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.subscribers.rst +0 -0
  44. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.util.rst +0 -0
  45. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.auth.rst +0 -0
  46. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.base.rst +0 -0
  47. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.common.rst +0 -0
  48. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.essential.rst +0 -0
  49. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.master.rst +0 -0
  50. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.people.rst +0 -0
  51. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.progress.rst +0 -0
  52. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.roles.rst +0 -0
  53. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.rst +0 -0
  54. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.settings.rst +0 -0
  55. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  56. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/api/wuttaweb.views.users.rst +0 -0
  57. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/conf.py +0 -0
  58. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/glossary.rst +0 -0
  59. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/index.rst +0 -0
  60. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/make.bat +0 -0
  61. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/narr/templates/base.rst +0 -0
  62. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/narr/templates/index.rst +0 -0
  63. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/narr/templates/lookup.rst +0 -0
  64. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/docs/narr/templates/overview.rst +0 -0
  65. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/__init__.py +0 -0
  66. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/_version.py +0 -0
  67. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/app.py +0 -0
  68. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/auth.py +0 -0
  69. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/conf.py +0 -0
  70. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/db/__init__.py +0 -0
  71. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/db/continuum.py +0 -0
  72. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/db/sess.py +0 -0
  73. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/email/templates/feedback.html.mako +0 -0
  74. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/email/templates/feedback.txt.mako +0 -0
  75. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/forms/__init__.py +0 -0
  76. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/forms/schema.py +0 -0
  77. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/forms/widgets.py +0 -0
  78. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/grids/__init__.py +0 -0
  79. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/grids/filters.py +0 -0
  80. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/handler.py +0 -0
  81. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/helpers.py +0 -0
  82. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/progress.py +0 -0
  83. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/static/__init__.py +0 -0
  84. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/static/img/favicon.ico +0 -0
  85. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/static/img/logo.png +0 -0
  86. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/static/img/testing.png +0 -0
  87. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/subscribers.py +0 -0
  88. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  89. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  90. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/auth/login.mako +0 -0
  91. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/base_meta.mako +0 -0
  92. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/configure.mako +0 -0
  93. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  94. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  95. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  96. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  97. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/password.pt +0 -0
  98. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  99. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  100. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  101. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  102. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  103. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  104. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  105. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/select.pt +0 -0
  106. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  107. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  108. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/forbidden.mako +0 -0
  109. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/form.mako +0 -0
  110. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  111. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  112. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  113. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/home.mako +0 -0
  114. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/configure.mako +0 -0
  115. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/create.mako +0 -0
  116. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/delete.mako +0 -0
  117. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/edit.mako +0 -0
  118. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/form.mako +0 -0
  119. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/index.mako +0 -0
  120. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/master/view.mako +0 -0
  121. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/notfound.mako +0 -0
  122. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/page.mako +0 -0
  123. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  124. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/progress.mako +0 -0
  125. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/setup.mako +0 -0
  126. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/upgrade.mako +0 -0
  127. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  128. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  129. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
  130. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/util.py +0 -0
  131. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/__init__.py +0 -0
  132. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/base.py +0 -0
  133. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/common.py +0 -0
  134. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/essential.py +2 -2
  135. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/people.py +0 -0
  136. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/progress.py +0 -0
  137. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/roles.py +0 -0
  138. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/src/wuttaweb/views/settings.py +0 -0
  139. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tasks.py +0 -0
  140. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/__init__.py +0 -0
  141. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/db/__init__.py +0 -0
  142. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/db/test_continuum.py +0 -0
  143. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/forms/__init__.py +0 -0
  144. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/forms/test_base.py +0 -0
  145. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/forms/test_schema.py +0 -0
  146. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/forms/test_widgets.py +0 -0
  147. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/grids/__init__.py +0 -0
  148. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/grids/test_filters.py +0 -0
  149. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  150. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  151. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_oruga.js +0 -0
  152. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_oruga_bulma.css +0 -0
  153. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_oruga_bulma.js +0 -0
  154. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_vue.js +0 -0
  155. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
  156. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/buefy.css +0 -0
  157. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/buefy.js +0 -0
  158. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/fontawesome.js +0 -0
  159. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/vue.js +0 -0
  160. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/libcache/vue_resource.js +0 -0
  161. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_app.py +0 -0
  162. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_auth.py +0 -0
  163. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_handler.py +0 -0
  164. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_helpers.py +0 -0
  165. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_progress.py +0 -0
  166. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_static.py +0 -0
  167. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_subscribers.py +0 -0
  168. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/test_util.py +0 -0
  169. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/util.py +0 -0
  170. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/__init__.py +0 -0
  171. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test___init__.py +0 -0
  172. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_base.py +0 -0
  173. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_common.py +0 -0
  174. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_people.py +0 -0
  175. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_progress.py +0 -0
  176. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_roles.py +0 -0
  177. {wuttaweb-0.14.2 → wuttaweb-0.16.0}/tests/views/test_settings.py +0 -0
  178. {wuttaweb-0.14.2 → wuttaweb-0.16.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.16.0 (2024-12-05)
9
+
10
+ ### Feat
11
+
12
+ - add `get_template_context()` method for master view
13
+
14
+ ### Fix
15
+
16
+ - add option for People entry in the Admin menu
17
+ - fix handling of `Upgrade.uuid`
18
+ - improve support for random objects with grid, master view
19
+ - hide CRUD header buttons if master view does not allow
20
+
21
+ ## v0.15.0 (2024-11-24)
22
+
23
+ ### Feat
24
+
25
+ - add logic to prevent edit for some user accounts
26
+
27
+ ### Fix
28
+
29
+ - fix default form value logic for bool checkbox fields
30
+ - always use configured app dist for appinfo/index page
31
+
8
32
  ## v0.14.2 (2024-11-24)
9
33
 
10
34
  ### Fix
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: WuttaWeb
3
- Version: 0.14.2
3
+ Version: 0.16.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
7
+ Project-URL: Issues, https://forgejo.wuttaproject.org/wutta/wuttaweb/issues
7
8
  Project-URL: Changelog, https://forgejo.wuttaproject.org/wutta/wuttaweb/src/branch/master/CHANGELOG.md
8
- Author-email: Lance Edgar <lance@edbob.org>
9
+ Author-email: Lance Edgar <lance@wuttaproject.org>
9
10
  License: GNU GPL v3+
10
11
  Classifier: Development Status :: 4 - Beta
11
12
  Classifier: Environment :: Web Environment
@@ -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.14.0
39
+ Requires-Dist: wuttjamaican[db]>=0.16.1
39
40
  Requires-Dist: zope-sqlalchemy>=1.5
40
41
  Provides-Extra: continuum
41
42
  Requires-Dist: wutta-continuum; extra == 'continuum'
@@ -6,10 +6,10 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "WuttaWeb"
9
- version = "0.14.2"
9
+ version = "0.16.0"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
- authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
12
+ authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
13
13
  license = {text = "GNU GPL v3+"}
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
@@ -42,7 +42,7 @@ dependencies = [
42
42
  "pyramid_tm",
43
43
  "waitress",
44
44
  "WebHelpers2",
45
- "WuttJamaican[db]>=0.14.0",
45
+ "WuttJamaican[db]>=0.16.1",
46
46
  "zope.sqlalchemy>=1.5",
47
47
  ]
48
48
 
@@ -68,6 +68,7 @@ wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
68
68
  [project.urls]
69
69
  Homepage = "https://wuttaproject.org/"
70
70
  Repository = "https://forgejo.wuttaproject.org/wutta/wuttaweb"
71
+ Issues = "https://forgejo.wuttaproject.org/wutta/wuttaweb/issues"
71
72
  Changelog = "https://forgejo.wuttaproject.org/wutta/wuttaweb/src/branch/master/CHANGELOG.md"
72
73
 
73
74
 
@@ -1002,7 +1002,7 @@ class Form:
1002
1002
  # for now we explicitly translate here, ugh. also
1003
1003
  # note this does not yet allow for null values.. :(
1004
1004
  if isinstance(field.typ, colander.Boolean):
1005
- value = True if field.typ.true_val else False
1005
+ value = True if value == field.typ.true_val else False
1006
1006
 
1007
1007
  model_data[field.oid] = make_json_safe(value)
1008
1008
 
@@ -1940,6 +1940,15 @@ class Grid:
1940
1940
  })
1941
1941
  return filters
1942
1942
 
1943
+ def object_to_dict(self, obj):
1944
+ """ """
1945
+ try:
1946
+ dct = dict(obj)
1947
+ except TypeError:
1948
+ dct = dict(obj.__dict__)
1949
+ dct.pop('_sa_instance_state', None)
1950
+ return dct
1951
+
1943
1952
  def get_vue_context(self):
1944
1953
  """
1945
1954
  Returns a dict of context for the grid, for use with the Vue
@@ -1976,7 +1985,7 @@ class Grid:
1976
1985
  original_record = record
1977
1986
 
1978
1987
  # convert record to new dict
1979
- record = dict(record)
1988
+ record = self.object_to_dict(record)
1980
1989
 
1981
1990
  # make all values safe for json
1982
1991
  record = make_json_safe(record, warn=False)
@@ -142,38 +142,54 @@ class MenuHandler(GenericHandler):
142
142
  The return value for this method should be a *single* dict,
143
143
  which will ultimately be one element of the final list of
144
144
  dicts as described in :class:`MenuHandler`.
145
+
146
+ :param include_people: You can pass this flag to indicate the
147
+ admin menu should contain an entry for the "People" view.
145
148
  """
149
+ items = []
150
+
151
+ if kwargs.get('include_people'):
152
+ items.extend([
153
+ {
154
+ 'title': "All People",
155
+ 'route': 'people',
156
+ 'perm': 'people.list',
157
+ },
158
+ ])
159
+
160
+ items.extend([
161
+ {
162
+ 'title': "Users",
163
+ 'route': 'users',
164
+ 'perm': 'users.list',
165
+ },
166
+ {
167
+ 'title': "Roles",
168
+ 'route': 'roles',
169
+ 'perm': 'roles.list',
170
+ },
171
+ {'type': 'sep'},
172
+ {
173
+ 'title': "App Info",
174
+ 'route': 'appinfo',
175
+ 'perm': 'appinfo.list',
176
+ },
177
+ {
178
+ 'title': "Raw Settings",
179
+ 'route': 'settings',
180
+ 'perm': 'settings.list',
181
+ },
182
+ {
183
+ 'title': "Upgrades",
184
+ 'route': 'upgrades',
185
+ 'perm': 'upgrades.list',
186
+ },
187
+ ])
188
+
146
189
  return {
147
190
  'title': "Admin",
148
191
  'type': 'menu',
149
- 'items': [
150
- {
151
- 'title': "Users",
152
- 'route': 'users',
153
- 'perm': 'users.list',
154
- },
155
- {
156
- 'title': "Roles",
157
- 'route': 'roles',
158
- 'perm': 'roles.list',
159
- },
160
- {'type': 'sep'},
161
- {
162
- 'title': "App Info",
163
- 'route': 'appinfo',
164
- 'perm': 'appinfo.list',
165
- },
166
- {
167
- 'title': "Raw Settings",
168
- 'route': 'settings',
169
- 'perm': 'settings.list',
170
- },
171
- {
172
- 'title': "Upgrades",
173
- 'route': 'upgrades',
174
- 'perm': 'upgrades.list',
175
- },
176
- ],
192
+ 'items': items,
177
193
  }
178
194
 
179
195
  ##############################
@@ -8,10 +8,10 @@
8
8
  <div class="panel-block">
9
9
  <div style="width: 100%;">
10
10
  <b-field horizontal label="Distribution">
11
- <span>${app.get_distribution(obj=app.get_web_handler()) or f'?? - set config for `{app.appname}.app_dist`'}</span>
11
+ <span>${app.get_distribution() or f'?? - set config for `{app.appname}.app_dist`'}</span>
12
12
  </b-field>
13
13
  <b-field horizontal label="Version">
14
- <span>${app.get_version(obj=app.get_web_handler()) or f'?? - set config for `{app.appname}.app_dist`'}</span>
14
+ <span>${app.get_version() or f'?? - set config for `{app.appname}.app_dist`'}</span>
15
15
  </b-field>
16
16
  <b-field horizontal label="App Title">
17
17
  <span>${app.get_title()}</span>
@@ -661,7 +661,9 @@
661
661
  </a>
662
662
  ${h.end_form()}
663
663
  % endif
664
- ${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
664
+ % if request.is_root or not request.user.prevent_edit:
665
+ ${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
666
+ % endif
665
667
  ${h.link_to("Logout", url('logout'), class_='navbar-item')}
666
668
  </div>
667
669
  </div>
@@ -680,13 +682,13 @@
680
682
  <%def name="render_crud_header_buttons()">
681
683
  % if master:
682
684
  % if master.viewing:
683
- % if instance_editable and master.has_perm('edit'):
685
+ % if master.editable and instance_editable and master.has_perm('edit'):
684
686
  <wutta-button once
685
687
  tag="a" href="${master.get_action_url('edit', instance)}"
686
688
  icon-left="edit"
687
689
  label="Edit This" />
688
690
  % endif
689
- % if instance_deletable and master.has_perm('delete'):
691
+ % if master.deletable and instance_deletable and master.has_perm('delete'):
690
692
  <wutta-button once type="is-danger"
691
693
  tag="a" href="${master.get_action_url('delete', instance)}"
692
694
  icon-left="trash"
@@ -699,7 +701,7 @@
699
701
  icon-left="eye"
700
702
  label="View This" />
701
703
  % endif
702
- % if instance_deletable and master.has_perm('delete'):
704
+ % if master.deletable and instance_deletable and master.has_perm('delete'):
703
705
  <wutta-button once type="is-danger"
704
706
  tag="a" href="${master.get_action_url('delete', instance)}"
705
707
  icon-left="trash"
@@ -712,7 +714,7 @@
712
714
  icon-left="eye"
713
715
  label="View This" />
714
716
  % endif
715
- % if instance_editable and master.has_perm('edit'):
717
+ % if master.editable and instance_editable and master.has_perm('edit'):
716
718
  <wutta-button once
717
719
  tag="a" href="${master.get_action_url('edit', instance)}"
718
720
  icon-left="edit"
@@ -157,6 +157,9 @@ class AuthView(View):
157
157
  if not self.request.user:
158
158
  return self.redirect(self.request.route_url('home'))
159
159
 
160
+ if self.request.user.prevent_edit:
161
+ raise self.forbidden()
162
+
160
163
  form = self.make_form(schema=self.change_password_make_schema(),
161
164
  show_button_cancel=False,
162
165
  show_button_reset=True)
@@ -1629,6 +1629,9 @@ class MasterView(View):
1629
1629
  if 'instance_deletable' not in context:
1630
1630
  context['instance_deletable'] = self.is_deletable(instance)
1631
1631
 
1632
+ # supplement context further if needed
1633
+ context = self.get_template_context(context)
1634
+
1632
1635
  # first try the template path most specific to this view
1633
1636
  template_prefix = self.get_template_prefix()
1634
1637
  mako_path = f'{template_prefix}/{template}.mako'
@@ -1648,6 +1651,26 @@ class MasterView(View):
1648
1651
  # let that error raise on up
1649
1652
  return render_to_response(mako_path, context, request=self.request)
1650
1653
 
1654
+ def get_template_context(self, context):
1655
+ """
1656
+ This method should return the "complete" context for rendering
1657
+ the current view template.
1658
+
1659
+ Default logic for this method returns the given context
1660
+ unchanged.
1661
+
1662
+ You may wish to override to pass extra context to the view
1663
+ template. Check :attr:`viewing` and similar, or
1664
+ ``request.current_route_name`` etc. in order to add extra
1665
+ context only for certain view templates.
1666
+
1667
+ :params: context: The context dict we have so far,
1668
+ auto-provided by the master view logic.
1669
+
1670
+ :returns: Final context dict for the template.
1671
+ """
1672
+ return context
1673
+
1651
1674
  def get_fallback_templates(self, template):
1652
1675
  """
1653
1676
  Returns a list of "fallback" template paths which may be
@@ -1995,8 +2018,12 @@ class MasterView(View):
1995
2018
  :param obj: Model instance object.
1996
2019
  """
1997
2020
  route_prefix = self.get_route_prefix()
1998
- kw = dict([(key, obj[key])
1999
- for key in self.get_model_key()])
2021
+ try:
2022
+ kw = dict([(key, obj[key])
2023
+ for key in self.get_model_key()])
2024
+ except TypeError:
2025
+ kw = dict([(key, getattr(obj, key))
2026
+ for key in self.get_model_key()])
2000
2027
  kw.update(kwargs)
2001
2028
  return self.request.route_url(f'{route_prefix}.{action}', **kw)
2002
2029
 
@@ -217,7 +217,7 @@ class UpgradeView(MasterView):
217
217
 
218
218
  def get_upgrade_filepath(self, upgrade, filename=None, create=True):
219
219
  """ """
220
- uuid = upgrade.uuid
220
+ uuid = str(upgrade.uuid)
221
221
  path = self.app.get_appdir('data', 'upgrades', uuid[:2], uuid[2:],
222
222
  create=create)
223
223
  if filename:
@@ -95,6 +95,15 @@ class UserView(MasterView):
95
95
  if not user.active:
96
96
  return 'has-background-warning'
97
97
 
98
+ def is_editable(self, user):
99
+ """ """
100
+
101
+ # only root can edit certain users
102
+ if user.prevent_edit and not self.request.is_root:
103
+ return False
104
+
105
+ return True
106
+
98
107
  def configure_form(self, f):
99
108
  """ """
100
109
  super().configure_form(f)
@@ -1373,6 +1373,25 @@ class TestGrid(WebTestCase):
1373
1373
  filters = grid.get_vue_filters()
1374
1374
  self.assertEqual(len(filters), 2)
1375
1375
 
1376
+ def test_object_to_dict(self):
1377
+ grid = self.make_grid()
1378
+ setting = {'name': 'foo', 'value': 'bar'}
1379
+
1380
+ # new dict but with same values
1381
+ dct = grid.object_to_dict(setting)
1382
+ self.assertIsInstance(dct, dict)
1383
+ self.assertIsNot(dct, setting)
1384
+ self.assertEqual(dct, setting)
1385
+
1386
+ # random object, not iterable
1387
+ class MockSetting:
1388
+ def __init__(self, **kw):
1389
+ self.__dict__.update(kw)
1390
+ mock = MockSetting(**setting)
1391
+ dct = grid.object_to_dict(mock)
1392
+ self.assertIsInstance(dct, dict)
1393
+ self.assertEqual(dct, setting)
1394
+
1376
1395
  def test_get_vue_context(self):
1377
1396
 
1378
1397
  # empty if no columns defined
@@ -14,8 +14,17 @@ class TestMenuHandler(WebTestCase):
14
14
  self.handler = mod.MenuHandler(self.config)
15
15
 
16
16
  def test_make_admin_menu(self):
17
- menus = self.handler.make_admin_menu(self.request)
18
- self.assertIsInstance(menus, dict)
17
+
18
+ # no people entry by default
19
+ menu = self.handler.make_admin_menu(self.request)
20
+ self.assertIsInstance(menu, dict)
21
+ routes = [item.get('route') for item in menu['items']]
22
+ self.assertNotIn('people', routes)
23
+
24
+ # but we can request it
25
+ menu = self.handler.make_admin_menu(self.request, include_people=True)
26
+ routes = [item.get('route') for item in menu['items']]
27
+ self.assertIn('people', routes)
19
28
 
20
29
  def test_make_menus(self):
21
30
  menus = self.handler.make_menus(self.request)
@@ -80,11 +80,18 @@ class TestAuthView(WebTestCase):
80
80
  redirect = view.change_password()
81
81
  self.assertIsInstance(redirect, HTTPFound)
82
82
 
83
- # now "login" the user, and set initial password
84
- self.request.user = barney
83
+ # set initial password
85
84
  auth.set_user_password(barney, 'foo')
86
85
  self.session.commit()
87
86
 
87
+ # forbidden if prevent_edit is set for user
88
+ self.request.user = barney
89
+ barney.prevent_edit = True
90
+ self.assertRaises(HTTPForbidden, view.change_password)
91
+
92
+ # okay let's test with edit allowed
93
+ barney.prevent_edit = False
94
+
88
95
  # view should now return context w/ form
89
96
  context = view.change_password()
90
97
  self.assertIn('form', context)
@@ -734,6 +734,41 @@ class TestMasterView(WebTestCase):
734
734
  self.request.matchdict = {'name': 'blarg'}
735
735
  self.assertRaises(HTTPNotFound, view.get_instance, session=self.session)
736
736
 
737
+ def test_get_action_url_for_dict(self):
738
+ model = self.app.model
739
+ setting = {'name': 'foo', 'value': 'bar'}
740
+ with patch.multiple(mod.MasterView, create=True,
741
+ model_class=model.Setting):
742
+ mod.MasterView.defaults(self.pyramid_config)
743
+ view = self.make_view()
744
+ url = view.get_action_url_view(setting, 0)
745
+ self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
746
+
747
+ def test_get_action_url_for_orm_object(self):
748
+ model = self.app.model
749
+ setting = model.Setting(name='foo', value='bar')
750
+ self.session.add(setting)
751
+ self.session.commit()
752
+ with patch.multiple(mod.MasterView, create=True,
753
+ model_class=model.Setting):
754
+ mod.MasterView.defaults(self.pyramid_config)
755
+ view = self.make_view()
756
+ url = view.get_action_url_view(setting, 0)
757
+ self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
758
+
759
+ def test_get_action_url_for_adhoc_object(self):
760
+ model = self.app.model
761
+ class MockSetting:
762
+ def __init__(self, **kw):
763
+ self.__dict__.update(kw)
764
+ setting = MockSetting(name='foo', value='bar')
765
+ with patch.multiple(mod.MasterView, create=True,
766
+ model_class=model.Setting):
767
+ mod.MasterView.defaults(self.pyramid_config)
768
+ view = self.make_view()
769
+ url = view.get_action_url_view(setting, 0)
770
+ self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
771
+
737
772
  def test_get_action_url_view(self):
738
773
  model = self.app.model
739
774
  setting = model.Setting(name='foo', value='bar')
@@ -127,7 +127,7 @@ class TestUpgradeView(WebTestCase):
127
127
  self.session.commit()
128
128
 
129
129
  view = self.make_view()
130
- uuid = upgrade.uuid
130
+ uuid = str(upgrade.uuid)
131
131
 
132
132
  # no filename
133
133
  path = view.download_path(upgrade, None)
@@ -153,7 +153,7 @@ class TestUpgradeView(WebTestCase):
153
153
  self.session.commit()
154
154
 
155
155
  view = self.make_view()
156
- uuid = upgrade.uuid
156
+ uuid = str(upgrade.uuid)
157
157
 
158
158
  # no filename
159
159
  path = view.get_upgrade_filepath(upgrade)
@@ -42,6 +42,26 @@ class TestUserView(WebTestCase):
42
42
  user.active = False
43
43
  self.assertEqual(view.grid_row_class(user, data, 1), 'has-background-warning')
44
44
 
45
+ def test_is_editable(self):
46
+ model = self.app.model
47
+ view = self.make_view()
48
+
49
+ # active user is editable
50
+ user = model.User(username='barney', active=True)
51
+ self.assertTrue(view.is_editable(user))
52
+
53
+ # inactive also editable
54
+ user = model.User(username='barney', active=False)
55
+ self.assertTrue(view.is_editable(user))
56
+
57
+ # but not if prevent_edit flag is set
58
+ user = model.User(username='barney', prevent_edit=True)
59
+ self.assertFalse(view.is_editable(user))
60
+
61
+ # unless request user is root
62
+ self.request.is_root = True
63
+ self.assertTrue(view.is_editable(user))
64
+
45
65
  def test_configure_form(self):
46
66
  model = self.app.model
47
67
  barney = model.User(username='barney')
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
@@ -29,8 +29,8 @@ Most apps should include this module::
29
29
 
30
30
  That will in turn include the following modules:
31
31
 
32
- * :mod:`wuttaweb.views.auth`
33
32
  * :mod:`wuttaweb.views.common`
33
+ * :mod:`wuttaweb.views.auth`
34
34
  * :mod:`wuttaweb.views.settings`
35
35
  * :mod:`wuttaweb.views.progress`
36
36
  * :mod:`wuttaweb.views.people`
@@ -43,8 +43,8 @@ That will in turn include the following modules:
43
43
  def defaults(config, **kwargs):
44
44
  mod = lambda spec: kwargs.get(spec, spec)
45
45
 
46
- config.include(mod('wuttaweb.views.auth'))
47
46
  config.include(mod('wuttaweb.views.common'))
47
+ config.include(mod('wuttaweb.views.auth'))
48
48
  config.include(mod('wuttaweb.views.settings'))
49
49
  config.include(mod('wuttaweb.views.progress'))
50
50
  config.include(mod('wuttaweb.views.people'))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes