WuttaWeb 0.17.2__tar.gz → 0.19.0__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.
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/CHANGELOG.md +17 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/PKG-INFO +5 -3
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/README.md +1 -1
- wuttaweb-0.19.0/docs/api/wuttaweb.cli.rst +6 -0
- wuttaweb-0.19.0/docs/api/wuttaweb.cli.webapp.rst +6 -0
- wuttaweb-0.19.0/docs/api/wuttaweb.emails.rst +6 -0
- wuttaweb-0.19.0/docs/api/wuttaweb.views.email.rst +6 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/conf.py +1 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/index.rst +5 -0
- wuttaweb-0.19.0/docs/narr/cli/builtin.rst +28 -0
- wuttaweb-0.19.0/docs/narr/cli/index.rst +14 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/pyproject.toml +7 -6
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/app.py +104 -26
- wuttaweb-0.19.0/src/wuttaweb/cli/__init__.py +28 -0
- wuttaweb-0.19.0/src/wuttaweb/cli/webapp.py +88 -0
- wuttaweb-0.19.0/src/wuttaweb/emails.py +48 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/forms/schema.py +34 -1
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/forms/widgets.py +37 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/menus.py +6 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/base.mako +11 -7
- wuttaweb-0.19.0/src/wuttaweb/templates/deform/readonly/email_recips.pt +5 -0
- wuttaweb-0.19.0/src/wuttaweb/templates/email/settings/view.mako +39 -0
- wuttaweb-0.19.0/src/wuttaweb/views/email.py +298 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/essential.py +2 -0
- wuttaweb-0.19.0/tests/cli/test_webapp.py +104 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/forms/test_schema.py +44 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/forms/test_widgets.py +50 -1
- wuttaweb-0.19.0/tests/test_app.py +131 -0
- wuttaweb-0.19.0/tests/test_emails.py +23 -0
- wuttaweb-0.19.0/tests/views/__init__.py +0 -0
- wuttaweb-0.19.0/tests/views/test_email.py +211 -0
- wuttaweb-0.19.0/tests/views/test_essential.py +10 -0
- wuttaweb-0.17.2/tests/test_app.py +0 -61
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/.gitignore +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/COPYING.txt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/Makefile +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/make.bat +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.17.2/src/wuttaweb/email/templates → wuttaweb-0.19.0/src/wuttaweb/email-templates}/feedback.html.mako +0 -0
- {wuttaweb-0.17.2/src/wuttaweb/email/templates → wuttaweb-0.19.0/src/wuttaweb/email-templates}/feedback.txt.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/grids/base.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/master.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tasks.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/__init__.py +0 -0
- {wuttaweb-0.17.2/tests/db → wuttaweb-0.19.0/tests/cli}/__init__.py +0 -0
- {wuttaweb-0.17.2/tests/forms → wuttaweb-0.19.0/tests/db}/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.17.2/tests/grids → wuttaweb-0.19.0/tests/forms}/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.17.2/tests/views → wuttaweb-0.19.0/tests/grids}/__init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/grids/test_base.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/grids/test_filters.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_progress.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_static.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/test_util.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/util.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_master.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tests/views/test_users.py +0 -0
- {wuttaweb-0.17.2 → wuttaweb-0.19.0}/tox.ini +0 -0
|
@@ -5,6 +5,23 @@ All notable changes to wuttaweb will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v0.19.0 (2024-12-23)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add feature to edit email settings, basic message preview
|
|
13
|
+
|
|
14
|
+
### Fix
|
|
15
|
+
|
|
16
|
+
- move CRUD header buttons toward center of screen
|
|
17
|
+
|
|
18
|
+
## v0.18.0 (2024-12-18)
|
|
19
|
+
|
|
20
|
+
### Feat
|
|
21
|
+
|
|
22
|
+
- add basic support for running in ASGI context
|
|
23
|
+
- add support for running via uvicorn; `wutta webapp` command
|
|
24
|
+
|
|
8
25
|
## v0.17.2 (2024-12-17)
|
|
9
26
|
|
|
10
27
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: Web App for Wutta Framework
|
|
5
5
|
Project-URL: Homepage, https://wuttaproject.org/
|
|
6
6
|
Project-URL: Repository, https://forgejo.wuttaproject.org/wutta/wuttaweb
|
|
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
25
25
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
|
+
Requires-Dist: asgiref
|
|
28
29
|
Requires-Dist: colanderalchemy
|
|
29
30
|
Requires-Dist: humanize
|
|
30
31
|
Requires-Dist: markdown
|
|
@@ -38,20 +39,21 @@ Requires-Dist: pyramid-tm
|
|
|
38
39
|
Requires-Dist: pyramid>=2
|
|
39
40
|
Requires-Dist: waitress
|
|
40
41
|
Requires-Dist: webhelpers2
|
|
41
|
-
Requires-Dist: wuttjamaican[db]>=0.
|
|
42
|
+
Requires-Dist: wuttjamaican[db]>=0.19.0
|
|
42
43
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
43
44
|
Provides-Extra: continuum
|
|
44
45
|
Requires-Dist: wutta-continuum; extra == 'continuum'
|
|
45
46
|
Provides-Extra: docs
|
|
46
47
|
Requires-Dist: furo; extra == 'docs'
|
|
47
48
|
Requires-Dist: sphinx; extra == 'docs'
|
|
49
|
+
Requires-Dist: sphinxcontrib-programoutput; extra == 'docs'
|
|
48
50
|
Provides-Extra: tests
|
|
49
51
|
Requires-Dist: pytest-cov; extra == 'tests'
|
|
50
52
|
Requires-Dist: tox; extra == 'tests'
|
|
51
53
|
Description-Content-Type: text/markdown
|
|
52
54
|
|
|
53
55
|
|
|
54
|
-
#
|
|
56
|
+
# WuttaWeb
|
|
55
57
|
|
|
56
58
|
Web app for Wutta Framework
|
|
57
59
|
|
|
@@ -19,6 +19,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
19
19
|
:caption: Documentation:
|
|
20
20
|
|
|
21
21
|
glossary
|
|
22
|
+
narr/cli/index
|
|
22
23
|
narr/templates/index
|
|
23
24
|
|
|
24
25
|
.. toctree::
|
|
@@ -28,10 +29,13 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
28
29
|
api/wuttaweb
|
|
29
30
|
api/wuttaweb.app
|
|
30
31
|
api/wuttaweb.auth
|
|
32
|
+
api/wuttaweb.cli
|
|
33
|
+
api/wuttaweb.cli.webapp
|
|
31
34
|
api/wuttaweb.conf
|
|
32
35
|
api/wuttaweb.db
|
|
33
36
|
api/wuttaweb.db.continuum
|
|
34
37
|
api/wuttaweb.db.sess
|
|
38
|
+
api/wuttaweb.emails
|
|
35
39
|
api/wuttaweb.forms
|
|
36
40
|
api/wuttaweb.forms.base
|
|
37
41
|
api/wuttaweb.forms.schema
|
|
@@ -51,6 +55,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
51
55
|
api/wuttaweb.views.base
|
|
52
56
|
api/wuttaweb.views.batch
|
|
53
57
|
api/wuttaweb.views.common
|
|
58
|
+
api/wuttaweb.views.email
|
|
54
59
|
api/wuttaweb.views.essential
|
|
55
60
|
api/wuttaweb.views.master
|
|
56
61
|
api/wuttaweb.views.people
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
===================
|
|
3
|
+
Built-in Commands
|
|
4
|
+
===================
|
|
5
|
+
|
|
6
|
+
Below are the :term:`subcommands <subcommand>` which come with
|
|
7
|
+
WuttaWeb.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
.. _wutta-webapp:
|
|
11
|
+
|
|
12
|
+
``wutta webapp``
|
|
13
|
+
----------------
|
|
14
|
+
|
|
15
|
+
Run the web app, according to config file(s).
|
|
16
|
+
|
|
17
|
+
This command is a convenience only; under the hood it can run `uvicorn
|
|
18
|
+
<https://www.uvicorn.org/#uvicornrun>`_ but by default will run
|
|
19
|
+
whatever :ref:`pserve <pyramid:pserve_script>` is setup to do (which
|
|
20
|
+
usually is `waitress
|
|
21
|
+
<https://docs.pylonsproject.org/projects/waitress/en/latest/index.html>`_).
|
|
22
|
+
|
|
23
|
+
Ultimately it's all up to config, so run different web apps with
|
|
24
|
+
different config files.
|
|
25
|
+
|
|
26
|
+
Defined in: :mod:`wuttaweb.cli.webapp`
|
|
27
|
+
|
|
28
|
+
.. program-output:: wutta webapp --help
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.19.0"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
]
|
|
31
31
|
requires-python = ">= 3.8"
|
|
32
32
|
dependencies = [
|
|
33
|
+
"asgiref",
|
|
33
34
|
"ColanderAlchemy",
|
|
34
35
|
"humanize",
|
|
35
36
|
"markdown",
|
|
@@ -43,32 +44,32 @@ dependencies = [
|
|
|
43
44
|
"pyramid_tm",
|
|
44
45
|
"waitress",
|
|
45
46
|
"WebHelpers2",
|
|
46
|
-
"WuttJamaican[db]>=0.
|
|
47
|
+
"WuttJamaican[db]>=0.19.0",
|
|
47
48
|
"zope.sqlalchemy>=1.5",
|
|
48
49
|
]
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
[project.optional-dependencies]
|
|
52
53
|
continuum = ["Wutta-Continuum"]
|
|
53
|
-
docs = ["Sphinx", "furo"]
|
|
54
|
+
docs = ["Sphinx", "furo", "sphinxcontrib-programoutput"]
|
|
54
55
|
tests = ["pytest-cov", "tox"]
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
[project.entry-points."fanstatic.libraries"]
|
|
58
59
|
wuttaweb_img = "wuttaweb.static:img"
|
|
59
60
|
|
|
60
|
-
|
|
61
61
|
[project.entry-points."paste.app_factory"]
|
|
62
62
|
main = "wuttaweb.app:main"
|
|
63
63
|
|
|
64
|
-
|
|
65
64
|
[project.entry-points."wutta.app.providers"]
|
|
66
65
|
wuttaweb = "wuttaweb.app:WebAppProvider"
|
|
67
66
|
|
|
68
|
-
|
|
69
67
|
[project.entry-points."wutta.config.extensions"]
|
|
70
68
|
wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
|
|
71
69
|
|
|
70
|
+
[project.entry-points."wutta.typer_imports"]
|
|
71
|
+
wuttaweb = "wuttaweb.cli"
|
|
72
|
+
|
|
72
73
|
|
|
73
74
|
[project.urls]
|
|
74
75
|
Homepage = "https://wuttaproject.org/"
|
|
@@ -24,23 +24,31 @@
|
|
|
24
24
|
Application
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
+
import logging
|
|
27
28
|
import os
|
|
28
29
|
|
|
29
30
|
from wuttjamaican.app import AppProvider
|
|
30
31
|
from wuttjamaican.conf import make_config
|
|
31
32
|
|
|
33
|
+
from asgiref.wsgi import WsgiToAsgi
|
|
32
34
|
from pyramid.config import Configurator
|
|
33
35
|
|
|
34
36
|
import wuttaweb.db
|
|
35
37
|
from wuttaweb.auth import WuttaSecurityPolicy
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
log = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
38
43
|
class WebAppProvider(AppProvider):
|
|
39
44
|
"""
|
|
40
45
|
The :term:`app provider` for WuttaWeb. This adds some methods to
|
|
41
|
-
the :term:`app handler`, which are specific to web apps.
|
|
46
|
+
the :term:`app handler`, which are specific to web apps. It also
|
|
47
|
+
registers some :term:`email templates <email template>` for the
|
|
48
|
+
app, etc.
|
|
42
49
|
"""
|
|
43
|
-
|
|
50
|
+
email_modules = ['wuttaweb.emails']
|
|
51
|
+
email_templates = ['wuttaweb:email-templates']
|
|
44
52
|
|
|
45
53
|
def get_web_handler(self, **kwargs):
|
|
46
54
|
"""
|
|
@@ -87,17 +95,20 @@ def make_wutta_config(settings, config_maker=None, **kwargs):
|
|
|
87
95
|
|
|
88
96
|
If this config file path cannot be discovered, an error is raised.
|
|
89
97
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
wutta_config = settings.get('wutta_config')
|
|
99
|
+
if not wutta_config:
|
|
100
|
+
|
|
101
|
+
# validate config file path
|
|
102
|
+
path = settings.get('wutta.config')
|
|
103
|
+
if not path or not os.path.exists(path):
|
|
104
|
+
raise ValueError("Please set 'wutta.config' in [app:main] "
|
|
105
|
+
"section of config to the path of your "
|
|
106
|
+
"config file. Lame, but necessary.")
|
|
107
|
+
|
|
108
|
+
# make config, add to settings
|
|
109
|
+
config_maker = config_maker or make_config
|
|
110
|
+
wutta_config = config_maker(path, **kwargs)
|
|
111
|
+
settings['wutta_config'] = wutta_config
|
|
101
112
|
|
|
102
113
|
# configure database sessions
|
|
103
114
|
if hasattr(wutta_config, 'appdb_engine'):
|
|
@@ -148,19 +159,18 @@ def make_pyramid_config(settings):
|
|
|
148
159
|
|
|
149
160
|
def main(global_config, **settings):
|
|
150
161
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
instead.
|
|
162
|
+
Make and return the WSGI application, per given settings.
|
|
163
|
+
|
|
164
|
+
This function is designed to be called via Paste, hence it does
|
|
165
|
+
require params and therefore can't be used directly as app factory
|
|
166
|
+
for general WSGI servers. For the latter see
|
|
167
|
+
:func:`make_wsgi_app()` instead.
|
|
168
|
+
|
|
169
|
+
And this *particular* function is not even that useful, it only
|
|
170
|
+
constructs an app with minimal views built-in to WuttaWeb. Most
|
|
171
|
+
apps will define their own ``main()`` function (e.g. as
|
|
172
|
+
``poser.web.app:main``), similar to this one but with additional
|
|
173
|
+
views and other config.
|
|
164
174
|
"""
|
|
165
175
|
wutta_config = make_wutta_config(settings)
|
|
166
176
|
pyramid_config = make_pyramid_config(settings)
|
|
@@ -170,3 +180,71 @@ def main(global_config, **settings):
|
|
|
170
180
|
pyramid_config.include('wuttaweb.views')
|
|
171
181
|
|
|
172
182
|
return pyramid_config.make_wsgi_app()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def make_wsgi_app(main_app=None, config=None):
|
|
186
|
+
"""
|
|
187
|
+
Make and return a WSGI app, using the given Paste app factory.
|
|
188
|
+
|
|
189
|
+
See also :func:`make_asgi_app()` for the ASGI equivalent.
|
|
190
|
+
|
|
191
|
+
This function could be used directly for general WSGI servers
|
|
192
|
+
(e.g. uvicorn), ***if*** you just want the built-in :func:`main()`
|
|
193
|
+
app factory.
|
|
194
|
+
|
|
195
|
+
But most likely you do not, in which case you must define your own
|
|
196
|
+
function and call this one with your preferred app factory::
|
|
197
|
+
|
|
198
|
+
from wuttaweb.app import make_wsgi_app
|
|
199
|
+
|
|
200
|
+
def my_main(global_config, **settings):
|
|
201
|
+
# TODO: build your app
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
def make_my_wsgi_app():
|
|
205
|
+
return make_wsgi_app(my_main)
|
|
206
|
+
|
|
207
|
+
So ``make_my_wsgi_app()`` could then be used as-is for general
|
|
208
|
+
WSGI servers. However, note that this approach will require
|
|
209
|
+
setting the ``WUTTA_CONFIG_FILES`` environment variable, unless
|
|
210
|
+
running via :ref:`wutta-webapp`.
|
|
211
|
+
|
|
212
|
+
:param main_app: Either a Paste-compatible app factory, or
|
|
213
|
+
:term:`spec` for one. If not specified, the built-in
|
|
214
|
+
:func:`main()` is assumed.
|
|
215
|
+
|
|
216
|
+
:param config: Optional :term:`config object`. If not specified,
|
|
217
|
+
one is created based on ``WUTTA_CONFIG_FILES`` environment
|
|
218
|
+
variable.
|
|
219
|
+
"""
|
|
220
|
+
if not config:
|
|
221
|
+
config = make_config()
|
|
222
|
+
app = config.get_app()
|
|
223
|
+
|
|
224
|
+
# extract pyramid settings
|
|
225
|
+
settings = config.get_dict('app:main')
|
|
226
|
+
|
|
227
|
+
# keep same config object
|
|
228
|
+
settings['wutta_config'] = config
|
|
229
|
+
|
|
230
|
+
# determine the app factory
|
|
231
|
+
if isinstance(main_app, str):
|
|
232
|
+
make_wsgi_app = app.load_object(main_app)
|
|
233
|
+
elif callable(main_app):
|
|
234
|
+
make_wsgi_app = main_app
|
|
235
|
+
else:
|
|
236
|
+
raise ValueError("main_app must be spec or callable")
|
|
237
|
+
|
|
238
|
+
# construct a pyramid app "per usual"
|
|
239
|
+
return make_wsgi_app({}, **settings)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def make_asgi_app(main_app=None, config=None):
|
|
243
|
+
"""
|
|
244
|
+
Make and return a ASGI app, using the given Paste app factory.
|
|
245
|
+
|
|
246
|
+
This works the same as :func:`make_wsgi_app()` and should be
|
|
247
|
+
called in the same way etc.
|
|
248
|
+
"""
|
|
249
|
+
wsgi_app = make_wsgi_app(main_app, config=config)
|
|
250
|
+
return WsgiToAsgi(wsgi_app)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
WuttaWeb - ``wutta`` subcommands
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# nb. must bring in all modules for discovery to work
|
|
28
|
+
from . import webapp
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
See also: :ref:`wutta-webapp`
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
from typing_extensions import Annotated
|
|
30
|
+
|
|
31
|
+
import typer
|
|
32
|
+
from pyramid.scripts import pserve
|
|
33
|
+
|
|
34
|
+
from wuttjamaican.cli import wutta_typer
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@wutta_typer.command()
|
|
38
|
+
def webapp(
|
|
39
|
+
ctx: typer.Context,
|
|
40
|
+
auto_reload: Annotated[
|
|
41
|
+
bool,
|
|
42
|
+
typer.Option('--reload', '-r',
|
|
43
|
+
help="Auto-reload web app when files change.")] = False,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Run the configured web app
|
|
47
|
+
"""
|
|
48
|
+
config = ctx.parent.wutta_config
|
|
49
|
+
|
|
50
|
+
# we'll need config file(s) to specify for web app
|
|
51
|
+
if not config.files_read:
|
|
52
|
+
sys.stderr.write("no config files found!\n")
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
runner = config.get(f'{config.appname}.web.app.runner', default='pserve')
|
|
56
|
+
if runner == 'pserve':
|
|
57
|
+
|
|
58
|
+
# run pserve
|
|
59
|
+
argv = ['pserve', f'file+ini:{config.files_read[0]}']
|
|
60
|
+
if ctx.params['auto_reload']:
|
|
61
|
+
argv.append('--reload')
|
|
62
|
+
pserve.main(argv=argv)
|
|
63
|
+
|
|
64
|
+
elif runner == 'uvicorn':
|
|
65
|
+
|
|
66
|
+
import uvicorn
|
|
67
|
+
|
|
68
|
+
# need service details from config
|
|
69
|
+
spec = config.require(f'{config.appname}.web.app.spec')
|
|
70
|
+
kw = {
|
|
71
|
+
'host': config.get(f'{config.appname}.web.app.host', default='127.0.0.1'),
|
|
72
|
+
'port': config.get_int(f'{config.appname}.web.app.port', default=8000),
|
|
73
|
+
'reload': ctx.params['auto_reload'],
|
|
74
|
+
'reload_dirs': config.get_list(f'{config.appname}.web.app.reload_dirs'),
|
|
75
|
+
'factory': config.get_bool(f'{config.appname}.web.app.factory', default=False),
|
|
76
|
+
'interface': config.get(f'{config.appname}.web.app.interface', default='auto'),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# also must inject our config files to env, since there is no
|
|
80
|
+
# other way to specify when running via uvicorn
|
|
81
|
+
os.environ['WUTTA_CONFIG_FILES'] = os.pathsep.join(config.files_read)
|
|
82
|
+
|
|
83
|
+
# run uvicorn
|
|
84
|
+
uvicorn.run(spec, **kw)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
sys.stderr.write(f"unknown web app runner: {runner}\n")
|
|
88
|
+
sys.exit(2)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024 Lance Edgar
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Wutta Framework.
|
|
8
|
+
#
|
|
9
|
+
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
15
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
17
|
+
# more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
|
20
|
+
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
21
|
+
#
|
|
22
|
+
################################################################################
|
|
23
|
+
"""
|
|
24
|
+
:term:`Email Settings <email setting>` for WuttaWeb
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from wuttjamaican.email import EmailSetting
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class feedback(EmailSetting):
|
|
31
|
+
"""
|
|
32
|
+
Sent when user submits feedback via the web app.
|
|
33
|
+
"""
|
|
34
|
+
default_subject = "User Feedback"
|
|
35
|
+
|
|
36
|
+
def sample_data(self):
|
|
37
|
+
""" """
|
|
38
|
+
model = self.app.model
|
|
39
|
+
person = model.Person(full_name="Barney Rubble")
|
|
40
|
+
user = model.User(username='barney', person=person)
|
|
41
|
+
return {
|
|
42
|
+
'user': user,
|
|
43
|
+
'user_name': str(person),
|
|
44
|
+
'user_url': '#',
|
|
45
|
+
'referrer': 'http://example.com/',
|
|
46
|
+
'client_ip': '127.0.0.1',
|
|
47
|
+
'message': "This app is cool but needs a new feature.\n\nAllow me to describe...",
|
|
48
|
+
}
|
|
@@ -30,9 +30,11 @@ import uuid as _uuid
|
|
|
30
30
|
import colander
|
|
31
31
|
import sqlalchemy as sa
|
|
32
32
|
|
|
33
|
+
from wuttjamaican.db.model import Person
|
|
34
|
+
from wuttjamaican.conf import parse_list
|
|
35
|
+
|
|
33
36
|
from wuttaweb.db import Session
|
|
34
37
|
from wuttaweb.forms import widgets
|
|
35
|
-
from wuttjamaican.db.model import Person
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
class WuttaDateTime(colander.DateTime):
|
|
@@ -569,5 +571,36 @@ class FileDownload(colander.String):
|
|
|
569
571
|
return widgets.FileDownloadWidget(self.request, **kwargs)
|
|
570
572
|
|
|
571
573
|
|
|
574
|
+
class EmailRecipients(colander.String):
|
|
575
|
+
"""
|
|
576
|
+
Custom schema type for :term:`email setting` recipient fields
|
|
577
|
+
(``To``, ``Cc``, ``Bcc``).
|
|
578
|
+
"""
|
|
579
|
+
|
|
580
|
+
def serialize(self, node, appstruct):
|
|
581
|
+
if appstruct is colander.null:
|
|
582
|
+
return colander.null
|
|
583
|
+
|
|
584
|
+
return '\n'.join(parse_list(appstruct))
|
|
585
|
+
|
|
586
|
+
def deserialize(self, node, cstruct):
|
|
587
|
+
""" """
|
|
588
|
+
if cstruct is colander.null:
|
|
589
|
+
return colander.null
|
|
590
|
+
|
|
591
|
+
values = [value for value in parse_list(cstruct)
|
|
592
|
+
if value]
|
|
593
|
+
return ', '.join(values)
|
|
594
|
+
|
|
595
|
+
def widget_maker(self, **kwargs):
|
|
596
|
+
"""
|
|
597
|
+
Constructs a default widget for the field.
|
|
598
|
+
|
|
599
|
+
:returns: Instance of
|
|
600
|
+
:class:`~wuttaweb.forms.widgets.EmailRecipientsWidget`.
|
|
601
|
+
"""
|
|
602
|
+
return widgets.EmailRecipientsWidget(**kwargs)
|
|
603
|
+
|
|
604
|
+
|
|
572
605
|
# nb. colanderalchemy schema overrides
|
|
573
606
|
sa.DateTime.__colanderalchemy_config__ = {'typ': WuttaDateTime}
|
|
@@ -51,6 +51,8 @@ from deform.widget import (Widget, TextInputWidget, TextAreaWidget,
|
|
|
51
51
|
DateTimeInputWidget, MoneyInputWidget)
|
|
52
52
|
from webhelpers2.html import HTML
|
|
53
53
|
|
|
54
|
+
from wuttjamaican.conf import parse_list
|
|
55
|
+
|
|
54
56
|
from wuttaweb.db import Session
|
|
55
57
|
from wuttaweb.grids import Grid
|
|
56
58
|
|
|
@@ -423,6 +425,41 @@ class PermissionsWidget(WuttaCheckboxChoiceWidget):
|
|
|
423
425
|
return super().serialize(field, cstruct, **kw)
|
|
424
426
|
|
|
425
427
|
|
|
428
|
+
class EmailRecipientsWidget(TextAreaWidget):
|
|
429
|
+
"""
|
|
430
|
+
Widget for :term:`email setting` recipient fields (``To``, ``Cc``,
|
|
431
|
+
``Bcc``).
|
|
432
|
+
|
|
433
|
+
This is a subclass of
|
|
434
|
+
:class:`deform:deform.widget.TextAreaWidget`. It uses these
|
|
435
|
+
Deform templates:
|
|
436
|
+
|
|
437
|
+
* ``textarea``
|
|
438
|
+
* ``readonly/email_recips``
|
|
439
|
+
|
|
440
|
+
See also the :class:`~wuttaweb.forms.schema.EmailRecipients`
|
|
441
|
+
schema type, which uses this widget.
|
|
442
|
+
"""
|
|
443
|
+
readonly_template = 'readonly/email_recips'
|
|
444
|
+
|
|
445
|
+
def serialize(self, field, cstruct, **kw):
|
|
446
|
+
""" """
|
|
447
|
+
readonly = kw.get('readonly', self.readonly)
|
|
448
|
+
if readonly:
|
|
449
|
+
kw['recips'] = parse_list(cstruct or '')
|
|
450
|
+
|
|
451
|
+
return super().serialize(field, cstruct, **kw)
|
|
452
|
+
|
|
453
|
+
def deserialize(self, field, pstruct):
|
|
454
|
+
""" """
|
|
455
|
+
if pstruct is colander.null:
|
|
456
|
+
return colander.null
|
|
457
|
+
|
|
458
|
+
values = [value for value in parse_list(pstruct)
|
|
459
|
+
if value]
|
|
460
|
+
return ', '.join(values)
|
|
461
|
+
|
|
462
|
+
|
|
426
463
|
class BatchIdWidget(Widget):
|
|
427
464
|
"""
|
|
428
465
|
Widget for use with the
|