settings.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. {{ define "title"}}{{ t "page.settings.title" }}{{ end }}
  2. {{ define "page_header"}}
  3. <section class="page-header" aria-labelledby="page-header-title">
  4. <h1 id="page-header-title">{{ t "page.settings.title" }}</h1>
  5. {{ template "settings_menu" dict "user" .user }}
  6. </section>
  7. {{ end }}
  8. {{ define "content"}}
  9. <form method="post" autocomplete="off" action="{{ route "updateSettings" }}">
  10. <input type="hidden" name="csrf" value="{{ .csrf }}">
  11. {{ if .errorMessage }}
  12. <div role="alert" class="alert alert-error">{{ .errorMessage }}</div>
  13. {{ end }}
  14. {{ if not disableLocalAuth }}
  15. <fieldset>
  16. <legend>{{ t "form.prefs.fieldset.authentication_settings" }}</legend>
  17. <label for="form-username">{{ t "form.user.label.username" }}</label>
  18. <input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" required>
  19. <label for="form-password">{{ t "form.user.label.password" }}</label>
  20. <input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="new-password">
  21. <label for="form-confirmation">{{ t "form.user.label.confirmation" }}</label>
  22. <input type="password" name="confirmation" id="form-confirmation" value="{{ .form.Confirmation }}" autocomplete="new-password">
  23. {{ if hasOAuth2Provider "google" }}
  24. <p>
  25. {{ if .user.GoogleID }}
  26. <a href="{{ route "oauth2Unlink" "provider" "google" }}">{{ t "page.settings.unlink_google_account" }}</a>
  27. {{ else }}
  28. <a href="{{ route "oauth2Redirect" "provider" "google" }}">{{ t "page.settings.link_google_account" }}</a>
  29. {{ end }}
  30. </p>
  31. {{ else if hasOAuth2Provider "oidc" }}
  32. <p>
  33. {{ if .user.OpenIDConnectID }}
  34. <a href="{{ route "oauth2Unlink" "provider" "oidc" }}">{{ t "page.settings.unlink_oidc_account" oidcProviderName }}</a>
  35. {{ else }}
  36. <a href="{{ route "oauth2Redirect" "provider" "oidc" }}">{{ t "page.settings.link_oidc_account" oidcProviderName }}</a>
  37. {{ end }}
  38. </p>
  39. {{ end }}
  40. <div class="buttons">
  41. <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
  42. </div>
  43. </fieldset>
  44. {{ end }}
  45. {{ if .webAuthnEnabled }}
  46. <fieldset>
  47. <legend>{{ t "page.settings.webauthn.passkeys" }}</legend>
  48. <template id="webauthn-error">
  49. <div role="alert" class="alert alert-error" id="webauthn-error-alert">
  50. <h4>{{ t "page.settings.webauthn.register.error" }}</h4>
  51. <p id="webauthn-error-message"></p>
  52. </div>
  53. </template>
  54. {{ if .webAuthnCerts}}
  55. <table>
  56. <tr>
  57. <th>{{ t "page.settings.webauthn.passkey_name" }}</th>
  58. <th>{{ t "page.settings.webauthn.added_on" }}</th>
  59. <th>{{ t "page.settings.webauthn.last_seen_on" }}</th>
  60. <th>{{ t "page.settings.webauthn.actions" }}</th>
  61. </tr>
  62. {{ range .webAuthnCerts }}
  63. <tr>
  64. <td>{{ .Name }}</td>
  65. <td>{{ elapsed $.user.Timezone .AddedOn }}</td>
  66. <td>{{ elapsed $.user.Timezone .LastSeenOn }}</td>
  67. <td>
  68. <a href="#"
  69. data-confirm="true"
  70. data-label-question="{{ t "confirm.question" }}"
  71. data-label-yes="{{ t "confirm.yes" }}"
  72. data-label-no="{{ t "confirm.no" }}"
  73. data-label-loading="{{ t "confirm.loading" }}"
  74. data-url="{{ route "webauthnDelete" "credentialHandle" .HandleEncoded }}">{{ icon "delete" }}{{ t "action.remove" }}</a>
  75. <a href="{{ route "webauthnRename" "credentialHandle" .HandleEncoded }}">{{ icon "edit" }} {{ t "action.edit" }}</a>
  76. </td>
  77. </tr>
  78. {{ end }}
  79. </table>
  80. {{ end }}
  81. <div class="buttons">
  82. <button class="button button-primary" id="webauthn-register" disabled>
  83. {{ t "page.settings.webauthn.register" }}
  84. </button>
  85. {{ if gt .countWebAuthnCerts 0}}
  86. <button class="button button-danger" id="webauthn-delete">
  87. {{ plural "page.settings.webauthn.delete" .countWebAuthnCerts .countWebAuthnCerts }}
  88. </button>
  89. {{ end }}
  90. </div>
  91. </fieldset>
  92. {{ end }}
  93. <fieldset>
  94. <legend>{{ t "form.prefs.fieldset.reader_settings" }}</legend>
  95. <label for="form-cjk-reading-speed">{{ t "form.prefs.label.cjk_reading_speed" }}</label>
  96. <input type="number" name="cjk_reading_speed" id="form-cjk-reading-speed" value="{{ .form.CJKReadingSpeed }}" min="1">
  97. <label for="form-default-reading-speed">{{ t "form.prefs.label.default_reading_speed" }}</label>
  98. <input type="number" name="default_reading_speed" id="form-default-reading-speed" value="{{ .form.DefaultReadingSpeed }}" min="1">
  99. <label for="form-media-playback-rate">{{ t "form.prefs.label.media_playback_rate" }}</label>
  100. <input type="number" name="media_playback_rate" id="form-media-playback-rate" value="{{ .form.MediaPlaybackRate }}" min="0.25" max="4" step="any" />
  101. <label><input type="checkbox" name="show_reading_time" value="1" {{ if .form.ShowReadingTime }}checked{{ end }}> {{ t "form.prefs.label.show_reading_time" }}</label>
  102. <label><input type="radio" name="mark_read_behavior" value="{{ .readBehaviors.NoAutoMarkAsRead }}"
  103. {{ if eq .form.MarkReadBehavior .readBehaviors.NoAutoMarkAsRead }}checked{{end}} > {{ t "form.prefs.label.mark_read_manually" }}</label>
  104. <label><input type="radio" name="mark_read_behavior" value="{{ .readBehaviors.MarkAsReadOnView }}"
  105. {{ if eq .form.MarkReadBehavior .readBehaviors.MarkAsReadOnView }}checked{{end}} > {{ t "form.prefs.label.mark_read_on_view" }}</label>
  106. <label><input type="radio" name="mark_read_behavior" value="{{ .readBehaviors.MarkAsReadOnViewButWaitForPlayerCompletion }}"
  107. {{ if eq .form.MarkReadBehavior .readBehaviors.MarkAsReadOnViewButWaitForPlayerCompletion }}checked{{end}}> {{ t "form.prefs.label.mark_read_on_view_or_media_completion" }}</label>
  108. <label><input type="radio" name="mark_read_behavior" value="{{ .readBehaviors.MarkAsReadOnlyOnPlayerCompletion }}"
  109. {{ if eq .form.MarkReadBehavior .readBehaviors.MarkAsReadOnlyOnPlayerCompletion }}checked{{end}} > {{ t "form.prefs.label.mark_read_on_media_completion" }}</label>
  110. <div class="buttons">
  111. <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
  112. </div>
  113. </fieldset>
  114. <fieldset>
  115. <legend>{{ t "form.prefs.fieldset.application_settings" }}</legend>
  116. <label for="form-language">{{ t "form.prefs.label.language" }}</label>
  117. <select id="form-language" name="language">
  118. {{ range $key, $value := .languages }}
  119. <option value="{{ $key }}" {{ if eq $key $.form.Language }}selected="selected"{{ end }}>{{ $value }}</option>
  120. {{ end }}
  121. </select>
  122. <label for="form-timezone">{{ t "form.prefs.label.timezone" }}</label>
  123. <select id="form-timezone" name="timezone">
  124. {{ range $key, $value := .timezones }}
  125. <option value="{{ $key }}" {{ if eq $key $.form.Timezone }}selected="selected"{{ end }}>{{ $value }}</option>
  126. {{ end }}
  127. </select>
  128. <label for="form-theme">{{ t "form.prefs.label.theme" }}</label>
  129. <select id="form-theme" name="theme">
  130. {{ range $key, $value := .themes }}
  131. <option value="{{ $key }}" {{ if eq $key $.form.Theme }}selected="selected"{{ end }}>{{ $value }}</option>
  132. {{ end }}
  133. </select>
  134. <div class="form-label-row">
  135. <label for="form-display-mode">{{ t "form.prefs.label.display_mode" }}</label>
  136. &nbsp;
  137. <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
  138. {{ icon "external-link" }}
  139. </a>
  140. </div>
  141. <select id="form-display-mode" name="display_mode">
  142. <option value="fullscreen" {{ if eq "fullscreen" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.fullscreen" }}</option>
  143. <option value="standalone" {{ if eq "standalone" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.standalone" }}</option>
  144. <option value="minimal-ui" {{ if eq "minimal-ui" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.minimal_ui" }}</option>
  145. <option value="browser" {{ if eq "browser" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.browser" }}</option>
  146. </select>
  147. <label for="form-default-home-page">{{ t "form.prefs.label.default_home_page" }}</label>
  148. <select id="form-default-home-page" name="default_home_page">
  149. {{ range $key, $value := .default_home_pages }}
  150. <option value="{{ $key }}" {{ if eq $key $.form.DefaultHomePage }}selected="selected"{{ end }}>{{ t $value }}</option>
  151. {{ end }}
  152. </select>
  153. <label for="form-entry-direction">{{ t "form.prefs.label.entry_sorting" }}</label>
  154. <select id="form-entry-direction" name="entry_direction">
  155. <option value="asc" {{ if eq "asc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.older_first" }}</option>
  156. <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
  157. </select>
  158. <label for="form-entry-order">{{ t "form.prefs.label.entry_order" }}</label>
  159. <select id="form-entry-order" name="entry_order">
  160. <option value="published_at" {{ if eq "published_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.publish_time" }}</option>
  161. <option value="created_at" {{ if eq "created_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.created_time" }}</option>
  162. </select>
  163. <label for="form-categories-sorting-order">{{ t "form.prefs.label.categories_sorting_order" }}</label>
  164. <select id="form-categories-sorting-order" name="categories_sorting_order">
  165. {{ range $key, $value := .categories_sorting_options }}
  166. <option value="{{ $key }}" {{ if eq $key $.form.CategoriesSortingOrder }}selected="selected"{{ end }}>{{ t $value }}</option>
  167. {{ end }}
  168. </select>
  169. <label for="form-gesture-nav">{{ t "form.prefs.label.gesture_nav" }}</label>
  170. <select id="form-gesture-nav" name="gesture_nav">
  171. <option value="none" {{ if eq "none" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.none" }}</option>
  172. <option value="tap" {{ if eq "tap" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.tap" }}</option>
  173. <option value="swipe" {{ if eq "swipe" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.swipe" }}</option>
  174. </select>
  175. <label for="form-entries-per-page">{{ t "form.prefs.label.entries_per_page" }}</label>
  176. <input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
  177. <label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
  178. <label><input type="checkbox" name="entry_swipe" value="1" {{ if .form.EntrySwipe }}checked{{ end }}> {{ t "form.prefs.label.entry_swipe" }}</label>
  179. <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>
  180. <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>
  181. <label for="form-custom-css">{{t "form.prefs.label.custom_css" }}</label>
  182. <textarea id="form-custom-css" name="custom_css" cols="40" rows="10" spellcheck="false">{{ .form.CustomCSS }}</textarea>
  183. <label for="form-external-font-hosts">{{t "form.prefs.label.external_font_hosts" }}</label>
  184. <input type="text" id="form-external-font-hosts" name="external_font_hosts" spellcheck="false" value="{{ .form.ExternalFontHosts }}">
  185. <div class="form-help">{{t "form.prefs.help.external_font_hosts" }}</div>
  186. <label for="form-custom-js">{{t "form.prefs.label.custom_js" }}</label>
  187. <textarea id="form-custom-js" name="custom_js" cols="40" rows="10" spellcheck="false">{{ .form.CustomJS }}</textarea>
  188. <div class="buttons">
  189. <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
  190. </div>
  191. </fieldset>
  192. <fieldset>
  193. <legend>{{ t "form.prefs.fieldset.global_feed_settings" }}</legend>
  194. <div class="form-label-row">
  195. <label for="form-block-filter-rules">
  196. {{ t "form.feed.label.block_filter_entry_rules" }}
  197. </label>
  198. &nbsp;
  199. <a href=" https://miniflux.app/docs/rules.html#filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
  200. {{ icon "external-link" }}
  201. </a>
  202. </div>
  203. <textarea id="form-block-filter-rules" name="block_filter_entry_rules" cols="40" rows="10" spellcheck="false">{{ .form.BlockFilterEntryRules }}</textarea>
  204. <div class="form-label-row">
  205. <label for="form-keep-filter-rules">
  206. {{ t "form.feed.label.keep_filter_entry_rules" }}
  207. </label>
  208. &nbsp;
  209. <a href=" https://miniflux.app/docs/rules.html#filtering-rules" {{ if $.user.OpenExternalLinksInNewTab }}target="_blank"{{ end }}>
  210. {{ icon "external-link" }}
  211. </a>
  212. </div>
  213. <textarea id="form-keep-filter-rules" name="keep_filter_entry_rules" cols="40" rows="10" spellcheck="false">{{ .form.KeepFilterEntryRules }}</textarea>
  214. <div class="buttons">
  215. <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
  216. </div>
  217. </fieldset>
  218. </form>
  219. {{ end }}