From 8d5c989d97598b69052ce0c5599a9995e70d23d8 Mon Sep 17 00:00:00 2001 From: xing Date: Sat, 21 Jan 2023 21:13:33 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8C=85=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=20=E5=AE=8C=E5=96=84=E9=A6=96=E9=A1=B5=E5=BD=92=E6=A1=A3?= =?UTF-8?q?=E3=80=81=E5=88=86=E7=B1=BB=E3=80=81=E4=BD=9C=E8=80=85=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/actions/index.go | 91 ++++++++++++++----- internal/pkg/cache/cache.go | 57 ++++++++---- internal/pkg/cache/users.go | 4 + internal/pkg/dao/comments.go | 2 +- internal/pkg/dao/common.go | 18 ++-- internal/pkg/dao/postmeta.go | 2 +- internal/pkg/dao/posts.go | 2 +- internal/pkg/dao/users.go | 16 +++- internal/plugins/digest.go | 2 +- internal/plugins/plugins.go | 15 ++- internal/plugins/posts.go | 2 +- internal/theme/hook.go | 18 ++-- .../theme/twentyseventeen/twentyseventeen.go | 4 +- 13 files changed, 158 insertions(+), 75 deletions(-) diff --git a/internal/actions/index.go b/internal/actions/index.go index aa8b41f..6708c64 100644 --- a/internal/actions/index.go +++ b/internal/actions/index.go @@ -1,11 +1,13 @@ package actions import ( + "errors" "fmt" + "github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/slice" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/internal/pkg/cache" - dao "github.com/fthvgb1/wp-go/internal/pkg/dao" + "github.com/fthvgb1/wp-go/internal/pkg/dao" "github.com/fthvgb1/wp-go/internal/pkg/logs" "github.com/fthvgb1/wp-go/internal/pkg/models" "github.com/fthvgb1/wp-go/internal/plugins" @@ -20,6 +22,7 @@ import ( "strconv" "strings" "sync/atomic" + "time" ) type indexHandle struct { @@ -40,10 +43,11 @@ type indexHandle struct { order string join model.SqlBuilder postType []any - status []any + postStatus []any header string paginationStep int - scene uint + scene int + status int } func newIndexHandle(ctx *gin.Context) *indexHandle { @@ -61,13 +65,19 @@ func newIndexHandle(ctx *gin.Context) *indexHandle { {"post_type", "in", ""}, {"post_status", "in", ""}, }, - orderBy: "post_date", - join: model.SqlBuilder{}, - postType: []any{"post"}, - status: []any{"publish"}, - scene: plugins.Home, + orderBy: "post_date", + join: model.SqlBuilder{}, + postType: []any{"post"}, + postStatus: []any{"publish"}, + scene: plugins.Home, + status: plugins.Ok, } } + +var months = slice.SimpleToMap(number.Range(1, 12, 1), func(v int) int { + return v +}) + func (h *indexHandle) setTitleLR(l, r string) { h.titleL = l h.titleR = r @@ -89,12 +99,27 @@ func (h *indexHandle) parseParams() (err error) { } year := h.c.Param("year") if year != "" { + y, er := strconv.Atoi(year) + if er != nil { + return err + } + if y > time.Now().Year() || y <= 1970 { + return errors.New(str.Join("year err : ", year)) + } h.where = append(h.where, []string{ "year(post_date)", year, }) } month := h.c.Param("month") if month != "" { + m, err := strconv.Atoi(month) + if err != nil { + return err + } + if _, ok := months[m]; !ok { + return errors.New(str.Join("months err ", month)) + } + h.where = append(h.where, []string{ "month(post_date)", month, }) @@ -111,12 +136,25 @@ func (h *indexHandle) parseParams() (err error) { h.header = fmt.Sprintf("标签: %s", category) } } else { + allNames := cache.AllCategoryNames(h.c) + if _, ok := allNames[category]; !ok { + return errors.New(str.Join("not exists category ", category)) + } h.categoryType = "category" h.header = fmt.Sprintf("分类: %s", category) } h.category = category username := h.c.Param("author") if username != "" { + allUsername, er := cache.GetAllUsername(h.c) + if er != nil { + err = er + return + } + if _, ok := allUsername[username]; !ok { + err = errors.New(str.Join("user ", username, " is not exists")) + return + } user, er := cache.GetUserByName(h.c, username) if er != nil { err = er @@ -181,7 +219,7 @@ func (h *indexHandle) getTotalPage(totalRaws int) int { func Index(c *gin.Context) { h := newIndexHandle(c) - var postIds []models.Posts + var posts []models.Posts var totalRaw int var err error archive := cache.Archives(c) @@ -189,6 +227,7 @@ func Index(c *gin.Context) { categoryItems := cache.Categories(c) recentComments := cache.RecentComments(c, 5) ginH := gin.H{ + "err": err, "options": wpconfig.Options, "recentPosts": recent, "archives": archive, @@ -198,14 +237,19 @@ func Index(c *gin.Context) { "recentComments": recentComments, } defer func() { - stat := http.StatusOK + code := http.StatusOK if err != nil { + code = http.StatusNotFound + if h.status == plugins.InternalErr { + code = http.StatusInternalServerError + c.Error(err) + return + } c.Error(err) - stat = http.StatusInternalServerError - return + h.status = plugins.Error } t := theme.GetTemplateName() - theme.Hook(t, stat, c, ginH, int(h.scene)) + theme.Hook(t, code, c, ginH, h.scene, h.status) }() err = h.parseParams() if err != nil { @@ -213,33 +257,34 @@ func Index(c *gin.Context) { } ginH["title"] = h.getTitle() if c.Param("month") != "" { - postIds, totalRaw, err = cache.GetMonthPostIds(c, c.Param("year"), c.Param("month"), h.page, h.pageSize, h.order) + posts, totalRaw, err = cache.GetMonthPostIds(c, c.Param("year"), c.Param("month"), h.page, h.pageSize, h.order) if err != nil { return } } else if h.search != "" { - postIds, totalRaw, err = cache.SearchPost(c, h.getSearchKey(), c, h.where, h.page, h.pageSize, model.SqlBuilder{{h.orderBy, h.order}}, h.join, h.postType, h.status) + posts, totalRaw, err = cache.SearchPost(c, h.getSearchKey(), c, h.where, h.page, h.pageSize, model.SqlBuilder{{h.orderBy, h.order}}, h.join, h.postType, h.postStatus) } else { - postIds, totalRaw, err = cache.PostLists(c, h.getSearchKey(), c, h.where, h.page, h.pageSize, model.SqlBuilder{{h.orderBy, h.order}}, h.join, h.postType, h.status) + posts, totalRaw, err = cache.PostLists(c, h.getSearchKey(), c, h.where, h.page, h.pageSize, model.SqlBuilder{{h.orderBy, h.order}}, h.join, h.postType, h.postStatus) } if err != nil { + h.status = plugins.Error logs.ErrPrintln(err, "获取数据错误") return } - if len(postIds) < 1 && h.category != "" { + if len(posts) < 1 && h.category != "" { h.titleL = "未找到页面" - h.scene = plugins.Empty404 + h.status = plugins.Empty404 } pw := h.session.Get("post_password") plug := plugins.NewPostPlugin(c, h.scene) - for i, post := range postIds { - plugins.PasswordProjectTitle(&postIds[i]) + for i, post := range posts { + plugins.PasswordProjectTitle(&posts[i]) if post.PostPassword != "" && pw != post.PostPassword { - plugins.PasswdProjectContent(&postIds[i]) + plugins.PasswdProjectContent(&posts[i]) } else { - plugins.ApplyPlugin(plug, &postIds[i]) + plugins.ApplyPlugin(plug, &posts[i]) } } for i, post := range recent { @@ -251,7 +296,7 @@ func Index(c *gin.Context) { if q != "" { q = fmt.Sprintf("?%s", q) } - ginH["posts"] = postIds + ginH["posts"] = posts ginH["totalPage"] = h.getTotalPage(totalRaw) ginH["currentPage"] = h.page ginH["title"] = h.getTitle() diff --git a/internal/pkg/cache/cache.go b/internal/pkg/cache/cache.go index 97005bd..aa7b469 100644 --- a/internal/pkg/cache/cache.go +++ b/internal/pkg/cache/cache.go @@ -3,6 +3,7 @@ package cache import ( "context" "github.com/fthvgb1/wp-go/cache" + "github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/internal/pkg/config" "github.com/fthvgb1/wp-go/internal/pkg/dao" "github.com/fthvgb1/wp-go/internal/pkg/logs" @@ -11,7 +12,7 @@ import ( "time" ) -var postContextCache *cache.MapCache[uint64, common.PostContext] +var postContextCache *cache.MapCache[uint64, dao.PostContext] var archivesCaches *Arch var categoryCaches *cache.VarCache[[]models.TermsMy] var recentPostsCaches *cache.VarCache[[]models.Posts] @@ -22,8 +23,8 @@ var postsCache *cache.MapCache[uint64, models.Posts] var postMetaCache *cache.MapCache[uint64, map[string]any] var monthPostsCache *cache.MapCache[string, []uint64] -var postListIdsCache *cache.MapCache[string, common.PostIds] -var searchPostIdsCache *cache.MapCache[string, common.PostIds] +var postListIdsCache *cache.MapCache[string, dao.PostIds] +var searchPostIdsCache *cache.MapCache[string, dao.PostIds] var maxPostIdCache *cache.VarCache[uint64] var usersCache *cache.MapCache[uint64, models.Users] @@ -38,40 +39,44 @@ var commentsFeedCache *cache.VarCache[[]string] var newCommentCache *cache.MapCache[string, string] +var allUsernameCache *cache.VarCache[map[string]struct{}] + +var allCategories *cache.VarCache[map[string]struct{}] + func InitActionsCommonCache() { c := config.Conf.Load() archivesCaches = &Arch{ mutex: &sync.Mutex{}, - setCacheFunc: common.Archives, + setCacheFunc: dao.Archives, } - searchPostIdsCache = cache.NewMapCacheByFn[string](common.SearchPostIds, c.SearchPostCacheTime) + searchPostIdsCache = cache.NewMapCacheByFn[string](dao.SearchPostIds, c.SearchPostCacheTime) - postListIdsCache = cache.NewMapCacheByFn[string](common.SearchPostIds, c.PostListCacheTime) + postListIdsCache = cache.NewMapCacheByFn[string](dao.SearchPostIds, c.PostListCacheTime) - monthPostsCache = cache.NewMapCacheByFn[string](common.MonthPost, c.MonthPostCacheTime) + monthPostsCache = cache.NewMapCacheByFn[string](dao.MonthPost, c.MonthPostCacheTime) - postContextCache = cache.NewMapCacheByFn[uint64](common.GetPostContext, c.ContextPostCacheTime) + postContextCache = cache.NewMapCacheByFn[uint64](dao.GetPostContext, c.ContextPostCacheTime) - postsCache = cache.NewMapCacheByBatchFn(common.GetPostsByIds, c.PostDataCacheTime) + postsCache = cache.NewMapCacheByBatchFn(dao.GetPostsByIds, c.PostDataCacheTime) - postMetaCache = cache.NewMapCacheByBatchFn(common.GetPostMetaByPostIds, c.PostDataCacheTime) + postMetaCache = cache.NewMapCacheByBatchFn(dao.GetPostMetaByPostIds, c.PostDataCacheTime) - categoryCaches = cache.NewVarCache(common.Categories, c.CategoryCacheTime) + categoryCaches = cache.NewVarCache(dao.Categories, c.CategoryCacheTime) - recentPostsCaches = cache.NewVarCache(common.RecentPosts, c.RecentPostCacheTime) + recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.RecentPostCacheTime) - recentCommentsCaches = cache.NewVarCache(common.RecentComments, c.RecentCommentsCacheTime) + recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.RecentCommentsCacheTime) - postCommentCaches = cache.NewMapCacheByFn[uint64](common.PostComments, c.PostCommentsCacheTime) + postCommentCaches = cache.NewMapCacheByFn[uint64](dao.PostComments, c.PostCommentsCacheTime) - maxPostIdCache = cache.NewVarCache(common.GetMaxPostId, c.MaxPostIdCacheTime) + maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.MaxPostIdCacheTime) - usersCache = cache.NewMapCacheByFn[uint64](common.GetUserById, c.UserInfoCacheTime) + usersCache = cache.NewMapCacheByFn[uint64](dao.GetUserById, c.UserInfoCacheTime) - usersNameCache = cache.NewMapCacheByFn[string](common.GetUserByName, c.UserInfoCacheTime) + usersNameCache = cache.NewMapCacheByFn[string](dao.GetUserByName, c.UserInfoCacheTime) - commentsCache = cache.NewMapCacheByBatchFn(common.GetCommentByIds, c.CommentsCacheTime) + commentsCache = cache.NewMapCacheByBatchFn(dao.GetCommentByIds, c.CommentsCacheTime) feedCache = cache.NewVarCache(feed, time.Hour) @@ -81,6 +86,15 @@ func InitActionsCommonCache() { newCommentCache = cache.NewMapCacheByFn[string, string](nil, 15*time.Minute) + allUsernameCache = cache.NewVarCache(dao.AllUsername, c.UserInfoCacheTime) + + allCategories = cache.NewVarCache(func(a ...any) (map[string]struct{}, error) { + ctx := a[0].(context.Context) + return slice.ToMap(Categories(ctx), func(v models.TermsMy) (string, struct{}) { + return v.Name, struct{}{} + }, true), nil + }, c.CategoryCacheTime) + InitFeed() } @@ -141,6 +155,11 @@ func (c *Arch) getArchiveCache(ctx context.Context) []models.PostArchive { func Categories(ctx context.Context) []models.TermsMy { r, err := categoryCaches.GetCache(ctx, time.Second, ctx) - logs.ErrPrintln(err, "get category ") + logs.ErrPrintln(err, "get category err") + return r +} + +func AllCategoryNames(ctx context.Context) map[string]struct{} { + r, _ := allCategories.GetCache(ctx, time.Second, ctx) return r } diff --git a/internal/pkg/cache/users.go b/internal/pkg/cache/users.go index 348578b..48ac880 100644 --- a/internal/pkg/cache/users.go +++ b/internal/pkg/cache/users.go @@ -19,6 +19,10 @@ func GetUserByName(ctx context.Context, username string) (models.Users, error) { return usersNameCache.GetCache(ctx, username, time.Second, ctx, username) } +func GetAllUsername(ctx context.Context) (map[string]struct{}, error) { + return allUsernameCache.GetCache(ctx, time.Second, ctx) +} + func GetUserById(ctx context.Context, uid uint64) models.Users { r, err := usersCache.GetCache(ctx, uid, time.Second, ctx, uid) logs.ErrPrintln(err, "get user", uid) diff --git a/internal/pkg/dao/comments.go b/internal/pkg/dao/comments.go index 6b65fa8..7c253c2 100644 --- a/internal/pkg/dao/comments.go +++ b/internal/pkg/dao/comments.go @@ -1,4 +1,4 @@ -package common +package dao import ( "context" diff --git a/internal/pkg/dao/common.go b/internal/pkg/dao/common.go index 23d1189..abb819e 100644 --- a/internal/pkg/dao/common.go +++ b/internal/pkg/dao/common.go @@ -1,9 +1,9 @@ -package common +package dao import ( "context" "fmt" - models2 "github.com/fthvgb1/wp-go/internal/pkg/models" + "github.com/fthvgb1/wp-go/internal/pkg/models" "github.com/fthvgb1/wp-go/internal/wpconfig" "github.com/fthvgb1/wp-go/model" ) @@ -16,20 +16,20 @@ type PostIds struct { } type PostContext struct { - Prev models2.Posts - Next models2.Posts + Prev models.Posts + Next models.Posts } -func PasswordProjectTitle(post *models2.Posts) { +func PasswordProjectTitle(post *models.Posts) { if post.PostPassword != "" { post.PostTitle = fmt.Sprintf("密码保护:%s", post.PostTitle) } } -func Categories(a ...any) (terms []models2.TermsMy, err error) { +func Categories(a ...any) (terms []models.TermsMy, err error) { ctx := a[0].(context.Context) var in = []any{"category"} - terms, err = model.Find[models2.TermsMy](ctx, model.SqlBuilder{ + terms, err = model.Find[models.TermsMy](ctx, model.SqlBuilder{ {"tt.count", ">", "0", "int"}, {"tt.taxonomy", "in", ""}, }, "t.term_id", "", model.SqlBuilder{ @@ -48,8 +48,8 @@ func Categories(a ...any) (terms []models2.TermsMy, err error) { return } -func Archives(ctx context.Context) ([]models2.PostArchive, error) { - return model.Find[models2.PostArchive](ctx, model.SqlBuilder{ +func Archives(ctx context.Context) ([]models.PostArchive, error) { + return model.Find[models.PostArchive](ctx, model.SqlBuilder{ {"post_type", "post"}, {"post_status", "publish"}, }, "YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts", "year,month", model.SqlBuilder{{"year", "desc"}, {"month", "desc"}}, nil, nil, 0) } diff --git a/internal/pkg/dao/postmeta.go b/internal/pkg/dao/postmeta.go index 1b0d9bf..f197d8a 100644 --- a/internal/pkg/dao/postmeta.go +++ b/internal/pkg/dao/postmeta.go @@ -1,4 +1,4 @@ -package common +package dao import ( "context" diff --git a/internal/pkg/dao/posts.go b/internal/pkg/dao/posts.go index 287cc85..e58e5f9 100644 --- a/internal/pkg/dao/posts.go +++ b/internal/pkg/dao/posts.go @@ -1,4 +1,4 @@ -package common +package dao import ( "context" diff --git a/internal/pkg/dao/users.go b/internal/pkg/dao/users.go index 6da26de..d4a933f 100644 --- a/internal/pkg/dao/users.go +++ b/internal/pkg/dao/users.go @@ -1,7 +1,8 @@ -package common +package dao import ( "context" + "github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/internal/pkg/models" "github.com/fthvgb1/wp-go/model" ) @@ -13,6 +14,19 @@ func GetUserById(a ...any) (r models.Users, err error) { return } +func AllUsername(a ...any) (map[string]struct{}, error) { + ctx := a[0].(context.Context) + r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{ + {"user_status", "=", "0", "int"}, + }, "user_login") + if err != nil { + return nil, err + } + return slice.ToMap(r, func(t models.Users) (string, struct{}) { + return t.UserLogin, struct{}{} + }, true), nil +} + func GetUserByName(a ...any) (r models.Users, err error) { u := a[1].(string) ctx := a[0].(context.Context) diff --git a/internal/plugins/digest.go b/internal/plugins/digest.go index 79162ec..ced314e 100644 --- a/internal/plugins/digest.go +++ b/internal/plugins/digest.go @@ -36,7 +36,7 @@ func digestRaw(arg ...any) (string, error) { return digest.Raw(str, limit, fmt.Sprintf("/p/%d", id)), nil } -func Digest(p *Plugin[models.Posts], c *gin.Context, post *models.Posts, scene uint) { +func Digest(p *Plugin[models.Posts], c *gin.Context, post *models.Posts, scene int) { if scene == Detail { return } diff --git a/internal/plugins/plugins.go b/internal/plugins/plugins.go index bda3fea..664533c 100644 --- a/internal/plugins/plugins.go +++ b/internal/plugins/plugins.go @@ -10,7 +10,11 @@ const ( Category Search Detail + + Ok Empty404 + Error + InternalErr ) var IndexSceneMap = map[int]struct{}{ @@ -20,22 +24,17 @@ var IndexSceneMap = map[int]struct{}{ Search: {}, } -var DetailSceneMap = map[int]struct{}{ - Detail: {}, - Empty404: {}, -} - -type Func[T any] func(*Plugin[T], *gin.Context, *T, uint) +type Func[T any] func(*Plugin[T], *gin.Context, *T, int) type Plugin[T any] struct { calls []Func[T] index int post *T - scene uint + scene int c *gin.Context } -func NewPlugin[T any](calls []Func[T], index int, post *T, scene uint, c *gin.Context) *Plugin[T] { +func NewPlugin[T any](calls []Func[T], index int, post *T, scene int, c *gin.Context) *Plugin[T] { return &Plugin[T]{calls: calls, index: index, post: post, scene: scene, c: c} } diff --git a/internal/plugins/posts.go b/internal/plugins/posts.go index e31a16e..fde5ea0 100644 --- a/internal/plugins/posts.go +++ b/internal/plugins/posts.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" ) -func NewPostPlugin(ctx *gin.Context, scene uint) *Plugin[models.Posts] { +func NewPostPlugin(ctx *gin.Context, scene int) *Plugin[models.Posts] { p := NewPlugin[models.Posts](nil, -1, nil, scene, ctx) p.Push(Digest) return p diff --git a/internal/theme/hook.go b/internal/theme/hook.go index bc67f32..ad31852 100644 --- a/internal/theme/hook.go +++ b/internal/theme/hook.go @@ -1,24 +1,26 @@ package theme import ( + "errors" + "github.com/fthvgb1/wp-go/internal/pkg/logs" "github.com/fthvgb1/wp-go/internal/plugins" "github.com/fthvgb1/wp-go/plugin/pagination" "github.com/gin-gonic/gin" ) -var themeMap = map[string]func(int, *gin.Context, gin.H, int){} +var themeMap = map[string]func(int, *gin.Context, gin.H, int, int){} -func AddThemeHookFunc(name string, fn func(int, *gin.Context, gin.H, int)) { +func AddThemeHookFunc(name string, fn func(int, *gin.Context, gin.H, int, int)) { if _, ok := themeMap[name]; ok { panic("exists same name theme") } themeMap[name] = fn } -func Hook(themeName string, status int, c *gin.Context, h gin.H, scene int) { +func Hook(themeName string, code int, c *gin.Context, h gin.H, scene, status int) { fn, ok := themeMap[themeName] if ok && fn != nil { - fn(status, c, h, scene) + fn(code, c, h, scene, status) return } if _, ok := plugins.IndexSceneMap[scene]; ok { @@ -29,11 +31,11 @@ func Hook(themeName string, status int, c *gin.Context, h gin.H, scene int) { h["pagination"] = pagination.Paginate(plugins.TwentyFifteenPagination(), pp) } } - c.HTML(status, "twentyfifteen/posts/index.gohtml", h) + c.HTML(code, "twentyfifteen/posts/index.gohtml", h) return - } else if _, ok := plugins.DetailSceneMap[scene]; ok { - c.HTML(status, "twentyfifteen/posts/detail.gohtml", h) + } else if scene == plugins.Detail { + c.HTML(code, "twentyfifteen/posts/detail.gohtml", h) return } - c.HTML(status, "twentyfifteen/posts/index.gohtml", h) + logs.ErrPrintln(errors.New("what happening"), " how reached here", themeName, code, h, scene, status) } diff --git a/internal/theme/twentyseventeen/twentyseventeen.go b/internal/theme/twentyseventeen/twentyseventeen.go index 804527b..291c1de 100644 --- a/internal/theme/twentyseventeen/twentyseventeen.go +++ b/internal/theme/twentyseventeen/twentyseventeen.go @@ -40,7 +40,7 @@ var paginate = func() plugins.PageEle { return p }() -func Hook(status int, c *gin.Context, h gin.H, scene int) { +func Hook(status int, c *gin.Context, h gin.H, scene, stats int) { templ := "twentyseventeen/posts/index.gohtml" if _, ok := plugins.IndexSceneMap[scene]; ok { h["HeaderImage"] = getHeaderImage(c) @@ -51,7 +51,7 @@ func Hook(status int, c *gin.Context, h gin.H, scene int) { h["pagination"] = pagination.Paginate(paginate, pp) } } - } else if _, ok := plugins.DetailSceneMap[scene]; ok { + } else if scene == plugins.Detail { templ = "twentyseventeen/posts/detail.gohtml" } c.HTML(status, templ, h)