diff --git a/datasette/views/table.py b/datasette/views/table.py index 5175c7d9..82c7e03d 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -54,6 +54,8 @@ from .database import QueryView, _custom_column_type_options_for_create_table from .table_extras import ( TABLE_EXTRA_BUNDLES, TableExtraContext, + precompute_database_action_permissions, + precompute_table_action_permissions, resolve_table_extras, table_extra_registry, ) @@ -1247,7 +1249,10 @@ class TableAlterView(BaseView): defaults[args.name] = _literal_default( db_for_write, args.default ) - if "default_expr" in args.model_fields_set and not args.not_null: + if ( + "default_expr" in args.model_fields_set + and not args.not_null + ): defaults[args.name] = _default_expression_sql( args.default_expr ) @@ -1363,9 +1368,9 @@ class TableAlterView(BaseView): "altered": altered, "schema": after_schema, "before_schema": before_schema, - "operations_applied": 0 - if alter_request.dry_run - else len(alter_request.operations), + "operations_applied": ( + 0 if alter_request.dry_run else len(alter_request.operations) + ), "dry_run": alter_request.dry_run, }, status=200, @@ -2068,6 +2073,15 @@ async def table_view_data( if redirect_response: return redirect_response + if context_for_html_hack: + await precompute_database_action_permissions( + datasette, request.actor, database_name + ) + if not is_view: + await precompute_table_action_permissions( + datasette, request.actor, database_name, table_name + ) + # Introspect columns and primary keys for table pks = await db.primary_keys(table_name) table_columns = await db.table_columns(table_name) diff --git a/datasette/views/table_extras.py b/datasette/views/table_extras.py index a0308e49..7cb4d8f0 100644 --- a/datasette/views/table_extras.py +++ b/datasette/views/table_extras.py @@ -367,23 +367,14 @@ class ActionsExtra(Extra): # that allowed() calls made inside the plugin hooks below # are served from the cache datasette = context.datasette - await datasette.allowed_many( - actions=[ - name - for name, action in datasette.actions.items() - if action.resource_class is TableResource - ], - resource=TableResource(context.database_name, context.table_name), - actor=context.request.actor, + await precompute_table_action_permissions( + datasette, + context.request.actor, + context.database_name, + context.table_name, ) - await datasette.allowed_many( - actions=[ - name - for name, action in datasette.actions.items() - if action.resource_class is DatabaseResource - ], - resource=DatabaseResource(context.database_name), - actor=context.request.actor, + await precompute_database_action_permissions( + datasette, context.request.actor, context.database_name ) for hook in method(**kwargs): extra_links = await await_me_maybe(hook) @@ -394,6 +385,32 @@ class ActionsExtra(Extra): return actions +async def precompute_table_action_permissions( + datasette, actor, database_name, table_name +): + await datasette.allowed_many( + actions=[ + name + for name, action in datasette.actions.items() + if action.resource_class is TableResource + ], + resource=TableResource(database_name, table_name), + actor=actor, + ) + + +async def precompute_database_action_permissions(datasette, actor, database_name): + await datasette.allowed_many( + actions=[ + name + for name, action in datasette.actions.items() + if action.resource_class is DatabaseResource + ], + resource=DatabaseResource(database_name), + actor=actor, + ) + + class IsViewExtra(Extra): description = "Whether this resource is a view instead of a table" example = ExtraExample("/fixtures/simple_view.json?_extra=is_view") diff --git a/docs/plugins.rst b/docs/plugins.rst index c2eb282a..d2b5c20a 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -280,6 +280,15 @@ If you run ``datasette plugins --all`` it will include default plugins that ship "query_actions" ] }, + { + "name": "datasette.default_table_actions", + "static": false, + "templates": false, + "version": null, + "hooks": [ + "table_actions" + ] + }, { "name": "datasette.events", "static": false, diff --git a/tests/test_playwright.py b/tests/test_playwright.py index b7990009..70b5a8c1 100644 --- a/tests/test_playwright.py +++ b/tests/test_playwright.py @@ -350,7 +350,9 @@ def test_alter_table_flow(page, datasette_server): assert first_more_options.inner_text() == "> Advanced options" first_more_options.click() assert first_more_options.inner_text() == "v Hide options" - expanded_options_text = dialog.locator(".table-alter-column-details").first.inner_text() + expanded_options_text = dialog.locator( + ".table-alter-column-details" + ).first.inner_text() assert dialog.locator(".table-alter-fields").evaluate( "node => node.scrollWidth <= node.clientWidth + 1" ) @@ -500,8 +502,7 @@ def test_alter_table_cancel_skips_discard_prompt(page, datasette_server): return dialog page.goto(f"{datasette_server}data/projects") - page.evaluate( - """ + page.evaluate(""" () => { window.__discardConfirmMessages = []; window.confirm = (message) => { @@ -509,8 +510,7 @@ def test_alter_table_cancel_skips_discard_prompt(page, datasette_server): return false; }; } - """ - ) + """) dialog = open_alter_dialog() dialog.locator(".table-alter-add-column").click()