kopia lustrzana https://github.com/simonw/datasette
view_actions plugin hook, closes #2297
rodzic
daf5ca02ca
commit
909c85cd2b
|
@ -145,6 +145,11 @@ def table_actions(datasette, actor, database, table, request):
|
|||
"""Links for the table actions menu"""
|
||||
|
||||
|
||||
@hookspec
|
||||
def view_actions(datasette, actor, database, view, request):
|
||||
"""Links for the view actions menu"""
|
||||
|
||||
|
||||
@hookspec
|
||||
def query_actions(datasette, actor, database, query_name, request, sql, params):
|
||||
"""Links for the query and canned query actions menu"""
|
||||
|
|
|
@ -24,17 +24,17 @@
|
|||
<div class="page-header" style="border-color: #{{ database_color }}">
|
||||
<h1>{{ metadata.get("title") or table }}{% if is_view %} (view){% endif %}{% if private %} 🔒{% endif %}</h1>
|
||||
</div>
|
||||
{% set links = table_actions() %}{% if links %}
|
||||
{% set links = actions() %}{% if links %}
|
||||
<div class="page-action-menu">
|
||||
<details class="actions-menu-links details-menu">
|
||||
<summary>
|
||||
<div class="icon-text">
|
||||
<svg class="icon" aria-labelledby="actions-menu-links-title" role="img" style="color: #fff" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<title id="actions-menu-links-title">Table actions</title>
|
||||
<title id="actions-menu-links-title">{% if is_view %}View{% else %}Table{% endif %} actions</title>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||||
</svg>
|
||||
<span>Table actions</span>
|
||||
<span>{% if is_view %}View{% else %}Table{% endif %} actions</span>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="dropdown-menu">
|
||||
|
|
|
@ -1401,22 +1401,28 @@ async def table_view_data(
|
|||
"Primary keys for this table"
|
||||
return pks
|
||||
|
||||
async def extra_table_actions():
|
||||
async def table_actions():
|
||||
async def extra_actions():
|
||||
async def actions():
|
||||
links = []
|
||||
for hook in pm.hook.table_actions(
|
||||
datasette=datasette,
|
||||
table=table_name,
|
||||
database=database_name,
|
||||
actor=request.actor,
|
||||
request=request,
|
||||
):
|
||||
kwargs = {
|
||||
"datasette": datasette,
|
||||
"database": database_name,
|
||||
"actor": request.actor,
|
||||
"request": request,
|
||||
}
|
||||
if is_view:
|
||||
kwargs["view"] = table_name
|
||||
method = pm.hook.view_actions
|
||||
else:
|
||||
kwargs["table"] = table_name
|
||||
method = pm.hook.table_actions
|
||||
for hook in method(**kwargs):
|
||||
extra_links = await await_me_maybe(hook)
|
||||
if extra_links:
|
||||
links.extend(extra_links)
|
||||
return links
|
||||
|
||||
return table_actions
|
||||
return actions
|
||||
|
||||
async def extra_is_view():
|
||||
return is_view
|
||||
|
@ -1606,7 +1612,7 @@ async def table_view_data(
|
|||
"database",
|
||||
"table",
|
||||
"database_color",
|
||||
"table_actions",
|
||||
"actions",
|
||||
"filters",
|
||||
"renderers",
|
||||
"custom_table_templates",
|
||||
|
@ -1647,7 +1653,7 @@ async def table_view_data(
|
|||
extra_database,
|
||||
extra_table,
|
||||
extra_database_color,
|
||||
extra_table_actions,
|
||||
extra_actions,
|
||||
extra_filters,
|
||||
extra_renderers,
|
||||
extra_custom_table_templates,
|
||||
|
|
|
@ -1521,6 +1521,28 @@ This example adds a new table action if the signed in user is ``"root"``:
|
|||
|
||||
Example: `datasette-graphql <https://datasette.io/plugins/datasette-graphql>`_
|
||||
|
||||
.. _plugin_hook_view_actions:
|
||||
|
||||
view_actions(datasette, actor, database, view, request)
|
||||
-------------------------------------------------------
|
||||
|
||||
``datasette`` - :ref:`internals_datasette`
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
|
||||
|
||||
``actor`` - dictionary or None
|
||||
The currently authenticated :ref:`actor <authentication_actor>`.
|
||||
|
||||
``database`` - string
|
||||
The name of the database.
|
||||
|
||||
``view`` - string
|
||||
The name of the SQL view.
|
||||
|
||||
``request`` - :ref:`internals_request` or None
|
||||
The current HTTP request. This can be ``None`` if the request object is not available.
|
||||
|
||||
Like :ref:`plugin_hook_table_actions` but for SQL views.
|
||||
|
||||
.. _plugin_hook_query_actions:
|
||||
|
||||
query_actions(datasette, actor, database, query_name, request, sql, params)
|
||||
|
@ -1657,7 +1679,9 @@ This example adds a link an imagined tool for editing the homepage, only for sig
|
|||
if actor:
|
||||
return [
|
||||
{
|
||||
"href": datasette.urls.path("/-/customize-homepage"),
|
||||
"href": datasette.urls.path(
|
||||
"/-/customize-homepage"
|
||||
),
|
||||
"label": "Customize homepage",
|
||||
}
|
||||
]
|
||||
|
|
|
@ -56,6 +56,7 @@ EXPECTED_PLUGINS = [
|
|||
"skip_csrf",
|
||||
"startup",
|
||||
"table_actions",
|
||||
"view_actions",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -391,6 +391,18 @@ def table_actions(datasette, database, table, actor):
|
|||
]
|
||||
|
||||
|
||||
@hookimpl
|
||||
def view_actions(datasette, database, view, actor):
|
||||
if actor:
|
||||
return [
|
||||
{
|
||||
"href": datasette.urls.instance(),
|
||||
"label": f"Database: {database}",
|
||||
},
|
||||
{"href": datasette.urls.instance(), "label": f"View: {view}"},
|
||||
]
|
||||
|
||||
|
||||
@hookimpl
|
||||
def query_actions(datasette, database, query_name, sql):
|
||||
# Don't explain an explain
|
||||
|
|
|
@ -923,18 +923,34 @@ async def test_hook_menu_links(ds_client):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("table_or_view", ["facetable", "simple_view"])
|
||||
async def test_hook_table_actions(ds_client, table_or_view):
|
||||
response = await ds_client.get(f"/fixtures/{table_or_view}")
|
||||
async def test_hook_table_actions(ds_client):
|
||||
response = await ds_client.get("/fixtures/facetable")
|
||||
assert get_actions_links(response.text) == []
|
||||
|
||||
response_2 = await ds_client.get(f"/fixtures/{table_or_view}?_bot=1&_hello=BOB")
|
||||
response_2 = await ds_client.get("/fixtures/facetable?_bot=1&_hello=BOB")
|
||||
assert sorted(
|
||||
get_actions_links(response_2.text), key=lambda link: link["label"]
|
||||
) == [
|
||||
{"label": "Database: fixtures", "href": "/", "description": None},
|
||||
{"label": "From async BOB", "href": "/", "description": None},
|
||||
{"label": f"Table: {table_or_view}", "href": "/", "description": None},
|
||||
{"label": "Table: facetable", "href": "/", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hook_view_actions(ds_client):
|
||||
response = await ds_client.get("/fixtures/simple_view")
|
||||
assert get_actions_links(response.text) == []
|
||||
|
||||
response_2 = await ds_client.get(
|
||||
"/fixtures/simple_view",
|
||||
cookies={"ds_actor": ds_client.actor_cookie({"id": "bob"})},
|
||||
)
|
||||
assert sorted(
|
||||
get_actions_links(response_2.text), key=lambda link: link["label"]
|
||||
) == [
|
||||
{"label": "Database: fixtures", "href": "/", "description": None},
|
||||
{"label": "View: simple_view", "href": "/", "description": None},
|
||||
]
|
||||
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue