Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions datasette/default_column_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from datasette import hookimpl
from datasette.column_types import ColumnType, SQLiteType
from datasette.utils import truncate_url


class UrlColumnType(ColumnType):
Expand All @@ -15,8 +16,12 @@ class UrlColumnType(ColumnType):
async def render_cell(self, value, column, table, database, datasette, request):
if not value or not isinstance(value, str):
return None
escaped = markupsafe.escape(value.strip())
return markupsafe.Markup(f'<a href="{escaped}">{escaped}</a>')
url = value.strip()
escaped = markupsafe.escape(url)
truncated = markupsafe.escape(
truncate_url(url, datasette.setting("truncate_cells_html"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve full URL text on row pages

When a table column is assigned the built-in url column type and truncate_cells_html is nonzero, this now truncates the link text everywhere the column type renderer is used. Individual row HTML pages deliberately call display_columns_and_rows(..., truncate_cells=0) so users can see/copy the full value there, but this line bypasses that per-page setting by reading the global setting directly; long URL values on /db/table/pk pages will now be shortened even though the row-page behavior is supposed to show full cell contents.

Useful? React with 👍 / 👎.

)
return markupsafe.Markup(f'<a href="{escaped}">{truncated}</a>')

async def validate(self, value, datasette):
if value is None or value == "":
Expand Down
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ The edit interface takes :ref:`custom column types <table_configuration_column_t
- Permission checks are now cached on a per-request basis, speeding up table pages with multiple plugins that check permissions in order to populate the :ref:`table actions menu <plugin_hook_table_actions>`.
- Fixed a warning about ``gen.throw(*sys.exc_info())``. (:issue:`2776`)
- New default custom column type ``textarea`` for multi-line text content. This is rendered as a ``<textarea>`` input in the edit UI.
- Links rendered by the ``url`` :ref:`custom column type <table_configuration_column_types>` now have their displayed text truncated according to the :ref:`setting_truncate_cells_html` setting, while still linking to the full URL. (:issue:`1805`)
- The ``json`` column type now implements client-side validation in the edit UI.
- The :ref:`makeColumnField() <javascript_plugins_makeColumnField>` JavaScript plugin hook allows plugins to define custom fields in the edit interface for their custom column types.
- New UI for inserting, editing, and deleting rows within Datasette. (:issue:`2780`)
Expand Down
38 changes: 38 additions & 0 deletions tests/test_column_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,44 @@ async def test_url_render_cell(ds_ct):
assert "https://example.com" in rendered["website"]


@pytest.mark.asyncio
async def test_url_render_cell_truncates(tmp_path_factory):
# truncate_cells_html should also truncate the displayed text of url
# column type links, while keeping the full URL in the href - refs #1805
db_directory = tmp_path_factory.mktemp("dbs")
db_path = str(db_directory / "data.db")
db = sqlite3.connect(db_path)
db.execute("create table posts (id integer primary key, website text)")
long_url = (
"https://images.openfoodfacts.org/images/products/000/000/000/088/"
"nutrition_fr.5.200.jpg"
)
db.execute("insert into posts values (1, ?)", [long_url])
db.commit()
db.close()
ds = Datasette(
[db_path],
settings={"truncate_cells_html": 30},
config={
"databases": {
"data": {"tables": {"posts": {"column_types": {"website": "url"}}}}
}
},
)
await ds.invoke_startup()
response = await ds.client.get("/data/posts.json?_extra=render_cell")
assert response.status_code == 200
rendered = response.json()["render_cell"][0]["website"]
# The full URL is preserved in the href
assert f'href="{long_url}"' in rendered
# ...but the visible link text is truncated
soup = Soup(rendered, "html.parser")
link_text = soup.find("a").text
assert link_text != long_url
assert "…" in link_text
ds.close()


@pytest.mark.asyncio
async def test_email_render_cell(ds_ct):
await ds_ct.invoke_startup()
Expand Down
Loading