mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
extra_css_urls(template, database, table, datasette)
The extra_css_urls and extra_js_urls hooks now take additional optional parameters. Also refactored them out of the Datasette class and into RenderMixin. Plus improved plugin documentation to explicitly list parameters.
This commit is contained in:
parent
fbf446965b
commit
b7c6a9f9bd
7 changed files with 124 additions and 52 deletions
|
|
@ -2,7 +2,6 @@ import asyncio
|
|||
import click
|
||||
import collections
|
||||
import hashlib
|
||||
import itertools
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
|
|
@ -258,33 +257,6 @@ class Datasette:
|
|||
def get_view_definition(self, database_name, view):
|
||||
return self.get_table_definition(database_name, view, 'view')
|
||||
|
||||
def asset_urls(self, key):
|
||||
# Flatten list-of-lists from plugins:
|
||||
seen_urls = set()
|
||||
for url_or_dict in itertools.chain(
|
||||
itertools.chain.from_iterable(getattr(pm.hook, key)()),
|
||||
(self.metadata(key) or [])
|
||||
):
|
||||
if isinstance(url_or_dict, dict):
|
||||
url = url_or_dict["url"]
|
||||
sri = url_or_dict.get("sri")
|
||||
else:
|
||||
url = url_or_dict
|
||||
sri = None
|
||||
if url in seen_urls:
|
||||
continue
|
||||
seen_urls.add(url)
|
||||
if sri:
|
||||
yield {"url": url, "sri": sri}
|
||||
else:
|
||||
yield {"url": url}
|
||||
|
||||
def extra_css_urls(self):
|
||||
return self.asset_urls("extra_css_urls")
|
||||
|
||||
def extra_js_urls(self):
|
||||
return self.asset_urls("extra_js_urls")
|
||||
|
||||
def update_with_inherited_metadata(self, metadata):
|
||||
# Fills in source/license with defaults, if available
|
||||
metadata.update(
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ def prepare_jinja2_environment(env):
|
|||
|
||||
|
||||
@hookspec
|
||||
def extra_css_urls():
|
||||
def extra_css_urls(template, database, table, datasette):
|
||||
"Extra CSS URLs added by this plugin"
|
||||
|
||||
|
||||
@hookspec
|
||||
def extra_js_urls():
|
||||
def extra_js_urls(template, database, table, datasette):
|
||||
"Extra JavaScript URLs added by this plugin"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import asyncio
|
||||
import csv
|
||||
import itertools
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
|
|
@ -46,6 +47,32 @@ class DatasetteError(Exception):
|
|||
|
||||
class RenderMixin(HTTPMethodView):
|
||||
|
||||
def _asset_urls(self, key, template, context):
|
||||
# Flatten list-of-lists from plugins:
|
||||
seen_urls = set()
|
||||
for url_or_dict in itertools.chain(
|
||||
itertools.chain.from_iterable(getattr(pm.hook, key)(
|
||||
template=template.name,
|
||||
database=context.get("database"),
|
||||
table=context.get("table"),
|
||||
datasette=self.ds
|
||||
)),
|
||||
(self.ds.metadata(key) or [])
|
||||
):
|
||||
if isinstance(url_or_dict, dict):
|
||||
url = url_or_dict["url"]
|
||||
sri = url_or_dict.get("sri")
|
||||
else:
|
||||
url = url_or_dict
|
||||
sri = None
|
||||
if url in seen_urls:
|
||||
continue
|
||||
seen_urls.add(url)
|
||||
if sri:
|
||||
yield {"url": url, "sri": sri}
|
||||
else:
|
||||
yield {"url": url}
|
||||
|
||||
def render(self, templates, **context):
|
||||
template = self.ds.jinja_env.select_template(templates)
|
||||
select_templates = [
|
||||
|
|
@ -69,6 +96,12 @@ class RenderMixin(HTTPMethodView):
|
|||
"select_templates": select_templates,
|
||||
"zip": zip,
|
||||
"body_scripts": body_scripts,
|
||||
"extra_css_urls": self._asset_urls(
|
||||
"extra_css_urls", template, context
|
||||
),
|
||||
"extra_js_urls": self._asset_urls(
|
||||
"extra_js_urls", template, context
|
||||
),
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -432,8 +465,6 @@ class BaseView(RenderMixin):
|
|||
"url_csv": url_csv,
|
||||
"url_csv_path": url_csv_path,
|
||||
"url_csv_args": url_csv_args,
|
||||
"extra_css_urls": self.ds.extra_css_urls(),
|
||||
"extra_js_urls": self.ds.extra_js_urls(),
|
||||
"datasette_version": __version__,
|
||||
"config": self.ds.config_dict(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,4 @@ class IndexView(RenderMixin):
|
|||
databases=databases,
|
||||
metadata=self.ds.metadata(),
|
||||
datasette_version=__version__,
|
||||
extra_css_urls=self.ds.extra_css_urls(),
|
||||
extra_js_urls=self.ds.extra_js_urls(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -231,6 +231,9 @@ The full list of available plugin hooks is as follows.
|
|||
prepare_connection(conn)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``conn`` - sqlite3 connection object
|
||||
The connection that is being opened
|
||||
|
||||
This hook is called when a new SQLite database connection is created. You can
|
||||
use it to `register custom SQL functions <https://docs.python.org/2/library/sqlite3.html#sqlite3.Connection.create_function>`_,
|
||||
aggregates and collations. For example:
|
||||
|
|
@ -252,6 +255,9 @@ arguments and can be called like this::
|
|||
prepare_jinja2_environment(env)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``env`` - jinja2 Environment
|
||||
The template environment that is being prepared
|
||||
|
||||
This hook is called with the Jinja2 environment that is used to evaluate
|
||||
Datasette HTML templates. You can use it to do things like `register custom
|
||||
template filters <http://jinja.pocoo.org/docs/2.10/api/#custom-filters>`_, for
|
||||
|
|
@ -269,10 +275,22 @@ You can now use this filter in your custom templates like so::
|
|||
|
||||
Table name: {{ table|uppercase }}
|
||||
|
||||
extra_css_urls()
|
||||
~~~~~~~~~~~~~~~~
|
||||
extra_css_urls(template, database, table, datasette)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Return a list of extra CSS URLs that should be included on every page. These can
|
||||
``template`` - string
|
||||
The template that is being rendered, e.g. ``database.html``
|
||||
|
||||
``database`` - string or None
|
||||
The name of the database
|
||||
|
||||
``table`` - string or None
|
||||
The name of the table
|
||||
|
||||
``datasette`` - Datasette instance
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``
|
||||
|
||||
Return a list of extra CSS URLs that should be included on the page. These can
|
||||
take advantage of the CSS class hooks described in :ref:`customization`.
|
||||
|
||||
This can be a list of URLs:
|
||||
|
|
@ -301,8 +319,10 @@ Or a list of dictionaries defining both a URL and an
|
|||
'sri': 'sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4',
|
||||
}]
|
||||
|
||||
extra_js_urls()
|
||||
~~~~~~~~~~~~~~~
|
||||
extra_js_urls(template, database, table, datasette)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Same arguments as ``extra_css_urls``.
|
||||
|
||||
This works in the same way as ``extra_css_urls()`` but for JavaScript. You can
|
||||
return either a list of URLs or a list of dictionaries:
|
||||
|
|
@ -334,6 +354,9 @@ you have one:
|
|||
publish_subcommand(publish)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``publish`` - Click publish command group
|
||||
The Click command group for the ``datasette publish`` subcommand
|
||||
|
||||
This hook allows you to create new providers for the ``datasette publish``
|
||||
command. Datasette uses this hook internally to implement the default ``now``
|
||||
and ``heroku`` subcommands, so you can read
|
||||
|
|
@ -351,8 +374,8 @@ Lets you customize the display of values within table cells in the HTML table vi
|
|||
``column`` - string
|
||||
The name of the column being rendered
|
||||
|
||||
``table`` - string
|
||||
The name of the table
|
||||
``table`` - string or None
|
||||
The name of the table - or ``None`` if this is a custom SQL query
|
||||
|
||||
``database`` - string
|
||||
The name of the database
|
||||
|
|
@ -411,6 +434,18 @@ If the value matches that pattern, the plugin returns an HTML link element:
|
|||
extra_body_script(template, database, table, datasette)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``template`` - string
|
||||
The template that is being rendered, e.g. ``database.html``
|
||||
|
||||
``database`` - string or None
|
||||
The name of the database, or ``None`` if the page does not correspond to a database (e.g. the root page)
|
||||
|
||||
``table`` - string or None
|
||||
The name of the table, or ``None`` if the page does not correct to a table
|
||||
|
||||
``datasette`` - Datasette instance
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``
|
||||
|
||||
Extra JavaScript to be added to a ``<script>`` block at the end of the ``<body>`` element on the page.
|
||||
|
||||
The ``template``, ``database`` and ``table`` options can be used to return different code depending on which template is being rendered and which database or table are being processed.
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ METADATA = {
|
|||
|
||||
PLUGIN1 = '''
|
||||
from datasette import hookimpl
|
||||
import base64
|
||||
import pint
|
||||
import json
|
||||
|
||||
|
|
@ -219,8 +220,14 @@ def prepare_connection(conn):
|
|||
|
||||
|
||||
@hookimpl
|
||||
def extra_css_urls():
|
||||
return ['https://example.com/app.css']
|
||||
def extra_css_urls(template, database, table, datasette):
|
||||
return ['https://example.com/{}/extra-css-urls-demo.css'.format(
|
||||
base64.b64encode(json.dumps({
|
||||
"template": template,
|
||||
"database": database,
|
||||
"table": table,
|
||||
}).encode("utf8")).decode("utf8")
|
||||
)]
|
||||
|
||||
|
||||
@hookimpl
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from bs4 import BeautifulSoup as Soup
|
|||
from .fixtures import ( # noqa
|
||||
app_client,
|
||||
)
|
||||
import base64
|
||||
import json
|
||||
import re
|
||||
import pytest
|
||||
|
|
@ -15,16 +16,44 @@ def test_plugins_dir_plugin(app_client):
|
|||
assert pytest.approx(328.0839) == response.json['rows'][0][0]
|
||||
|
||||
|
||||
def test_plugin_extra_css_urls(app_client):
|
||||
response = app_client.get('/')
|
||||
@pytest.mark.parametrize(
|
||||
"path,expected_decoded_object",
|
||||
[
|
||||
(
|
||||
"/",
|
||||
{
|
||||
"template": "index.html",
|
||||
"database": None,
|
||||
"table": None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"/fixtures/",
|
||||
{
|
||||
"template": "database.html",
|
||||
"database": "fixtures",
|
||||
"table": None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"/fixtures/sortable",
|
||||
{
|
||||
"template": "table.html",
|
||||
"database": "fixtures",
|
||||
"table": "sortable",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_plugin_extra_css_urls(app_client, path, expected_decoded_object):
|
||||
response = app_client.get(path)
|
||||
links = Soup(response.body, 'html.parser').findAll('link')
|
||||
assert [
|
||||
l for l in links
|
||||
if l.attrs == {
|
||||
'rel': ['stylesheet'],
|
||||
'href': 'https://example.com/app.css'
|
||||
}
|
||||
]
|
||||
special_href = [
|
||||
l for l in links if l.attrs["href"].endswith("/extra-css-urls-demo.css")
|
||||
][0]["href"]
|
||||
# This link has a base64-encoded JSON blob in it
|
||||
encoded = special_href.split("/")[3]
|
||||
assert expected_decoded_object == json.loads(base64.b64decode(encoded))
|
||||
|
||||
|
||||
def test_plugin_extra_js_urls(app_client):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue