File: engine.go

package info (click to toggle)
miniflux 2.2.16-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,188 kB
  • sloc: xml: 4,853; javascript: 1,158; sh: 257; makefile: 161
file content (116 lines) | stat: -rw-r--r-- 4,326 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package template // import "miniflux.app/v2/internal/template"

import (
	"bytes"
	"embed"
	"html/template"
	"time"

	"miniflux.app/v2/internal/locale"

	"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
}

// NewEngine returns a new template engine.
func NewEngine(router *mux.Router) *Engine {
	return &Engine{
		templates: make(map[string]*template.Template),
		funcMap:   &funcMap{router},
	}
}

func (e *Engine) ParseTemplates() {
	funcMap := e.funcMap.Map()
	templates := map[string][]string{ // this isn't a global variable so that it can be garbage-collected.
		"about.html":               {"layout.html", "settings_menu.html"},
		"add_subscription.html":    {"feed_menu.html", "layout.html", "settings_menu.html"},
		"api_keys.html":            {"layout.html", "settings_menu.html"},
		"starred_entries.html":     {"item_meta.html", "layout.html", "pagination.html"},
		"categories.html":          {"layout.html"},
		"category_entries.html":    {"item_meta.html", "layout.html", "pagination.html"},
		"category_feeds.html":      {"feed_list.html", "layout.html"},
		"choose_subscription.html": {"feed_menu.html", "layout.html"},
		"create_api_key.html":      {"layout.html", "settings_menu.html"},
		"create_category.html":     {"layout.html"},
		"create_user.html":         {"layout.html", "settings_menu.html"},
		"edit_category.html":       {"layout.html", "settings_menu.html"},
		"edit_feed.html":           {"layout.html"},
		"edit_user.html":           {"layout.html", "settings_menu.html"},
		"entry.html":               {"layout.html"},
		"feed_entries.html":        {"item_meta.html", "layout.html", "pagination.html"},
		"feeds.html":               {"feed_list.html", "feed_menu.html", "item_meta.html", "layout.html", "pagination.html"},
		"history_entries.html":     {"item_meta.html", "layout.html", "pagination.html"},
		"import.html":              {"feed_menu.html", "layout.html"},
		"integrations.html":        {"layout.html", "settings_menu.html"},
		"login.html":               {"layout.html"},
		"offline.html":             {},
		"search.html":              {"item_meta.html", "layout.html", "pagination.html"},
		"sessions.html":            {"layout.html", "settings_menu.html"},
		"settings.html":            {"layout.html", "settings_menu.html"},
		"shared_entries.html":      {"layout.html", "pagination.html"},
		"tag_entries.html":         {"item_meta.html", "layout.html", "pagination.html"},
		"unread_entries.html":      {"item_meta.html", "layout.html", "pagination.html"},
		"users.html":               {"layout.html", "settings_menu.html"},
		"webauthn_rename.html":     {"layout.html"},
	}

	for name, dependencies := range templates {
		tpl := template.New("").Funcs(funcMap)
		for _, dependency := range dependencies {
			template.Must(tpl.ParseFS(commonTemplateFiles, "templates/common/"+dependency))
		}
		e.templates[name] = template.Must(tpl.ParseFS(viewTemplateFiles, "templates/views/"+name))
	}

	// Sanity check to ensure that all templates are correctly declared in `templates`.
	if entries, err := viewTemplateFiles.ReadDir("templates/views"); err == nil {
		for _, entry := range entries {
			if _, ok := e.templates[entry.Name()]; !ok {
				panic("Template " + entry.Name() + " isn't declared in ParseTemplates")
			}
		}
	} else {
		panic("Unable to read all embedded views templates")
	}
}

// Render process a template.
func (e *Engine) Render(name string, data map[string]any) []byte {
	tpl, ok := e.templates[name]
	if !ok {
		panic("The template " + name + " does not exists.")
	}

	printer := locale.NewPrinter(data["language"].(string))

	// Functions that need to be declared at runtime.
	tpl.Funcs(template.FuncMap{
		"elapsed": func(timezone string, t time.Time) string {
			return elapsedTime(printer, timezone, t)
		},
		"t":      printer.Printf,
		"plural": printer.Plural,
	})

	var b bytes.Buffer
	if err := tpl.ExecuteTemplate(&b, "base", data); err != nil {
		panic(err)
	}

	return b.Bytes()
}