From 997077fb45a2a577d08dc2de5f78a6a7afabc4ca Mon Sep 17 00:00:00 2001 From: Developer Date: Tue, 14 Apr 2026 05:14:44 -0700 Subject: [PATCH] Add config setting to disable bulk operations confirmations Add a user preference 'DisableBulkOperationsConfirmations' that when set to true, skips confirmation dialogs for bulk operations like 'mark all as read' and 'mark page as read'. Defaults to false for backward compatibility. Changes: - Model: Add DisableBulkOperationsConfirmations to User and UserModificationRequest - Storage: Update all SQL queries (CreateUser, UpdateUser, UserBy*, fetchUser) - Database: Add migration to add the new column - Form: Add field to SettingsForm, Merge, and NewSettingsForm - Templates: Conditionally render data-confirm attributes in unread_entries, feed_entries, category_entries, and categories templates - Localization: Add English translation string - Tests: Add unit tests for model and form --- internal/database/migrations.go | 4 + internal/locale/translations/ar_SA.json | 1 + internal/locale/translations/de_DE.json | 1 + internal/locale/translations/el_EL.json | 1 + internal/locale/translations/en_US.json | 1 + internal/locale/translations/es_ES.json | 1 + internal/locale/translations/fi_FI.json | 1 + internal/locale/translations/fr_FR.json | 1 + internal/locale/translations/gl_ES.json | 1 + internal/locale/translations/hi_IN.json | 1 + internal/locale/translations/id_ID.json | 1 + internal/locale/translations/it_IT.json | 1 + internal/locale/translations/ja_JP.json | 1 + .../locale/translations/nan_Latn_pehoeji.json | 1 + internal/locale/translations/nl_NL.json | 1 + internal/locale/translations/pl_PL.json | 1 + internal/locale/translations/pt_BR.json | 1 + internal/locale/translations/ro_RO.json | 1 + internal/locale/translations/ru_RU.json | 1 + internal/locale/translations/tr_TR.json | 1 + internal/locale/translations/uk_UA.json | 1 + internal/locale/translations/zh_CN.json | 1 + internal/locale/translations/zh_TW.json | 1 + internal/model/user.go | 14 +- internal/model/user_test.go | 99 +++++++ internal/storage/user.go | 29 +- .../template/templates/views/categories.html | 8 +- .../templates/views/category_entries.html | 20 +- .../templates/views/feed_entries.html | 20 +- .../template/templates/views/settings.html | 2 + .../templates/views/unread_entries.html | 20 +- internal/ui/form/settings.go | 11 +- internal/ui/form/settings_test.go | 103 +++---- internal/ui/settings_show.go | 51 ++-- internal/ui/static/js/app.js | 13 + internal/ui/static/js/app_test.js | 266 ++++++++++++++++++ 36 files changed, 541 insertions(+), 141 deletions(-) create mode 100644 internal/model/user_test.go create mode 100644 internal/ui/static/js/app_test.js diff --git a/internal/database/migrations.go b/internal/database/migrations.go index 548e57061d9..879ba6850e8 100644 --- a/internal/database/migrations.go +++ b/internal/database/migrations.go @@ -1457,4 +1457,8 @@ var migrations = [...]func(tx *sql.Tx) error{ `) return err }, + func(tx *sql.Tx) (err error) { + _, err = tx.Exec(`ALTER TABLE users ADD COLUMN disable_bulk_operations_confirmations bool default 'f'`) + return err + }, } diff --git a/internal/locale/translations/ar_SA.json b/internal/locale/translations/ar_SA.json index 0c8f4d19dec..2291cde8f09 100644 --- a/internal/locale/translations/ar_SA.json +++ b/internal/locale/translations/ar_SA.json @@ -385,6 +385,7 @@ "form.prefs.label.show_reading_time": "إظهار الوقت المقدر للقراءة للمقالات", "form.prefs.label.theme": "السمة", "form.prefs.label.timezone": "المنطقة الزمنية", + "form.prefs.label.disable_bulk_operations_confirmations": "تعطيل التأكيدات للعمليات الجماعية (تعليم الجميع كمقروء، تعليم الصفحة كمقروء)", "form.prefs.select.alphabetical": "أبجدي", "form.prefs.select.browser": "المتصفح", "form.prefs.select.created_time": "وقت إنشاء المقال", diff --git a/internal/locale/translations/de_DE.json b/internal/locale/translations/de_DE.json index bcbd59e2f7a..5d352148680 100644 --- a/internal/locale/translations/de_DE.json +++ b/internal/locale/translations/de_DE.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen", "form.prefs.label.theme": "Thema", "form.prefs.label.timezone": "Zeitzone", + "form.prefs.label.disable_bulk_operations_confirmations": "Bestätigungsaufforderungen für Massenvorgänge deaktivieren (alle als gelesen markieren, Seite als gelesen markieren)", "form.prefs.select.alphabetical": "Alphabetisch", "form.prefs.select.browser": "Systembrowser", "form.prefs.select.created_time": "Artikel erstellt am", diff --git a/internal/locale/translations/el_EL.json b/internal/locale/translations/el_EL.json index 792f57f69b4..e1b8c277555 100644 --- a/internal/locale/translations/el_EL.json +++ b/internal/locale/translations/el_EL.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα", "form.prefs.label.theme": "Θέμα", "form.prefs.label.timezone": "Ζώνη Ώρας", + "form.prefs.label.disable_bulk_operations_confirmations": "Απενεργοποίηση προτροπών επιβεβαίωσης για μαζικές λειτουργίες (σήμανση όλων ως διαβασμένα, σήμανση σελίδας ως διαβασμένη)", "form.prefs.select.alphabetical": "Αλφαβητική σειρά", "form.prefs.select.browser": "Περιηγητής", "form.prefs.select.created_time": "Χρόνος δημιουργίας καταχώρησης", diff --git a/internal/locale/translations/en_US.json b/internal/locale/translations/en_US.json index c9198d3718b..0f712dbc299 100644 --- a/internal/locale/translations/en_US.json +++ b/internal/locale/translations/en_US.json @@ -370,6 +370,7 @@ "form.prefs.label.mark_read_on_view_or_media_completion": "Mark entries as read when viewed. For audio/video, mark as read at 90%% completion", "form.prefs.label.media_playback_rate": "Playback speed of the audio/video", "form.prefs.label.open_external_links_in_new_tab": "Open external links in a new tab (adds target=\"_blank\" to links)", + "form.prefs.label.disable_bulk_operations_confirmations": "Disable confirmation prompts for bulk operations (mark all as read, mark page as read)", "form.prefs.label.show_reading_time": "Show estimated reading time for entries", "form.prefs.label.theme": "Theme", "form.prefs.label.timezone": "Timezone", diff --git a/internal/locale/translations/es_ES.json b/internal/locale/translations/es_ES.json index 80691d3af30..3bc648ec238 100644 --- a/internal/locale/translations/es_ES.json +++ b/internal/locale/translations/es_ES.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos", "form.prefs.label.theme": "Tema", "form.prefs.label.timezone": "Zona horaria", + "form.prefs.label.disable_bulk_operations_confirmations": "Deshabilitar mensajes de confirmación para operaciones masivas (marcar todo como leído, marcar página como leída)", "form.prefs.select.alphabetical": "Alfabético", "form.prefs.select.browser": "Navegador", "form.prefs.select.created_time": "Hora de creación del artículo", diff --git a/internal/locale/translations/fi_FI.json b/internal/locale/translations/fi_FI.json index 4784663f80e..a1fe0ad52d4 100644 --- a/internal/locale/translations/fi_FI.json +++ b/internal/locale/translations/fi_FI.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Näytä artikkeleiden arvioitu lukuaika", "form.prefs.label.theme": "Teema", "form.prefs.label.timezone": "Aikavyöhyke", + "form.prefs.label.disable_bulk_operations_confirmations": "Poista vahvistuskehotteet massatoiminnoista (merkitse kaikki luetuiksi, merkitse sivu luetuksi)", "form.prefs.select.alphabetical": "Aakkosjärjestys", "form.prefs.select.browser": "Selain", "form.prefs.select.created_time": "Luomisaika", diff --git a/internal/locale/translations/fr_FR.json b/internal/locale/translations/fr_FR.json index 7723d021c1a..1faf216bdc3 100644 --- a/internal/locale/translations/fr_FR.json +++ b/internal/locale/translations/fr_FR.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Afficher le temps de lecture estimé des articles", "form.prefs.label.theme": "Thème", "form.prefs.label.timezone": "Fuseau horaire", + "form.prefs.label.disable_bulk_operations_confirmations": "Désactiver les invites de confirmation pour les opérations en masse (marquer tout comme lu, marquer la page comme lue)", "form.prefs.select.alphabetical": "Alphabétique", "form.prefs.select.browser": "Navigateur", "form.prefs.select.created_time": "Heure de création de l'entrée", diff --git a/internal/locale/translations/gl_ES.json b/internal/locale/translations/gl_ES.json index e16b07fb4f2..9de7d28c423 100644 --- a/internal/locale/translations/gl_ES.json +++ b/internal/locale/translations/gl_ES.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Mostrar tempo de lectura estimado para as entradas", "form.prefs.label.theme": "Decorado", "form.prefs.label.timezone": "Zona horaria", + "form.prefs.label.disable_bulk_operations_confirmations": "Desactivar mensaxes de confirmación para operacións masivas (marcar todo como lido, marcar páxina como lida)", "form.prefs.select.alphabetical": "Alfabética", "form.prefs.select.browser": "Navegador", "form.prefs.select.created_time": "Hora de creación da entrada", diff --git a/internal/locale/translations/hi_IN.json b/internal/locale/translations/hi_IN.json index 89613e8b8d6..367181e429d 100644 --- a/internal/locale/translations/hi_IN.json +++ b/internal/locale/translations/hi_IN.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "विषय के लिए अनुमानित पढ़ने का समय दिखाएं", "form.prefs.label.theme": "थीम", "form.prefs.label.timezone": "समय क्षेत्र", + "form.prefs.label.disable_bulk_operations_confirmations": "ब्यूक संचालनों के लिए पुष्टि स्वीकृत अस्वीकार करें (सभी को पढ़ा हुआ चिह्नित करें, पृष्ठ को पढ़ा हुआ चिह्नित करें)", "form.prefs.select.alphabetical": "वर्णक्रम", "form.prefs.select.browser": "ब्राउज़र", "form.prefs.select.created_time": "प्रवेश बनाया समय", diff --git a/internal/locale/translations/id_ID.json b/internal/locale/translations/id_ID.json index b6e104224ff..00f5a7bff0e 100644 --- a/internal/locale/translations/id_ID.json +++ b/internal/locale/translations/id_ID.json @@ -370,6 +370,7 @@ "form.prefs.label.show_reading_time": "Tampilkan perkiraan waktu baca untuk artikel", "form.prefs.label.theme": "Tema", "form.prefs.label.timezone": "Zona Waktu", + "form.prefs.label.disable_bulk_operations_confirmations": "Nonaktifkan konfirmasi untuk operasi massal (tandai semua sebagai dibaca, tandai halaman sebagai dibaca)", "form.prefs.select.alphabetical": "Secara alfabet", "form.prefs.select.browser": "Peramban", "form.prefs.select.created_time": "Waktu entri dibuat", diff --git a/internal/locale/translations/it_IT.json b/internal/locale/translations/it_IT.json index 6dcc0f7ff56..f5d7bf1dc82 100644 --- a/internal/locale/translations/it_IT.json +++ b/internal/locale/translations/it_IT.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli", "form.prefs.label.theme": "Tema", "form.prefs.label.timezone": "Fuso orario", + "form.prefs.label.disable_bulk_operations_confirmations": "Disabilita le richieste di conferma per le operazioni in blocco (segna tutto come letto, segna pagina come letta)", "form.prefs.select.alphabetical": "In ordine alfabetico", "form.prefs.select.browser": "Browser", "form.prefs.select.created_time": "Tempo di creazione dell'entrata", diff --git a/internal/locale/translations/ja_JP.json b/internal/locale/translations/ja_JP.json index f2d8f523a4a..ffd1c1303ad 100644 --- a/internal/locale/translations/ja_JP.json +++ b/internal/locale/translations/ja_JP.json @@ -370,6 +370,7 @@ "form.prefs.label.show_reading_time": "記事の推定読書時間を表示する", "form.prefs.label.theme": "テーマ", "form.prefs.label.timezone": "タイムゾーン", + "form.prefs.label.disable_bulk_operations_confirmations": "一括操作の確認ダイアログを無効化する(全て既読にする、ページを既読にする)", "form.prefs.select.alphabetical": "アルファベット順", "form.prefs.select.browser": "ブラウザ", "form.prefs.select.created_time": "記事の取得時刻", diff --git a/internal/locale/translations/nan_Latn_pehoeji.json b/internal/locale/translations/nan_Latn_pehoeji.json index c307b67e8d8..96805438827 100644 --- a/internal/locale/translations/nan_Latn_pehoeji.json +++ b/internal/locale/translations/nan_Latn_pehoeji.json @@ -370,6 +370,7 @@ "form.prefs.label.show_reading_time": "Hián-sī siau-sit àn-sǹg ài gōa-kú lâi tha̍k", "form.prefs.label.theme": "Chú-tôe", "form.prefs.label.timezone": "Sî-khu", + "form.prefs.label.disable_bulk_operations_confirmations": "Hip ê chènghoat chhiam-miâ úi tō-hōn chok-iŭ (àn sóŭ Ů chiút chèn chò hoat-thǎk, àn iáh chiút chèn chò hoat-thǎk)", "form.prefs.select.alphabetical": "Chiàu lī-bú pâi", "form.prefs.select.browser": "Iû-lâm-khì", "form.prefs.select.created_time": "Siau-sit kiàn-li̍p sî-kan", diff --git a/internal/locale/translations/nl_NL.json b/internal/locale/translations/nl_NL.json index baa201904d9..e8da2333f46 100644 --- a/internal/locale/translations/nl_NL.json +++ b/internal/locale/translations/nl_NL.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Toon geschatte leestijd van artikelen", "form.prefs.label.theme": "Thema", "form.prefs.label.timezone": "Tijdzone", + "form.prefs.label.disable_bulk_operations_confirmations": "Bevestigingsprompts uitschakelen voor bulkoperaties (markeer alles als gelezen, markeer pagina als gelezen)", "form.prefs.select.alphabetical": "Alfabetisch", "form.prefs.select.browser": "Systeembrowser", "form.prefs.select.created_time": "Tijdstip van aanmaken artikel", diff --git a/internal/locale/translations/pl_PL.json b/internal/locale/translations/pl_PL.json index fc997a717c9..79b47db513a 100644 --- a/internal/locale/translations/pl_PL.json +++ b/internal/locale/translations/pl_PL.json @@ -376,6 +376,7 @@ "form.prefs.label.show_reading_time": "Pokaż szacowany czas czytania wpisów", "form.prefs.label.theme": "Wygląd", "form.prefs.label.timezone": "Strefa czasowa", + "form.prefs.label.disable_bulk_operations_confirmations": "Wyłącz potwierdzenia dla operacji zbiorczych (oznacz wszystko jako przeczytane, oznacz stronę jako przeczytaną)", "form.prefs.select.alphabetical": "Alfabetycznie", "form.prefs.select.browser": "Przeglądarkowy", "form.prefs.select.created_time": "Czas utworzenia wpisu", diff --git a/internal/locale/translations/pt_BR.json b/internal/locale/translations/pt_BR.json index 448df76638a..ed62dbd390e 100644 --- a/internal/locale/translations/pt_BR.json +++ b/internal/locale/translations/pt_BR.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Mostrar tempo estimado de leitura de artigos", "form.prefs.label.theme": "Tema", "form.prefs.label.timezone": "Fuso horário", + "form.prefs.label.disable_bulk_operations_confirmations": "Desabilitar confirmações para operações em massa (marcar tudo como lido, marcar página como lida)", "form.prefs.select.alphabetical": "Por ordem alfabética", "form.prefs.select.browser": "Navegador", "form.prefs.select.created_time": "Entrada tempo criado", diff --git a/internal/locale/translations/ro_RO.json b/internal/locale/translations/ro_RO.json index bfdeb6e0dbb..ec916bbbf76 100644 --- a/internal/locale/translations/ro_RO.json +++ b/internal/locale/translations/ro_RO.json @@ -376,6 +376,7 @@ "form.prefs.label.show_reading_time": "Afișare timp estimat de citire pentru înregistrări", "form.prefs.label.theme": "Temă", "form.prefs.label.timezone": "Fus orar", + "form.prefs.label.disable_bulk_operations_confirmations": "Dezactivează confirmările pentru operațiunile în masă (marchează tot ca citit, marchează pagina ca citită)", "form.prefs.select.alphabetical": "Alfabetic", "form.prefs.select.browser": "Browser", "form.prefs.select.created_time": "Dată creare înregistrare", diff --git a/internal/locale/translations/ru_RU.json b/internal/locale/translations/ru_RU.json index 285e6ccb9a8..169990a00d7 100644 --- a/internal/locale/translations/ru_RU.json +++ b/internal/locale/translations/ru_RU.json @@ -376,6 +376,7 @@ "form.prefs.label.show_reading_time": "Показать примерное время чтения статей", "form.prefs.label.theme": "Тема", "form.prefs.label.timezone": "Часовой пояс", + "form.prefs.label.disable_bulk_operations_confirmations": "Отключить подтверждения для массовых операций (отметить всё как прочитанное, отметить страницу как прочитанную)", "form.prefs.select.alphabetical": "В алфавитном порядке", "form.prefs.select.browser": "Браузер", "form.prefs.select.created_time": "Время создания статьи", diff --git a/internal/locale/translations/tr_TR.json b/internal/locale/translations/tr_TR.json index 9c07dec6ae2..287c3df068c 100644 --- a/internal/locale/translations/tr_TR.json +++ b/internal/locale/translations/tr_TR.json @@ -373,6 +373,7 @@ "form.prefs.label.show_reading_time": "Makaleler için tahmini okuma süresini göster", "form.prefs.label.theme": "Tema", "form.prefs.label.timezone": "Saat Dilimi", + "form.prefs.label.disable_bulk_operations_confirmations": "Toplu işlemler için onay istemlerini devre dışı bırak (tümünü okundu olarak işaretle, sayfayı okundu olarak işaretle)", "form.prefs.select.alphabetical": "Alfabetik", "form.prefs.select.browser": "Tarayıcı", "form.prefs.select.created_time": "İçeriğin oluşturulma zamanı", diff --git a/internal/locale/translations/uk_UA.json b/internal/locale/translations/uk_UA.json index 47af81260d2..3ac565df340 100644 --- a/internal/locale/translations/uk_UA.json +++ b/internal/locale/translations/uk_UA.json @@ -376,6 +376,7 @@ "form.prefs.label.show_reading_time": "Показувати приблизний час читання для записів", "form.prefs.label.theme": "Тема", "form.prefs.label.timezone": "Часовий пояс", + "form.prefs.label.disable_bulk_operations_confirmations": "Вимкнути підтвердження для масових операцій (позначити все як прочитане, позначити сторінку як прочитану)", "form.prefs.select.alphabetical": "За алфавітом", "form.prefs.select.browser": "Браузер", "form.prefs.select.created_time": "Дата створення запису", diff --git a/internal/locale/translations/zh_CN.json b/internal/locale/translations/zh_CN.json index 23a71d2b543..12323414526 100644 --- a/internal/locale/translations/zh_CN.json +++ b/internal/locale/translations/zh_CN.json @@ -370,6 +370,7 @@ "form.prefs.label.show_reading_time": "显示条目的预计阅读时间", "form.prefs.label.theme": "主题", "form.prefs.label.timezone": "时区", + "form.prefs.label.disable_bulk_operations_confirmations": "Disable confirmation prompts for bulk operations (mark all as read, mark page as read)", "form.prefs.select.alphabetical": "字母顺序", "form.prefs.select.browser": "浏览器", "form.prefs.select.created_time": "条目创建时间", diff --git a/internal/locale/translations/zh_TW.json b/internal/locale/translations/zh_TW.json index 5cb5cca02f1..cecd967c877 100644 --- a/internal/locale/translations/zh_TW.json +++ b/internal/locale/translations/zh_TW.json @@ -370,6 +370,7 @@ "form.prefs.label.show_reading_time": "顯示文章的預計閱讀時間", "form.prefs.label.theme": "主題", "form.prefs.label.timezone": "時區", + "form.prefs.label.disable_bulk_operations_confirmations": "Disable confirmation prompts for bulk operations (mark all as read, mark page as read)", "form.prefs.select.alphabetical": "按字母順序", "form.prefs.select.browser": "瀏覽器", "form.prefs.select.created_time": "文章建立時間", diff --git a/internal/model/user.go b/internal/model/user.go index a16db615ce1..e33164b57c1 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -41,8 +41,9 @@ type User struct { MediaPlaybackRate float64 `json:"media_playback_rate"` BlockFilterEntryRules string `json:"block_filter_entry_rules"` KeepFilterEntryRules string `json:"keep_filter_entry_rules"` - AlwaysOpenExternalLinks bool `json:"always_open_external_links"` - OpenExternalLinksInNewTab bool `json:"open_external_links_in_new_tab"` + AlwaysOpenExternalLinks bool `json:"always_open_external_links"` + OpenExternalLinksInNewTab bool `json:"open_external_links_in_new_tab"` + DisableBulkOperationsConfirmations bool `json:"disable_bulk_operations_confirmations"` } // UserCreationRequest represents the request to create a user. @@ -84,8 +85,9 @@ type UserModificationRequest struct { MediaPlaybackRate *float64 `json:"media_playback_rate"` BlockFilterEntryRules *string `json:"block_filter_entry_rules"` KeepFilterEntryRules *string `json:"keep_filter_entry_rules"` - AlwaysOpenExternalLinks *bool `json:"always_open_external_links"` - OpenExternalLinksInNewTab *bool `json:"open_external_links_in_new_tab"` + AlwaysOpenExternalLinks *bool `json:"always_open_external_links"` + OpenExternalLinksInNewTab *bool `json:"open_external_links_in_new_tab"` + DisableBulkOperationsConfirmations *bool `json:"disable_bulk_operations_confirmations"` } // Patch updates the User object with the modification request. @@ -209,6 +211,10 @@ func (u *UserModificationRequest) Patch(user *User) { if u.OpenExternalLinksInNewTab != nil { user.OpenExternalLinksInNewTab = *u.OpenExternalLinksInNewTab } + + if u.DisableBulkOperationsConfirmations != nil { + user.DisableBulkOperationsConfirmations = *u.DisableBulkOperationsConfirmations + } } // UseTimezone converts last login date to the given timezone. diff --git a/internal/model/user_test.go b/internal/model/user_test.go new file mode 100644 index 00000000000..ead13662960 --- /dev/null +++ b/internal/model/user_test.go @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package model + +import ( + "testing" +) + +func TestUserModificationRequestPatch(t *testing.T) { + user := &User{ + Username: "original", + Theme: "original_theme", + Language: "original_language", + Timezone: "original_timezone", + AlwaysOpenExternalLinks: false, + OpenExternalLinksInNewTab: false, + DisableBulkOperationsConfirmations: false, + } + + // Test patching DisableBulkOperationsConfirmations + trueValue := true + req := &UserModificationRequest{ + DisableBulkOperationsConfirmations: &trueValue, + } + req.Patch(user) + + if !user.DisableBulkOperationsConfirmations { + t.Error("Expected DisableBulkOperationsConfirmations to be true after patch") + } + + // Test that nil values don't modify the user + user2 := &User{ + Username: "original", + DisableBulkOperationsConfirmations: true, + } + req2 := &UserModificationRequest{} // All fields nil + req2.Patch(user2) + + if user2.Username != "original" { + t.Error("Username should not change when not provided in request") + } + if !user2.DisableBulkOperationsConfirmations { + t.Error("DisableBulkOperationsConfirmations should remain true when not provided in request") + } + + // Test patching false value + falseValue := false + user3 := &User{ + DisableBulkOperationsConfirmations: true, + } + req3 := &UserModificationRequest{ + DisableBulkOperationsConfirmations: &falseValue, + } + req3.Patch(user3) + + if user3.DisableBulkOperationsConfirmations { + t.Error("Expected DisableBulkOperationsConfirmations to be false after patch") + } +} + +func TestUserModificationRequestPatchMultipleFields(t *testing.T) { + user := &User{ + Username: "original_user", + Theme: "original_theme", + AlwaysOpenExternalLinks: false, + OpenExternalLinksInNewTab: false, + DisableBulkOperationsConfirmations: false, + } + + newUsername := "new_user" + newTheme := "new_theme" + trueValue := true + + req := &UserModificationRequest{ + Username: &newUsername, + Theme: &newTheme, + AlwaysOpenExternalLinks: &trueValue, + OpenExternalLinksInNewTab: &trueValue, + DisableBulkOperationsConfirmations: &trueValue, + } + req.Patch(user) + + if user.Username != "new_user" { + t.Errorf("Expected Username to be 'new_user', got '%s'", user.Username) + } + if user.Theme != "new_theme" { + t.Errorf("Expected Theme to be 'new_theme', got '%s'", user.Theme) + } + if !user.AlwaysOpenExternalLinks { + t.Error("Expected AlwaysOpenExternalLinks to be true") + } + if !user.OpenExternalLinksInNewTab { + t.Error("Expected OpenExternalLinksInNewTab to be true") + } + if !user.DisableBulkOperationsConfirmations { + t.Error("Expected DisableBulkOperationsConfirmations to be true") + } +} diff --git a/internal/storage/user.go b/internal/storage/user.go index 44a12896ef8..ec08ab97ddc 100644 --- a/internal/storage/user.go +++ b/internal/storage/user.go @@ -98,7 +98,8 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m block_filter_entry_rules, keep_filter_entry_rules, always_open_external_links, - open_external_links_in_new_tab + open_external_links_in_new_tab, + disable_bulk_operations_confirmations ` tx, err := s.db.Begin() @@ -144,6 +145,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m &user.KeepFilterEntryRules, &user.AlwaysOpenExternalLinks, &user.OpenExternalLinksInNewTab, + &user.DisableBulkOperationsConfirmations, ) if err != nil { tx.Rollback() @@ -210,9 +212,10 @@ func (s *Storage) UpdateUser(user *model.User) error { block_filter_entry_rules=$27, keep_filter_entry_rules=$28, always_open_external_links=$29, - open_external_links_in_new_tab=$30 + open_external_links_in_new_tab=$30, + disable_bulk_operations_confirmations=$31 WHERE - id=$31 + id=$32 ` _, err = s.db.Exec( @@ -247,6 +250,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.KeepFilterEntryRules, user.AlwaysOpenExternalLinks, user.OpenExternalLinksInNewTab, + user.DisableBulkOperationsConfirmations, user.ID, ) if err != nil { @@ -283,9 +287,10 @@ func (s *Storage) UpdateUser(user *model.User) error { block_filter_entry_rules=$26, keep_filter_entry_rules=$27, always_open_external_links=$28, - open_external_links_in_new_tab=$29 + open_external_links_in_new_tab=$29, + disable_bulk_operations_confirmations=$30 WHERE - id=$30 + id=$31 ` _, err := s.db.Exec( @@ -319,6 +324,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.KeepFilterEntryRules, user.AlwaysOpenExternalLinks, user.OpenExternalLinksInNewTab, + user.DisableBulkOperationsConfirmations, user.ID, ) @@ -374,7 +380,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) { block_filter_entry_rules, keep_filter_entry_rules, always_open_external_links, - open_external_links_in_new_tab + open_external_links_in_new_tab, + disable_bulk_operations_confirmations FROM users WHERE @@ -417,7 +424,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) { block_filter_entry_rules, keep_filter_entry_rules, always_open_external_links, - open_external_links_in_new_tab + open_external_links_in_new_tab, + disable_bulk_operations_confirmations FROM users WHERE @@ -460,7 +468,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) { block_filter_entry_rules, keep_filter_entry_rules, always_open_external_links, - open_external_links_in_new_tab + open_external_links_in_new_tab, + disable_bulk_operations_confirmations FROM users WHERE @@ -511,7 +520,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) { u.block_filter_entry_rules, u.keep_filter_entry_rules, u.always_open_external_links, - u.open_external_links_in_new_tab + u.open_external_links_in_new_tab, + u.disable_bulk_operations_confirmations FROM users u LEFT JOIN @@ -556,6 +566,7 @@ func (s *Storage) fetchUser(query string, args ...any) (*model.User, error) { &user.KeepFilterEntryRules, &user.AlwaysOpenExternalLinks, &user.OpenExternalLinksInNewTab, + &user.DisableBulkOperationsConfirmations, ) if err == sql.ErrNoRows { diff --git a/internal/template/templates/views/categories.html b/internal/template/templates/views/categories.html index 37beb6200cf..f9b559a1ebf 100644 --- a/internal/template/templates/views/categories.html +++ b/internal/template/templates/views/categories.html @@ -69,10 +69,10 @@

  • diff --git a/internal/template/templates/views/category_entries.html b/internal/template/templates/views/category_entries.html index 4bca4458362..51174d16109 100644 --- a/internal/template/templates/views/category_entries.html +++ b/internal/template/templates/views/category_entries.html @@ -20,19 +20,19 @@

  • @@ -127,9 +127,9 @@

    diff --git a/internal/template/templates/views/feed_entries.html b/internal/template/templates/views/feed_entries.html index f083987ec68..6232bb82e94 100644 --- a/internal/template/templates/views/feed_entries.html +++ b/internal/template/templates/views/feed_entries.html @@ -20,19 +20,19 @@

  • @@ -139,9 +139,9 @@

    diff --git a/internal/template/templates/views/settings.html b/internal/template/templates/views/settings.html index 0d6ff2e9b9e..4db813b1549 100644 --- a/internal/template/templates/views/settings.html +++ b/internal/template/templates/views/settings.html @@ -214,6 +214,8 @@

    {{ t "page.settings.webauthn.register.error" }}

    + + diff --git a/internal/template/templates/views/unread_entries.html b/internal/template/templates/views/unread_entries.html index 70b7dfe2bde..ef8d713dbd4 100644 --- a/internal/template/templates/views/unread_entries.html +++ b/internal/template/templates/views/unread_entries.html @@ -15,20 +15,20 @@

    class="page-button" data-action="markPageAsRead" data-show-only-unread="1" - data-label-question="{{ t "confirm.question" }}" - data-label-yes="{{ t "confirm.yes" }}" - data-label-no="{{ t "confirm.no" }}" + {{ if not .user.DisableBulkOperationsConfirmations }}data-label-question="{{ t "confirm.question" }}"{{ end }} + {{ if not .user.DisableBulkOperationsConfirmations }}data-label-yes="{{ t "confirm.yes" }}"{{ end }} + {{ if not .user.DisableBulkOperationsConfirmations }}data-label-no="{{ t "confirm.no" }}"{{ end }} data-label-loading="{{ t "confirm.loading" }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}
  • @@ -78,9 +78,9 @@