Flask-Exts 0.2.2__tar.gz → 0.2.3__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 (390) hide show
  1. {flask_exts-0.2.2 → flask_exts-0.2.3}/.gitignore +3 -0
  2. {flask_exts-0.2.2 → flask_exts-0.2.3}/CHANGES.rst +10 -0
  3. {flask_exts-0.2.2 → flask_exts-0.2.3}/PKG-INFO +1 -1
  4. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/configure.rst +3 -2
  5. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/app.py +2 -2
  6. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/app.py +7 -5
  7. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/pagination.html +1 -1
  8. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/admin_views.py +6 -1
  9. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/build_sample.py +16 -4
  10. flask_exts-0.2.3/examples/demo/file_op.py +13 -0
  11. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/make_app.py +18 -9
  12. flask_exts-0.2.2/examples/sqla-custom-inline-forms/models/model.py → flask_exts-0.2.3/examples/demo/models/location_image.py +21 -5
  13. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/tree.py +3 -2
  14. flask_exts-0.2.2/examples/sqla-custom-inline-forms/views/image_view.py → flask_exts-0.2.3/examples/demo/views/location_image_view.py +5 -4
  15. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/fileadmin/make_app.py +2 -8
  16. flask_exts-0.2.3/examples/instance/config.py +3 -0
  17. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/rediscli.py +1 -1
  18. flask_exts-0.2.3/examples/simple.py +27 -0
  19. flask_exts-0.2.3/src/flask_exts/__about__.py +1 -0
  20. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/__init__.py +0 -1
  21. flask_exts-0.2.3/src/flask_exts/admin/admin.py +15 -0
  22. flask_exts-0.2.2/src/flask_exts/admin/admin.py → flask_exts-0.2.3/src/flask_exts/admin/base_admin.py +57 -49
  23. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/form.py +3 -3
  24. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/view.py +8 -10
  25. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/form.py +18 -18
  26. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/view.py +4 -4
  27. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/view.py +19 -28
  28. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/commands.py +6 -6
  29. flask_exts-0.2.3/src/flask_exts/datastore/sqla/__init__.py +9 -0
  30. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/datastore/sqla/models/role.py +0 -1
  31. flask_exts-0.2.3/src/flask_exts/datastore/sqla/models/user.py +70 -0
  32. flask_exts-0.2.3/src/flask_exts/datastore/sqla/models/user_profile.py +29 -0
  33. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/datastore/sqla/models/webauthn.py +6 -5
  34. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/datastore/sqla/orm.py +1 -3
  35. flask_exts-0.2.3/src/flask_exts/email/base.py +43 -0
  36. flask_exts-0.2.3/src/flask_exts/email/sender.py +6 -0
  37. flask_exts-0.2.3/src/flask_exts/email/smtp.py +41 -0
  38. flask_exts-0.2.3/src/flask_exts/email/verify_email_sender.py +12 -0
  39. flask_exts-0.2.3/src/flask_exts/forms/login.py +13 -0
  40. {flask_exts-0.2.2/src/flask_exts/security → flask_exts-0.2.3/src/flask_exts}/forms/profile.py +1 -1
  41. flask_exts-0.2.2/src/flask_exts/security/forms/mixins.py → flask_exts-0.2.3/src/flask_exts/forms/register.py +2 -9
  42. flask_exts-0.2.3/src/flask_exts/manager.py +83 -0
  43. flask_exts-0.2.3/src/flask_exts/proxies.py +22 -0
  44. {flask_exts-0.2.2/src/flask_exts/security/authorize → flask_exts-0.2.3/src/flask_exts/security/authorizer}/base.py +1 -1
  45. {flask_exts-0.2.2/src/flask_exts/security/authorize → flask_exts-0.2.3/src/flask_exts/security/authorizer}/casbin_authorizer.py +10 -17
  46. flask_exts-0.2.2/src/flask_exts/security/authorize/casbin_sqlalchemy_adapter/adapter.py → flask_exts-0.2.3/src/flask_exts/security/authorizer/sqlalchemy_adapter.py +2 -2
  47. flask_exts-0.2.3/src/flask_exts/security/core.py +29 -0
  48. flask_exts-0.2.3/src/flask_exts/security/email_verification.py +83 -0
  49. flask_exts-0.2.3/src/flask_exts/security/hasher.py +22 -0
  50. flask_exts-0.2.3/src/flask_exts/security/serializer.py +50 -0
  51. flask_exts-0.2.3/src/flask_exts/signals.py +7 -0
  52. flask_exts-0.2.3/src/flask_exts/template/base.py +48 -0
  53. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/sqla.py +2 -2
  54. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/form/base_form.py +1 -1
  55. flask_exts-0.2.3/src/flask_exts/template/form/csrf.py +65 -0
  56. flask_exts-0.2.3/src/flask_exts/template/form/form_opts.py +6 -0
  57. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/form/meta.py +2 -25
  58. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template/form}/utils.py +0 -9
  59. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/template/utils.py +0 -16
  60. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/base.html +2 -0
  61. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/index.html +1 -1
  62. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/menu.html +1 -1
  63. flask_exts-0.2.3/src/flask_exts/templates/views/user/verify_email.html +9 -0
  64. flask_exts-0.2.3/src/flask_exts/usercenter/base_user.py +7 -0
  65. flask_exts-0.2.3/src/flask_exts/usercenter/base_usercenter.py +40 -0
  66. {flask_exts-0.2.2/src/flask_exts/security → flask_exts-0.2.3/src/flask_exts}/usercenter/sqla_usercenter.py +50 -37
  67. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/__init__.py +1 -1
  68. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/authorize.py +4 -4
  69. flask_exts-0.2.2/src/flask_exts/decorators/auth_required_decorator.py → flask_exts-0.2.3/src/flask_exts/utils/decorators.py +2 -2
  70. flask_exts-0.2.3/src/flask_exts/utils/flask.py +16 -0
  71. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/request_user.py +6 -6
  72. flask_exts-0.2.2/src/flask_exts/utils/flask.py → flask_exts-0.2.3/src/flask_exts/utils/url.py +0 -14
  73. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/file_view/base_file_view.py +4 -3
  74. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/index_view.py +3 -2
  75. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/user_view.py +26 -12
  76. {flask_exts-0.2.2/tests → flask_exts-0.2.3/tests/admin/customer}/conftest.py +10 -6
  77. flask_exts-0.2.3/tests/admin/customer/test_admin.py +97 -0
  78. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/test_basic.py +324 -413
  79. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/test_form_rules.py +12 -19
  80. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/test_inlineform.py +4 -4
  81. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/test_multi_pk.py +13 -16
  82. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/test_translation.py +1 -1
  83. flask_exts-0.2.3/tests/admin/test_base.py +153 -0
  84. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/test_model.py +79 -77
  85. flask_exts-0.2.3/tests/conftest.py +46 -0
  86. flask_exts-0.2.3/tests/email/test_base.py +18 -0
  87. flask_exts-0.2.3/tests/email/test_smtp.py +24 -0
  88. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_adapter/test_sqlalchemy_adapter.py +5 -5
  89. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/test_casbin_auth.py +21 -19
  90. flask_exts-0.2.3/tests/security/test_core.py +41 -0
  91. flask_exts-0.2.3/tests/template/bootstrap4/__init__.py +0 -0
  92. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_field.py +17 -21
  93. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_form.py +40 -48
  94. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_form_row.py +10 -14
  95. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_hidden_errors.py +3 -4
  96. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_icon_sprite.py +25 -30
  97. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_messages.py +15 -19
  98. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_pager.py +10 -12
  99. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_pagination.py +9 -11
  100. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap4/test_render_table.py +48 -55
  101. flask_exts-0.2.3/tests/template/bootstrap5/__init__.py +0 -0
  102. flask_exts-0.2.3/tests/template/bootstrap5/conftest.py +21 -0
  103. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap5/test_base.py +2 -2
  104. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap5/test_render_form.py +31 -37
  105. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap5/test_render_form_row.py +4 -5
  106. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap5/test_render_messages.py +2 -3
  107. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/bootstrap5/test_render_pagination.py +5 -6
  108. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/conftest.py +1 -1
  109. flask_exts-0.2.2/tests/forms/test_form.py → flask_exts-0.2.3/tests/template/forms/test_csrf.py +4 -3
  110. {flask_exts-0.2.2/tests → flask_exts-0.2.3/tests/template}/forms/test_fields.py +2 -2
  111. {flask_exts-0.2.2/tests → flask_exts-0.2.3/tests/template}/forms/test_filefield.py +1 -1
  112. {flask_exts-0.2.2/tests → flask_exts-0.2.3/tests/template}/forms/test_uploadfile.py +3 -3
  113. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/macros/test_render_breadcrumb_item.py +4 -6
  114. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/macros/test_render_nav_item.py +4 -6
  115. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/template/test_base.py +2 -2
  116. flask_exts-0.2.3/tests/templates/mock.html +1 -0
  117. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/test_babel.py +7 -6
  118. flask_exts-0.2.3/tests/test_base.py +27 -0
  119. flask_exts-0.2.3/tests/test_flask.py +34 -0
  120. flask_exts-0.2.3/tests/usercenter/test_sqla_usercenter.py +34 -0
  121. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/utils/test_request_user.py +2 -2
  122. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/utils/test_url.py +1 -1
  123. flask_exts-0.2.3/tests/views/files/dummy.txt +0 -0
  124. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/views/test_file_view.py +26 -28
  125. flask_exts-0.2.3/tests/views/test_user_view.py +96 -0
  126. flask_exts-0.2.2/examples/simple.py +0 -13
  127. flask_exts-0.2.2/examples/sqla-custom-inline-forms/__init__.py +0 -1
  128. flask_exts-0.2.2/examples/sqla-custom-inline-forms/data.py +0 -23
  129. flask_exts-0.2.2/examples/sqla-custom-inline-forms/make_app.py +0 -88
  130. flask_exts-0.2.2/examples/sqla-custom-inline-forms/models/__init__.py +0 -1
  131. flask_exts-0.2.2/examples/sqla-custom-inline-forms/templates/admin/admin.html +0 -5
  132. flask_exts-0.2.2/src/flask_exts/__about__.py +0 -1
  133. flask_exts-0.2.2/src/flask_exts/datastore/sqla/__init__.py +0 -9
  134. flask_exts-0.2.2/src/flask_exts/datastore/sqla/models/user.py +0 -38
  135. flask_exts-0.2.2/src/flask_exts/datastore/sqla/models/user_role.py +0 -11
  136. flask_exts-0.2.2/src/flask_exts/decorators/__init__.py +0 -2
  137. flask_exts-0.2.2/src/flask_exts/forms/__init__.py +0 -3
  138. flask_exts-0.2.2/src/flask_exts/forms/form/__init__.py +0 -2
  139. flask_exts-0.2.2/src/flask_exts/forms/form/csrf.py +0 -57
  140. flask_exts-0.2.2/src/flask_exts/manager.py +0 -74
  141. flask_exts-0.2.2/src/flask_exts/proxies.py +0 -17
  142. flask_exts-0.2.2/src/flask_exts/security/authorize/__init__.py +0 -1
  143. flask_exts-0.2.2/src/flask_exts/security/authorize/casbin_sqlalchemy_adapter/__init__.py +0 -1
  144. flask_exts-0.2.2/src/flask_exts/security/core.py +0 -43
  145. flask_exts-0.2.2/src/flask_exts/security/forms/__init__.py +0 -2
  146. flask_exts-0.2.2/src/flask_exts/security/forms/login.py +0 -6
  147. flask_exts-0.2.2/src/flask_exts/security/forms/register.py +0 -6
  148. flask_exts-0.2.2/src/flask_exts/security/mixins/webauthn_mixin.py +0 -9
  149. flask_exts-0.2.2/src/flask_exts/security/usercenter/base.py +0 -21
  150. flask_exts-0.2.2/src/flask_exts/security/usercenter/memory_usercenter.py +0 -59
  151. flask_exts-0.2.2/src/flask_exts/template/__init__.py +0 -37
  152. flask_exts-0.2.2/tests/admin/test_base.py +0 -315
  153. flask_exts-0.2.2/tests/template/bootstrap5/conftest.py +0 -14
  154. flask_exts-0.2.2/tests/test_base.py +0 -19
  155. flask_exts-0.2.2/tests/views/test_user_view.py +0 -60
  156. {flask_exts-0.2.2 → flask_exts-0.2.3}/.github/workflows/release.yml +0 -0
  157. {flask_exts-0.2.2 → flask_exts-0.2.3}/.readthedocs.yaml +0 -0
  158. {flask_exts-0.2.2 → flask_exts-0.2.3}/LICENSE.rst +0 -0
  159. {flask_exts-0.2.2 → flask_exts-0.2.3}/README.rst +0 -0
  160. {flask_exts-0.2.2 → flask_exts-0.2.3}/babel/babel.cfg +0 -0
  161. {flask_exts-0.2.2 → flask_exts-0.2.3}/babel/babel.sh +0 -0
  162. {flask_exts-0.2.2 → flask_exts-0.2.3}/babel/messages.pot +0 -0
  163. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/Makefile +0 -0
  164. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/_static/logo.png +0 -0
  165. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/changes.rst +0 -0
  166. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/conf.py +0 -0
  167. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/develop.rst +0 -0
  168. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/examples.rst +0 -0
  169. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/index.rst +0 -0
  170. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/make.bat +0 -0
  171. {flask_exts-0.2.2 → flask_exts-0.2.3}/docs/requirements.txt +0 -0
  172. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/README.rst +0 -0
  173. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/static/favicon.ico +0 -0
  174. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/bootswatch.html +0 -0
  175. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/flash.html +0 -0
  176. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/form.html +0 -0
  177. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/icon.html +0 -0
  178. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/icons.html +0 -0
  179. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/index.html +0 -0
  180. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/mybase.html +0 -0
  181. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/nav.html +0 -0
  182. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/pagination.html +0 -0
  183. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap4/templates/table.html +0 -0
  184. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/static/favicon.ico +0 -0
  185. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/bootswatch.html +0 -0
  186. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/flash.html +0 -0
  187. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/form.html +0 -0
  188. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/icon.html +0 -0
  189. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/icons.html +0 -0
  190. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/index.html +0 -0
  191. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/mybase.html +0 -0
  192. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/nav.html +0 -0
  193. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/bootstrap5/templates/table.html +0 -0
  194. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/__init__.py +0 -0
  195. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/__init__.py +0 -0
  196. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/author.py +0 -0
  197. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/keyword.py +0 -0
  198. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/post.py +0 -0
  199. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/post_tag.py +0 -0
  200. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/tag.py +0 -0
  201. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/user.py +0 -0
  202. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/models/user_keyword.py +0 -0
  203. {flask_exts-0.2.2/examples/sqla-custom-inline-forms → flask_exts-0.2.3/examples/demo}/templates/field_list.html +0 -0
  204. {flask_exts-0.2.2/examples/sqla-custom-inline-forms → flask_exts-0.2.3/examples/demo}/templates/locations.html +0 -0
  205. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/templates/my/index.html +0 -0
  206. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/templates/tree_list.html +0 -0
  207. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/author_view.py +0 -0
  208. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/keyword_view.py +0 -0
  209. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/my_view.py +0 -0
  210. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/post_view.py +0 -0
  211. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/tag_view.py +0 -0
  212. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/tree_view.py +0 -0
  213. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/demo/views/user_view.py +0 -0
  214. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/fileadmin/__init__.py +0 -0
  215. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/fileadmin/file_view.py +0 -0
  216. {flask_exts-0.2.2 → flask_exts-0.2.3}/examples/fileadmin/tmp/bbb/a.txt +0 -0
  217. {flask_exts-0.2.2 → flask_exts-0.2.3}/pyproject.toml +0 -0
  218. {flask_exts-0.2.2 → flask_exts-0.2.3}/requirements/build.in +0 -0
  219. {flask_exts-0.2.2 → flask_exts-0.2.3}/requirements/test.in +0 -0
  220. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/__init__.py +0 -0
  221. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/actions.py +0 -0
  222. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/menu.py +0 -0
  223. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/__init__.py +0 -0
  224. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/ajax.py +0 -0
  225. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/filters.py +0 -0
  226. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/model/typefmt.py +0 -0
  227. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/row_action.py +0 -0
  228. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/__init__.py +0 -0
  229. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/ajax.py +0 -0
  230. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/filters.py +0 -0
  231. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/admin/sqla/typefmt.py +0 -0
  232. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/babel/__init__.py +0 -0
  233. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/babel/selector.py +0 -0
  234. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/datastore/__init__.py +0 -0
  235. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/datastore/sqla/models/__init__.py +0 -0
  236. {flask_exts-0.2.2/src/flask_exts/security/usercenter → flask_exts-0.2.3/src/flask_exts/email}/__init__.py +0 -0
  237. {flask_exts-0.2.2/src/flask_exts/translations → flask_exts-0.2.3/src/flask_exts/forms}/__init__.py +0 -0
  238. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/security/__init__.py +0 -0
  239. {flask_exts-0.2.2/tests/admin → flask_exts-0.2.3/src/flask_exts/security/authorizer}/__init__.py +0 -0
  240. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/security/principal/identity.py +0 -0
  241. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/security/principal/identity_context.py +0 -0
  242. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/security/principal/permission.py +0 -0
  243. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/security/principal/principal.py +0 -0
  244. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/css/admin.css +0 -0
  245. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/css/rediscli.css +0 -0
  246. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/actions.js +0 -0
  247. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/details_filter.js +0 -0
  248. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/filters.js +0 -0
  249. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/form.js +0 -0
  250. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/helper.js +0 -0
  251. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/modal.js +0 -0
  252. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/rediscli.js +0 -0
  253. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/security/base64.js +0 -0
  254. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/js/security/webauthn.js +0 -0
  255. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/logo.png +0 -0
  256. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/bootstrap-icons.svg +0 -0
  257. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/bootstrap-icons.css +0 -0
  258. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/bootstrap-icons.json +0 -0
  259. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/bootstrap-icons.min.css +0 -0
  260. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/bootstrap-icons.scss +0 -0
  261. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/fonts/bootstrap-icons.woff +0 -0
  262. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap-icons/font/fonts/bootstrap-icons.woff2 +0 -0
  263. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap4/bootstrap.bundle.min.js +0 -0
  264. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap4/bootstrap.min.css +0 -0
  265. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap5/bootstrap.bundle.min.js +0 -0
  266. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/bootstrap5/bootstrap.min.css +0 -0
  267. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/daterangepicker/daterangepicker.css +0 -0
  268. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/daterangepicker/daterangepicker.js +0 -0
  269. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/jquery/jquery.min.js +0 -0
  270. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/moment.min.js +0 -0
  271. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/select2/select2.min.css +0 -0
  272. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/select2/select2.min.js +0 -0
  273. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/vanilla-editor/css/bootstrap-editable.css +0 -0
  274. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/vanilla-editor/img/clear.png +0 -0
  275. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/vanilla-editor/img/loading.gif +0 -0
  276. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/static/vendor/vanilla-editor/js/bootstrap-editable.min.js +0 -0
  277. {flask_exts-0.2.2/src/flask_exts/security/utils → flask_exts-0.2.3/src/flask_exts/template}/__init__.py +0 -0
  278. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/__init__.py +0 -0
  279. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/ajax_select.py +0 -0
  280. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/datetime.py +0 -0
  281. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/file.py +0 -0
  282. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/inline.py +0 -0
  283. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/json.py +0 -0
  284. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/recaptcha.py +0 -0
  285. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/select.py +0 -0
  286. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/switch.py +0 -0
  287. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/fields/taglist.py +0 -0
  288. {flask_exts-0.2.2/tests/admin/sqla → flask_exts-0.2.3/src/flask_exts/template/form}/__init__.py +0 -0
  289. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/form/flask_form.py +0 -0
  290. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/form/session_csrf.py +0 -0
  291. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/rules.py +0 -0
  292. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/template/theme.py +0 -0
  293. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/validators/__init__.py +0 -0
  294. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/validators/field_list.py +0 -0
  295. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/validators/file.py +0 -0
  296. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/validators/recaptcha.py +0 -0
  297. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/validators/sqla.py +0 -0
  298. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/__init__.py +0 -0
  299. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/checkbox.py +0 -0
  300. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/datetime.py +0 -0
  301. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/file.py +0 -0
  302. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/inline.py +0 -0
  303. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/recaptcha.py +0 -0
  304. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/render_template.py +0 -0
  305. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/select.py +0 -0
  306. {flask_exts-0.2.2/src/flask_exts/forms → flask_exts-0.2.3/src/flask_exts/template}/widgets/xeditable.py +0 -0
  307. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/master.html +0 -0
  308. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/create.html +0 -0
  309. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/details.html +0 -0
  310. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/edit.html +0 -0
  311. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/list.html +0 -0
  312. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/modals/create.html +0 -0
  313. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/modals/details.html +0 -0
  314. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/admin/model/modals/edit.html +0 -0
  315. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/base.html +0 -0
  316. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/bootstrap4/form.html +0 -0
  317. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/bootstrap4/message.html +0 -0
  318. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/bootstrap4/pagination.html +0 -0
  319. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/bootstrap5/form.html +0 -0
  320. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/bootstrap5/message.html +0 -0
  321. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/index.html +0 -0
  322. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/layout.html +0 -0
  323. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/actions.html +0 -0
  324. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/form.html +0 -0
  325. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/icon.html +0 -0
  326. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/inline.html +0 -0
  327. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/layout.html +0 -0
  328. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/message.html +0 -0
  329. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/nav.html +0 -0
  330. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/pagination.html +0 -0
  331. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/row_actions.html +0 -0
  332. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/table.html +0 -0
  333. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/macro/vendor.html +0 -0
  334. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/file/form.html +0 -0
  335. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/file/list.html +0 -0
  336. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/file/modals/form.html +0 -0
  337. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/rediscli/console.html +0 -0
  338. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/rediscli/response.html +0 -0
  339. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/user/index.html +0 -0
  340. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/user/list.html +0 -0
  341. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/user/login.html +0 -0
  342. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/views/user/register.html +0 -0
  343. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/widgets/inline_field_list.html +0 -0
  344. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/templates/widgets/inline_form.html +0 -0
  345. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/src/flask_exts/translations}/__init__.py +0 -0
  346. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/translations/en/LC_MESSAGES/messages.mo +0 -0
  347. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/translations/en/LC_MESSAGES/messages.po +0 -0
  348. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  349. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/translations/zh_CN/LC_MESSAGES/messages.po +0 -0
  350. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/types.py +0 -0
  351. {flask_exts-0.2.2/tests/template → flask_exts-0.2.3/src/flask_exts/usercenter}/__init__.py +0 -0
  352. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/jwt.py +0 -0
  353. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/sqla.py +0 -0
  354. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/utils/tools.py +0 -0
  355. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/file_view/__init__.py +0 -0
  356. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/file_view/local_file_storage.py +0 -0
  357. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/file_view/local_file_view.py +0 -0
  358. {flask_exts-0.2.2 → flask_exts-0.2.3}/src/flask_exts/views/rediscli_view.py +0 -0
  359. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/__init__.py +0 -0
  360. {flask_exts-0.2.2/tests/template/bootstrap4 → flask_exts-0.2.3/tests/admin}/__init__.py +0 -0
  361. {flask_exts-0.2.2/tests → flask_exts-0.2.3/tests/admin/customer}/templates/mock.html +0 -0
  362. {flask_exts-0.2.2/tests/template/bootstrap5 → flask_exts-0.2.3/tests/admin/sqla}/__init__.py +0 -0
  363. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/templates/another_macro.html +0 -0
  364. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/admin/sqla/templates/macro.html +0 -0
  365. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/data/copyleft.gif +0 -0
  366. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/data/copyleft.jpeg +0 -0
  367. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/data/copyleft.jpg +0 -0
  368. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/data/copyleft.png +0 -0
  369. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/data/copyleft.tiff +0 -0
  370. /flask_exts-0.2.2/tests/funcs.py → /flask_exts-0.2.3/tests/helper.py +0 -0
  371. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/__init__.py +0 -0
  372. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/message.py +0 -0
  373. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/model1.py +0 -0
  374. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/multpk.py +0 -0
  375. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/polymorphic.py +0 -0
  376. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/tree.py +0 -0
  377. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/models/user.py +0 -0
  378. /flask_exts-0.2.2/src/flask_exts/utils/url.py → /flask_exts-0.2.3/tests/security/authorizer/__init__.py +0 -0
  379. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_adapter/rbac_model.conf +0 -0
  380. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_adapter/rbac_policy.csv +0 -0
  381. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_files/authz_policy.csv +0 -0
  382. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_files/rbac_model.conf +0 -0
  383. {flask_exts-0.2.2/tests/security/authorize → flask_exts-0.2.3/tests/security/authorizer}/casbin_files/rbac_policy.csv +0 -0
  384. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/security/test_principal.py +0 -0
  385. /flask_exts-0.2.2/tests/views/files/dummy.txt → /flask_exts-0.2.3/tests/template/__init__.py +0 -0
  386. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/templates/method.html +0 -0
  387. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/translations/en/LC_MESSAGES/messages.mo +0 -0
  388. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/translations/en/LC_MESSAGES/messages.po +0 -0
  389. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/translations/zh_CN/LC_MESSAGES/messages.po +0 -0
  390. {flask_exts-0.2.2 → flask_exts-0.2.3}/tests/views/test_rediscli_view.py +0 -0
@@ -32,3 +32,6 @@ docs/_build/
32
32
 
33
33
  # sqlite db
34
34
  *.sqlite
35
+
36
+ # production config
37
+ **/config_prod.py
@@ -1,3 +1,13 @@
1
+ v0.2.3
2
+ ------
3
+
4
+ Released 2025-08-28
5
+
6
+ - move form into template
7
+ - add folder email
8
+ - add security.email_verification
9
+
10
+
1
11
  v0.2.2
2
12
  ------
3
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Flask-Exts
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Flask extensions with admin, SQLAlchemy, babel, forms, fields, widgets, and so on.
5
5
  Project-URL: Documentation, https://flask-exts.readthedocs.io
6
6
  Project-URL: Source, https://github.com/ojso/flask-exts
@@ -18,7 +18,8 @@ Configuration
18
18
  Default is ``csrf_token``.
19
19
  ``CSRF_TIME_LIMIT`` Max age in seconds for CSRF tokens.
20
20
  Default is ``1800``.
21
- ``TEMPLATE_THEME`` Set to ``theme`` to create custom theme
22
- Default is ``template.theme.BootstrapTheme(4)``
21
+ ``VERIFY_EMAIL_SENDER`` Email address used to send verification emails.
22
+ It is a dict for smtp with keys: host,port,user,password.
23
+ Default is None.
23
24
  ========================== =====================================================================
24
25
 
@@ -3,8 +3,8 @@ from flask import Flask, render_template, request, flash, redirect, url_for
3
3
  from markupsafe import Markup
4
4
  from wtforms.validators import DataRequired, Length, Regexp
5
5
  from wtforms.fields import *
6
- from flask_exts.forms.form import FlaskForm
7
- from flask_exts.forms.fields import SwitchField
6
+ from flask_exts.template.form.flask_form import FlaskForm
7
+ from flask_exts.template.fields import SwitchField
8
8
  from flask_exts import Manager
9
9
  from flask_exts.datastore.sqla import db
10
10
 
@@ -4,13 +4,12 @@ from markupsafe import Markup
4
4
  from wtforms.validators import DataRequired, Length, Regexp
5
5
  from wtforms.fields import *
6
6
 
7
- from flask_exts.forms.form import FlaskForm
8
- from flask_exts.forms.fields import SwitchField
7
+ from flask_exts.template.form.flask_form import FlaskForm
8
+ from flask_exts.template.fields import SwitchField
9
9
  from flask_exts import Manager
10
10
  from flask_exts.template.theme import BootstrapTheme
11
11
  from flask_exts.datastore.sqla import db
12
12
 
13
- theme = BootstrapTheme(version=5)
14
13
 
15
14
  app = Flask(__name__)
16
15
  app.secret_key = "dev"
@@ -18,10 +17,13 @@ app.secret_key = "dev"
18
17
  app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
19
18
  app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
20
19
  # set bootstrap5
21
- app.config["TEMPLATE_THEME"] = theme
22
20
  app.config['ADMIN_ENABLED'] = False
23
21
 
24
- manager = Manager()
22
+ class MyManager(Manager):
23
+ def get_theme(self):
24
+ return BootstrapTheme(5)
25
+
26
+ manager = MyManager()
25
27
  manager.init_app(app)
26
28
 
27
29
 
@@ -1,5 +1,5 @@
1
1
  {% extends "mybase.html" %}
2
- {% from 'pagination4/pagination.html' import render_pager, render_pagination %}
2
+ {% from 'bootstrap4/pagination.html' import render_pager, render_pagination %}
3
3
 
4
4
  {% block content %}
5
5
  <h2>Messages</h2>
@@ -5,11 +5,12 @@ from .views.tree_view import treeview
5
5
  from .views.tag_view import tagview
6
6
  from .views.author_view import authorview
7
7
  from .views.post_view import postview
8
+ from .views.location_image_view import locationview
8
9
  from flask_exts.admin.menu import MenuLink
9
10
 
10
11
 
11
12
  def add_views(app):
12
- admin = app.extensions["manager"].admins[0]
13
+ admin = app.extensions["manager"].admin
13
14
 
14
15
  admin.add_view(myview)
15
16
  admin.add_view(userview)
@@ -18,6 +19,10 @@ def add_views(app):
18
19
  admin.add_view(tagview)
19
20
  admin.add_view(postview)
20
21
  admin.add_view(treeview)
22
+ admin.add_view(locationview)
23
+ admin.menu.add_link(
24
+ MenuLink(name="locationlist", url="/locationlist", category="List")
25
+ )
21
26
  admin.menu.add_category(name="Links")
22
27
  admin.menu.add_link(MenuLink(name="Back Home", url="/", category="Links"))
23
28
  admin.menu.add_sub_category(name='External',parent_name='Links')
@@ -1,6 +1,6 @@
1
1
  import random
2
2
  import datetime
3
- from flask_exts.proxies import current_usercenter
3
+ from flask_exts.proxies import _usercenter
4
4
  from .models import db
5
5
  from .models.author import Author
6
6
  from .models.author import AVAILABLE_USER_TYPES
@@ -9,14 +9,25 @@ from .models.post import Post
9
9
  from .models.tree import Tree
10
10
  from .models.keyword import Keyword
11
11
  from .models.user import MyUser
12
+ from .models.location_image import ImageType
12
13
 
13
14
 
14
15
  def build_user_admin():
15
- u, _ = current_usercenter.create_user(
16
+ u, _ = _usercenter.create_user(
16
17
  username="admin", password="admin", email="admin@example.com"
17
18
  )
18
- r, _ = current_usercenter.create_role(name="admin")
19
- current_usercenter.user_add_role(u, r)
19
+ r, _ = _usercenter.create_role(name="admin")
20
+ _usercenter.user_add_role(u, r)
21
+
22
+
23
+ def build_sample_image_type():
24
+
25
+ # Add some image types for the form_ajax_refs inside the inline_model
26
+ image_types = ("JPEG", "PNG", "GIF")
27
+ for image_type in image_types:
28
+ itype = ImageType(name=image_type)
29
+ db.session.add(itype)
30
+ db.session.commit()
20
31
 
21
32
 
22
33
  def build_sample_userkeyword():
@@ -225,5 +236,6 @@ def build_sample_db():
225
236
  build_sample_tree()
226
237
  build_sample_post()
227
238
  build_user_admin()
239
+ build_sample_image_type()
228
240
 
229
241
  return
@@ -0,0 +1,13 @@
1
+ import os.path as op
2
+ import os
3
+
4
+ # Figure out base upload path
5
+ static_path = op.join(op.dirname(__file__), "static")
6
+
7
+
8
+ def remove_image(image_path):
9
+ os.remove(op.join(static_path, image_path))
10
+
11
+
12
+ def save_image(file_data, image_path):
13
+ file_data.save(op.join(static_path, image_path))
@@ -2,6 +2,11 @@ import os.path as op
2
2
  from flask import Flask
3
3
  from flask_exts import Manager
4
4
 
5
+ class MyManager(Manager):
6
+ def get_admin_class(self):
7
+ from flask_exts.admin.admin import Admin
8
+ return Admin
9
+
5
10
 
6
11
  def get_sqlite_path():
7
12
  app_dir = op.realpath(op.dirname(__file__))
@@ -10,26 +15,21 @@ def get_sqlite_path():
10
15
 
11
16
 
12
17
  def create_app():
13
- app = Flask(__name__)
18
+ app = Flask(__name__, instance_relative_config=True)
14
19
  app.config["SECRET_KEY"] = "dev"
15
-
20
+ app.config.from_pyfile('config.py',silent=True)
21
+ app.config.from_pyfile('config_prod.py',silent=True)
16
22
  # app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
17
23
  # app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
18
24
  # app.config["SQLALCHEMY_ECHO"] = True
19
25
  app.config["DATABASE_FILE"] = get_sqlite_path()
20
26
  app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + app.config["DATABASE_FILE"]
21
- # JWT
22
- app.config["JWT_SECRET_KEY"] = "SECRET_KEY"
23
- app.config["JWT_HASH"] = "HS256"
24
- # app.config["ADMIN_ACCESS_ENABLED"] = False
25
27
  init_app(app)
26
-
27
28
  return app
28
29
 
29
30
 
30
31
  def init_app(app: Flask):
31
-
32
- manager = Manager()
32
+ manager = MyManager()
33
33
  manager.init_app(app)
34
34
 
35
35
  from .models import init_models
@@ -40,6 +40,15 @@ def init_app(app: Flask):
40
40
 
41
41
  add_views(app)
42
42
 
43
+ @app.route("/locationlist")
44
+ def location_list():
45
+ from .models.location_image import Location
46
+ from flask import render_template
47
+ from .models import db
48
+
49
+ locations = db.session.query(Location).all()
50
+ return render_template("locations.html", locations=locations)
51
+
43
52
  if not op.exists(app.config["DATABASE_FILE"]):
44
53
  with app.app_context():
45
54
  from .build_sample import build_sample_db
@@ -1,14 +1,19 @@
1
1
  from . import db
2
2
  from typing import Optional
3
+ from typing import List
3
4
  from sqlalchemy import ForeignKey
4
5
  from sqlalchemy.orm import relationship
5
6
  from sqlalchemy.orm import Mapped
6
7
  from sqlalchemy.orm import mapped_column
7
-
8
+ from sqlalchemy import event
9
+ from ..file_op import remove_image
8
10
 
9
11
  class Location(db.Model):
10
12
  id: Mapped[int] = mapped_column(primary_key=True)
11
13
  name: Mapped[str]
14
+ images: Mapped[List["LocationImage"]] = relationship(
15
+ "LocationImage", back_populates="location", cascade="all, delete-orphan"
16
+ )
12
17
 
13
18
 
14
19
  class ImageType(db.Model):
@@ -33,10 +38,21 @@ class LocationImage(db.Model):
33
38
  alt: Mapped[str]
34
39
  path: Mapped[str]
35
40
 
36
- location_id: Mapped[int] = mapped_column(ForeignKey("location.id"))
37
- location: Mapped["Location"] = relationship(backref='images')
41
+ location_id = mapped_column(ForeignKey("location.id"))
42
+ location: Mapped["Location"] = relationship(back_populates="images")
43
+
44
+ image_type_id = mapped_column(ForeignKey("image_type.id"))
45
+ image_type: Mapped["ImageType"] = relationship()
46
+
38
47
 
39
- image_type_id: Mapped[int] = mapped_column(ForeignKey("image_type.id"))
40
- image_type: Mapped["ImageType"] = relationship(backref='images')
41
48
 
42
49
 
50
+ # Register after_delete handler which will delete image file after model gets deleted
51
+ @event.listens_for(Location, "after_delete")
52
+ def _handle_image_delete(mapper, conn, target):
53
+ for location_image in target.images:
54
+ try:
55
+ if location_image.path:
56
+ remove_image(location_image.path)
57
+ except:
58
+ pass
@@ -5,6 +5,7 @@ from sqlalchemy.orm import relationship
5
5
  from sqlalchemy.orm import Mapped
6
6
  from sqlalchemy.orm import mapped_column
7
7
 
8
+
8
9
  class Tree(db.Model):
9
10
  id: Mapped[int] = mapped_column(primary_key=True)
10
11
  name: Mapped[str]
@@ -12,7 +13,7 @@ class Tree(db.Model):
12
13
  # recursive relationship
13
14
  parent_id: Mapped[Optional[int]] = mapped_column(ForeignKey("tree.id"))
14
15
  parent = relationship("Tree", back_populates="children", remote_side=id)
15
- children = relationship("Tree",back_populates="parent")
16
+ children = relationship("Tree", back_populates="parent")
16
17
 
17
18
  def __str__(self):
18
- return "{}".format(self.name)
19
+ return "{}".format(self.name)
@@ -3,12 +3,13 @@ from flask import request
3
3
  from flask import current_app
4
4
  from wtforms.fields import FileField
5
5
  from flask_exts.admin.sqla.ajax import QueryAjaxModelLoader
6
- from flask_exts.forms.widgets.render_template import RenderTemplateWidget
6
+ from flask_exts.template.widgets.render_template import RenderTemplateWidget
7
7
  from flask_exts.admin.model.form import InlineFormAdmin
8
8
  from flask_exts.admin.sqla import ModelView
9
9
  from flask_exts.admin.sqla.form import InlineModelConverter
10
- from flask_exts.forms.fields.sqla import InlineModelFormList
11
- from ..models.model import ImageType, Location, LocationImage
10
+ from flask_exts.template.fields.sqla import InlineModelFormList
11
+ from ..models.location_image import ImageType, Location, LocationImage
12
+ from ..file_op import save_image
12
13
 
13
14
 
14
15
  # This widget uses custom template for inline field list
@@ -60,7 +61,7 @@ class LocationImageInlineModelForm(InlineFormAdmin):
60
61
 
61
62
  if file_data:
62
63
  model.path = secure_filename(file_data.filename)
63
- current_app.save_image(file_data, model.path)
64
+ save_image(file_data, model.path)
64
65
 
65
66
 
66
67
  # Administrative class
@@ -9,7 +9,7 @@ def create_app():
9
9
  app = Flask(__name__)
10
10
  app.config["SECRET_KEY"] = "dev"
11
11
  app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
12
- app.config["ADMIN_ACCESS_ENABLED"] = False
12
+
13
13
  init_app(app)
14
14
  return app
15
15
 
@@ -22,11 +22,5 @@ def init_app(app: Flask):
22
22
  # db.drop_all()
23
23
  db.create_all()
24
24
 
25
- # Flask views
26
- @app.route("/")
27
- def index():
28
-
29
- return '<a href="/admin/">Click me to get to Admin!</a>'
30
-
31
- admin = app.extensions["manager"].admins[0]
25
+ admin = manager.admins[0]
32
26
  admin.add_view(file_view)
@@ -0,0 +1,3 @@
1
+ # JWT
2
+ JWT_SECRET_KEY = "SECRET_KEY"
3
+ JWT_HASH = "HS256"
@@ -5,7 +5,7 @@ from flask_exts.views.rediscli_view import RedisCli
5
5
 
6
6
  app = Flask(__name__)
7
7
  app.config["SECRET_KEY"] = "dev"
8
- app.config["ADMIN_ACCESS_ENABLED"] =False
8
+
9
9
  # Manager init
10
10
  manager = Manager()
11
11
  manager.init_app(app)
@@ -0,0 +1,27 @@
1
+ # simple.py
2
+
3
+ from flask import Flask
4
+ from flask_exts import Manager
5
+ from flask_exts.admin import expose
6
+ from flask_exts.admin import BaseView
7
+ from flask_exts.views.index_view import IndexView
8
+
9
+
10
+ class MockView(BaseView):
11
+ @expose("/")
12
+ def index(self):
13
+ return "Success!"
14
+
15
+
16
+ app = Flask(__name__)
17
+ app.config["SECRET_KEY"] = "dev"
18
+ manager = Manager()
19
+ manager.init_app(app)
20
+ # Register the index view
21
+ manager.admin.add_view(IndexView(), is_menu=False)
22
+ # Register a mock view
23
+ manager.admin.add_view(MockView())
24
+
25
+
26
+ if __name__ == "__main__":
27
+ app.run(debug=True)
@@ -0,0 +1 @@
1
+ __version__ = "0.2.3"
@@ -1,3 +1,2 @@
1
- from .admin import Admin
2
1
  from .view import BaseView
3
2
  from .view import expose
@@ -0,0 +1,15 @@
1
+ from .base_admin import BaseAdmin
2
+ from ..utils.authorize import authorize_allow
3
+ class Admin(BaseAdmin):
4
+
5
+ def allow(self, *args, **kwargs):
6
+ return authorize_allow(*args, **kwargs)
7
+
8
+ def init_views(self):
9
+ from ..views.index_view import IndexView
10
+ from ..views.user_view import UserView
11
+ index_view = IndexView()
12
+ self.add_view(index_view, is_menu=False)
13
+
14
+ user_view = UserView()
15
+ self.add_view(user_view, is_menu=False)
@@ -3,20 +3,25 @@ from flask import url_for
3
3
  from .menu import Menu
4
4
 
5
5
 
6
- class Admin:
6
+ class BaseAdmin:
7
7
  """
8
8
  Collection of the admin views. Also manages menu structure.
9
9
  """
10
10
 
11
- def __init__(
12
- self,
13
- app=None,
14
- name="Admin",
15
- url="/admin",
16
- endpoint="admin",
17
- template_folder=None,
18
- static_folder=None,
19
- ):
11
+ def __init__(self, app=None):
12
+ self.app = app
13
+ self.name = self.get_admin_name()
14
+ self.url = self.get_admin_url()
15
+ self.endpoint = self.get_admin_endpoint()
16
+ self.template_folder = self.get_admin_template_folder()
17
+ self.static_folder = self.get_admin_static_folder()
18
+ self._views = {}
19
+ self.menu = Menu(self)
20
+ self.init_views()
21
+ if app is not None:
22
+ self.init_app(app)
23
+
24
+ def init_app(self, app):
20
25
  """
21
26
  Constructor.
22
27
 
@@ -31,18 +36,32 @@ class Admin:
31
36
  a single Flask application, you have to set a unique endpoint name for each instance.
32
37
 
33
38
  """
34
- self._access_callback = None
35
39
  self.app = app
36
- self.name = name
37
- self.endpoint = endpoint
38
- self.url = url
39
- if not self.url.startswith("/"):
40
+
41
+ # Register views
42
+ for v in self._views.values():
43
+ app.register_blueprint(v.create_blueprint())
44
+
45
+ def get_admin_name(self):
46
+ name = "Admin"
47
+ return name
48
+
49
+ def get_admin_url(self):
50
+ url = "/admin"
51
+ if not url.startswith("/"):
40
52
  raise ValueError("admin.url must startswith /")
41
- self.template_folder = template_folder or "../templates"
42
- self.static_folder = static_folder or "../static"
43
- self._views = []
44
- self.menu = Menu(self)
53
+ return url
54
+
55
+ def get_admin_endpoint(self):
56
+ return "admin"
45
57
 
58
+ def get_admin_template_folder(self):
59
+ template_folder = "../templates"
60
+ return template_folder
61
+
62
+ def get_admin_static_folder(self):
63
+ static_folder = "../static"
64
+ return static_folder
46
65
 
47
66
  def add_view(self, view, is_menu=True, category=None):
48
67
  """
@@ -54,8 +73,13 @@ class Admin:
54
73
  # attach self(admin) to view
55
74
  view.admin = self
56
75
 
57
- # Add to views
58
- self._views.append(view)
76
+ # Add to _views
77
+ view_name = view.name.lower().replace(" ", "_")
78
+
79
+ if view_name in self._views:
80
+ raise ValueError(f"View with name {view.name} already exists")
81
+
82
+ self._views[view_name] = view
59
83
 
60
84
  # If app was provided in constructor, register view with Flask app
61
85
  if self.app is not None:
@@ -66,9 +90,7 @@ class Admin:
66
90
 
67
91
  def get_url(self, endpoint, **kwargs):
68
92
  """
69
- Generate URL for the endpoint. If you want to customize URL generation
70
- logic (persist some query string argument, for example), this is
71
- right place to do it.
93
+ Generate URL for the endpoint.
72
94
 
73
95
  :param endpoint:
74
96
  Flask endpoint name
@@ -77,6 +99,15 @@ class Admin:
77
99
  """
78
100
  return url_for(endpoint, **kwargs)
79
101
 
102
+ def allow(self, *args, **kwargs):
103
+ return True
104
+
105
+ def init_views(self):
106
+ """
107
+ Initialize views. This method can be overridden in subclasses to add custom views.
108
+ """
109
+ pass
110
+
80
111
  def render(self, template, **kwargs):
81
112
  """
82
113
  Render template
@@ -86,32 +117,9 @@ class Admin:
86
117
  :param kwargs:
87
118
  Template arguments
88
119
  """
89
- # Store self as admin
120
+ # Add admin instance to kwargs
90
121
  kwargs["admin"] = self
91
- # Expose get_url helper
122
+ # Add URL generation method to kwargs
92
123
  kwargs["get_url"] = self.get_url
93
124
 
94
125
  return render_template(template, **kwargs)
95
-
96
- def init_app(self, app):
97
- """
98
- Register all views with the Flask application.
99
-
100
- :param app:
101
- Flask application instance
102
- """
103
- self.app = app
104
-
105
- # Register views
106
- for view in self._views:
107
- app.register_blueprint(view.create_blueprint())
108
-
109
- def access(self, *args, **kwargs):
110
- if self._access_callback:
111
- return self._access_callback(*args, **kwargs)
112
- else:
113
- return True
114
-
115
- def set_access_callback(self, callback):
116
- self._access_callback = callback
117
- return
@@ -3,9 +3,9 @@ import warnings
3
3
  from wtforms.fields import HiddenField
4
4
  from wtforms.fields.core import UnboundField
5
5
  from wtforms.validators import InputRequired
6
- from ...forms.widgets import XEditableWidget
7
- from ...forms.form import BaseForm
8
- from ...forms.rules import RuleSet
6
+ from ...template.widgets import XEditableWidget
7
+ from ...template.form.base_form import BaseForm
8
+ from ...template.rules import RuleSet
9
9
 
10
10
 
11
11
  def converts(*args):
@@ -32,11 +32,11 @@ from ..view import BaseView
32
32
  from ..actions import ActionsMixin
33
33
  from .. import row_action
34
34
  from ..view import expose
35
- from ...forms.form import BaseForm
36
- from ...forms import FormOpts
37
- from ...forms.rules import RuleSet
38
- from ...forms.utils import get_form_data
39
- from ...forms.utils import validate_form_on_submit
35
+ from ...template.form.base_form import BaseForm
36
+ from ...template.form.form_opts import FormOpts
37
+ from ...template.rules import RuleSet
38
+ from ...template.form.utils import get_form_data
39
+ from ...template.form.utils import validate_form_on_submit
40
40
  from ...utils import flash_errors
41
41
  from ...utils import get_redirect_target
42
42
  from ...utils import rec_getattr
@@ -703,7 +703,7 @@ class BaseModelView(BaseView, ActionsMixin):
703
703
 
704
704
  Here's simple example which illustrates how to use::
705
705
 
706
- from flask_exts.forms.form import rules
706
+ from .forms.form import rules
707
707
 
708
708
  class MyModelView(ModelView):
709
709
  form_rules = [
@@ -799,7 +799,7 @@ class BaseModelView(BaseView, ActionsMixin):
799
799
  :param menu_class_name:
800
800
  Optional class name for the menu item.
801
801
  :param menu_icon_type:
802
- Optional icon.
802
+ Optional icon.
803
803
  :param menu_icon_value:
804
804
  Icon name or URL.
805
805
  """
@@ -819,7 +819,6 @@ class BaseModelView(BaseView, ActionsMixin):
819
819
  menu_icon_value=menu_icon_value,
820
820
  )
821
821
 
822
-
823
822
  # Actions
824
823
  self.init_actions()
825
824
 
@@ -1429,7 +1428,6 @@ class BaseModelView(BaseView, ActionsMixin):
1429
1428
 
1430
1429
  return missing_fields
1431
1430
 
1432
-
1433
1431
  def _validate_form_class(self, ruleset, form_class, remove_missing=True):
1434
1432
  form_fields = []
1435
1433
  for name, obj in form_class.__dict__.items():
@@ -1557,7 +1555,7 @@ class BaseModelView(BaseView, ActionsMixin):
1557
1555
  flash(exc, "error")
1558
1556
  return True
1559
1557
 
1560
- if current_app.config.get("FLASK_EXTS_RAISE_ON_VIEW_EXCEPTION"):
1558
+ if current_app.config.get("RAISE_ON_VIEW_EXCEPTION"):
1561
1559
  raise
1562
1560
 
1563
1561
  return False