1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-01 17:38:37 +00:00

feat: add option to always open articles externally

This commit is contained in:
Kelly Norton 2025-05-24 22:46:01 -04:00 committed by GitHub
parent 52b184394f
commit 09fb05aaaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 397 additions and 303 deletions

View file

@ -48,6 +48,11 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
entry.Status = model.EntryStatusRead
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryOrder, user.EntryDirection)
entryPaginationBuilder.WithStarred()
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()

View file

@ -51,6 +51,11 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
entry.Status = model.EntryStatusRead
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryOrder, user.EntryDirection)
entryPaginationBuilder.WithCategoryID(categoryID)
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()

View file

@ -51,6 +51,11 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
entry.Status = model.EntryStatusRead
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryOrder, user.EntryDirection)
entryPaginationBuilder.WithFeedID(feedID)
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()

View file

@ -38,6 +38,11 @@ func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) {
return
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, "changed_at", "desc")
entryPaginationBuilder.WithStatus(model.EntryStatusRead)
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()

View file

@ -50,6 +50,11 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
entry.Status = model.EntryStatusRead
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryOrder, user.EntryDirection)
entryPaginationBuilder.WithSearchQuery(searchQuery)
prevEntry, nextEntry, err := entryPaginationBuilder.Entries()

View file

@ -79,6 +79,11 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
}
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
sess := session.New(h.store, request.SessionID(r))
view := view.New(h.tpl, r, sess)
view.Set("entry", entry)

View file

@ -48,10 +48,11 @@ type SettingsForm struct {
CategoriesSortingOrder string
MarkReadOnView bool
// MarkReadBehavior is a string representation of the MarkReadOnView and MarkReadOnMediaPlayerCompletion fields together
MarkReadBehavior MarkReadBehavior
MediaPlaybackRate float64
BlockFilterEntryRules string
KeepFilterEntryRules string
MarkReadBehavior MarkReadBehavior
MediaPlaybackRate float64
BlockFilterEntryRules string
KeepFilterEntryRules string
AlwaysOpenExternalLinks bool
}
// MarkAsReadBehavior returns the MarkReadBehavior from the given MarkReadOnView and MarkReadOnMediaPlayerCompletion values.
@ -114,6 +115,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
user.MediaPlaybackRate = s.MediaPlaybackRate
user.BlockFilterEntryRules = s.BlockFilterEntryRules
user.KeepFilterEntryRules = s.KeepFilterEntryRules
user.AlwaysOpenExternalLinks = s.AlwaysOpenExternalLinks
MarkReadOnView, MarkReadOnMediaPlayerCompletion := ExtractMarkAsReadBehavior(s.MarkReadBehavior)
user.MarkReadOnView = MarkReadOnView
@ -179,31 +181,32 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
mediaPlaybackRate = 1
}
return &SettingsForm{
Username: r.FormValue("username"),
Password: r.FormValue("password"),
Confirmation: r.FormValue("confirmation"),
Theme: r.FormValue("theme"),
Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"),
EntryDirection: r.FormValue("entry_direction"),
EntryOrder: r.FormValue("entry_order"),
EntriesPerPage: int(entriesPerPage),
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
ShowReadingTime: r.FormValue("show_reading_time") == "1",
CustomCSS: r.FormValue("custom_css"),
CustomJS: r.FormValue("custom_js"),
ExternalFontHosts: r.FormValue("external_font_hosts"),
EntrySwipe: r.FormValue("entry_swipe") == "1",
GestureNav: r.FormValue("gesture_nav"),
DisplayMode: r.FormValue("display_mode"),
DefaultReadingSpeed: int(defaultReadingSpeed),
CJKReadingSpeed: int(cjkReadingSpeed),
DefaultHomePage: r.FormValue("default_home_page"),
CategoriesSortingOrder: r.FormValue("categories_sorting_order"),
MarkReadOnView: r.FormValue("mark_read_on_view") == "1",
MarkReadBehavior: MarkReadBehavior(r.FormValue("mark_read_behavior")),
MediaPlaybackRate: mediaPlaybackRate,
BlockFilterEntryRules: r.FormValue("block_filter_entry_rules"),
KeepFilterEntryRules: r.FormValue("keep_filter_entry_rules"),
Username: r.FormValue("username"),
Password: r.FormValue("password"),
Confirmation: r.FormValue("confirmation"),
Theme: r.FormValue("theme"),
Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"),
EntryDirection: r.FormValue("entry_direction"),
EntryOrder: r.FormValue("entry_order"),
EntriesPerPage: int(entriesPerPage),
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
ShowReadingTime: r.FormValue("show_reading_time") == "1",
CustomCSS: r.FormValue("custom_css"),
CustomJS: r.FormValue("custom_js"),
ExternalFontHosts: r.FormValue("external_font_hosts"),
EntrySwipe: r.FormValue("entry_swipe") == "1",
GestureNav: r.FormValue("gesture_nav"),
DisplayMode: r.FormValue("display_mode"),
DefaultReadingSpeed: int(defaultReadingSpeed),
CJKReadingSpeed: int(cjkReadingSpeed),
DefaultHomePage: r.FormValue("default_home_page"),
CategoriesSortingOrder: r.FormValue("categories_sorting_order"),
MarkReadOnView: r.FormValue("mark_read_on_view") == "1",
MarkReadBehavior: MarkReadBehavior(r.FormValue("mark_read_behavior")),
MediaPlaybackRate: mediaPlaybackRate,
BlockFilterEntryRules: r.FormValue("block_filter_entry_rules"),
KeepFilterEntryRules: r.FormValue("keep_filter_entry_rules"),
AlwaysOpenExternalLinks: r.FormValue("always_open_external_links") == "1",
}
}

View file

@ -9,20 +9,21 @@ import (
func TestValid(t *testing.T) {
settings := &SettingsForm{
Username: "user",
Password: "hunter2",
Confirmation: "hunter2",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
Username: "user",
Password: "hunter2",
Confirmation: "hunter2",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
AlwaysOpenExternalLinks: true,
}
err := settings.Validate()
@ -33,20 +34,21 @@ func TestValid(t *testing.T) {
func TestConfirmationEmpty(t *testing.T) {
settings := &SettingsForm{
Username: "user",
Password: "hunter2",
Confirmation: "",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
Username: "user",
Password: "hunter2",
Confirmation: "",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
AlwaysOpenExternalLinks: true,
}
err := settings.Validate()
@ -61,20 +63,21 @@ func TestConfirmationEmpty(t *testing.T) {
func TestConfirmationIncorrect(t *testing.T) {
settings := &SettingsForm{
Username: "user",
Password: "hunter2",
Confirmation: "unter2",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
Username: "user",
Password: "hunter2",
Confirmation: "unter2",
Theme: "default",
Language: "en_US",
Timezone: "UTC",
EntryDirection: "asc",
EntriesPerPage: 50,
DisplayMode: "standalone",
GestureNav: "tap",
DefaultReadingSpeed: 35,
CJKReadingSpeed: 25,
DefaultHomePage: "unread",
MediaPlaybackRate: 1.25,
AlwaysOpenExternalLinks: true,
}
err := settings.Validate()

View file

@ -23,29 +23,30 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
}
settingsForm := form.SettingsForm{
Username: user.Username,
Theme: user.Theme,
Language: user.Language,
Timezone: user.Timezone,
EntryDirection: user.EntryDirection,
EntryOrder: user.EntryOrder,
EntriesPerPage: user.EntriesPerPage,
KeyboardShortcuts: user.KeyboardShortcuts,
ShowReadingTime: user.ShowReadingTime,
CustomCSS: user.Stylesheet,
CustomJS: user.CustomJS,
ExternalFontHosts: user.ExternalFontHosts,
EntrySwipe: user.EntrySwipe,
GestureNav: user.GestureNav,
DisplayMode: user.DisplayMode,
DefaultReadingSpeed: user.DefaultReadingSpeed,
CJKReadingSpeed: user.CJKReadingSpeed,
DefaultHomePage: user.DefaultHomePage,
CategoriesSortingOrder: user.CategoriesSortingOrder,
MarkReadBehavior: form.MarkAsReadBehavior(user.MarkReadOnView, user.MarkReadOnMediaPlayerCompletion),
MediaPlaybackRate: user.MediaPlaybackRate,
BlockFilterEntryRules: user.BlockFilterEntryRules,
KeepFilterEntryRules: user.KeepFilterEntryRules,
Username: user.Username,
Theme: user.Theme,
Language: user.Language,
Timezone: user.Timezone,
EntryDirection: user.EntryDirection,
EntryOrder: user.EntryOrder,
EntriesPerPage: user.EntriesPerPage,
KeyboardShortcuts: user.KeyboardShortcuts,
ShowReadingTime: user.ShowReadingTime,
CustomCSS: user.Stylesheet,
CustomJS: user.CustomJS,
ExternalFontHosts: user.ExternalFontHosts,
EntrySwipe: user.EntrySwipe,
GestureNav: user.GestureNav,
DisplayMode: user.DisplayMode,
DefaultReadingSpeed: user.DefaultReadingSpeed,
CJKReadingSpeed: user.CJKReadingSpeed,
DefaultHomePage: user.DefaultHomePage,
CategoriesSortingOrder: user.CategoriesSortingOrder,
MarkReadBehavior: form.MarkAsReadBehavior(user.MarkReadOnView, user.MarkReadOnMediaPlayerCompletion),
MediaPlaybackRate: user.MediaPlaybackRate,
BlockFilterEntryRules: user.BlockFilterEntryRules,
KeepFilterEntryRules: user.KeepFilterEntryRules,
AlwaysOpenExternalLinks: user.AlwaysOpenExternalLinks,
}
timezones, err := h.store.Timezones()

View file

@ -88,6 +88,11 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
}
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
sess := session.New(h.store, request.SessionID(r))
view := view.New(h.tpl, r, sess)
view.Set("entry", entry)

View file

@ -88,6 +88,11 @@ func (h *handler) showUnreadFeedEntryPage(w http.ResponseWriter, r *http.Request
}
}
if user.AlwaysOpenExternalLinks {
html.Redirect(w, r, entry.URL)
return
}
sess := session.New(h.store, request.SessionID(r))
view := view.New(h.tpl, r, sess)
view.Set("entry", entry)