Skip to content

fix(dashboard): display dashboard images configured via external URL link - #313

Merged
deaflynx merged 10 commits into
thingsboard:develop/1.9.0from
deaflynx:fix/dashboard-image-external-url-link
Sep 8, 2026
Merged

fix(dashboard): display dashboard images configured via external URL link#313
deaflynx merged 10 commits into
thingsboard:develop/1.9.0from
deaflynx:fix/dashboard-image-external-url-link

Conversation

@deaflynx

@deaflynx deaflynx commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Merges the CE fix (thingsboard/flutter_thingsboard_app#249) from ce-origin/fix/dashboard-image-external-url-link, plus one PE-side resolution noted below.

Dashboard cards on the Home grid showed an empty white box instead of the thumbnail when the dashboard image was configured via the "Image link" field. Images uploaded to the Image Gallery displayed fine.

Fixes PROD-8502.

Two distinct inputs produced the same empty box, and both are fixed here:

  1. An external web URLhttps://example.com/pic.png
  2. An image public link copied from the gallery/api/images/public/{key}, found while reviewing (1) and reproduced on a device

Root cause

The platform stores every dashboard image value with a tb-image; prefix, external URLs included (prependTbImagePrefix in gallery-image-input.component.ts). Utils.imageFromTbImage computed the prefix-stripped URL but only used it in the gallery-resource branch, while the base64 and external-URL branches checked and loaded the raw prefixed string. Uri.tryParse('tb-image;https://…') returns null, so external links fell through to the error placeholder — a transparent 1×1 GIF stretched by the card's FittedBox, which renders as the empty white box.

The public-link case has a separate cause. TbResourceInfo.getPublicLink() returns a relative path, the "Embed image" dialog hands users exactly that string, and the "Image link" field has no validators. IMAGES_URL_REGEXP matches only tenant|system, so a public link is classified as external, and _isValidUrl — which was just Uri.tryParse(url) != null and therefore true for almost any string — let the relative path reach Image.network, where it cannot be fetched at all (ArgumentError: No host specified in URI).

Changes

  • Use the prefix-stripped URL in every branch, and rename it to resolvedImageUrl to match ImageService.resolveImageUrl on the web side.
  • Anchor the tb-image; strip with startsWith/substring instead of replaceFirst, so an occurrence inside a link is no longer removed.
  • Replace _isValidUrl with _resolveNetworkImageLink, which classifies and resolves in one step: absolute http/https links are fetched as they are, platform-relative links resolve against the active endpoint the way a browser resolves them against its origin, and anything without a meaningful target renders the missing image. Resolved relative links carry no auth header, matching the platform — /api/images/public/{key} is a noauth endpoint and a browser <img src> does not send the JWT either.

Since imageFromTbImage also backs device and device-profile images, this repairs image links there too, not only the dashboard grid.

Behaviour

Value in "Image link" Result
https://example.com/pic.png fetched as is
tb-image;https://example.com/pic.png prefix stripped, fetched
/api/images/tenant/pic.png endpoint + path, with auth header
/api/images/public/pic.png endpoint + path, no auth header
data:image/png;base64,… decoded in memory
ftp://…, or text that is not a link missing image placeholder

Test plan

  • Dashboard image set via an external URL → thumbnail displays on the Home grid
  • Dashboard image set to an image public link copied from the gallery Embed dialog → thumbnail displays
  • Dashboard image uploaded to the Image Gallery → still displays
  • Dashboard without an image → placeholder icon still displays
  • Device and device profile images still display

PE-specific note

The merge conflicted in lib/utils/utils.dart, in the image-resource branch that PE rewrote for the whitelabel login logo. Resolved by keeping the PE loginLogo structure and taking the rename to resolvedImageUrl.

That resolution also brings PE's _isImageResourceUrl check onto the prefix-stripped value, where it was previously testing the still-prefixed string. Behaviour is unchanged today, because _imagesUrlRegexp is unanchored and matches with or without the prefix — but it puts PE in line with CE and is a prerequisite for anchoring that pattern, which would otherwise classify every gallery image as external in PE.

deaflynx and others added 4 commits July 2, 2026 18:26
@deaflynx deaflynx added the bug Something isn't working label Aug 20, 2026
@deaflynx deaflynx added this to the 1.9.0 milestone Aug 20, 2026
Address review feedback on PR thingsboard#249:

- use the prefix-stripped url consistently and rename it to
  resolvedImageUrl, matching ImageService.resolveImageUrl on the web
- anchor the tb-image prefix strip so an occurrence inside a link is kept
- replace _isValidUrl, which accepted almost any string, with
  _resolveNetworkImageLink: absolute http/https links are fetched as
  they are, platform-relative links such as an image public link are
  resolved against the active endpoint the way a browser resolves them
  against its origin, and anything else renders the missing image
…url-link' into fix/dashboard-image-external-url-link

Conflict in lib/utils/utils.dart resolved by keeping the PE whitelabel
login logo branch and taking the rename to resolvedImageUrl, which also
brings the image resource check onto the prefix-stripped value.
…url-link' into fix/dashboard-image-external-url-link
@deaflynx
deaflynx merged commit d46046a into thingsboard:develop/1.9.0 Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant