1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

Move internal packages to an internal folder

For reference: https://go.dev/doc/go1.4#internalpackages
This commit is contained in:
Frédéric Guillot 2023-08-10 19:46:45 -07:00
parent c234903255
commit 168a870c02
433 changed files with 1121 additions and 1123 deletions

View file

@ -0,0 +1,39 @@
{{ 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 }}
<li><strong>{{t "page.about.go_version" }}</strong> {{ .go_version }}</li>
</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,120 @@
{{ 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>
<label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</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-cookie">{{ t "form.feed.label.cookie" }}</label>
<input type="text" name="cookie" id="form-cookie" value="{{ .form.Cookie }}" 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">
<div class="form-label-row">
<label for="form-scraper-rules">
{{ t "form.feed.label.scraper_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#scraper-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="scraper_rules" id="form-scraper-rules" value="{{ .form.ScraperRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-rewrite-rules">
{{ t "form.feed.label.rewrite_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="rewrite_rules" id="form-rewrite-rules" value="{{ .form.RewriteRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-blocklist-rules">
{{ t "form.feed.label.blocklist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#filtering-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="blocklist_rules" id="form-blocklist-rules" value="{{ .form.BlocklistRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-keeplist-rules">
{{ t "form.feed.label.keeplist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#filtering-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="keeplist_rules" id="form-keeplist-rules" value="{{ .form.KeeplistRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-urlrewrite-rules">
{{ t "form.feed.label.urlrewrite_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#rewriteurl-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="urlrewrite_rules" id="form-urlrewrite-rules" value="{{ .form.UrlRewriteRules }}" 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,35 @@
{{ 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="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items">
{{ range .entries }}
<article role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,70 @@
{{ 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" }}">{{ icon "add-category" }}{{ 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 role="article" class="item category-item {{if gt (deRef .TotalUnread) 0 }} category-has-unread{{end}}">
<div class="item-header" dir="auto">
<span class="item-title">
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ .Title }}</a>
</span>
(<span title="{{ t "page.categories.unread_counter" }}">{{ .TotalUnread }}</span>)
</div>
<div class="item-meta">
<ul class="item-meta-info">
<li class="item-meta-info-feed-count">
{{ if eq (deRef .FeedCount) 0 }}{{ t "page.categories.no_feed" }}{{ else }}{{ plural "page.categories.feed_count" (deRef .FeedCount) (deRef .FeedCount) }}{{ end }}
</li>
</ul>
<ul class="item-meta-icons">
<li class="item-meta-icons-entries">
<a href="{{ route "categoryEntries" "categoryID" .ID }}">{{ icon "entries" }}<span class="icon-label">{{ t "page.categories.entries" }}</span></a>
</li>
<li class="item-meta-icons-feeds">
<a href="{{ route "categoryFeeds" "categoryID" .ID }}">{{ icon "feeds" }}<span class="icon-label">{{ t "page.categories.feeds" }}</span></a>
</li>
<li class="item-meta-icons-edit">
<a href="{{ route "editCategory" "categoryID" .ID }}">{{ icon "edit" }}<span class="icon-label">{{ t "menu.edit_category" }}</span></a>
</li>
{{ if eq (deRef .FeedCount) 0 }}
<li class="item-meta-icons-delete">
<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 }}">{{ icon "delete" }}<span class="icon-label">{{ t "action.remove" }}</span></a>
</li>
{{ end }}
{{ if gt (deRef .TotalUnread) 0 }}
<li class="item-meta-icons-mark-as-read">
<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 "markCategoryAsRead" "categoryID" .ID }}">{{ icon "read" }}<span class="icon-label">{{ t "menu.mark_all_as_read" }}</span></a>
</li>
{{ end }}
</ul>
</div>
</article>
{{ end }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,87 @@
{{ 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 }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}</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 "markCategoryAsRead" "categoryID" .category.ID }}">{{ icon "mark-all-as-read" }}{{ t "menu.mark_all_as_read" }}</a>
</li>
{{ end }}
{{ if .showOnlyUnreadEntries }}
<li>
<a href="{{ route "categoryEntriesAll" "categoryID" .category.ID }}">{{ icon "show-all-entries" }}{{ t "menu.show_all_entries" }}</a>
</li>
{{ else }}
<li>
<a href="{{ route "categoryEntries" "categoryID" .category.ID }}">{{ icon "show-unread-entries" }}{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ icon "feeds" }}{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "refreshCategoryEntriesPage" "categoryID" .category.ID }}">{{ icon "refresh" }}{{ t "menu.refresh_all_feeds" }}</a>
</li>
</ul>
</section>
{{ if not .entries }}
<p class="alert">{{ t "alert.no_category_entry" }}</p>
{{ else }}
<div class="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items">
{{ range .entries }}
<article role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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 }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,37 @@
{{ 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 }}">{{ icon "entries" }}{{ t "menu.feed_entries" }}</a>
</li>
<li>
<a href="{{ route "editCategory" "categoryID" .category.ID }}">{{ icon "edit" }}{{ 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 }}">{{ icon "delete" }}{{ t "action.remove" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "refreshCategoryFeedsPage" "categoryID" .category.ID }}">{{ icon "refresh" }}{{ t "menu.refresh_all_feeds" }}</a>
</li>
</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,44 @@
{{ 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="cookie" value="{{ .form.Cookie }}">
<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 }}">
<input type="hidden" name="blocklist_rules" value="{{ .form.BlocklistRules }}">
<input type="hidden" name="keeplist_rules" value="{{ .form.KeeplistRules }}">
<input type="hidden" name="urlrewrite_rules" value="{{ .form.UrlRewriteRules }}">
{{ if .form.FetchViaProxy }}
<input type="hidden" name="fetch_via_proxy" value="1">
{{ end }}
{{ if .form.Crawler }}
<input type="hidden" name="crawler" value="1">
{{ end }}
{{ if .form.AllowSelfSignedCertificates }}
<input type="hidden" name="allow_self_signed_certificates" 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" }}">{{ icon "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,38 @@
{{ 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" }}">{{ icon "categories" }}{{ t "menu.categories" }}</a>
</li>
<li>
<a href="{{ route "categoryFeeds" "categoryID" .category.ID }}">{{ icon "feeds" }}{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "createCategory" }}">{{ icon "add-category" }}{{ 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>
<label>
<input type="checkbox" name="hide_globally" {{ if .form.HideGlobally }}checked{{ end }}>
{{ t "form.category.hide_globally" }}
</label>
<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,172 @@
{{ 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" }}">{{ icon "feeds" }}{{ t "menu.feeds" }}</a>
</li>
<li>
<a href="{{ route "feedEntries" "feedID" .feed.ID }}">{{ icon "entries" }}{{ t "menu.feed_entries" }}</a>
</li>
<li>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question.refresh" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=true"
data-no-action-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=false">{{ icon "refresh" }}{{ 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-category">{{ t "form.feed.label.category" }}</label>
<select id="form-category" name="category_id" autofocus>
{{ range .categories }}
<option value="{{ .ID }}" {{ if eq .ID $.form.CategoryID }}selected="selected"{{ end }}>{{ .Title }}</option>
{{ end }}
</select>
<label for="form-title">{{ t "form.feed.label.title" }}</label>
<input type="text" name="title" id="form-title" value="{{ .form.Title }}" spellcheck="false" required>
<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-cookie">{{ t "form.feed.label.cookie" }}</label>
<input type="text" name="cookie" id="form-cookie" value="{{ .form.Cookie }}" spellcheck="false">
<div class="form-label-row">
<label for="form-scraper-rules">
{{ t "form.feed.label.scraper_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#scraper-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="scraper_rules" id="form-scraper-rules" value="{{ .form.ScraperRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-rewrite-rules">
{{ t "form.feed.label.rewrite_rules" }}
</label>
&nbsp;
<a href="https://miniflux.app/docs/rules.html#rewrite-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="rewrite_rules" id="form-rewrite-rules" value="{{ .form.RewriteRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-blocklist-rules">
{{ t "form.feed.label.blocklist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#filtering-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="blocklist_rules" id="form-blocklist-rules" value="{{ .form.BlocklistRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-keeplist-rules">
{{ t "form.feed.label.keeplist_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#filtering-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="keeplist_rules" id="form-keeplist-rules" value="{{ .form.KeeplistRules }}" spellcheck="false">
<div class="form-label-row">
<label for="form-urlrewrite-rules">
{{ t "form.feed.label.urlrewrite_rules" }}
</label>
&nbsp;
<a href=" https://miniflux.app/docs/rules.html#rewriteurl-rules" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<input type="text" name="urlrewrite_rules" id="form-urlrewrite-rules" value="{{ .form.UrlRewriteRules }}" spellcheck="false">
<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>
<label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</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>
<label><input type="checkbox" name="no_media_player" {{ if .form.NoMediaPlayer }}checked{{ end }} value="1" > {{ t "form.feed.label.no_media_player" }} </label>
{{ if not .form.CategoryHidden }}
<label><input type="checkbox" name="hide_globally" value="1"{{ if .form.HideGlobally }} checked{{ end }}> {{ t "form.feed.label.hide_globally" }}</label>
{{ end }}
<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,252 @@
{{ 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-loading="{{ t "entry.state.saving" }}"
data-label-unread="{{ t "entry.status.unread" }}"
data-label-read="{{ t "entry.status.read" }}"
data-toast-unread="{{ t "entry.status.toast.unread" }}"
data-toast-read="{{ t "entry.status.toast.read" }}"
data-value="{{ if eq .entry.Status "read" }}read{{ else }}unread{{ end }}"
>{{ if eq .entry.Status "unread" }}{{ icon "read" }}{{ else }}{{ 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="{{ t "entry.bookmark.toast.on" }}"
data-toast-unstar="{{ t "entry.bookmark.toast.off" }}"
data-value="{{ if .entry.Starred }}star{{ else }}unstar{{ end }}"
>{{ if .entry.Starred }}{{ icon "unstar" }}{{ else }}{{ 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" }}"
>{{ icon "save" }}<span class="icon-label">{{ t "entry.save.label" }}</span></a>
</li>
{{ end }}
{{ if .entry.ShareCode }}
<li>
<a href="{{ route "sharedEntry" "shareCode" .entry.ShareCode }}"
title="{{ t "entry.shared_entry.title" }}"
data-share-status="shared"
target="_blank">{{ icon "share" }}<span class="icon-label">{{ t "entry.shared_entry.label" }}</span></a>
</li>
<li>
<a href="#"
data-confirm="true"
data-url="{{ route "unshareEntry" "entryID" .entry.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" }}">{{ icon "delete" }}<span class="icon-label">{{ t "entry.unshare.label" }}</span></a>
</li>
{{ else }}
<li>
<a href="{{ route "shareEntry" "entryID" .entry.ID }}"
title="{{ t "entry.share.title" }}"
data-share-status="share"
target="_blank">{{ icon "share" }}<span class="icon-label">{{ t "entry.share.label" }}</span></a>
</li>
{{ end }}
<li>
<a href="{{ .entry.URL | safeURL }}"
target="_blank"
rel="noopener noreferrer"
referrerpolicy="no-referrer"
data-original-link="{{ .user.MarkReadOnView }}">{{ 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" }}"
>{{ 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"
>{{ 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>
{{ if .entry.Tags }}
<div class="entry-tags">
{{ t "entry.tags.label" }}
{{range $i, $e := .entry.Tags}}{{if $i}}, {{end}}<strong>{{ $e }}</strong>{{end}}
</div>
{{ end }}
<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 }}
{{ if and .user.ShowReadingTime (gt .entry.ReadingTime 0) }}
&centerdot;
<span class="entry-reading-time">
{{ plural "entry.estimated_reading_time" .entry.ReadingTime .entry.ReadingTime }}
</span>
{{ end }}
</div>
</header>
{{ if gt (len .entry.Content) 120 }}
{{ if .user }}
<div class="pagination-entry-top">
{{ template "entry_pagination" . }}
</div>
{{ end }}
{{ end }}
<article role="article" class="entry-content gesture-nav-{{ $.user.GestureNav }}" dir="auto">
{{ if (and .entry.Enclosures (not .entry.Feed.NoMediaPlayer)) }}
{{ range .entry.Enclosures }}
{{ if ne .URL "" }}
{{ if hasPrefix .MimeType "audio/" }}
<div class="enclosure-audio" >
<audio controls preload="metadata"
data-last-position="{{ .MediaProgression }}"
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
>
{{ if (and $.user (mustBeProxyfied "audio")) }}
<source src="{{ proxyURL .URL }}" type="{{ .Html5MimeType }}">
{{ else }}
<source src="{{ .URL | safeURL }}" type="{{ .Html5MimeType }}">
{{ end }}
</audio>
</div>
{{ else if hasPrefix .MimeType "video/" }}
<div class="enclosure-video">
<video controls preload="metadata"
data-last-position="{{ .MediaProgression }}"
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
>
{{ if (and $.user (mustBeProxyfied "video")) }}
<source src="{{ proxyURL .URL }}" type="{{ .Html5MimeType }}">
{{ else }}
<source src="{{ .URL | safeURL }}" type="{{ .Html5MimeType }}">
{{ end }}
</video>
</div>
{{ end }}
{{ end }}
{{ end }}
{{end}}
{{ 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"
data-last-position="{{ .MediaProgression }}"
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
>
{{ if (and $.user (mustBeProxyfied "audio")) }}
<source src="{{ proxyURL .URL }}" type="{{ .Html5MimeType }}">
{{ else }}
<source src="{{ .URL | safeURL }}" type="{{ .Html5MimeType }}">
{{ end }}
</audio>
</div>
{{ else if hasPrefix .MimeType "video/" }}
<div class="enclosure-video">
<video controls preload="metadata"
data-last-position="{{ .MediaProgression }}"
data-save-url="{{ route "saveEnclosureProgression" "enclosureID" .ID }}"
>
{{ if (and $.user (mustBeProxyfied "video")) }}
<source src="{{ proxyURL .URL }}" type="{{ .Html5MimeType }}">
{{ else }}
<source src="{{ .URL | safeURL }}" type="{{ .Html5MimeType }}">
{{ end }}
</video>
</div>
{{ else if hasPrefix .MimeType "image/" }}
<div class="enclosure-image">
{{ if (and $.user (mustBeProxyfied "image")) }}
<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-entry-bottom">
{{ template "entry_pagination" . }}
</div>
{{ end }}
{{ end }}

View file

@ -0,0 +1,119 @@
{{ 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="{{ .user.MarkReadOnView }}">{{ .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 }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}</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 "markFeedAsRead" "feedID" .feed.ID }}">{{ icon "mark-all-as-read" }}{{ t "menu.mark_all_as_read" }}</a>
</li>
{{ end }}
{{ if .showOnlyUnreadEntries }}
<li>
<a href="{{ route "feedEntriesAll" "feedID" .feed.ID }}">{{ icon "show-all-entries" }}{{ t "menu.show_all_entries" }}</a>
</li>
{{ else }}
<li>
<a href="{{ route "feedEntries" "feedID" .feed.ID }}">{{ icon "show-unread-entries" }}{{ t "menu.show_only_unread_entries" }}</a>
</li>
{{ end }}
<li>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question.refresh" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=true"
data-no-action-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=false">{{ icon "refresh" }}{{ t "menu.refresh_feed" }}</a>
</li>
<li>
<a href="{{ route "editFeed" "feedID" .feed.ID }}">{{ icon "edit" }}{{ 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" }}">{{ icon "delete" }}{{ 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="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items">
{{ range .entries }}
<article role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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 }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ 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,51 @@
{{ define "title"}}{{ t "page.history.title" }} ({{ .total }}){{ end }}
{{ define "content"}}
<section class="page-header">
<h1>{{ t "page.history.title" }} ({{ .total }})</h1>
<ul>
{{ if .entries }}
<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" }}">{{ icon "delete" }}{{ t "menu.flush_history" }}</a>
</li>
{{ end }}
<li>
<a href="{{ route "sharedEntries" }}">{{ icon "share" }}{{ t "menu.shared_entries" }}</a>
</li>
</ul>
</section>
{{ if not .entries }}
<p class="alert alert-info">{{ t "alert.no_history" }}</p>
{{ else }}
<div class="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items">
{{ range .entries }}
<article class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ 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,340 @@
{{ 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" }}" class="integration-form">
<input type="hidden" name="csrf" value="{{ .csrf }}">
{{ if .errorMessage }}
<div class="alert alert-error">{{ t .errorMessage }}</div>
{{ end }}
<details {{ if .form.FeverEnabled }}open{{ end }}>
<summary>Fever</summary>
<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>
</details>
<details {{ if .form.GoogleReaderEnabled }}open{{ end }}>
<summary>Google Reader</summary>
<div class="form-section">
<label>
<input type="checkbox" name="googlereader_enabled" value="1" {{ if .form.GoogleReaderEnabled }}checked{{ end }}> {{ t "form.integration.googlereader_activate" }}
</label>
<label for="form-googlereader-username">{{ t "form.integration.googlereader_username" }}</label>
<input type="text" name="googlereader_username" id="form-googlereader-username" value="{{ .form.GoogleReaderUsername }}" autocomplete="username" spellcheck="false">
<label for="form-googlereader-password">{{ t "form.integration.googlereader_password" }}</label>
<input type="password" name="googlereader_password" id="form-googlereader-password" value="{{ .form.GoogleReaderPassword }}" autocomplete="new-password">
<p>{{ t "form.integration.googlereader_endpoint" }} <strong>{{ rootURL }}{{ route "login" }}</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>
</details>
<details {{ if .form.PinboardEnabled }}open{{ end }}>
<summary>Pinboard</summary>
<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>
</details>
<details {{ if .form.InstapaperEnabled }}open{{ end }}>
<summary>Instapaper</summary>
<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>
</details>
<details {{ if .form.PocketEnabled }}open{{ end }}>
<summary>Pocket</summary>
<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>
</details>
<details {{ if .form.WallabagEnabled }}open{{ end }}>
<summary>Wallabag</summary>
<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>
<input type="checkbox" name="wallabag_only_url" value="1" {{ if .form.WallabagOnlyURL }}checked{{ end }}> {{ t "form.integration.wallabag_only_url" }}
</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>
</details>
<details {{ if .form.NotionEnabled }}open{{ end }}>
<summary>Notion</summary>
<div class="form-section">
<label>
<input type="checkbox" name="notion_enabled" value="1" {{ if .form.NotionEnabled }}checked{{ end }}> {{ t "form.integration.notion_activate" }}
</label>
<label for="form-notion-token">{{ t "form.integration.notion_token" }}</label>
<input type="password" name="notion_token" id="form-notion-token" value="{{ .form.NotionToken }}" spellcheck="false">
<label for="form-notion-page-id">{{ t "form.integration.notion_page_id" }}</label>
<input type="text" name="notion_page_id" id="form-notion-page-id" value="{{ .form.NotionPageID }}" 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>
</details>
<details {{ if .form.NunuxKeeperEnabled }}open{{ end }}>
<summary>Nunux Keeper</summary>
<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>
</details>
<details {{ if .form.EspialEnabled }}open{{ end }}>
<summary>Espial</summary>
<div class="form-section">
<label>
<input type="checkbox" name="espial_enabled" value="1" {{ if .form.EspialEnabled }}checked{{ end }}> {{ t "form.integration.espial_activate" }}
</label>
<label for="form-espial-url">{{ t "form.integration.espial_endpoint" }}</label>
<input type="url" name="espial_url" id="form-espial-url" value="{{ .form.EspialURL }}" placeholder="https://esp.ae8.org" spellcheck="false">
<label for="form-espial-api-key">{{ t "form.integration.espial_api_key" }}</label>
<input type="text" name="espial_api_key" id="form-espial-api-key" value="{{ .form.EspialAPIKey }}" spellcheck="false">
<label for="form-espial-tags">{{ t "form.integration.espial_tags" }}</label>
<input type="text" name="espial_tags" id="form-espial-tags" value="{{ .form.EspialTags }}" 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>
</details>
<details {{ if .form.ReadwiseEnabled }}open{{ end }}>
<summary>Readwise Reader</summary>
<div class="form-section">
<label>
<input type="checkbox" name="readwise_enabled" value="1" {{ if .form.ReadwiseEnabled }}checked{{ end }}> {{ t "form.integration.readwise_activate" }}
</label>
<label for="form-readwise-api-key">{{ t "form.integration.readwise_api_key" }}</label>
<input type="text" name="readwise_api_key" id="form-readwise-api-key" value="{{ .form.ReadwiseAPIKey }}" spellcheck="false">
<p><a href="https://readwise.io/access_token" target="_blank">{{ t "form.integration.readwise_api_key_link" }}</a></p>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</div>
</details>
<details {{ if .form.LinkdingEnabled }}open{{ end }}>
<summary>Linkding</summary>
<div class="form-section">
<label>
<input type="checkbox" name="linkding_enabled" value="1" {{ if .form.LinkdingEnabled }}checked{{ end }}> {{ t "form.integration.linkding_activate" }}
</label>
<label for="form-linkding-url">{{ t "form.integration.linkding_endpoint" }}</label>
<input type="url" name="linkding_url" id="form-linkding-url" value="{{ .form.LinkdingURL }}" placeholder="https://linkding.com" spellcheck="false">
<label for="form-linkding-api-key">{{ t "form.integration.linkding_api_key" }}</label>
<input type="text" name="linkding_api_key" id="form-linkding-api-key" value="{{ .form.LinkdingAPIKey }}" spellcheck="false">
<label for="form-linkding-tags">{{ t "form.integration.linkding_tags" }}</label>
<input type="text" name="linkding_tags" id="form-linkding-tags" value="{{ .form.LinkdingTags }}" spellcheck="false">
<label>
<input type="checkbox" name="linkding_mark_as_unread" value="1" {{ if .form.LinkdingMarkAsUnread }}checked{{ end }}> {{ t "form.integration.linkding_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>
</details>
<details {{ if .form.AppriseEnabled }}open{{ end }}>
<summary>Apprise</summary>
<div class="form-section">
<label>
<input type="checkbox" name="apprise_enabled" value="1" {{ if .form.AppriseEnabled }}checked{{ end }}> {{ t "form.integration.apprise_activate" }}
</label>
<label for="form-apprise-url">{{ t "form.integration.apprise_url" }}</label>
<input type="text" name="apprise_url" id="form-apprise-url" value="{{ .form.AppriseURL }}" placeholder="http://apprise:8080" spellcheck="false">
<label for="form-apprise-services-url">{{ t "form.integration.apprise_services_url" }}
<a href="https://github.com/caronc/apprise/wiki" target="_blank">
{{ icon "external-link" }}
</a>
</label>
<input type="text" name="apprise_services_url" id="form-apprise-services-urls" value="{{ .form.AppriseServicesURL }}" placeholder="tgram://<token>/<chat_id>/,matrix://" 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>
</details>
<details {{ if .form.TelegramBotEnabled }}open{{ end }}>
<summary>Telegram Bot</summary>
<div class="form-section">
<label>
<input type="checkbox" name="telegram_bot_enabled" value="1" {{ if .form.TelegramBotEnabled }}checked{{ end }}> {{ t "form.integration.telegram_bot_activate" }}
</label>
<label for="form-telegram-bot-token">{{ t "form.integration.telegram_bot_token" }}</label>
<input type="text" name="telegram_bot_token" id="form-telegram-bot-token" value="{{ .form.TelegramBotToken }}" placeholder="bot123456:Abcdefg" spellcheck="false">
<label for="form-telegram-chat-id">{{ t "form.integration.telegram_chat_id" }}</label>
<input type="text" name="telegram_bot_chat_id" id="form-telegram-chat-id" value="{{ .form.TelegramBotChatID }}" 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>
</details>
<details {{ if .form.MatrixBotEnabled }}open{{ end }}>
<summary>Matrix Bot</summary>
<div class="form-section">
<label>
<input type="checkbox" name="matrix_bot_enabled" value="1" {{ if .form.MatrixBotEnabled }}checked{{ end }}> {{ t "form.integration.matrix_bot_activate" }}
</label>
<label for="form-matrix-bot-user">{{ t "form.integration.matrix_bot_user" }}</label>
<input type="text" name="matrix_bot_user" id="form-matrix-bot-user" value="{{ .form.MatrixBotUser }}" spellcheck="false">
<label for="form-matrix-chat-password">{{ t "form.integration.matrix_bot_password" }}</label>
<input type="password" name="matrix_bot_password" id="form-matrix-password" value="{{ .form.MatrixBotPassword }}" spellcheck="false">
<label for="form-matrix-url">{{ t "form.integration.matrix_bot_url" }}</label>
<input type="text" name="matrix_bot_url" id="form-matrix-url" value="{{ .form.MatrixBotURL }}" spellcheck="false">
<label for="form-matrix-chat-id">{{ t "form.integration.matrix_bot_chat_id" }}</label>
<input type="text" name="matrix_bot_chat_id" id="form-matrix-chat-id" value="{{ .form.MatrixBotChatID }}" 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>
</details>
</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" role="button">{{ icon "home" }}<span class="icon-label">{{ t "action.home_screen" }}</span></a>
</footer>
{{ end }}

View file

@ -0,0 +1,35 @@
{{ 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="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items">
{{ range .entries }}
<article role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ 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 }}">{{ icon "delete" }}{{ t "action.remove" }}</a>
{{ end }}
</td>
</tr>
{{ end }}
</table>
{{ end }}

View file

@ -0,0 +1,134 @@
{{ 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>
<div class="form-label-row">
<label for="form-display-mode">{{ t "form.prefs.label.display_mode" }}</label>
&nbsp;
<a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/display" target="_blank">
{{ icon "external-link" }}
</a>
</div>
<select id="form-display-mode" name="display_mode">
<option value="fullscreen" {{ if eq "fullscreen" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.fullscreen" }}</option>
<option value="standalone" {{ if eq "standalone" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.standalone" }}</option>
<option value="minimal-ui" {{ if eq "minimal-ui" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.minimal_ui" }}</option>
<option value="browser" {{ if eq "browser" $.form.DisplayMode }}selected="selected"{{ end }}>{{ t "form.prefs.select.browser" }}</option>
</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-entry-order">{{ t "form.prefs.label.entry_order" }}</label>
<select id="form-entry-order" name="entry_order">
<option value="published_at" {{ if eq "published_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.publish_time" }}</option>
<option value="created_at" {{ if eq "created_at" $.form.EntryOrder }}selected="selected"{{ end }}>{{ t "form.prefs.select.created_time" }}</option>
</select>
<label for="form-default-home-page">{{ t "form.prefs.label.default_home_page" }}</label>
<select id="form-default-home-page" name="default_home_page">
{{ range $key, $value := .default_home_pages }}
<option value="{{ $key }}" {{ if eq $key $.form.DefaultHomePage }}selected="selected"{{ end }}>{{ t $value }}</option>
{{ end }}
</select>
<label for="form-categories-sorting-order">{{ t "form.prefs.label.categories_sorting_order" }}</label>
<select id="form-categories-sorting-order" name="categories_sorting_order">
{{ range $key, $value := .categories_sorting_options }}
<option value="{{ $key }}" {{ if eq $key $.form.CategoriesSortingOrder }}selected="selected"{{ end }}>{{ t $value }}</option>
{{ end }}
</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="entry_swipe" value="1" {{ if .form.EntrySwipe }}checked{{ end }}> {{ t "form.prefs.label.entry_swipe" }}</label>
<label for="form-gesture-nav">{{ t "form.prefs.label.gesture_nav" }}</label>
<select id="form-gesture-nav" name="gesture_nav">
<option value="none" {{ if eq "none" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.none" }}</option>
<option value="tap" {{ if eq "tap" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.tap" }}</option>
<option value="swipe" {{ if eq "swipe" $.form.GestureNav }}selected="selected"{{ end }}>{{ t "form.prefs.select.swipe" }}</option>
</select>
<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="mark_read_on_view" value="1" {{ if .form.MarkReadOnView }}checked{{ end }}> {{ t "form.prefs.label.mark_read_on_view" }}</label>
<label for="form-cjk-reading-speed">{{ t "form.prefs.label.cjk_reading_speed" }}</label>
<input type="number" name="cjk_reading_speed" id="form-cjk-reading-speed" value="{{ .form.CJKReadingSpeed }}" min="1">
<label for="form-default-reading-speed">{{ t "form.prefs.label.default_reading_speed" }}</label>
<input type="number" name="default_reading_speed" id="form-default-reading-speed" value="{{ .form.DefaultReadingSpeed }}" min="1">
<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" }}">{{ icon "delete" }}{{ t "menu.flush_history" }}</a>
</li>
<li>
<a href="{{ route "sharedEntries" }}">{{ icon "share" }}{{ 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 role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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">{{ 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 class="item-meta-info-site-url">
<a href="{{ route "feedEntries" "feedID" .Feed.ID }}" title="{{ .Feed.SiteURL }}">{{ truncate .Feed.Title 35 }}</a>
</li>
<li class="item-meta-info-timestamp">
<time datetime="{{ isodate .Date }}" title="{{ isodate .Date }}">{{ elapsed $.user.Timezone .Date }}</time>
</li>
</ul>
<ul class="item-meta-icons">
<li class="item-meta-icons-delete">
{{ 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,72 @@
{{ 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" }}">{{ icon "mark-page-as-read" }}{{ 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" }}">{{ icon "mark-all-as-read" }}{{ 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="pagination-top">
{{ template "pagination" .pagination }}
</div>
<div class="items hide-read-items">
{{ range .entries }}
<article role="article" class="item entry-item {{ if $.user.EntrySwipe }}entry-swipe{{ 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" }}">{{ icon "mark-page-as-read" }}{{ t "menu.mark_page_as_read" }}</a>
</li>
</ul>
{{ end }}
</section>
<div class="pagination-bottom">
{{ template "pagination" .pagination }}
</div>
{{ 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 }}