| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- // 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"
- "log/slog"
- "strings"
- "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
- //go:embed templates/standalone/*.html
- var standaloneTemplateFiles 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},
- }
- }
- // 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 _, 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)
- slog.Debug("Parsing template",
- slog.String("template_name", templateName),
- )
- e.templates[templateName] = template.Must(template.New("main").Funcs(e.funcMap.Map()).Parse(templateContents.String()))
- }
- dirEntries, err = standaloneTemplateFiles.ReadDir("templates/standalone")
- if err != nil {
- return err
- }
- for _, dirEntry := range dirEntries {
- templateName := dirEntry.Name()
- fileData, err := standaloneTemplateFiles.ReadFile("templates/standalone/" + dirEntry.Name())
- if err != nil {
- return err
- }
- slog.Debug("Parsing template",
- slog.String("template_name", templateName),
- )
- e.templates[templateName] = template.Must(template.New("base").Funcs(e.funcMap.Map()).Parse(string(fileData)))
- }
- return nil
- }
- // Render process a template.
- func (e *Engine) Render(name string, data map[string]interface{}) []byte {
- tpl, ok := e.templates[name]
- if !ok {
- panic("This template does not exists: " + name)
- }
- 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": func(key interface{}, args ...interface{}) string {
- switch k := key.(type) {
- case string:
- return printer.Printf(k, args...)
- case error:
- return k.Error()
- default:
- return ""
- }
- },
- "plural": func(key string, n int, args ...interface{}) string {
- return printer.Plural(key, n, args...)
- },
- })
- var b bytes.Buffer
- err := tpl.ExecuteTemplate(&b, "base", data)
- if err != nil {
- panic(err)
- }
- return b.Bytes()
- }
|