1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-21 18:11:09 +00:00

feat(ui): add user setting to control target="_blank" on links

Rationale: Opening links in the current tab is the default browser behavior.

Using `target="_blank"` on external links can lead to accessibility issues and override user preferences. It may also interfere with assistive technologies and expected browser behavior.

To maintain backward compatibility, this option is enabled by default (`true`), which adds `target="_blank"` to links.
This commit is contained in:
Frédéric Guillot 2025-06-08 20:47:57 -07:00
parent 699deea72c
commit 8db637cb39
39 changed files with 345 additions and 259 deletions

View file

@ -37,7 +37,7 @@
<div class="item-meta">
<ul class="item-meta-info">
<li class="item-meta-info-site-url" dir="auto">
<a href="{{ .SiteURL | safeURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="{{ $.user.MarkReadOnView }}">{{ domain .SiteURL }}</a>
<a href="{{ .SiteURL | safeURL }}" title="{{ .SiteURL }}" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }} rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="{{ $.user.MarkReadOnView }}">{{ domain .SiteURL }}</a>
</li>
<li class="item-meta-info-checked-at">
{{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>

View file

@ -43,7 +43,7 @@
<a href="{{ route "sharedEntry" "shareCode" .entry.ShareCode }}"
aria-describedby="entry-title-{{ .entry.ID }}"
title="{{ t "entry.shared_entry.title" }}"
target="_blank">{{ icon "share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>{{ icon "share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
</li>
<li class="item-meta-icons-delete">
<button
@ -71,7 +71,7 @@
<li class="item-meta-icons-external-url">
<a href="{{ .entry.URL | safeURL }}"
aria-describedby="entry-title-{{ .entry.ID }}"
target="_blank"
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-original-link="{{ .user.MarkReadOnView }}">{{ icon "external-link" }}<span class="icon-label">{{ t "entry.external_link.label" }}</span></a>
@ -81,7 +81,7 @@
<a href="{{ .entry.CommentsURL | safeURL }}"
aria-describedby="entry-title-{{ .entry.ID }}"
title="{{ t "entry.comments.title" }}"
target="_blank"
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-comments-link="true">{{ icon "comment" }}<span class="icon-label">{{ t "entry.comments.label" }}</span></a>

View file

@ -66,7 +66,7 @@
{{ t "form.feed.label.scraper_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#scraper-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#scraper-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -77,7 +77,7 @@
{{ t "form.feed.label.rewrite_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -87,7 +87,7 @@
{{ t "form.feed.label.blocklist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#feed-filtering-rules" target="_blank">
<a href=" https://miniflux.app/docs/rules.html#feed-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -98,7 +98,7 @@
{{ t "form.feed.label.keeplist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#feed-filtering-rules" target="_blank">
<a href=" https://miniflux.app/docs/rules.html#feed-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -109,7 +109,7 @@
{{ t "form.feed.label.urlrewrite_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#rewriteurl-rules" target="_blank">
<a href=" https://miniflux.app/docs/rules.html#rewriteurl-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>

View file

@ -39,7 +39,7 @@
{{ range .subscriptions }}
<div class="radio-group">
<label title="{{ .URL | safeURL }}"><input type="radio" name="url" value="{{ .URL | safeURL }}"> {{ .Title }}</label> ({{ .Type }})
<small title="Type = {{ .Type }}"><a href="{{ .URL | safeURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a></small>
<small title="Type = {{ .Type }}"><a href="{{ .URL | safeURL }}" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }} rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a></small>
</div>
{{ end }}

View file

@ -124,7 +124,7 @@
{{ t "form.feed.label.scraper_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#scraper-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#scraper-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -135,7 +135,7 @@
{{ t "form.feed.label.rewrite_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -145,7 +145,7 @@
{{ t "form.feed.label.blocklist_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#feed-filtering-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#feed-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -156,7 +156,7 @@
{{ t "form.feed.label.keeplist_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#feed-filtering-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#feed-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -167,7 +167,7 @@
{{ t "form.feed.label.urlrewrite_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#rewriteurl-rules" target="_blank">
<a href="https://miniflux.app/docs/rules.html#rewriteurl-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -205,7 +205,7 @@
{{ t "form.feed.label.ntfy_priority" }}
</label>
&nbsp;
<a href="https://docs.ntfy.sh/publish/#message-priority" target="_blank">
<a href="https://docs.ntfy.sh/publish/#message-priority" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -226,7 +226,7 @@
{{ t "form.feed.label.pushover_priority" }}
</label>
&nbsp;
<a href="https://pushover.net/api#priority" target="_blank">
<a href="https://pushover.net/api#priority" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>

View file

@ -4,7 +4,7 @@
<section class="entry" data-id="{{ .entry.ID }}" aria-labelledby="page-header-title">
<header class="entry-header">
<h1 id="page-header-title" dir="auto">
<a href="{{ .entry.URL | safeURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .entry.Title }}</a>
<a href="{{ .entry.URL | safeURL }}" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }} rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .entry.Title }}</a>
</h1>
{{ if .user }}
<div class="entry-actions">
@ -53,7 +53,7 @@
<a href="{{ route "sharedEntry" "shareCode" .entry.ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
data-share-status="shared"
target="_blank">{{ icon "share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>{{ icon "share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
</li>
<li>
<button
@ -78,7 +78,7 @@
<li>
<a href="{{ .entry.URL | safeURL }}"
class="page-link"
target="_blank"
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-original-link="{{ .user.MarkReadOnView }}">{{ icon "external-link" }}<span class="icon-label">{{ t "entry.external_link.label" }}</span></a>
@ -97,7 +97,7 @@
<a href="{{ .entry.CommentsURL | safeURL }}"
class="page-link"
title="{{ t "entry.comments.title" }}"
target="_blank"
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-comments-link="true"
@ -267,7 +267,7 @@
{{ end }}
<div class="entry-enclosure-download">
<a href="{{ .URL | safeURL }}" title="{{ t "action.download" }}{{ if gt .Size 0 }} - {{ formatFileSize .Size }}{{ end }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a>
<a href="{{ .URL | safeURL }}" title="{{ t "action.download" }}{{ if gt .Size 0 }} - {{ formatFileSize .Size }}{{ end }}" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }} rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a>
<small>{{ if gt .Size 0 }} - <strong>{{ formatFileSize .Size }}</strong>{{ end }}</small>
</div>
</div>

View file

@ -3,7 +3,7 @@
{{ define "page_header"}}
<section class="page-header" aria-labelledby="page-header-title">
<h1 id="page-header-title" dir="auto">
<a href="{{ .feed.SiteURL | safeURL }}" title="{{ .feed.SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="{{ .user.MarkReadOnView }}">{{ .feed.Title }}</a>
<a href="{{ .feed.SiteURL | safeURL }}" title="{{ .feed.SiteURL }}" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }} rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="{{ .user.MarkReadOnView }}">{{ .feed.Title }}</a>
<span aria-hidden="true">({{ .total }})</span>
</h1>
<span class="sr-only">

View file

@ -26,7 +26,7 @@
<input type="url" name="apprise_url" id="form-apprise-url" value="{{ .form.AppriseURL }}" placeholder="http://apprise:8080" spellcheck="false">
<label for="form-apprise-services-urls">{{ t "form.integration.apprise_services_url" }}
<a href="https://github.com/caronc/apprise/wiki" target="_blank">
<a href="https://github.com/caronc/apprise/wiki" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</label>
@ -514,7 +514,7 @@
<label for="form-readwise-api-key">{{ t "form.integration.readwise_api_key" }}</label>
<input type="text" name="readwise_api_key" id="form-readwise-api-key" value="{{ .form.ReadwiseAPIKey }}" spellcheck="false">
<p><a href="https://readwise.io/access_token" target="_blank">{{ t "form.integration.readwise_api_key_link" }}</a></p>
<p><a href="https://readwise.io/access_token" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>{{ t "form.integration.readwise_api_key_link" }}</a></p>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>

View file

@ -156,7 +156,7 @@
<div class="form-label-row">
<label for="form-display-mode">{{ t "form.prefs.label.display_mode" }}</label>
&nbsp;
<a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display" target="_blank">
<a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -209,6 +209,8 @@
<label><input type="checkbox" name="always_open_external_links" value="1" {{ if .form.AlwaysOpenExternalLinks }}checked{{ end }}> {{ t "form.prefs.label.always_open_external_links" }}</label>
<label><input type="checkbox" name="open_external_links_in_new_tab" value="1" {{ if .form.OpenExternalLinksInNewTab }}checked{{ end }}> {{ t "form.prefs.label.open_external_links_in_new_tab" }}</label>
<label for="form-custom-css">{{t "form.prefs.label.custom_css" }}</label>
<textarea id="form-custom-css" name="custom_css" cols="40" rows="10" spellcheck="false">{{ .form.CustomCSS }}</textarea>
@ -231,7 +233,7 @@
{{ t "form.feed.label.blocklist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#global-filtering-rules" target="_blank">
<a href=" https://miniflux.app/docs/rules.html#global-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>
@ -241,7 +243,7 @@
<label for="form-keeplist-rules">
{{ t "form.feed.label.keeplist_rules" }}
</label>
<a href=" https://miniflux.app/docs/rules.html#global-filtering-rules" target="_blank">
<a href=" https://miniflux.app/docs/rules.html#global-filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
{{ icon "external-link" }}
</a>
</div>

View file

@ -55,7 +55,7 @@
{{ if .ShareCode }}
<a href="{{ route "sharedEntry" "shareCode" .ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
target="_blank">{{ icon "share" }}</a>
{{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>{{ icon "share" }}</a>
{{ end }}
</h2>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>