Permalink
Browse files

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.
  • Loading branch information...
simonw committed Aug 28, 2018
1 parent fbf4469 commit b7c6a9f9bd2bbd06ef64fef4a1fcc86fa0039039
Showing with 124 additions and 52 deletions.
  1. +0 −28 datasette/app.py
  2. +2 −2 datasette/hookspecs.py
  3. +33 −2 datasette/views/base.py
  4. +0 −2 datasette/views/index.py
  5. +42 −7 docs/plugins.rst
  6. +9 −2 tests/fixtures.py
  7. +38 −9 tests/test_plugins.py
View
@@ -2,7 +2,6 @@
import click
import collections
import hashlib
import itertools
import os
import sys
import threading
@@ -258,33 +257,6 @@ def get_canned_query(self, database_name, query_name):
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(
View
@@ -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"
View
@@ -1,5 +1,6 @@
import asyncio
import csv
import itertools
import json
import re
import time
@@ -46,6 +47,32 @@ def __init__(self, message, title=None, error_dict=None, status=500, template=No
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 @@ def render(self, templates, **context):
"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 @@ def get_templates(self, database, table=None):
"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(),
}
View
@@ -51,6 +51,4 @@ def __init__(self, datasette):
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(),
)
View
@@ -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)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``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 every page. These can
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.
View
@@ -204,6 +204,7 @@ def generate_sortable_rows(num):
PLUGIN1 = '''
from datasette import hookimpl
import base64
import pint
import json
@@ -219,8 +220,14 @@ def convert_units(amount, from_, to_):
@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
View
@@ -2,6 +2,7 @@
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):

0 comments on commit b7c6a9f

Please sign in to comment.