WuttaWeb 0.18.0__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.18.0 → wuttaweb-0.19.0}/CHANGELOG.md +10 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/PKG-INFO +3 -3
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/README.md +1 -1
- 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.18.0 → wuttaweb-0.19.0}/docs/index.rst +2 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/pyproject.toml +2 -2
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/app.py +5 -2
- wuttaweb-0.19.0/src/wuttaweb/emails.py +48 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/forms/schema.py +34 -1
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/forms/widgets.py +37 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/menus.py +6 -0
- {wuttaweb-0.18.0 → 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.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/essential.py +2 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/forms/test_schema.py +44 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/forms/test_widgets.py +50 -1
- wuttaweb-0.19.0/tests/test_emails.py +23 -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.18.0 → wuttaweb-0.19.0}/.gitignore +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/COPYING.txt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/Makefile +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/conf.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/make.bat +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.18.0/src/wuttaweb/email/templates → wuttaweb-0.19.0/src/wuttaweb/email-templates}/feedback.html.mako +0 -0
- {wuttaweb-0.18.0/src/wuttaweb/email/templates → wuttaweb-0.19.0/src/wuttaweb/email-templates}/feedback.txt.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/grids/base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/batch.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/master.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tasks.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/cli/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/db/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/forms/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/grids/test_base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/grids/test_filters.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_app.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_progress.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_static.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/test_util.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/util.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_master.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tests/views/test_users.py +0 -0
- {wuttaweb-0.18.0 → wuttaweb-0.19.0}/tox.ini +0 -0
|
@@ -5,6 +5,16 @@ 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
|
+
|
|
8
18
|
## v0.18.0 (2024-12-18)
|
|
9
19
|
|
|
10
20
|
### Feat
|
|
@@ -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
|
|
@@ -39,7 +39,7 @@ Requires-Dist: pyramid-tm
|
|
|
39
39
|
Requires-Dist: pyramid>=2
|
|
40
40
|
Requires-Dist: waitress
|
|
41
41
|
Requires-Dist: webhelpers2
|
|
42
|
-
Requires-Dist: wuttjamaican[db]>=0.
|
|
42
|
+
Requires-Dist: wuttjamaican[db]>=0.19.0
|
|
43
43
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
44
44
|
Provides-Extra: continuum
|
|
45
45
|
Requires-Dist: wutta-continuum; extra == 'continuum'
|
|
@@ -53,7 +53,7 @@ Requires-Dist: tox; extra == 'tests'
|
|
|
53
53
|
Description-Content-Type: text/markdown
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
#
|
|
56
|
+
# WuttaWeb
|
|
57
57
|
|
|
58
58
|
Web app for Wutta Framework
|
|
59
59
|
|
|
@@ -35,6 +35,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
35
35
|
api/wuttaweb.db
|
|
36
36
|
api/wuttaweb.db.continuum
|
|
37
37
|
api/wuttaweb.db.sess
|
|
38
|
+
api/wuttaweb.emails
|
|
38
39
|
api/wuttaweb.forms
|
|
39
40
|
api/wuttaweb.forms.base
|
|
40
41
|
api/wuttaweb.forms.schema
|
|
@@ -54,6 +55,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
54
55
|
api/wuttaweb.views.base
|
|
55
56
|
api/wuttaweb.views.batch
|
|
56
57
|
api/wuttaweb.views.common
|
|
58
|
+
api/wuttaweb.views.email
|
|
57
59
|
api/wuttaweb.views.essential
|
|
58
60
|
api/wuttaweb.views.master
|
|
59
61
|
api/wuttaweb.views.people
|
|
@@ -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"}]
|
|
@@ -44,7 +44,7 @@ dependencies = [
|
|
|
44
44
|
"pyramid_tm",
|
|
45
45
|
"waitress",
|
|
46
46
|
"WebHelpers2",
|
|
47
|
-
"WuttJamaican[db]>=0.
|
|
47
|
+
"WuttJamaican[db]>=0.19.0",
|
|
48
48
|
"zope.sqlalchemy>=1.5",
|
|
49
49
|
]
|
|
50
50
|
|
|
@@ -43,9 +43,12 @@ log = logging.getLogger(__name__)
|
|
|
43
43
|
class WebAppProvider(AppProvider):
|
|
44
44
|
"""
|
|
45
45
|
The :term:`app provider` for WuttaWeb. This adds some methods to
|
|
46
|
-
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.
|
|
47
49
|
"""
|
|
48
|
-
|
|
50
|
+
email_modules = ['wuttaweb.emails']
|
|
51
|
+
email_templates = ['wuttaweb:email-templates']
|
|
49
52
|
|
|
50
53
|
def get_web_handler(self, **kwargs):
|
|
51
54
|
"""
|
|
@@ -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
|
|
@@ -174,6 +174,12 @@ class MenuHandler(GenericHandler):
|
|
|
174
174
|
'perm': 'permissions.list',
|
|
175
175
|
},
|
|
176
176
|
{'type': 'sep'},
|
|
177
|
+
{
|
|
178
|
+
'title': "Email Settings",
|
|
179
|
+
'route': 'email_settings',
|
|
180
|
+
'perm': 'email_settings.list',
|
|
181
|
+
},
|
|
182
|
+
{'type': 'sep'},
|
|
177
183
|
{
|
|
178
184
|
'title': "App Info",
|
|
179
185
|
'route': 'appinfo',
|
|
@@ -277,17 +277,21 @@
|
|
|
277
277
|
% if capture(self.content_title):
|
|
278
278
|
<section id="content-title"
|
|
279
279
|
class="has-background-primary">
|
|
280
|
-
<div style="display: flex; align-items: center; padding: 0.5rem;">
|
|
280
|
+
<div style="display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem;">
|
|
281
281
|
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
<div style="width: 60%; display: flex; gap: 0.5rem;">
|
|
283
|
+
|
|
284
|
+
<h1 class="title has-text-white"
|
|
285
|
+
v-html="contentTitleHTML">
|
|
286
|
+
</h1>
|
|
287
|
+
|
|
288
|
+
<div style="display: flex; gap: 0.5rem;">
|
|
289
|
+
${self.render_instance_header_title_extras()}
|
|
290
|
+
</div>
|
|
285
291
|
|
|
286
|
-
<div style="flex-grow: 1; display: flex; gap: 0.5rem;">
|
|
287
|
-
${self.render_instance_header_title_extras()}
|
|
288
292
|
</div>
|
|
289
293
|
|
|
290
|
-
<div style="display: flex; gap: 0.5rem;">
|
|
294
|
+
<div style="width: 40%; display: flex; gap: 0.5rem;">
|
|
291
295
|
${self.render_instance_header_buttons()}
|
|
292
296
|
</div>
|
|
293
297
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## -*- coding: utf-8; -*-
|
|
2
|
+
<%inherit file="/master/view.mako" />
|
|
3
|
+
|
|
4
|
+
<%def name="tool_panels()">
|
|
5
|
+
${parent.tool_panels()}
|
|
6
|
+
${self.tool_panel_preview()}
|
|
7
|
+
</%def>
|
|
8
|
+
|
|
9
|
+
<%def name="tool_panel_preview()">
|
|
10
|
+
<wutta-tool-panel heading="Email Preview">
|
|
11
|
+
|
|
12
|
+
<b-button type="is-primary"
|
|
13
|
+
% if has_html_template:
|
|
14
|
+
tag="a" target="_blank"
|
|
15
|
+
href="${master.get_action_url('preview', setting)}?mode=html"
|
|
16
|
+
% else:
|
|
17
|
+
disabled
|
|
18
|
+
title="HTML template not found"
|
|
19
|
+
% endif
|
|
20
|
+
icon-pack="fas"
|
|
21
|
+
icon-left="external-link-alt">
|
|
22
|
+
Preview HTML
|
|
23
|
+
</b-button>
|
|
24
|
+
|
|
25
|
+
<b-button type="is-primary"
|
|
26
|
+
% if has_txt_template:
|
|
27
|
+
tag="a" target="_blank"
|
|
28
|
+
href="${master.get_action_url('preview', setting)}?mode=txt"
|
|
29
|
+
% else:
|
|
30
|
+
disabled
|
|
31
|
+
title="TXT template not found"
|
|
32
|
+
% endif
|
|
33
|
+
icon-pack="fas"
|
|
34
|
+
icon-left="external-link-alt">
|
|
35
|
+
Preview TXT
|
|
36
|
+
</b-button>
|
|
37
|
+
|
|
38
|
+
</wutta-tool-panel>
|
|
39
|
+
</%def>
|
|
@@ -0,0 +1,298 @@
|
|
|
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
|
+
Views for email settings
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import colander
|
|
28
|
+
|
|
29
|
+
from wuttaweb.views import MasterView
|
|
30
|
+
from wuttaweb.forms.schema import EmailRecipients
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EmailSettingView(MasterView):
|
|
34
|
+
"""
|
|
35
|
+
Master view for :term:`email settings <email setting>`.
|
|
36
|
+
"""
|
|
37
|
+
model_name = 'email_setting'
|
|
38
|
+
model_key = 'key'
|
|
39
|
+
model_title = "Email Setting"
|
|
40
|
+
url_prefix = '/email/settings'
|
|
41
|
+
filterable = False
|
|
42
|
+
sortable = True
|
|
43
|
+
sort_on_backend = False
|
|
44
|
+
paginated = False
|
|
45
|
+
creatable = False
|
|
46
|
+
deletable = False
|
|
47
|
+
|
|
48
|
+
labels = {
|
|
49
|
+
'key': "Email Key",
|
|
50
|
+
'replyto': "Reply-To",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
grid_columns = [
|
|
54
|
+
'key',
|
|
55
|
+
'subject',
|
|
56
|
+
'to',
|
|
57
|
+
'enabled',
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
# TODO: why does this not work?
|
|
61
|
+
sort_defaults = 'key'
|
|
62
|
+
|
|
63
|
+
form_fields = [
|
|
64
|
+
'key',
|
|
65
|
+
'description',
|
|
66
|
+
'subject',
|
|
67
|
+
'sender',
|
|
68
|
+
'replyto',
|
|
69
|
+
'to',
|
|
70
|
+
'cc',
|
|
71
|
+
'bcc',
|
|
72
|
+
'notes',
|
|
73
|
+
'enabled',
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
def __init__(self, request, context=None):
|
|
77
|
+
super().__init__(request, context=context)
|
|
78
|
+
self.email_handler = self.app.get_email_handler()
|
|
79
|
+
|
|
80
|
+
def get_grid_data(self, columns=None, session=None):
|
|
81
|
+
"""
|
|
82
|
+
This view calls
|
|
83
|
+
:meth:`~wuttjamaican:wuttjamaican.email.EmailHandler.get_email_settings()`
|
|
84
|
+
on the :attr:`email_handler` to obtain its grid data.
|
|
85
|
+
"""
|
|
86
|
+
data = []
|
|
87
|
+
for setting in self.email_handler.get_email_settings().values():
|
|
88
|
+
data.append(self.normalize_setting(setting))
|
|
89
|
+
return data
|
|
90
|
+
|
|
91
|
+
def normalize_setting(self, setting):
|
|
92
|
+
""" """
|
|
93
|
+
key = setting.__name__
|
|
94
|
+
return {
|
|
95
|
+
'key': key,
|
|
96
|
+
'description': setting.__doc__,
|
|
97
|
+
'subject': self.email_handler.get_auto_subject(key, rendered=False, setting=setting),
|
|
98
|
+
'sender': self.email_handler.get_auto_sender(key),
|
|
99
|
+
'replyto': self.email_handler.get_auto_replyto(key) or colander.null,
|
|
100
|
+
'to': self.email_handler.get_auto_to(key),
|
|
101
|
+
'cc': self.email_handler.get_auto_cc(key),
|
|
102
|
+
'bcc': self.email_handler.get_auto_bcc(key),
|
|
103
|
+
'notes': self.email_handler.get_notes(key) or colander.null,
|
|
104
|
+
'enabled': self.email_handler.is_enabled(key),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
def configure_grid(self, g):
|
|
108
|
+
""" """
|
|
109
|
+
super().configure_grid(g)
|
|
110
|
+
|
|
111
|
+
# key
|
|
112
|
+
g.set_searchable('key')
|
|
113
|
+
g.set_link('key')
|
|
114
|
+
|
|
115
|
+
# subject
|
|
116
|
+
g.set_searchable('subject')
|
|
117
|
+
g.set_link('subject')
|
|
118
|
+
|
|
119
|
+
# to
|
|
120
|
+
g.set_renderer('to', self.render_to_short)
|
|
121
|
+
|
|
122
|
+
def render_to_short(self, setting, field, value):
|
|
123
|
+
""" """
|
|
124
|
+
recips = value
|
|
125
|
+
if not recips:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
if len(recips) < 3:
|
|
129
|
+
return ', '.join(recips)
|
|
130
|
+
|
|
131
|
+
recips = ', '.join(recips[:2])
|
|
132
|
+
return f"{recips}, ..."
|
|
133
|
+
|
|
134
|
+
def get_instance(self):
|
|
135
|
+
""" """
|
|
136
|
+
key = self.request.matchdict['key']
|
|
137
|
+
setting = self.email_handler.get_email_setting(key, instance=False)
|
|
138
|
+
if setting:
|
|
139
|
+
return self.normalize_setting(setting)
|
|
140
|
+
|
|
141
|
+
raise self.notfound()
|
|
142
|
+
|
|
143
|
+
def get_instance_title(self, setting):
|
|
144
|
+
""" """
|
|
145
|
+
return setting['subject']
|
|
146
|
+
|
|
147
|
+
def configure_form(self, f):
|
|
148
|
+
""" """
|
|
149
|
+
super().configure_form(f)
|
|
150
|
+
|
|
151
|
+
# description
|
|
152
|
+
f.set_readonly('description')
|
|
153
|
+
|
|
154
|
+
# replyto
|
|
155
|
+
f.set_required('replyto', False)
|
|
156
|
+
|
|
157
|
+
# to
|
|
158
|
+
f.set_node('to', EmailRecipients())
|
|
159
|
+
|
|
160
|
+
# cc
|
|
161
|
+
f.set_node('cc', EmailRecipients())
|
|
162
|
+
|
|
163
|
+
# bcc
|
|
164
|
+
f.set_node('bcc', EmailRecipients())
|
|
165
|
+
|
|
166
|
+
# notes
|
|
167
|
+
f.set_widget('notes', 'notes')
|
|
168
|
+
f.set_required('notes', False)
|
|
169
|
+
|
|
170
|
+
# enabled
|
|
171
|
+
f.set_node('enabled', colander.Boolean())
|
|
172
|
+
|
|
173
|
+
def persist(self, setting):
|
|
174
|
+
""" """
|
|
175
|
+
session = self.Session()
|
|
176
|
+
key = self.request.matchdict['key']
|
|
177
|
+
|
|
178
|
+
def save(name, value):
|
|
179
|
+
self.app.save_setting(session, f'{self.config.appname}.email.{key}.{name}', value)
|
|
180
|
+
|
|
181
|
+
def delete(name):
|
|
182
|
+
self.app.delete_setting(session, f'{self.config.appname}.email.{key}.{name}')
|
|
183
|
+
|
|
184
|
+
# subject
|
|
185
|
+
if setting['subject']:
|
|
186
|
+
save('subject', setting['subject'])
|
|
187
|
+
else:
|
|
188
|
+
delete('subject')
|
|
189
|
+
|
|
190
|
+
# sender
|
|
191
|
+
if setting['sender']:
|
|
192
|
+
save('sender', setting['sender'])
|
|
193
|
+
else:
|
|
194
|
+
delete('sender')
|
|
195
|
+
|
|
196
|
+
# replyto
|
|
197
|
+
if setting['replyto']:
|
|
198
|
+
save('replyto', setting['replyto'])
|
|
199
|
+
else:
|
|
200
|
+
delete('replyto')
|
|
201
|
+
|
|
202
|
+
# to
|
|
203
|
+
if setting['to']:
|
|
204
|
+
save('to', setting['to'])
|
|
205
|
+
else:
|
|
206
|
+
delete('to')
|
|
207
|
+
|
|
208
|
+
# cc
|
|
209
|
+
if setting['cc']:
|
|
210
|
+
save('cc', setting['cc'])
|
|
211
|
+
else:
|
|
212
|
+
delete('cc')
|
|
213
|
+
|
|
214
|
+
# bcc
|
|
215
|
+
if setting['bcc']:
|
|
216
|
+
save('bcc', setting['bcc'])
|
|
217
|
+
else:
|
|
218
|
+
delete('bcc')
|
|
219
|
+
|
|
220
|
+
# notes
|
|
221
|
+
if setting['notes']:
|
|
222
|
+
save('notes', setting['notes'])
|
|
223
|
+
else:
|
|
224
|
+
delete('notes')
|
|
225
|
+
|
|
226
|
+
# enabled
|
|
227
|
+
save('enabled', 'true' if setting['enabled'] else 'false')
|
|
228
|
+
|
|
229
|
+
def render_to_response(self, template, context):
|
|
230
|
+
""" """
|
|
231
|
+
if self.viewing:
|
|
232
|
+
setting = context['instance']
|
|
233
|
+
context['setting'] = setting
|
|
234
|
+
context['has_html_template'] = self.email_handler.get_auto_body_template(
|
|
235
|
+
setting['key'], 'html')
|
|
236
|
+
context['has_txt_template'] = self.email_handler.get_auto_body_template(
|
|
237
|
+
setting['key'], 'txt')
|
|
238
|
+
|
|
239
|
+
return super().render_to_response(template, context)
|
|
240
|
+
|
|
241
|
+
def preview(self):
|
|
242
|
+
"""
|
|
243
|
+
View for showing a rendered preview of a given email template.
|
|
244
|
+
|
|
245
|
+
This will render the email template according to the "mode"
|
|
246
|
+
requested - i.e. HTML or TXT.
|
|
247
|
+
"""
|
|
248
|
+
key = self.request.matchdict['key']
|
|
249
|
+
setting = self.email_handler.get_email_setting(key)
|
|
250
|
+
context = setting.sample_data()
|
|
251
|
+
mode = self.request.params.get('mode', 'html')
|
|
252
|
+
|
|
253
|
+
if mode == 'txt':
|
|
254
|
+
body = self.email_handler.get_auto_txt_body(key, context)
|
|
255
|
+
self.request.response.content_type = 'text/plain'
|
|
256
|
+
|
|
257
|
+
else: # html
|
|
258
|
+
body = self.email_handler.get_auto_html_body(key, context)
|
|
259
|
+
|
|
260
|
+
self.request.response.text = body
|
|
261
|
+
return self.request.response
|
|
262
|
+
|
|
263
|
+
@classmethod
|
|
264
|
+
def defaults(cls, config):
|
|
265
|
+
""" """
|
|
266
|
+
cls._email_defaults(config)
|
|
267
|
+
cls._defaults(config)
|
|
268
|
+
|
|
269
|
+
@classmethod
|
|
270
|
+
def _email_defaults(cls, config):
|
|
271
|
+
""" """
|
|
272
|
+
route_prefix = cls.get_route_prefix()
|
|
273
|
+
permission_prefix = cls.get_permission_prefix()
|
|
274
|
+
model_title_plural = cls.get_model_title_plural()
|
|
275
|
+
instance_url_prefix = cls.get_instance_url_prefix()
|
|
276
|
+
|
|
277
|
+
# fix permission group
|
|
278
|
+
config.add_wutta_permission_group(permission_prefix,
|
|
279
|
+
model_title_plural,
|
|
280
|
+
overwrite=False)
|
|
281
|
+
|
|
282
|
+
# preview
|
|
283
|
+
config.add_route(f'{route_prefix}.preview',
|
|
284
|
+
f'{instance_url_prefix}/preview')
|
|
285
|
+
config.add_view(cls, attr='preview',
|
|
286
|
+
route_name=f'{route_prefix}.preview',
|
|
287
|
+
permission=f'{permission_prefix}.view')
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def defaults(config, **kwargs):
|
|
291
|
+
base = globals()
|
|
292
|
+
|
|
293
|
+
EmailSettingView = kwargs.get('EmailSettingView', base['EmailSettingView'])
|
|
294
|
+
EmailSettingView.defaults(config)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def includeme(config):
|
|
298
|
+
defaults(config)
|
|
@@ -31,6 +31,7 @@ That will in turn include the following modules:
|
|
|
31
31
|
|
|
32
32
|
* :mod:`wuttaweb.views.common`
|
|
33
33
|
* :mod:`wuttaweb.views.auth`
|
|
34
|
+
* :mod:`wuttaweb.views.email`
|
|
34
35
|
* :mod:`wuttaweb.views.settings`
|
|
35
36
|
* :mod:`wuttaweb.views.progress`
|
|
36
37
|
* :mod:`wuttaweb.views.people`
|
|
@@ -45,6 +46,7 @@ def defaults(config, **kwargs):
|
|
|
45
46
|
|
|
46
47
|
config.include(mod('wuttaweb.views.common'))
|
|
47
48
|
config.include(mod('wuttaweb.views.auth'))
|
|
49
|
+
config.include(mod('wuttaweb.views.email'))
|
|
48
50
|
config.include(mod('wuttaweb.views.settings'))
|
|
49
51
|
config.include(mod('wuttaweb.views.progress'))
|
|
50
52
|
config.include(mod('wuttaweb.views.people'))
|