WuttaWeb 0.19.0__tar.gz → 0.19.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 (202) hide show
  1. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/CHANGELOG.md +20 -0
  2. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/PKG-INFO +3 -2
  3. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/pyproject.toml +3 -2
  4. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/forms/schema.py +57 -19
  5. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/forms/widgets.py +75 -7
  6. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/grids/base.py +112 -27
  7. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/progress.py +4 -1
  8. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/base.mako +1 -1
  9. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/batch/view.mako +1 -14
  10. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/view.mako +15 -0
  11. wuttaweb-0.19.0/tests/util.py → wuttaweb-0.19.1/src/wuttaweb/testing.py +26 -39
  12. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/util.py +33 -6
  13. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/batch.py +11 -1
  14. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/master.py +84 -1
  15. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/db/test_continuum.py +1 -2
  16. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/forms/test_schema.py +127 -93
  17. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/forms/test_widgets.py +162 -93
  18. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/grids/test_base.py +96 -1
  19. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/grids/test_filters.py +1 -1
  20. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_auth.py +1 -1
  21. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_handler.py +1 -1
  22. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_menus.py +1 -1
  23. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_progress.py +5 -2
  24. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_util.py +17 -6
  25. wuttaweb-0.19.1/tests/util.py +40 -0
  26. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test___init__.py +1 -1
  27. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_auth.py +1 -1
  28. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_base.py +1 -1
  29. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_batch.py +7 -1
  30. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_common.py +1 -1
  31. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_email.py +1 -1
  32. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_essential.py +1 -1
  33. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_master.py +19 -1
  34. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_people.py +1 -1
  35. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_progress.py +1 -1
  36. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_roles.py +1 -1
  37. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_settings.py +1 -1
  38. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_upgrades.py +1 -1
  39. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/test_users.py +1 -1
  40. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/.gitignore +0 -0
  41. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/COPYING.txt +0 -0
  42. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/README.md +0 -0
  43. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/Makefile +0 -0
  44. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/_static/.keepme +0 -0
  45. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.app.rst +0 -0
  46. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.auth.rst +0 -0
  47. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.cli.rst +0 -0
  48. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.cli.webapp.rst +0 -0
  49. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.conf.rst +0 -0
  50. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.db.continuum.rst +0 -0
  51. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.db.rst +0 -0
  52. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.db.sess.rst +0 -0
  53. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.emails.rst +0 -0
  54. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.forms.base.rst +0 -0
  55. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.forms.rst +0 -0
  56. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.forms.schema.rst +0 -0
  57. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  58. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.grids.base.rst +0 -0
  59. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.grids.filters.rst +0 -0
  60. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.grids.rst +0 -0
  61. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.handler.rst +0 -0
  62. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.helpers.rst +0 -0
  63. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.menus.rst +0 -0
  64. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.progress.rst +0 -0
  65. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.rst +0 -0
  66. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.static.rst +0 -0
  67. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.subscribers.rst +0 -0
  68. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.util.rst +0 -0
  69. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.auth.rst +0 -0
  70. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.base.rst +0 -0
  71. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.batch.rst +0 -0
  72. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.common.rst +0 -0
  73. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.email.rst +0 -0
  74. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.essential.rst +0 -0
  75. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.master.rst +0 -0
  76. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.people.rst +0 -0
  77. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.progress.rst +0 -0
  78. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.roles.rst +0 -0
  79. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.rst +0 -0
  80. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.settings.rst +0 -0
  81. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  82. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/api/wuttaweb.views.users.rst +0 -0
  83. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/conf.py +0 -0
  84. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/glossary.rst +0 -0
  85. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/index.rst +0 -0
  86. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/make.bat +0 -0
  87. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/cli/builtin.rst +0 -0
  88. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/cli/index.rst +0 -0
  89. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/templates/base.rst +0 -0
  90. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/templates/index.rst +0 -0
  91. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/templates/lookup.rst +0 -0
  92. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/docs/narr/templates/overview.rst +0 -0
  93. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/__init__.py +0 -0
  94. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/_version.py +0 -0
  95. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/app.py +0 -0
  96. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/auth.py +0 -0
  97. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/cli/__init__.py +0 -0
  98. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/cli/webapp.py +0 -0
  99. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/conf.py +0 -0
  100. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/db/__init__.py +0 -0
  101. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/db/continuum.py +0 -0
  102. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/db/sess.py +0 -0
  103. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
  104. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
  105. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/emails.py +0 -0
  106. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/forms/__init__.py +0 -0
  107. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/forms/base.py +0 -0
  108. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/grids/__init__.py +0 -0
  109. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/grids/filters.py +0 -0
  110. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/handler.py +0 -0
  111. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/helpers.py +0 -0
  112. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/menus.py +0 -0
  113. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/static/__init__.py +0 -0
  114. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/static/img/favicon.ico +0 -0
  115. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/static/img/logo.png +0 -0
  116. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/static/img/testing.png +0 -0
  117. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/subscribers.py +0 -0
  118. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  119. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  120. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  121. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/auth/login.mako +0 -0
  122. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/base_meta.mako +0 -0
  123. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/configure.mako +0 -0
  124. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  125. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  126. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  127. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  128. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  129. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  130. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/password.pt +0 -0
  131. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  132. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  133. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
  134. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  135. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  136. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  137. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  138. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  139. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/select.pt +0 -0
  140. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  141. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  142. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/email/settings/view.mako +0 -0
  143. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/forbidden.mako +0 -0
  144. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/form.mako +0 -0
  145. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  146. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  147. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  148. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/home.mako +0 -0
  149. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/configure.mako +0 -0
  150. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/create.mako +0 -0
  151. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/delete.mako +0 -0
  152. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/edit.mako +0 -0
  153. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/form.mako +0 -0
  154. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/master/index.mako +0 -0
  155. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/notfound.mako +0 -0
  156. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/page.mako +0 -0
  157. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  158. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/progress.mako +0 -0
  159. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/setup.mako +0 -0
  160. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/upgrade.mako +0 -0
  161. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  162. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  163. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/templates/wutta-components.mako +0 -0
  164. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/__init__.py +0 -0
  165. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/auth.py +0 -0
  166. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/base.py +0 -0
  167. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/common.py +0 -0
  168. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/email.py +0 -0
  169. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/essential.py +0 -0
  170. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/people.py +0 -0
  171. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/progress.py +0 -0
  172. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/roles.py +0 -0
  173. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/settings.py +0 -0
  174. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/upgrades.py +0 -0
  175. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/src/wuttaweb/views/users.py +0 -0
  176. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tasks.py +0 -0
  177. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/__init__.py +0 -0
  178. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/cli/__init__.py +0 -0
  179. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/cli/test_webapp.py +0 -0
  180. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/db/__init__.py +0 -0
  181. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/forms/__init__.py +0 -0
  182. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/forms/test_base.py +0 -0
  183. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/grids/__init__.py +0 -0
  184. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  185. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  186. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_oruga.js +0 -0
  187. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_oruga_bulma.css +0 -0
  188. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_oruga_bulma.js +0 -0
  189. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_vue.js +0 -0
  190. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/bb_vue_fontawesome.js +0 -0
  191. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/buefy.css +0 -0
  192. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/buefy.js +0 -0
  193. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/fontawesome.js +0 -0
  194. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/vue.js +0 -0
  195. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/libcache/vue_resource.js +0 -0
  196. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_app.py +0 -0
  197. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_emails.py +0 -0
  198. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_helpers.py +0 -0
  199. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_static.py +0 -0
  200. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/test_subscribers.py +0 -0
  201. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tests/views/__init__.py +0 -0
  202. {wuttaweb-0.19.0 → wuttaweb-0.19.1}/tox.ini +0 -0
@@ -5,6 +5,26 @@ 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.19.1 (2025-01-06)
9
+
10
+ ### Fix
11
+
12
+ - improve built-in grid renderer logic
13
+ - allow session injection for ObjectRef constructor
14
+ - improve rendering for batch row status
15
+ - add basic support for row grid "view" action links
16
+ - add "xref buttons" tool panel for master view
17
+ - add WuttaQuantity schema type, widget
18
+ - remove `session` param from some form schema, widget classes
19
+ - add grid renderers for bool, currency, quantity
20
+ - use proper bulma styles for markdown content
21
+ - use span element for readonly money field widget render
22
+ - include grid filters for all column properties of model class
23
+ - use app handler to render error string, when progress fails
24
+ - add schema node type, widget for "money" (currency) fields
25
+ - exclude FK fields by default, for model forms
26
+ - fix style for header title text
27
+
8
28
  ## v0.19.0 (2024-12-23)
9
29
 
10
30
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.19.0
3
+ Version: 0.19.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
@@ -37,9 +37,10 @@ Requires-Dist: pyramid-fanstatic
37
37
  Requires-Dist: pyramid-mako
38
38
  Requires-Dist: pyramid-tm
39
39
  Requires-Dist: pyramid>=2
40
+ Requires-Dist: sqlalchemy-utils
40
41
  Requires-Dist: waitress
41
42
  Requires-Dist: webhelpers2
42
- Requires-Dist: wuttjamaican[db]>=0.19.0
43
+ Requires-Dist: wuttjamaican[db]>=0.19.2
43
44
  Requires-Dist: zope-sqlalchemy>=1.5
44
45
  Provides-Extra: continuum
45
46
  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.19.0"
9
+ version = "0.19.1"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -42,9 +42,10 @@ dependencies = [
42
42
  "pyramid_fanstatic",
43
43
  "pyramid_mako",
44
44
  "pyramid_tm",
45
+ "SQLAlchemy-Utils",
45
46
  "waitress",
46
47
  "WebHelpers2",
47
- "WuttJamaican[db]>=0.19.0",
48
+ "WuttJamaican[db]>=0.19.2",
48
49
  "zope.sqlalchemy>=1.5",
49
50
  ]
50
51
 
@@ -155,25 +155,63 @@ class WuttaEnum(colander.Enum):
155
155
  return widgets.SelectWidget(**kwargs)
156
156
 
157
157
 
158
+ class WuttaMoney(colander.Money):
159
+ """
160
+ Custom schema type for "money" fields.
161
+
162
+ This is a subclass of :class:`colander:colander.Money`, but uses
163
+ the custom :class:`~wuttaweb.forms.widgets.WuttaMoneyInputWidget`
164
+ by default.
165
+
166
+ :param request: Current :term:`request` object.
167
+ """
168
+
169
+ def __init__(self, request, *args, **kwargs):
170
+ super().__init__(*args, **kwargs)
171
+ self.request = request
172
+ self.config = self.request.wutta_config
173
+ self.app = self.config.get_app()
174
+
175
+ def widget_maker(self, **kwargs):
176
+ """ """
177
+ return widgets.WuttaMoneyInputWidget(self.request, **kwargs)
178
+
179
+
180
+ class WuttaQuantity(colander.Decimal):
181
+ """
182
+ Custom schema type for "quantity" fields.
183
+
184
+ This is a subclass of :class:`colander:colander.Decimal` but uses
185
+ :class:`~wuttaweb.forms.widgets.WuttaQuantityWidget` by default.
186
+
187
+ :param request: Current :term:`request` object.
188
+ """
189
+
190
+ def __init__(self, request, *args, **kwargs):
191
+ super().__init__(*args, **kwargs)
192
+ self.request = request
193
+ self.config = self.request.wutta_config
194
+ self.app = self.config.get_app()
195
+
196
+ def widget_maker(self, **kwargs):
197
+ """ """
198
+ return widgets.WuttaQuantityWidget(self.request, **kwargs)
199
+
200
+
158
201
  class WuttaSet(colander.Set):
159
202
  """
160
203
  Custom schema type for :class:`python:set` fields.
161
204
 
162
- This is a subclass of :class:`colander.Set`, but adds
163
- Wutta-related params to the constructor.
205
+ This is a subclass of :class:`colander.Set`.
164
206
 
165
207
  :param request: Current :term:`request` object.
166
-
167
- :param session: Optional :term:`db session` to use instead of
168
- :class:`wuttaweb.db.sess.Session`.
169
208
  """
170
209
 
171
- def __init__(self, request, session=None):
210
+ def __init__(self, request):
172
211
  super().__init__()
173
212
  self.request = request
174
213
  self.config = self.request.wutta_config
175
214
  self.app = self.config.get_app()
176
- self.session = session or Session()
177
215
 
178
216
 
179
217
  class ObjectRef(colander.SchemaType):
@@ -209,16 +247,16 @@ class ObjectRef(colander.SchemaType):
209
247
  self,
210
248
  request,
211
249
  empty_option=None,
212
- session=None,
213
250
  *args,
214
251
  **kwargs,
215
252
  ):
253
+ # nb. allow session injection for tests
254
+ self.session = kwargs.pop('session', Session())
216
255
  super().__init__(*args, **kwargs)
217
256
  self.request = request
218
257
  self.config = self.request.wutta_config
219
258
  self.app = self.config.get_app()
220
259
  self.model_instance = None
221
- self.session = session or Session()
222
260
 
223
261
  if empty_option:
224
262
  if empty_option is True:
@@ -450,7 +488,7 @@ class RoleRefs(WuttaSet):
450
488
  :returns: Instance of
451
489
  :class:`~wuttaweb.forms.widgets.RoleRefsWidget`.
452
490
  """
453
- kwargs.setdefault('session', self.session)
491
+ session = kwargs.setdefault('session', Session())
454
492
 
455
493
  if 'values' not in kwargs:
456
494
  model = self.app.model
@@ -458,20 +496,20 @@ class RoleRefs(WuttaSet):
458
496
 
459
497
  # avoid built-ins which cannot be assigned to users
460
498
  avoid = {
461
- auth.get_role_authenticated(self.session),
462
- auth.get_role_anonymous(self.session),
499
+ auth.get_role_authenticated(session),
500
+ auth.get_role_anonymous(session),
463
501
  }
464
502
  avoid = set([role.uuid for role in avoid])
465
503
 
466
504
  # also avoid admin unless current user is root
467
505
  if not self.request.is_root:
468
- avoid.add(auth.get_role_administrator(self.session).uuid)
506
+ avoid.add(auth.get_role_administrator(session).uuid)
469
507
 
470
508
  # everything else can be (un)assigned for users
471
- roles = self.session.query(model.Role)\
472
- .filter(~model.Role.uuid.in_(avoid))\
473
- .order_by(model.Role.name)\
474
- .all()
509
+ roles = session.query(model.Role)\
510
+ .filter(~model.Role.uuid.in_(avoid))\
511
+ .order_by(model.Role.name)\
512
+ .all()
475
513
  values = [(role.uuid.hex, role.name) for role in roles]
476
514
  kwargs['values'] = values
477
515
 
@@ -496,7 +534,7 @@ class UserRefs(WuttaSet):
496
534
  :returns: Instance of
497
535
  :class:`~wuttaweb.forms.widgets.UserRefsWidget`.
498
536
  """
499
- kwargs.setdefault('session', self.session)
537
+ kwargs.setdefault('session', Session())
500
538
  return widgets.UserRefsWidget(self.request, **kwargs)
501
539
 
502
540
 
@@ -526,7 +564,7 @@ class Permissions(WuttaSet):
526
564
  :returns: Instance of
527
565
  :class:`~wuttaweb.forms.widgets.PermissionsWidget`.
528
566
  """
529
- kwargs.setdefault('session', self.session)
567
+ kwargs.setdefault('session', Session())
530
568
  kwargs.setdefault('permissions', self.permissions)
531
569
 
532
570
  if 'values' not in kwargs:
@@ -41,6 +41,7 @@ in the namespace:
41
41
  """
42
42
 
43
43
  import datetime
44
+ import decimal
44
45
  import os
45
46
 
46
47
  import colander
@@ -135,26 +136,21 @@ class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
135
136
  Custom widget for :class:`python:set` fields.
136
137
 
137
138
  This is a subclass of
138
- :class:`deform:deform.widget.CheckboxChoiceWidget`, but adds
139
- Wutta-related params to the constructor.
139
+ :class:`deform:deform.widget.CheckboxChoiceWidget`.
140
140
 
141
141
  :param request: Current :term:`request` object.
142
142
 
143
- :param session: Optional :term:`db session` to use instead of
144
- :class:`wuttaweb.db.sess.Session`.
145
-
146
143
  It uses these Deform templates:
147
144
 
148
145
  * ``checkbox_choice``
149
146
  * ``readonly/checkbox_choice``
150
147
  """
151
148
 
152
- def __init__(self, request, session=None, *args, **kwargs):
149
+ def __init__(self, request, *args, **kwargs):
153
150
  super().__init__(*args, **kwargs)
154
151
  self.request = request
155
152
  self.config = self.request.wutta_config
156
153
  self.app = self.config.get_app()
157
- self.session = session or Session()
158
154
 
159
155
 
160
156
  class WuttaDateTimeWidget(DateTimeInputWidget):
@@ -194,6 +190,78 @@ class WuttaDateTimeWidget(DateTimeInputWidget):
194
190
  return super().serialize(field, cstruct, **kw)
195
191
 
196
192
 
193
+ class WuttaMoneyInputWidget(MoneyInputWidget):
194
+ """
195
+ Custom widget for "money" fields. This is used by default for
196
+ :class:`~wuttaweb.forms.schema.WuttaMoney` type nodes.
197
+
198
+ The main purpose of this widget is to leverage
199
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_currency()`
200
+ for the readonly display.
201
+
202
+ This is a subclass of
203
+ :class:`deform:deform.widget.MoneyInputWidget` and uses these
204
+ Deform templates:
205
+
206
+ * ``moneyinput``
207
+
208
+ :param request: Current :term:`request` object.
209
+ """
210
+
211
+ def __init__(self, request, *args, **kwargs):
212
+ super().__init__(*args, **kwargs)
213
+ self.request = request
214
+ self.config = self.request.wutta_config
215
+ self.app = self.config.get_app()
216
+
217
+ def serialize(self, field, cstruct, **kw):
218
+ """ """
219
+ readonly = kw.get('readonly', self.readonly)
220
+ if readonly:
221
+ if cstruct in (colander.null, None):
222
+ return HTML.tag('span')
223
+ cstruct = decimal.Decimal(cstruct)
224
+ return HTML.tag('span', c=[self.app.render_currency(cstruct)])
225
+
226
+ return super().serialize(field, cstruct, **kw)
227
+
228
+
229
+ class WuttaQuantityWidget(TextInputWidget):
230
+ """
231
+ Custom widget for "quantity" fields. This is used by default for
232
+ :class:`~wuttaweb.forms.schema.WuttaQuantity` type nodes.
233
+
234
+ The main purpose of this widget is to leverage
235
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_quantity()`
236
+ for the readonly display.
237
+
238
+ This is a subclass of
239
+ :class:`deform:deform.widget.TextInputWidget` and uses these
240
+ Deform templates:
241
+
242
+ * ``textinput``
243
+
244
+ :param request: Current :term:`request` object.
245
+ """
246
+
247
+ def __init__(self, request, *args, **kwargs):
248
+ super().__init__(*args, **kwargs)
249
+ self.request = request
250
+ self.config = self.request.wutta_config
251
+ self.app = self.config.get_app()
252
+
253
+ def serialize(self, field, cstruct, **kw):
254
+ """ """
255
+ readonly = kw.get('readonly', self.readonly)
256
+ if readonly:
257
+ if cstruct in (colander.null, None):
258
+ return HTML.tag('span')
259
+ cstruct = decimal.Decimal(cstruct)
260
+ return HTML.tag('span', c=[self.app.render_quantity(cstruct)])
261
+
262
+ return super().serialize(field, cstruct, **kw)
263
+
264
+
197
265
  class FileDownloadWidget(Widget):
198
266
  """
199
267
  Widget for use with :class:`~wuttaweb.forms.schema.FileDownload`
@@ -32,6 +32,7 @@ from collections import namedtuple, OrderedDict
32
32
 
33
33
  import sqlalchemy as sa
34
34
  from sqlalchemy import orm
35
+ from sqlalchemy_utils import get_columns
35
36
 
36
37
  import paginate
37
38
  from paginate_sqlalchemy import SqlalchemyOrmPage
@@ -115,7 +116,8 @@ class Grid:
115
116
 
116
117
  Dict of column (cell) value renderer overrides.
117
118
 
118
- See also :meth:`set_renderer()`.
119
+ See also :meth:`set_renderer()` and
120
+ :meth:`set_default_renderers()`.
119
121
 
120
122
  .. attribute:: row_class
121
123
 
@@ -387,7 +389,6 @@ class Grid:
387
389
  self.key = key
388
390
  self.data = data
389
391
  self.labels = labels or {}
390
- self.renderers = renderers or {}
391
392
  self.row_class = row_class
392
393
  self.actions = actions or []
393
394
  self.linked_columns = linked_columns or []
@@ -397,6 +398,10 @@ class Grid:
397
398
  self.app = self.config.get_app()
398
399
 
399
400
  self.set_columns(columns or self.get_columns())
401
+ self.renderers = {}
402
+ if renderers:
403
+ for key, val in renderers.items():
404
+ self.set_renderer(key, val)
400
405
  self.set_default_renderers()
401
406
  self.set_tools(tools)
402
407
 
@@ -591,8 +596,29 @@ class Grid:
591
596
  grid = Grid(request, columns=['foo', 'bar'])
592
597
  grid.set_renderer('foo', render_foo)
593
598
 
599
+ For convenience, in lieu of a renderer callable, you may
600
+ specify one of the following strings, which will be
601
+ interpreted as a built-in renderer callable, as shown below:
602
+
603
+ * ``'batch_id'`` -> :meth:`render_batch_id()`
604
+ * ``'boolean'`` -> :meth:`render_boolean()`
605
+ * ``'currency'`` -> :meth:`render_currency()`
606
+ * ``'datetime'`` -> :meth:`render_datetime()`
607
+ * ``'quantity'`` -> :meth:`render_quantity()`
608
+
594
609
  Renderer overrides are tracked via :attr:`renderers`.
595
610
  """
611
+ builtins = {
612
+ 'batch_id': self.render_batch_id,
613
+ 'boolean': self.render_boolean,
614
+ 'currency': self.render_currency,
615
+ 'datetime': self.render_datetime,
616
+ 'quantity': self.render_quantity,
617
+ }
618
+
619
+ if renderer in builtins:
620
+ renderer = builtins[renderer]
621
+
596
622
  if kwargs:
597
623
  renderer = functools.partial(renderer, **kwargs)
598
624
  self.renderers[key] = renderer
@@ -601,15 +627,18 @@ class Grid:
601
627
  """
602
628
  Set default column value renderers, where applicable.
603
629
 
604
- This will add new entries to :attr:`renderers` for columns
605
- whose data type implies a default renderer should be used.
606
- This is generally only possible if :attr:`model_class` is set
607
- to a valid SQLAlchemy mapped class.
630
+ This is called automatically from the class constructor. It
631
+ will add new entries to :attr:`renderers` for columns whose
632
+ data type implies a default renderer. This is only possible
633
+ if :attr:`model_class` is set to a SQLAlchemy mapped class.
608
634
 
609
- This (for now?) only looks for
610
- :class:`sqlalchemy:sqlalchemy.types.DateTime` columns and if
611
- any are found, they are configured to use
612
- :meth:`render_datetime()`.
635
+ This only looks for a couple of data types, and configures as
636
+ follows:
637
+
638
+ * :class:`sqlalchemy:sqlalchemy.types.Boolean` ->
639
+ :meth:`render_boolean()`
640
+ * :class:`sqlalchemy:sqlalchemy.types.DateTime` ->
641
+ :meth:`render_datetime()`
613
642
  """
614
643
  if not self.model_class:
615
644
  return
@@ -625,6 +654,8 @@ class Grid:
625
654
  column = prop.columns[0]
626
655
  if isinstance(column.type, sa.DateTime):
627
656
  self.set_renderer(key, self.render_datetime)
657
+ elif isinstance(column.type, sa.Boolean):
658
+ self.set_renderer(key, self.render_boolean)
628
659
 
629
660
  def set_link(self, key, link=True):
630
661
  """
@@ -1116,19 +1147,16 @@ class Grid:
1116
1147
  filters = filters or {}
1117
1148
 
1118
1149
  if self.model_class:
1119
- # TODO: i tried using self.get_model_columns() here but in
1120
- # many cases that will be too aggressive. however it is
1121
- # often the case that the *grid* columns are a subset of
1122
- # the unerlying *table* columns. so until a better way
1123
- # is found, we choose "too few" instead of "too many"
1124
- # filters here. surely must improve it at some point.
1125
- for key in self.columns:
1126
- if key in filters:
1150
+ # nb. i first tried self.get_model_columns() but my notes
1151
+ # say that was too aggressive in many cases. then i tried
1152
+ # using the *subset* of self.columns, just the ones which
1153
+ # corresponded to a property on the model class. and now
1154
+ # i am using sa-utils to give the "true" column list..
1155
+ for col in get_columns(self.model_class):
1156
+ if col.key in filters:
1127
1157
  continue
1128
- prop = getattr(self.model_class, key, None)
1129
- if (prop and hasattr(prop, 'property')
1130
- and isinstance(prop.property, orm.ColumnProperty)):
1131
- filters[prop.key] = self.make_filter(prop)
1158
+ prop = getattr(self.model_class, col.key)
1159
+ filters[prop.key] = self.make_filter(prop)
1132
1160
 
1133
1161
  return filters
1134
1162
 
@@ -1755,23 +1783,80 @@ class Grid:
1755
1783
  # rendering methods
1756
1784
  ##############################
1757
1785
 
1786
+ def render_batch_id(self, obj, key, value):
1787
+ """
1788
+ Column renderer for batch ID values.
1789
+
1790
+ This is not used automatically but you can use it explicitly::
1791
+
1792
+ grid.set_renderer('foo', 'batch_id')
1793
+ """
1794
+ if value is None:
1795
+ return ""
1796
+
1797
+ batch_id = int(value)
1798
+ return f'{batch_id:08d}'
1799
+
1800
+ def render_boolean(self, obj, key, value):
1801
+ """
1802
+ Column renderer for boolean values.
1803
+
1804
+ This calls
1805
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_boolean()`
1806
+ for the return value.
1807
+
1808
+ This may be used automatically per
1809
+ :meth:`set_default_renderers()` or you can use it explicitly::
1810
+
1811
+ grid.set_renderer('foo', grid.render_boolean)
1812
+ """
1813
+ return self.app.render_boolean(value)
1814
+
1815
+ def render_currency(self, obj, key, value, **kwargs):
1816
+ """
1817
+ Column renderer for currency values.
1818
+
1819
+ This calls
1820
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_currency()`
1821
+ for the return value.
1822
+
1823
+ This is not used automatically but you can use it explicitly::
1824
+
1825
+ grid.set_renderer('foo', 'currency')
1826
+ grid.set_renderer('foo', 'currency', scale=4)
1827
+ """
1828
+ return self.app.render_currency(value, **kwargs)
1829
+
1758
1830
  def render_datetime(self, obj, key, value):
1759
1831
  """
1760
- Default cell value renderer for
1761
- :class:`sqlalchemy:sqlalchemy.types.DateTime` columns, which
1762
- calls
1832
+ Column renderer for :class:`python:datetime.datetime` values.
1833
+
1834
+ This calls
1763
1835
  :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_datetime()`
1764
1836
  for the return value.
1765
1837
 
1766
1838
  This may be used automatically per
1767
- :meth:`set_default_renderers()` or you can use it explicitly
1768
- for any :class:`python:datetime.datetime` column with::
1839
+ :meth:`set_default_renderers()` or you can use it explicitly::
1769
1840
 
1770
1841
  grid.set_renderer('foo', grid.render_datetime)
1771
1842
  """
1772
1843
  dt = getattr(obj, key)
1773
1844
  return self.app.render_datetime(dt)
1774
1845
 
1846
+ def render_quantity(self, obj, key, value):
1847
+ """
1848
+ Column renderer for quantity values.
1849
+
1850
+ This calls
1851
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_quantity()`
1852
+ for the return value.
1853
+
1854
+ This is not used automatically but you can use it explicitly::
1855
+
1856
+ grid.set_renderer('foo', grid.render_quantity)
1857
+ """
1858
+ return self.app.render_quantity(value)
1859
+
1775
1860
  def render_table_element(
1776
1861
  self,
1777
1862
  form=None,
@@ -92,6 +92,9 @@ class SessionProgress(ProgressBase):
92
92
  """
93
93
 
94
94
  def __init__(self, request, key, success_msg=None, success_url=None, error_url=None):
95
+ self.request = request
96
+ self.config = self.request.wutta_config
97
+ self.app = self.config.get_app()
95
98
  self.key = key
96
99
  self.success_msg = success_msg
97
100
  self.success_url = success_url
@@ -137,7 +140,7 @@ class SessionProgress(ProgressBase):
137
140
  """
138
141
  self.session.load()
139
142
  self.session['error'] = True
140
- self.session['error_msg'] = str(error)
143
+ self.session['error_msg'] = self.app.render_error(error)
141
144
  self.session['error_url'] = error_url or self.error_url
142
145
  self.session.save()
143
146
 
@@ -155,7 +155,7 @@
155
155
  }
156
156
 
157
157
  #content-title h1 {
158
- max-width: 50%;
158
+ max-width: 80%;
159
159
  overflow: hidden;
160
160
  padding-left: 0.5rem;
161
161
  text-overflow: ellipsis;
@@ -1,19 +1,6 @@
1
1
  ## -*- coding: utf-8; -*-
2
2
  <%inherit file="/master/view.mako" />
3
3
 
4
- <%def name="extra_styles()">
5
- ${parent.extra_styles()}
6
- <style>
7
-
8
- ## TODO: should we do something like this site-wide?
9
- ## (so far this is the only place we use markdown)
10
- .markdown p {
11
- margin-bottom: 1.5rem;
12
- }
13
-
14
- </style>
15
- </%def>
16
-
17
4
  <%def name="tool_panels()">
18
5
  ${parent.tool_panels()}
19
6
  ${self.tool_panel_execution()}
@@ -65,7 +52,7 @@
65
52
  <p class="block has-text-weight-bold">
66
53
  What will happen when this batch is executed?
67
54
  </p>
68
- <div class="markdown">
55
+ <div class="content">
69
56
  ${execution_described|n}
70
57
  </div>
71
58
  ${h.form(master.get_action_url('execute', batch), ref='executeForm')}
@@ -33,6 +33,21 @@
33
33
  % endif
34
34
  </%def>
35
35
 
36
+ <%def name="tool_panels()">
37
+ ${parent.tool_panels()}
38
+ ${self.tool_panel_xref()}
39
+ </%def>
40
+
41
+ <%def name="tool_panel_xref()">
42
+ % if xref_buttons:
43
+ <wutta-tool-panel heading="Cross-Reference">
44
+ % for button in xref_buttons:
45
+ ${button}
46
+ % endfor
47
+ </wutta-tool-panel>
48
+ % endif
49
+ </%def>
50
+
36
51
  <%def name="render_vue_templates()">
37
52
  ${parent.render_vue_templates()}
38
53
  % if master.has_rows: