mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
parent
ca588b6a30
commit
1c36d07dd4
6 changed files with 117 additions and 7 deletions
|
|
@ -12,6 +12,7 @@ import asyncio
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
import itertools
|
||||||
import json
|
import json
|
||||||
import jinja2
|
import jinja2
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
@ -1126,7 +1127,12 @@ class Datasette:
|
||||||
}
|
}
|
||||||
|
|
||||||
def asset_urls(self, key):
|
def asset_urls(self, key):
|
||||||
for url_or_dict in (self.metadata.get(key) or []):
|
urls_or_dicts = (self.metadata.get(key) or [])
|
||||||
|
# Flatten list-of-lists from plugins:
|
||||||
|
urls_or_dicts += list(
|
||||||
|
itertools.chain.from_iterable(getattr(pm.hook, key)())
|
||||||
|
)
|
||||||
|
for url_or_dict in urls_or_dicts:
|
||||||
if isinstance(url_or_dict, dict):
|
if isinstance(url_or_dict, dict):
|
||||||
yield {
|
yield {
|
||||||
'url': url_or_dict['url'],
|
'url': url_or_dict['url'],
|
||||||
|
|
@ -1297,10 +1303,14 @@ class Datasette:
|
||||||
app.static(path, dirname)
|
app.static(path, dirname)
|
||||||
# Mount any plugin static/ directories
|
# Mount any plugin static/ directories
|
||||||
for plugin_module in pm.get_plugins():
|
for plugin_module in pm.get_plugins():
|
||||||
if pkg_resources.resource_isdir(plugin_module.__name__, 'static'):
|
try:
|
||||||
modpath = '/-/static-plugins/{}/'.format(plugin_module.__name__)
|
if pkg_resources.resource_isdir(plugin_module.__name__, 'static'):
|
||||||
dirpath = pkg_resources.resource_filename(plugin_module.__name__, 'static')
|
modpath = '/-/static-plugins/{}/'.format(plugin_module.__name__)
|
||||||
app.static(modpath, dirpath)
|
dirpath = pkg_resources.resource_filename(plugin_module.__name__, 'static')
|
||||||
|
app.static(modpath, dirpath)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
# Caused by --plugins_dir= plugins
|
||||||
|
pass
|
||||||
app.add_route(
|
app.add_route(
|
||||||
DatabaseView.as_view(self),
|
DatabaseView.as_view(self),
|
||||||
'/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>'
|
'/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>'
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,13 @@ def prepare_connection(conn):
|
||||||
@hookspec
|
@hookspec
|
||||||
def prepare_jinja2_environment(env):
|
def prepare_jinja2_environment(env):
|
||||||
"Modify Jinja2 template environment e.g. register custom template tags"
|
"Modify Jinja2 template environment e.g. register custom template tags"
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def extra_css_urls():
|
||||||
|
"Extra CSS URLs added by this plugin"
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def extra_js_urls():
|
||||||
|
"Extra JavaScript URLs added by this plugin"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
Customizing Datasette's appearance
|
.. _customization:
|
||||||
==================================
|
|
||||||
|
Customization
|
||||||
|
=============
|
||||||
|
|
||||||
Datasette provides a number of ways of customizing the way data is displayed.
|
Datasette provides a number of ways of customizing the way data is displayed.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -169,3 +169,65 @@ example:
|
||||||
You can now use this filter in your custom templates like so::
|
You can now use this filter in your custom templates like so::
|
||||||
|
|
||||||
Table name: {{ table|uppercase }}
|
Table name: {{ table|uppercase }}
|
||||||
|
|
||||||
|
extra_css_urls()
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Return a list of extra CSS URLs that should be included on every page. These can
|
||||||
|
take advantage of the CSS class hooks described in :ref:`customization`.
|
||||||
|
|
||||||
|
This can be a list of URLs:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from datasette import hookimpl
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_css_urls():
|
||||||
|
return [
|
||||||
|
'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css'
|
||||||
|
]
|
||||||
|
|
||||||
|
Or a list of dictionaries defining both a URL and an
|
||||||
|
`SRI hash <https://www.srihash.org/>`_:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from datasette import hookimpl
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_css_urls():
|
||||||
|
return [{
|
||||||
|
'url': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css',
|
||||||
|
'sri': 'sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4',
|
||||||
|
}]
|
||||||
|
|
||||||
|
extra_js_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:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from datasette import hookimpl
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_js_urls():
|
||||||
|
return [{
|
||||||
|
'url': 'https://code.jquery.com/jquery-3.3.1.slim.min.js',
|
||||||
|
'sri': 'sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo',
|
||||||
|
}]
|
||||||
|
|
||||||
|
You can also return URLs to files from your plugin's ``static/`` directory, if
|
||||||
|
you have one:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from datasette import hookimpl
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_js_urls():
|
||||||
|
return [
|
||||||
|
'/-/static-plugins/your_plugin/app.js'
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,19 @@ def prepare_connection(conn):
|
||||||
"select convert_units(100, 'm', 'ft');"
|
"select convert_units(100, 'm', 'ft');"
|
||||||
return (amount * ureg(from_)).to(to_).to_tuple()[0]
|
return (amount * ureg(from_)).to(to_).to_tuple()[0]
|
||||||
conn.create_function('convert_units', 3, convert_units)
|
conn.create_function('convert_units', 3, convert_units)
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_css_urls():
|
||||||
|
return ['https://example.com/app.css']
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def extra_js_urls():
|
||||||
|
return [{
|
||||||
|
'url': 'https://example.com/app.js',
|
||||||
|
'sri': 'SRIHASH',
|
||||||
|
}]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
TABLES = '''
|
TABLES = '''
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,19 @@ def test_view_html(app_client):
|
||||||
assert expected == [[str(td) for td in tr.select('td')] for tr in table.select('tbody tr')]
|
assert expected == [[str(td) for td in tr.select('td')] for tr in table.select('tbody tr')]
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_extra_css_urls(app_client):
|
||||||
|
response = app_client.get('/', gather_request=False)
|
||||||
|
assert b'<link rel="stylesheet" href="https://example.com/app.css">' in response.body
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_extra_js_urls(app_client):
|
||||||
|
response = app_client.get('/', gather_request=False)
|
||||||
|
assert (
|
||||||
|
b'<script src="https://example.com/app.js" integrity="SRIHASH" crossorigin="anonymous"></script>'
|
||||||
|
in response.body
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_index_metadata(app_client):
|
def test_index_metadata(app_client):
|
||||||
response = app_client.get('/', gather_request=False)
|
response = app_client.get('/', gather_request=False)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue