diff --git a/datasette/app.py b/datasette/app.py index cfce8e0b..cee5ae21 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -46,6 +46,7 @@ from .database import Database, QueryInterrupted from .utils import ( PrefixedUrlString, + PrependingLoader, StartupError, async_call_with_supported_arguments, await_me_maybe, @@ -309,7 +310,9 @@ class Datasette: ] ) self.jinja_env = Environment( - loader=template_loader, autoescape=True, enable_async=True + loader=PrependingLoader(template_loader, "_macros.html"), + autoescape=True, + enable_async=True, ) self.jinja_env.filters["escape_css_string"] = escape_css_string self.jinja_env.filters["quote_plus"] = lambda u: urllib.parse.quote_plus(u) @@ -759,6 +762,14 @@ class Datasette: renderer.get("can_render") or (lambda: True), ) + def _include_templates(self, name, **kwargs): + include_templates = [] + for templates in getattr(pm.hook, name)(**kwargs): + if isinstance(templates, str): + templates = [templates] + include_templates.extend(templates) + return include_templates + async def render_template( self, templates, context=None, request=None, view_name=None ): diff --git a/datasette/hookspecs.py b/datasette/hookspecs.py index 240bf80f..13a10680 100644 --- a/datasette/hookspecs.py +++ b/datasette/hookspecs.py @@ -112,53 +112,3 @@ def table_actions(datasette, actor, database, table): @hookspec def database_actions(datasette, actor, database): """Links for the database actions menu""" - - -@hookspec -def include_table_top(datasette, database, actor, table): - """Templates to include at the top of the table page""" - - -@hookspec -def include_table_bottom(datasette, database, actor, table): - """Templates to include at the bottom of the table page""" - - -@hookspec -def include_row_top(datasette, database, actor, table): - """Templates to include at the top of the row page""" - - -@hookspec -def include_row_bottom(datasette, database, actor, table): - """Templates to include at the bottom of the row page""" - - -@hookspec -def include_database_top(datasette, database, actor): - """Templates to include at the top of the database page""" - - -@hookspec -def include_database_bottom(datasette, database, actor): - """Templates to include at the bottom of the database page""" - - -@hookspec -def include_query_top(datasette, database, actor): - """Templates to include at the top of the query page""" - - -@hookspec -def include_query_bottom(datasette, database, actor): - """Templates to include at the bottom of the query page""" - - -@hookspec -def include_index_top(datasette, actor): - """Templates to include at the top of the index page""" - - -@hookspec -def include_index_bottom(datasette, actor): - """Templates to include at the bottom of the index page""" diff --git a/datasette/templates/table.html b/datasette/templates/table.html index 077332dc..88cfef22 100644 --- a/datasette/templates/table.html +++ b/datasette/templates/table.html @@ -51,6 +51,8 @@ {% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} +{{ includes("table-top-includes", table_top_includes) }} + {% if filtered_table_rows_count or human_description_en %}
{{ table_definition }}
{% endif %}
diff --git a/datasette/utils/__init__.py b/datasette/utils/__init__.py
index 47ca0551..70f25002 100644
--- a/datasette/utils/__init__.py
+++ b/datasette/utils/__init__.py
@@ -18,6 +18,7 @@ import shutil
import urllib
import numbers
import yaml
+from jinja2 import BaseLoader
from .shutil_backport import copytree
from .sqlite import sqlite3, sqlite_version, supports_table_xinfo
from ..plugins import pm
@@ -1068,3 +1069,26 @@ class PrefixedUrlString(str):
class StartupError(Exception):
pass
+
+
+class PrependingLoader(BaseLoader):
+ # Based on http://codyaray.com/2015/05/auto-load-jinja2-macros
+ def __init__(self, delegate, prepend_template):
+ self.delegate = delegate
+ self.prepend_template = prepend_template
+
+ def get_source(self, environment, template):
+ prepend_source, _, prepend_uptodate = self.delegate.get_source(
+ environment, self.prepend_template
+ )
+ main_source, main_filename, main_uptodate = self.delegate.get_source(
+ environment, template
+ )
+ uptodate = lambda: prepend_uptodate() and main_uptodate()
+ return prepend_source + main_source, main_filename, uptodate
+
+ def list_templates(self):
+ return self.delegate.list_templates()
+
+ def select_template(self, *args, **kwargs):
+ return self.delegate.select_template(*args, **kwargs)
diff --git a/datasette/views/table.py b/datasette/views/table.py
index cc8ef9f1..a921e9f7 100644
--- a/datasette/views/table.py
+++ b/datasette/views/table.py
@@ -880,6 +880,20 @@ class TableView(RowTableShared):
"metadata": metadata,
"view_definition": await db.get_view_definition(table),
"table_definition": await db.get_table_definition(table),
+ "table_top_includes": self.ds._include_templates(
+ "include_table_top",
+ database=database,
+ table=table,
+ actor=request.actor,
+ datasette=self.ds,
+ ),
+ "table_bottom_includes": self.ds._include_templates(
+ "include_table_bottom",
+ database=database,
+ table=table,
+ actor=request.actor,
+ datasette=self.ds,
+ ),
}
return (