diff --git a/internal/database/migrations.go b/internal/database/migrations.go index f7c2bd060ce..2c0939dabe6 100644 --- a/internal/database/migrations.go +++ b/internal/database/migrations.go @@ -942,4 +942,9 @@ var migrations = []func(tx *sql.Tx) error{ _, err = tx.Exec(sql) return err }, + func(tx *sql.Tx) (err error) { + sql := `ALTER TABLE users ADD COLUMN custom_js text not null default '';` + _, err = tx.Exec(sql) + return err + }, } diff --git a/internal/locale/translations/de_DE.json b/internal/locale/translations/de_DE.json index c8debd4e743..82c508ee88e 100644 --- a/internal/locale/translations/de_DE.json +++ b/internal/locale/translations/de_DE.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Geste zum Navigieren zwischen Einträgen", "form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen", "form.prefs.label.custom_css": "Benutzerdefiniertes CSS", + "form.prefs.label.custom_js": "Benutzerdefiniertes JS", "form.prefs.label.entry_order": "Artikel-Sortierspalte", "form.prefs.label.default_home_page": "Standard-Startseite", "form.prefs.label.categories_sorting_order": "Kategorie-Sortierung", diff --git a/internal/locale/translations/el_EL.json b/internal/locale/translations/el_EL.json index d9e6d9d5700..5f80554632f 100644 --- a/internal/locale/translations/el_EL.json +++ b/internal/locale/translations/el_EL.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Χειρονομία για πλοήγηση μεταξύ των καταχωρήσεων", "form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα", "form.prefs.label.custom_css": "Προσαρμοσμένο CSS", + "form.prefs.label.custom_js": "Προσαρμοσμένο JS", "form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου", "form.prefs.label.default_home_page": "Προεπιλεγμένη αρχική σελίδα", "form.prefs.label.categories_sorting_order": "Ταξινόμηση κατηγοριών", diff --git a/internal/locale/translations/en_US.json b/internal/locale/translations/en_US.json index 1af4ed0b09e..8c7f65ae0b8 100644 --- a/internal/locale/translations/en_US.json +++ b/internal/locale/translations/en_US.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Gesture to navigate between entries", "form.prefs.label.show_reading_time": "Show estimated reading time for entries", "form.prefs.label.custom_css": "Custom CSS", + "form.prefs.label.custom_js": "Custom JS", "form.prefs.label.entry_order": "Entry sorting column", "form.prefs.label.default_home_page": "Default home page", "form.prefs.label.categories_sorting_order": "Categories sorting", diff --git a/internal/locale/translations/es_ES.json b/internal/locale/translations/es_ES.json index d57b77d7c8a..1e76897eeb2 100644 --- a/internal/locale/translations/es_ES.json +++ b/internal/locale/translations/es_ES.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Gesto para navegar entre entradas", "form.prefs.label.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos", "form.prefs.label.custom_css": "CSS personalizado", + "form.prefs.label.custom_js": "JS personalizado", "form.prefs.label.entry_order": "Columna de clasificación de artículos", "form.prefs.label.default_home_page": "Página de inicio por defecto", "form.prefs.label.categories_sorting_order": "Clasificación por categorías", diff --git a/internal/locale/translations/fi_FI.json b/internal/locale/translations/fi_FI.json index 533b7c8757a..e543dfd3819 100644 --- a/internal/locale/translations/fi_FI.json +++ b/internal/locale/translations/fi_FI.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Ele siirtyäksesi merkintöjen välillä", "form.prefs.label.show_reading_time": "Näytä artikkeleiden arvioitu lukuaika", "form.prefs.label.custom_css": "Mukautettu CSS", + "form.prefs.label.custom_js": "Mukautettu JS", "form.prefs.label.entry_order": "Lajittele sarakkeen mukaan", "form.prefs.label.default_home_page": "Oletusarvoinen etusivu", "form.prefs.label.categories_sorting_order": "Kategorioiden lajittelu", diff --git a/internal/locale/translations/fr_FR.json b/internal/locale/translations/fr_FR.json index 9d34bc920c6..d54e689c97a 100644 --- a/internal/locale/translations/fr_FR.json +++ b/internal/locale/translations/fr_FR.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Geste pour naviguer entre les entrées", "form.prefs.label.show_reading_time": "Afficher le temps de lecture estimé des articles", "form.prefs.label.custom_css": "Feuille de style personnalisée", + "form.prefs.label.custom_js": "Script personnalisée", "form.prefs.label.entry_order": "Colonne de tri des entrées", "form.prefs.label.default_home_page": "Page d'accueil par défaut", "form.prefs.label.categories_sorting_order": "Colonne de tri des catégories", diff --git a/internal/locale/translations/hi_IN.json b/internal/locale/translations/hi_IN.json index 37b225eb429..026f3e4c08a 100644 --- a/internal/locale/translations/hi_IN.json +++ b/internal/locale/translations/hi_IN.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "प्रविष्टियों के बीच नेविगेट करने के लिए इशारा", "form.prefs.label.show_reading_time": "विषय के लिए अनुमानित पढ़ने का समय दिखाएं", "form.prefs.label.custom_css": "कस्टम सीएसएस", + "form.prefs.label.custom_js": "कस्टम जेएस", "form.prefs.label.entry_order": "प्रवेश छँटाई कॉलम", "form.prefs.label.default_home_page": "डिफ़ॉल्ट होमपेज़", "form.prefs.label.categories_sorting_order": "श्रेणियाँ छँटाई", diff --git a/internal/locale/translations/id_ID.json b/internal/locale/translations/id_ID.json index 540c83445f4..75cef1fc46a 100644 --- a/internal/locale/translations/id_ID.json +++ b/internal/locale/translations/id_ID.json @@ -381,6 +381,7 @@ "form.prefs.label.gesture_nav": "Isyarat untuk menavigasi antar entri", "form.prefs.label.show_reading_time": "Tampilkan perkiraan waktu baca untuk artikel", "form.prefs.label.custom_css": "Modifikasi CSS", + "form.prefs.label.custom_js": "Modifikasi JS", "form.prefs.label.entry_order": "Pengurutan Kolom Entri", "form.prefs.label.default_home_page": "Beranda Baku", "form.prefs.label.categories_sorting_order": "Pengurutan Kategori", diff --git a/internal/locale/translations/it_IT.json b/internal/locale/translations/it_IT.json index 5d63868f8bb..930b80ba944 100644 --- a/internal/locale/translations/it_IT.json +++ b/internal/locale/translations/it_IT.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Gesto per navigare tra le voci", "form.prefs.label.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli", "form.prefs.label.custom_css": "CSS personalizzati", + "form.prefs.label.custom_js": "JS personalizzati", "form.prefs.label.entry_order": "Colonna di ordinamento delle voci", "form.prefs.label.default_home_page": "Pagina iniziale predefinita", "form.prefs.label.categories_sorting_order": "Ordinamento delle categorie", diff --git a/internal/locale/translations/ja_JP.json b/internal/locale/translations/ja_JP.json index 0c3ce2f5c83..ad4cceeee2c 100644 --- a/internal/locale/translations/ja_JP.json +++ b/internal/locale/translations/ja_JP.json @@ -381,6 +381,7 @@ "form.prefs.label.gesture_nav": "エントリ間を移動するジェスチャー", "form.prefs.label.show_reading_time": "記事の推定読書時間を表示する", "form.prefs.label.custom_css": "カスタム CSS", + "form.prefs.label.custom_js": "カスタム JS", "form.prefs.label.entry_order": "記事の表示順の基準", "form.prefs.label.default_home_page": "デフォルトのトップページ", "form.prefs.label.categories_sorting_order": "カテゴリの表示順", diff --git a/internal/locale/translations/nl_NL.json b/internal/locale/translations/nl_NL.json index 49503c8aa9d..0e278c1a1ac 100644 --- a/internal/locale/translations/nl_NL.json +++ b/internal/locale/translations/nl_NL.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Gebaar om tussen artikelen te navigeren", "form.prefs.label.show_reading_time": "Toon geschatte leestijd van artikelen", "form.prefs.label.custom_css": "Aangepaste CSS", + "form.prefs.label.custom_js": "Aangepaste JS", "form.prefs.label.entry_order": "Artikelen sorteren", "form.prefs.label.default_home_page": "Startpagina", "form.prefs.label.categories_sorting_order": "Volgorde categorieën", diff --git a/internal/locale/translations/pl_PL.json b/internal/locale/translations/pl_PL.json index 54a46fe95d6..0dd6ae04bd5 100644 --- a/internal/locale/translations/pl_PL.json +++ b/internal/locale/translations/pl_PL.json @@ -401,6 +401,7 @@ "form.prefs.select.tap": "Podwójne wciśnięcie", "form.prefs.select.swipe": "Trzepnąć", "form.prefs.label.custom_css": "Niestandardowy CSS", + "form.prefs.label.custom_js": "Niestandardowy JS", "form.prefs.label.entry_order": "Kolumna sortowania wpisów", "form.prefs.label.default_home_page": "Domyślna strona główna", "form.prefs.label.categories_sorting_order": "Sortowanie kategorii", diff --git a/internal/locale/translations/pt_BR.json b/internal/locale/translations/pt_BR.json index ab9a7bb5629..0e10d728498 100644 --- a/internal/locale/translations/pt_BR.json +++ b/internal/locale/translations/pt_BR.json @@ -391,6 +391,7 @@ "form.prefs.label.gesture_nav": "Gesto para navegar entre as entradas", "form.prefs.label.show_reading_time": "Mostrar tempo estimado de leitura de artigos", "form.prefs.label.custom_css": "CSS customizado", + "form.prefs.label.custom_js": "JS customizado", "form.prefs.label.entry_order": "Coluna de Ordenação de Entrada", "form.prefs.label.default_home_page": "Página inicial predefinida", "form.prefs.label.categories_sorting_order": "Classificação das categorias", diff --git a/internal/locale/translations/ru_RU.json b/internal/locale/translations/ru_RU.json index b3db37957c9..a040d8797c0 100644 --- a/internal/locale/translations/ru_RU.json +++ b/internal/locale/translations/ru_RU.json @@ -401,6 +401,7 @@ "form.prefs.label.gesture_nav": "Жест для перехода между статьями", "form.prefs.label.show_reading_time": "Показать примерное время чтения статей", "form.prefs.label.custom_css": "Пользовательский CSS", + "form.prefs.label.custom_js": "Пользовательский JS", "form.prefs.label.entry_order": "Столбец сортировки статей", "form.prefs.label.default_home_page": "Домашняя страница по умолчанию", "form.prefs.label.categories_sorting_order": "Сортировка категорий", diff --git a/internal/locale/translations/tr_TR.json b/internal/locale/translations/tr_TR.json index cd4c6ff4e18..c9f55c5dd82 100644 --- a/internal/locale/translations/tr_TR.json +++ b/internal/locale/translations/tr_TR.json @@ -293,6 +293,7 @@ "form.prefs.label.categories_sorting_order": "Kategori sıralaması", "form.prefs.label.cjk_reading_speed": "Çince, Korece ve Japonca için okuma hızı (dakika başına karakter)", "form.prefs.label.custom_css": "Özel CSS", + "form.prefs.label.custom_js": "Özel JS", "form.prefs.label.default_home_page": "Varsayılan ana sayfa", "form.prefs.label.default_reading_speed": "Diğer diller için okuma hızı (dakika başına kelime)", "form.prefs.label.display_mode": "Progressive Web App (PWA) görüntüleme modu", diff --git a/internal/locale/translations/uk_UA.json b/internal/locale/translations/uk_UA.json index b1cacf371bd..91027cb77b3 100644 --- a/internal/locale/translations/uk_UA.json +++ b/internal/locale/translations/uk_UA.json @@ -401,6 +401,7 @@ "form.prefs.label.gesture_nav": "Жест для переходу між записами", "form.prefs.label.show_reading_time": "Показувати приблизний час читання для записів", "form.prefs.label.custom_css": "Спеціальний CSS", + "form.prefs.label.custom_js": "Спеціальний JS", "form.prefs.label.entry_order": "Стовпець сортування записів", "form.prefs.label.default_home_page": "Домашня сторінка за умовчанням", "form.prefs.label.categories_sorting_order": "Сортування за категоріями", diff --git a/internal/locale/translations/zh_CN.json b/internal/locale/translations/zh_CN.json index efce46fb88f..2942847e517 100644 --- a/internal/locale/translations/zh_CN.json +++ b/internal/locale/translations/zh_CN.json @@ -381,6 +381,7 @@ "form.prefs.label.gesture_nav": "在条目之间导航的手势", "form.prefs.label.show_reading_time": "显示文章的预计阅读时间", "form.prefs.label.custom_css": "自定义 CSS", + "form.prefs.label.custom_js": "自定义 JS", "form.prefs.label.entry_order": "文章排序依据", "form.prefs.label.default_home_page": "默认主页", "form.prefs.label.categories_sorting_order": "分类排序", diff --git a/internal/locale/translations/zh_TW.json b/internal/locale/translations/zh_TW.json index cc7d480be23..b9505aac4c3 100644 --- a/internal/locale/translations/zh_TW.json +++ b/internal/locale/translations/zh_TW.json @@ -381,6 +381,7 @@ "form.prefs.label.gesture_nav": "在條目之間導航的手勢", "form.prefs.label.show_reading_time": "顯示文章的預計閱讀時間", "form.prefs.label.custom_css": "自定義 CSS", + "form.prefs.label.custom_js": "自定義 JS", "form.prefs.label.entry_order": "文章排序依據", "form.prefs.label.default_home_page": "預設主頁", "form.prefs.label.categories_sorting_order": "分類排序", diff --git a/internal/model/user.go b/internal/model/user.go index 9a3428e96e3..5b78c59760c 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -21,6 +21,7 @@ type User struct { EntryDirection string `json:"entry_sorting_direction"` EntryOrder string `json:"entry_sorting_order"` Stylesheet string `json:"stylesheet"` + CustomJS string `json:"script"` GoogleID string `json:"google_id"` OpenIDConnectID string `json:"openid_connect_id"` EntriesPerPage int `json:"entries_per_page"` @@ -60,6 +61,7 @@ type UserModificationRequest struct { EntryDirection *string `json:"entry_sorting_direction"` EntryOrder *string `json:"entry_sorting_order"` Stylesheet *string `json:"stylesheet"` + CustomJS *string `json:"script"` GoogleID *string `json:"google_id"` OpenIDConnectID *string `json:"openid_connect_id"` EntriesPerPage *int `json:"entries_per_page"` @@ -118,6 +120,10 @@ func (u *UserModificationRequest) Patch(user *User) { user.Stylesheet = *u.Stylesheet } + if u.CustomJS != nil { + user.CustomJS = *u.CustomJS + } + if u.GoogleID != nil { user.GoogleID = *u.GoogleID } diff --git a/internal/storage/user.go b/internal/storage/user.go index 94bf4b6d188..43baf0e1d54 100644 --- a/internal/storage/user.go +++ b/internal/storage/user.go @@ -83,6 +83,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m entry_swipe, gesture_nav, stylesheet, + custom_js, google_id, openid_connect_id, display_mode, @@ -124,6 +125,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m &user.EntrySwipe, &user.GestureNav, &user.Stylesheet, + &user.CustomJS, &user.GoogleID, &user.OpenIDConnectID, &user.DisplayMode, @@ -184,21 +186,22 @@ func (s *Storage) UpdateUser(user *model.User) error { entry_swipe=$11, gesture_nav=$12, stylesheet=$13, - google_id=$14, - openid_connect_id=$15, - display_mode=$16, - entry_order=$17, - default_reading_speed=$18, - cjk_reading_speed=$19, - default_home_page=$20, - categories_sorting_order=$21, - mark_read_on_view=$22, - mark_read_on_media_player_completion=$23, - media_playback_rate=$24, - block_filter_entry_rules=$25, - keep_filter_entry_rules=$26 + custom_js=$14, + google_id=$15, + openid_connect_id=$16, + display_mode=$17, + entry_order=$18, + default_reading_speed=$19, + cjk_reading_speed=$20, + default_home_page=$21, + categories_sorting_order=$22, + mark_read_on_view=$23, + mark_read_on_media_player_completion=$24, + media_playback_rate=$25, + block_filter_entry_rules=$26, + keep_filter_entry_rules=$27 WHERE - id=$27 + id=$28 ` _, err = s.db.Exec( @@ -216,6 +219,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.EntrySwipe, user.GestureNav, user.Stylesheet, + user.CustomJS, user.GoogleID, user.OpenIDConnectID, user.DisplayMode, @@ -249,21 +253,22 @@ func (s *Storage) UpdateUser(user *model.User) error { entry_swipe=$10, gesture_nav=$11, stylesheet=$12, - google_id=$13, - openid_connect_id=$14, - display_mode=$15, - entry_order=$16, - default_reading_speed=$17, - cjk_reading_speed=$18, - default_home_page=$19, - categories_sorting_order=$20, - mark_read_on_view=$21, - mark_read_on_media_player_completion=$22, - media_playback_rate=$23, - block_filter_entry_rules=$24, - keep_filter_entry_rules=$25 + custom_js=$13, + google_id=$14, + openid_connect_id=$15, + display_mode=$16, + entry_order=$17, + default_reading_speed=$18, + cjk_reading_speed=$19, + default_home_page=$20, + categories_sorting_order=$21, + mark_read_on_view=$22, + mark_read_on_media_player_completion=$23, + media_playback_rate=$24, + block_filter_entry_rules=$25, + keep_filter_entry_rules=$26 WHERE - id=$26 + id=$27 ` _, err := s.db.Exec( @@ -280,6 +285,7 @@ func (s *Storage) UpdateUser(user *model.User) error { user.EntrySwipe, user.GestureNav, user.Stylesheet, + user.CustomJS, user.GoogleID, user.OpenIDConnectID, user.DisplayMode, @@ -332,6 +338,7 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) { gesture_nav, last_login_at, stylesheet, + custom_js, google_id, openid_connect_id, display_mode, @@ -371,6 +378,7 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) { gesture_nav, last_login_at, stylesheet, + custom_js, google_id, openid_connect_id, display_mode, @@ -410,6 +418,7 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) { gesture_nav, last_login_at, stylesheet, + custom_js, google_id, openid_connect_id, display_mode, @@ -456,6 +465,7 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) { u.gesture_nav, u.last_login_at, u.stylesheet, + u.custom_js, u.google_id, u.openid_connect_id, u.display_mode, @@ -496,6 +506,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err &user.GestureNav, &user.LastLoginAt, &user.Stylesheet, + &user.CustomJS, &user.GoogleID, &user.OpenIDConnectID, &user.DisplayMode, @@ -608,6 +619,7 @@ func (s *Storage) Users() (model.Users, error) { gesture_nav, last_login_at, stylesheet, + custom_js, google_id, openid_connect_id, display_mode, @@ -649,6 +661,7 @@ func (s *Storage) Users() (model.Users, error) { &user.GestureNav, &user.LastLoginAt, &user.Stylesheet, + &user.CustomJS, &user.GoogleID, &user.OpenIDConnectID, &user.DisplayMode, diff --git a/internal/template/functions.go b/internal/template/functions.go index a084db86c70..3b088d20ca6 100644 --- a/internal/template/functions.go +++ b/internal/template/functions.go @@ -56,6 +56,9 @@ func (f *funcMap) Map() template.FuncMap { "safeCSS": func(str string) template.CSS { return template.CSS(str) }, + "safeJS": func(str string) template.JS { + return template.JS(str) + }, "noescape": func(str string) template.HTML { return template.HTML(str) }, diff --git a/internal/template/templates/common/layout.html b/internal/template/templates/common/layout.html index ca91aebc47b..e0c6850506b 100644 --- a/internal/template/templates/common/layout.html +++ b/internal/template/templates/common/layout.html @@ -34,10 +34,15 @@ - {{ if and .user .user.Stylesheet }} + {{ if .user }} {{ $cspNonce := nonce }} - + + {{ if .user.Stylesheet }} + {{ end }} + {{ if .user.Script }} + + {{ end }} {{ else }} {{ end }} diff --git a/internal/template/templates/views/settings.html b/internal/template/templates/views/settings.html index 99ac5863fdb..535c5a1a53a 100644 --- a/internal/template/templates/views/settings.html +++ b/internal/template/templates/views/settings.html @@ -210,6 +210,9 @@