Settings - Plugins: show each plugin logo, as the server page already does #288

Closed
opened 2026-09-19 10:21:36 +00:00 by tiagoagueda · 0 comments
Owner

Server settings → Plugins shows each plugin's logo beside its name. Settings → Plugins — the page a person actually visits to see what is running for them — shows none. The same plugins, listed twice, recognisable on one page and not the other.

A logo is how somebody picks Paperless out of a dozen rows without reading each label. It is also the page's own argument: this is the list of whose code is acting on your behalf, and a mark you recognise is worth more here than on the administrator's page.

Why it is missing

Not a design decision — the row simply never carries the plugin.

plugins/policy.py:246-262 builds each row as a dict of name, label, description, kind, provenance and decision, and the plugin instance is the loop variable that is never passed out. The server's own builder (core/server_views.py:931-933) does pass it, with the reason written down:

“The instance itself, so the template can ask for a logo rather than being handed one it has no way to fall back from (#106).”

So the fix is that key, plus the tag the other page already calls:

  • plugins/policy.py — add "plugin": plugin to the row.
  • templates/settings/plugins.html — {% plugin_logo row.plugin "…" %} in the row, as server/plugins.html:53 does at size-8 text-xs.

{% plugin_logo %} (core/templatetags/postulo.py:271) already handles everything else: a plugin with no logo gets an initials tile rather than a broken image, the tag is decorative by design so it needs no alternative text beside the name it sits next to, and the image is served by this instance — never from whoever wrote the plugin, because an <img> at their server would tell them which instances run their code (#106). logos.png_for is cached by plugin name for the life of the process, so a dozen rows cost one lookup each and no file reads after the first page view.

Points to settle

  • Where in the row. The row is <li class="flex items-start gap-3"> and already opens with a 3.5-unit element: the checkbox, or — for a plugin Postulo ships — a blank <span aria-hidden="true"> holding its place (settings/plugins.html:55-62). The logo is a third thing. Either it goes between that column and the label, or it takes the place of the blank span on shipped rows, which would make those rows read better and the two row shapes diverge further. Worth looking at with the mark ticked, where both shapes are on screen together.
  • Size. size-8 matches the server page; size-6 is the tag's default and may sit better in a row this dense. One glance at both answers it.
  • The touch-target warning right next to where this lands. settings/plugins.html:64 says the label is the target because the checkbox is 13 pixels, and that it passed SC 2.5.8 “only on the spacing exception — these rows are tall and nothing else is near. That is a thin thing to rest on: one more control in the row and it stops being true.” A logo is decorative and not a control, so the exception is not spent — but it is now not the only thing near the checkbox either, and tests/e2e/test_target_size.py measures rather than reads. Check it rather than reasoning about it.

Adjacent, deliberately not in scope

{% plugin_logo %} is used in exactly one template today. Settings → Connections (connections/list.html:33, connections/pick.html:17) names plugins without logos, and pick.html already has entry.plugin in hand. Those are the same one-line change and would finish what #106 started — but they are a separate issue, not a quiet widening of this one.

Notes

  • No migration, no new string, no new dependency.
  • tests/e2e/test_target_size.py and test_reflow.py both visit this page; the row gains width at 320 pixels, which is where #113 and #167 found their problems.
  • The browser suite reads the live tree, so no template edit while it runs; npm run build:css if the row gains classes, since the compiled CSS is committed and CI checks it.
*Server settings → Plugins* shows each plugin's logo beside its name. *Settings → Plugins* — the page a person actually visits to see what is running for them — shows none. The same plugins, listed twice, recognisable on one page and not the other. A logo is how somebody picks *Paperless* out of a dozen rows without reading each label. It is also the page's own argument: this is the list of whose code is acting on your behalf, and a mark you recognise is worth more here than on the administrator's page. ## Why it is missing Not a design decision — the row simply never carries the plugin. `plugins/policy.py:246-262` builds each row as a dict of name, label, description, kind, provenance and decision, and the plugin instance is the loop variable that is never passed out. The server's own builder (`core/server_views.py:931-933`) does pass it, with the reason written down: > *“The instance itself, so the template can ask for a logo rather than being handed one it has no way to fall back from (#106).”* So the fix is that key, plus the tag the other page already calls: - **`plugins/policy.py`** — add `"plugin": plugin` to the row. - **`templates/settings/plugins.html`** — `{% plugin_logo row.plugin "…" %}` in the row, as `server/plugins.html:53` does at `size-8 text-xs`. `{% plugin_logo %}` (`core/templatetags/postulo.py:271`) already handles everything else: a plugin with no logo gets an initials tile rather than a broken image, the tag is decorative by design so it needs no alternative text beside the name it sits next to, and the image is served by **this** instance — never from whoever wrote the plugin, because an `<img>` at their server would tell them which instances run their code (#106). `logos.png_for` is cached by plugin name for the life of the process, so a dozen rows cost one lookup each and no file reads after the first page view. ## Points to settle - **Where in the row.** The row is `<li class="flex items-start gap-3">` and already opens with a 3.5-unit element: the checkbox, or — for a plugin Postulo ships — a blank `<span aria-hidden="true">` holding its place (`settings/plugins.html:55-62`). The logo is a third thing. Either it goes between that column and the label, or it takes the place of the blank span on shipped rows, which would make those rows read better and the two row shapes diverge further. Worth looking at with the mark ticked, where both shapes are on screen together. - **Size.** `size-8` matches the server page; `size-6` is the tag's default and may sit better in a row this dense. One glance at both answers it. - **The touch-target warning right next to where this lands.** `settings/plugins.html:64` says the label is the target because the checkbox is 13 pixels, and that it passed SC 2.5.8 *“only on the spacing exception — these rows are tall and nothing else is near. That is a thin thing to rest on: **one more control in the row and it stops being true.**”* A logo is decorative and not a control, so the exception is not spent — but it is now not the only thing near the checkbox either, and `tests/e2e/test_target_size.py` measures rather than reads. Check it rather than reasoning about it. ## Adjacent, deliberately not in scope `{% plugin_logo %}` is used in **exactly one template** today. *Settings → Connections* (`connections/list.html:33`, `connections/pick.html:17`) names plugins without logos, and `pick.html` already has `entry.plugin` in hand. Those are the same one-line change and would finish what #106 started — but they are a separate issue, not a quiet widening of this one. ## Notes - No migration, no new string, no new dependency. - `tests/e2e/test_target_size.py` and `test_reflow.py` both visit this page; the row gains width at 320 pixels, which is where #113 and #167 found their problems. - The browser suite reads the live tree, so no template edit while it runs; `npm run build:css` if the row gains classes, since the compiled CSS is committed and CI checks it.
tiagoagueda added this to the 0.4.0 milestone 2026-09-19 10:21:36 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#288
No description provided.