Repository: jarun/buku

Issue:
[Bukuserver] Non-netloc URLs are hidden entirely unless render mode is 'full'
In cases when the URL has no hostname (e.g. `file:`, `data:` or `javascript:` URLs), the only value of `BUKUSERVER_URL_RENDER_MODE` which permits displaying them in any way or form is `full`.

Suggested solution: render the URL instead of "&lt;EMPTY TITLE&gt;" in such cases.

<details><summary><h3>Examples</h3></summary>

(note the two `javascript:` URLs)

`BUKUSERVER_URL_RENDER_MODE=full`
![full](https://github.com/user-attachments/assets/fb8d731a-5817-40d2-8d65-5120704a9162 "full")

`BUKUSERVER_URL_RENDER_MODE=netloc-tag`
![netloc-tag](https://github.com/user-attachments/assets/39784125-e501-43c4-b434-182439e41996 "netloc-tag")

`BUKUSERVER_URL_RENDER_MODE=netloc`
![netloc](https://github.com/user-attachments/assets/2b209933-bd32-42f9-98b2-fecc69f2f89d "netloc")
</details>


Discussion:
👍 
Upd: forgot about URLs _with_ titles… the issue applies to them as well 😅

`BUKUSERVER_URL_RENDER_MODE=full`
![full](https://github.com/user-attachments/assets/b6bb495d-b08e-4a19-9fc7-395657a69aba "full")
`BUKUSERVER_URL_RENDER_MODE=netloc-tag`
![netloc-tag](https://github.com/user-attachments/assets/4a4ce795-204f-4d6f-876d-2893f49ab4b2 "netloc-tag")
`BUKUSERVER_URL_RENDER_MODE=netloc`
![netloc](https://github.com/user-attachments/assets/a8b7bcba-2dd0-497f-b635-079709d6b9cd "netloc")

When a title is present, the "&lt;EMPTY TITLE&gt;" fix cannot be applied; so in these cases we need to either drop the title, ignore render mode, or render the title as a regular link (but without a netloc).

I'm thinking the latter approach might be a better choice, since it's the closest to the behaviour I suggested for cases without a title (and still roughly matches the configured setting without discarding the title) 🤔

Alternatively this behaviour can be applied with empty titles as well (i.e. render title or "&lt;EMPTY TITLE&gt;" as a hyperlink, just without a netloc tag/link… and I guess the URL can be also added as a tooltip?)



Patch:
diff --git a/bukuserver/views.py b/bukuserver/views.py
index df68c7f9..979b2ef0 100644
--- a/bukuserver/views.py
+++ b/bukuserver/views.py
@@ -127,10 +127,9 @@ def _list_entry(self, context: Any, model: Namespace, name: str) -> Markup:
         url_for_index_view_netloc = None
         if netloc:
             url_for_index_view_netloc = get_index_view_url(flt0_url_netloc_match=netloc)
-        if netloc and parsed_url.scheme:
-            res += [f'<span class="title">{link(title, model.url, new_tab=new_tab)}</span>']
-        else:
-            res += [f'<span class="title">{escape(title)}</span>']
+        _title = (escape(title) if self.url_render_mode == 'full' and (not netloc or not parsed_url.scheme) else
+                  link(title, model.url, new_tab=new_tab))
+        res += [f'<span class="title" title="{model.url}">{_title}</span>']
         if self.url_render_mode == 'netloc' and url_for_index_view_netloc:
             res += [f'<span class="netloc"> ({link(netloc, url_for_index_view_netloc)})</span>']
         if not parsed_url.scheme:


Test patch:
diff --git a/tests/test_views.py b/tests/test_views.py
index 4e8ff6ab..ee0d5740 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -302,7 +302,19 @@ def test_env_per_page(bukudb, app, client, total, per_page, pages, last_page):
 @pytest.mark.parametrize('favicons', [False, True, None])
 @pytest.mark.parametrize('mode', ['full', 'netloc', 'netloc-tag', None])
 def test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab):
-    url, netloc, title, desc, tags = 'http://example.com', 'example.com', 'Sample site', 'Foo bar baz', ',bar,baz,foo,'
+    _test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab, 'http://example.com', 'example.com', 'Sample site')
+
+@pytest.mark.parametrize('url, netloc, title', [
+    ('http://example.com', 'example.com', ''),
+    ('javascript:void(0)', '', 'Sample site'),
+    ('javascript:void(0)', '', ''),
+])
+@pytest.mark.parametrize('mode', ['full', 'netloc', 'netloc-tag'])
+def test_env_entry_render_params_blanks(bukudb, app, client, mode, url, netloc, title):
+    _test_env_entry_render_params(bukudb, app, client, mode, True, True, url, netloc, title)
+
+def _test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab, url, netloc, title):
+    desc, tags = 'Foo bar baz', ',bar,baz,foo,'
     _add_rec(bukudb, url, title, tags, desc)
     _tags = tags.strip(',').split(',')
     if mode:
@@ -315,14 +327,17 @@ def test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab):
     dom = assert_response(client.get('/bookmark/'), '/bookmark/')
     cell = ' '.join(etree.tostring(dom.xpath(f'//td{xpath_cls("col-entry")}')[0], encoding='unicode').strip().split())
     target = '' if not new_tab else ' target="_blank"'
-    icon = '' if not favicons else f'<img class="favicon" src="http://www.google.com/s2/favicons?domain={netloc}"/> '
-    prefix = f'<td class="col-entry"> {icon}<span class="title"><a href="{url}"{target}>{title}</a></span>'
+    icon = '' if not favicons else (netloc and f'<img class="favicon" src="http://www.google.com/s2/favicons?domain={netloc}"/> ')
+    urltext = title or '&lt;EMPTY TITLE&gt;'
+    _title = (urltext if not netloc and mode in ('full', None) else f'<a href="{url}"{target}>{urltext}</a>')
+    prefix = f'<td class="col-entry"> {icon}<span class="title" title="{url}">{_title}</span>'
     tags = [f'<a class="btn label label-default" href="/bookmark/?flt0_tags_contain={s}">{s}</a>' for s in _tags]
-    netloc_tag = ('' if mode == 'netloc' else
+    netloc_tag = ('' if mode == 'netloc' or not netloc else
                   f'<a class="btn label label-success" href="/bookmark/?flt0_url_netloc_match={netloc}">netloc:{netloc}</a>')
     suffix = f'<div class="tag-list">{netloc_tag}{"".join(tags)}</div><div class="description">{desc}</div> </td>'
     if mode == 'netloc':
-        assert cell == f'{prefix}<span class="netloc"> (<a href="/bookmark/?flt0_url_netloc_match={netloc}">{netloc}</a>)</span>{suffix}'
+        _netloc = netloc and f'<span class="netloc"> (<a href="/bookmark/?flt0_url_netloc_match={netloc}">{netloc}</a>)</span>'
+        assert cell == prefix + _netloc + suffix
     elif mode == 'netloc-tag':
         assert cell == prefix + suffix
     else: