Make request available to menu plugin hooks, closes #1371

This commit is contained in:
Simon Willison 2021-06-09 21:45:24 -07:00
commit d23a267138
8 changed files with 44 additions and 23 deletions

View file

@ -833,7 +833,9 @@ class Datasette:
async def menu_links(): async def menu_links():
links = [] links = []
for hook in pm.hook.menu_links( for hook in pm.hook.menu_links(
datasette=self, actor=request.actor if request else None datasette=self,
actor=request.actor if request else None,
request=request or None,
): ):
extra_links = await await_me_maybe(hook) extra_links = await await_me_maybe(hook)
if extra_links: if extra_links:

View file

@ -100,15 +100,15 @@ def forbidden(datasette, request, message):
@hookspec @hookspec
def menu_links(datasette, actor): def menu_links(datasette, actor, request):
"""Links for the navigation menu""" """Links for the navigation menu"""
@hookspec @hookspec
def table_actions(datasette, actor, database, table): def table_actions(datasette, actor, database, table, request):
"""Links for the table actions menu""" """Links for the table actions menu"""
@hookspec @hookspec
def database_actions(datasette, actor, database): def database_actions(datasette, actor, database, request):
"""Links for the database actions menu""" """Links for the database actions menu"""

View file

@ -110,6 +110,7 @@ class DatabaseView(DataView):
datasette=self.ds, datasette=self.ds,
database=database, database=database,
actor=request.actor, actor=request.actor,
request=request,
): ):
extra_links = await await_me_maybe(hook) extra_links = await await_me_maybe(hook)
if extra_links: if extra_links:

View file

@ -894,6 +894,7 @@ class TableView(RowTableShared):
table=table, table=table,
database=database, database=database,
actor=request.actor, actor=request.actor,
request=request,
): ):
extra_links = await await_me_maybe(hook) extra_links = await await_me_maybe(hook)
if extra_links: if extra_links:

View file

@ -1015,8 +1015,8 @@ The function can alternatively return an awaitable function if it needs to make
.. _plugin_hook_menu_links: .. _plugin_hook_menu_links:
menu_links(datasette, actor) menu_links(datasette, actor, request)
---------------------------- -------------------------------------
``datasette`` - :ref:`internals_datasette` ``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. You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
@ -1024,6 +1024,9 @@ menu_links(datasette, actor)
``actor`` - dictionary or None ``actor`` - dictionary or None
The currently authenticated :ref:`actor <authentication_actor>`. The currently authenticated :ref:`actor <authentication_actor>`.
``request`` - object or None
The current HTTP :ref:`internals_request`. This can be ``None`` if the request object is not available.
This hook allows additional items to be included in the menu displayed by Datasette's top right menu icon. This hook allows additional items to be included in the menu displayed by Datasette's top right menu icon.
The hook should return a list of ``{"href": "...", "label": "..."}`` menu items. These will be added to the menu. The hook should return a list of ``{"href": "...", "label": "..."}`` menu items. These will be added to the menu.
@ -1045,11 +1048,10 @@ This example adds a new menu item but only if the signed in user is ``"root"``:
Using :ref:`internals_datasette_urls` here ensures that links in the menu will take the :ref:`setting_base_url` setting into account. Using :ref:`internals_datasette_urls` here ensures that links in the menu will take the :ref:`setting_base_url` setting into account.
.. _plugin_hook_table_actions: .. _plugin_hook_table_actions:
table_actions(datasette, actor, database, table) table_actions(datasette, actor, database, table, request)
------------------------------------------------ ---------------------------------------------------------
``datasette`` - :ref:`internals_datasette` ``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. You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
@ -1063,6 +1065,9 @@ table_actions(datasette, actor, database, table)
``table`` - string ``table`` - string
The name of the table. The name of the table.
``request`` - object
The current HTTP :ref:`internals_request`. This can be ``None`` if the request object is not available.
This hook allows table actions to be displayed in a menu accessed via an action icon at the top of the table page. It should return a list of ``{"href": "...", "label": "..."}`` menu items. This hook allows table actions to be displayed in a menu accessed via an action icon at the top of the table page. It should return a list of ``{"href": "...", "label": "..."}`` menu items.
It can alternatively return an ``async def`` awaitable function which returns a list of menu items. It can alternatively return an ``async def`` awaitable function which returns a list of menu items.
@ -1083,8 +1088,8 @@ This example adds a new table action if the signed in user is ``"root"``:
.. _plugin_hook_database_actions: .. _plugin_hook_database_actions:
database_actions(datasette, actor, database) database_actions(datasette, actor, database, request)
-------------------------------------------- -----------------------------------------------------
``datasette`` - :ref:`internals_datasette` ``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. You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
@ -1095,4 +1100,7 @@ database_actions(datasette, actor, database)
``database`` - string ``database`` - string
The name of the database. The name of the database.
``request`` - object
The current HTTP :ref:`internals_request`.
This hook is similar to :ref:`plugin_hook_table_actions` but populates an actions menu on the database page. This hook is similar to :ref:`plugin_hook_table_actions` but populates an actions menu on the database page.

View file

@ -316,9 +316,12 @@ def forbidden(datasette, request, message):
@hookimpl @hookimpl
def menu_links(datasette, actor): def menu_links(datasette, actor, request):
if actor: if actor:
return [{"href": datasette.urls.instance(), "label": "Hello"}] label = "Hello"
if request.args.get("_hello"):
label += ", " + request.args["_hello"]
return [{"href": datasette.urls.instance(), "label": label}]
@hookimpl @hookimpl
@ -334,11 +337,14 @@ def table_actions(datasette, database, table, actor):
@hookimpl @hookimpl
def database_actions(datasette, database, actor): def database_actions(datasette, database, actor, request):
if actor: if actor:
label = f"Database: {database}"
if request.args.get("_hello"):
label += " - " + request.args["_hello"]
return [ return [
{ {
"href": datasette.urls.instance(), "href": datasette.urls.instance(),
"label": f"Database: {database}", "label": label,
} }
] ]

View file

@ -158,9 +158,12 @@ def menu_links(datasette, actor):
@hookimpl @hookimpl
def table_actions(datasette, database, table, actor): def table_actions(datasette, database, table, actor, request):
async def inner(): async def inner():
if actor: if actor:
return [{"href": datasette.urls.instance(), "label": "From async"}] label = "From async"
if request.args.get("_hello"):
label += " " + request.args["_hello"]
return [{"href": datasette.urls.instance(), "label": label}]
return inner return inner

View file

@ -781,9 +781,9 @@ def test_hook_menu_links(app_client):
response = app_client.get("/") response = app_client.get("/")
assert get_menu_links(response.text) == [] assert get_menu_links(response.text) == []
response_2 = app_client.get("/?_bot=1") response_2 = app_client.get("/?_bot=1&_hello=BOB")
assert get_menu_links(response_2.text) == [ assert get_menu_links(response_2.text) == [
{"label": "Hello", "href": "/"}, {"label": "Hello, BOB", "href": "/"},
{"label": "Hello 2", "href": "/"}, {"label": "Hello 2", "href": "/"},
] ]
@ -800,12 +800,12 @@ def test_hook_table_actions(app_client, table_or_view):
response = app_client.get(f"/fixtures/{table_or_view}") response = app_client.get(f"/fixtures/{table_or_view}")
assert get_table_actions_links(response.text) == [] assert get_table_actions_links(response.text) == []
response_2 = app_client.get(f"/fixtures/{table_or_view}?_bot=1") response_2 = app_client.get(f"/fixtures/{table_or_view}?_bot=1&_hello=BOB")
assert sorted( assert sorted(
get_table_actions_links(response_2.text), key=lambda l: l["label"] get_table_actions_links(response_2.text), key=lambda l: l["label"]
) == [ ) == [
{"label": "Database: fixtures", "href": "/"}, {"label": "Database: fixtures", "href": "/"},
{"label": "From async", "href": "/"}, {"label": "From async BOB", "href": "/"},
{"label": f"Table: {table_or_view}", "href": "/"}, {"label": f"Table: {table_or_view}", "href": "/"},
] ]
@ -821,7 +821,7 @@ def test_hook_database_actions(app_client):
response = app_client.get("/fixtures") response = app_client.get("/fixtures")
assert get_table_actions_links(response.text) == [] assert get_table_actions_links(response.text) == []
response_2 = app_client.get("/fixtures?_bot=1") response_2 = app_client.get("/fixtures?_bot=1&_hello=BOB")
assert get_table_actions_links(response_2.text) == [ assert get_table_actions_links(response_2.text) == [
{"label": "Database: fixtures", "href": "/"}, {"label": "Database: fixtures - BOB", "href": "/"},
] ]