WuttaWeb 0.23.2__tar.gz → 0.25.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.23.2 → wuttaweb-0.25.0}/CHANGELOG.md +27 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/PKG-INFO +3 -3
- wuttaweb-0.25.0/docs/api/wuttaweb.diffs.rst +6 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/index.rst +1 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/pyproject.toml +9 -3
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/db/sess.py +2 -2
- wuttaweb-0.25.0/src/wuttaweb/diffs.py +224 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/forms/schema.py +22 -3
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/forms/widgets.py +3 -1
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/grids/base.py +4 -2
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/handler.py +1 -1
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/subscribers.py +12 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/appinfo/configure.mako +78 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/appinfo/index.mako +3 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/base.mako +32 -3
- wuttaweb-0.25.0/src/wuttaweb/templates/diff.mako +15 -0
- wuttaweb-0.25.0/src/wuttaweb/templates/master/create_row.mako +4 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/view.mako +12 -0
- wuttaweb-0.25.0/src/wuttaweb/templates/master/view_version.mako +35 -0
- wuttaweb-0.25.0/src/wuttaweb/templates/master/view_versions.mako +20 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/testing.py +67 -1
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/batch.py +87 -5
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/master.py +716 -17
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/settings.py +48 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/upgrades.py +1 -2
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tasks.py +2 -1
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/forms/test_schema.py +74 -19
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/forms/test_widgets.py +29 -11
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/grids/test_base.py +16 -10
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/grids/test_filters.py +0 -4
- wuttaweb-0.25.0/tests/test_diffs.py +222 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_handler.py +3 -3
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_subscribers.py +13 -0
- wuttaweb-0.25.0/tests/util.py +15 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_batch.py +132 -39
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_master.py +457 -7
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_settings.py +20 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tox.ini +7 -2
- wuttaweb-0.23.2/tests/util.py +0 -43
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/.gitignore +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/.hgignore +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/.pylintrc +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/COPYING.txt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/README.md +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/Makefile +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.emails.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.email.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.reports.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/conf.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/make.bat +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/emails.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/forms/base.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/deform/wutta_checked_password.pt +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/email/settings/view.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/form.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/reports/view.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/themes/butterfly/base.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/themes/butterfly/buefy-components.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/themes/butterfly/buefy-plugin.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/themes/butterfly/http-plugin.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/users/view.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/common.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/email.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/essential.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/reports.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/src/wuttaweb/views/users.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/cli/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/db/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/forms/test_base.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_app.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_emails.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_progress.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_static.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/test_util.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_email.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_essential.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_reports.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.23.2 → wuttaweb-0.25.0}/tests/views/test_users.py +0 -0
|
@@ -5,6 +5,33 @@ 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.25.0 (2025-12-17)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add "complete" (sic) timezone support
|
|
13
|
+
|
|
14
|
+
### Fix
|
|
15
|
+
|
|
16
|
+
- add local timezone awareness for datetime fields
|
|
17
|
+
|
|
18
|
+
## v0.24.0 (2025-12-15)
|
|
19
|
+
|
|
20
|
+
### Feat
|
|
21
|
+
|
|
22
|
+
- basic support for displaying version history
|
|
23
|
+
|
|
24
|
+
### Fix
|
|
25
|
+
|
|
26
|
+
- use UTC when updating timestamp in DB
|
|
27
|
+
- workaround error when 'fanstatic.needed' missing from environ
|
|
28
|
+
- workaround error when 'fanstatic.needed' missing from environ
|
|
29
|
+
- address pylint warnings
|
|
30
|
+
- add basic `create_row()` support, esp. for batch views
|
|
31
|
+
- update dependencies for wuttjamaican, wutta-continuum
|
|
32
|
+
- make master view auto-detect continuum versioning for model class
|
|
33
|
+
- fix 'invalid-name' for pylint
|
|
34
|
+
|
|
8
35
|
## v0.23.2 (2025-10-19)
|
|
9
36
|
|
|
10
37
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.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,10 +39,10 @@ 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.26.0
|
|
43
43
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
44
44
|
Provides-Extra: continuum
|
|
45
|
-
Requires-Dist: wutta-continuum; extra == 'continuum'
|
|
45
|
+
Requires-Dist: wutta-continuum>=0.2.2; extra == 'continuum'
|
|
46
46
|
Provides-Extra: docs
|
|
47
47
|
Requires-Dist: furo; extra == 'docs'
|
|
48
48
|
Requires-Dist: sphinx; extra == 'docs'
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.25.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,13 +44,13 @@ dependencies = [
|
|
|
44
44
|
"pyramid_tm",
|
|
45
45
|
"waitress",
|
|
46
46
|
"WebHelpers2",
|
|
47
|
-
"WuttJamaican[db]>=0.
|
|
47
|
+
"WuttJamaican[db]>=0.26.0",
|
|
48
48
|
"zope.sqlalchemy>=1.5",
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
[project.optional-dependencies]
|
|
53
|
-
continuum = ["Wutta-Continuum"]
|
|
53
|
+
continuum = ["Wutta-Continuum>=0.2.2"]
|
|
54
54
|
docs = ["Sphinx", "furo", "sphinxcontrib-programoutput"]
|
|
55
55
|
tests = ["pylint", "pytest", "pytest-cov", "tox"]
|
|
56
56
|
|
|
@@ -91,3 +91,9 @@ update_changelog_on_bump = true
|
|
|
91
91
|
exclude = [
|
|
92
92
|
"htmlcov/",
|
|
93
93
|
]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
[tool.pytest.ini_options]
|
|
97
|
+
markers = [
|
|
98
|
+
"versioned: tests with SQLAlchemy-Continuum versioning feature enabled",
|
|
99
|
+
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
################################################################################
|
|
3
3
|
#
|
|
4
4
|
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
-
# Copyright © 2024 Lance Edgar
|
|
5
|
+
# Copyright © 2024-2025 Lance Edgar
|
|
6
6
|
#
|
|
7
7
|
# This file is part of Wutta Framework.
|
|
8
8
|
#
|
|
@@ -61,6 +61,6 @@ from sqlalchemy import orm
|
|
|
61
61
|
from zope.sqlalchemy.datamanager import register
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
Session = orm.scoped_session(orm.sessionmaker())
|
|
64
|
+
Session = orm.scoped_session(orm.sessionmaker()) # pylint: disable=invalid-name
|
|
65
65
|
|
|
66
66
|
register(Session)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# -*- coding: utf-8; -*-
|
|
2
|
+
################################################################################
|
|
3
|
+
#
|
|
4
|
+
# wuttaweb -- Web App for Wutta Framework
|
|
5
|
+
# Copyright © 2024-2025 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
|
+
Tools for displaying simple data diffs
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import sqlalchemy as sa
|
|
28
|
+
|
|
29
|
+
from pyramid.renderers import render
|
|
30
|
+
from webhelpers2.html import HTML
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Diff:
|
|
34
|
+
"""
|
|
35
|
+
Represent / display a basic "diff" between two data records.
|
|
36
|
+
|
|
37
|
+
You must provide both the "old" and "new" data records, when
|
|
38
|
+
constructing an instance of this class. Then call
|
|
39
|
+
:meth:`render_html()` to display the diff table.
|
|
40
|
+
|
|
41
|
+
:param old_data: Dict of "old" data record.
|
|
42
|
+
|
|
43
|
+
:param new_data: Dict of "new" data record.
|
|
44
|
+
|
|
45
|
+
:param fields: Optional list of field names. If not specified,
|
|
46
|
+
will be derived from the data records.
|
|
47
|
+
|
|
48
|
+
:param nature: What sort of diff is being represented; must be one
|
|
49
|
+
of: ``("create", "update", "delete")``
|
|
50
|
+
|
|
51
|
+
:param old_color: Background color to display for "old/deleted"
|
|
52
|
+
field data, when applicable.
|
|
53
|
+
|
|
54
|
+
:param new_color: Background color to display for "new/created"
|
|
55
|
+
field data, when applicable.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
59
|
+
self,
|
|
60
|
+
old_data: dict,
|
|
61
|
+
new_data: dict,
|
|
62
|
+
fields: list = None,
|
|
63
|
+
nature="update",
|
|
64
|
+
old_color="#ffebe9",
|
|
65
|
+
new_color="#dafbe1",
|
|
66
|
+
):
|
|
67
|
+
self.old_data = old_data
|
|
68
|
+
self.new_data = new_data
|
|
69
|
+
self.columns = ["field name", "old value", "new value"]
|
|
70
|
+
self.fields = fields or self.make_fields()
|
|
71
|
+
self.nature = nature
|
|
72
|
+
self.old_color = old_color
|
|
73
|
+
self.new_color = new_color
|
|
74
|
+
|
|
75
|
+
def make_fields(self): # pylint: disable=missing-function-docstring
|
|
76
|
+
return sorted(set(self.old_data) | set(self.new_data), key=lambda x: x.lower())
|
|
77
|
+
|
|
78
|
+
def old_value(self, field): # pylint: disable=missing-function-docstring
|
|
79
|
+
return self.old_data.get(field)
|
|
80
|
+
|
|
81
|
+
def new_value(self, field): # pylint: disable=missing-function-docstring
|
|
82
|
+
return self.new_data.get(field)
|
|
83
|
+
|
|
84
|
+
def values_differ(self, field): # pylint: disable=missing-function-docstring
|
|
85
|
+
return self.new_value(field) != self.old_value(field)
|
|
86
|
+
|
|
87
|
+
def render_html(self, template="/diff.mako", **kwargs):
|
|
88
|
+
"""
|
|
89
|
+
Render the diff as HTML table.
|
|
90
|
+
|
|
91
|
+
:param template: Name of template to render, if you need to
|
|
92
|
+
override the default.
|
|
93
|
+
|
|
94
|
+
:param \\**kwargs: Remaining kwargs are passed as context to
|
|
95
|
+
the template renderer.
|
|
96
|
+
|
|
97
|
+
:returns: HTML literal string
|
|
98
|
+
"""
|
|
99
|
+
context = kwargs
|
|
100
|
+
context["diff"] = self
|
|
101
|
+
return HTML.literal(render(template, context))
|
|
102
|
+
|
|
103
|
+
def render_field_row(self, field): # pylint: disable=missing-function-docstring
|
|
104
|
+
is_diff = self.values_differ(field)
|
|
105
|
+
|
|
106
|
+
td_field = HTML.tag("td", class_="field", c=field)
|
|
107
|
+
|
|
108
|
+
td_old_value = HTML.tag(
|
|
109
|
+
"td",
|
|
110
|
+
c=self.render_old_value(field),
|
|
111
|
+
**self.get_old_value_attrs(is_diff),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
td_new_value = HTML.tag(
|
|
115
|
+
"td",
|
|
116
|
+
c=self.render_new_value(field),
|
|
117
|
+
**self.get_new_value_attrs(is_diff),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return HTML.tag("tr", c=[td_field, td_old_value, td_new_value])
|
|
121
|
+
|
|
122
|
+
def render_old_value(self, field): # pylint: disable=missing-function-docstring
|
|
123
|
+
value = self.old_value(field)
|
|
124
|
+
return repr(value)
|
|
125
|
+
|
|
126
|
+
def render_new_value(self, field): # pylint: disable=missing-function-docstring
|
|
127
|
+
value = self.new_value(field)
|
|
128
|
+
return repr(value)
|
|
129
|
+
|
|
130
|
+
def get_old_value_attrs( # pylint: disable=missing-function-docstring
|
|
131
|
+
self, is_diff
|
|
132
|
+
):
|
|
133
|
+
attrs = {}
|
|
134
|
+
if self.nature == "update" and is_diff:
|
|
135
|
+
attrs["style"] = f"background-color: {self.old_color};"
|
|
136
|
+
elif self.nature == "delete":
|
|
137
|
+
attrs["style"] = f"background-color: {self.old_color};"
|
|
138
|
+
return attrs
|
|
139
|
+
|
|
140
|
+
def get_new_value_attrs( # pylint: disable=missing-function-docstring
|
|
141
|
+
self, is_diff
|
|
142
|
+
):
|
|
143
|
+
attrs = {}
|
|
144
|
+
if self.nature == "create":
|
|
145
|
+
attrs["style"] = f"background-color: {self.new_color};"
|
|
146
|
+
elif self.nature == "update" and is_diff:
|
|
147
|
+
attrs["style"] = f"background-color: {self.new_color};"
|
|
148
|
+
return attrs
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class VersionDiff(Diff):
|
|
152
|
+
"""
|
|
153
|
+
Special diff class, for use with version history views. Note that
|
|
154
|
+
while based on :class:`Diff`, this class uses a different
|
|
155
|
+
signature for the constructor.
|
|
156
|
+
|
|
157
|
+
:param version: Reference to a Continuum version record (object).
|
|
158
|
+
|
|
159
|
+
:param \\**kwargs: Remaining kwargs are passed as-is to the
|
|
160
|
+
:class:`Diff` constructor.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, version, **kwargs):
|
|
164
|
+
import sqlalchemy_continuum as continuum # pylint: disable=import-outside-toplevel
|
|
165
|
+
from wutta_continuum.util import ( # pylint: disable=import-outside-toplevel
|
|
166
|
+
render_operation_type,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
self.version = version
|
|
170
|
+
self.model_class = continuum.parent_class(type(self.version))
|
|
171
|
+
self.mapper = sa.inspect(self.model_class)
|
|
172
|
+
self.version_mapper = sa.inspect(type(self.version))
|
|
173
|
+
self.title = kwargs.pop("title", self.model_class.__name__)
|
|
174
|
+
|
|
175
|
+
self.operation_title = render_operation_type(self.version.operation_type)
|
|
176
|
+
|
|
177
|
+
if "nature" not in kwargs:
|
|
178
|
+
if (
|
|
179
|
+
version.previous
|
|
180
|
+
and version.operation_type == continuum.Operation.DELETE
|
|
181
|
+
):
|
|
182
|
+
kwargs["nature"] = "delete"
|
|
183
|
+
elif version.previous:
|
|
184
|
+
kwargs["nature"] = "update"
|
|
185
|
+
else:
|
|
186
|
+
kwargs["nature"] = "create"
|
|
187
|
+
|
|
188
|
+
if "fields" not in kwargs:
|
|
189
|
+
kwargs["fields"] = self.get_default_fields()
|
|
190
|
+
|
|
191
|
+
old_data = {}
|
|
192
|
+
new_data = {}
|
|
193
|
+
for field in kwargs["fields"]:
|
|
194
|
+
if version.previous:
|
|
195
|
+
old_data[field] = getattr(version.previous, field)
|
|
196
|
+
new_data[field] = getattr(version, field)
|
|
197
|
+
|
|
198
|
+
super().__init__(old_data, new_data, **kwargs)
|
|
199
|
+
|
|
200
|
+
def get_default_fields(self): # pylint: disable=missing-function-docstring
|
|
201
|
+
fields = sorted(self.version_mapper.columns.keys())
|
|
202
|
+
|
|
203
|
+
unwanted = [
|
|
204
|
+
"transaction_id",
|
|
205
|
+
"end_transaction_id",
|
|
206
|
+
"operation_type",
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
return [field for field in fields if field not in unwanted]
|
|
210
|
+
|
|
211
|
+
def render_version_value(self, value): # pylint: disable=missing-function-docstring
|
|
212
|
+
return HTML.tag("span", c=[repr(value)], style="font-family: monospace;")
|
|
213
|
+
|
|
214
|
+
def render_old_value(self, field):
|
|
215
|
+
if self.nature == "create":
|
|
216
|
+
return ""
|
|
217
|
+
value = self.old_value(field)
|
|
218
|
+
return self.render_version_value(value)
|
|
219
|
+
|
|
220
|
+
def render_new_value(self, field):
|
|
221
|
+
if self.nature == "delete":
|
|
222
|
+
return ""
|
|
223
|
+
value = self.new_value(field)
|
|
224
|
+
return self.render_version_value(value)
|
|
@@ -48,10 +48,22 @@ class WuttaDateTime(colander.DateTime):
|
|
|
48
48
|
the Buefy datepicker + timepicker widgets.
|
|
49
49
|
"""
|
|
50
50
|
|
|
51
|
-
def
|
|
51
|
+
def serialize(self, node, appstruct):
|
|
52
|
+
if not appstruct:
|
|
53
|
+
return colander.null
|
|
54
|
+
|
|
55
|
+
request = node.widget.request
|
|
56
|
+
config = request.wutta_config
|
|
57
|
+
app = config.get_app()
|
|
58
|
+
|
|
59
|
+
dt = app.localtime(appstruct)
|
|
60
|
+
if self.format:
|
|
61
|
+
return dt.strftime(self.format)
|
|
62
|
+
return dt.isoformat()
|
|
63
|
+
|
|
64
|
+
def deserialize( # pylint: disable=inconsistent-return-statements
|
|
52
65
|
self, node, cstruct
|
|
53
66
|
):
|
|
54
|
-
""" """
|
|
55
67
|
if not cstruct:
|
|
56
68
|
return colander.null
|
|
57
69
|
|
|
@@ -60,9 +72,16 @@ class WuttaDateTime(colander.DateTime):
|
|
|
60
72
|
"%Y-%m-%dT%I:%M %p",
|
|
61
73
|
]
|
|
62
74
|
|
|
75
|
+
request = node.widget.request
|
|
76
|
+
config = request.wutta_config
|
|
77
|
+
app = config.get_app()
|
|
78
|
+
|
|
63
79
|
for fmt in formats:
|
|
64
80
|
try:
|
|
65
|
-
|
|
81
|
+
dt = datetime.datetime.strptime(cstruct, fmt)
|
|
82
|
+
if not dt.tzinfo:
|
|
83
|
+
dt = app.localtime(dt, from_utc=False)
|
|
84
|
+
return app.make_utc(dt)
|
|
66
85
|
except Exception: # pylint: disable=broad-exception-caught
|
|
67
86
|
pass
|
|
68
87
|
|
|
@@ -252,7 +252,9 @@ class WuttaDateTimeWidget(DateTimeInputWidget):
|
|
|
252
252
|
def serialize(self, field, cstruct, **kw): # pylint: disable=empty-docstring
|
|
253
253
|
""" """
|
|
254
254
|
readonly = kw.get("readonly", self.readonly)
|
|
255
|
-
if readonly
|
|
255
|
+
if readonly:
|
|
256
|
+
if not cstruct:
|
|
257
|
+
return ""
|
|
256
258
|
dt = datetime.datetime.fromisoformat(cstruct)
|
|
257
259
|
return self.app.render_datetime(dt)
|
|
258
260
|
|
|
@@ -84,7 +84,8 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
84
84
|
.. attribute:: vue_tagname
|
|
85
85
|
|
|
86
86
|
String name for Vue component tag. By default this is
|
|
87
|
-
``'wutta-grid'``. See also :meth:`render_vue_tag()
|
|
87
|
+
``'wutta-grid'``. See also :meth:`render_vue_tag()`
|
|
88
|
+
and :attr:`vue_component`.
|
|
88
89
|
|
|
89
90
|
.. attribute:: model_class
|
|
90
91
|
|
|
@@ -841,8 +842,9 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
841
842
|
def set_tools(self, tools):
|
|
842
843
|
"""
|
|
843
844
|
Set the :attr:`tools` attribute using the given tools collection.
|
|
844
|
-
|
|
845
845
|
This will normalize the list/dict to desired internal format.
|
|
846
|
+
|
|
847
|
+
See also :meth:`add_tool()`.
|
|
846
848
|
"""
|
|
847
849
|
if tools and isinstance(tools, list):
|
|
848
850
|
if not any(isinstance(t, (tuple, list)) for t in tools):
|
|
@@ -41,6 +41,7 @@ import logging
|
|
|
41
41
|
from collections import OrderedDict
|
|
42
42
|
|
|
43
43
|
from pyramid import threadlocal
|
|
44
|
+
from pyramid.httpexceptions import HTTPFound
|
|
44
45
|
|
|
45
46
|
from wuttaweb import helpers
|
|
46
47
|
from wuttaweb.db import Session
|
|
@@ -123,6 +124,17 @@ def new_request(event):
|
|
|
123
124
|
config = request.registry.settings["wutta_config"]
|
|
124
125
|
app = config.get_app()
|
|
125
126
|
|
|
127
|
+
# nb. in rare circumstances i have received unhandled error email,
|
|
128
|
+
# which somehow was triggered by 'fanstatic.needed' being missing
|
|
129
|
+
# from the environ. not sure why that would happen, but it seems
|
|
130
|
+
# to when crawlers request a non-existent filename under the
|
|
131
|
+
# fanstatic path. there isn't a great way to handle it since
|
|
132
|
+
# e.g. can't show the normal error page if JS resources won't
|
|
133
|
+
# load, so we try a hail-mary redirect..
|
|
134
|
+
# (nb. also we skip this if environ is empty, i.e. for tests)
|
|
135
|
+
if request.environ and "fanstatic.needed" not in request.environ:
|
|
136
|
+
raise HTTPFound(location=request.route_url("home"))
|
|
137
|
+
|
|
126
138
|
request.wutta_config = config
|
|
127
139
|
|
|
128
140
|
def get_referrer(default=None):
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
</b-checkbox>
|
|
49
49
|
</b-field>
|
|
50
50
|
|
|
51
|
+
<b-field label="Time Zone"
|
|
52
|
+
:message="timezoneFieldMessage"
|
|
53
|
+
:type="timezoneFieldType">
|
|
54
|
+
<b-input name="${app.appname}.timezone.default"
|
|
55
|
+
v-model="simpleSettings['${app.appname}.timezone.default']"
|
|
56
|
+
## TODO: ideally could use @change here but it does not work..?
|
|
57
|
+
##@change="timezoneCheck()"
|
|
58
|
+
@input="timezoneCheck(); settingsNeedSaved = true" />
|
|
59
|
+
</b-field>
|
|
60
|
+
|
|
51
61
|
<b-field label="Menu Handler">
|
|
52
62
|
<input type="hidden"
|
|
53
63
|
name="${app.appname}.web.menus.handler.spec"
|
|
@@ -270,6 +280,74 @@
|
|
|
270
280
|
|
|
271
281
|
ThisPageData.menuHandlers = ${json.dumps(menu_handlers)|n}
|
|
272
282
|
|
|
283
|
+
ThisPageData.timezoneChecking = false
|
|
284
|
+
ThisPageData.timezoneInvalid = false
|
|
285
|
+
ThisPageData.timezoneError = false
|
|
286
|
+
|
|
287
|
+
ThisPage.computed.timezoneFieldMessage = function() {
|
|
288
|
+
if (this.timezoneChecking) {
|
|
289
|
+
return "Working, please wait..."
|
|
290
|
+
}
|
|
291
|
+
if (this.timezoneInvalid) {
|
|
292
|
+
return this.timezoneInvalid
|
|
293
|
+
}
|
|
294
|
+
if (this.timezoneError) {
|
|
295
|
+
return this.timezoneError
|
|
296
|
+
}
|
|
297
|
+
return "RESTART REQUIRED IF YOU CHANGE THIS. The system (default) timezone is: ${default_timezone}"
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
ThisPage.computed.timezoneFieldType = function() {
|
|
301
|
+
if (this.timezoneChecking) {
|
|
302
|
+
return 'is-warning'
|
|
303
|
+
}
|
|
304
|
+
if (this.timezoneInvalid || this.timezoneError) {
|
|
305
|
+
return 'is-danger'
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
ThisPage.methods.timezoneCheck = function() {
|
|
310
|
+
if (this.timezoneChecking) {
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.timezoneError = false
|
|
315
|
+
|
|
316
|
+
if (!this.simpleSettings['${config.appname}.timezone.default']) {
|
|
317
|
+
this.timezoneInvalid = false
|
|
318
|
+
|
|
319
|
+
} else {
|
|
320
|
+
this.timezoneChecking = true
|
|
321
|
+
const url = '${url(f"{route_prefix}.check_timezone")}'
|
|
322
|
+
const params = {
|
|
323
|
+
tzname: this.simpleSettings['${config.appname}.timezone.default'],
|
|
324
|
+
}
|
|
325
|
+
this.wuttaGET(url, params, response => {
|
|
326
|
+
this.timezoneInvalid = response.data.invalid
|
|
327
|
+
this.timezoneChecking = false
|
|
328
|
+
}, response => {
|
|
329
|
+
this.timezoneError = response?.data?.error || "unknown error"
|
|
330
|
+
this.timezoneChecking = false
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
ThisPage.methods.timezoneValidate = function() {
|
|
336
|
+
if (this.timezoneChecking) {
|
|
337
|
+
return "Still checking time zone, please try again in a moment."
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (this.timezoneError) {
|
|
341
|
+
return "Error checking time zone! Please reload page and try again."
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (this.timezoneInvalid) {
|
|
345
|
+
return "The time zone is invalid!"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
ThisPageData.validators.push(ThisPage.methods.timezoneValidate)
|
|
350
|
+
|
|
273
351
|
ThisPageData.weblibs = ${json.dumps(weblibs or [])|n}
|
|
274
352
|
|
|
275
353
|
ThisPageData.editWebLibraryShowDialog = false
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
<b-field horizontal label="Node Title">
|
|
20
20
|
<span>${app.get_node_title()}</span>
|
|
21
21
|
</b-field>
|
|
22
|
+
<b-field horizontal label="Time Zone">
|
|
23
|
+
<span>${app.get_timezone_name()}</span>
|
|
24
|
+
</b-field>
|
|
22
25
|
<b-field horizontal label="Production Mode">
|
|
23
26
|
<span>${"Yes" if config.production() else "No"}</span>
|
|
24
27
|
</b-field>
|
|
@@ -152,6 +152,8 @@
|
|
|
152
152
|
font-size: 2rem;
|
|
153
153
|
font-weight: bold;
|
|
154
154
|
margin-bottom: 0 !important;
|
|
155
|
+
display: flex;
|
|
156
|
+
gap: 0.6rem;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
#content-title h1 {
|
|
@@ -242,7 +244,9 @@
|
|
|
242
244
|
## nb. this is the index title proper
|
|
243
245
|
<div class="level-left">
|
|
244
246
|
<div id="header-index-title" class="level-item">
|
|
245
|
-
% if
|
|
247
|
+
% if index_title_rendered is not Undefined and index_title_rendered:
|
|
248
|
+
<h1 class="title">${index_title_rendered}</h1>
|
|
249
|
+
% elif index_title:
|
|
246
250
|
% if index_url:
|
|
247
251
|
<h1 class="title">${h.link_to(index_title, index_url)}</h1>
|
|
248
252
|
% else:
|
|
@@ -279,7 +283,7 @@
|
|
|
279
283
|
class="has-background-primary">
|
|
280
284
|
<div style="display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem;">
|
|
281
285
|
|
|
282
|
-
<div style="width: 60%; display: flex; gap:
|
|
286
|
+
<div style="width: 60%; display: flex; gap: 1rem;">
|
|
283
287
|
|
|
284
288
|
<h1 class="title has-text-white"
|
|
285
289
|
v-html="contentTitleHTML">
|
|
@@ -781,7 +785,32 @@
|
|
|
781
785
|
% endif
|
|
782
786
|
</%def>
|
|
783
787
|
|
|
784
|
-
<%def name="render_prevnext_header_buttons()"
|
|
788
|
+
<%def name="render_prevnext_header_buttons()">
|
|
789
|
+
% if show_prev_next is not Undefined and show_prev_next:
|
|
790
|
+
<b-button tag="a"
|
|
791
|
+
% if prev_url:
|
|
792
|
+
href="${prev_url}"
|
|
793
|
+
% else:
|
|
794
|
+
href="#"
|
|
795
|
+
disabled
|
|
796
|
+
% endif
|
|
797
|
+
icon-pack="fas"
|
|
798
|
+
icon-left="arrow-left">
|
|
799
|
+
Older
|
|
800
|
+
</b-button>
|
|
801
|
+
<b-button tag="a"
|
|
802
|
+
% if next_url:
|
|
803
|
+
href="${next_url}"
|
|
804
|
+
% else:
|
|
805
|
+
href="#"
|
|
806
|
+
disabled
|
|
807
|
+
% endif
|
|
808
|
+
icon-pack="fas"
|
|
809
|
+
icon-left="arrow-right">
|
|
810
|
+
Newer
|
|
811
|
+
</b-button>
|
|
812
|
+
% endif
|
|
813
|
+
</%def>
|
|
785
814
|
|
|
786
815
|
##############################
|
|
787
816
|
## vue components + app
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## -*- coding: utf-8; -*-
|
|
2
|
+
<table class="table is-fullwidth is-bordered is-narrow">
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
% for column in diff.columns:
|
|
6
|
+
<th>${column}</th>
|
|
7
|
+
% endfor
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
% for field in diff.fields:
|
|
12
|
+
${diff.render_field_row(field)}
|
|
13
|
+
% endfor
|
|
14
|
+
</tbody>
|
|
15
|
+
</table>
|
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
<%def name="content_title()">${instance_title}</%def>
|
|
7
7
|
|
|
8
|
+
<%def name="render_instance_header_title_extras()">
|
|
9
|
+
${parent.render_instance_header_title_extras()}
|
|
10
|
+
% if master.should_expose_versions():
|
|
11
|
+
<b-button tag="a"
|
|
12
|
+
href="${master.get_action_url('versions', instance)}"
|
|
13
|
+
icon-pack="fas"
|
|
14
|
+
icon-left="history">
|
|
15
|
+
View History
|
|
16
|
+
</b-button>
|
|
17
|
+
% endif
|
|
18
|
+
</%def>
|
|
19
|
+
|
|
8
20
|
<%def name="page_layout()">
|
|
9
21
|
|
|
10
22
|
% if master.has_rows:
|