Drop "unrolled/render" package (#23965)
None of the features of `unrolled/render` package is used. The Golang builtin "html/template" just works well. Then we can improve our HTML render to resolve the "$.root.locale.Tr" problem as much as possible. Next step: we can have a template render pool (by Clone), then we can inject global functions with dynamic context to every `Execute` calls. Then we can use `{{Locale.Tr ....}}` directly in all templates , no need to pass the `$.root.locale` again and again.
This commit is contained in:
parent
7ee2c1336c
commit
8f00979f73
9 changed files with 77 additions and 73 deletions
5
assets/go-licenses.json
generated
5
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
|
@ -7,9 +7,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
goPath "path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -27,9 +28,14 @@ type LicenseEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if len(os.Args) != 3 {
|
||||||
|
fmt.Println("usage: go run generate-go-licenses.go <base-dir> <out-json-file>")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
base, out := os.Args[1], os.Args[2]
|
base, out := os.Args[1], os.Args[2]
|
||||||
|
|
||||||
paths := []string{}
|
var paths []string
|
||||||
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -46,28 +52,27 @@ func main() {
|
||||||
|
|
||||||
sort.Strings(paths)
|
sort.Strings(paths)
|
||||||
|
|
||||||
entries := []LicenseEntry{}
|
var entries []LicenseEntry
|
||||||
for _, path := range paths {
|
for _, filePath := range paths {
|
||||||
path := filepath.ToSlash(path)
|
licenseText, err := os.ReadFile(filePath)
|
||||||
|
|
||||||
licenseText, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path = strings.Replace(path, base+"/", "", 1)
|
pkgPath := filepath.ToSlash(filePath)
|
||||||
name := goPath.Dir(path)
|
pkgPath = strings.TrimPrefix(pkgPath, base+"/")
|
||||||
|
pkgName := path.Dir(pkgPath)
|
||||||
|
|
||||||
// There might be a bug somewhere in go-licenses that sometimes interprets the
|
// There might be a bug somewhere in go-licenses that sometimes interprets the
|
||||||
// root package as "." and sometimes as "code.gitea.io/gitea". Workaround by
|
// root package as "." and sometimes as "code.gitea.io/gitea". Workaround by
|
||||||
// removing both of them for the sake of stable output.
|
// removing both of them for the sake of stable output.
|
||||||
if name == "." || name == "code.gitea.io/gitea" {
|
if pkgName == "." || pkgName == "code.gitea.io/gitea" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = append(entries, LicenseEntry{
|
entries = append(entries, LicenseEntry{
|
||||||
Name: name,
|
Name: pkgName,
|
||||||
Path: path,
|
Path: pkgPath,
|
||||||
LicenseText: string(licenseText),
|
LicenseText: string(licenseText),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -96,7 +96,6 @@ require (
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/tstranex/u2f v1.0.0
|
github.com/tstranex/u2f v1.0.0
|
||||||
github.com/unrolled/render v1.5.0
|
|
||||||
github.com/urfave/cli v1.22.12
|
github.com/urfave/cli v1.22.12
|
||||||
github.com/xanzy/go-gitlab v0.80.2
|
github.com/xanzy/go-gitlab v0.80.2
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1182,8 +1182,6 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
|
||||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||||
github.com/unrolled/render v1.5.0 h1:uNTHMvVoI9pyyXfgoDHHycIqFONNY2p4eQR9ty+NsxM=
|
|
||||||
github.com/unrolled/render v1.5.0/go.mod h1:eLTosBkQqEPEk7pRfkCRApXd++lm++nCsVlFOHpeedw=
|
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
|
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
|
||||||
|
|
|
@ -42,14 +42,13 @@ import (
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
chi "github.com/go-chi/chi/v5"
|
chi "github.com/go-chi/chi/v5"
|
||||||
"github.com/minio/sha256-simd"
|
"github.com/minio/sha256-simd"
|
||||||
"github.com/unrolled/render"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Render represents a template render
|
// Render represents a template render
|
||||||
type Render interface {
|
type Render interface {
|
||||||
TemplateLookup(tmpl string) *template.Template
|
TemplateLookup(tmpl string) *template.Template
|
||||||
HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...render.HTMLOptions) error
|
HTML(w io.Writer, status int, name string, data interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context represents context of a request.
|
// Context represents context of a request.
|
||||||
|
|
|
@ -7,15 +7,18 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/watcher"
|
"code.gitea.io/gitea/modules/watcher"
|
||||||
|
|
||||||
"github.com/unrolled/render"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -27,14 +30,50 @@ var (
|
||||||
expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`)
|
expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use
|
type HTMLRender struct {
|
||||||
func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) {
|
templates atomic.Pointer[template.Template]
|
||||||
rendererInterface := ctx.Value(rendererKey)
|
|
||||||
if rendererInterface != nil {
|
|
||||||
renderer, ok := rendererInterface.(*render.Render)
|
|
||||||
if ok {
|
|
||||||
return ctx, renderer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data interface{}) error {
|
||||||
|
if respWriter, ok := w.(http.ResponseWriter); ok {
|
||||||
|
if respWriter.Header().Get("Content-Type") == "" {
|
||||||
|
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
}
|
||||||
|
respWriter.WriteHeader(status)
|
||||||
|
}
|
||||||
|
return h.templates.Load().ExecuteTemplate(w, name, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTMLRender) TemplateLookup(t string) *template.Template {
|
||||||
|
return h.templates.Load().Lookup(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTMLRender) CompileTemplates() error {
|
||||||
|
dirPrefix := "templates/"
|
||||||
|
tmpls := template.New("")
|
||||||
|
for _, path := range GetTemplateAssetNames() {
|
||||||
|
name := path[len(dirPrefix):]
|
||||||
|
name = strings.TrimSuffix(name, ".tmpl")
|
||||||
|
tmpl := tmpls.New(filepath.ToSlash(name))
|
||||||
|
for _, fm := range NewFuncMap() {
|
||||||
|
tmpl.Funcs(fm)
|
||||||
|
}
|
||||||
|
buf, err := GetAsset(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = tmpl.Parse(string(buf)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.templates.Store(tmpls)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use
|
||||||
|
func HTMLRenderer(ctx context.Context) (context.Context, *HTMLRender) {
|
||||||
|
if renderer, ok := ctx.Value(rendererKey).(*HTMLRender); ok {
|
||||||
|
return ctx, renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
rendererType := "static"
|
rendererType := "static"
|
||||||
|
@ -43,53 +82,24 @@ func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) {
|
||||||
}
|
}
|
||||||
log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer")
|
log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer")
|
||||||
|
|
||||||
compilingTemplates := true
|
renderer := &HTMLRender{}
|
||||||
defer func() {
|
if err := renderer.CompileTemplates(); err != nil {
|
||||||
if !compilingTemplates {
|
handleFatalError(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panicked := recover()
|
|
||||||
if panicked == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK try to handle the panic...
|
|
||||||
err, ok := panicked.(error)
|
|
||||||
if ok {
|
|
||||||
handlePanicError(err)
|
|
||||||
}
|
|
||||||
log.Fatal("PANIC: Unable to compile templates!\n%v\n\nStacktrace:\n%s", panicked, log.Stack(2))
|
|
||||||
}()
|
|
||||||
|
|
||||||
renderer := render.New(render.Options{
|
|
||||||
Extensions: []string{".tmpl"},
|
|
||||||
Directory: "templates",
|
|
||||||
Funcs: NewFuncMap(),
|
|
||||||
Asset: GetAsset,
|
|
||||||
AssetNames: GetTemplateAssetNames,
|
|
||||||
UseMutexLock: !setting.IsProd,
|
|
||||||
IsDevelopment: false,
|
|
||||||
DisableHTTPErrorRendering: true,
|
|
||||||
})
|
|
||||||
compilingTemplates = false
|
|
||||||
if !setting.IsProd {
|
if !setting.IsProd {
|
||||||
watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{
|
watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{
|
||||||
PathsCallback: walkTemplateFiles,
|
PathsCallback: walkTemplateFiles,
|
||||||
BetweenCallback: func() {
|
BetweenCallback: func() {
|
||||||
defer func() {
|
if err := renderer.CompileTemplates(); err != nil {
|
||||||
if err := recover(); err != nil {
|
log.Error("Template error: %v\n%s", err, log.Stack(2))
|
||||||
log.Error("PANIC: %v\n%s", err, log.Stack(2))
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
renderer.CompileTemplates()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return context.WithValue(ctx, rendererKey, renderer), renderer
|
return context.WithValue(ctx, rendererKey, renderer), renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePanicError(err error) {
|
func handleFatalError(err error) {
|
||||||
wrapFatal(handleNotDefinedPanicError(err))
|
wrapFatal(handleNotDefinedPanicError(err))
|
||||||
wrapFatal(handleUnexpected(err))
|
wrapFatal(handleUnexpected(err))
|
||||||
wrapFatal(handleExpectedEnd(err))
|
wrapFatal(handleExpectedEnd(err))
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
|
|
||||||
chi "github.com/go-chi/chi/v5"
|
chi "github.com/go-chi/chi/v5"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/unrolled/render"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockContext mock context for unit tests
|
// MockContext mock context for unit tests
|
||||||
|
@ -138,7 +137,7 @@ func (tr *mockRender) TemplateLookup(tmpl string) *template.Template {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}, _ ...render.HTMLOptions) error {
|
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}) error {
|
||||||
if resp, ok := w.(http.ResponseWriter); ok {
|
if resp, ok := w.(http.ResponseWriter); ok {
|
||||||
resp.WriteHeader(status)
|
resp.WriteHeader(status)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
|
|
||||||
gouuid "github.com/google/uuid"
|
gouuid "github.com/google/uuid"
|
||||||
"github.com/quasoft/websspi"
|
"github.com/quasoft/websspi"
|
||||||
"github.com/unrolled/render"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -48,7 +47,7 @@ var (
|
||||||
// On successful authentication returns a valid user object.
|
// On successful authentication returns a valid user object.
|
||||||
// Returns nil if authentication fails.
|
// Returns nil if authentication fails.
|
||||||
type SSPI struct {
|
type SSPI struct {
|
||||||
rnd *render.Render
|
rnd *templates.HTMLRender
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init creates a new global websspi.Authenticator object
|
// Init creates a new global websspi.Authenticator object
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestExternalMarkupRenderer(t *testing.T) {
|
||||||
const repoURL = "user30/renderer"
|
const repoURL = "user30/renderer"
|
||||||
req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html")
|
req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html")
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
assert.EqualValues(t, "text/html; charset=UTF-8", resp.Header()["Content-Type"][0])
|
assert.EqualValues(t, "text/html; charset=utf-8", resp.Header()["Content-Type"][0])
|
||||||
|
|
||||||
bs, err := io.ReadAll(resp.Body)
|
bs, err := io.ReadAll(resp.Body)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
Loading…
Reference in a new issue