From 7339cc51de96a55d6d0b9022ee0511c3a9640c42 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Tue, 12 Mar 2024 15:44:10 -0700 Subject: [PATCH] Rearrange plugin hooks page with more sections, closes #2300 --- docs/plugin_hooks.rst | 172 +++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 79 deletions(-) diff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst index d08ea57ab7..e43aa7841f 100644 --- a/docs/plugin_hooks.rst +++ b/docs/plugin_hooks.rst @@ -92,10 +92,17 @@ This function can return an awaitable function if it needs to run any async code Examples: `datasette-edit-templates `_ +.. _plugin_page_extras: + +Page extras +----------- + +These plugin hooks can be used to affect the way HTML pages for different Datasette interfaces are rendered. + .. _plugin_hook_extra_template_vars: extra_template_vars(template, database, table, columns, view_name, request, datasette) --------------------------------------------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Extra template variables that should be made available in the rendered template context. @@ -184,7 +191,7 @@ Examples: `datasette-search-all ` @@ -238,7 +245,7 @@ Examples: `datasette-cluster-map ` @@ -288,7 +295,7 @@ Examples: `datasette-cluster-map `` block at the end of the ```` element on the page. @@ -1430,6 +1437,76 @@ This example logs an error to `Sentry `__ and then renders a Example: `datasette-sentry `_ +.. _plugin_hook_skip_csrf: + +skip_csrf(datasette, scope) +--------------------------- + +``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. + +``scope`` - dictionary + The `ASGI scope `__ for the incoming HTTP request. + +This hook can be used to skip :ref:`internals_csrf` for a specific incoming request. For example, you might have a custom path at ``/submit-comment`` which is designed to accept comments from anywhere, whether or not the incoming request originated on the site and has an accompanying CSRF token. + +This example will disable CSRF protection for that specific URL path: + +.. code-block:: python + + from datasette import hookimpl + + + @hookimpl + def skip_csrf(scope): + return scope["path"] == "/submit-comment" + +If any of the currently active ``skip_csrf()`` plugin hooks return ``True``, CSRF protection will be skipped for the request. + +.. _plugin_hook_get_metadata: + +get_metadata(datasette, key, database, table) +--------------------------------------------- + +``datasette`` - :ref:`internals_datasette` + You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``. + +``actor`` - dictionary or None + The currently authenticated :ref:`actor `. + +``database`` - string or None + The name of the database metadata is being asked for. + +``table`` - string or None + The name of the table. + +``key`` - string or None + The name of the key for which data is being asked for. + +This hook is responsible for returning a dictionary corresponding to Datasette :ref:`metadata`. This function is passed the ``database``, ``table`` and ``key`` which were passed to the upstream internal request for metadata. Regardless, it is important to return a global metadata object, where ``"databases": []`` would be a top-level key. The dictionary returned here, will be merged with, and overwritten by, the contents of the physical ``metadata.yaml`` if one is present. + +.. warning:: + The design of this plugin hook does not currently provide a mechanism for interacting with async code, and may change in the future. See `issue 1384 `__. + +.. code-block:: python + + @hookimpl + def get_metadata(datasette, key, database, table): + metadata = { + "title": "This will be the Datasette landing page title!", + "description": get_instance_description(datasette), + "databases": [], + } + for db_name, db_data_dict in get_my_database_meta( + datasette, database, table, key + ): + metadata["databases"][db_name] = db_data_dict + # whatever we return here will be merged with any other plugins using this hook and + # will be overwritten by a local metadata.yaml if one exists! + return metadata + +Example: `datasette-remote-metadata plugin `__ + .. _plugin_hook_menu_links: menu_links(datasette, actor, request) @@ -1473,10 +1550,17 @@ Using :ref:`internals_datasette_urls` here ensures that links in the menu will t Examples: `datasette-search-all `_, `datasette-graphql `_ +.. _plugin_actions: + +Action hooks +------------ + +Action hooks can be used to add items to the action menus that appear at the top of different pages within Datasette. Unlike :ref:`menu_links() `, actions which are displayed on every page, actions should only be relevant to the page the user is currently viewing. + .. _plugin_hook_table_actions: table_actions(datasette, actor, database, table, 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. @@ -1524,7 +1608,7 @@ Example: `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. @@ -1546,7 +1630,7 @@ Like :ref:`plugin_hook_table_actions` but for SQL views. .. _plugin_hook_query_actions: query_actions(datasette, actor, database, query_name, request, sql, params) ---------------------------------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``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. @@ -1603,7 +1687,7 @@ Example: `datasette-create-view `_, .. _plugin_hook_homepage_actions: homepage_actions(datasette, actor, 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. @@ -1686,76 +1770,6 @@ This example adds a link an imagined tool for editing the homepage, only for sig } ] -.. _plugin_hook_skip_csrf: - -skip_csrf(datasette, scope) ---------------------------- - -``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. - -``scope`` - dictionary - The `ASGI scope `__ for the incoming HTTP request. - -This hook can be used to skip :ref:`internals_csrf` for a specific incoming request. For example, you might have a custom path at ``/submit-comment`` which is designed to accept comments from anywhere, whether or not the incoming request originated on the site and has an accompanying CSRF token. - -This example will disable CSRF protection for that specific URL path: - -.. code-block:: python - - from datasette import hookimpl - - - @hookimpl - def skip_csrf(scope): - return scope["path"] == "/submit-comment" - -If any of the currently active ``skip_csrf()`` plugin hooks return ``True``, CSRF protection will be skipped for the request. - -.. _plugin_hook_get_metadata: - -get_metadata(datasette, key, database, table) ---------------------------------------------- - -``datasette`` - :ref:`internals_datasette` - You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``. - -``actor`` - dictionary or None - The currently authenticated :ref:`actor `. - -``database`` - string or None - The name of the database metadata is being asked for. - -``table`` - string or None - The name of the table. - -``key`` - string or None - The name of the key for which data is being asked for. - -This hook is responsible for returning a dictionary corresponding to Datasette :ref:`metadata`. This function is passed the ``database``, ``table`` and ``key`` which were passed to the upstream internal request for metadata. Regardless, it is important to return a global metadata object, where ``"databases": []`` would be a top-level key. The dictionary returned here, will be merged with, and overwritten by, the contents of the physical ``metadata.yaml`` if one is present. - -.. warning:: - The design of this plugin hook does not currently provide a mechanism for interacting with async code, and may change in the future. See `issue 1384 `__. - -.. code-block:: python - - @hookimpl - def get_metadata(datasette, key, database, table): - metadata = { - "title": "This will be the Datasette landing page title!", - "description": get_instance_description(datasette), - "databases": [], - } - for db_name, db_data_dict in get_my_database_meta( - datasette, database, table, key - ): - metadata["databases"][db_name] = db_data_dict - # whatever we return here will be merged with any other plugins using this hook and - # will be overwritten by a local metadata.yaml if one exists! - return metadata - -Example: `datasette-remote-metadata plugin `__ - .. _plugin_hook_slots: Template slots