settings.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package form // import "miniflux.app/v2/internal/ui/form"
  4. import (
  5. "net/http"
  6. "strconv"
  7. "miniflux.app/v2/internal/config"
  8. "miniflux.app/v2/internal/locale"
  9. "miniflux.app/v2/internal/model"
  10. )
  11. // MarkReadBehavior list all possible behaviors for automatically marking an entry as read
  12. type MarkReadBehavior string
  13. var (
  14. NoAutoMarkAsRead MarkReadBehavior = "no-auto"
  15. MarkAsReadOnView MarkReadBehavior = "on-view"
  16. MarkAsReadOnViewButWaitForPlayerCompletion MarkReadBehavior = "on-view-but-wait-for-player-completion"
  17. MarkAsReadOnlyOnPlayerCompletion MarkReadBehavior = "on-player-completion"
  18. )
  19. // SettingsForm represents the settings form.
  20. type SettingsForm struct {
  21. Username string
  22. Password string
  23. Confirmation string
  24. Theme string
  25. Language string
  26. Timezone string
  27. EntryDirection string
  28. EntryOrder string
  29. EntriesPerPage int
  30. KeyboardShortcuts bool
  31. ShowReadingTime bool
  32. CustomCSS string
  33. EntrySwipe bool
  34. GestureNav string
  35. DisplayMode string
  36. DefaultReadingSpeed int
  37. CJKReadingSpeed int
  38. DefaultHomePage string
  39. CategoriesSortingOrder string
  40. MarkReadOnView bool
  41. // MarkReadBehavior is a string representation of the MarkReadOnView and MarkReadOnMediaPlayerCompletion fields together
  42. MarkReadBehavior MarkReadBehavior
  43. MediaPlaybackRate float64
  44. BlockFilterEntryRules string
  45. KeepFilterEntryRules string
  46. }
  47. // MarkAsReadBehavior returns the MarkReadBehavior from the given MarkReadOnView and MarkReadOnMediaPlayerCompletion values.
  48. // Useful to convert the values from the User model to the form
  49. func MarkAsReadBehavior(markReadOnView, markReadOnMediaPlayerCompletion bool) MarkReadBehavior {
  50. switch {
  51. case markReadOnView && !markReadOnMediaPlayerCompletion:
  52. return MarkAsReadOnView
  53. case markReadOnView && markReadOnMediaPlayerCompletion:
  54. return MarkAsReadOnViewButWaitForPlayerCompletion
  55. case !markReadOnView && markReadOnMediaPlayerCompletion:
  56. return MarkAsReadOnlyOnPlayerCompletion
  57. case !markReadOnView && !markReadOnMediaPlayerCompletion:
  58. fallthrough // Explicit defaulting
  59. default:
  60. return NoAutoMarkAsRead
  61. }
  62. }
  63. // ExtractMarkAsReadBehavior returns the MarkReadOnView and MarkReadOnMediaPlayerCompletion values from the given MarkReadBehavior.
  64. // Useful to extract the values from the form to the User model
  65. func ExtractMarkAsReadBehavior(behavior MarkReadBehavior) (markReadOnView, markReadOnMediaPlayerCompletion bool) {
  66. switch behavior {
  67. case MarkAsReadOnView:
  68. return true, false
  69. case MarkAsReadOnViewButWaitForPlayerCompletion:
  70. return true, true
  71. case MarkAsReadOnlyOnPlayerCompletion:
  72. return false, true
  73. case NoAutoMarkAsRead:
  74. fallthrough // Explicit defaulting
  75. default:
  76. return false, false
  77. }
  78. }
  79. // Merge updates the fields of the given user.
  80. func (s *SettingsForm) Merge(user *model.User) *model.User {
  81. if !config.Opts.DisableLocalAuth() {
  82. user.Username = s.Username
  83. }
  84. user.Theme = s.Theme
  85. user.Language = s.Language
  86. user.Timezone = s.Timezone
  87. user.EntryDirection = s.EntryDirection
  88. user.EntryOrder = s.EntryOrder
  89. user.EntriesPerPage = s.EntriesPerPage
  90. user.KeyboardShortcuts = s.KeyboardShortcuts
  91. user.ShowReadingTime = s.ShowReadingTime
  92. user.Stylesheet = s.CustomCSS
  93. user.EntrySwipe = s.EntrySwipe
  94. user.GestureNav = s.GestureNav
  95. user.DisplayMode = s.DisplayMode
  96. user.CJKReadingSpeed = s.CJKReadingSpeed
  97. user.DefaultReadingSpeed = s.DefaultReadingSpeed
  98. user.DefaultHomePage = s.DefaultHomePage
  99. user.CategoriesSortingOrder = s.CategoriesSortingOrder
  100. user.MediaPlaybackRate = s.MediaPlaybackRate
  101. user.BlockFilterEntryRules = s.BlockFilterEntryRules
  102. user.KeepFilterEntryRules = s.KeepFilterEntryRules
  103. MarkReadOnView, MarkReadOnMediaPlayerCompletion := ExtractMarkAsReadBehavior(s.MarkReadBehavior)
  104. user.MarkReadOnView = MarkReadOnView
  105. user.MarkReadOnMediaPlayerCompletion = MarkReadOnMediaPlayerCompletion
  106. if s.Password != "" {
  107. user.Password = s.Password
  108. }
  109. return user
  110. }
  111. // Validate makes sure the form values are valid.
  112. func (s *SettingsForm) Validate() *locale.LocalizedError {
  113. if (s.Username == "" && !config.Opts.DisableLocalAuth()) || s.Theme == "" || s.Language == "" || s.Timezone == "" || s.EntryDirection == "" || s.DisplayMode == "" || s.DefaultHomePage == "" {
  114. return locale.NewLocalizedError("error.settings_mandatory_fields")
  115. }
  116. if s.CJKReadingSpeed <= 0 || s.DefaultReadingSpeed <= 0 {
  117. return locale.NewLocalizedError("error.settings_reading_speed_is_positive")
  118. }
  119. if s.Confirmation == "" {
  120. // Firefox insists on auto-completing the password field.
  121. // If the confirmation field is blank, the user probably
  122. // didn't intend to change their password.
  123. s.Password = ""
  124. } else if s.Password != "" {
  125. if s.Password != s.Confirmation {
  126. return locale.NewLocalizedError("error.different_passwords")
  127. }
  128. }
  129. if s.MediaPlaybackRate < 0.25 || s.MediaPlaybackRate > 4 {
  130. return locale.NewLocalizedError("error.settings_media_playback_rate_range")
  131. }
  132. return nil
  133. }
  134. // NewSettingsForm returns a new SettingsForm.
  135. func NewSettingsForm(r *http.Request) *SettingsForm {
  136. entriesPerPage, err := strconv.ParseInt(r.FormValue("entries_per_page"), 10, 0)
  137. if err != nil {
  138. entriesPerPage = 0
  139. }
  140. defaultReadingSpeed, err := strconv.ParseInt(r.FormValue("default_reading_speed"), 10, 0)
  141. if err != nil {
  142. defaultReadingSpeed = 0
  143. }
  144. cjkReadingSpeed, err := strconv.ParseInt(r.FormValue("cjk_reading_speed"), 10, 0)
  145. if err != nil {
  146. cjkReadingSpeed = 0
  147. }
  148. mediaPlaybackRate, err := strconv.ParseFloat(r.FormValue("media_playback_rate"), 64)
  149. if err != nil {
  150. mediaPlaybackRate = 1
  151. }
  152. return &SettingsForm{
  153. Username: r.FormValue("username"),
  154. Password: r.FormValue("password"),
  155. Confirmation: r.FormValue("confirmation"),
  156. Theme: r.FormValue("theme"),
  157. Language: r.FormValue("language"),
  158. Timezone: r.FormValue("timezone"),
  159. EntryDirection: r.FormValue("entry_direction"),
  160. EntryOrder: r.FormValue("entry_order"),
  161. EntriesPerPage: int(entriesPerPage),
  162. KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
  163. ShowReadingTime: r.FormValue("show_reading_time") == "1",
  164. CustomCSS: r.FormValue("custom_css"),
  165. EntrySwipe: r.FormValue("entry_swipe") == "1",
  166. GestureNav: r.FormValue("gesture_nav"),
  167. DisplayMode: r.FormValue("display_mode"),
  168. DefaultReadingSpeed: int(defaultReadingSpeed),
  169. CJKReadingSpeed: int(cjkReadingSpeed),
  170. DefaultHomePage: r.FormValue("default_home_page"),
  171. CategoriesSortingOrder: r.FormValue("categories_sorting_order"),
  172. MarkReadOnView: r.FormValue("mark_read_on_view") == "1",
  173. MarkReadBehavior: MarkReadBehavior(r.FormValue("mark_read_behavior")),
  174. MediaPlaybackRate: mediaPlaybackRate,
  175. BlockFilterEntryRules: r.FormValue("block_filter_entry_rules"),
  176. KeepFilterEntryRules: r.FormValue("keep_filter_entry_rules"),
  177. }
  178. }