mirror of
https://github.com/miniflux/v2.git
synced 2025-08-26 18:21:01 +00:00
- Implement a better/simpler polyfill for web browsers that don't supported trusted types yet - Use two separate policies: one to create HTML, another to create/use script urls - Instead of having the policy live in the top-level scope, they're now declared at the lowest possible scope, right before they're used, making them inaccessible outside of it. This puts their usage completely out of reach of an attacker unable to gain some control outside of those two (small) scopes, and thus removes the need to tighten the policies. - Remove the now-unused tt.js file This has been tested on Firefox (doesn't support trusted types) and on Chromium (does support trusted types).
199 lines
12 KiB
HTML
199 lines
12 KiB
HTML
{{ define "base" }}
|
|
<!DOCTYPE html>
|
|
<html lang="{{ replace .language "_" "-"}}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>{{template "title" .}} - Miniflux</title>
|
|
|
|
<meta name="apple-mobile-web-app-title" content="Miniflux">
|
|
<meta name="googlebot" content="notranslate">
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="referrer" content="no-referrer">
|
|
<meta name="robots" content="noindex,nofollow">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<meta name="theme-color" content="{{ theme_color .theme "light" }}" media="(prefers-color-scheme: light)">
|
|
<meta name="theme-color" content="{{ theme_color .theme "dark" }}" media="(prefers-color-scheme: dark)">
|
|
|
|
<link rel="manifest" href="{{ route "webManifest" }}" crossorigin="use-credentials">
|
|
|
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ route "appIcon" "filename" "icon-16.png" }}">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="{{ route "appIcon" "filename" "icon-32.png" }}">
|
|
<link rel="icon" type="image/png" sizes="128x128" href="{{ route "appIcon" "filename" "icon-128.png" }}">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="{{ route "appIcon" "filename" "icon-192.png" }}">
|
|
<link rel="apple-touch-icon" sizes="120x120" href="{{ route "appIcon" "filename" "icon-120.png" }}">
|
|
<link rel="apple-touch-icon" sizes="152x152" href="{{ route "appIcon" "filename" "icon-152.png" }}">
|
|
<link rel="apple-touch-icon" sizes="167x167" href="{{ route "appIcon" "filename" "icon-167.png" }}">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ route "appIcon" "filename" "icon-180.png" }}">
|
|
|
|
<link rel="stylesheet" type="text/css" href="{{ route "stylesheet" "name" .theme "checksum" .theme_checksum }}">
|
|
|
|
{{ if .user }}
|
|
{{ $cspNonce := nonce }}
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; {{ if .user.ExternalFontHosts }}font-src {{ .user.ExternalFontHosts }}; {{ end }}style-src 'self'{{ if .user.Stylesheet }}{{ if .user.ExternalFontHosts }} {{ .user.ExternalFontHosts }}{{ end }} 'nonce-{{ $cspNonce }}'{{ end }}{{ if .user.CustomJS }}; script-src 'self' 'nonce-{{ $cspNonce }}'{{ end }}; require-trusted-types-for 'script'; trusted-types html url;">
|
|
|
|
{{ if .user.Stylesheet -}}
|
|
<style nonce="{{ $cspNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
|
|
{{ end -}}
|
|
|
|
{{ if .user.CustomJS -}}
|
|
<script type="module" nonce="{{ $cspNonce }}">{{ .user.CustomJS | safeJS }}</script>
|
|
{{ end -}}
|
|
{{ else -}}
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; require-trusted-types-for 'script'; trusted-types html url;">
|
|
{{ end -}}
|
|
|
|
<script src="{{ route "javascript" "name" "app" "checksum" .app_js_checksum }}" type="module"></script>
|
|
</head>
|
|
<body
|
|
data-service-worker-url="{{ route "javascript" "name" "service-worker" "checksum" .sw_js_checksum }}"
|
|
{{ if .csrf }}data-csrf-token="{{ .csrf }}"{{ end }}
|
|
data-add-subscription-url="{{ route "addSubscription" }}"
|
|
data-entries-status-url="{{ route "updateEntriesStatus" }}"
|
|
data-refresh-all-feeds-url="{{ route "refreshAllFeeds" }}"
|
|
{{ if .webAuthnEnabled }}
|
|
data-webauthn-register-begin-url="{{ route "webauthnRegisterBegin" }}"
|
|
data-webauthn-register-finish-url="{{ route "webauthnRegisterFinish" }}"
|
|
data-webauthn-login-begin-url="{{ route "webauthnLoginBegin" }}"
|
|
data-webauthn-login-finish-url="{{ route "webauthnLoginFinish" }}"
|
|
data-webauthn-delete-all-url="{{ route "webauthnDeleteAll" }}"
|
|
{{ end }}
|
|
{{ if .user }}
|
|
{{ if not .user.KeyboardShortcuts }}data-disable-keyboard-shortcuts="true"{{ end }}
|
|
data-mark-as-read-on-view="{{ if .user.MarkReadOnView }}true{{ else }}false{{ end }}"
|
|
{{ end }}>
|
|
|
|
{{ if .user }}
|
|
<a class="skip-to-content-link" href="#main">{{ t "skip_to_content" }}</a>
|
|
<header class="header">
|
|
<nav>
|
|
<div class="logo" data-toggle-button-label="{{ t "menu.title" }}">
|
|
<a aria-label="{{ t "menu.home_page" }}" href="{{ route .user.DefaultHomePage }}">
|
|
Mini<span>flux</span>
|
|
</a>
|
|
{{ icon "chevron-down"}}
|
|
</div>
|
|
<ul id="header-menu">
|
|
<li {{ if eq .menu "unread" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g u" }}">
|
|
<a href="{{ route "unread" }}"
|
|
data-page="unread"
|
|
{{ if gt .countUnread 0 }}
|
|
aria-label="{{ t "menu.unread" }}, {{ plural "page.unread_entry_count" .countUnread .countUnread }}"
|
|
{{ end }}
|
|
>
|
|
{{ icon "entries" }}{{ t "menu.unread" }}
|
|
{{ if gt .countUnread 0 }}
|
|
<span class="unread-counter-wrapper" aria-hidden="true">(<span class="unread-counter">{{ .countUnread }}</span>)</span>
|
|
{{ end }}
|
|
</a>
|
|
</li>
|
|
<li {{ if eq .menu "starred" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g b" }}">
|
|
<a href="{{ route "starred" }}" data-page="starred">{{ icon "star" }}{{ t "menu.starred" }}</a>
|
|
</li>
|
|
<li {{ if eq .menu "history" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g h" }}">
|
|
<a href="{{ route "history" }}" data-page="history">{{ icon "history" }}{{ t "menu.history" }}</a>
|
|
</li>
|
|
<li {{ if eq .menu "feeds" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g f" }}">
|
|
<a href="{{ route "feeds" }}" data-page="feeds">{{ icon "feeds" }}{{ t "menu.feeds" }}
|
|
{{ if gt .countErrorFeeds 0 }}
|
|
<span class="error-feeds-counter-wrapper">(<span class="error-feeds-counter">{{ .countErrorFeeds }}</span>)</span>
|
|
{{ end }}
|
|
</a>
|
|
<a href="{{ route "addSubscription" }}" title="{{ t "tooltip.keyboard_shortcuts" "+" }}" aria-label="{{ t "menu.add_feed" }}">
|
|
(+)
|
|
</a>
|
|
</li>
|
|
<li {{ if eq .menu "categories" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g c" }}">
|
|
<a href="{{ route "categories" }}" data-page="categories">{{ icon "categories" }}{{ t "menu.categories" }}</a>
|
|
</li>
|
|
<li {{ if eq .menu "search" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "/" }}">
|
|
<a href="{{ route "search" }}" data-page="search">{{ icon "search" }}{{ t "menu.search" }}</a>
|
|
</li>
|
|
<li {{ if eq .menu "settings" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g s" }}">
|
|
<a href="{{ route "settings" }}" data-page="settings">{{ icon "settings" }}{{ t "menu.settings" }}</a>
|
|
</li>
|
|
{{ if not hasAuthProxy }}
|
|
<li>
|
|
<a href="{{ route "logout" }}" title="{{ t "tooltip.logged_user" .user.Username }}">{{ icon "logout" }}{{ t "menu.logout" }}</a>
|
|
</li>
|
|
{{ end }}
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
{{ end }}
|
|
{{ if .flashMessage }}
|
|
<div role="alert" class="flash-message alert alert-success">{{ .flashMessage }}</div>
|
|
{{ end }}
|
|
{{ if .flashErrorMessage }}
|
|
<div role="alert" class="flash-error-message alert alert-error">{{ .flashErrorMessage }}</div>
|
|
{{ end }}
|
|
|
|
{{template "page_header" .}}
|
|
|
|
<main id="main">
|
|
{{template "content" .}}
|
|
</main>
|
|
<template id="keyboard-shortcuts">
|
|
<div id="modal-left">
|
|
<button class="btn-close-modal" aria-label="Close">x</button>
|
|
<h3 tabindex="-1" id="dialog-title">{{ t "page.keyboard_shortcuts.title" }}</h3>
|
|
|
|
<div class="keyboard-shortcuts">
|
|
<p>{{ t "page.keyboard_shortcuts.subtitle.sections" }}</p>
|
|
<ul>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_unread" }} = <strong>g + u</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_starred" }} = <strong>g + b</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_history" }} = <strong>g + h</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_feeds" }} = <strong>g + f</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_categories" }} = <strong>g + c</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_settings" }} = <strong>g + s</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.show_keyboard_shortcuts" }} = <strong>?</strong></li>
|
|
<li>{{ t "menu.add_feed" }} = <strong>+</strong></li>
|
|
</ul>
|
|
|
|
<p>{{ t "page.keyboard_shortcuts.subtitle.items" }}</p>
|
|
<ul>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_previous_item" }} = <strong>p</strong>, <strong>k</strong>, <strong>⏴</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_next_item" }} = <strong>n</strong>, <strong>j</strong>, <strong>⏵</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_feed" }} = <strong>F</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_top_item" }} = <strong>g + g</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_bottom_item" }} = <strong>G</strong></li>
|
|
</ul>
|
|
|
|
<p>{{ t "page.keyboard_shortcuts.subtitle.pages" }}</p>
|
|
<ul>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_previous_page" }} = <strong>h</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_next_page" }} = <strong>l</strong></li>
|
|
</ul>
|
|
|
|
<p>{{ t "page.keyboard_shortcuts.subtitle.actions" }}</p>
|
|
<ul>
|
|
<li>{{ t "page.keyboard_shortcuts.open_item" }} = <strong>o</strong>, <strong>Enter</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.open_original" }} = <strong>v</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.open_original_same_window" }} = <strong>V</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.open_comments" }} = <strong>c</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.open_comments_same_window" }} = <strong>C</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.toggle_read_status_next" }} = <strong>m</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.toggle_read_status_prev" }} = <strong>M</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.mark_page_as_read" }} = <strong>A</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.download_content" }} = <strong>d</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.toggle_star_status" }} = <strong>f</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.toggle_entry_attachments" }} = <strong>a</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.scroll_item_to_top" }} = <strong>z + t</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.refresh_all_feeds" }} = <strong>R</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.remove_feed" }} = <strong>#</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.go_to_search" }} = <strong>/</strong></li>
|
|
<li>{{ t "page.keyboard_shortcuts.close_modal" }} = <strong>Esc</strong></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="icon-read">{{ icon "read" }}</template>
|
|
<template id="icon-unread">{{ icon "unread" }}</template>
|
|
<template id="icon-star">{{ icon "star" }}</template>
|
|
<template id="icon-unstar">{{ icon "unstar" }}</template>
|
|
<template id="icon-save">{{ icon "save" }}</template>
|
|
</body>
|
|
</html>
|
|
{{ end }}
|