cherrypy-foundation 1.0.0a16__tar.gz → 1.0.0a18__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 (164) hide show
  1. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/.gitlab-ci.yml +3 -0
  2. {cherrypy_foundation-1.0.0a16/src/cherrypy_foundation.egg-info → cherrypy_foundation-1.0.0a18}/PKG-INFO +21 -9
  3. cherrypy_foundation-1.0.0a18/README.md +30 -0
  4. cherrypy_foundation-1.0.0a18/debian/TODO +3 -0
  5. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/copyright +1 -1
  6. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/rules +3 -5
  7. cherrypy_foundation-1.0.0a18/doc/_templates/version.html +2 -0
  8. cherrypy_foundation-1.0.0a18/doc/conf.py +69 -0
  9. cherrypy_foundation-1.0.0a18/doc/error_page.md +163 -0
  10. cherrypy_foundation-1.0.0a18/doc/flash.md +200 -0
  11. cherrypy_foundation-1.0.0a18/doc/form.md +286 -0
  12. cherrypy_foundation-1.0.0a18/doc/index.rst +8 -0
  13. cherrypy_foundation-1.0.0a18/doc/introduction.md +23 -0
  14. cherrypy_foundation-1.0.0a18/doc/modules.rst +10 -0
  15. cherrypy_foundation-1.0.0a18/doc/url.md +230 -0
  16. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/pyproject.toml +1 -1
  17. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/sonar-project.properties +1 -1
  18. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Datatable.js +2 -2
  19. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/__init__.py +2 -2
  20. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/tests/test_static.py +1 -1
  21. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/error_page.py +2 -2
  22. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/flash.py +2 -2
  23. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/form.py +2 -2
  24. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/logging.py +2 -2
  25. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/passwd.py +2 -2
  26. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/db.py +1 -1
  27. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/ldap.py +3 -1
  28. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/restapi.py +1 -1
  29. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/scheduler.py +1 -1
  30. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/smtp.py +8 -2
  31. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/test_db.py +2 -2
  32. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/test_ldap.py +1 -1
  33. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/test_scheduler.py +1 -1
  34. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/test_scheduler_db.py +1 -1
  35. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/test_smtp.py +31 -1
  36. cherrypy_foundation-1.0.0a18/src/cherrypy_foundation/sessions.py +82 -0
  37. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/__init__.py +1 -1
  38. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/test_error_page.py +1 -1
  39. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/test_flash.py +3 -3
  40. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/test_form.py +3 -3
  41. cherrypy_foundation-1.0.0a18/src/cherrypy_foundation/tests/test_logging.py +78 -0
  42. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/test_passwd.py +2 -2
  43. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/test_url.py +2 -2
  44. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/auth.py +31 -27
  45. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/auth_mfa.py +87 -83
  46. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/i18n.py +1 -1
  47. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/jinja2.py +1 -1
  48. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/ratelimit.py +33 -19
  49. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/secure_headers.py +1 -1
  50. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/sessions_timeout.py +23 -21
  51. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_auth.py +20 -3
  52. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_auth_mfa.py +6 -4
  53. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_i18n.py +1 -1
  54. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_jinja2.py +2 -2
  55. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_ratelimit.py +2 -2
  56. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/test_secure_headers.py +4 -4
  57. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/url.py +2 -2
  58. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/widgets.py +2 -2
  59. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18/src/cherrypy_foundation.egg-info}/PKG-INFO +21 -9
  60. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation.egg-info/SOURCES.txt +11 -1
  61. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/tox.ini +10 -2
  62. cherrypy_foundation-1.0.0a16/README.md +0 -18
  63. cherrypy_foundation-1.0.0a16/debian/TODO +0 -2
  64. cherrypy_foundation-1.0.0a16/debian/python3-cherrypy-foundation.docs +0 -1
  65. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/.gitignore +0 -0
  66. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/LICENSE.md +0 -0
  67. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/changelog +0 -0
  68. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/control +0 -0
  69. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/python3-cherrypy-foundation.links +0 -0
  70. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/source/format +0 -0
  71. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/debian/upstream/metadata +0 -0
  72. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/setup.cfg +0 -0
  73. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/__init__.py +0 -0
  74. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/ColorModes.jinja +0 -0
  75. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Datatable.css +0 -0
  76. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Datatable.jinja +0 -0
  77. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Field.css +0 -0
  78. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Field.jinja +0 -0
  79. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Field.js +0 -0
  80. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Fields.jinja +0 -0
  81. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Flash.jinja +0 -0
  82. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Icon.jinja +0 -0
  83. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/LocaleSelection.jinja +0 -0
  84. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/LocaleSelection.js +0 -0
  85. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/SideBySideMultiSelect.css +0 -0
  86. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/SideBySideMultiSelect.jinja +0 -0
  87. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/SideBySideMultiSelect.js +0 -0
  88. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Typeahead.css +0 -0
  89. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Typeahead.jinja +0 -0
  90. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/Typeahead.js +0 -0
  91. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/tests/__init__.py +0 -0
  92. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css +0 -0
  93. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css +0 -0
  94. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  95. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  96. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css +0 -0
  97. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css.map +0 -0
  98. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css +0 -0
  99. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css.map +0 -0
  100. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js +0 -0
  101. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js.map +0 -0
  102. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js +0 -0
  103. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js.map +0 -0
  104. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/bootstrap5/js/color-modes.js +0 -0
  105. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.css +0 -0
  106. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.min.css +0 -0
  107. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/images/sort_asc.png +0 -0
  108. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/images/sort_asc_disabled.png +0 -0
  109. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/images/sort_both.png +0 -0
  110. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/images/sort_desc.png +0 -0
  111. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/images/sort_desc_disabled.png +0 -0
  112. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/js/dataTables.js +0 -0
  113. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables/js/dataTables.min.js +0 -0
  114. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.css +0 -0
  115. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css +0 -0
  116. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.js +0 -0
  117. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.min.js +0 -0
  118. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.js +0 -0
  119. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js +0 -0
  120. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css +0 -0
  121. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.min.css +0 -0
  122. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.js +0 -0
  123. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js +0 -0
  124. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.js +0 -0
  125. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.min.js +0 -0
  126. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.css +0 -0
  127. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css +0 -0
  128. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.js +0 -0
  129. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js +0 -0
  130. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.js +0 -0
  131. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.min.js +0 -0
  132. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/vfs_fonts.js +0 -0
  133. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.css +0 -0
  134. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css +0 -0
  135. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js +0 -0
  136. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.min.js +0 -0
  137. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/jquery/jquery.min.js +0 -0
  138. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/multi/LICENSE +0 -0
  139. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/multi/README.md +0 -0
  140. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/multi/multi.css +0 -0
  141. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/multi/multi.js +0 -0
  142. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/popper/popper.js +0 -0
  143. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/popper/popper.min.js +0 -0
  144. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css +0 -0
  145. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js +0 -0
  146. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/__init__.py +0 -0
  147. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/plugins/tests/__init__.py +0 -0
  148. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/templates/test_flash.html +0 -0
  149. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/templates/test_form.html +0 -0
  150. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tests/templates/test_url.html +0 -0
  151. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/__init__.py +0 -0
  152. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/__init__.py +0 -0
  153. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/components/Button.jinja +0 -0
  154. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
  155. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po +0 -0
  156. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo +0 -0
  157. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po +0 -0
  158. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/locales/messages.pot +0 -0
  159. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/templates/test_jinja2.html +0 -0
  160. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/templates/test_jinjax.html +0 -0
  161. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +0 -0
  162. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation.egg-info/dependency_links.txt +0 -0
  163. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation.egg-info/requires.txt +0 -0
  164. {cherrypy_foundation-1.0.0a16 → cherrypy_foundation-1.0.0a18}/src/cherrypy_foundation.egg-info/top_level.txt +0 -0
@@ -26,6 +26,9 @@ isort:
26
26
  djlint:
27
27
  <<: *tox
28
28
 
29
+ doc:
30
+ <<: *tox
31
+
29
32
  py3:
30
33
  <<: *tox
31
34
  artifacts:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cherrypy-foundation
3
- Version: 1.0.0a16
4
- Summary: CherryPy Foundation
3
+ Version: 1.0.0a18
4
+ Summary: Cherrypy-foundation
5
5
  Author-email: Patrik Dufresne <patrik@ikus-soft.com>
6
6
  License: GPLv3
7
7
  Project-URL: Homepage, https://gitlab.com/ikus-soft/cherrypy-foundation
@@ -48,12 +48,24 @@ Dynamic: license-file
48
48
  <a href="https://sonar.ikus-soft.com/dashboard?id=cherrypy-foundation"><img alt="Coverage" src="https://sonar.ikus-soft.com/api/project_badges/measure?project=cherrypy-foundation&metric=coverage"></a>
49
49
  </p>
50
50
 
51
- This project provides a collection of reusable utilities that simplify
52
- building web applications with CherryPy. It offers built-in integrations
53
- with SQLAlchemy for database access, Jinja2 for templating, WTForms for
54
- form handling, and Bootstrap for UI components, helping streamline
55
- application development.
51
+ Cherrypy-foundation is a comprehensive toolkit that accelerates web application development with CherryPy. It provides a curated collection of utilities and integrations that handle common web development tasks, allowing you to focus on building your application's unique features.
56
52
 
57
- # Usage
53
+ ## What's Included
58
54
 
59
- TODO
55
+ - **Database Integration**: Seamless SQLAlchemy integration for database operations
56
+ - **Template Engine**: Jinja2 and JinjaX support for flexible, component-based templating
57
+ - **Form Handling**: Enhanced WTForms integration with automatic validation and Bootstrap rendering
58
+ - **URL Management**: Flexible URL generation with `url_for` utility
59
+ - **Error Handling**: Smart error pages that adapt to response content type (HTML, JSON, plain text)
60
+ - **UI Components**: Bootstrap-ready components for rapid interface development
61
+
62
+ ## Who Is This For?
63
+
64
+ Cherrypy-foundation is designed for developers who:
65
+
66
+ - Want to build modern web applications with CherryPy without reinventing the wheel
67
+ - Need a lightweight alternative to full-stack frameworks while maintaining flexibility
68
+ - Prefer convention over configuration but value customization options
69
+ - Want battle-tested components that integrate seamlessly with CherryPy's architecture
70
+
71
+ This documentation will guide you through all available features, from basic utilities to advanced integrations, with practical examples to help you get started quickly.
@@ -0,0 +1,30 @@
1
+ # Cherrypy-foundation
2
+
3
+ <p align="center">
4
+ <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-GPL--3.0-orange"></a>
5
+ <a href="https://gitlab.com/ikus-soft/cherrypy-foundation/pipelines"><img alt="Build" src="https://gitlab.com/ikus-soft/cherrypy-foundation/badges/master/pipeline.svg"></a>
6
+ <a href="https://sonar.ikus-soft.com/dashboard?id=cherrypy-foundation"><img alt="Quality Gate" src="https://sonar.ikus-soft.com/api/project_badges/measure?project=cherrypy-foundation&metric=alert_status"></a>
7
+ <a href="https://sonar.ikus-soft.com/dashboard?id=cherrypy-foundation"><img alt="Coverage" src="https://sonar.ikus-soft.com/api/project_badges/measure?project=cherrypy-foundation&metric=coverage"></a>
8
+ </p>
9
+
10
+ Cherrypy-foundation is a comprehensive toolkit that accelerates web application development with CherryPy. It provides a curated collection of utilities and integrations that handle common web development tasks, allowing you to focus on building your application's unique features.
11
+
12
+ ## What's Included
13
+
14
+ - **Database Integration**: Seamless SQLAlchemy integration for database operations
15
+ - **Template Engine**: Jinja2 and JinjaX support for flexible, component-based templating
16
+ - **Form Handling**: Enhanced WTForms integration with automatic validation and Bootstrap rendering
17
+ - **URL Management**: Flexible URL generation with `url_for` utility
18
+ - **Error Handling**: Smart error pages that adapt to response content type (HTML, JSON, plain text)
19
+ - **UI Components**: Bootstrap-ready components for rapid interface development
20
+
21
+ ## Who Is This For?
22
+
23
+ Cherrypy-foundation is designed for developers who:
24
+
25
+ - Want to build modern web applications with CherryPy without reinventing the wheel
26
+ - Need a lightweight alternative to full-stack frameworks while maintaining flexibility
27
+ - Prefer convention over configuration but value customization options
28
+ - Want battle-tested components that integrate seamlessly with CherryPy's architecture
29
+
30
+ This documentation will guide you through all available features, from basic utilities to advanced integrations, with practical examples to help you get started quickly.
@@ -0,0 +1,3 @@
1
+ - Symlink for all vendor files - bootstrap, jquery, etc.
2
+ - How to run the unit test with the symlink files
3
+ - symlink for sphinxdoc warning: ignoring unknown JavaScript code: debian/python3-cherrypy-foundation/usr/share/doc/python3-cherrypy-foundation/_static/scripts/fontawesome.js
@@ -6,7 +6,7 @@ Files-Excluded:
6
6
  src/cherrypy_foundation/components/vendor
7
7
 
8
8
  Files: *
9
- Copyright: 2019-2025 Patrik Dufresne <patrik@ikus-soft.com>
9
+ Copyright: 2019-2026 IKUS Software <patrik@ikus-soft.com>
10
10
  License: GPL-3+
11
11
 
12
12
  License: GPL-3+
@@ -11,11 +11,7 @@ export PYBUILD_SYSTEM=pyproject
11
11
  export TZ=UTC
12
12
 
13
13
  %:
14
- dh $@ --buildsystem=pybuild --test-tox
15
-
16
- # Remove embedded copies of JavaScript libraries.
17
- override_dh_auto_build:
18
- dh_auto_build
14
+ dh $@ --with python3 --buildsystem=pybuild --test-tox
19
15
 
20
16
  # Install symlink before the test for JavaScript libraries.
21
17
  override_dh_auto_test:
@@ -23,6 +19,8 @@ override_dh_auto_test:
23
19
 
24
20
  execute_before_dh_auto_clean:
25
21
  rm -rf *.egg-info
22
+ # Clean Sphinx build artifacts
23
+ rm -rf build/html docs/_build
26
24
 
27
25
  # Generate orig.tar.gz
28
26
  gentarball: SOURCE=cherrypy-foundation
@@ -0,0 +1,2 @@
1
+ <!-- This will display the version of the docs -->
2
+ cherrypy-foundation {{ version }}
@@ -0,0 +1,69 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # This file only contains a selection of the most common options. For a full
4
+ # list see the documentation:
5
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
6
+
7
+ # -- Path setup --------------------------------------------------------------
8
+
9
+ # If extensions (or modules to document with autodoc) are in another directory,
10
+ # add these directories to sys.path here. If the directory is relative to the
11
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
12
+ #
13
+ # import os
14
+ # import sys
15
+ # sys.path.insert(0, os.path.abspath('.'))
16
+
17
+ # -- Project information -----------------------------------------------------
18
+
19
+ project = 'Cherrypy-foundation'
20
+ copyright = 'Copyright (C) 2026 IKUS Software'
21
+ author = 'Patrik Dufresne'
22
+
23
+ # -- General configuration ---------------------------------------------------
24
+
25
+ # Add any Sphinx extension module names here, as strings. They can be
26
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
27
+ # ones.
28
+ extensions = [
29
+ 'myst_parser',
30
+ 'sphinx.ext.autosectionlabel',
31
+ ]
32
+
33
+ autosectionlabel_prefix_document = True
34
+
35
+ # Enable anchors for cross references
36
+ myst_heading_anchors = 2
37
+
38
+ # Add any paths that contain templates here, relative to this directory.
39
+ templates_path = ['_templates']
40
+
41
+ # List of patterns, relative to source directory, that match files and
42
+ # directories to ignore when looking for source files.
43
+ # This pattern also affects html_static_path and html_extra_path.
44
+ exclude_patterns = []
45
+
46
+ # -- Options for HTML output -------------------------------------------------
47
+
48
+ # The theme to use for HTML and HTML Help pages. See the documentation for
49
+ # a list of builtin themes.
50
+ #
51
+ html_theme = 'pydata_sphinx_theme'
52
+ # Ref: https://alabaster.readthedocs.io/en/latest/customization.html#theme-options
53
+ html_theme_options = {
54
+ "footer_start": ["copyright"],
55
+ "footer_end": ["version"],
56
+ 'use_edit_page_button': True,
57
+ }
58
+ html_context = {
59
+ "gitlab_user": "ikus-soft",
60
+ "gitlab_repo": "cherrypy-foundation",
61
+ "gitlab_version": "main",
62
+ "doc_path": "doc",
63
+ }
64
+ html_show_sourcelink = False
65
+
66
+ # Add any paths that contain custom static files (such as style sheets) here,
67
+ # relative to this directory. They are copied after the builtin static files,
68
+ # so a file named "default.css" will overwrite the builtin "default.css".
69
+ #html_static_path = ['_static']
@@ -0,0 +1,163 @@
1
+ # Error Pages
2
+
3
+ The `error_page` utility replaces CherryPy's default error pages with a more flexible error handler.
4
+ It generates error responses based on the response `Content-Type`, returning either:
5
+
6
+ - an HTML page (default),
7
+ - a JSON error response for `application/json`
8
+ - plain text for `text/plain`.
9
+
10
+ If available, the HTML error page is rendered using Jinja2 with a template named `error_page.html`.
11
+
12
+ ⚠️ **Note:** For 404 errors, the error handler sanitizes the error message to avoid leaking path information. A generic message is returned instead of exposing the requested path.
13
+
14
+ ## Setup
15
+
16
+ ```python
17
+ from cherrypy_foundation.error_page import error_page
18
+
19
+ cherrypy.config.update({
20
+ 'error_page.default': error_page,
21
+ })
22
+ ```
23
+
24
+ ## Features
25
+
26
+ ### Content Negotiation
27
+
28
+ The error handler automatically detects the expected response format:
29
+
30
+ **HTML Response** (default for browsers):
31
+ ```http
32
+ GET /invalid-page HTTP/1.1
33
+ Accept: text/html
34
+
35
+ HTTP/1.1 404 Not Found
36
+ Content-Type: text/html
37
+
38
+ <!DOCTYPE html>
39
+ <html>
40
+ <head><title>404 Not Found</title></head>
41
+ <body>
42
+ <h2>404 Not Found</h2>
43
+ <p>Nothing matches the given URI</p>
44
+ </body>
45
+ </html>
46
+ ```
47
+
48
+ **JSON Response** (for API clients):
49
+ ```http
50
+ GET /api/invalid HTTP/1.1
51
+ Accept: application/json
52
+
53
+ HTTP/1.1 404 Not Found
54
+ Content-Type: application/json
55
+
56
+ {"message": "Nothing matches the given URI", "status": "404 Not Found"}
57
+ ```
58
+
59
+ **Plain Text Response**:
60
+ ```http
61
+ GET /invalid HTTP/1.1
62
+ Accept: text/plain
63
+
64
+ HTTP/1.1 404 Not Found
65
+ Content-Type: text/plain
66
+
67
+ Nothing matches the given URI
68
+ ```
69
+
70
+ ### Server Error Logging
71
+
72
+ All internal server error 500 are automatically logged with full traceback information:
73
+
74
+ ```python
75
+ @cherrypy.expose
76
+ def broken(self):
77
+ raise ValueError("Something went wrong!")
78
+
79
+ # Logs: error page status=500 Internal Server Error message=ValueError: Something went wrong!
80
+ # [full traceback]
81
+ ```
82
+
83
+ ### Custom Error Pages with Jinja2
84
+
85
+ To use custom error page templates, create an `error_page.html` template in your Jinja2 environment:
86
+
87
+ The following variables are available in the `error_page.html` template:
88
+
89
+ - `status` (str): HTTP status code and message (e.g., "404 Not Found")
90
+ - `message` (str): Detailed error message
91
+ - `traceback` (str): Stack trace (only for server errors when debug mode is enabled)
92
+ - `version` (str): CherryPy version
93
+
94
+
95
+ ```html
96
+ <!DOCTYPE html>
97
+ <html lang="en">
98
+ <head>
99
+ <meta charset="utf-8">
100
+ <title>{{ status }}</title>
101
+ <link rel="stylesheet" href="/static/css/bootstrap.min.css">
102
+ </head>
103
+ <body>
104
+ <div class="container mt-5">
105
+ <div class="alert alert-danger">
106
+ <h1>{{ status }}</h1>
107
+ <p>{{ message }}</p>
108
+
109
+ {% if traceback %}
110
+ <details>
111
+ <summary>Technical Details</summary>
112
+ <pre>{{ traceback }}</pre>
113
+ </details>
114
+ {% endif %}
115
+ </div>
116
+ </div>
117
+ </body>
118
+ </html>
119
+ ```
120
+
121
+ ## Complete Example
122
+
123
+ ```python
124
+ import cherrypy
125
+ from cherrypy_foundation.error_page import error_page
126
+
127
+ # Setup Jinja2
128
+ env = cherrypy.tools.jinja2.create_env(
129
+ package_name=__package__,
130
+ )
131
+
132
+ # Configure error handler
133
+ cherrypy.config.update({
134
+ 'error_page.default': error_page,
135
+ })
136
+
137
+ @cherrypy.tools.jinja2(env=env)
138
+ class Root:
139
+
140
+ @cherrypy.expose
141
+ @cherrypy.tools.jinja2(template='index.html')
142
+ def index(self):
143
+ return {}
144
+
145
+ @cherrypy.expose
146
+ def not_found(self):
147
+ # Will trigger 404 error page
148
+ raise cherrypy.NotFound()
149
+
150
+ @cherrypy.expose
151
+ def server_error(self):
152
+ # Will trigger 500 error page with logging
153
+ raise ValueError("Database connection failed")
154
+
155
+ @cherrypy.expose
156
+ @cherrypy.tools.json_out()
157
+ def api_endpoint(self):
158
+ # API errors will return JSON
159
+ raise cherrypy.HTTPError(400, "Invalid request parameters")
160
+
161
+ if __name__ == '__main__':
162
+ cherrypy.quickstart(Root())
163
+ ```
@@ -0,0 +1,200 @@
1
+ # Flash Messages
2
+
3
+ Flash messages are used to display informational, warning, or error messages to the user.
4
+ They are especially useful for reporting success or validation errors after form submissions.
5
+
6
+ ⚠️ **Important:** Flash messages require the CherryPy **sessions tool** to be enabled, as messages are stored in the user session.
7
+
8
+ ## How It Works
9
+
10
+ 1. **Storage**: Flash messages are stored in the user's session under the `flash` key
11
+ 2. **Retrieval**: `get_flashed_messages()` retrieves all messages and clears them from the session
12
+ 3. **One-time display**: Messages are automatically removed after being retrieved, ensuring they only display once
13
+
14
+ This makes flash messages perfect for post-redirect-get patterns where you want to show a message after a form submission.
15
+
16
+ ## Setup
17
+
18
+ ### 1. Enable Sessions and Configure Jinja2
19
+
20
+ ```python
21
+ import cherrypy
22
+ from cherrypy_foundation.flash import get_flashed_messages
23
+
24
+ # Create Jinja2 environment with get_flashed_messages available
25
+ env = cherrypy.tools.jinja2.create_env(
26
+ package_name=__package__,
27
+ globals={'get_flashed_messages': get_flashed_messages},
28
+ )
29
+
30
+ @cherrypy.tools.sessions(locking='explicit') # Required for flash messages
31
+ @cherrypy.tools.jinja2(env=env)
32
+ class Root:
33
+ pass
34
+ ```
35
+
36
+ ### 2. Display Messages in Templates
37
+
38
+ Call `get_flashed_messages()` in your Jinja2 template to retrieve and display flash messages:
39
+
40
+ ```html
41
+ <!DOCTYPE html>
42
+ <html lang="en">
43
+ <head>
44
+ <title>My Page</title>
45
+ <link rel="stylesheet" href="/static/css/bootstrap.min.css">
46
+ </head>
47
+ <body>
48
+ <!-- Display flash messages -->
49
+ {% for message in get_flashed_messages() %}
50
+ <div class="alert alert-{{ message.level }} alert-dismissible fade show">
51
+ {{ message.message }}
52
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
53
+ </div>
54
+ {% endfor %}
55
+
56
+ <!-- Page content -->
57
+ <h1>Welcome!</h1>
58
+ </body>
59
+ </html>
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ### Adding Flash Messages
65
+
66
+ Use the `flash()` function to add messages in your handler:
67
+
68
+ ```python
69
+ from cherrypy_foundation.flash import flash
70
+
71
+ @cherrypy.expose
72
+ def save_profile(self, **kwargs):
73
+ # Save user profile...
74
+ flash('Profile updated successfully!', level='success')
75
+ raise cherrypy.HTTPRedirect('/profile')
76
+ ```
77
+
78
+ ### Message Levels
79
+
80
+ Flash messages support four severity levels:
81
+
82
+ - `info` - Informational messages (default)
83
+ - `success` - Success confirmations
84
+ - `warning` - Warning messages
85
+ - `error` - Error messages
86
+
87
+ ```python
88
+ flash('Account created successfully', level='success')
89
+ flash('Please verify your email address', level='info')
90
+ flash('Your session will expire in 5 minutes', level='warning')
91
+ flash('Failed to save changes', level='error')
92
+ ```
93
+
94
+ ### HTML Markup Support
95
+
96
+ Flash messages support both plain strings and HTML markup:
97
+
98
+ ```python
99
+ from markupsafe import Markup
100
+
101
+ # Plain text
102
+ flash('Simple message', level='info')
103
+
104
+ # HTML markup (safe HTML)
105
+ flash(Markup('Click <a href="/help">here</a> for help'), level='info')
106
+ ```
107
+
108
+ ### Using the JinjaX Flash Component
109
+
110
+ If you have JinjaX installed, you can use the built-in `<Flash />` component for easier rendering:
111
+
112
+ ```html
113
+ <!DOCTYPE html>
114
+ <html lang="en">
115
+ <head>
116
+ <title>My Page</title>
117
+ </head>
118
+ <body>
119
+ <Flash
120
+ messages="{{ get_flashed_messages() }}"
121
+ class="mb-2"
122
+ style="min-height: 100px"
123
+ />
124
+
125
+ <h1>Welcome!</h1>
126
+ </body>
127
+ </html>
128
+ ```
129
+
130
+ The `<Flash />` component automatically handles:
131
+ - Rendering messages with appropriate Bootstrap alert classes
132
+ - Dismiss buttons for each message
133
+ - Custom styling via `class` and `style` attributes
134
+
135
+ ## Complete Example
136
+
137
+ ```python
138
+ import cherrypy
139
+ import cherrypy_foundation.tools.jinja2
140
+ from cherrypy_foundation.flash import flash, get_flashed_messages
141
+
142
+ # Setup Jinja2
143
+ env = cherrypy.tools.jinja2.create_env(
144
+ package_name=__package__,
145
+ globals={'get_flashed_messages': get_flashed_messages},
146
+ )
147
+
148
+ @cherrypy.tools.sessions(locking='explicit')
149
+ @cherrypy.tools.jinja2(env=env)
150
+ class Root:
151
+
152
+ @cherrypy.expose
153
+ @cherrypy.tools.jinja2(template='index.html')
154
+ def index(self):
155
+ return {}
156
+
157
+ @cherrypy.expose
158
+ def submit(self, name=None):
159
+ if not name:
160
+ flash('Name is required', level='error')
161
+ else:
162
+ flash(f'Welcome, {name}!', level='success')
163
+
164
+ raise cherrypy.HTTPRedirect('/')
165
+
166
+ if __name__ == '__main__':
167
+ cherrypy.quickstart(Root())
168
+ ```
169
+
170
+ **Template: `form.html`**
171
+
172
+ ```html
173
+ <!DOCTYPE html>
174
+ <html lang="en">
175
+ <head>
176
+ <title>Registration Form</title>
177
+ <link rel="stylesheet" href="/static/css/bootstrap.min.css">
178
+ </head>
179
+ <body>
180
+ <div class="container mt-5">
181
+ <!-- Flash messages -->
182
+ {% for message in get_flashed_messages() %}
183
+ <div class="alert alert-{{ message.level }} alert-dismissible">
184
+ {{ message.message }}
185
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
186
+ </div>
187
+ {% endfor %}
188
+
189
+ <!-- Form -->
190
+ <form method="post" action="/submit">
191
+ <div class="mb-3">
192
+ <label for="name" class="form-label">Name</label>
193
+ <input type="text" class="form-control" id="name" name="name">
194
+ </div>
195
+ <button type="submit" class="btn btn-primary">Submit</button>
196
+ </form>
197
+ </div>
198
+ </body>
199
+ </html>
200
+ ```