WuttaWeb 0.15.0__tar.gz → 0.16.1__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.15.0 → wuttaweb-0.16.1}/CHANGELOG.md +19 -0
  2. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/PKG-INFO +4 -3
  3. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/pyproject.toml +4 -3
  4. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/forms/schema.py +10 -1
  5. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/grids/base.py +10 -1
  6. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/menus.py +44 -28
  7. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/base.mako +5 -5
  8. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/master.py +29 -2
  9. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/upgrades.py +1 -1
  10. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/forms/test_widgets.py +4 -4
  11. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/grids/test_base.py +19 -0
  12. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_auth.py +2 -1
  13. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_menus.py +11 -2
  14. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_common.py +1 -1
  15. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_master.py +35 -0
  16. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_upgrades.py +2 -2
  17. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/.gitignore +0 -0
  18. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/COPYING.txt +0 -0
  19. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/README.md +0 -0
  20. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/Makefile +0 -0
  21. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/_static/.keepme +0 -0
  22. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.app.rst +0 -0
  23. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.auth.rst +0 -0
  24. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.conf.rst +0 -0
  25. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.db.continuum.rst +0 -0
  26. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.db.rst +0 -0
  27. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.db.sess.rst +0 -0
  28. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.forms.base.rst +0 -0
  29. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.forms.rst +0 -0
  30. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.forms.schema.rst +0 -0
  31. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  32. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.grids.base.rst +0 -0
  33. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.grids.filters.rst +0 -0
  34. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.grids.rst +0 -0
  35. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.handler.rst +0 -0
  36. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.helpers.rst +0 -0
  37. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.menus.rst +0 -0
  38. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.progress.rst +0 -0
  39. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.rst +0 -0
  40. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.static.rst +0 -0
  41. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.subscribers.rst +0 -0
  42. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.util.rst +0 -0
  43. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.auth.rst +0 -0
  44. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.base.rst +0 -0
  45. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.common.rst +0 -0
  46. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.essential.rst +0 -0
  47. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.master.rst +0 -0
  48. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.people.rst +0 -0
  49. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.progress.rst +0 -0
  50. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.roles.rst +0 -0
  51. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.rst +0 -0
  52. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.settings.rst +0 -0
  53. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  54. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/api/wuttaweb.views.users.rst +0 -0
  55. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/conf.py +0 -0
  56. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/glossary.rst +0 -0
  57. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/index.rst +0 -0
  58. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/make.bat +0 -0
  59. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/narr/templates/base.rst +0 -0
  60. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/narr/templates/index.rst +0 -0
  61. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/narr/templates/lookup.rst +0 -0
  62. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/docs/narr/templates/overview.rst +0 -0
  63. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/__init__.py +0 -0
  64. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/_version.py +0 -0
  65. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/app.py +0 -0
  66. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/auth.py +0 -0
  67. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/conf.py +0 -0
  68. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/db/__init__.py +0 -0
  69. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/db/continuum.py +0 -0
  70. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/db/sess.py +0 -0
  71. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/email/templates/feedback.html.mako +0 -0
  72. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/email/templates/feedback.txt.mako +0 -0
  73. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/forms/__init__.py +0 -0
  74. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/forms/base.py +0 -0
  75. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/forms/widgets.py +0 -0
  76. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/grids/__init__.py +0 -0
  77. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/grids/filters.py +0 -0
  78. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/handler.py +0 -0
  79. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/helpers.py +0 -0
  80. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/progress.py +0 -0
  81. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/static/__init__.py +0 -0
  82. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/static/img/favicon.ico +0 -0
  83. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/static/img/logo.png +0 -0
  84. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/static/img/testing.png +0 -0
  85. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/subscribers.py +0 -0
  86. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  87. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  88. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  89. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/auth/login.mako +0 -0
  90. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/base_meta.mako +0 -0
  91. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/configure.mako +0 -0
  92. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  93. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  94. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  95. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  96. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/password.pt +0 -0
  97. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  98. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  99. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  100. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  101. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  102. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  103. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  104. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/select.pt +0 -0
  105. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  106. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  107. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/forbidden.mako +0 -0
  108. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/form.mako +0 -0
  109. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  110. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  111. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  112. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/home.mako +0 -0
  113. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/configure.mako +0 -0
  114. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/create.mako +0 -0
  115. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/delete.mako +0 -0
  116. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/edit.mako +0 -0
  117. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/form.mako +0 -0
  118. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/index.mako +0 -0
  119. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/master/view.mako +0 -0
  120. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/notfound.mako +0 -0
  121. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/page.mako +0 -0
  122. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  123. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/progress.mako +0 -0
  124. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/setup.mako +0 -0
  125. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/upgrade.mako +0 -0
  126. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  127. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  128. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/templates/wutta-components.mako +0 -0
  129. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/util.py +0 -0
  130. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/__init__.py +0 -0
  131. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/auth.py +0 -0
  132. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/base.py +0 -0
  133. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/common.py +0 -0
  134. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/essential.py +0 -0
  135. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/people.py +0 -0
  136. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/progress.py +0 -0
  137. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/roles.py +0 -0
  138. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/settings.py +0 -0
  139. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/src/wuttaweb/views/users.py +0 -0
  140. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tasks.py +0 -0
  141. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/__init__.py +0 -0
  142. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/db/__init__.py +0 -0
  143. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/db/test_continuum.py +0 -0
  144. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/forms/__init__.py +0 -0
  145. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/forms/test_base.py +0 -0
  146. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/forms/test_schema.py +0 -0
  147. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/grids/__init__.py +0 -0
  148. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/grids/test_filters.py +0 -0
  149. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  150. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  151. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_oruga.js +0 -0
  152. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_oruga_bulma.css +0 -0
  153. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_oruga_bulma.js +0 -0
  154. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_vue.js +0 -0
  155. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/bb_vue_fontawesome.js +0 -0
  156. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/buefy.css +0 -0
  157. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/buefy.js +0 -0
  158. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/fontawesome.js +0 -0
  159. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/vue.js +0 -0
  160. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/libcache/vue_resource.js +0 -0
  161. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_app.py +0 -0
  162. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_handler.py +0 -0
  163. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_helpers.py +0 -0
  164. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_progress.py +0 -0
  165. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_static.py +0 -0
  166. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_subscribers.py +0 -0
  167. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/test_util.py +0 -0
  168. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/util.py +0 -0
  169. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/__init__.py +0 -0
  170. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test___init__.py +0 -0
  171. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_auth.py +0 -0
  172. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_base.py +0 -0
  173. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_people.py +0 -0
  174. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_progress.py +0 -0
  175. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_roles.py +0 -0
  176. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_settings.py +0 -0
  177. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tests/views/test_users.py +0 -0
  178. {wuttaweb-0.15.0 → wuttaweb-0.16.1}/tox.ini +0 -0
@@ -5,6 +5,25 @@ 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.1 (2024-12-08)
9
+
10
+ ### Fix
11
+
12
+ - refactor to reflect usage of proper UUID values
13
+
14
+ ## v0.16.0 (2024-12-05)
15
+
16
+ ### Feat
17
+
18
+ - add `get_template_context()` method for master view
19
+
20
+ ### Fix
21
+
22
+ - add option for People entry in the Admin menu
23
+ - fix handling of `Upgrade.uuid`
24
+ - improve support for random objects with grid, master view
25
+ - hide CRUD header buttons if master view does not allow
26
+
8
27
  ## v0.15.0 (2024-11-24)
9
28
 
10
29
  ### Feat
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: WuttaWeb
3
- Version: 0.15.0
3
+ Version: 0.16.1
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.15.0
39
+ Requires-Dist: wuttjamaican[db]>=0.17.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.15.0"
9
+ version = "0.16.1"
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.15.0",
45
+ "WuttJamaican[db]>=0.17.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
 
@@ -24,6 +24,8 @@
24
24
  Form schema types
25
25
  """
26
26
 
27
+ import uuid as _uuid
28
+
27
29
  import colander
28
30
 
29
31
  from wuttaweb.db import Session
@@ -246,7 +248,14 @@ class ObjectRef(colander.SchemaType):
246
248
 
247
249
  # fetch object from DB
248
250
  model = self.app.model
249
- obj = self.session.get(self.model_class, value)
251
+ obj = None
252
+ if isinstance(value, _uuid.UUID):
253
+ obj = self.session.get(self.model_class, value)
254
+ else:
255
+ try:
256
+ obj = self.session.get(self.model_class, _uuid.UUID(value))
257
+ except ValueError:
258
+ pass
250
259
 
251
260
  # raise error if not found
252
261
  if not obj:
@@ -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
  ##############################
@@ -563,7 +563,7 @@
563
563
 
564
564
  const WuttaFeedbackFormData = {
565
565
  referrer: null,
566
- userUUID: ${json.dumps(request.user.uuid if request.user else None)|n},
566
+ userUUID: ${json.dumps(request.user.uuid.hex if request.user else None)|n},
567
567
  userName: ${json.dumps(str(request.user) if request.user else None)|n},
568
568
  showDialog: false,
569
569
  sendingFeedback: false,
@@ -682,13 +682,13 @@
682
682
  <%def name="render_crud_header_buttons()">
683
683
  % if master:
684
684
  % if master.viewing:
685
- % if instance_editable and master.has_perm('edit'):
685
+ % if master.editable and instance_editable and master.has_perm('edit'):
686
686
  <wutta-button once
687
687
  tag="a" href="${master.get_action_url('edit', instance)}"
688
688
  icon-left="edit"
689
689
  label="Edit This" />
690
690
  % endif
691
- % if instance_deletable and master.has_perm('delete'):
691
+ % if master.deletable and instance_deletable and master.has_perm('delete'):
692
692
  <wutta-button once type="is-danger"
693
693
  tag="a" href="${master.get_action_url('delete', instance)}"
694
694
  icon-left="trash"
@@ -701,7 +701,7 @@
701
701
  icon-left="eye"
702
702
  label="View This" />
703
703
  % endif
704
- % if instance_deletable and master.has_perm('delete'):
704
+ % if master.deletable and instance_deletable and master.has_perm('delete'):
705
705
  <wutta-button once type="is-danger"
706
706
  tag="a" href="${master.get_action_url('delete', instance)}"
707
707
  icon-left="trash"
@@ -714,7 +714,7 @@
714
714
  icon-left="eye"
715
715
  label="View This" />
716
716
  % endif
717
- % if instance_editable and master.has_perm('edit'):
717
+ % if master.editable and instance_editable and master.has_perm('edit'):
718
718
  <wutta-button once
719
719
  tag="a" href="${master.get_action_url('edit', instance)}"
720
720
  icon-left="edit"
@@ -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:
@@ -146,14 +146,14 @@ class TestRoleRefsWidget(WebTestCase):
146
146
 
147
147
  # editable values list *excludes* admin (by default)
148
148
  html = widget.serialize(field, {admin.uuid, blokes.uuid})
149
- self.assertNotIn(admin.uuid, html)
150
- self.assertIn(blokes.uuid, html)
149
+ self.assertNotIn(str(admin.uuid), html)
150
+ self.assertIn(str(blokes.uuid), html)
151
151
 
152
152
  # but admin is included for root user
153
153
  self.request.is_root = True
154
154
  html = widget.serialize(field, {admin.uuid, blokes.uuid})
155
- self.assertIn(admin.uuid, html)
156
- self.assertIn(blokes.uuid, html)
155
+ self.assertIn(str(admin.uuid), html)
156
+ self.assertIn(str(blokes.uuid), html)
157
157
 
158
158
 
159
159
  class TestUserRefsWidget(WebTestCase):
@@ -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
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8; -*-
2
2
 
3
+ import uuid as _uuid
3
4
  from unittest import TestCase
4
5
  from unittest.mock import MagicMock
5
6
 
@@ -90,7 +91,7 @@ class TestWuttaSecurityPolicy(TestCase):
90
91
 
91
92
  # invalid identity yields no user
92
93
  self.policy = self.make_policy()
93
- self.policy.remember(self.request, 'bogus-user-uuid')
94
+ self.policy.remember(self.request, _uuid.uuid4()) # random uuid
94
95
  user = self.policy.identity(self.request)
95
96
  self.assertIsNone(user)
96
97
 
@@ -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)
@@ -88,7 +88,7 @@ class TestCommonView(WebTestCase):
88
88
 
89
89
  # basic send, with user
90
90
  self.request.user = user
91
- self.request.POST['user_uuid'] = user.uuid
91
+ self.request.POST['user_uuid'] = str(user.uuid)
92
92
  with patch.object(mod, 'Session', return_value=self.session):
93
93
  context = view.feedback()
94
94
  self.assertEqual(context, {'ok': True})
@@ -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)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes