1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-01 17:38:37 +00:00

Remove completely generated files

This commit is contained in:
Frédéric Guillot 2021-02-18 21:33:29 -08:00 committed by fguillot
parent 4855fbd13f
commit c2571f9f47
44 changed files with 79 additions and 2358 deletions

View file

@ -0,0 +1,19 @@
{{ define "entry_pagination" }}
<div class="pagination">
<div class="pagination-prev">
{{ if .prevEntry }}
<a href="{{ .prevEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .prevEntry.Title }}" data-page="previous" rel="prev">{{ t "pagination.previous" }}</a>
{{ else }}
{{ t "pagination.previous" }}
{{ end }}
</div>
<div class="pagination-next">
{{ if .nextEntry }}
<a href="{{ .nextEntryRoute }}{{ if .searchQuery }}?q={{ .searchQuery }}{{ end }}" title="{{ .nextEntry.Title }}" data-page="next" rel="next">{{ t "pagination.next" }}</a>
{{ else }}
{{ t "pagination.next" }}
{{ end }}
</div>
</div>
{{ end }}

View file

@ -0,0 +1,61 @@
{{ define "feed_list" }}
<div class="items">
{{ range .feeds }}
<article class="item {{ if ne .ParsingErrorCount 0 }}feed-parsing-error{{ end }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if .Icon }}
<img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
{{ end }}
{{ if .Disabled }} 🚫 {{ end }}
<a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
</span>
<span class="feed-entries-counter">
(<span title="{{ t "page.feeds.unread_counter" }}">{{ .UnreadCount }}</span>/<span title="{{ t "page.feeds.read_counter" }}">{{ .ReadCount }}</span>)
</span>
<span class="category">
<a href="{{ route "categoryEntries" "categoryID" .Category.ID }}">{{ .Category.Title }}</a>
</span>
</div>
<div class="item-meta">
<ul class="item-meta-info">
<li dir="auto">
<a href="{{ .SiteURL | safeURL }}" title="{{ .SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ domain .SiteURL }}</a>
</li>
<li>
{{ t "page.feeds.last_check" }} <time datetime="{{ isodate .CheckedAt }}" title="{{ isodate .CheckedAt }}">{{ elapsed $.user.Timezone .CheckedAt }}</time>
</li>
</ul>
<ul class="item-meta-icons">
<li>
<a href="{{ route "refreshFeed" "feedID" .ID }}">{{ template "icon_refresh" }}<span class="icon-label">{{ t "menu.refresh_feed" }}</span></a>
</li>
<li>
<a href="{{ route "editFeed" "feedID" .ID }}">{{ template "icon_edit" }}<span class="icon-label">{{ t "menu.edit_feed" }}</span></a>
</li>
<li>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeFeed" "feedID" .ID }}">{{ template "icon_delete" }}<span class="icon-label">{{ t "action.remove" }}</span></a>
</li>
{{ if .UnreadCount }}
<li>
<a href="{{ route "markFeedAsRead" "feedID" .ID }}">{{ template "icon_read" }}<span class="icon-label">{{ t "menu.mark_all_as_read" }}</span></a>
</li>
{{ end }}
</ul>
</div>
{{ if ne .ParsingErrorCount 0 }}
<div class="parsing-error">
<strong title="{{ .ParsingErrorMsg }}" class="parsing-error-count">{{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}</strong>
- <small class="parsing-error-message">{{ .ParsingErrorMsg }}</small>
</div>
{{ end }}
</article>
{{ end }}
</div>
{{ end }}

View file

@ -0,0 +1,19 @@
{{ define "feed_menu" }}
<ul>
<li>
<a href="{{ route "feeds" }}">{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "addSubscription" }}">{{ t "menu.add_feed" }}</a>
</li>
<li>
<a href="{{ route "export" }}">{{ t "menu.export" }}</a>
</li>
<li>
<a href="{{ route "import" }}">{{ t "menu.import" }}</a>
</li>
<li>
<a href="{{ route "refreshAllFeeds" }}">{{ t "menu.refresh_all_feeds" }}</a>
</li>
</ul>
{{ end }}

View file

@ -0,0 +1,135 @@
<!--
MIT License
Copyright (c) 2020 Paweł Kuna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
{{ define "icon_read" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-check" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="12" cy="12" r="9" />
<path d="M9 12l2 2l4 -4" />
</svg>
{{ end }}
{{ define "icon_unread" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="12" cy="12" r="9" />
<path d="M10 10l4 4m0 -4l-4 4" />
</svg>
{{ end }}
{{ define "icon_star" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-star" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M12 17.75l-6.172 3.245 1.179-6.873-4.993-4.867 6.9-1.002L12 2l3.086 6.253 6.9 1.002-4.993 4.867 1.179 6.873z" />
</svg>
{{ end }}
{{ define "icon_unstar" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-unstar" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path fill="currentColor" d="M12 17.75l-6.172 3.245 1.179-6.873-4.993-4.867 6.9-1.002L12 2l3.086 6.253 6.9 1.002-4.993 4.867 1.179 6.873z" />
</svg>
{{ end }}
{{ define "icon_save" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-download" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2" />
<polyline points="7 11 12 16 17 11" />
<line x1="12" y1="4" x2="12" y2="16" />
</svg>
{{ end }}
{{ define "icon_scraper" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cloud-download" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M19 18a3.5 3.5 0 0 0 0 -7h-1a5 4.5 0 0 0 -11 -2a4.6 4.4 0 0 0 -2.1 8.4" />
<line x1="12" y1="13" x2="12" y2="22" />
<polyline points="9 19 12 22 15 19" />
</svg>
{{ end }}
{{ define "icon_share" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-share" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="6" cy="12" r="3" />
<circle cx="18" cy="6" r="3" />
<circle cx="18" cy="18" r="3" />
<line x1="8.7" y1="10.7" x2="15.3" y2="7.3" />
<line x1="8.7" y1="13.3" x2="15.3" y2="16.7" />
</svg>
{{ end }}
{{ define "icon_comment" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-message-circle" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M3 20l1.3 -3.9a9 8 0 1 1 3.4 2.9l-4.7 1" />
<line x1="12" y1="12" x2="12" y2="12.01" />
<line x1="8" y1="12" x2="8" y2="12.01" />
<line x1="16" y1="12" x2="16" y2="12.01" />
</svg>
{{ end }}
{{ define "icon_external_link" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-external-link" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
<line x1="10" y1="14" x2="20" y2="4" />
<polyline points="15 4 20 4 20 9" />
</svg>
{{ end }}
{{ define "icon_delete" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-trash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<line x1="4" y1="7" x2="20" y2="7" />
<line x1="10" y1="11" x2="10" y2="17" />
<line x1="14" y1="11" x2="14" y2="17" />
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
</svg>
{{ end }}
{{ define "icon_edit" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-edit" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M9 7 h-3a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-3" />
<path d="M9 15h3l8.5 -8.5a1.5 1.5 0 0 0 -3 -3l-8.5 8.5v3" />
<line x1="16" y1="5" x2="19" y2="8" />
</svg>
{{ end }}
{{ define "icon_feeds" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folders" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M9 4h3l2 2h5a2 2 0 0 1 2 2v7a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2" />
<path d="M17 17v2a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2h2" />
</svg>
{{ end }}
{{ define "icon_entries" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-news" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M16 6h3a1 1 0 0 1 1 1v11a2 2 0 0 1 -4 0v-13a1 1 0 0 0 -1 -1h-10a1 1 0 0 0 -1 1v12a3 3 0 0 0 3 3h11" />
<line x1="8" y1="8" x2="12" y2="8" />
<line x1="8" y1="12" x2="12" y2="12" />
<line x1="8" y1="16" x2="12" y2="16" />
</svg>
{{ end }}
{{ define "icon_refresh" }}
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -5v5h5" />
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 5v-5h-5" />
</svg>
{{ end }}

View file

@ -0,0 +1,75 @@
{{ define "item_meta" }}
<div class="item-meta">
<ul class="item-meta-info">
<li>
<a href="{{ route "feedEntries" "feedID" .entry.Feed.ID }}" title="{{ .entry.Feed.SiteURL }}" data-feed-link="true">{{ truncate .entry.Feed.Title 35 }}</a>
</li>
<li>
<time datetime="{{ isodate .entry.Date }}" title="{{ isodate .entry.Date }}">{{ elapsed .user.Timezone .entry.Date }}</time>
</li>
{{ if and .user.ShowReadingTime (gt .entry.ReadingTime 0) }}
<li>
<span>
{{ plural "entry.estimated_reading_time" .entry.ReadingTime .entry.ReadingTime }}
</span>
</li>
{{ end }}
</ul>
<ul class="item-meta-icons">
<li>
<a href="#"
title="{{ t "entry.status.title" }}"
data-toggle-status="true"
data-label-read="{{ t "entry.status.read" }}"
data-label-unread="{{ t "entry.status.unread" }}"
data-value="{{ if eq .entry.Status "read" }}read{{ else }}unread{{ end }}"
>{{ if eq .entry.Status "read" }}{{ template "icon_unread" }}{{ else }}{{ template "icon_read" }}{{ end }}<span class="icon-label">{{ if eq .entry.Status "read" }}{{ t "entry.status.unread" }}{{ else }}{{ t "entry.status.read" }}{{ end }}</span></a>
</li>
<li>
<a href="#"
data-toggle-bookmark="true"
data-bookmark-url="{{ route "toggleBookmark" "entryID" .entry.ID }}"
data-label-loading="{{ t "entry.state.saving" }}"
data-label-star="{{ t "entry.bookmark.toggle.on" }}"
data-label-unstar="{{ t "entry.bookmark.toggle.off" }}"
data-value="{{ if .entry.Starred }}star{{ else }}unstar{{ end }}"
>{{ if .entry.Starred }}{{ template "icon_unstar" }}{{ else }}{{ template "icon_star" }}{{ end }}<span class="icon-label">{{ if .entry.Starred }}{{ t "entry.bookmark.toggle.off" }}{{ else }}{{ t "entry.bookmark.toggle.on" }}{{ end }}</span></a>
</li>
{{ if .entry.ShareCode }}
<li>
<a href="{{ route "sharedEntry" "shareCode" .entry.ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
target="_blank">{{ template "icon_share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
</li>
{{ end }}
{{ if .hasSaveEntry }}
<li>
<a href="#"
title="{{ t "entry.save.title" }}"
data-save-entry="true"
data-save-url="{{ route "saveEntry" "entryID" .entry.ID }}"
data-label-loading="{{ t "entry.state.saving" }}"
data-label-done="{{ t "entry.save.completed" }}"
>{{ template "icon_save" }}<span class="icon-label">{{ t "entry.save.label" }}</span></a>
</li>
{{ end }}
<li>
<a href="{{ .entry.URL | safeURL }}"
target="_blank"
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-original-link="true">{{ template "icon_external_link" }}<span class="icon-label">{{ t "entry.external_link.label" }}</span></a>
</li>
{{ if .entry.CommentsURL }}
<li>
<a href="{{ .entry.CommentsURL | safeURL }}"
title="{{ t "entry.comments.title" }}"
target="_blank"
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-comments-link="true">{{ template "icon_comment" }}<span class="icon-label">{{ t "entry.comments.label" }}</span></a>
</li>
{{ end }}
</ul>
</div>
{{ end }}

View file

@ -0,0 +1,174 @@
{{ define "base" }}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{template "title" .}} - Miniflux</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Miniflux">
<link rel="manifest" href="{{ route "webManifest" }}" crossorigin="use-credentials"/>
<meta name="robots" content="noindex,nofollow">
<meta name="referrer" content="no-referrer">
<meta name="google" content="notranslate">
<!-- Favicons -->
<link rel="icon" type="image/png" sizes="16x16" href="{{ route "appIcon" "filename" "favicon-16.png" }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ route "appIcon" "filename" "favicon-32.png" }}">
<!-- Android icons -->
<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" }}">
<!-- iOS icons -->
<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" }}">
{{ if .csrf }}
<meta name="X-CSRF-Token" value="{{ .csrf }}">
{{ end }}
<meta name="theme-color" content="{{ theme_color .theme }}">
<link rel="stylesheet" type="text/css" href="{{ route "stylesheet" "name" .theme }}?{{ .theme_checksum }}">
{{ if and .user .user.Stylesheet }}
<link rel="stylesheet" type="text/css" href="{{ route "stylesheet" "name" "custom_css" }}">
{{ end }}
<script type="text/javascript" src="{{ route "javascript" "name" "app" }}?{{ .app_js_checksum }}" defer></script>
<script type="text/javascript" src="{{ route "javascript" "name" "service-worker" }}?{{ .sw_js_checksum }}" defer id="service-worker-script"></script>
</head>
<body
data-entries-status-url="{{ route "updateEntriesStatus" }}"
data-refresh-all-feeds-url="{{ route "refreshAllFeeds" }}"
{{ if .user }}{{ if not .user.KeyboardShortcuts }}data-disable-keyboard-shortcuts="true"{{ end }}{{ end }}>
<div class="toast-wrap">
<span class="toast-msg"></span>
</div>
{{ if .user }}
<header class="header">
<nav>
<div class="logo">
<a href="{{ route "unread" }}">Mini<span>flux</span></a>
</div>
<ul>
<li {{ if eq .menu "unread" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g u" }}">
<a href="{{ route "unread" }}" data-page="unread">{{ t "menu.unread" }}
{{ if gt .countUnread 0 }}
<span class="unread-counter-wrapper">(<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">{{ 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">{{ 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">{{ t "menu.feeds" }}
{{ if gt .countErrorFeeds 0 }}
<span class="error-feeds-counter-wrapper">(<span class="error-feeds-counter">{{ .countErrorFeeds }}</span>)</span>
{{ end }}
</a>
</li>
<li {{ if eq .menu "categories" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g c" }}">
<a href="{{ route "categories" }}" data-page="categories">{{ t "menu.categories" }}</a>
</li>
<li {{ if eq .menu "settings" }}class="active"{{ end }} title="{{ t "tooltip.keyboard_shortcuts" "g s" }}">
<a href="{{ route "settings" }}" data-page="settings">{{ t "menu.settings" }}</a>
</li>
<li>
<a href="{{ route "logout" }}" title="{{ t "tooltip.logged_user" .user.Username }}">{{ t "menu.logout" }}</a>
</li>
</ul>
<div class="search">
<div class="search-toggle-switch {{ if $.searchQuery }}has-search-query{{ end }}">
<a href="#" data-action="search">&laquo;&nbsp;{{ t "search.label" }}</a>
</div>
<form action="{{ route "searchEntries" }}" class="search-form {{ if $.searchQuery }}has-search-query{{ end }}">
<input type="search" name="q" id="search-input" placeholder="{{ t "search.placeholder" }}" {{ if $.searchQuery }}value="{{ .searchQuery }}"{{ end }} required>
</form>
</div>
</nav>
</header>
{{ end }}
{{ if .flashMessage }}
<div class="flash-message alert alert-success">{{ .flashMessage }}</div>
{{ end }}
{{ if .flashErrorMessage }}
<div class="flash-error-message alert alert-error">{{ .flashErrorMessage }}</div>
{{ end }}
<main>
{{template "content" .}}
</main>
<template id="keyboard-shortcuts">
<div id="modal-left">
<a href="#" class="btn-close-modal">x</a>
<h3>{{ 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>
</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>
</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></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" }} = <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_bookmark_status" }} = <strong>f</strong></li>
<li>{{ t "page.keyboard_shortcuts.save_article" }} = <strong>s</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">
{{ template "icon_read" }}
</template>
<template id="icon_unread">
{{ template "icon_unread" }}
</template>
<template id="icon_star">
{{ template "icon_star" }}
</template>
<template id="icon_unstar">
{{ template "icon_unstar" }}
</template>
</body>
</html>
{{ end }}

View file

@ -0,0 +1,19 @@
{{ define "pagination" }}
<div class="pagination">
<div class="pagination-prev">
{{ if .ShowPrev }}
<a href="{{ .Route }}{{ if gt .PrevOffset 0 }}?offset={{ .PrevOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}{{ else }}{{ if .SearchQuery }}?q={{ .SearchQuery }}{{ end }}{{ end }}" data-page="previous" rel="prev">{{ t "pagination.previous" }}</a>
{{ else }}
{{ t "pagination.previous" }}
{{ end }}
</div>
<div class="pagination-next">
{{ if .ShowNext }}
<a href="{{ .Route }}?offset={{ .NextOffset }}{{ if .SearchQuery }}&amp;q={{ .SearchQuery }}{{ end }}" data-page="next" rel="next">{{ t "pagination.next" }}</a>
{{ else }}
{{ t "pagination.next" }}
{{ end }}
</div>
</div>
{{ end }}

View file

@ -0,0 +1,24 @@
{{ define "settings_menu" }}
<ul>
<li>
<a href="{{ route "settings" }}">{{ t "menu.settings" }}</a>
</li>
<li>
<a href="{{ route "integrations" }}">{{ t "menu.integrations" }}</a>
</li>
<li>
<a href="{{ route "apiKeys" }}">{{ t "menu.api_keys" }}</a>
</li>
<li>
<a href="{{ route "sessions" }}">{{ t "menu.sessions" }}</a>
</li>
{{ if .user.IsAdmin }}
<li>
<a href="{{ route "users" }}">{{ t "menu.users" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "about" }}">{{ t "menu.about" }}</a>
</li>
</ul>
{{ end }}

View file

@ -0,0 +1,38 @@
{{ define "title"}}{{ t "page.about.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.about.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<div class="panel">
<h3>Miniflux</h3>
<ul>
<li><strong>{{ t "page.about.version" }}</strong> {{ .version }}</li>
<li><strong>Git Commit</strong> {{ .commit }}</li>
<li><strong>{{ t "page.about.build_date" }}</strong> {{ .build_date }}</li>
{{ if .user.IsAdmin }}<li><strong>{{ t "page.about.postgres_version" }}</strong> {{ .postgres_version }}</li>{{ end }}
</ul>
</div>
<div class="panel">
<h3>{{ t "page.about.credits" }}</h3>
<ul>
<li><strong>{{ t "page.about.author" }}</strong> Frédéric Guillot</li>
<li><strong>{{ t "page.about.license" }}</strong> Apache 2.0</li>
</ul>
</div>
{{ if .user.IsAdmin }}
<div class="panel">
<h3>{{ t "page.about.global_config_options" }}</h3>
<ul>
{{ range .globalConfigOptions }}
<li><code><strong>{{ .Key }}</strong>={{ .Value }}</code></li>
{{ end }}
</ul>
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,73 @@
{{ define "title"}}{{ t "page.add_feed.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.add_feed.title" }}</h1>
{{ template "feed_menu" }}
</section>
{{ if not .categories }}
<p class="alert alert-error">{{ t "page.add_feed.no_category" }}</p>
{{ else }}
<form action="{{ route "submitSubscription" }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-url">{{ t "page.add_feed.label.url" }}</label>
<input type="url" name="url" id="form-url" placeholder="https://domain.tld/" value="{{ .form.URL }}" spellcheck="false" required autofocus>
<label for="form-category">{{ t "form.feed.label.category" }}</label>
<select id="form-category" name="category_id">
{{ range .categories }}
<option value="{{ .ID }}" {{ if eq $.form.CategoryID .ID }}selected="selected"{{ end }}>{{ .Title }}</option>
{{ end }}
</select>
<details>
<summary>{{ t "page.add_feed.legend.advanced_options" }}</summary>
<div class="details-content">
<label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
{{ if .hasProxyConfigured }}
<label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label>
{{ end }}
<label for="form-user-agent">{{ t "form.feed.label.user_agent" }}</label>
<input type="text" name="user_agent" id="form-user-agent" placeholder="{{ .defaultUserAgent }}" value="{{ .form.UserAgent }}" spellcheck="false" autocomplete="off">
<label for="form-feed-username">{{ t "form.feed.label.feed_username" }}</label>
<input type="text" name="feed_username" id="form-feed-username" value="{{ .form.Username }}" spellcheck="false">
<label for="form-feed-password">{{ t "form.feed.label.feed_password" }}</label>
<!--
We are using the type "text" otherwise Firefox always autocomplete this password:
- autocomplete="off" or autocomplete="new-password" doesn't change anything
- Changing the input ID doesn't change anything
- Using a different input name doesn't change anything
-->
<input type="text" name="feed_password" id="form-feed-password" value="{{ .form.Password }}" spellcheck="false">
<label for="form-scraper-rules">{{ t "form.feed.label.scraper_rules" }}</label>
<input type="text" name="scraper_rules" id="form-scraper-rules" value="{{ .form.ScraperRules }}" spellcheck="false">
<label for="form-rewrite-rules">{{ t "form.feed.label.rewrite_rules" }}</label>
<input type="text" name="rewrite_rules" id="form-rewrite-rules" value="{{ .form.RewriteRules }}" spellcheck="false">
<label for="form-blocklist-rules">{{ t "form.feed.label.blocklist_rules" }}</label>
<input type="text" name="blocklist_rules" id="form-blocklist-rules" value="{{ .form.BlocklistRules }}" spellcheck="false">
<label for="form-keeplist-rules">{{ t "form.feed.label.keeplist_rules" }}</label>
<input type="text" name="keeplist_rules" id="form-keeplist-rules" value="{{ .form.KeeplistRules }}" spellcheck="false">
</div>
</details>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.loading" }}">{{ t "page.add_feed.submit" }}</button>
</div>
</form>
{{ end }}
{{ end }}

View file

@ -0,0 +1,72 @@
{{ define "title"}}{{ t "page.api_keys.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.api_keys.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
{{ if .apiKeys }}
{{ range .apiKeys }}
<table>
<tr>
<th class="column-25">{{ t "page.api_keys.table.description" }}</th>
<td>{{ .Description }}</td>
</tr>
<tr>
<th>{{ t "page.api_keys.table.token" }}</th>
<td>{{ .Token }}</td>
</tr>
<tr>
<th>{{ t "page.api_keys.table.last_used_at" }}</th>
<td>
{{ if .LastUsedAt }}
<time datetime="{{ isodate .LastUsedAt }}" title="{{ isodate .LastUsedAt }}">{{ elapsed $.user.Timezone .LastUsedAt }}</time>
{{ else }}
{{ t "page.api_keys.never_used" }}
{{ end }}
</td>
</tr>
<tr>
<th>{{ t "page.api_keys.table.created_at" }}</th>
<td>
<time datetime="{{ isodate .CreatedAt }}" title="{{ isodate .CreatedAt }}">{{ elapsed $.user.Timezone .CreatedAt }}</time>
</td>
</tr>
<tr>
<th>{{ t "page.api_keys.table.actions" }}</th>
<td>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeAPIKey" "keyID" .ID }}">{{ t "action.remove" }}</a>
</td>
</tr>
</table>
<br>
{{ end }}
{{ end }}
<h3>{{ t "page.integration.miniflux_api" }}</h3>
<div class="panel">
<ul>
<li>
{{ t "page.integration.miniflux_api_endpoint" }} = <strong>{{ baseURL }}/v1/</strong>
</li>
<li>
{{ t "page.integration.miniflux_api_username" }} = <strong>{{ .user.Username }}</strong>
</li>
<li>
{{ t "page.integration.miniflux_api_password" }} = <strong>{{ t "page.integration.miniflux_api_password_value" }}</strong>
</li>
</ul>
</div>
<p>
<a href="{{ route "createAPIKey" }}" class="button button-primary">{{ t "menu.create_api_key" }}</a>
</p>
{{ end }}

View file

@ -0,0 +1,30 @@
{{ define "title"}}{{ t "page.starred.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.starred.title" }} ({{ .total }})</h1>
</section>
{{ if not .entries }}
<p class="alert alert-info">{{ t "alert.no_bookmark" }}</p>
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "starredEntry" "entryID" .ID }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,59 @@
{{ define "title"}}{{ t "page.categories.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.categories.title" }} ({{ .total }})</h1>
<ul>
<li>
<a href="{{ route "createCategory" }}">{{ t "menu.create_category" }}</a>
</li>
</ul>
</section>
{{ if not .categories }}
<p class="alert alert-error">{{ t "alert.no_category" }}</p>
{{ else }}
<div class="items">
{{ range .categories }}
<article class="item">
<div class="item-header" dir="auto">
<span class="item-title">
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ .Title }}</a>
</span>
(<span title="{{ if eq .FeedCount 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" .FeedCount .FeedCount }}{{ end }}">{{ .FeedCount }}</span>)
</div>
<div class="item-meta">
<ul class="item-meta-info">
<li>
{{ if eq .FeedCount 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" .FeedCount .FeedCount }}{{ end }}
</li>
</ul>
<ul class="item-meta-icons">
<li>
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ template "icon_entries" }}<span class="icon-label">{{ t "page.categories.entries" }}</span></a>
</li>
<li>
<a href="{{ route "categoryFeeds" "categoryID" .ID }}">{{ template "icon_feeds" }}<span class="icon-label">{{ t "page.categories.feeds" }}</span></a>
</li>
<li>
<a href="{{ route "editCategory" "categoryID" .ID }}">{{ template "icon_edit" }}<span class="icon-label">{{ t "menu.edit_category" }}</span></a>
</li>
{{ if eq .FeedCount 0 }}
<li>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeCategory" "categoryID" .ID }}">{{ template "icon_delete" }}<span class="icon-label">{{ t "action.remove" }}</span></a>
</li>
{{ end }}
</ul>
</div>
</article>
{{ end }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,70 @@
{{ define "title"}}{{ .category.Title }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1 dir="auto">{{ .category.Title }} ({{ .total }})</h1>
<ul>
{{ if .entries }}
<li>
<a href="#"
data-action="markPageAsRead"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-show-only-unread="{{ if .showOnlyUnreadEntries }}1{{ end }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
{{ end }}
{{ if .showOnlyUnreadEntries }}
<li>
<a href="{{ route "categoryEntriesAll" "categoryID" .category.ID }}">{{ t "menu.show_all_entries" }}</a>
</li>
{{ else }}
<li>
<a href="{{ route "categoryEntries" "categoryID" .category.ID }}">{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
</li>
</ul>
</section>
{{ if not .entries }}
<p class="alert">{{ t "alert.no_category_entry" }}</p>
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "categoryEntry" "categoryID" .Feed.Category.ID "entryID" .ID }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
<section class="page-footer">
{{ if .entries }}
<ul>
<li>
<a href="#"
data-action="markPageAsRead"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-show-only-unread="{{ if .showOnlyUnreadEntries }}1{{ end }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,34 @@
{{ define "title"}}{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1 dir="auto">{{ .category.Title }} &gt; {{ t "page.feeds.title" }} ({{ .total }})</h1>
<ul>
<li>
<a href="{{ route "categoryEntries" "categoryID" .category.ID }}">{{ t "menu.feed_entries" }}</a>
</li>
<li>
<a href="{{ route "editCategory" "categoryID" .category.ID }}">{{ t "menu.edit_category" }}</a>
</li>
{{ if eq .total 0 }}
<li>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-redirect-url="{{ route "categories" }}"
data-url="{{ route "removeCategory" "categoryID" .category.ID }}">{{ t "action.remove" }}</a>
</li>
{{ end }}
</ul>
</section>
{{ if not .feeds }}
<p class="alert">{{ t "alert.no_feed_in_category" }}</p>
{{ else }}
{{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,37 @@
{{ define "title"}}{{ t "page.add_feed.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.add_feed.title" }}</h1>
{{ template "feed_menu" }}
</section>
<form action="{{ route "chooseSubscription" }}" method="POST">
<input type="hidden" name="csrf" value="{{ .csrf }}">
<input type="hidden" name="category_id" value="{{ .form.CategoryID }}">
<input type="hidden" name="user_agent" value="{{ .form.UserAgent }}">
<input type="hidden" name="feed_username" value="{{ .form.Username }}">
<input type="hidden" name="feed_password" value="{{ .form.Password }}">
<input type="hidden" name="scraper_rules" value="{{ .form.ScraperRules }}">
<input type="hidden" name="rewrite_rules" value="{{ .form.RewriteRules }}">
{{ if .form.FetchViaProxy }}
<input type="hidden" name="fetch_via_proxy" value="1">
{{ end }}
{{ if .form.Crawler }}
<input type="hidden" name="crawler" value="1">
{{ end }}
<h3>{{ t "page.add_feed.choose_feed" }}</h3>
{{ range .subscriptions }}
<div class="radio-group">
<label title="{{ .URL | safeURL }}"><input type="radio" name="url" value="{{ .URL | safeURL }}"> {{ .Title }}</label> ({{ .Type }})
<small title="Type = {{ .Type }}"><a href="{{ .URL | safeURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a></small>
</div>
{{ end }}
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.loading" }}">{{ t "action.subscribe" }}</button>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,23 @@
{{ define "title"}}{{ t "page.new_api_key.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.new_api_key.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<form action="{{ route "saveAPIKey" }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-description">{{ t "form.api_key.label.description" }}</label>
<input type="text" name="description" id="form-description" value="{{ .form.Description }}" spellcheck="false" required autofocus>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.save" }}</button> {{ t "action.or" }} <a href="{{ route "apiKeys" }}">{{ t "action.cancel" }}</a>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,27 @@
{{ define "title"}}{{ t "page.new_category.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.new_category.title" }}</h1>
<ul>
<li>
<a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
</li>
</ul>
</section>
<form action="{{ route "saveCategory" }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-title">{{ t "form.category.label.title" }}</label>
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" required autofocus>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.save" }}</button> {{ t "action.or" }} <a href="{{ route "categories" }}">{{ t "action.cancel" }}</a>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,31 @@
{{ define "title"}}{{ t "page.new_user.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.new_user.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<form action="{{ route "saveUser" }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-username">{{ t "form.user.label.username" }}</label>
<input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" spellcheck="false" required autofocus>
<label for="form-password">{{ t "form.user.label.password" }}</label>
<input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="new-password" required>
<label for="form-confirmation">{{ t "form.user.label.confirmation" }}</label>
<input type="password" name="confirmation" id="form-confirmation" value="{{ .form.Confirmation }}" autocomplete="new-password" required>
<label><input type="checkbox" name="is_admin" value="1" {{ if .form.IsAdmin }}checked{{ end }}> {{ t "form.user.label.admin" }}</label>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.save" }}</button> {{ t "action.or" }} <a href="{{ route "users" }}">{{ t "action.cancel" }}</a>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,33 @@
{{ define "title"}}{{ t "page.edit_category.title" .category.Title }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.edit_category.title" .category.Title }}</h1>
<ul>
<li>
<a href="{{ route "categories" }}">{{ t "menu.categories" }}</a>
</li>
<li>
<a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "createCategory" }}">{{ t "menu.create_category" }}</a>
</li>
</ul>
</section>
<form action="{{ route "updateCategory" "categoryID" .category.ID }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-title">{{ t "form.category.label.title" }}</label>
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" required autofocus>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,113 @@
{{ define "title"}}{{ t "page.edit_feed.title" .feed.Title }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1 dir="auto">{{ .feed.Title }}</h1>
<ul>
<li>
<a href="{{ route "feeds" }}">{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "feedEntries" "feedID" .feed.ID }}">{{ t "menu.feed_entries" }}</a>
</li>
<li>
<a href="{{ route "refreshFeed" "feedID" .feed.ID }}">{{ t "menu.refresh_feed" }}</a>
</li>
</ul>
</section>
{{ if not .categories }}
<p class="alert alert-error">{{ t "page.add_feed.no_category" }}</p>
{{ else }}
{{ if ne .feed.ParsingErrorCount 0 }}
<div class="alert alert-error">
<h3>{{ t "page.edit_feed.last_parsing_error" }}</h3>
<p>{{ t .feed.ParsingErrorMsg }}</p>
</div>
{{ end }}
<form action="{{ route "updateFeed" "feedID" .feed.ID }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-title">{{ t "form.feed.label.title" }}</label>
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" spellcheck="false" required autofocus>
<label for="form-site-url">{{ t "form.feed.label.site_url" }}</label>
<input type="url" name="site_url" id="form-site-url" placeholder="https://domain.tld/" value="{{ .form.SiteURL }}" spellcheck="false" required>
<label for="form-feed-url">{{ t "form.feed.label.feed_url" }}</label>
<input type="url" name="feed_url" id="form-feed-url" placeholder="https://domain.tld/" value="{{ .form.FeedURL }}" spellcheck="false" required>
<label for="form-feed-username">{{ t "form.feed.label.feed_username" }}</label>
<input type="text" name="feed_username" id="form-feed-username" value="{{ .form.Username }}" spellcheck="false">
<label for="form-feed-password">{{ t "form.feed.label.feed_password" }}</label>
<!--
We are using the type "text" otherwise Firefox always autocomplete this password:
- autocomplete="off" or autocomplete="new-password" doesn't change anything
- Changing the input ID doesn't change anything
- Using a different input name doesn't change anything
-->
<input type="text" name="feed_password" id="form-feed-password" value="{{ .form.Password }}" spellcheck="false">
<label for="form-user-agent">{{ t "form.feed.label.user_agent" }}</label>
<input type="text" name="user_agent" id="form-user-agent" placeholder="{{ .defaultUserAgent }}" value="{{ .form.UserAgent }}" spellcheck="false">
<label for="form-scraper-rules">{{ t "form.feed.label.scraper_rules" }}</label>
<input type="text" name="scraper_rules" id="form-scraper-rules" value="{{ .form.ScraperRules }}" spellcheck="false">
<label for="form-rewrite-rules">{{ t "form.feed.label.rewrite_rules" }}</label>
<input type="text" name="rewrite_rules" id="form-rewrite-rules" value="{{ .form.RewriteRules }}" spellcheck="false">
<label for="form-blocklist-rules">{{ t "form.feed.label.blocklist_rules" }}</label>
<input type="text" name="blocklist_rules" id="form-blocklist-rules" value="{{ .form.BlocklistRules }}" spellcheck="false">
<label for="form-keeplist-rules">{{ t "form.feed.label.keeplist_rules" }}</label>
<input type="text" name="keeplist_rules" id="form-keeplist-rules" value="{{ .form.KeeplistRules }}" spellcheck="false">
<label for="form-category">{{ t "form.feed.label.category" }}</label>
<select id="form-category" name="category_id">
{{ range .categories }}
<option value="{{ .ID }}" {{ if eq .ID $.form.CategoryID }}selected="selected"{{ end }}>{{ .Title }}</option>
{{ end }}
</select>
<label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
<label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label>
{{ if .hasProxyConfigured }}
<label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label>
{{ end }}
<label><input type="checkbox" name="disabled" value="1" {{ if .form.Disabled }}checked{{ end }}> {{ t "form.feed.label.disabled" }}</label>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> {{ t "action.or" }} <a href="{{ route "feeds" }}">{{ t "action.cancel" }}</a>
</div>
</form>
<div class="panel">
<ul>
<li><strong>{{ t "page.edit_feed.last_check" }} </strong><time datetime="{{ isodate .feed.CheckedAt }}" title="{{ isodate .feed.CheckedAt }}">{{ elapsed $.user.Timezone .feed.CheckedAt }}</time></li>
<li><strong>{{ t "page.edit_feed.etag_header" }} </strong>{{ if .feed.EtagHeader }}{{ .feed.EtagHeader }}{{ else }}{{ t "page.edit_feed.no_header" }}{{ end }}</li>
<li><strong>{{ t "page.edit_feed.last_modified_header" }} </strong>{{ if .feed.LastModifiedHeader }}{{ .feed.LastModifiedHeader }}{{ else }}{{ t "page.edit_feed.no_header" }}{{ end }}</li>
</ul>
</div>
<div class="alert alert-error">
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeFeed" "feedID" .feed.ID }}"
data-redirect-url="{{ route "feeds" }}">{{ t "action.remove_feed" }}</a>
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,31 @@
{{ define "title"}}{{ t "page.edit_user.title" .selected_user.Username }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.edit_user.title" .selected_user.Username }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<form action="{{ route "updateUser" "userID" .selected_user.ID }}" method="post" autocomplete="off">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-username">{{ t "form.user.label.username" }}</label>
<input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" spellcheck="false" required autofocus>
<label for="form-password">{{ t "form.user.label.password" }}</label>
<input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="new-password">
<label for="form-confirmation">{{ t "form.user.label.confirmation" }}</label>
<input type="password" name="confirmation" id="form-confirmation" value="{{ .form.Confirmation }}" autocomplete="new-password">
<label><input type="checkbox" name="is_admin" value="1" {{ if .form.IsAdmin }}checked{{ end }}> {{ t "form.user.label.admin" }}</label>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> {{ t "action.or" }} <a href="{{ route "users" }}">{{ t "action.cancel" }}</a>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,179 @@
{{ define "title"}}{{ .entry.Title }}{{ end }}
{{ define "content"}}
<section class="entry" data-id="{{ .entry.ID }}">
<header class="entry-header">
<h1 dir="auto">
<a href="{{ .entry.URL | safeURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .entry.Title }}</a>
</h1>
{{ if .user }}
<div class="entry-actions">
<ul>
<li>
<a href="#"
title="{{ t "entry.status.title" }}"
data-toggle-status="true"
data-label-unread="{{ t "entry.status.unread" }}"
data-label-read="{{ t "entry.status.read" }}"
data-toast-unread="✘&nbsp;{{ t "entry.status.toast.unread" }}"
data-toast-read="✔︎&nbsp;{{ t "entry.status.toast.read" }}"
data-value="{{ if eq .entry.Status "read" }}read{{ else }}unread{{ end }}"
>{{ if eq .entry.Status "unread" }}{{ template "icon_read" }}{{ else }}{{ template "icon_unread" }}{{ end }}<span class="icon-label">{{ if eq .entry.Status "unread" }}{{ t "entry.status.read" }}{{ else }}{{ t "entry.status.unread" }}{{ end }}</span></a>
</li>
<li>
<a href="#"
data-toggle-bookmark="true"
data-bookmark-url="{{ route "toggleBookmark" "entryID" .entry.ID }}"
data-label-loading="{{ t "entry.state.saving" }}"
data-label-star="{{ t "entry.bookmark.toggle.on" }}"
data-label-unstar="{{ t "entry.bookmark.toggle.off" }}"
data-toast-star="★&nbsp;{{ t "entry.bookmark.toast.on" }}"
data-toast-unstar="☆&nbsp;{{ t "entry.bookmark.toast.off" }}"
data-value="{{ if .entry.Starred }}star{{ else }}unstar{{ end }}"
>{{ if .entry.Starred }}{{ template "icon_unstar" }}{{ else }}{{ template "icon_star" }}{{ end }}<span class="icon-label">{{ if .entry.Starred }}{{ t "entry.bookmark.toggle.off" }}{{ else }}{{ t "entry.bookmark.toggle.on" }}{{ end }}</span></a>
</li>
{{ if .hasSaveEntry }}
<li>
<a href="#"
title="{{ t "entry.save.title" }}"
data-save-entry="true"
data-save-url="{{ route "saveEntry" "entryID" .entry.ID }}"
data-label-loading="{{ t "entry.state.saving" }}"
data-label-done="{{ t "entry.save.completed" }}"
data-toast-done="{{ t "entry.save.toast.completed" }}"
>{{ template "icon_save" }}<span class="icon-label">{{ t "entry.save.label" }}</span></a>
</li>
{{ end }}
<li>
{{ if .entry.ShareCode }}
<a href="{{ route "sharedEntry" "shareCode" .entry.ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
target="_blank">{{ template "icon_share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
{{ else }}
<a href="{{ route "shareEntry" "entryID" .entry.ID }}"
title="{{ t "entry.share.title" }}"
target="_blank">{{ template "icon_share" }}<span class="icon-label">{{ t "entry.share.label" }}</span></a>
{{ end }}
</li>
<li>
<a href="{{ .entry.URL | safeURL }}"
target="_blank"
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-original-link="true">{{ template "icon_external_link" }}<span class="icon-label">{{ t "entry.external_link.label" }}</span></a>
</li>
<li>
<a href="#"
title="{{ t "entry.scraper.title" }}"
data-fetch-content-entry="true"
data-fetch-content-url="{{ route "fetchContent" "entryID" .entry.ID }}"
data-label-loading="{{ t "entry.state.loading" }}"
>{{ template "icon_scraper" }}<span class="icon-label">{{ t "entry.scraper.label" }}</span></a>
</li>
{{ if .entry.CommentsURL }}
<li>
<a href="{{ .entry.CommentsURL | safeURL }}"
title="{{ t "entry.comments.title" }}"
target="_blank"
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-comments-link="true"
>{{ template "icon_comment" }}<span class="icon-label">{{ t "entry.comments.label" }}</span></a>
</li>
{{ end }}
</ul>
</div>
{{ end }}
<div class="entry-meta" dir="auto">
<span class="entry-website">
{{ if and .user (ne .entry.Feed.Icon.IconID 0) }}
<img src="{{ route "icon" "iconID" .entry.Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .entry.Feed.Title }}">
{{ end }}
{{ if .user }}
<a href="{{ route "feedEntries" "feedID" .entry.Feed.ID }}">{{ .entry.Feed.Title }}</a>
{{ else }}
<a href="{{ .entry.Feed.SiteURL | safeURL }}">{{ .entry.Feed.Title }}</a>
{{ end }}
</span>
{{ if .entry.Author }}
<span class="entry-author">
{{ if isEmail .entry.Author }}
- <a href="mailto:{{ .entry.Author }}">{{ .entry.Author }}</a>
{{ else }}
<em>{{ .entry.Author }}</em>
{{ end }}
</span>
{{ end }}
{{ if .user }}
<span class="category">
<a href="{{ route "categoryEntries" "categoryID" .entry.Feed.Category.ID }}">{{ .entry.Feed.Category.Title }}</a>
</span>
{{ end }}
</div>
<div class="entry-date">
{{ if .user }}
<time datetime="{{ isodate .entry.Date }}" title="{{ isodate .entry.Date }}">{{ elapsed $.user.Timezone .entry.Date }}</time>
{{ else }}
<time datetime="{{ isodate .entry.Date }}" title="{{ isodate .entry.Date }}">{{ elapsed "UTC" .entry.Date }}</time>
{{ end }}
</div>
</header>
{{ if gt (len .entry.Content) 120 }}
{{ if .user }}
<div class="pagination-top">
{{ template "entry_pagination" . }}
</div>
{{ end }}
{{ end }}
<article class="entry-content" dir="auto">
{{ if .user }}
{{ noescape (proxyFilter .entry.Content) }}
{{ else }}
{{ noescape .entry.Content }}
{{ end }}
</article>
{{ if .entry.Enclosures }}
<details class="entry-enclosures">
<summary>{{ t "page.entry.attachments" }} ({{ len .entry.Enclosures }})</summary>
{{ range .entry.Enclosures }}
{{ if ne .URL "" }}
<div class="entry-enclosure">
{{ if hasPrefix .MimeType "audio/" }}
<div class="enclosure-audio">
<audio controls preload="metadata">
<source src="{{ .URL | safeURL }}" type="{{ .MimeType }}">
</audio>
</div>
{{ else if hasPrefix .MimeType "video/" }}
<div class="enclosure-video">
<video controls preload="metadata">
<source src="{{ .URL | safeURL }}" type="{{ .MimeType }}">
</video>
</div>
{{ else if hasPrefix .MimeType "image/" }}
<div class="enclosure-image">
{{ if $.user }}
<img src="{{ proxyURL .URL }}" title="{{ .URL }} ({{ .MimeType }})" loading="lazy" alt="{{ .URL }} ({{ .MimeType }})">
{{ else }}
<img src="{{ .URL | safeURL }}" title="{{ .URL }} ({{ .MimeType }})" loading="lazy" alt="{{ .URL }} ({{ .MimeType }})">
{{ end }}
</div>
{{ end }}
<div class="entry-enclosure-download">
<a href="{{ .URL | safeURL }}" title="{{ t "action.download" }}{{ if gt .Size 0 }} - {{ formatFileSize .Size }}{{ end }} ({{ .MimeType }})" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">{{ .URL | safeURL }}</a>
<small>{{ if gt .Size 0 }} - <strong>{{ formatFileSize .Size }}</strong>{{ end }}</small>
</div>
</div>
{{ end }}
{{ end }}
</details>
{{ end }}
</section>
{{ if .user }}
<div class="pagination-bottom">
{{ template "entry_pagination" . }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,98 @@
{{ define "title"}}{{ .feed.Title }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1 dir="auto">
<a href="{{ .feed.SiteURL | safeURL }}" title="{{ .feed.SiteURL }}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer" data-original-link="true">{{ .feed.Title }}</a>
({{ .total }})
</h1>
<ul>
{{ if .entries }}
<li>
<a href="#"
data-action="markPageAsRead"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-show-only-unread="{{ if .showOnlyUnreadEntries }}1{{ end }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
{{ end }}
{{ if .showOnlyUnreadEntries }}
<li>
<a href="{{ route "feedEntriesAll" "feedID" .feed.ID }}">{{ t "menu.show_all_entries" }}</a>
</li>
{{ else }}
<li>
<a href="{{ route "feedEntries" "feedID" .feed.ID }}">{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "refreshFeed" "feedID" .feed.ID }}">{{ t "menu.refresh_feed" }}</a>
</li>
<li>
<a href="{{ route "editFeed" "feedID" .feed.ID }}">{{ t "menu.edit_feed" }}</a>
</li>
<li>
<a href="#"
data-confirm="true"
data-action="remove-feed"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeFeed" "feedID" .feed.ID }}"
data-redirect-url="{{ route "feeds" }}">{{ t "action.remove_feed" }}</a>
</li>
</ul>
</section>
{{ if ne .feed.ParsingErrorCount 0 }}
<div class="alert alert-error">
<h3>{{ t "alert.feed_error" }}</h3>
<p>{{ t .feed.ParsingErrorMsg }}</p>
</div>
{{ end }}
{{ if not .entries }}
{{ if .showOnlyUnreadEntries }}
<p class="alert">{{ t "alert.no_unread_entry" }}</p>
{{ else }}
<p class="alert">{{ t "alert.no_feed_entry" }}</p>
{{ end }}
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "feedEntry" "feedID" .Feed.ID "entryID" .ID }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
<section class="page-footer">
{{ if .entries }}
<ul>
<li>
<a href="#"
data-action="markPageAsRead"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-show-only-unread="{{ if .showOnlyUnreadEntries }}1{{ end }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,15 @@
{{ define "title"}}{{ t "page.feeds.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.feeds.title" }} ({{ .total }})</h1>
{{ template "feed_menu" }}
</section>
{{ if not .feeds }}
<p class="alert">{{ t "alert.no_feed" }}</p>
{{ else }}
{{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,52 @@
{{ define "title"}}{{ t "page.history.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.history.title" }} ({{ .total }})</h1>
{{ if .entries }}
<ul>
<li>
<a href="#"
data-confirm="true"
data-url="{{ route "flushHistory" }}"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "menu.flush_history" }}</a>
</li>
<li>
<a href="{{ route "sharedEntries" }}">{{ t "menu.shared_entries" }}</a>
</li>
</ul>
{{ else }}
<ul>
<li>
<a href="{{ route "sharedEntries" }}">{{ t "menu.shared_entries" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ if not .entries }}
<p class="alert alert-info">{{ t "alert.no_history" }}</p>
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "readEntry" "entryID" .ID }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,35 @@
{{ define "title"}}{{ t "page.import.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.import.title" }}</h1>
{{ template "feed_menu" }}
</section>
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<form action="{{ route "uploadOPML" }}" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf" value="{{ .csrf }}">
<label for="form-file">{{ t "form.import.label.file" }}</label>
<input type="file" name="file" id="form-file">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.import" }}</button>
</div>
</form>
<hr>
<form action="{{ route "fetchOPML" }}" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf" value="{{ .csrf }}">
<label for="form-url">{{ t "form.import.label.url" }}</label>
<input type="url" name="url" id="form-url" required>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.import" }}</button>
</div>
</form>
{{ end }}

View file

@ -0,0 +1,152 @@
{{ define "title"}}{{ t "page.integrations.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.integrations.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<form method="post" autocomplete="off" action="{{ route "updateIntegration" }}">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<h3>Fever</h3>
<div class="form-section">
<label>
<input type="checkbox" name="fever_enabled" value="1" {{ if .form.FeverEnabled }}checked{{ end }}> {{ t "form.integration.fever_activate" }}
</label>
<label for="form-fever-username">{{ t "form.integration.fever_username" }}</label>
<input type="text" name="fever_username" id="form-fever-username" value="{{ .form.FeverUsername }}" autocomplete="username" spellcheck="false">
<label for="form-fever-password">{{ t "form.integration.fever_password" }}</label>
<input type="password" name="fever_password" id="form-fever-password" value="{{ .form.FeverPassword }}" autocomplete="new-password">
<p>{{ t "form.integration.fever_endpoint" }} <strong>{{ rootURL }}{{ route "feverEndpoint" }}</strong></p>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Pinboard</h3>
<div class="form-section">
<label>
<input type="checkbox" name="pinboard_enabled" value="1" {{ if .form.PinboardEnabled }}checked{{ end }}> {{ t "form.integration.pinboard_activate" }}
</label>
<label for="form-pinboard-token">{{ t "form.integration.pinboard_token" }}</label>
<input type="password" name="pinboard_token" id="form-pinboard-token" value="{{ .form.PinboardToken }}" autocomplete="new-password">
<label for="form-pinboard-tags">{{ t "form.integration.pinboard_tags" }}</label>
<input type="text" name="pinboard_tags" id="form-pinboard-tags" value="{{ .form.PinboardTags }}" spellcheck="false">
<label>
<input type="checkbox" name="pinboard_mark_as_unread" value="1" {{ if .form.PinboardMarkAsUnread }}checked{{ end }}> {{ t "form.integration.pinboard_bookmark" }}
</label>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Instapaper</h3>
<div class="form-section">
<label>
<input type="checkbox" name="instapaper_enabled" value="1" {{ if .form.InstapaperEnabled }}checked{{ end }}> {{ t "form.integration.instapaper_activate" }}
</label>
<label for="form-instapaper-username">{{ t "form.integration.instapaper_username" }}</label>
<input type="text" name="instapaper_username" id="form-instapaper-username" value="{{ .form.InstapaperUsername }}" spellcheck="false">
<label for="form-instapaper-password">{{ t "form.integration.instapaper_password" }}</label>
<input type="password" name="instapaper_password" id="form-instapaper-password" value="{{ .form.InstapaperPassword }}" autocomplete="new-password">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Pocket</h3>
<div class="form-section">
<label>
<input type="checkbox" name="pocket_enabled" value="1" {{ if .form.PocketEnabled }}checked{{ end }}> {{ t "form.integration.pocket_activate" }}
</label>
{{ if not .hasPocketConsumerKeyConfigured }}
<label for="form-pocket-consumer-key">{{ t "form.integration.pocket_consumer_key" }}</label>
<input type="text" name="pocket_consumer_key" id="form-pocket-consumer-key" value="{{ .form.PocketConsumerKey }}" spellcheck="false">
{{ end }}
<label for="form-pocket-access-token">{{ t "form.integration.pocket_access_token" }}</label>
<input type="password" name="pocket_access_token" id="form-pocket-access-token" value="{{ .form.PocketAccessToken }}" autocomplete="new-password">
{{ if not .form.PocketAccessToken }}
<p><a href="{{ route "pocketAuthorize" }}">{{ t "form.integration.pocket_connect_link" }}</a></p>
{{ end }}
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Wallabag</h3>
<div class="form-section">
<label>
<input type="checkbox" name="wallabag_enabled" value="1" {{ if .form.WallabagEnabled }}checked{{ end }}> {{ t "form.integration.wallabag_activate" }}
</label>
<label for="form-wallabag-url">{{ t "form.integration.wallabag_endpoint" }}</label>
<input type="url" name="wallabag_url" id="form-wallabag-url" value="{{ .form.WallabagURL }}" placeholder="http://v2.wallabag.org/" spellcheck="false">
<label for="form-wallabag-client-id">{{ t "form.integration.wallabag_client_id" }}</label>
<input type="text" name="wallabag_client_id" id="form-wallabag-client-id" value="{{ .form.WallabagClientID }}" spellcheck="false">
<label for="form-wallabag-client-secret">{{ t "form.integration.wallabag_client_secret" }}</label>
<input type="password" name="wallabag_client_secret" id="form-wallabag-client-secret" value="{{ .form.WallabagClientSecret }}" autocomplete="new-password">
<label for="form-wallabag-username">{{ t "form.integration.wallabag_username" }}</label>
<input type="text" name="wallabag_username" id="form-wallabag-username" value="{{ .form.WallabagUsername }}" spellcheck="false">
<label for="form-wallabag-password">{{ t "form.integration.wallabag_password" }}</label>
<input type="password" name="wallabag_password" id="form-wallabag-password" value="{{ .form.WallabagPassword }}" autocomplete="new-password">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
<h3>Nunux Keeper</h3>
<div class="form-section">
<label>
<input type="checkbox" name="nunux_keeper_enabled" value="1" {{ if .form.NunuxKeeperEnabled }}checked{{ end }}> {{ t "form.integration.nunux_keeper_activate" }}
</label>
<label for="form-nunux-keeper-url">{{ t "form.integration.nunux_keeper_endpoint" }}</label>
<input type="url" name="nunux_keeper_url" id="form-nunux-keeper-url" value="{{ .form.NunuxKeeperURL }}" placeholder="https://api.nunux.org/keeper" spellcheck="false">
<label for="form-nunux-keeper-api-key">{{ t "form.integration.nunux_keeper_api_key" }}</label>
<input type="text" name="nunux_keeper_api_key" id="form-nunux-keeper-api-key" value="{{ .form.NunuxKeeperAPIKey }}" spellcheck="false">
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
</form>
<h3>{{ t "page.integration.bookmarklet" }}</h3>
<div class="panel">
<p>{{ t "page.integration.bookmarklet.help" }}</p>
<div class="bookmarklet">
<a href="javascript:location.href='{{ rootURL }}{{ route "bookmarklet" }}?uri='+encodeURIComponent(window.location.href)">{{ t "page.integration.bookmarklet.name" }}</a>
</div>
<p>{{ t "page.integration.bookmarklet.instructions" }}</p>
</div>
{{ end }}

View file

@ -0,0 +1,35 @@
{{ define "title"}}{{ t "page.login.title" }}{{ end }}
{{ define "content"}}
<section class="login-form">
<form action="{{ route "checkLogin" }}" method="post">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-username">{{ t "form.user.label.username" }}</label>
<input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" required autofocus>
<label for="form-password">{{ t "form.user.label.password" }}</label>
<input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="current-password" required>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.loading" }}">{{ t "action.login" }}</button>
</div>
</form>
{{ if hasOAuth2Provider "google" }}
<div class="oauth2">
<a href="{{ route "oauth2Redirect" "provider" "google" }}">{{ t "page.login.google_signin" }}</a>
</div>
{{ else if hasOAuth2Provider "oidc" }}
<div class="oauth2">
<a href="{{ route "oauth2Redirect" "provider" "oidc" }}">{{ t "page.login.oidc_signin" }}</a>
</div>
{{ end }}
</section>
<footer id="prompt-home-screen">
<a href="#" id="btn-add-to-home-screen">★ {{ t "action.home_screen" }}</a>
</footer>
{{ end }}

View file

@ -0,0 +1,30 @@
{{ define "title"}}{{ t "page.search.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.search.title" }} ({{ .total }})</h1>
</section>
{{ if not .entries }}
<p class="alert alert-info">{{ t "alert.no_search_result" }}</p>
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "searchEntry" "entryID" .ID }}?q={{ $.searchQuery }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,38 @@
{{ define "title"}}{{ t "page.sessions.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.sessions.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<table>
<tr>
<th>{{ t "page.sessions.table.date" }}</th>
<th>{{ t "page.sessions.table.ip" }}</th>
<th>{{ t "page.sessions.table.user_agent" }}</th>
<th>{{ t "page.sessions.table.actions" }}</th>
</tr>
{{ range .sessions }}
<tr {{ if eq .Token $.currentSessionToken }}class="row-highlighted"{{ end }}>
<td class="column-20" title="{{ isodate .CreatedAt }}">{{ elapsed $.user.Timezone .CreatedAt }}</td>
<td class="column-20" title="{{ .IP }}">{{ .IP }}</td>
<td title="{{ .UserAgent }}">{{ .UserAgent }}</td>
<td class="column-20">
{{ if eq .Token $.currentSessionToken }}
{{ t "page.sessions.table.current_session" }}
{{ else }}
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeSession" "sessionID" .ID }}">{{ t "action.remove" }}</a>
{{ end }}
</td>
</tr>
{{ end }}
</table>
{{ end }}

View file

@ -0,0 +1,85 @@
{{ define "title"}}{{ t "page.settings.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.settings.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
<form method="post" autocomplete="off" action="{{ route "updateSettings" }}">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<label for="form-username">{{ t "form.user.label.username" }}</label>
<input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" required>
<label for="form-password">{{ t "form.user.label.password" }}</label>
<input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="new-password">
<label for="form-confirmation">{{ t "form.user.label.confirmation" }}</label>
<input type="password" name="confirmation" id="form-confirmation" value="{{ .form.Confirmation }}" autocomplete="new-password">
<label for="form-language">{{ t "form.prefs.label.language" }}</label>
<select id="form-language" name="language">
{{ range $key, $value := .languages }}
<option value="{{ $key }}" {{ if eq $key $.form.Language }}selected="selected"{{ end }}>{{ $value }}</option>
{{ end }}
</select>
<label for="form-timezone">{{ t "form.prefs.label.timezone" }}</label>
<select id="form-timezone" name="timezone">
{{ range $key, $value := .timezones }}
<option value="{{ $key }}" {{ if eq $key $.form.Timezone }}selected="selected"{{ end }}>{{ $value }}</option>
{{ end }}
</select>
<label for="form-theme">{{ t "form.prefs.label.theme" }}</label>
<select id="form-theme" name="theme">
{{ range $key, $value := .themes }}
<option value="{{ $key }}" {{ if eq $key $.form.Theme }}selected="selected"{{ end }}>{{ $value }}</option>
{{ end }}
</select>
<label for="form-entry-direction">{{ t "form.prefs.label.entry_sorting" }}</label>
<select id="form-entry-direction" name="entry_direction">
<option value="asc" {{ if eq "asc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.older_first" }}</option>
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
</select>
<label for="form-entries-per-page">{{ t "form.prefs.label.entries_per_page" }}</label>
<input type="number" name="entries_per_page" id="form-entries-per-page" value="{{ .form.EntriesPerPage }}" min="1">
<label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
<label><input type="checkbox" name="show_reading_time" value="1" {{ if .form.ShowReadingTime }}checked{{ end }}> {{ t "form.prefs.label.show_reading_time" }}</label>
<label><input type="checkbox" name="entry_swipe" value="1" {{ if .form.EntrySwipe }}checked{{ end }}> {{ t "form.prefs.label.entry_swipe" }}</label>
<label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="8" spellcheck="false">{{ .form.CustomCSS }}</textarea>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</form>
{{ if hasOAuth2Provider "google" }}
<div class="panel">
{{ if .user.GoogleID }}
<a href="{{ route "oauth2Unlink" "provider" "google" }}">{{ t "page.settings.unlink_google_account" }}</a>
{{ else }}
<a href="{{ route "oauth2Redirect" "provider" "google" }}">{{ t "page.settings.link_google_account" }}</a>
{{ end }}
</div>
{{ else if hasOAuth2Provider "oidc" }}
<div class="panel">
{{ if .user.OpenIDConnectID }}
<a href="{{ route "oauth2Unlink" "provider" "oidc" }}">{{ t "page.settings.unlink_oidc_account" }}</a>
{{ else }}
<a href="{{ route "oauth2Redirect" "provider" "oidc" }}">{{ t "page.settings.link_oidc_account" }}</a>
{{ end }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,71 @@
{{ define "title"}}{{ t "page.shared_entries.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.shared_entries.title" }} ({{ .total }})</h1>
{{ if .entries }}
<ul>
<li>
<a href="#"
data-confirm="true"
data-url="{{ route "flushHistory" }}"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "menu.flush_history" }}</a>
</li>
<li>
<a href="{{ route "sharedEntries" }}">{{ t "menu.shared_entries" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ if not .entries }}
<p class="alert alert-info">{{ t "alert.no_shared_entry" }}</p>
{{ else }}
<div class="items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "readEntry" "entryID" .ID }}">{{ .Title }}</a>
{{ if .ShareCode }}
<a href="{{ route "sharedEntry" "shareCode" .ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
target="_blank">{{ template "icon_share" }}</a>
{{ end }}
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
<div class="item-meta">
<ul class="item-meta-info">
<li>
<a href="{{ route "feedEntries" "feedID" .Feed.ID }}" title="{{ .Feed.SiteURL }}">{{ truncate .Feed.Title 35 }}</a>
</li>
<li>
<time datetime="{{ isodate .Date }}" title="{{ isodate .Date }}">{{ elapsed $.user.Timezone .Date }}</time>
</li>
</ul>
<ul class="item-meta-icons">
<li>
{{ template "icon_delete" }}
<a href="#"
data-confirm="true"
data-url="{{ route "unshareEntry" "entryID" .ID }}"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "entry.unshare.label" }}</a>
</li>
</ul>
</div>
</article>
{{ end }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,67 @@
{{ define "title"}}{{ t "page.unread.title" }} {{ if gt .countUnread 0 }}({{ .countUnread }}){{ end }} {{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.unread.title" }} (<span class="unread-counter">{{ .countUnread }}</span>)</h1>
{{ if .entries }}
<ul>
<li>
<a href="#"
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" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
<li>
<a href="#"
data-confirm="true"
data-url="{{ route "markAllAsRead" }}"
data-redirect-url="{{ route "unread" }}"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "menu.mark_all_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ if not .entries }}
<p class="alert">{{ t "alert.no_unread_entry" }}</p>
{{ else }}
<div class="items hide-read-items">
{{ range .entries }}
<article class="item {{ if $.user.EntrySwipe }}touch-item{{ end }} item-status-{{ .Status }}" data-id="{{ .ID }}">
<div class="item-header" dir="auto">
<span class="item-title">
{{ if ne .Feed.Icon.IconID 0 }}
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
{{ end }}
<a href="{{ route "unreadEntry" "entryID" .ID }}">{{ .Title }}</a>
</span>
<span class="category"><a href="{{ route "categoryEntries" "categoryID" .Feed.Category.ID }}">{{ .Feed.Category.Title }}</a></span>
</div>
{{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
</article>
{{ end }}
</div>
<section class="page-footer">
{{ if .entries }}
<ul>
<li>
<a href="#"
data-action="markPageAsRead"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}">{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
{{ template "pagination" .pagination }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,52 @@
{{ define "title"}}{{ t "page.users.title" }}{{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.users.title" }}</h1>
{{ template "settings_menu" dict "user" .user }}
</section>
{{ if eq (len .users) 1 }}
<p class="alert">{{ t "alert.no_user" }}</p>
{{ else }}
<table>
<tr>
<th class="column-20">{{ t "page.users.username" }}</th>
<th>{{ t "page.users.is_admin" }}</th>
<th>{{ t "page.users.last_login" }}</th>
<th>{{ t "page.users.actions" }}</th>
</tr>
{{ range .users }}
{{ if ne .ID $.user.ID }}
<tr>
<td>{{ .Username }}</td>
<td>{{ if eq .IsAdmin true }}{{ t "page.users.admin.yes" }}{{ else }}{{ t "page.users.admin.no" }}{{ end }}</td>
<td>
{{ if .LastLoginAt }}
<time datetime="{{ isodate .LastLoginAt }}" title="{{ isodate .LastLoginAt }}">{{ elapsed $.user.Timezone .LastLoginAt }}</time>
{{ else }}
{{ t "page.users.never_logged" }}
{{ end }}
</td>
<td>
<a href="{{ route "editUser" "userID" .ID }}">{{ t "action.edit" }}</a>,
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "removeUser" "userID" .ID }}">{{ t "action.remove" }}</a>
</td>
</tr>
{{ end }}
{{ end }}
</table>
<br>
{{ end }}
<p>
<a href="{{ route "createUser" }}" class="button button-primary">{{ t "menu.add_user" }}</a>
</p>
{{ end }}