{"html_url": "https://github.com/simonw/datasette/issues/1077#issuecomment-720654925", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1077", "id": 720654925, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDY1NDkyNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-11-02T18:43:25Z", "updated_at": "2020-11-02T18:43:25Z", "author_association": "OWNER", "body": "Demo: https://latest.datasette.io/fixtures?_bot=1", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733829385, "label": "database_actions plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1077#issuecomment-720637322", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1077", "id": 720637322, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDYzNzMyMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-11-02T18:09:17Z", "updated_at": "2020-11-02T18:09:17Z", "author_association": "OWNER", "body": "Here's the `table_actions` implementation: 2f7731e9e5ff9b324beb5039fbe2be55d704a184", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733829385, "label": "database_actions plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/838#issuecomment-720354227", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/838", "id": 720354227, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDM1NDIyNw==", "user": {"value": 82988, "label": "psychemedia"}, "created_at": "2020-11-02T09:33:58Z", "updated_at": "2020-11-02T09:33:58Z", "author_association": "CONTRIBUTOR", "body": "Thanks; just a note that the `datasette.urls.static(path)` and `datasette.urls.static_plugins(plugin_name, path)` items both seem to be repeated and appear in the docs twice?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 637395097, "label": "Incorrect URLs when served behind a proxy with base_url set"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1079#issuecomment-720110298", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1079", "id": 720110298, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDExMDI5OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-11-01T15:58:22Z", "updated_at": "2020-11-01T15:58:22Z", "author_association": "OWNER", "body": "Might try a drop shadow on that menu too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733999615, "label": "Handle long breadcrumbs better with new menu"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/782#issuecomment-720028476", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/782", "id": 720028476, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDAyODQ3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-11-01T05:00:05Z", "updated_at": "2020-11-01T05:00:05Z", "author_association": "OWNER", "body": "This should be the key focus for Datasette 0.52.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 627794879, "label": "Redesign default .json format"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/949#issuecomment-720021029", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/949", "id": 720021029, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDAyMTAyOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-11-01T03:29:48Z", "updated_at": "2020-11-01T03:29:48Z", "author_association": "OWNER", "body": "I'm not going to do any more work on this - SQL isn't an auto-complete friendly enough language.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 684961449, "label": "Try out CodeMirror SQL hints"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1077#issuecomment-720003026", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1077", "id": 720003026, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDAwMzAyNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T23:48:21Z", "updated_at": "2020-10-31T23:50:07Z", "author_association": "OWNER", "body": "Needed by https://github.com/simonw/datasette-backup/issues/6", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733829385, "label": "database_actions plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1047#issuecomment-719994676", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1047", "id": 719994676, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk5NDY3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T22:11:25Z", "updated_at": "2020-10-31T22:11:25Z", "author_association": "OWNER", "body": "https://docs.datasette.io/en/latest/binary_data.html", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 728895233, "label": "A new section in the docs about how Datasette handles BLOB columns"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1027#issuecomment-719988113", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1027", "id": 719988113, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4ODExMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T21:03:37Z", "updated_at": "2020-10-31T21:03:37Z", "author_association": "OWNER", "body": "On my Mac, I run:\r\n\r\n datasette . -p 8009 --config base_url:/datasette-prefix/\r\n\r\nThen I edited `/usr/local/etc/httpd/httpd.conf` and add this section:\r\n```\r\nLoadModule proxy_module lib/httpd/modules/mod_proxy.so\r\nLoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so\r\nProxyPass /datasette-prefix/ http://localhost:8009/datasette-prefix/\r\n```\r\nI ran Apache in the foreground like so:\r\n\r\n apachectl -X\r\n\r\nNow hitting http://localhost:8081/datasette-prefix/fixtures/compound_three_primary_keys worked!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722758132, "label": "Add documentation on serving Datasette behind a proxy using base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1023#issuecomment-719986922", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1023", "id": 719986922, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjkyMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:51:01Z", "updated_at": "2020-10-31T20:51:01Z", "author_association": "OWNER", "body": "This should all be working correctly now.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722673818, "label": "Fix issues relating to base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/838#issuecomment-719986904", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/838", "id": 719986904, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjkwNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:50:41Z", "updated_at": "2020-10-31T20:50:41Z", "author_association": "OWNER", "body": "OK, this should be working now. You can use the `datasette.urls.static_plugins()` method to generate the correct URLs in the `extra_css_urls` plugin hook: https://docs.datasette.io/en/latest/internals.html#datasette-urls", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 637395097, "label": "Incorrect URLs when served behind a proxy with base_url set"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1041#issuecomment-719986800", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1041", "id": 719986800, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjgwMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:49:28Z", "updated_at": "2020-10-31T20:49:28Z", "author_association": "OWNER", "body": "Implemented in a4ca26a2659d21779adf625183061d8879954c15", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 727627923, "label": "extra_js_urls and extra_css_urls should respect base_url setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719986698", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719986698, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjY5OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:48:17Z", "updated_at": "2020-10-31T20:48:17Z", "author_association": "OWNER", "body": "Here's the `datasette-edit-templates` plugin WIP I had before removing the hook: https://github.com/simonw/datasette-edit-templates/tree/82855c2612b84bc09c48fca885f831633a0d1552", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733499930, "label": "load_template hook doesn't work for include/extends"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983750", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983750, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4Mzc1MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:22:29Z", "updated_at": "2020-10-31T20:22:29Z", "author_association": "OWNER", "body": "I bet this is because I'm mucking around with one of those `__` methods. I'll try just doing the non-underscore methods instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983565", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983565, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzU2NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:21:03Z", "updated_at": "2020-10-31T20:21:03Z", "author_association": "OWNER", "body": "Here's the output of `dir(str)`:\r\n\r\n`['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983484", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983484, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzQ4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:20:28Z", "updated_at": "2020-10-31T20:20:28Z", "author_association": "OWNER", "body": "It looks like this is specific to the way `PrefixedUrlString` is built.\r\n```\r\n(Pdb) class Weird(str): pass\r\n(Pdb) isinstance(Weird('bob'), collections.abc.Awaitable)\r\nFalse\r\n```\r\nSo subclassing strings doesn't trigger this bug, but something about `PrefixedUrlString` causes the problem.\r\n\r\nHere's the current `PrefixedUrlString` implementation:\r\n\r\nhttps://github.com/simonw/datasette/blob/bf18b9ba175a7b25fb8b765847397dd6efb8bb7b/datasette/utils/__init__.py#L1015-L1035", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983240", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983240, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzI0MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:18:49Z", "updated_at": "2020-10-31T20:18:49Z", "author_association": "OWNER", "body": "Here's the core problem:\r\n```\r\n(Pdb) isinstance('bob', collections.abc.Awaitable)\r\nFalse\r\n(Pdb) isinstance(PrefixedUrlString('bob'), collections.abc.Awaitable)\r\n*** TypeError: issubclass() arg 1 must be a class\r\n```\r\nFor some reason `isinstance()` does not like being handed an instance of PrefixedUrlString.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719981173", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719981173, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MTE3Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:02:30Z", "updated_at": "2020-10-31T20:03:45Z", "author_association": "OWNER", "body": "I wonder how Jinja's `Markup()` class works? It uses https://pypi.org/project/MarkupSafe/\r\n\r\nIt's a subclass of `str`, defined here: https://github.com/pallets/markupsafe/blob/master/src/markupsafe/__init__.py", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719980742", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719980742, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MDc0Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T19:58:57Z", "updated_at": "2020-10-31T19:58:57Z", "author_association": "OWNER", "body": "Sample traceback:\r\n```\r\n \r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/jinja2/asyncsupport.py:173: in auto_await\r\n if inspect.isawaitable(value):\r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/inspect.py:226: in isawaitable\r\n isinstance(object, collections.abc.Awaitable))\r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/abc.py:139: in __instancecheck__\r\n return _abc_instancecheck(cls, instance)\r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\n\r\ncls = \r\nsubclass = .method of '/-/static/app.css'>\r\n\r\n def __subclasscheck__(cls, subclass):\r\n \"\"\"Override for issubclass(subclass, cls).\"\"\"\r\n> return _abc_subclasscheck(cls, subclass)\r\nE TypeError: issubclass() arg 1 must be a class\r\n```\r\nThis is within Jinja. It looks like Jinja really doesn't like methods that return non-string objects like `PrefixedUrlString`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719977864", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719977864, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk3Nzg2NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T19:35:01Z", "updated_at": "2020-10-31T19:35:01Z", "author_association": "OWNER", "body": "These plugins were not designed to be actually hosted online, so they do some nasty things like linking to the made-up `plugin-example.com` domain. I should fix that.\r\n\r\n\"Datasette_Fixtures__fixtures\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719966176", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719966176, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2NjE3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:51:31Z", "updated_at": "2020-10-31T17:51:31Z", "author_association": "OWNER", "body": "Demo:\r\n\r\n- https://latest.datasette.io/fixtures/facetable?_bot=1\r\n- https://latest.datasette.io/fixtures/simple_view?_bot=1", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719965426", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719965426, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2NTQyNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:45:00Z", "updated_at": "2020-10-31T17:45:00Z", "author_association": "OWNER", "body": "This is working. Go to https://latest.datasette.io/login-as-root and click the button, then visit this page to see extra content added by plugins: https://latest.datasette.io/fixtures/compound_three_primary_keys\r\n\r\n![latest-plugins](https://user-images.githubusercontent.com/9599/97786052-19e53d00-1b66-11eb-8268-b452e08965e3.gif)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719963074", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719963074, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2MzA3NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:23:48Z", "updated_at": "2020-10-31T17:23:48Z", "author_association": "OWNER", "body": "Needs a way to login as root, seeing as several plugins only show extra content if the user is logged in as root.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719961701", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719961701, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2MTcwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:11:59Z", "updated_at": "2020-10-31T17:11:59Z", "author_association": "OWNER", "body": "It bothers me that these aren't visible in any public demos. Maybe `latest.datasette.io` should include the `my_plugins.py` and `my_plugins2.py` plugins?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1026#issuecomment-719959754", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1026", "id": 719959754, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1OTc1NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:56:35Z", "updated_at": "2020-10-31T16:56:35Z", "author_association": "OWNER", "body": "#1041 can also benefit from the string subclass that shows that `base_url` has been added.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722738988, "label": "How should datasette.client interact with base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719959419", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719959419, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1OTQxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:53:42Z", "updated_at": "2020-10-31T16:53:42Z", "author_association": "OWNER", "body": "For the 0.51 release I'm going to add tests that show this works on view pages. I won't implement it for query pages.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719956184", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719956184, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NjE4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:26:09Z", "updated_at": "2020-10-31T16:26:09Z", "author_association": "OWNER", "body": "Should the hook provide an indication that it's running on a different type of page? I think yes for queries. Not sure about views - they behave very much like tables, and the plugin can always introspect to see if something is a view if it needs to.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1070#issuecomment-719955724", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1070", "id": 719955724, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NTcyNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:22:45Z", "updated_at": "2020-10-31T16:22:45Z", "author_association": "OWNER", "body": "I've removed this plugin hook in #1073.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733390884, "label": "load_template() example in documentation showing loading from a database"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719955491", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719955491, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NTQ5MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:20:58Z", "updated_at": "2020-10-31T16:20:58Z", "author_association": "OWNER", "body": "Here's the proof of concept `FunctionLoader` that showed me that this wasn't going to work:\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex 4b28e71..b076be7 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -21,7 +21,7 @@ from pathlib import Path\r\n from markupsafe import Markup\r\n from itsdangerous import URLSafeSerializer\r\n import jinja2\r\n-from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader\r\n+from jinja2 import ChoiceLoader, Environment, FileSystemLoader, FunctionLoader, PrefixLoader\r\n from jinja2.environment import Template\r\n from jinja2.exceptions import TemplateNotFound\r\n import uvicorn\r\n@@ -300,6 +300,7 @@ class Datasette:\r\n template_paths.append(default_templates)\r\n template_loader = ChoiceLoader(\r\n [\r\n+ FunctionLoader(self._load_template_from_plugins),\r\n FileSystemLoader(template_paths),\r\n # Support {% extends \"default:table.html\" %}:\r\n PrefixLoader(\r\n@@ -322,6 +323,17 @@ class Datasette:\r\n self._root_token = secrets.token_hex(32)\r\n self.client = DatasetteClient(self)\r\n \r\n+ def _load_template_from_plugins(self, template):\r\n+ # \"If auto reloading is enabled it\u2019s called to check if the template changed\"\r\n+ uptodatefunc = lambda: True\r\n+ source = pm.hook.load_template(\r\n+ template=template,\r\n+ datasette=self,\r\n+ )\r\n+ if source is None:\r\n+ return None\r\n+ return source, template, uptodatefunc\r\n+\r\n @property\r\n def urls(self):\r\n return Urls(self)\r\n@@ -719,35 +731,7 @@ class Datasette:\r\n else:\r\n if isinstance(templates, str):\r\n templates = [templates]\r\n-\r\n- # Give plugins first chance at loading the template\r\n- break_outer = False\r\n- plugin_template_source = None\r\n- plugin_template_name = None\r\n- template_name = None\r\n- for template_name in templates:\r\n- if break_outer:\r\n- break\r\n- plugin_template_source = pm.hook.load_template(\r\n- template=template_name,\r\n- request=request,\r\n- datasette=self,\r\n- )\r\n- plugin_template_source = await await_me_maybe(plugin_template_source)\r\n- if plugin_template_source:\r\n- break_outer = True\r\n- plugin_template_name = template_name\r\n- break\r\n- if plugin_template_source is not None:\r\n- template = self.jinja_env.from_string(plugin_template_source)\r\n- else:\r\n- template = self.jinja_env.select_template(templates)\r\n- for template_name in templates:\r\n- from_plugin = template_name == plugin_template_name\r\n- used = from_plugin or template_name == template.name\r\n- templates_considered.append(\r\n- {\"name\": template_name, \"used\": used, \"from_plugin\": from_plugin}\r\n- )\r\n+ template = self.jinja_env.select_template(templates)\r\n body_scripts = []\r\n # pylint: disable=no-member\r\n for extra_script in pm.hook.extra_body_script(\r\ndiff --git a/datasette/hookspecs.py b/datasette/hookspecs.py\r\nindex ca84b35..7804def 100644\r\n--- a/datasette/hookspecs.py\r\n+++ b/datasette/hookspecs.py\r\n@@ -50,7 +50,7 @@ def extra_template_vars(\r\n \r\n \r\n @hookspec(firstresult=True)\r\n-def load_template(template, request, datasette):\r\n+def load_template(template, datasette):\r\n \"Load the specified template, returning the template code as a string\"\r\n \r\n \r\ndiff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst\r\nindex 3c57b6a..8f2704e 100644\r\n--- a/docs/plugin_hooks.rst\r\n+++ b/docs/plugin_hooks.rst\r\n@@ -273,15 +273,12 @@ Example: `datasette-cluster-map >> def load_template(name):\r\n... if name == 'index.html':\r\n... return '...'\r\n...\r\n>>> loader = FunctionLoader(load_template)\r\n```\r\n\r\nJust one catch: I need to be able to load templates asynchronously, because they live in the database. Let's hope Jinja has a mechanism for that!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733499930, "label": "load_template hook doesn't work for include/extends"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719785005", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719785005, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTc4NTAwNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T20:36:22Z", "updated_at": "2020-10-30T20:36:22Z", "author_association": "OWNER", "body": "It should be easy enough to show a comment that says which original template names were considered, but I may not be able to show which one was actually used (or which ones came from plugins).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733499930, "label": "load_template hook doesn't work for include/extends"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719784606", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719784606, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTc4NDYwNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T20:35:33Z", "updated_at": "2020-10-30T20:35:33Z", "author_association": "OWNER", "body": "To fix this I think I need to move the `load_template` implementation into a Jinja template loader.\r\n\r\nI'm not sure I'll be able to keep the `Templates considered` comment working though:\r\n\r\nhttps://github.com/simonw/datasette/blob/a2a709072059c6b3da365df9a332ca744c2079e9/datasette/app.py#L745-L750", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733499930, "label": "load_template hook doesn't work for include/extends"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1071#issuecomment-719777499", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1071", "id": 719777499, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTc3NzQ5OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T20:20:01Z", "updated_at": "2020-10-30T20:20:01Z", "author_association": "OWNER", "body": "Fixed: https://latest.datasette.io/-/messages\r\n\r\n![demo-message](https://user-images.githubusercontent.com/9599/97753199-9c142980-1ab2-11eb-9be9-c0be41acc68e.gif)\r\n\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733485423, "label": "Messages should be displayed full width"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719657478", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719657478, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY1NzQ3OA==", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2020-10-30T16:31:21Z", "updated_at": "2020-10-30T17:46:36Z", "author_association": "NONE", "body": "# [Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=h1) Report\n> Merging [#1069](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=desc) into [main](https://codecov.io/gh/simonw/datasette/commit/222f79bb4c6e2aa5426cc5ff25f1b2461e18a300?el=desc) will **increase** coverage by `0.01%`.\n> The diff coverage is `95.83%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/simonw/datasette/pull/1069/graphs/tree.svg?width=650&height=150&src=pr&token=eSahVY7kw1)](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## main #1069 +/- ##\n==========================================\n+ Coverage 91.30% 91.32% +0.01% \n==========================================\n Files 29 29 \n Lines 3736 3756 +20 \n==========================================\n+ Hits 3411 3430 +19 \n- Misses 325 326 +1 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=tree) | Coverage \u0394 | |\n|---|---|---|\n| [datasette/views/base.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3ZpZXdzL2Jhc2UucHk=) | `93.94% <\u00f8> (-0.04%)` | :arrow_down: |\n| [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `96.38% <95.45%> (-0.05%)` | :arrow_down: |\n| [datasette/hookspecs.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2hvb2tzcGVjcy5weQ==) | `100.00% <100.00%> (\u00f8)` | |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `\u0394 = absolute (impact)`, `\u00f8 = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=footer). Last update [222f79b...92f3840](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719672967", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719672967, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY3Mjk2Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T16:58:01Z", "updated_at": "2020-10-30T16:58:01Z", "author_association": "OWNER", "body": "OK, new hook specification is:\r\n```python\r\n@hookspec(firstresult=True)\r\ndef load_template(template, request, datasette):\r\n \"Load the specified template, returning the template code as a string\"\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719670714", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719670714, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY3MDcxNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T16:53:56Z", "updated_at": "2020-10-30T16:53:56Z", "author_association": "OWNER", "body": "I'm having second thoughts about the design of the plugin hook.\r\n\r\nConsider the following:\r\n```python\r\n plugin_template_source = pm.hook.load_template(\r\n template=template_name,\r\n database=context.get(\"database\"),\r\n table=context.get(\"table\"),\r\n columns=context.get(\"columns\"),\r\n view_name=self.name,\r\n request=request,\r\n datasette=self.ds,\r\n )\r\n```\r\nIt's a bit gross that `database`, `table` and `columns` are pulled out of the context like that. This doesn't make sense for pages that are rendered by plugins, for example.\r\n\r\nSo maybe for the first release of this plugin hook I should cut it down to just seeing `template`, `request` and `datasette`. I can add the table/view/etc stuff back in later if it turns out to be necessary.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719666912", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719666912, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY2NjkxMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T16:47:44Z", "updated_at": "2020-10-30T16:47:44Z", "author_association": "OWNER", "body": "Bringing over a comment from #1042:\r\n\r\n> I'd like to do this all in the `datasette.render_template()` method to ensure it's available to plugins as well, not just core code that uses the `BaseView` class.\r\n> \r\n> This code is the problem:\r\n> \r\n> https://github.com/simonw/datasette/blob/d3e9b0aecb6f8e9b2befd9c654ccb7ce852db3e7/datasette/views/base.py#L114-L133\r\n> \r\n> I think I'll fix this by moving the `select_templates` mechanism into `datasette.render_templates()`.\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719664530", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719664530, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY2NDUzMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T16:43:40Z", "updated_at": "2020-10-30T16:43:40Z", "author_association": "OWNER", "body": "I should include an example in the documentation that shows loading templates from a database table.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1069#issuecomment-719640430", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1069", "id": 719640430, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTY0MDQzMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T16:01:13Z", "updated_at": "2020-10-30T16:01:13Z", "author_association": "OWNER", "body": "Next steps: build a demonstration plugin against this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733303548, "label": "load_template() plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1068#issuecomment-719630745", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1068", "id": 719630745, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTYzMDc0NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T15:44:13Z", "updated_at": "2020-10-30T15:44:13Z", "author_association": "OWNER", "body": "Documentation: https://docs.datasette.io/en/latest/authentication.html#debug-menu", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732939921, "label": "Default menu links should check a real permission "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1068#issuecomment-719332460", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1068", "id": 719332460, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMzMjQ2MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:13:10Z", "updated_at": "2020-10-30T07:13:10Z", "author_association": "OWNER", "body": "I mainly want this so I can add that debug menu to my Dogsheep.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732939921, "label": "Default menu links should check a real permission "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1068#issuecomment-719331236", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1068", "id": 719331236, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMzMTIzNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:11:58Z", "updated_at": "2020-10-30T07:11:58Z", "author_association": "OWNER", "body": "Document the new permission here: https://docs.datasette.io/en/stable/authentication.html#built-in-permissions", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732939921, "label": "Default menu links should check a real permission "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1068#issuecomment-719329219", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1068", "id": 719329219, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMyOTIxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:09:59Z", "updated_at": "2020-10-30T07:09:59Z", "author_association": "OWNER", "body": "Permission idea: `debug-menu`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732939921, "label": "Default menu links should check a real permission "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1068#issuecomment-719328661", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1068", "id": 719328661, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMyODY2MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:09:30Z", "updated_at": "2020-10-30T07:09:30Z", "author_association": "OWNER", "body": "Then this can make it available to root: https://github.com/simonw/datasette/blob/18a64fbb29271ce607937110bbdb55488c43f4e0/datasette/default_permissions.py", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732939921, "label": "Default menu links should check a real permission "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719322666", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719322666, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMyMjY2Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:04:02Z", "updated_at": "2020-10-30T07:04:02Z", "author_association": "OWNER", "body": "Maybe rename it to `actions_menu` and have it work for database, view, table and query pages using different arguments on each.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719320948", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719320948, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTMyMDk0OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T07:02:37Z", "updated_at": "2020-10-30T07:02:37Z", "author_association": "OWNER", "body": "Yes, this should be possible - no point restricting what plugin authors can do with the feature. Will need to add some extra arguments to the plugin hook for this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/690#issuecomment-719195346", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/690", "id": 719195346, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTE5NTM0Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T05:20:42Z", "updated_at": "2020-10-30T05:20:42Z", "author_association": "OWNER", "body": "I've now added two new plugin hooks: [menu_links()](https://docs.datasette.io/en/latest/plugin_hooks.html#menu-links-datasette-actor) and [table_actions()](https://docs.datasette.io/en/latest/plugin_hooks.html#table-actions-datasette-actor-database-table).\r\n\r\nI'm going to close this issue. Further work (on column actions and and database actions) can happen in separate tickets, but I won't include them in Datasette 0.51 since they're much less interesting than table and instance actions.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 573755726, "label": "Mechanism for plugins to add action menu items for various things"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1066#issuecomment-719194756", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1066", "id": 719194756, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTE5NDc1Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T05:18:35Z", "updated_at": "2020-10-30T05:18:35Z", "author_association": "OWNER", "body": "Documentation: https://docs.datasette.io/en/latest/plugin_hooks.html#table-actions-datasette-actor-database-table", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732859030, "label": "Table actions menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1066#issuecomment-719194619", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1066", "id": 719194619, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTE5NDYxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T05:18:04Z", "updated_at": "2020-10-30T05:18:04Z", "author_association": "OWNER", "body": "The cog only appears if at least one table action has been registered by a plugin. It looks like this:\r\n\r\n![table-actions](https://user-images.githubusercontent.com/9599/97662535-9bd54900-1a34-11eb-8e0f-56d159c8834e.gif)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732859030, "label": "Table actions menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1066#issuecomment-719154646", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1066", "id": 719154646, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTE1NDY0Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T03:48:15Z", "updated_at": "2020-10-30T03:48:15Z", "author_association": "OWNER", "body": "This will use a very similar implementation to the navigation menu in #1064 - similar plugin hook and I'll use a `
` to implement it.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732859030, "label": "Table actions menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1065#issuecomment-719153773", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1065", "id": 719153773, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTE1Mzc3Mw==", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2020-10-30T03:44:57Z", "updated_at": "2020-10-30T03:44:57Z", "author_association": "NONE", "body": "# [Codecov](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=h1) Report\n> Merging [#1065](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=desc) into [main](https://codecov.io/gh/simonw/datasette/commit/1a861be19e326e0c88230a711a1b6536366697d7?el=desc) will **increase** coverage by `0.03%`.\n> The diff coverage is `100.00%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/simonw/datasette/pull/1065/graphs/tree.svg?width=650&height=150&src=pr&token=eSahVY7kw1)](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## main #1065 +/- ##\n==========================================\n+ Coverage 91.23% 91.27% +0.03% \n==========================================\n Files 28 29 +1 \n Lines 3710 3724 +14 \n==========================================\n+ Hits 3385 3399 +14 \n Misses 325 325 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=tree) | Coverage \u0394 | |\n|---|---|---|\n| [datasette/plugins.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3BsdWdpbnMucHk=) | `82.35% <\u00f8> (\u00f8)` | |\n| [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `96.42% <100.00%> (+0.03%)` | :arrow_up: |\n| [datasette/default\\_menu\\_links.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2RlZmF1bHRfbWVudV9saW5rcy5weQ==) | `100.00% <100.00%> (\u00f8)` | |\n| [datasette/hookspecs.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2hvb2tzcGVjcy5weQ==) | `100.00% <100.00%> (\u00f8)` | |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `\u0394 = absolute (impact)`, `\u00f8 = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=footer). Last update [1a861be...5f118b5](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732856937, "label": "Nav menu plus menu_links() hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719117185", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719117185, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTExNzE4NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:35:17Z", "updated_at": "2020-10-30T01:35:17Z", "author_association": "OWNER", "body": "I'm going to go with a list of `{\"label\": ..., \"href\": ...}` as the first iteration of this. The logout link will not be returned as part of the plugin output. A default plugin will provide the debug tools if the user is logged in as root.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719111597", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719111597, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTExMTU5Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:15:05Z", "updated_at": "2020-10-30T01:15:05Z", "author_association": "OWNER", "body": "I'm torn on this one. I think I have a very slight preference for plugins returning structured objects as opposed to HTML. Less likely to regret that choice in the future?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719111373", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719111373, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTExMTM3Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:14:13Z", "updated_at": "2020-10-30T01:14:13Z", "author_association": "OWNER", "body": "Plugins returning HTML makes more sense for some of the other areas that plugins will be able to inject content - e.g. injecting content on the table or row page above the table.\r\n\r\nIf I'm going to have that as a pattern though it may make sense to use HTML here, since that will be consistent with other places that plugins can inject additional content.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719110808", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719110808, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTExMDgwOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:12:09Z", "updated_at": "2020-10-30T01:12:19Z", "author_association": "OWNER", "body": "Or... plugins could return HTML - maybe optionally using helper functions to generate common HTML such that plugins which use the helpers can have their HTML modified in the future.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719110582", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719110582, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTExMDU4Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:11:13Z", "updated_at": "2020-10-30T01:11:13Z", "author_association": "OWNER", "body": "Should plugins be able to add forms like the logout form here, or should they be restricted to adding navigation links?\r\n\r\nI can't think of a reason a plugin would need to add a form. The logout form is a special case to protect against logout-csrf attacks.\r\n\r\nSo I think plugins get to return a list of dictionaries, each with a `label` and an `href`:\r\n\r\n```python\r\nreturn [{\r\n \"label\": \"Upload CSVs\",\r\n \"href\": datasette.urls.path(\"/-/upload-csvs\")\r\n}]\r\n```\r\nBut... is there an argument for returning headings, to divide up the menu?\r\n\r\nI think so. I also like the idea that a default plugin checks for the `root` user and outputs links to the different debugging tools - maybe those should be wrapped in a section heading.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719109770", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719109770, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTEwOTc3MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T01:08:14Z", "updated_at": "2020-10-30T01:08:14Z", "author_association": "OWNER", "body": "How should the plugin hook work?\r\n\r\nHere's the first version of the HTML:\r\n```html\r\n
\r\n \r\n
\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719106174", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719106174, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTEwNjE3NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T00:55:12Z", "updated_at": "2020-10-30T00:55:12Z", "author_association": "OWNER", "body": "So what should go in this menu?\r\n\r\nIf the user is logged in as root, I'll link to the various debug pages.\r\n\r\nIf they're not logged in at all I don't think the menu should appear.\r\n\r\nIf they are logged in as anyone, it should display to give them access to the \"log out\" button.\r\n\r\nPlugins can add links to it. If those plugins add links, the menu will display.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719105641", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719105641, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTEwNTY0MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T00:53:00Z", "updated_at": "2020-10-30T00:53:00Z", "author_association": "OWNER", "body": "Tips for making this accessible: https://css-tricks.com/accessible-svgs/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719104883", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719104883, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTEwNDg4Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T00:50:01Z", "updated_at": "2020-10-30T00:52:29Z", "author_association": "OWNER", "body": "Here's what the prototype looks like so far:\r\n\r\n![menu](https://user-images.githubusercontent.com/9599/97647443-7eda4f00-1a0f-11eb-8d78-b703b7a13616.gif)\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1064#issuecomment-719105197", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1064", "id": 719105197, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTEwNTE5Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T00:51:16Z", "updated_at": "2020-10-30T00:51:16Z", "author_association": "OWNER", "body": "I used a `
` for this: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L16-L36\r\n\r\nI added a bit of JavaScript so that clicking outside the menu would close it: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L59-L74", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732798913, "label": "Navigation menu plus plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1034#issuecomment-719094027", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1034", "id": 719094027, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA5NDAyNw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-30T00:11:17Z", "updated_at": "2020-10-30T00:11:17Z", "author_association": "OWNER", "body": "Demos:\r\n\r\nhttps://latest.datasette.io/fixtures/binary_data.csv?_size=max\r\n\r\n```csv\r\nrowid,data\r\n1,http://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data\r\n2,http://latest.datasette.io/fixtures/binary_data/2.blob?_blob_column=data\r\n3,\r\n```\r\n\r\nhttps://latest.datasette.io/fixtures.csv?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max\r\n\r\n```csv\r\nrowid,data\r\n1,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d\r\n2,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=b835b0483cedb86130b9a2c280880bf5fadc5318ddf8c18d0df5204d40df1724\r\n3,\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 725184645, "label": "Better way of representing binary data in .csv output"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1063#issuecomment-719066706", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1063", "id": 719066706, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA2NjcwNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T22:46:28Z", "updated_at": "2020-10-29T22:46:28Z", "author_association": "OWNER", "body": "I'm not going to do the base64 thing unless someone asks for it.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732685643, "label": ".csv should link to .blob downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1051#issuecomment-719053669", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1051", "id": 719053669, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA1MzY2OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T22:12:16Z", "updated_at": "2020-10-29T22:12:16Z", "author_association": "OWNER", "body": "https://latest.datasette.io/fixtures?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+101 now looks like this:\r\n\r\n\"fixtures__select_rowid__data_from_binary_data_order_by_rowid_limit_101_and_understanding_Google_auths_-_for_your_information\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729096595, "label": "Better display of binary data on arbitrary query results page"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1034#issuecomment-719050754", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1034", "id": 719050754, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA1MDc1NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T22:04:52Z", "updated_at": "2020-10-29T22:04:52Z", "author_association": "OWNER", "body": "I'm going to link to. the new `.blob` representation using the new `?_blob_hash=xxx` argument to ensure that the content served is the expected binary blob.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 725184645, "label": "Better way of representing binary data in .csv output"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1063#issuecomment-719050390", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1063", "id": 719050390, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA1MDM5MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T22:04:00Z", "updated_at": "2020-10-29T22:04:00Z", "author_association": "OWNER", "body": "This will close #1034.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732685643, "label": ".csv should link to .blob downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1061#issuecomment-719049115", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1061", "id": 719049115, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA0OTExNQ==", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2020-10-29T22:00:57Z", "updated_at": "2020-10-29T22:00:57Z", "author_association": "NONE", "body": "# [Codecov](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=h1) Report\n> Merging [#1061](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=desc) into [main](https://codecov.io/gh/simonw/datasette/commit/d6f9ff71378c4eab34dad181c23cfc143a4aef2d?el=desc) will **increase** coverage by `0.07%`.\n> The diff coverage is `96.87%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/simonw/datasette/pull/1061/graphs/tree.svg?width=650&height=150&src=pr&token=eSahVY7kw1)](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## main #1061 +/- ##\n==========================================\n+ Coverage 91.13% 91.20% +0.07% \n==========================================\n Files 27 28 +1 \n Lines 3677 3697 +20 \n==========================================\n+ Hits 3351 3372 +21 \n+ Misses 326 325 -1 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=tree) | Coverage \u0394 | |\n|---|---|---|\n| [datasette/plugins.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3BsdWdpbnMucHk=) | `82.35% <\u00f8> (\u00f8)` | |\n| [datasette/views/base.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3ZpZXdzL2Jhc2UucHk=) | `93.77% <0.00%> (\u00f8)` | |\n| [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `96.38% <100.00%> (+0.15%)` | :arrow_up: |\n| [datasette/blob\\_renderer.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2Jsb2JfcmVuZGVyZXIucHk=) | `100.00% <100.00%> (\u00f8)` | |\n| [datasette/utils/asgi.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3V0aWxzL2FzZ2kucHk=) | `92.13% <100.00%> (+0.17%)` | :arrow_up: |\n| [datasette/views/database.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3ZpZXdzL2RhdGFiYXNlLnB5) | `97.04% <100.00%> (+0.07%)` | :arrow_up: |\n| [datasette/views/table.py](https://codecov.io/gh/simonw/datasette/pull/1061/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3ZpZXdzL3RhYmxlLnB5) | `95.86% <100.00%> (-0.22%)` | :arrow_down: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `\u0394 = absolute (impact)`, `\u00f8 = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=footer). Last update [d6f9ff7...1196d08](https://codecov.io/gh/simonw/datasette/pull/1061?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732634375, "label": ".blob output renderer"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1061#issuecomment-719042601", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1061", "id": 719042601, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA0MjYwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:45:35Z", "updated_at": "2020-10-29T21:50:42Z", "author_association": "OWNER", "body": "Moving the CSV work to a separate issue.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732634375, "label": ".blob output renderer"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1063#issuecomment-719043108", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1063", "id": 719043108, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTA0MzEwOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:46:48Z", "updated_at": "2020-10-29T21:46:48Z", "author_association": "OWNER", "body": "Remove this `xfail` and `import pytest`: https://github.com/simonw/datasette/blob/503a5b7b4080a26ef9ceb1ecd1a4a6f4ef4ffc59/tests/test_csv.py#L83-L96", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732685643, "label": ".csv should link to .blob downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1061#issuecomment-719035336", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1061", "id": 719035336, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTAzNTMzNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:29:29Z", "updated_at": "2020-10-29T21:29:29Z", "author_association": "OWNER", "body": "Those display_rows have already been processed by the `render_cell` plugin hook: https://github.com/simonw/datasette/blob/d6f9ff71378c4eab34dad181c23cfc143a4aef2d/datasette/views/database.py#L320-L346", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732634375, "label": ".blob output renderer"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1061#issuecomment-719033013", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1061", "id": 719033013, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTAzMzAxMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:27:14Z", "updated_at": "2020-10-29T21:27:14Z", "author_association": "OWNER", "body": "Next challenge: link to `.blob` downloads from https://latest.datasette.io/fixtures?sql=select+rowid%2C+data+from+binary_data\r\n\r\nThis will be a bit tricky. Here's how that template works at the moment: https://github.com/simonw/datasette/blob/d6f9ff71378c4eab34dad181c23cfc143a4aef2d/datasette/templates/query.html#L69-L77", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732634375, "label": ".blob output renderer"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1062#issuecomment-719031901", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1062", "id": 719031901, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTAzMTkwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:25:54Z", "updated_at": "2020-10-29T21:25:54Z", "author_association": "OWNER", "body": "Relevant code: https://github.com/simonw/datasette/blob/d6f9ff71378c4eab34dad181c23cfc143a4aef2d/datasette/views/base.py#L258-L345", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732674148, "label": "Refactor .csv to be an output renderer - and teach register_output_renderer to stream all rows"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-719021514", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 719021514, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTAyMTUxNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T21:05:08Z", "updated_at": "2020-10-29T21:05:08Z", "author_association": "OWNER", "body": "Idea: what if Datasette had a custom SQLite function that could be used to generate URLs to the row-level BLOB download for a value? Then custom SQL query authors could use that function to link to the relevant content.\r\n\r\nThis could be expanded to exposing other `datasette.urls` functionality as well.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-719001701", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 719001701, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTAwMTcwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T20:26:44Z", "updated_at": "2020-10-29T20:26:44Z", "author_association": "OWNER", "body": "I'll do the rest of the work on this in the pull request #1061.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-718989895", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 718989895, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODk4OTg5NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T20:04:15Z", "updated_at": "2020-10-29T20:04:15Z", "author_association": "OWNER", "body": "I'll use `hashlib.sha256` for these hashes.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-718987852", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 718987852, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODk4Nzg1Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T20:00:32Z", "updated_at": "2020-10-29T20:00:32Z", "author_association": "OWNER", "body": "The reason I like the `?_blob_hash=` solution is that it feels really misleading to provide a link to \"download this binary\" which could conceivably download some other data.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-718980944", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 718980944, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODk4MDk0NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T19:46:19Z", "updated_at": "2020-10-29T19:46:19Z", "author_association": "OWNER", "body": "Had an idea in https://github.com/simonw/datasette/issues/1051#issuecomment-718980659\r\n\r\n> OK, alternative idea. The `.blob` output renderer from #1050 gets to see multiple rows at once.\r\n> \r\n> For an arbitrary SQL query, how about if I link to this?\r\n> \r\n> `/db.blob?sql=...&_blob_column=data&_blob_hash=bc4c24181ed3ce666`\r\n> \r\n> Then the output renderer loops through all of the `data` results that are available to it and, if one of them hashes to that value, serves up that data?\r\n> \r\n> If no matches are found it can show an error message telling you that the link has expired (presumably because the underlying database has changed since the link was generated).\r\n> \r\n> I think this might be the best solution to the problem.\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1051#issuecomment-718980659", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1051", "id": 718980659, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODk4MDY1OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T19:45:42Z", "updated_at": "2020-10-29T19:45:42Z", "author_association": "OWNER", "body": "OK, alternative idea. The `.blob` output renderer from #1050 gets to see multiple rows at once. \r\n\r\nFor an arbitrary SQL query, how about if I link to this?\r\n\r\n`/db.blob?sql=...&_blob_column=data&_blob_hash=bc4c24181ed3ce666`\r\n\r\nThen the output renderer loops through all of the `data` results that are available to it and, if one of them hashes to that value, serves up that data?\r\n\r\nIf no matches are found it can show an error message telling you that the link has expired (presumably because the underlying database has changed since the link was generated).\r\n\r\nI think this might be the best solution to the problem.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729096595, "label": "Better display of binary data on arbitrary query results page"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1053#issuecomment-718976679", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1053", "id": 718976679, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODk3NjY3OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T19:37:57Z", "updated_at": "2020-10-29T19:37:57Z", "author_association": "OWNER", "body": "https://docs.datasette.io/en/latest/writing_plugins.html#designing-urls-for-your-plugin", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729604838, "label": "Document recommendations for plugin authors to design URLs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1049#issuecomment-718528252", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1049", "id": 718528252, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODUyODI1Mg==", "user": {"value": 82988, "label": "psychemedia"}, "created_at": "2020-10-29T09:20:34Z", "updated_at": "2020-10-29T09:20:34Z", "author_association": "CONTRIBUTOR", "body": "That workaround is probably fine. I was trying to work out whether there might be other situations where a pre-external package load might be useful but couldn't offhand bring any other examples to mind. The static plugins option also looks interesting.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729017519, "label": "Add template block prior to extra URL loaders"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-718346019", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 718346019, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODM0NjAxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T04:05:07Z", "updated_at": "2020-10-29T04:05:07Z", "author_association": "OWNER", "body": "Yes, confirmed - this is a bug where if the `BLOB` column contains a `null` you get a nasty exception if you try to download it.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1050#issuecomment-718342036", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1050", "id": 718342036, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODM0MjAzNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T03:49:57Z", "updated_at": "2020-10-29T03:49:57Z", "author_association": "OWNER", "body": "@thadk from that error it looks like the problem may have been that you had a BLOB column containing a `null` value? If so that's definitely a bug, I'll fix that.", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729057388, "label": "Switch to .blob render extension for BLOB downloads"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1049#issuecomment-718340847", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1049", "id": 718340847, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODM0MDg0Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T03:45:47Z", "updated_at": "2020-10-29T03:48:26Z", "author_association": "OWNER", "body": "[thebe](https://thebelab.readthedocs.io/en/latest/examples/minimal_example.html) is the first time I've seen a library that requires you to set up some global JavaScript configuration before loading the script itself.\r\n\r\nI'm hesitant to add an extra template block just to cover that one case since it's such a rare pattern. But it's important that `thebelab` can be used with Datasette.\r\n\r\nWould this pattern work for you instead?\r\n\r\n```html+jinja\r\n{% block extra_head %}\r\n\r\n\r\n{% endblock %}\r\n```\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729017519, "label": "Add template block prior to extra URL loaders"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1049#issuecomment-718341542", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1049", "id": 718341542, "node_id": "MDEyOklzc3VlQ29tbWVudDcxODM0MTU0Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-29T03:48:12Z", "updated_at": "2020-10-29T03:48:12Z", "author_association": "OWNER", "body": "You could use Datasette's new `{{ urls.static_plugins(...) }}` template option - see https://docs.datasette.io/en/latest/internals.html#internals-datasette-urls - to generate a link to code that was bundled with the plugin:\r\n\r\n```html+jinja\r\n{% block extra_head %}\r\n\r\n\r\n{% endblock %}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 729017519, "label": "Add template block prior to extra URL loaders"}, "performed_via_github_app": null}