WuttaWeb 0.25.1__tar.gz → 0.26.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.25.1 → wuttaweb-0.26.0}/CHANGELOG.md +19 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/PKG-INFO +3 -2
- wuttaweb-0.26.0/docs/api/wuttaweb.views.alembic.rst +6 -0
- wuttaweb-0.26.0/docs/api/wuttaweb.views.tables.rst +6 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/conf.py +1 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/index.rst +2 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/pyproject.toml +3 -2
- wuttaweb-0.26.0/src/wuttaweb/code-templates/new-table.mako +68 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/forms/base.py +175 -24
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/forms/widgets.py +74 -1
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/grids/base.py +12 -7
- wuttaweb-0.26.0/src/wuttaweb/templates/alembic/dashboard.mako +234 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/alembic/migrations/configure.mako +31 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/alembic/migrations/create.mako +70 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/alembic/migrations/index.mako +25 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/alembic/migrations/view.mako +17 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/appinfo/index.mako +20 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/checkbox.pt +4 -2
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/form.mako +8 -6
- wuttaweb-0.26.0/src/wuttaweb/templates/forms/vue_buttons.mako +43 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/forms/vue_fields.mako +4 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/forms/vue_template.mako +54 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/tables/app/create.mako +1035 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/tables/app/index.mako +25 -0
- wuttaweb-0.26.0/src/wuttaweb/templates/upgrades/index.mako +14 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/wutta-components.mako +67 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/testing.py +5 -3
- wuttaweb-0.26.0/src/wuttaweb/views/alembic.py +565 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/batch.py +2 -2
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/common.py +3 -3
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/essential.py +23 -1
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/master.py +268 -146
- wuttaweb-0.26.0/src/wuttaweb/views/tables.py +406 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/users.py +1 -0
- wuttaweb-0.26.0/tests/forms/main_template.mako +9 -0
- wuttaweb-0.26.0/tests/forms/main_template_with_fields.mako +11 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/forms/test_base.py +43 -29
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/forms/test_widgets.py +127 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/grids/test_base.py +13 -0
- wuttaweb-0.26.0/tests/views/test_alembic.py +460 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_master.py +127 -48
- wuttaweb-0.26.0/tests/views/test_tables.py +326 -0
- wuttaweb-0.25.1/src/wuttaweb/templates/forms/vue_template.mako +0 -99
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/.gitignore +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/.hgignore +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/.pylintrc +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/COPYING.txt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/README.md +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/Makefile +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/_static/.keepme +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.app.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.auth.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.cli.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.cli.webapp.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.conf.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.db.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.db.sess.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.diffs.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.emails.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.forms.base.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.forms.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.grids.base.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.grids.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.handler.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.helpers.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.menus.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.progress.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.static.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.subscribers.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.util.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.auth.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.base.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.batch.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.common.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.email.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.essential.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.master.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.people.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.progress.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.reports.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.roles.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.settings.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/api/wuttaweb.views.users.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/glossary.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/make.bat +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/cli/builtin.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/cli/index.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/templates/base.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/templates/index.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/templates/lookup.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/docs/narr/templates/overview.rst +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/_version.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/app.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/auth.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/cli/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/cli/webapp.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/conf.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/db/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/db/continuum.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/db/sess.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/diffs.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/email-templates/feedback.html.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/email-templates/feedback.txt.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/emails.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/forms/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/forms/schema.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/grids/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/grids/filters.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/handler.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/helpers.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/menus.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/progress.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/static/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/static/img/favicon.ico +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/static/img/logo.png +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/static/img/testing.png +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/subscribers.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/auth/login.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/base.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/base_meta.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/batch/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/configure.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/password.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/email_recips.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/select.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/deform/wutta_checked_password.pt +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/diff.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/email/settings/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/forbidden.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/home.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/configure.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/create.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/create_row.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/delete.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/edit.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/form.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/index.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/view_version.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/master/view_versions.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/notfound.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/page.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/progress.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/reports/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/setup.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/themes/butterfly/base.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/themes/butterfly/buefy-components.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/themes/butterfly/buefy-plugin.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/themes/butterfly/http-plugin.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/upgrade.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/templates/users/view.mako +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/util.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/auth.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/base.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/email.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/people.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/progress.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/reports.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/roles.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/settings.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/src/wuttaweb/views/upgrades.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tasks.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/cli/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/cli/test_webapp.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/db/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/db/test_continuum.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/forms/test_schema.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/grids/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/grids/test_filters.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_oruga.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_oruga_bulma.css +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_oruga_bulma.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_vue.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/buefy.css +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/buefy.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/fontawesome.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/vue.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/libcache/vue_resource.js +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_app.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_auth.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_diffs.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_emails.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_handler.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_helpers.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_menus.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_progress.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_static.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_subscribers.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/test_util.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/util.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/__init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test___init__.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_auth.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_base.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_batch.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_common.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_email.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_essential.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_people.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_progress.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_reports.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_roles.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_settings.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_upgrades.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tests/views/test_users.py +0 -0
- {wuttaweb-0.25.1 → wuttaweb-0.26.0}/tox.ini +0 -0
|
@@ -5,6 +5,25 @@ 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.26.0 (2025-12-28)
|
|
9
|
+
|
|
10
|
+
### Feat
|
|
11
|
+
|
|
12
|
+
- add "wizard" for creating new table/model/revision
|
|
13
|
+
- add support for Create Alembic Migration
|
|
14
|
+
- add CopyableTextWidget and `<wutta-copyable-text>` component
|
|
15
|
+
- overhaul how form vue template is rendered
|
|
16
|
+
- add basic views for Alembic Migrations, Dashboard
|
|
17
|
+
- add basic Table views
|
|
18
|
+
|
|
19
|
+
### Fix
|
|
20
|
+
|
|
21
|
+
- let checkbox widget show static text instead of Yes/No
|
|
22
|
+
- rename form-saving methods etc. for consistency in MasterView
|
|
23
|
+
- temporarily avoid make_uuid()
|
|
24
|
+
- remove password filter option for Users grid
|
|
25
|
+
- use smarter default for `grid.sort_multiple` based on model class
|
|
26
|
+
|
|
8
27
|
## v0.25.1 (2025-12-20)
|
|
9
28
|
|
|
10
29
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: WuttaWeb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.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
|
|
@@ -37,9 +37,10 @@ Requires-Dist: pyramid-fanstatic
|
|
|
37
37
|
Requires-Dist: pyramid-mako
|
|
38
38
|
Requires-Dist: pyramid-tm
|
|
39
39
|
Requires-Dist: pyramid>=2
|
|
40
|
+
Requires-Dist: sqlalchemy-utils
|
|
40
41
|
Requires-Dist: waitress
|
|
41
42
|
Requires-Dist: webhelpers2
|
|
42
|
-
Requires-Dist: wuttjamaican[db]>=0.
|
|
43
|
+
Requires-Dist: wuttjamaican[db]>=0.28.0
|
|
43
44
|
Requires-Dist: zope-sqlalchemy>=1.5
|
|
44
45
|
Provides-Extra: continuum
|
|
45
46
|
Requires-Dist: wutta-continuum>=0.3.0; extra == 'continuum'
|
|
@@ -28,6 +28,7 @@ templates_path = ["_templates"]
|
|
|
28
28
|
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
29
29
|
|
|
30
30
|
intersphinx_mapping = {
|
|
31
|
+
"alembic": ("https://alembic.sqlalchemy.org/en/latest/", None),
|
|
31
32
|
"colander": ("https://docs.pylonsproject.org/projects/colander/en/latest/", None),
|
|
32
33
|
"deform": ("https://docs.pylonsproject.org/projects/deform/en/latest/", None),
|
|
33
34
|
"fanstatic": ("https://www.fanstatic.org/en/latest/", None),
|
|
@@ -58,6 +58,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
58
58
|
api/wuttaweb.subscribers
|
|
59
59
|
api/wuttaweb.util
|
|
60
60
|
api/wuttaweb.views
|
|
61
|
+
api/wuttaweb.views.alembic
|
|
61
62
|
api/wuttaweb.views.auth
|
|
62
63
|
api/wuttaweb.views.base
|
|
63
64
|
api/wuttaweb.views.batch
|
|
@@ -70,6 +71,7 @@ the narrative docs are pretty scant. That will eventually change.
|
|
|
70
71
|
api/wuttaweb.views.reports
|
|
71
72
|
api/wuttaweb.views.roles
|
|
72
73
|
api/wuttaweb.views.settings
|
|
74
|
+
api/wuttaweb.views.tables
|
|
73
75
|
api/wuttaweb.views.upgrades
|
|
74
76
|
api/wuttaweb.views.users
|
|
75
77
|
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "WuttaWeb"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.26.0"
|
|
10
10
|
description = "Web App for Wutta Framework"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
|
@@ -42,9 +42,10 @@ dependencies = [
|
|
|
42
42
|
"pyramid_fanstatic",
|
|
43
43
|
"pyramid_mako",
|
|
44
44
|
"pyramid_tm",
|
|
45
|
+
"SQLAlchemy-Utils",
|
|
45
46
|
"waitress",
|
|
46
47
|
"WebHelpers2",
|
|
47
|
-
"WuttJamaican[db]>=0.
|
|
48
|
+
"WuttJamaican[db]>=0.28.0",
|
|
48
49
|
"zope.sqlalchemy>=1.5",
|
|
49
50
|
]
|
|
50
51
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
## -*- coding: utf-8; mode: python; -*-
|
|
2
|
+
# -*- coding: utf-8; -*-
|
|
3
|
+
"""
|
|
4
|
+
Model definition for ${model_title_plural}
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sqlalchemy as sa
|
|
8
|
+
from sqlalchemy import orm
|
|
9
|
+
|
|
10
|
+
from wuttjamaican.db import model
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ${model_name}(model.Base):
|
|
14
|
+
"""
|
|
15
|
+
${description}
|
|
16
|
+
"""
|
|
17
|
+
__tablename__ = "${table_name}"
|
|
18
|
+
% if any([c["data_type"]["type"] == "_fk_uuid_" for c in columns]):
|
|
19
|
+
__table_args__ = (
|
|
20
|
+
% for column in columns:
|
|
21
|
+
% if column["data_type"]["type"] == "_fk_uuid_":
|
|
22
|
+
sa.ForeignKeyConstraint(["${column['name']}"], ["${column['data_type']['reference']}.uuid"],
|
|
23
|
+
name="${table_name}_fk_${column['data_type']['reference']}"),
|
|
24
|
+
% endif
|
|
25
|
+
% endfor
|
|
26
|
+
)
|
|
27
|
+
% endif
|
|
28
|
+
% if versioned:
|
|
29
|
+
% if all([c["versioned"] for c in columns]):
|
|
30
|
+
__versioned__ = {}
|
|
31
|
+
% else:
|
|
32
|
+
__versioned__ = {
|
|
33
|
+
"exclude": [
|
|
34
|
+
% for column in columns:
|
|
35
|
+
% if not column["versioned"]:
|
|
36
|
+
"${column['name']}",
|
|
37
|
+
% endif
|
|
38
|
+
% endfor
|
|
39
|
+
],
|
|
40
|
+
}
|
|
41
|
+
% endif
|
|
42
|
+
% endif
|
|
43
|
+
__wutta_hint__ = {
|
|
44
|
+
"model_title": "${model_title}",
|
|
45
|
+
"model_title_plural": "${model_title_plural}",
|
|
46
|
+
}
|
|
47
|
+
% for column in columns:
|
|
48
|
+
|
|
49
|
+
% if column["name"] == "uuid":
|
|
50
|
+
uuid = model.uuid_column()
|
|
51
|
+
% else:
|
|
52
|
+
${column["name"]} = sa.Column(${column["formatted_data_type"]}, nullable=${column["nullable"]}, doc="""
|
|
53
|
+
${column["description"] or ""}
|
|
54
|
+
""")
|
|
55
|
+
% if column["data_type"]["type"] == "_fk_uuid_" and column["relationship"]:
|
|
56
|
+
${column["relationship"]["name"]} = orm.relationship(
|
|
57
|
+
"${column['relationship']['reference_model']}",
|
|
58
|
+
doc="""
|
|
59
|
+
${column["description"] or ""}
|
|
60
|
+
""")
|
|
61
|
+
% endif
|
|
62
|
+
% endif
|
|
63
|
+
% endfor
|
|
64
|
+
|
|
65
|
+
# TODO: you usually should define the __str__() method
|
|
66
|
+
|
|
67
|
+
# def __str__(self):
|
|
68
|
+
# return self.name or ""
|
|
@@ -275,6 +275,10 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
275
275
|
deform_form = None
|
|
276
276
|
validated = None
|
|
277
277
|
|
|
278
|
+
vue_template = "/forms/vue_template.mako"
|
|
279
|
+
fields_template = "/forms/vue_fields.mako"
|
|
280
|
+
buttons_template = "/forms/vue_buttons.mako"
|
|
281
|
+
|
|
278
282
|
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
|
|
279
283
|
self,
|
|
280
284
|
request,
|
|
@@ -331,6 +335,7 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
331
335
|
self.show_button_cancel = show_button_cancel
|
|
332
336
|
self.button_label_cancel = button_label_cancel
|
|
333
337
|
self.auto_disable_cancel = auto_disable_cancel
|
|
338
|
+
self.form_attrs = {}
|
|
334
339
|
|
|
335
340
|
self.config = self.request.wutta_config
|
|
336
341
|
self.app = self.config.get_app()
|
|
@@ -806,7 +811,10 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
806
811
|
# get fields
|
|
807
812
|
fields = self.get_fields()
|
|
808
813
|
if not fields:
|
|
809
|
-
raise
|
|
814
|
+
raise ValueError(
|
|
815
|
+
"could not determine fields list; "
|
|
816
|
+
"please set model_class or fields explicitly"
|
|
817
|
+
)
|
|
810
818
|
|
|
811
819
|
if self.model_class:
|
|
812
820
|
|
|
@@ -939,7 +947,7 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
939
947
|
"""
|
|
940
948
|
return HTML.tag(self.vue_tagname, **kwargs)
|
|
941
949
|
|
|
942
|
-
def render_vue_template(self, template=
|
|
950
|
+
def render_vue_template(self, template=None, **context):
|
|
943
951
|
"""
|
|
944
952
|
Render the Vue template block for the form.
|
|
945
953
|
|
|
@@ -954,8 +962,8 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
954
962
|
</script>
|
|
955
963
|
|
|
956
964
|
<script>
|
|
957
|
-
WuttaFormData = {}
|
|
958
|
-
WuttaForm = {
|
|
965
|
+
const WuttaFormData = {}
|
|
966
|
+
const WuttaForm = {
|
|
959
967
|
template: 'wutta-form-template',
|
|
960
968
|
}
|
|
961
969
|
</script>
|
|
@@ -969,36 +977,121 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
969
977
|
Actual output will of course depend on form attributes, i.e.
|
|
970
978
|
:attr:`vue_tagname` and :attr:`fields` list etc.
|
|
971
979
|
|
|
972
|
-
|
|
973
|
-
|
|
980
|
+
Default logic will also invoke (indirectly):
|
|
981
|
+
|
|
982
|
+
* :meth:`render_vue_fields()`
|
|
983
|
+
* :meth:`render_vue_buttons()`
|
|
984
|
+
|
|
985
|
+
:param template: Optional template path to override the class
|
|
986
|
+
default.
|
|
987
|
+
|
|
988
|
+
:returns: HTML literal
|
|
974
989
|
"""
|
|
990
|
+
context = self.get_vue_context(**context)
|
|
991
|
+
html = render(template or self.vue_template, context)
|
|
992
|
+
return HTML.literal(html)
|
|
993
|
+
|
|
994
|
+
def get_vue_context(self, **context): # pylint: disable=missing-function-docstring
|
|
975
995
|
context["form"] = self
|
|
976
996
|
context["dform"] = self.get_deform()
|
|
977
997
|
context.setdefault("request", self.request)
|
|
978
998
|
context["model_data"] = self.get_vue_model_data()
|
|
979
999
|
|
|
980
1000
|
# set form method, enctype
|
|
981
|
-
context.setdefault("form_attrs",
|
|
982
|
-
|
|
1001
|
+
form_attrs = context.setdefault("form_attrs", dict(self.form_attrs))
|
|
1002
|
+
form_attrs.setdefault("method", self.action_method)
|
|
983
1003
|
if self.action_method == "post":
|
|
984
|
-
|
|
1004
|
+
form_attrs.setdefault("enctype", "multipart/form-data")
|
|
985
1005
|
|
|
986
1006
|
# auto disable button on submit
|
|
987
1007
|
if self.auto_disable_submit:
|
|
988
|
-
|
|
1008
|
+
form_attrs["@submit"] = "formSubmitting = true"
|
|
1009
|
+
|
|
1010
|
+
# duplicate entire context for sake of fields/buttons template
|
|
1011
|
+
context["form_context"] = context
|
|
1012
|
+
|
|
1013
|
+
return context
|
|
1014
|
+
|
|
1015
|
+
def render_vue_fields(self, context, template=None, **kwargs):
|
|
1016
|
+
"""
|
|
1017
|
+
Render the fields section within the form template.
|
|
1018
|
+
|
|
1019
|
+
This is normally invoked from within the form's
|
|
1020
|
+
``vue_template`` like this:
|
|
1021
|
+
|
|
1022
|
+
.. code-block:: none
|
|
1023
|
+
|
|
1024
|
+
${form.render_vue_fields(form_context)}
|
|
1025
|
+
|
|
1026
|
+
There is a default ``fields_template`` but that is only the
|
|
1027
|
+
last resort. Logic will first look for a
|
|
1028
|
+
``form_vue_fields()`` def within the *main template* being
|
|
1029
|
+
rendered for the page.
|
|
1030
|
+
|
|
1031
|
+
An example will surely help:
|
|
1032
|
+
|
|
1033
|
+
.. code-block:: mako
|
|
1034
|
+
|
|
1035
|
+
<%inherit file="/master/edit.mako" />
|
|
1036
|
+
|
|
1037
|
+
<%def name="form_vue_fields()">
|
|
1038
|
+
|
|
1039
|
+
<p>this is my custom fields section:</p>
|
|
1040
|
+
|
|
1041
|
+
${form.render_vue_field("myfield")}
|
|
1042
|
+
|
|
1043
|
+
</%def>
|
|
1044
|
+
|
|
1045
|
+
This keeps the custom fields section within the main page
|
|
1046
|
+
template as opposed to yet another file. But if your page
|
|
1047
|
+
template has no ``form_vue_fields()`` def, then the class
|
|
1048
|
+
default template is used. (Unless the ``template`` param
|
|
1049
|
+
is specified.)
|
|
1050
|
+
|
|
1051
|
+
See also :meth:`render_vue_template()` and
|
|
1052
|
+
:meth:`render_vue_buttons()`.
|
|
1053
|
+
|
|
1054
|
+
:param context: This must be the original context as provided
|
|
1055
|
+
to the form's ``vue_template``. See example above.
|
|
1056
|
+
|
|
1057
|
+
:param template: Optional template path to use instead of the
|
|
1058
|
+
defaults described above.
|
|
1059
|
+
|
|
1060
|
+
:returns: HTML literal
|
|
1061
|
+
"""
|
|
1062
|
+
context.update(kwargs)
|
|
1063
|
+
html = False
|
|
1064
|
+
|
|
1065
|
+
if not template:
|
|
1066
|
+
|
|
1067
|
+
if main_template := context.get("main_template"):
|
|
1068
|
+
try:
|
|
1069
|
+
vue_fields = main_template.get_def("form_vue_fields")
|
|
1070
|
+
except AttributeError:
|
|
1071
|
+
pass
|
|
1072
|
+
else:
|
|
1073
|
+
html = vue_fields.render(**context)
|
|
989
1074
|
|
|
990
|
-
|
|
991
|
-
|
|
1075
|
+
if html is False:
|
|
1076
|
+
template = self.fields_template
|
|
1077
|
+
|
|
1078
|
+
if html is False:
|
|
1079
|
+
html = render(template, context)
|
|
1080
|
+
|
|
1081
|
+
return HTML.literal(html)
|
|
992
1082
|
|
|
993
1083
|
def render_vue_field( # pylint: disable=unused-argument,too-many-locals
|
|
994
1084
|
self,
|
|
995
1085
|
fieldname,
|
|
996
1086
|
readonly=None,
|
|
1087
|
+
label=True,
|
|
1088
|
+
horizontal=True,
|
|
997
1089
|
**kwargs,
|
|
998
1090
|
):
|
|
999
1091
|
"""
|
|
1000
1092
|
Render the given field completely, i.e. ``<b-field>`` wrapper
|
|
1001
|
-
with label and
|
|
1093
|
+
with label and a widget, with validation errors flagged as
|
|
1094
|
+
needed.
|
|
1002
1095
|
|
|
1003
1096
|
Actual output will depend on the field attributes etc.
|
|
1004
1097
|
Typical output might look like:
|
|
@@ -1009,14 +1102,23 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
1009
1102
|
horizontal
|
|
1010
1103
|
type="is-danger"
|
|
1011
1104
|
message="something went wrong!">
|
|
1012
|
-
|
|
1105
|
+
<b-input name="foo"
|
|
1106
|
+
v-model="${form.get_field_vmodel('foo')}" />
|
|
1013
1107
|
</b-field>
|
|
1014
1108
|
|
|
1015
|
-
|
|
1109
|
+
:param fieldname: Name of field to render.
|
|
1110
|
+
|
|
1111
|
+
:param readonly: Optional override for readonly flag.
|
|
1016
1112
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1113
|
+
:param label: Whether to include/set the field label.
|
|
1114
|
+
|
|
1115
|
+
:param horizontal: Boolean value for the ``horizontal`` flag
|
|
1116
|
+
on the field.
|
|
1117
|
+
|
|
1118
|
+
:param \\**kwargs: Remaining kwargs are passed to widget's
|
|
1119
|
+
``serialize()`` method.
|
|
1120
|
+
|
|
1121
|
+
:returns: HTML literal
|
|
1020
1122
|
"""
|
|
1021
1123
|
# readonly comes from: caller, field flag, or form flag
|
|
1022
1124
|
if readonly is None:
|
|
@@ -1034,10 +1136,9 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
1034
1136
|
|
|
1035
1137
|
# render proper widget if field is in deform/schema
|
|
1036
1138
|
field = dform[fieldname]
|
|
1037
|
-
kw = {}
|
|
1038
1139
|
if readonly:
|
|
1039
|
-
|
|
1040
|
-
html = field.serialize(**
|
|
1140
|
+
kwargs["readonly"] = True
|
|
1141
|
+
html = field.serialize(**kwargs)
|
|
1041
1142
|
|
|
1042
1143
|
else:
|
|
1043
1144
|
# render static text if field not in deform/schema
|
|
@@ -1052,12 +1153,13 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
1052
1153
|
html = HTML.literal(html or " ")
|
|
1053
1154
|
|
|
1054
1155
|
# render field label
|
|
1055
|
-
label
|
|
1156
|
+
if label:
|
|
1157
|
+
label = self.get_label(fieldname)
|
|
1056
1158
|
|
|
1057
1159
|
# b-field attrs
|
|
1058
1160
|
attrs = {
|
|
1059
|
-
":horizontal": "true",
|
|
1060
|
-
"label": label,
|
|
1161
|
+
":horizontal": "true" if horizontal else "false",
|
|
1162
|
+
"label": label or "",
|
|
1061
1163
|
}
|
|
1062
1164
|
|
|
1063
1165
|
# next we will build array of messages to display..some
|
|
@@ -1085,6 +1187,36 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
1085
1187
|
|
|
1086
1188
|
return HTML.tag("b-field", c=[html], **attrs)
|
|
1087
1189
|
|
|
1190
|
+
def render_vue_buttons(self, context, template=None, **kwargs):
|
|
1191
|
+
"""
|
|
1192
|
+
Render the buttons section within the form template.
|
|
1193
|
+
|
|
1194
|
+
This is normally invoked from within the form's
|
|
1195
|
+
``vue_template`` like this:
|
|
1196
|
+
|
|
1197
|
+
.. code-block:: none
|
|
1198
|
+
|
|
1199
|
+
${form.render_vue_buttons(form_context)}
|
|
1200
|
+
|
|
1201
|
+
.. note::
|
|
1202
|
+
|
|
1203
|
+
This method does not yet inspect the main page template,
|
|
1204
|
+
unlike :meth:`render_vue_fields()`.
|
|
1205
|
+
|
|
1206
|
+
See also :meth:`render_vue_template()`.
|
|
1207
|
+
|
|
1208
|
+
:param context: This must be the original context as provided
|
|
1209
|
+
to the form's ``vue_template``. See example above.
|
|
1210
|
+
|
|
1211
|
+
:param template: Optional template path to override the class
|
|
1212
|
+
default.
|
|
1213
|
+
|
|
1214
|
+
:returns: HTML literal
|
|
1215
|
+
"""
|
|
1216
|
+
context.update(kwargs)
|
|
1217
|
+
html = render(template or self.buttons_template, context)
|
|
1218
|
+
return HTML.literal(html)
|
|
1219
|
+
|
|
1088
1220
|
def render_vue_finalize(self):
|
|
1089
1221
|
"""
|
|
1090
1222
|
Render the Vue "finalize" script for the form.
|
|
@@ -1103,6 +1235,25 @@ class Form: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
1103
1235
|
"""
|
|
1104
1236
|
return render_vue_finalize(self.vue_tagname, self.vue_component)
|
|
1105
1237
|
|
|
1238
|
+
def get_field_vmodel(self, field):
|
|
1239
|
+
"""
|
|
1240
|
+
Convenience to return the ``v-model`` data reference for the
|
|
1241
|
+
given field. For instance:
|
|
1242
|
+
|
|
1243
|
+
.. code-block:: none
|
|
1244
|
+
|
|
1245
|
+
<b-input name="myfield"
|
|
1246
|
+
v-model="${form.get_field_vmodel('myfield')}" />
|
|
1247
|
+
|
|
1248
|
+
<div v-show="${form.get_field_vmodel('myfield')} == 'easter'">
|
|
1249
|
+
easter egg!
|
|
1250
|
+
</div>
|
|
1251
|
+
|
|
1252
|
+
:returns: JS-valid string referencing the field value
|
|
1253
|
+
"""
|
|
1254
|
+
dform = self.get_deform()
|
|
1255
|
+
return f"modelData.{dform[field].oid}"
|
|
1256
|
+
|
|
1106
1257
|
def get_vue_model_data(self):
|
|
1107
1258
|
"""
|
|
1108
1259
|
Returns a dict with form model data. Values may be nested
|
|
@@ -60,7 +60,7 @@ from deform.widget import ( # pylint: disable=unused-import
|
|
|
60
60
|
DateTimeInputWidget,
|
|
61
61
|
MoneyInputWidget,
|
|
62
62
|
)
|
|
63
|
-
from webhelpers2.html import HTML
|
|
63
|
+
from webhelpers2.html import HTML, tags
|
|
64
64
|
|
|
65
65
|
from wuttjamaican.conf import parse_list
|
|
66
66
|
|
|
@@ -147,6 +147,24 @@ class NotesWidget(TextAreaWidget):
|
|
|
147
147
|
readonly_template = "readonly/notes"
|
|
148
148
|
|
|
149
149
|
|
|
150
|
+
class CopyableTextWidget(Widget): # pylint: disable=abstract-method
|
|
151
|
+
"""
|
|
152
|
+
A readonly text widget which adds a "copy" icon/link just after
|
|
153
|
+
the text.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def serialize(self, field, cstruct, **kw): # pylint: disable=empty-docstring
|
|
157
|
+
""" """
|
|
158
|
+
if not cstruct:
|
|
159
|
+
return colander.null
|
|
160
|
+
|
|
161
|
+
return HTML.tag("wutta-copyable-text", **{"text": cstruct})
|
|
162
|
+
|
|
163
|
+
def deserialize(self, field, pstruct): # pylint: disable=empty-docstring
|
|
164
|
+
""" """
|
|
165
|
+
raise NotImplementedError
|
|
166
|
+
|
|
167
|
+
|
|
150
168
|
class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
|
|
151
169
|
"""
|
|
152
170
|
Custom widget for :class:`python:set` fields.
|
|
@@ -537,3 +555,58 @@ class BatchIdWidget(Widget): # pylint: disable=abstract-method
|
|
|
537
555
|
|
|
538
556
|
batch_id = int(cstruct)
|
|
539
557
|
return f"{batch_id:08d}"
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
class AlembicRevisionWidget(Widget): # pylint: disable=missing-class-docstring
|
|
561
|
+
"""
|
|
562
|
+
Widget to show an Alembic revision identifier, with link to view
|
|
563
|
+
the revision.
|
|
564
|
+
"""
|
|
565
|
+
|
|
566
|
+
def __init__(self, request, *args, **kwargs):
|
|
567
|
+
super().__init__(*args, **kwargs)
|
|
568
|
+
self.request = request
|
|
569
|
+
|
|
570
|
+
def serialize(self, field, cstruct, **kw): # pylint: disable=empty-docstring
|
|
571
|
+
""" """
|
|
572
|
+
if not cstruct:
|
|
573
|
+
return colander.null
|
|
574
|
+
|
|
575
|
+
return tags.link_to(
|
|
576
|
+
cstruct, self.request.route_url("alembic.migrations.view", revision=cstruct)
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
def deserialize(self, field, pstruct): # pylint: disable=empty-docstring
|
|
580
|
+
""" """
|
|
581
|
+
raise NotImplementedError
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
class AlembicRevisionsWidget(Widget):
|
|
585
|
+
"""
|
|
586
|
+
Widget to show list of Alembic revision identifiers, with links to
|
|
587
|
+
view each revision.
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
def __init__(self, request, *args, **kwargs):
|
|
591
|
+
super().__init__(*args, **kwargs)
|
|
592
|
+
self.request = request
|
|
593
|
+
self.config = self.request.wutta_config
|
|
594
|
+
|
|
595
|
+
def serialize(self, field, cstruct, **kw): # pylint: disable=empty-docstring
|
|
596
|
+
""" """
|
|
597
|
+
if not cstruct:
|
|
598
|
+
return colander.null
|
|
599
|
+
|
|
600
|
+
revisions = []
|
|
601
|
+
for rev in self.config.parse_list(cstruct):
|
|
602
|
+
revisions.append(
|
|
603
|
+
tags.link_to(
|
|
604
|
+
rev, self.request.route_url("alembic.migrations.view", revision=rev)
|
|
605
|
+
)
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
return ", ".join(revisions)
|
|
609
|
+
|
|
610
|
+
def deserialize(self, field, pstruct): # pylint: disable=empty-docstring
|
|
611
|
+
""" """
|
|
612
|
+
raise NotImplementedError
|
|
@@ -186,8 +186,8 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
186
186
|
.. attribute:: sort_multiple
|
|
187
187
|
|
|
188
188
|
Boolean indicating whether "multi-column" sorting is allowed.
|
|
189
|
-
|
|
190
|
-
may be sorted at a time.
|
|
189
|
+
This is true by default, where possible. If false then only
|
|
190
|
+
one column may be sorted at a time.
|
|
191
191
|
|
|
192
192
|
Only relevant if :attr:`sortable` is true, but applies to both
|
|
193
193
|
frontend and backend sorting.
|
|
@@ -382,7 +382,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
382
382
|
joined = None
|
|
383
383
|
pager = None
|
|
384
384
|
|
|
385
|
-
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
|
|
385
|
+
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals,too-many-branches,too-many-statements
|
|
386
386
|
self,
|
|
387
387
|
request,
|
|
388
388
|
vue_tagname="wutta-grid",
|
|
@@ -399,7 +399,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
399
399
|
linked_columns=None,
|
|
400
400
|
hidden_columns=None,
|
|
401
401
|
sortable=False,
|
|
402
|
-
sort_multiple=
|
|
402
|
+
sort_multiple=None,
|
|
403
403
|
sort_on_backend=True,
|
|
404
404
|
sorters=None,
|
|
405
405
|
sort_defaults=None,
|
|
@@ -441,7 +441,12 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
441
441
|
|
|
442
442
|
# sorting
|
|
443
443
|
self.sortable = sortable
|
|
444
|
-
|
|
444
|
+
if sort_multiple is not None:
|
|
445
|
+
self.sort_multiple = sort_multiple
|
|
446
|
+
elif self.request.use_oruga:
|
|
447
|
+
self.sort_multiple = False
|
|
448
|
+
else:
|
|
449
|
+
self.sort_multiple = bool(self.model_class)
|
|
445
450
|
if self.sort_multiple and self.request.use_oruga:
|
|
446
451
|
log.warning(
|
|
447
452
|
"grid.sort_multiple is not implemented for Oruga-based templates"
|
|
@@ -848,7 +853,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
848
853
|
"""
|
|
849
854
|
if tools and isinstance(tools, list):
|
|
850
855
|
if not any(isinstance(t, (tuple, list)) for t in tools):
|
|
851
|
-
tools = [(self.app.
|
|
856
|
+
tools = [(self.app.make_true_uuid().hex, t) for t in tools]
|
|
852
857
|
self.tools = OrderedDict(tools or [])
|
|
853
858
|
|
|
854
859
|
def add_tool(self, html, key=None):
|
|
@@ -864,7 +869,7 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
|
|
|
864
869
|
See also :meth:`set_tools()`.
|
|
865
870
|
"""
|
|
866
871
|
if not key:
|
|
867
|
-
key = self.app.
|
|
872
|
+
key = self.app.make_true_uuid().hex
|
|
868
873
|
self.tools[key] = html
|
|
869
874
|
|
|
870
875
|
##############################
|