accrete 0.0.26__tar.gz → 0.0.28__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 (203) hide show
  1. {accrete-0.0.26 → accrete-0.0.28}/PKG-INFO +2 -1
  2. accrete-0.0.28/accrete/contrib/sequence/migrations/0002_alter_sequence_name.py +18 -0
  3. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/models.py +1 -1
  4. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/queries.py +2 -2
  5. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/tasks.py +10 -3
  6. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/__init__.py +2 -1
  7. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/components.py +29 -0
  8. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/context.py +16 -11
  9. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/filter.py +24 -0
  10. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/querystring.py +1 -1
  11. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/js/filter.js +20 -2
  12. accrete-0.0.28/accrete/contrib/ui/templates/ui/detail.html +35 -0
  13. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/layout.html +5 -3
  14. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/list.html +0 -5
  15. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/filter.html +1 -0
  16. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/header.html +3 -1
  17. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/table.html +3 -6
  18. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templatetags/accrete_ui.py +12 -0
  19. accrete-0.0.28/accrete/contrib/user/forms.py +119 -0
  20. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/templates/user/accrete_navbar_end.html +5 -1
  21. accrete-0.0.28/accrete/contrib/user/templates/user/change_email.html +29 -0
  22. accrete-0.0.28/accrete/contrib/user/templates/user/change_password.html +40 -0
  23. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/urls.py +3 -2
  24. accrete-0.0.28/accrete/contrib/user/views.py +105 -0
  25. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/forms.py +6 -2
  26. {accrete-0.0.26 → accrete-0.0.28}/accrete/forms.py +6 -5
  27. accrete-0.0.28/accrete/utils/__init__.py +0 -0
  28. {accrete-0.0.26 → accrete-0.0.28}/accrete/utils/dates.py +10 -2
  29. {accrete-0.0.26 → accrete-0.0.28}/pyproject.toml +3 -2
  30. accrete-0.0.26/accrete/contrib/ui/static/js/filter_old.js +0 -734
  31. accrete-0.0.26/accrete/contrib/ui/templates/ui/detail.html +0 -17
  32. accrete-0.0.26/accrete/contrib/user/forms.py +0 -28
  33. accrete-0.0.26/accrete/contrib/user/views.py +0 -60
  34. {accrete-0.0.26 → accrete-0.0.28}/.gitignore +0 -0
  35. {accrete-0.0.26 → accrete-0.0.28}/LICENSE +0 -0
  36. {accrete-0.0.26 → accrete-0.0.28}/README.md +0 -0
  37. {accrete-0.0.26 → accrete-0.0.28}/accrete/__init__.py +0 -0
  38. {accrete-0.0.26 → accrete-0.0.28}/accrete/admin.py +0 -0
  39. {accrete-0.0.26 → accrete-0.0.28}/accrete/apps.py +0 -0
  40. {accrete-0.0.26 → accrete-0.0.28}/accrete/config.py +0 -0
  41. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/__init__.py +0 -0
  42. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/__init__.py +0 -0
  43. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/admin.py +0 -0
  44. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/apps.py +0 -0
  45. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/forms.py +0 -0
  46. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/migrations/0001_initial.py +0 -0
  47. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/migrations/__init__.py +0 -0
  48. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/tests.py +0 -0
  49. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/sequence/views.py +0 -0
  50. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/__init__.py +0 -0
  51. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/admin.py +0 -0
  52. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/apps.py +0 -0
  53. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/forms.py +0 -0
  54. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/migrations/0001_initial.py +0 -0
  55. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/migrations/__init__.py +0 -0
  56. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/models.py +0 -0
  57. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/tests.py +0 -0
  58. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/system_mail/views.py +0 -0
  59. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/admin.py +0 -0
  60. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/apps.py +0 -0
  61. /accrete-0.0.26/accrete/contrib/ui/migrations/__init__.py → /accrete-0.0.28/accrete/contrib/ui/elements.py +0 -0
  62. {accrete-0.0.26/accrete/contrib/ui/templatetags → accrete-0.0.28/accrete/contrib/ui/migrations}/__init__.py +0 -0
  63. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/LICENSE +0 -0
  64. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/README.md +0 -0
  65. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/bulma.sass +0 -0
  66. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css +0 -0
  67. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma-rtl.css.map +0 -0
  68. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma-rtl.min.css +0 -0
  69. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma.css +0 -0
  70. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma.css.map +0 -0
  71. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/css/bulma.min.css +0 -0
  72. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/package.json +0 -0
  73. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/base/_all.sass +0 -0
  74. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/base/animations.sass +0 -0
  75. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/base/generic.sass +0 -0
  76. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/base/helpers.sass +0 -0
  77. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/base/minireset.sass +0 -0
  78. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/_all.sass +0 -0
  79. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/breadcrumb.sass +0 -0
  80. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/card.sass +0 -0
  81. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/dropdown.sass +0 -0
  82. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/level.sass +0 -0
  83. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/media.sass +0 -0
  84. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/menu.sass +0 -0
  85. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/message.sass +0 -0
  86. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/modal.sass +0 -0
  87. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/navbar.sass +0 -0
  88. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/pagination.sass +0 -0
  89. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/panel.sass +0 -0
  90. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/components/tabs.sass +0 -0
  91. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/_all.sass +0 -0
  92. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/box.sass +0 -0
  93. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/button.sass +0 -0
  94. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/container.sass +0 -0
  95. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/content.sass +0 -0
  96. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/form.sass +0 -0
  97. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/icon.sass +0 -0
  98. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/image.sass +0 -0
  99. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/notification.sass +0 -0
  100. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/other.sass +0 -0
  101. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/progress.sass +0 -0
  102. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/table.sass +0 -0
  103. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/tag.sass +0 -0
  104. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/elements/title.sass +0 -0
  105. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/_all.sass +0 -0
  106. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.sass +0 -0
  107. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/file.sass +0 -0
  108. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/input-textarea.sass +0 -0
  109. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/select.sass +0 -0
  110. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/shared.sass +0 -0
  111. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/form/tools.sass +0 -0
  112. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/grid/_all.sass +0 -0
  113. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/grid/columns.sass +0 -0
  114. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/grid/tiles.sass +0 -0
  115. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/_all.sass +0 -0
  116. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/color.sass +0 -0
  117. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/flexbox.sass +0 -0
  118. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/float.sass +0 -0
  119. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/other.sass +0 -0
  120. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/overflow.sass +0 -0
  121. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/position.sass +0 -0
  122. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/spacing.sass +0 -0
  123. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/typography.sass +0 -0
  124. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/helpers/visibility.sass +0 -0
  125. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/layout/_all.sass +0 -0
  126. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/layout/footer.sass +0 -0
  127. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/layout/hero.sass +0 -0
  128. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/layout/section.sass +0 -0
  129. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/_all.sass +0 -0
  130. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/animations.sass +0 -0
  131. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/controls.sass +0 -0
  132. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.sass +0 -0
  133. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/extends.sass +0 -0
  134. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/functions.sass +0 -0
  135. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.sass +0 -0
  136. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/bulma/sass/utilities/mixins.sass +0 -0
  137. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/css/accrete.css +0 -0
  138. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/css/accrete.css.bak +0 -0
  139. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/css/accrete.css.map +0 -0
  140. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/css/accrete.scss +0 -0
  141. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/css/icons.css +0 -0
  142. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/icons/Logo.svg +0 -0
  143. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/icons/accrete.svg +0 -0
  144. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/js/list.js +0 -0
  145. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/js/navbar.js +0 -0
  146. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/static/js/utils.js +0 -0
  147. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/attrs.html +0 -0
  148. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/email.html +0 -0
  149. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/file.html +0 -0
  150. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/input.html +0 -0
  151. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/select.html +0 -0
  152. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/text.html +0 -0
  153. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/django/forms/widgets/textarea.html +0 -0
  154. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/form.html +0 -0
  155. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -0
  156. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/form_modal.html +0 -0
  157. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -0
  158. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -0
  159. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -0
  160. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/table_field.html +0 -0
  161. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -0
  162. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -0
  163. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -0
  164. {accrete-0.0.26/accrete/contrib/user → accrete-0.0.28/accrete/contrib/ui/templatetags}/__init__.py +0 -0
  165. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/tests.py +0 -0
  166. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/urls.py +0 -0
  167. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/ui/views.py +0 -0
  168. {accrete-0.0.26/accrete/contrib/user/migrations → accrete-0.0.28/accrete/contrib/user}/__init__.py +0 -0
  169. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/admin.py +0 -0
  170. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/apps.py +0 -0
  171. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/locale/de/LC_MESSAGES/django.mo +0 -0
  172. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/locale/de/LC_MESSAGES/django.po +0 -0
  173. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/middleware.py +0 -0
  174. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/migrations/0001_initial.py +0 -0
  175. {accrete-0.0.26/accrete/contrib/user_registration → accrete-0.0.28/accrete/contrib/user/migrations}/__init__.py +0 -0
  176. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/models.py +0 -0
  177. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/templates/user/login.html +0 -0
  178. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/templates/user/user_detail.html +0 -0
  179. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/templates/user/user_form.html +0 -0
  180. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user/tests.py +0 -0
  181. {accrete-0.0.26/accrete/contrib/user_registration/migrations → accrete-0.0.28/accrete/contrib/user_registration}/__init__.py +0 -0
  182. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/admin.py +0 -0
  183. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/apps.py +0 -0
  184. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/config.py +0 -0
  185. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/migrations/0001_initial.py +0 -0
  186. {accrete-0.0.26/accrete → accrete-0.0.28/accrete/contrib/user_registration}/migrations/__init__.py +0 -0
  187. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/models.py +0 -0
  188. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/templates/user_registration/mail_templates/confirmation_mail.html +0 -0
  189. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/templates/user_registration/registration.html +0 -0
  190. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/tests.py +0 -0
  191. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/urls.py +0 -0
  192. {accrete-0.0.26 → accrete-0.0.28}/accrete/contrib/user_registration/views.py +0 -0
  193. {accrete-0.0.26 → accrete-0.0.28}/accrete/decorators.py +0 -0
  194. {accrete-0.0.26 → accrete-0.0.28}/accrete/middleware.py +0 -0
  195. {accrete-0.0.26 → accrete-0.0.28}/accrete/migrations/0001_initial.py +0 -0
  196. {accrete-0.0.26 → accrete-0.0.28}/accrete/migrations/0002_initial.py +0 -0
  197. {accrete-0.0.26/accrete/utils → accrete-0.0.28/accrete/migrations}/__init__.py +0 -0
  198. {accrete-0.0.26 → accrete-0.0.28}/accrete/models.py +0 -0
  199. {accrete-0.0.26 → accrete-0.0.28}/accrete/queries.py +0 -0
  200. {accrete-0.0.26 → accrete-0.0.28}/accrete/querystring.py +0 -0
  201. {accrete-0.0.26 → accrete-0.0.28}/accrete/tenant.py +0 -0
  202. {accrete-0.0.26 → accrete-0.0.28}/accrete/tests.py +0 -0
  203. {accrete-0.0.26 → accrete-0.0.28}/accrete/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.26
3
+ Version: 0.0.28
4
4
  Summary: Django Shared Schema Multi Tenant
5
5
  Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
6
6
  License: Copyright (c) 2023 Benedikt Jilek
@@ -35,6 +35,7 @@ Requires-Dist: django>=4.2
35
35
  Provides-Extra: contrib
36
36
  Requires-Dist: celery>=5.3.4; extra == 'contrib'
37
37
  Requires-Dist: django-celery-beat; extra == 'contrib'
38
+ Requires-Dist: sqlalchemy; extra == 'contrib'
38
39
  Provides-Extra: dev
39
40
  Requires-Dist: build; extra == 'dev'
40
41
  Requires-Dist: pytest>=7.0; extra == 'dev'
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.9 on 2024-01-27 07:50
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('sequence', '0001_initial'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='sequence',
15
+ name='name',
16
+ field=models.CharField(max_length=255, verbose_name='Name'),
17
+ ),
18
+ ]
@@ -21,7 +21,7 @@ class Sequence(TenantModel):
21
21
 
22
22
  name = models.CharField(
23
23
  verbose_name=_('Name'),
24
- max_length=50,
24
+ max_length=255,
25
25
  )
26
26
 
27
27
  nextval = models.PositiveBigIntegerField(
@@ -9,11 +9,11 @@ def get_nextval(name, create_if_none=True):
9
9
  tenant = get_tenant()
10
10
  with transaction.atomic():
11
11
  seq = Sequence.objects.filter(
12
- tenant=tenant.id, name=name
12
+ tenant=tenant, name=name
13
13
  ).select_for_update().first()
14
14
 
15
15
  if seq is None and not create_if_none:
16
- raise AttributeError(f'No sequence for {name} found!')
16
+ raise ValueError(f'Sequence "{name}" does not exist.')
17
17
  elif seq is None:
18
18
  seq = Sequence(name=name, tenant=tenant)
19
19
  seq.save()
@@ -1,15 +1,22 @@
1
1
  import logging
2
- from celery import shared_task
3
-
2
+ from celery import shared_task, current_app
3
+ from celery.schedules import crontab
4
4
  from django.core import mail
5
5
  from django.db import transaction
6
6
  from django.utils.html import strip_tags
7
-
8
7
  from .models import SystemMail
9
8
 
10
9
  _logger = logging.getLogger(__name__)
11
10
 
12
11
 
12
+ @current_app.on_after_finalize.connect
13
+ def setup_periodic_tasks(sender, **kwargs):
14
+ sender.add_periodic_task(
15
+ crontab(hour='*', minute='*/5'),
16
+ run_mail_queue.s()
17
+ )
18
+
19
+
13
20
  @shared_task()
14
21
  def run_mail_queue():
15
22
  with transaction.atomic():
@@ -6,9 +6,10 @@ from .context import (
6
6
  )
7
7
  from .components import (
8
8
  ClientAction,
9
+ ActionMethod,
9
10
  BreadCrumb,
10
11
  TableField,
11
12
  TableFieldAlignment,
12
13
  TableFieldType
13
14
  )
14
- from .querystring import load_querystring, build_url_params
15
+ from .querystring import load_querystring, build_querystring
@@ -1,5 +1,17 @@
1
1
  from dataclasses import dataclass, field
2
2
  from enum import Enum
3
+ from django.db.models import Model, QuerySet, Q
4
+
5
+ DEFAULT_PAGINATE_BY = 40
6
+
7
+
8
+ class ActionMethod(Enum):
9
+
10
+ HREF = 'href'
11
+ GET = 'hx-get'
12
+ POST = 'hx-post'
13
+ PUT = 'hx-put'
14
+ DELETE = 'hx-delete'
3
15
 
4
16
 
5
17
  class TableFieldAlignment(Enum):
@@ -37,6 +49,7 @@ class BreadCrumb:
37
49
 
38
50
  name: str
39
51
  url: str
52
+ add_url_params: bool = True
40
53
 
41
54
 
42
55
  @dataclass
@@ -44,8 +57,24 @@ class ClientAction:
44
57
 
45
58
  name: str
46
59
  url: str = ''
60
+ method: ActionMethod = ActionMethod.HREF
47
61
  query_params: str = ''
48
62
  attrs: list[tuple[str, str]] = field(default_factory=list)
49
63
  submit: bool = False
50
64
  form_id: str = 'form'
51
65
  class_list: list[str] = field(default_factory=list)
66
+ add_url_params: bool = True
67
+
68
+ def attrs_str(self):
69
+ return ' '.join(['='.join(attr) for attr in self.attrs])
70
+
71
+
72
+ @dataclass
73
+ class List:
74
+
75
+ queryset: QuerySet
76
+ title: str = None
77
+ paginate_by: int = DEFAULT_PAGINATE_BY
78
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list)
79
+ actions: list[ClientAction] = field(default_factory=list)
80
+ fields: list[TableField] = field(default_factory=list)
@@ -4,9 +4,10 @@ from dataclasses import dataclass, field
4
4
  from django.utils.translation import gettext_lazy as _
5
5
  from django.db.models import Model, QuerySet, Q
6
6
  from django.core.paginator import Paginator
7
+ from django.forms import Form, ModelForm
7
8
  from accrete.querystring import parse_querystring
8
9
 
9
- from .querystring import load_querystring, build_url_params
10
+ from .querystring import load_querystring, build_querystring
10
11
  from .components import ClientAction, BreadCrumb, TableField
11
12
  from .filter import Filter
12
13
 
@@ -42,16 +43,16 @@ class ListContext:
42
43
 
43
44
  order = self.order_by or self.model._meta.ordering
44
45
 
45
- pks = self.model.objects.filter(
46
- parse_querystring(self.model, self.get_params.get('q', '[]'))
47
- ).distinct().values_list('pk', flat=True)
46
+ # pks = self.model.objects.filter(
47
+ # parse_querystring(self.model, self.get_params.get('q', '[]'))
48
+ # ).distinct().values_list('pk', flat=True)
48
49
 
49
50
  queryset = self.model.objects.select_related(
50
51
  *self.select_related
51
52
  ).prefetch_related(
52
53
  *self.prefetch_related
53
54
  ).filter(
54
- Q(pk__in=pks)
55
+ parse_querystring(self.model, self.get_params.get('q', '[]'))
55
56
  ).annotate(
56
57
  **self.get_annotations()
57
58
  ).order_by(
@@ -109,7 +110,7 @@ class ListContext:
109
110
  'default_filter_term': self.default_filter_term,
110
111
  'breadcrumbs': self.breadcrumbs,
111
112
  'querystring': load_querystring(self.get_params),
112
- 'url_params': build_url_params(self.get_params),
113
+ 'url_params': build_querystring(self.get_params),
113
114
  'actions': self.actions,
114
115
  'fields': self.fields
115
116
  }
@@ -208,7 +209,7 @@ class DetailContext:
208
209
  'paginate_by': paginate_by,
209
210
  'detail_pagination': False,
210
211
  'breadcrumbs': self.breadcrumbs,
211
- 'url_params': build_url_params(self.get_params, ['page']),
212
+ 'url_params': build_querystring(self.get_params, ['page']),
212
213
  'actions': self.actions
213
214
  }
214
215
  if self.paginate_by > 0:
@@ -221,6 +222,7 @@ class DetailContext:
221
222
  class FormContext:
222
223
 
223
224
  model: Model | type[Model]
225
+ form: Form | ModelForm
224
226
  get_params: dict
225
227
  title: str = None
226
228
  context: dict = field(default_factory=dict)
@@ -228,6 +230,7 @@ class FormContext:
228
230
  add_default_actions: bool = True
229
231
  discard_url: str = None
230
232
  actions: list[ClientAction] = field(default_factory=list)
233
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list)
231
234
 
232
235
  def get_default_form_actions(self):
233
236
  actions = [
@@ -266,16 +269,18 @@ class FormContext:
266
269
  return self.title
267
270
  try:
268
271
  int(self.model.pk)
269
- return f'Edit {self.model}'
272
+ return _('Edit')
270
273
  except TypeError:
271
- return f'Add {self.model._meta.verbose_name}'
274
+ return _('Add')
272
275
 
273
276
  def dict(self):
274
277
  ctx = {
275
278
  'title': self.get_title(),
279
+ 'form': self.form,
276
280
  'form_id': self.form_id,
277
- 'url_params': build_url_params(self.get_params, ['page']),
278
- 'actions': []
281
+ 'url_params': build_querystring(self.get_params, ['page']),
282
+ 'actions': [],
283
+ 'breadcrumbs': self.breadcrumbs,
279
284
  }
280
285
  if self.add_default_actions:
281
286
  ctx.update({'actions': self.get_default_form_actions()})
@@ -115,6 +115,28 @@ class Filter:
115
115
  ]
116
116
  }
117
117
 
118
+ def get_choice_int_query_term(self, label, param, choices):
119
+ choices = [(choice[0], str(choice[1])) for choice in choices]
120
+ return {
121
+ 'label': str(label),
122
+ 'param': param,
123
+ 'params': [
124
+ {
125
+ 'label': str(self.LABEL_EXACT),
126
+ 'data_type': 'selection',
127
+ 'choices': choices,
128
+ 'param': 'exact'
129
+ },
130
+ {
131
+ 'label': str(self.LABEL_EXACT_NOT),
132
+ 'data_type': 'selection',
133
+ 'choices': choices,
134
+ 'invert': True,
135
+ 'param': 'exact'
136
+ }
137
+ ]
138
+ }
139
+
118
140
  def get_int_query_term(self, label, param):
119
141
  return {
120
142
  'label': str(label),
@@ -258,6 +280,8 @@ class Filter:
258
280
  label, param, self.cast_decimal_places_to_step(field.decimal_places or 0)
259
281
  )
260
282
  elif internal_type in self.query_int_fields:
283
+ if field.choices:
284
+ return self.get_choice_int_query_term(label, param, field.choices)
261
285
  return self.get_int_query_term(label, param)
262
286
  elif internal_type in self.query_boolean_fields:
263
287
  return self.get_boolean_query_term(label, param)
@@ -5,7 +5,7 @@ def load_querystring(get_params: dict) -> list:
5
5
  return json.loads(get_params.get('q', '[]'))
6
6
 
7
7
 
8
- def build_url_params(get_params: dict, extra_params: list[str] = None) -> str:
8
+ def build_querystring(get_params: dict, extra_params: list[str] = None) -> str:
9
9
  querystring = f'?q={get_params.get("q", "[]")}'
10
10
  if paginate_by := get_params.get('paginate_by', False):
11
11
  querystring += f'&paginate_by={paginate_by}'
@@ -8,6 +8,7 @@ function buildQueryTagsFromUrl() {
8
8
  const queryGroup = createQueryGroup();
9
9
  queryTags.appendChild(queryGroup)
10
10
  buildQueryFromQueryString(normalizeQueryString(), queryGroup, '&');
11
+ toggleFilterModalButton();
11
12
  }
12
13
 
13
14
  function removeQueryTagsChildren() {
@@ -18,7 +19,6 @@ function removeQueryTagsChildren() {
18
19
  }
19
20
 
20
21
  function buildQueryFromQueryString(jsonQuery, queryGroup, operator='&') {
21
- console.log(jsonQuery)
22
22
  for (let item of jsonQuery) {
23
23
  if (isString(item)) {
24
24
  operator = item
@@ -229,6 +229,9 @@ function appendAfterTag(queryTagContainer) {
229
229
  function resetInput() {
230
230
  const input = document.getElementById('query-input');
231
231
  const queryParam = getParamElement(input.getAttribute('data-default-term'))
232
+ if (!queryParam) {
233
+ return
234
+ }
232
235
  const queryLabel = getCompleteQueryText(queryParam);
233
236
  const queryNotificationLabel = document.getElementById('query-label');
234
237
  queryNotificationLabel.innerText = queryLabel;
@@ -244,6 +247,18 @@ function fetchQuery(queryString=null) {
244
247
  queryApply.setAttribute('hx-get', url.toString());
245
248
  htmx.process(queryApply);
246
249
  queryApply.dispatchEvent(queryApplyEvent);
250
+ toggleFilterModalButton();
251
+ }
252
+
253
+ function toggleFilterModalButton() {
254
+ const button = document.getElementById('modal-filter-button');
255
+ const query = document.getElementById('query-tags');
256
+ if (query.firstElementChild && query.firstElementChild.childElementCount) {
257
+ button.classList.add('is-primary');
258
+ }
259
+ else {
260
+ button.classList.remove('is-primary');
261
+ }
247
262
  }
248
263
 
249
264
  //-----------------------------------------------------------------------------
@@ -253,6 +268,9 @@ function fetchQuery(queryString=null) {
253
268
  function showHide() {
254
269
  const target = this.event.target;
255
270
  const queryInput = document.getElementById('query-input-fieldset');
271
+ if (!queryInput) {
272
+ return;
273
+ }
256
274
  const queryParamDropDown = document.getElementById('query-params-dropdown');
257
275
  const operation = document.getElementById('query-operation');
258
276
  if (queryInput.contains(target)
@@ -683,4 +701,4 @@ function isBoolean(value) {
683
701
  return value.toLowerCase() === 'false' || value.toLowerCase() === 'true'
684
702
  }
685
703
  return false
686
- }
704
+ }
@@ -0,0 +1,35 @@
1
+ {% extends 'ui/layout.html' %}
2
+ {% load i18n %}
3
+ {% load accrete_ui %}
4
+
5
+ {% block messages %}
6
+ {% if messages %}
7
+ <div class="columns is-desktop">
8
+ <div class="column is-8">
9
+ {% for message in messages %}
10
+ <div class="columns py-0">
11
+ <div class="column is-12">
12
+ <div class="notification {{ message|message_class }} is-light">
13
+ <p>{{ message }}</p>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ {% endfor %}
18
+ </div>
19
+ </div>
20
+ {% endif %}
21
+ {% endblock %}
22
+
23
+ {% block content %}
24
+ <div class="columns is-desktop">
25
+ <div class="column p-0 is-8-desktop">
26
+ {% block detail_content %}{% endblock %}
27
+ </div>
28
+ <div class="info-panel column is-4">
29
+ <nav class="panel">
30
+ {% block info_panel %}
31
+ {% endblock %}
32
+ </nav>
33
+ </div>
34
+ </div>
35
+ {% endblock %}
@@ -93,12 +93,14 @@
93
93
  {% if action.submit %}
94
94
  <input class="button is-fullwidth {{ action.class_list|join:' ' }}"
95
95
  type="submit" form={{ action.form_id }} value="{{ action.name }}"
96
- {% for attr in action.attrs %}{{ attr }}{% endfor %}
96
+ {{ action.attrs_str }}
97
97
  >
98
98
  {% else %}
99
99
  <a class="button is-fullwidth {{ action.class_list|join:' ' }}"
100
- href="{{ action.url }}{{ url_params|default_if_none:'?' }}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
101
- {% for attr, val in action.attrs %}{{ attr }}="{{ val }}"{% endfor %}
100
+ {% if action.url %}
101
+ {{ action.method.value }}="{{ action.url }}{% if action.add_url_params %}{{ url_params|default_if_none:'?' }}{% endif %}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
102
+ {% endif %}
103
+ {{ action.attrs_str }}
102
104
  >{{ action.name }}
103
105
  </a>
104
106
  {% endif %}
@@ -2,11 +2,6 @@
2
2
  {% load static %}
3
3
  {% load i18n %}
4
4
 
5
- {% block script %}
6
- {{ block.super }}
7
- <script src="{% static "js/filter.js" %}" defer type="text/javascript"></script>
8
- {% endblock %}
9
-
10
5
  {% block content %}
11
6
  <div class="columns is-multiline">
12
7
  {% for obj in page %}
@@ -4,6 +4,7 @@
4
4
  {% load accrete_ui %}
5
5
 
6
6
  <div id="query-block" class="panel-block pt-0" style="position: relative; display: inline-block; width: 100%">
7
+ <script src="{% static "js/filter.js" %}" defer type="text/javascript"></script>
7
8
  <div id="query-apply" hx-get="" hx-trigger="click" hx-replace-url="true"
8
9
  hx-select-oob="#content,#list-pagination,#panel-actions,#header-actions,#query-apply">
9
10
 
@@ -7,7 +7,9 @@
7
7
  <nav id="breadcrumbs" class="breadcrumb" aria-label="breadcrumbs" style="white-space: unset; word-break: break-word">
8
8
  <ul>
9
9
  {% for crumb in breadcrumbs %}
10
- <li><a class="is-underlined" href="{{ crumb.url|default_if_none:'#' }}{{ url_params }}" aria-current="page">{{ crumb.name }}</a></li>
10
+ <li><a class="is-underlined" hx-get="{{ crumb.url|default_if_none:'#' }}{% if crumb.add_url_params %}{{ url_params }}{% endif %}"
11
+ hx-target="body" hx-push-url="true" aria-current="page"
12
+ >{{ crumb.name }}</a></li>
11
13
  {% endfor %}
12
14
  <li class="is-active"><a aria-current="page">{{ title }}</a></li>
13
15
  </ul>
@@ -3,11 +3,6 @@
3
3
  {% load i18n %}
4
4
  {% load accrete_ui %}
5
5
 
6
- {% block script %}
7
- {{ block.super }}
8
- <script src="{% static "js/filter.js" %}" defer type="text/javascript"></script>
9
- {% endblock %}
10
-
11
6
  {% block content %}
12
7
  <table class="table hax-box is-fullwidth is-hoverable" hx-indicator=".htmx-indicator">
13
8
  <thead style="position: sticky; top: 0; background: white; z-index: 10">
@@ -36,7 +31,9 @@
36
31
  hx-swap="afterend"
37
32
  {% endif %}>
38
33
  {% block table_data %}
39
- <td><a class="is-underlined" href="{{ obj.get_absolute_url }}{{ url_params }}&page={{ page.number }}">{{ obj }}</a></td>
34
+ <td><a class="is-underlined" hx-get="{{ obj.get_absolute_url }}{{ url_params }}&page={{ page.number }}"
35
+ hx-target="body" hx-push-url="true"
36
+ >{{ obj }}</a></td>
40
37
  {% for field in fields %}
41
38
  {% if field.template %}
42
39
  {% include field.template %}
@@ -3,6 +3,7 @@ from django import template
3
3
  from django.conf import settings
4
4
  from django.template.loader import render_to_string
5
5
  from django.utils.safestring import mark_safe
6
+ from django.contrib import messages
6
7
 
7
8
  _logger = logging.getLogger(__name__)
8
9
  register = template.Library()
@@ -34,6 +35,17 @@ def get_attr_from_string(param, value):
34
35
  return attr
35
36
 
36
37
 
38
+ @register.filter(name='message_class')
39
+ def message_class(param):
40
+ if param.level == 25:
41
+ return 'is-success'
42
+ if param.level == 30:
43
+ return 'is-warning'
44
+ if param.level == 40:
45
+ return 'is-danger'
46
+
47
+
48
+
37
49
  @register.filter(name='render_query_params')
38
50
  def query_params_to_html(params):
39
51
  html = ''
@@ -0,0 +1,119 @@
1
+ from django.conf import settings
2
+ from django.core.validators import validate_email
3
+ from django.utils.translation import gettext_lazy as _
4
+ from django.forms import ModelForm, fields, ValidationError, PasswordInput, EmailInput
5
+ from .models import User
6
+
7
+
8
+ class UserForm(ModelForm):
9
+
10
+ language_code = fields.ChoiceField(
11
+ label=_('Language'),
12
+ choices=settings.LANGUAGES,
13
+ required=True
14
+ )
15
+
16
+ class Meta:
17
+ model = User
18
+ fields = [
19
+ 'username',
20
+ 'first_name',
21
+ 'last_name',
22
+ ]
23
+
24
+ def save(self, commit=True):
25
+ super().save(commit=False)
26
+ self.instance.language_code = self.cleaned_data['language_code']
27
+ if commit:
28
+ self.instance.save()
29
+ self.save_m2m()
30
+
31
+
32
+ class ChangePasswordForm(ModelForm):
33
+
34
+ old_password = fields.CharField(
35
+ label=_('Old password'),
36
+ widget=PasswordInput(render_value=True),
37
+ max_length=128,
38
+ )
39
+
40
+ new_password = fields.CharField(
41
+ label=_('New password'),
42
+ widget=PasswordInput(render_value=True),
43
+ max_length=128
44
+ )
45
+
46
+ new_password_confirm = fields.CharField(
47
+ label=_('New password confirmation'),
48
+ widget=PasswordInput(render_value=True),
49
+ max_length=128
50
+ )
51
+
52
+ class Meta:
53
+ model = User
54
+ fields = []
55
+
56
+ def clean_old_password(self):
57
+ old_password = self.cleaned_data['old_password']
58
+ if not self.instance.check_password(old_password):
59
+ raise ValidationError(_(
60
+ 'Password Validation failed'
61
+ ))
62
+ return old_password
63
+
64
+ def clean(self):
65
+ super().clean()
66
+ new_password = self.cleaned_data.get('new_password')
67
+ new_password_confirm = self.cleaned_data.get('new_password_confirm')
68
+ if not new_password or not new_password_confirm:
69
+ raise ValidationError(_(
70
+ 'New password and password confirmation are required'
71
+ ))
72
+ if new_password != new_password_confirm:
73
+ raise ValidationError(
74
+ _('New password and confirmation did not match.')
75
+ )
76
+ return self.cleaned_data
77
+
78
+ def save(self, commit=True):
79
+ user = self.instance
80
+ user.set_password(self.cleaned_data['new_password'])
81
+ user.save()
82
+
83
+
84
+ class ChangeEmailForm(ModelForm):
85
+
86
+ email = fields.EmailField(
87
+ label=_('New Email Address'),
88
+ widget=EmailInput(),
89
+ )
90
+
91
+ password = fields.CharField(
92
+ label=_('Password'),
93
+ widget=PasswordInput()
94
+ )
95
+
96
+ class Meta:
97
+ model = User
98
+ fields = []
99
+
100
+ def clean_email(self):
101
+ email = self.cleaned_data.get('email', '').lower()
102
+ validate_email(email)
103
+ if User.objects.filter(email=email):
104
+ raise ValidationError(
105
+ _('This email address is already bound to an account.')
106
+ )
107
+ return email
108
+
109
+ def clean_password(self):
110
+ password = self.cleaned_data.get('password')
111
+ if not self.instance.check_password(password):
112
+ raise ValidationError(_(
113
+ 'Password incorrect'
114
+ ))
115
+ return password
116
+
117
+ def save(self, commit=True):
118
+ self.instance.email = self.cleaned_data['email']
119
+ self.instance.save()
@@ -6,4 +6,8 @@
6
6
  <a class="navbar-item" href="/admin/">Admin</a>
7
7
  {% endif %}
8
8
  <hr class="navbar-divider">
9
- <a class="navbar-item" href="{% url 'user:logout' %}">{% translate 'Logout' %}</a>
9
+ <form id="logout-form" method="post" action="{% url 'user:logout' %}">
10
+ {% csrf_token %}
11
+ <a class="navbar-item" href="#" onclick="document.getElementById('logout-form').submit()">{% translate "Log out" %}</a>
12
+ </form>
13
+
@@ -0,0 +1,29 @@
1
+ {% extends 'ui/form.html' %}
2
+
3
+ {% block form %}
4
+ <form id="form" method="post" action="">
5
+ {% csrf_token %}
6
+ <div class="columns">
7
+ <div class="column is-6">
8
+ <label class="label has-field is-required">
9
+ {{ form.email.label_tag }}
10
+ <span class="has-text-danger is-size-7">{{ form.email.errors }}</span>
11
+ <div class="field">
12
+ <p class="control is-fullwidth">{{ form.email }}</p>
13
+ </div>
14
+ </label>
15
+ </div>
16
+ </div>
17
+ <div class="columns">
18
+ <div class="column is-6">
19
+ <label class="label has-field is-required">
20
+ {{ form.password.label_tag }}
21
+ <span class="has-text-danger is-size-7">{{ form.password.errors }}</span>
22
+ <div class="field">
23
+ <p class="control is-fullwidth">{{ form.password }}</p>
24
+ </div>
25
+ </label>
26
+ </div>
27
+ </div>
28
+ </form>
29
+ {% endblock %}