From 18d3ebbbd6e21c024c5ccd6d876796d2f898dbe5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 1 May 2023 11:37:37 +0800 Subject: [PATCH] poc --- modules/context/context.go | 25 ++++++++++++++++++++-- modules/templates/helper.go | 4 ++++ modules/templates/htmlrenderer.go | 19 ++++++++++++++++ modules/templates/scopedtmpl/scopedtmpl.go | 8 +++---- templates/devtest/ctxfunc.tmpl | 2 ++ 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 templates/devtest/ctxfunc.tmpl diff --git a/modules/context/context.go b/modules/context/context.go index cd7fcebe55da5..db8e92dc571db 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -31,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" @@ -216,6 +217,26 @@ func (ctx *Context) RedirectToFirst(location ...string) { const tplStatus500 base.TplName = "status/500" +func (ctx *Context) newCtxFuncMap() template.FuncMap { + return template.FuncMap{ + "CtxLocale": func(s string) string { + return ctx.Locale.Tr(s) + }, + "CtxDateTime": func(format string, datetime any) template.HTML { + username := "(anonymous)" + if ctx.Doer != nil { + username = ctx.Doer.Name + } + s := fmt.Sprintf("PoC demo for %q: %s", html.EscapeString(username), timeutil.DateTime(format, datetime)) + return template.HTML(s) + }, + } +} + +func (ctx *Context) renderHTML(w io.Writer, status int, name base.TplName, data map[string]any) error { + return ctx.Render.(*templates.HTMLRender).HTMLCtxFuncs(ctx.Resp, status, string(name), templates.BaseVars().Merge(data), ctx.newCtxFuncMap()) +} + // HTML calls Context.HTML and renders the template to HTTP response func (ctx *Context) HTML(status int, name base.TplName) { log.Debug("Template: %s", name) @@ -226,7 +247,7 @@ func (ctx *Context) HTML(status int, name base.TplName) { ctx.Data["TemplateLoadTimes"] = func() string { return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms" } - if err := ctx.Render.HTML(ctx.Resp, status, string(name), templates.BaseVars().Merge(ctx.Data)); err != nil { + if err := ctx.renderHTML(ctx.Resp, status, name, ctx.Data); err != nil { if status == http.StatusInternalServerError && name == tplStatus500 { ctx.PlainText(http.StatusInternalServerError, "Unable to find HTML templates, the template system is not initialized, or Gitea can't find your template files.") return @@ -239,7 +260,7 @@ func (ctx *Context) HTML(status int, name base.TplName) { // RenderToString renders the template content to a string func (ctx *Context) RenderToString(name base.TplName, data map[string]interface{}) (string, error) { var buf strings.Builder - err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data) + err := ctx.renderHTML(&buf, http.StatusOK, name, data) return buf.String(), err } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 4abd94d46e59e..f19b36965e1d8 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -30,6 +30,10 @@ func NewFuncMap() template.FuncMap { return map[string]interface{}{ "DumpVar": dumpVar, + // ctx func + "CtxLocale": func(v ...any) any { panic("not implemented") }, + "CtxDateTime": func(v ...any) any { panic("not implemented") }, + // ----------------------------------------------------------------- // html/template related functions "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names. diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index d60be887278a5..41ab04a7ece45 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -8,6 +8,7 @@ import ( "bytes" "errors" "fmt" + "html/template" "io" "net/http" "path/filepath" @@ -53,6 +54,24 @@ func (h *HTMLRender) HTML(w io.Writer, status int, name string, data interface{} return t.Execute(w, data) } +func (h *HTMLRender) HTMLCtxFuncs(w io.Writer, status int, name string, data interface{}, ctxFuncMap template.FuncMap) 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) + } + tmpls := h.templates.Load() + if tmpls == nil { + return ErrTemplateNotInitialized + } + st, err := tmpls.Executor(name, NewFuncMap(), ctxFuncMap) + if err != nil { + return texttemplate.ExecError{Name: name, Err: err} + } + return st.Execute(w, data) +} + func (h *HTMLRender) TemplateLookup(name string) (TemplateExecutor, error) { tmpls := h.templates.Load() if tmpls == nil { diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index a8b67ad0f908c..30df9714fe436 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -62,7 +62,7 @@ func (t *ScopedTemplate) Freeze() { t.all.Funcs(m) } -func (t *ScopedTemplate) Executor(name string, funcMap template.FuncMap) (TemplateExecutor, error) { +func (t *ScopedTemplate) Executor(name string, funcMaps ...template.FuncMap) (TemplateExecutor, error) { t.scopedMu.RLock() scopedTmplSet, ok := t.scopedTemplateSets[name] t.scopedMu.RUnlock() @@ -84,7 +84,7 @@ func (t *ScopedTemplate) Executor(name string, funcMap template.FuncMap) (Templa if scopedTmplSet == nil { return nil, fmt.Errorf("template %s not found", name) } - return scopedTmplSet.newExecutor(funcMap), nil + return scopedTmplSet.newExecutor(funcMaps...), nil } type scopedTemplateSet struct { @@ -216,14 +216,14 @@ func newScopedTemplateSet(all *template.Template, name string) (*scopedTemplateS return ts, collectErr } -func (ts *scopedTemplateSet) newExecutor(funcMap map[string]any) TemplateExecutor { +func (ts *scopedTemplateSet) newExecutor(funcMaps ...template.FuncMap) TemplateExecutor { tmpl := texttemplate.New("") tmplPtr := ptr[textTemplate](tmpl) tmplPtr.execFuncs = map[string]reflect.Value{} for k, v := range ts.execFuncs { tmplPtr.execFuncs[k] = v } - if funcMap != nil { + for _, funcMap := range funcMaps { tmpl.Funcs(funcMap) } // after escapeTemplate, the html templates are also escaped text templates, so it could be added to the text template directly diff --git a/templates/devtest/ctxfunc.tmpl b/templates/devtest/ctxfunc.tmpl new file mode 100644 index 0000000000000..573f30851fff7 --- /dev/null +++ b/templates/devtest/ctxfunc.tmpl @@ -0,0 +1,2 @@ +Locale: {{CtxLocale "home"}}
+DateTime: {{CtxDateTime "full" "2001-02-03"}}