diff --git a/Makefile b/Makefile
index 2084cc41..feb94df5 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ DEB_IMG_ARCH := amd64
export PGPASSWORD := postgres
-.PHONY: generate \
+.PHONY: \
miniflux \
linux-amd64 \
linux-arm64 \
@@ -41,64 +41,61 @@ export PGPASSWORD := postgres
debian \
debian-packages
-generate:
- @ go generate
-
-miniflux: generate
+miniflux:
@ go build -ldflags=$(LD_FLAGS) -o $(APP) main.go
-linux-amd64: generate
+linux-amd64:
@ GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-amd64 main.go
-linux-arm64: generate
+linux-arm64:
@ GOOS=linux GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-arm64 main.go
-linux-armv7: generate
+linux-armv7:
@ GOOS=linux GOARCH=arm GOARM=7 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv7 main.go
-linux-armv6: generate
+linux-armv6:
@ GOOS=linux GOARCH=arm GOARM=6 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv6 main.go
-linux-armv5: generate
+linux-armv5:
@ GOOS=linux GOARCH=arm GOARM=5 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv5 main.go
-darwin-amd64: generate
+darwin-amd64:
@ GOOS=darwin GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-amd64 main.go
-darwin-arm64: generate
+darwin-arm64:
@ GOOS=darwin GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-arm64 main.go
-freebsd-amd64: generate
+freebsd-amd64:
@ GOOS=freebsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-amd64 main.go
-openbsd-amd64: generate
+openbsd-amd64:
@ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-openbsd-amd64 main.go
-windows-amd64: generate
+windows-amd64:
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-amd64 main.go
build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 darwin-arm64 freebsd-amd64 openbsd-amd64 windows-amd64
# NOTE: unsupported targets
-netbsd-amd64: generate
+netbsd-amd64:
@ GOOS=netbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-amd64 main.go
-linux-x86: generate
+linux-x86:
@ GOOS=linux GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-x86 main.go
-freebsd-x86: generate
+freebsd-x86:
@ GOOS=freebsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
-netbsd-x86: generate
+netbsd-x86:
@ GOOS=netbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-x86 main.go
-openbsd-x86: generate
+openbsd-x86:
@ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
-windows-x86: generate
+windows-x86:
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-x86 main.go
-run: generate
+run:
@ LOG_DATE_TIME=1 go run main.go -debug
clean:
diff --git a/generate.go b/generate.go
deleted file mode 100644
index 9edc992d..00000000
--- a/generate.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2017 Frédéric Guillot. All rights reserved.
-// Use of this source code is governed by the Apache 2.0
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-package main
-
-import (
- "crypto/sha256"
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
- "text/template"
-)
-
-const tpl = `// Code generated by go generate; DO NOT EDIT.
-
-package {{ .Package }} // import "miniflux.app/{{ .ImportPath }}"
-
-var {{ .Map }} = map[string]string{
-{{ range $constant, $content := .Files }}` + "\t" + `"{{ $constant }}": ` + "`{{ $content }}`" + `,
-{{ end }}}
-
-var {{ .Map }}Checksums = map[string]string{
-{{ range $constant, $content := .Checksums }}` + "\t" + `"{{ $constant }}": "{{ $content }}",
-{{ end }}}
-`
-
-var bundleTpl = template.Must(template.New("").Parse(tpl))
-
-type Bundle struct {
- Package string
- Map string
- ImportPath string
- Files map[string]string
- Checksums map[string]string
-}
-
-func (b *Bundle) Write(filename string) {
- f, err := os.Create(filename)
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- bundleTpl.Execute(f, b)
-}
-
-func NewBundle(pkg, mapName, importPath string) *Bundle {
- return &Bundle{
- Package: pkg,
- Map: mapName,
- ImportPath: importPath,
- Files: make(map[string]string),
- Checksums: make(map[string]string),
- }
-}
-
-func readFile(filename string) []byte {
- data, err := os.ReadFile(filename)
- if err != nil {
- panic(err)
- }
- return data
-}
-
-func checksum(data []byte) string {
- return fmt.Sprintf("%x", sha256.Sum256(data))
-}
-
-func basename(filename string) string {
- return path.Base(filename)
-}
-
-func stripExtension(filename string) string {
- filename = strings.TrimSuffix(filename, path.Ext(filename))
- return strings.Replace(filename, " ", "_", -1)
-}
-
-func glob(pattern string) []string {
- // There is no Glob function in path package, so we have to use filepath and replace in case of Windows
- files, _ := filepath.Glob(pattern)
- for i := range files {
- if strings.Contains(files[i], "\\") {
- files[i] = strings.Replace(files[i], "\\", "/", -1)
- }
- }
- return files
-}
-
-func generateBundle(bundleFile, pkg, mapName string, srcFiles []string) {
- bundle := NewBundle(pkg, mapName, pkg)
-
- for _, srcFile := range srcFiles {
- data := readFile(srcFile)
- filename := stripExtension(basename(srcFile))
-
- bundle.Files[filename] = string(data)
- bundle.Checksums[filename] = checksum(data)
- }
-
- bundle.Write(bundleFile)
-}
-
-func main() {
- generateBundle("template/views.go", "template", "templateViewsMap", glob("template/html/*.html"))
- generateBundle("template/common.go", "template", "templateCommonMap", glob("template/html/common/*.html"))
-}
diff --git a/main.go b/main.go
index 668827d6..aaed0c53 100644
--- a/main.go
+++ b/main.go
@@ -4,10 +4,6 @@
package main // import "miniflux.app"
-//go:generate go run generate.go
-//go:generate gofmt -s -w template/views.go
-//go:generate gofmt -s -w template/common.go
-
import (
"miniflux.app/cli"
)
diff --git a/template/common.go b/template/common.go
deleted file mode 100644
index 1a85700f..00000000
--- a/template/common.go
+++ /dev/null
@@ -1,548 +0,0 @@
-// Code generated by go generate; DO NOT EDIT.
-
-package template // import "miniflux.app/template"
-
-var templateCommonMap = map[string]string{
- "entry_pagination": `{{ define "entry_pagination" }}
-
-{{ end }}
-`,
- "feed_list": `{{ define "feed_list" }}
-
- {{ range .feeds }}
-
-
-
- {{ if ne .ParsingErrorCount 0 }}
-
- {{ plural "page.feeds.error_count" .ParsingErrorCount .ParsingErrorCount }}
- - {{ .ParsingErrorMsg }}
-
- {{ end }}
-
- {{ end }}
-
-{{ end }}
-`,
- "feed_menu": `{{ define "feed_menu" }}
-
-{{ end }}`,
- "icons": `
-{{ define "icon_read" }}
-
-
-
-
-
-{{ end }}
-{{ define "icon_unread" }}
-
-
-
-
-
-{{ end }}
-{{ define "icon_star" }}
-
-
-
-
-{{ end }}
-{{ define "icon_unstar" }}
-
-
-
-
-{{ end }}
-{{ define "icon_save" }}
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_scraper" }}
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_share" }}
-
-
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_comment" }}
-
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_external_link" }}
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_delete" }}
-
-
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_edit" }}
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_feeds" }}
-
-
-
-
-
-{{ end }}
-{{ define "icon_entries" }}
-
-
-
-
-
-
-
-{{ end }}
-{{ define "icon_refresh" }}
-
-
-
-
-
-{{ end }}`,
- "item_meta": `{{ define "item_meta" }}
-
-{{ end }}
-`,
- "layout": `{{ define "base" }}
-
-
-
-
- {{template "title" .}} - Miniflux
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ if .csrf }}
-
- {{ end }}
-
-
-
- {{ if and .user .user.Stylesheet }}
-
- {{ end }}
-
-
-
-
-
-
-
-
- {{ if .user }}
-
- {{ end }}
- {{ if .flashMessage }}
- {{ .flashMessage }}
- {{ end }}
- {{ if .flashErrorMessage }}
- {{ .flashErrorMessage }}
- {{ end }}
-
- {{template "content" .}}
-
-
-
-
x
-
{{ t "page.keyboard_shortcuts.title" }}
-
-
-
{{ t "page.keyboard_shortcuts.subtitle.sections" }}
-
- {{ t "page.keyboard_shortcuts.go_to_unread" }} = g + u
- {{ t "page.keyboard_shortcuts.go_to_starred" }} = g + b
- {{ t "page.keyboard_shortcuts.go_to_history" }} = g + h
- {{ t "page.keyboard_shortcuts.go_to_feeds" }} = g + f
- {{ t "page.keyboard_shortcuts.go_to_categories" }} = g + c
- {{ t "page.keyboard_shortcuts.go_to_settings" }} = g + s
- {{ t "page.keyboard_shortcuts.show_keyboard_shortcuts" }} = ?
-
-
-
{{ t "page.keyboard_shortcuts.subtitle.items" }}
-
- {{ t "page.keyboard_shortcuts.go_to_previous_item" }} = p , k , ◄
- {{ t "page.keyboard_shortcuts.go_to_next_item" }} = n , j , ►
- {{ t "page.keyboard_shortcuts.go_to_feed" }} = F
-
-
-
{{ t "page.keyboard_shortcuts.subtitle.pages" }}
-
- {{ t "page.keyboard_shortcuts.go_to_previous_page" }} = h
- {{ t "page.keyboard_shortcuts.go_to_next_page" }} = l
-
-
-
{{ t "page.keyboard_shortcuts.subtitle.actions" }}
-
- {{ t "page.keyboard_shortcuts.open_item" }} = o
- {{ t "page.keyboard_shortcuts.open_original" }} = v
- {{ t "page.keyboard_shortcuts.open_original_same_window" }} = V
- {{ t "page.keyboard_shortcuts.open_comments" }} = c
- {{ t "page.keyboard_shortcuts.open_comments_same_window" }} = C
- {{ t "page.keyboard_shortcuts.toggle_read_status" }} = m
- {{ t "page.keyboard_shortcuts.mark_page_as_read" }} = A
- {{ t "page.keyboard_shortcuts.download_content" }} = d
- {{ t "page.keyboard_shortcuts.toggle_bookmark_status" }} = f
- {{ t "page.keyboard_shortcuts.save_article" }} = s
- {{ t "page.keyboard_shortcuts.scroll_item_to_top" }} = z + t
- {{ t "page.keyboard_shortcuts.refresh_all_feeds" }} = R
- {{ t "page.keyboard_shortcuts.remove_feed" }} = #
- {{ t "page.keyboard_shortcuts.go_to_search" }} = /
- {{ t "page.keyboard_shortcuts.close_modal" }} = Esc
-
-
-
-
-
- {{ template "icon_read" }}
-
-
- {{ template "icon_unread" }}
-
-
- {{ template "icon_star" }}
-
-
- {{ template "icon_unstar" }}
-
-
-
-{{ end }}
-`,
- "pagination": `{{ define "pagination" }}
-
-{{ end }}
-`,
- "settings_menu": `{{ define "settings_menu" }}
-
-{{ end }}`,
-}
-
-var templateCommonMapChecksums = map[string]string{
- "entry_pagination": "cdca9cf12586e41e5355190b06d9168f57f77b85924d1e63b13524bc15abcbf6",
- "feed_list": "931e43d328a116318c510de5658c688cd940b934c86b6ec82a472e1f81e020ae",
- "feed_menu": "318d8662dda5ca9dfc75b909c8461e79c86fb5082df1428f67aaf856f19f4b50",
- "icons": "7161afa4cce46245a99cb1e49a605d3ff30e907c3f568ef9c17218718d20e042",
- "item_meta": "fefa219c8296f0370632336ed59a2c8b0c2146ee77f3b10de1d9b87982219dc5",
- "layout": "03c77ed0163b790c0622ecec173119537087c66f6a3925a931ae83a9a94d32cf",
- "pagination": "7b61288e86283c4cf0dc83bcbf8bf1c00c7cb29e60201c8c0b633b2450d2911f",
- "settings_menu": "e2b777630c0efdbc529800303c01d6744ed3af80ec505ac5a5b3f99c9b989156",
-}
diff --git a/template/doc.go b/template/doc.go
deleted file mode 100644
index a4da6b2a..00000000
--- a/template/doc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2018 Frédéric Guillot. All rights reserved.
-// Use of this source code is governed by the Apache 2.0
-// license that can be found in the LICENSE file.
-
-/*
-
-Package template handles template parsing and execution.
-
-*/
-package template // import "miniflux.app/template"
diff --git a/template/engine.go b/template/engine.go
index c7f75660..5f4d71e2 100644
--- a/template/engine.go
+++ b/template/engine.go
@@ -6,7 +6,9 @@ package template // import "miniflux.app/template"
import (
"bytes"
+ "embed"
"html/template"
+ "strings"
"time"
"miniflux.app/errors"
@@ -16,22 +18,64 @@ import (
"github.com/gorilla/mux"
)
+//go:embed templates/common/*.html
+var commonTemplateFiles embed.FS
+
+//go:embed templates/views/*.html
+var viewTemplateFiles embed.FS
+
// Engine handles the templating system.
type Engine struct {
templates map[string]*template.Template
funcMap *funcMap
}
-func (e *Engine) parseAll() {
- commonTemplates := ""
- for _, content := range templateCommonMap {
- commonTemplates += content
+// NewEngine returns a new template engine.
+func NewEngine(router *mux.Router) *Engine {
+ return &Engine{
+ templates: make(map[string]*template.Template),
+ funcMap: &funcMap{router},
+ }
+}
+
+// ParseTemplates parses template files embed into the application.
+func (e *Engine) ParseTemplates() error {
+ var commonTemplateContents strings.Builder
+
+ dirEntries, err := commonTemplateFiles.ReadDir("templates/common")
+ if err != nil {
+ return err
}
- for name, content := range templateViewsMap {
- logger.Debug("[Template] Parsing: %s", name)
- e.templates[name] = template.Must(template.New("main").Funcs(e.funcMap.Map()).Parse(commonTemplates + content))
+ for _, dirEntry := range dirEntries {
+ fileData, err := commonTemplateFiles.ReadFile("templates/common/" + dirEntry.Name())
+ if err != nil {
+ return err
+ }
+ commonTemplateContents.Write(fileData)
}
+
+ dirEntries, err = viewTemplateFiles.ReadDir("templates/views")
+ if err != nil {
+ return err
+ }
+
+ for _, dirEntry := range dirEntries {
+ templateName := dirEntry.Name()
+ fileData, err := viewTemplateFiles.ReadFile("templates/views/" + dirEntry.Name())
+ if err != nil {
+ return err
+ }
+
+ var templateContents strings.Builder
+ templateContents.WriteString(commonTemplateContents.String())
+ templateContents.Write(fileData)
+
+ logger.Debug("[Template] Parsing: %s", templateName)
+ e.templates[templateName] = template.Must(template.New("main").Funcs(e.funcMap.Map()).Parse(templateContents.String()))
+ }
+
+ return nil
}
// Render process a template.
@@ -75,14 +119,3 @@ func (e *Engine) Render(name, language string, data interface{}) []byte {
return b.Bytes()
}
-
-// NewEngine returns a new template engine.
-func NewEngine(router *mux.Router) *Engine {
- tpl := &Engine{
- templates: make(map[string]*template.Template),
- funcMap: &funcMap{router},
- }
-
- tpl.parseAll()
- return tpl
-}
diff --git a/template/html/common/entry_pagination.html b/template/templates/common/entry_pagination.html
similarity index 100%
rename from template/html/common/entry_pagination.html
rename to template/templates/common/entry_pagination.html
diff --git a/template/html/common/feed_list.html b/template/templates/common/feed_list.html
similarity index 100%
rename from template/html/common/feed_list.html
rename to template/templates/common/feed_list.html
diff --git a/template/html/common/feed_menu.html b/template/templates/common/feed_menu.html
similarity index 100%
rename from template/html/common/feed_menu.html
rename to template/templates/common/feed_menu.html
diff --git a/template/html/common/icons.html b/template/templates/common/icons.html
similarity index 100%
rename from template/html/common/icons.html
rename to template/templates/common/icons.html
diff --git a/template/html/common/item_meta.html b/template/templates/common/item_meta.html
similarity index 100%
rename from template/html/common/item_meta.html
rename to template/templates/common/item_meta.html
diff --git a/template/html/common/layout.html b/template/templates/common/layout.html
similarity index 100%
rename from template/html/common/layout.html
rename to template/templates/common/layout.html
diff --git a/template/html/common/pagination.html b/template/templates/common/pagination.html
similarity index 100%
rename from template/html/common/pagination.html
rename to template/templates/common/pagination.html
diff --git a/template/html/common/settings_menu.html b/template/templates/common/settings_menu.html
similarity index 100%
rename from template/html/common/settings_menu.html
rename to template/templates/common/settings_menu.html
diff --git a/template/html/about.html b/template/templates/views/about.html
similarity index 100%
rename from template/html/about.html
rename to template/templates/views/about.html
diff --git a/template/html/add_subscription.html b/template/templates/views/add_subscription.html
similarity index 100%
rename from template/html/add_subscription.html
rename to template/templates/views/add_subscription.html
diff --git a/template/html/api_keys.html b/template/templates/views/api_keys.html
similarity index 100%
rename from template/html/api_keys.html
rename to template/templates/views/api_keys.html
diff --git a/template/html/bookmark_entries.html b/template/templates/views/bookmark_entries.html
similarity index 100%
rename from template/html/bookmark_entries.html
rename to template/templates/views/bookmark_entries.html
diff --git a/template/html/categories.html b/template/templates/views/categories.html
similarity index 100%
rename from template/html/categories.html
rename to template/templates/views/categories.html
diff --git a/template/html/category_entries.html b/template/templates/views/category_entries.html
similarity index 100%
rename from template/html/category_entries.html
rename to template/templates/views/category_entries.html
diff --git a/template/html/category_feeds.html b/template/templates/views/category_feeds.html
similarity index 100%
rename from template/html/category_feeds.html
rename to template/templates/views/category_feeds.html
diff --git a/template/html/choose_subscription.html b/template/templates/views/choose_subscription.html
similarity index 100%
rename from template/html/choose_subscription.html
rename to template/templates/views/choose_subscription.html
diff --git a/template/html/create_api_key.html b/template/templates/views/create_api_key.html
similarity index 100%
rename from template/html/create_api_key.html
rename to template/templates/views/create_api_key.html
diff --git a/template/html/create_category.html b/template/templates/views/create_category.html
similarity index 100%
rename from template/html/create_category.html
rename to template/templates/views/create_category.html
diff --git a/template/html/create_user.html b/template/templates/views/create_user.html
similarity index 100%
rename from template/html/create_user.html
rename to template/templates/views/create_user.html
diff --git a/template/html/edit_category.html b/template/templates/views/edit_category.html
similarity index 100%
rename from template/html/edit_category.html
rename to template/templates/views/edit_category.html
diff --git a/template/html/edit_feed.html b/template/templates/views/edit_feed.html
similarity index 100%
rename from template/html/edit_feed.html
rename to template/templates/views/edit_feed.html
diff --git a/template/html/edit_user.html b/template/templates/views/edit_user.html
similarity index 100%
rename from template/html/edit_user.html
rename to template/templates/views/edit_user.html
diff --git a/template/html/entry.html b/template/templates/views/entry.html
similarity index 100%
rename from template/html/entry.html
rename to template/templates/views/entry.html
diff --git a/template/html/feed_entries.html b/template/templates/views/feed_entries.html
similarity index 100%
rename from template/html/feed_entries.html
rename to template/templates/views/feed_entries.html
diff --git a/template/html/feeds.html b/template/templates/views/feeds.html
similarity index 100%
rename from template/html/feeds.html
rename to template/templates/views/feeds.html
diff --git a/template/html/history_entries.html b/template/templates/views/history_entries.html
similarity index 100%
rename from template/html/history_entries.html
rename to template/templates/views/history_entries.html
diff --git a/template/html/import.html b/template/templates/views/import.html
similarity index 100%
rename from template/html/import.html
rename to template/templates/views/import.html
diff --git a/template/html/integrations.html b/template/templates/views/integrations.html
similarity index 100%
rename from template/html/integrations.html
rename to template/templates/views/integrations.html
diff --git a/template/html/login.html b/template/templates/views/login.html
similarity index 100%
rename from template/html/login.html
rename to template/templates/views/login.html
diff --git a/template/html/search_entries.html b/template/templates/views/search_entries.html
similarity index 100%
rename from template/html/search_entries.html
rename to template/templates/views/search_entries.html
diff --git a/template/html/sessions.html b/template/templates/views/sessions.html
similarity index 100%
rename from template/html/sessions.html
rename to template/templates/views/sessions.html
diff --git a/template/html/settings.html b/template/templates/views/settings.html
similarity index 100%
rename from template/html/settings.html
rename to template/templates/views/settings.html
diff --git a/template/html/shared_entries.html b/template/templates/views/shared_entries.html
similarity index 100%
rename from template/html/shared_entries.html
rename to template/templates/views/shared_entries.html
diff --git a/template/html/unread_entries.html b/template/templates/views/unread_entries.html
similarity index 100%
rename from template/html/unread_entries.html
rename to template/templates/views/unread_entries.html
diff --git a/template/html/users.html b/template/templates/views/users.html
similarity index 100%
rename from template/html/users.html
rename to template/templates/views/users.html
diff --git a/template/views.go b/template/views.go
deleted file mode 100644
index 56c9284f..00000000
--- a/template/views.go
+++ /dev/null
@@ -1,1643 +0,0 @@
-// Code generated by go generate; DO NOT EDIT.
-
-package template // import "miniflux.app/template"
-
-var templateViewsMap = map[string]string{
- "about": `{{ define "title"}}{{ t "page.about.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-
Miniflux
-
- {{ t "page.about.version" }} {{ .version }}
- Git Commit {{ .commit }}
- {{ t "page.about.build_date" }} {{ .build_date }}
- {{ if .user.IsAdmin }}{{ t "page.about.postgres_version" }} {{ .postgres_version }} {{ end }}
-
-
-
-
-
{{ t "page.about.credits" }}
-
- {{ t "page.about.author" }} Frédéric Guillot
- {{ t "page.about.license" }} Apache 2.0
-
-
-
-{{ if .user.IsAdmin }}
-
-
{{ t "page.about.global_config_options" }}
-
- {{ range .globalConfigOptions }}
- {{ .Key }} ={{ .Value }}
- {{ end }}
-
-
-{{ end }}
-
-{{ end }}
-`,
- "add_subscription": `{{ define "title"}}{{ t "page.add_feed.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .categories }}
- {{ t "page.add_feed.no_category" }}
-{{ else }}
-
-{{ end }}
-
-{{ end }}
-`,
- "api_keys": `{{ define "title"}}{{ t "page.api_keys.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ if .apiKeys }}
-{{ range .apiKeys }}
-
-
- {{ t "page.api_keys.table.description" }}
- {{ .Description }}
-
-
- {{ t "page.api_keys.table.token" }}
- {{ .Token }}
-
-
- {{ t "page.api_keys.table.last_used_at" }}
-
- {{ if .LastUsedAt }}
- {{ elapsed $.user.Timezone .LastUsedAt }}
- {{ else }}
- {{ t "page.api_keys.never_used" }}
- {{ end }}
-
-
-
- {{ t "page.api_keys.table.created_at" }}
-
- {{ elapsed $.user.Timezone .CreatedAt }}
-
-
-
- {{ t "page.api_keys.table.actions" }}
-
- {{ t "action.remove" }}
-
-
-
-
-{{ end }}
-{{ end }}
-
-{{ t "page.integration.miniflux_api" }}
-
-
-
- {{ t "page.integration.miniflux_api_endpoint" }} = {{ baseURL }}/v1/
-
-
- {{ t "page.integration.miniflux_api_username" }} = {{ .user.Username }}
-
-
- {{ t "page.integration.miniflux_api_password" }} = {{ t "page.integration.miniflux_api_password_value" }}
-
-
-
-
-
- {{ t "menu.create_api_key" }}
-
-
-{{ end }}
-`,
- "bookmark_entries": `{{ define "title"}}{{ t "page.starred.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_bookmark" }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "categories": `{{ define "title"}}{{ t "page.categories.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .categories }}
- {{ t "alert.no_category" }}
-{{ else }}
-
- {{ range .categories }}
-
-
-
-
- {{ end }}
-
-{{ end }}
-
-{{ end }}
-`,
- "category_entries": `{{ define "title"}}{{ .category.Title }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_category_entry" }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "category_feeds": `{{ define "title"}}{{ .category.Title }} > {{ t "page.feeds.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .feeds }}
- {{ t "alert.no_feed_in_category" }}
-{{ else }}
- {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
-{{ end }}
-
-{{ end }}
-`,
- "choose_subscription": `{{ define "title"}}{{ t "page.add_feed.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "create_api_key": `{{ define "title"}}{{ t "page.new_api_key.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "create_category": `{{ define "title"}}{{ t "page.new_category.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "create_user": `{{ define "title"}}{{ t "page.new_user.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "edit_category": `{{ define "title"}}{{ t "page.edit_category.title" .category.Title }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "edit_feed": `{{ define "title"}}{{ t "page.edit_feed.title" .feed.Title }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .categories }}
- {{ t "page.add_feed.no_category" }}
-{{ else }}
- {{ if ne .feed.ParsingErrorCount 0 }}
-
-
{{ t "page.edit_feed.last_parsing_error" }}
-
{{ t .feed.ParsingErrorMsg }}
-
- {{ end }}
-
-
-
-
-
- {{ t "page.edit_feed.last_check" }} {{ elapsed $.user.Timezone .feed.CheckedAt }}
- {{ t "page.edit_feed.etag_header" }} {{ if .feed.EtagHeader }}{{ .feed.EtagHeader }}{{ else }}{{ t "page.edit_feed.no_header" }}{{ end }}
- {{ t "page.edit_feed.last_modified_header" }} {{ if .feed.LastModifiedHeader }}{{ .feed.LastModifiedHeader }}{{ else }}{{ t "page.edit_feed.no_header" }}{{ end }}
-
-
-
-
-{{ end }}
-
-{{ end }}
-`,
- "edit_user": `{{ define "title"}}{{ t "page.edit_user.title" .selected_user.Username }}{{ end }}
-
-{{ define "content"}}
-
-
-
-{{ end }}
-`,
- "entry": `{{ define "title"}}{{ .entry.Title }}{{ end }}
-
-{{ define "content"}}
-
-
- {{ if gt (len .entry.Content) 120 }}
- {{ if .user }}
-
- {{ end }}
- {{ end }}
-
- {{ if .user }}
- {{ noescape (proxyFilter .entry.Content) }}
- {{ else }}
- {{ noescape .entry.Content }}
- {{ end }}
-
- {{ if .entry.Enclosures }}
-
- {{ t "page.entry.attachments" }} ({{ len .entry.Enclosures }})
- {{ range .entry.Enclosures }}
- {{ if ne .URL "" }}
-
- {{ if hasPrefix .MimeType "audio/" }}
-
- {{ else if hasPrefix .MimeType "video/" }}
-
-
-
-
-
- {{ else if hasPrefix .MimeType "image/" }}
-
- {{ if $.user }}
-
- {{ else }}
-
- {{ end }}
-
- {{ end }}
-
-
-
{{ .URL | safeURL }}
-
{{ if gt .Size 0 }} - {{ formatFileSize .Size }} {{ end }}
-
-
- {{ end }}
- {{ end }}
-
- {{ end }}
-
-
-{{ if .user }}
-
-{{ end }}
-{{ end }}
-`,
- "feed_entries": `{{ define "title"}}{{ .feed.Title }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if ne .feed.ParsingErrorCount 0 }}
-
-
{{ t "alert.feed_error" }}
-
{{ t .feed.ParsingErrorMsg }}
-
-{{ end }}
-
-{{ if not .entries }}
- {{ if .showOnlyUnreadEntries }}
- {{ t "alert.no_unread_entry" }}
- {{ else }}
- {{ t "alert.no_feed_entry" }}
- {{ end }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "feeds": `{{ define "title"}}{{ t "page.feeds.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .feeds }}
- {{ t "alert.no_feed" }}
-{{ else }}
- {{ template "feed_list" dict "user" .user "feeds" .feeds "ParsingErrorCount" .ParsingErrorCount }}
-{{ end }}
-
-{{ end }}
-`,
- "history_entries": `{{ define "title"}}{{ t "page.history.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_history" }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "import": `{{ define "title"}}{{ t "page.import.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ if .errorMessage }}
- {{ t .errorMessage }}
-{{ end }}
-
-
-
-
-
-{{ end }}
-`,
- "integrations": `{{ define "title"}}{{ t "page.integrations.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-
-{{ t "page.integration.bookmarklet" }}
-
-
{{ t "page.integration.bookmarklet.help" }}
-
-
-
-
{{ t "page.integration.bookmarklet.instructions" }}
-
-
-{{ end }}
-`,
- "login": `{{ define "title"}}{{ t "page.login.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ end }}
-`,
- "search_entries": `{{ define "title"}}{{ t "page.search.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_search_result" }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "sessions": `{{ define "title"}}{{ t "page.sessions.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-
- {{ t "page.sessions.table.date" }}
- {{ t "page.sessions.table.ip" }}
- {{ t "page.sessions.table.user_agent" }}
- {{ t "page.sessions.table.actions" }}
-
- {{ range .sessions }}
-
- {{ elapsed $.user.Timezone .CreatedAt }}
- {{ .IP }}
- {{ .UserAgent }}
-
- {{ if eq .Token $.currentSessionToken }}
- {{ t "page.sessions.table.current_session" }}
- {{ else }}
- {{ t "action.remove" }}
- {{ end }}
-
-
- {{ end }}
-
-
-{{ end }}
-`,
- "settings": `{{ define "title"}}{{ t "page.settings.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-
-
-{{ if hasOAuth2Provider "google" }}
-
-{{ else if hasOAuth2Provider "oidc" }}
-
-{{ end }}
-
-{{ end }}
-`,
- "shared_entries": `{{ define "title"}}{{ t "page.shared_entries.title" }} ({{ .total }}){{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_shared_entry" }}
-{{ else }}
-
- {{ range .entries }}
-
-
-
-
- {{ end }}
-
-{{ end }}
-
-{{ end }}
-`,
- "unread_entries": `{{ define "title"}}{{ t "page.unread.title" }} {{ if gt .countUnread 0 }}({{ .countUnread }}){{ end }} {{ end }}
-
-{{ define "content"}}
-
-
-{{ if not .entries }}
- {{ t "alert.no_unread_entry" }}
-{{ else }}
-
- {{ range .entries }}
-
-
- {{ template "item_meta" dict "user" $.user "entry" . "hasSaveEntry" $.hasSaveEntry }}
-
- {{ end }}
-
-
- {{ template "pagination" .pagination }}
-{{ end }}
-
-{{ end }}
-`,
- "users": `{{ define "title"}}{{ t "page.users.title" }}{{ end }}
-
-{{ define "content"}}
-
-
-{{ if eq (len .users) 1 }}
- {{ t "alert.no_user" }}
-{{ else }}
-
-
- {{ t "page.users.username" }}
- {{ t "page.users.is_admin" }}
- {{ t "page.users.last_login" }}
- {{ t "page.users.actions" }}
-
- {{ range .users }}
- {{ if ne .ID $.user.ID }}
-
- {{ .Username }}
- {{ if eq .IsAdmin true }}{{ t "page.users.admin.yes" }}{{ else }}{{ t "page.users.admin.no" }}{{ end }}
-
- {{ if .LastLoginAt }}
- {{ elapsed $.user.Timezone .LastLoginAt }}
- {{ else }}
- {{ t "page.users.never_logged" }}
- {{ end }}
-
-
- {{ t "action.edit" }} ,
- {{ t "action.remove" }}
-
-
- {{ end }}
- {{ end }}
-
-
-{{ end }}
-
-
- {{ t "menu.add_user" }}
-
-
-{{ end }}
-`,
-}
-
-var templateViewsMapChecksums = map[string]string{
- "about": "ec7987f2612764363a07fa61bdbaacd3ccaa12f567b84677bbd20caf026fe5b2",
- "add_subscription": "bc0f878b37692a00d51e834536f211843a59703991d2a743ef204b9d6ae38549",
- "api_keys": "27d401b31a72881d5232486ba17eb47edaf5246eaedce81de88698c15ebb2284",
- "bookmark_entries": "eacbbdce7fa85ec66c4c12f02879daab562a17ff79f1aac1805617e83e3a3a42",
- "categories": "9dfc3cb7bb91c7750753fe962ee4540dd1843e5f75f9e0a575ee964f6f9923e9",
- "category_entries": "ef3005f8f4c96182587acbf31b979cc26b1ac8f755a74cd5a25681260f4b6d63",
- "category_feeds": "07154127087f9b127f7290abad6020c35ad9ceb2490b869120b7628bc4413808",
- "choose_subscription": "22109d760ea8079c491561d0106f773c885efbf66f87d81fcf8700218260d2a0",
- "create_api_key": "2fbd74342176b9970d9162a54da99186589621e4c005566a5368fc4a7994ad20",
- "create_category": "6b22b5ce51abf4e225e23a79f81be09a7fb90acb265e93a8faf9446dff74018d",
- "create_user": "cca0dbdbd846639d5295707de0674e5e75df987dd22b80d75f030f8daa503a85",
- "edit_category": "b1c0b38f1b714c5d884edcd61e5b5295a5f1c8b71c469b35391e4dcc97cc6d36",
- "edit_feed": "3da1edc78a464f33359663028f0b3fd11706b98e0c3851b090a20ccb2f780b02",
- "edit_user": "04423f5ea4249a97440ddd892f99ff96c646f6ce26313765ac5293abf257ef3c",
- "entry": "07ccdd5b9e99c63872bcab44b70b347cb59424fc8b69fd671b99b832c47277cc",
- "feed_entries": "89977ea86b8d43305d587b70e6d9c45c2c88249b3966f2d31051dc7a5f1c48b6",
- "feeds": "ec7d3fa96735bd8422ba69ef0927dcccddc1cc51327e0271f0312d3f881c64fd",
- "history_entries": "261b47e5f2f699a9cef1b3b690f80d7aabf585d05b77d67645d623f7ff6c0fbb",
- "import": "1b59b3bd55c59fcbc6fbb346b414dcdd26d1b4e0c307e437bb58b3f92ef01ad1",
- "integrations": "92d5ab36361f9a2e2b9b7e2318494123f4976d4410daf117810ebf6eca8250b6",
- "login": "9165434b2405e9332de4bebbb54a93dc5692276ea72e7c5e07f655a002dfd290",
- "search_entries": "6a3e5876cb7541a2f08f56e30ab46a2d7d64894ec5e170f627b2dd674d8aeefe",
- "sessions": "5d5c677bddbd027e0b0c9f7a0dd95b66d9d95b4e130959f31fb955b926c2201c",
- "settings": "8e90e9e48c62990c2aca217054cb4e122e4ed58c377e28d4c150e2d2d22ebe74",
- "shared_entries": "f87a42bf44dc3606c5a44b185263c1b9a612a8ae194f75061253d4dde7b095a2",
- "unread_entries": "21c584da7ca8192655c62f16a7ac92dbbfdf1307588ffe51eb4a8bbf3f9f7526",
- "users": "d7ff52efc582bbad10504f4a04fa3adcc12d15890e45dff51cac281e0c446e45",
-}
diff --git a/ui/ui.go b/ui/ui.go
index 027b08f3..3ef2324d 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -8,6 +8,7 @@ import (
"net/http"
"miniflux.app/config"
+ "miniflux.app/logger"
"miniflux.app/storage"
"miniflux.app/template"
"miniflux.app/worker"
@@ -19,7 +20,13 @@ import (
// Serve declares all routes for the user interface.
func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
middleware := newMiddleware(router, store)
- handler := &handler{router, store, template.NewEngine(router), pool}
+
+ templateEngine := template.NewEngine(router)
+ if err := templateEngine.ParseTemplates(); err != nil {
+ logger.Fatal(`Unable to parse templates: %v`, err)
+ }
+
+ handler := &handler{router, store, templateEngine, pool}
uiRouter := router.NewRoute().Subrouter()
uiRouter.Use(middleware.handleUserSession)
diff --git a/ui/view/view.go b/ui/view/view.go
index 7307b0be..f655637c 100644
--- a/ui/view/view.go
+++ b/ui/view/view.go
@@ -28,7 +28,7 @@ func (v *View) Set(param string, value interface{}) *View {
// Render executes the template with arguments.
func (v *View) Render(template string) []byte {
- return v.tpl.Render(template, request.UserLanguage(v.r), v.params)
+ return v.tpl.Render(template+".html", request.UserLanguage(v.r), v.params)
}
// New returns a new view with default parameters.