优化代码 继承方式重构详情页 骚操作。。。。。

This commit is contained in:
xing 2023-02-11 23:51:07 +08:00
parent a2cebfbb3f
commit 23f32c8fed
12 changed files with 610 additions and 553 deletions

View File

@ -1,107 +0,0 @@
package actions
import (
"fmt"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/internal/pkg/cache"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
"github.com/fthvgb1/wp-go/internal/theme"
"github.com/fthvgb1/wp-go/internal/theme/common"
"github.com/fthvgb1/wp-go/internal/wpconfig"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"net/http"
)
func Detail(c *gin.Context) {
var err error
var post models.Posts
recent := slice.Map(cache.RecentPosts(c, 5), common.ProjectTitle)
archive := cache.Archives(c)
categoryItems := cache.CategoriesTags(c, constraints.Category)
recentComments := cache.RecentComments(c, 5)
var ginH = gin.H{
"title": wpconfig.Options.Value("blogname"),
"recentPosts": recent,
"archives": archive,
"categories": categoryItems,
"recentComments": recentComments,
"post": post,
}
isApproveComment := false
stats := constraints.Ok
pw := sessions.Default(c).Get("post_password")
defer func() {
code := http.StatusOK
if err != nil {
code = http.StatusNotFound
c.Error(err)
stats = constraints.ParamError
return
}
if isApproveComment == true {
return
}
t := theme.GetTemplateName()
theme.Hook(t, common.Handle{
C: c,
GinH: ginH,
Password: "",
Scene: constraints.Detail,
Code: code,
Stats: stats,
})
}()
id := str.ToInteger[uint64](c.Param("id"), 0)
maxId, err := cache.GetMaxPostId(c)
logs.ErrPrintln(err, "get max post id")
if id > maxId || id <= 0 || err != nil {
stats = constraints.Error404
return
}
post, err = cache.GetPostById(c, id)
if post.Id == 0 || err != nil || post.PostStatus != "publish" {
stats = constraints.Error404
return
}
showComment := false
if post.CommentCount > 0 || post.CommentStatus == "open" {
showComment = true
}
user := cache.GetUserById(c, post.PostAuthor)
if post.PostPassword != "" {
plugins.PasswordProjectTitle(&post)
if pw != post.PostPassword {
plugins.PasswdProjectContent(&post)
showComment = false
}
} else if s, ok := cache.NewCommentCache().Get(c, c.Request.URL.RawQuery); ok && s != "" && (post.PostPassword == "" || post.PostPassword != "" && pw == post.PostPassword) {
c.Writer.WriteHeader(http.StatusOK)
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
_, err = c.Writer.WriteString(s)
isApproveComment = true
return
}
comments, err := cache.PostComments(c, post.Id)
logs.ErrPrintln(err, "get post comment", post.Id)
ginH["comments"] = comments
prev, next, err := cache.GetContextPost(c, post.Id, post.PostDate)
logs.ErrPrintln(err, "get pre and next post", post.Id, post.PostDate)
ginH["title"] = fmt.Sprintf("%s-%s", post.PostTitle, wpconfig.Options.Value("blogname"))
ginH["post"] = post
ginH["showComment"] = showComment
ginH["prev"] = prev
d := str.ToInteger(wpconfig.Options.Value("thread_comments_depth"), 5)
ginH["maxDep"] = d
ginH["next"] = next
ginH["user"] = user
ginH["scene"] = constraints.Detail
}

View File

@ -1,21 +0,0 @@
package actions
import (
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/theme"
"github.com/fthvgb1/wp-go/internal/theme/common"
"github.com/gin-gonic/gin"
"net/http"
)
func Index(scene int) func(*gin.Context) {
return func(ctx *gin.Context) {
theme.Hook(theme.GetTemplateName(), common.Handle{
C: ctx,
GinH: gin.H{},
Scene: scene,
Code: http.StatusOK,
Stats: constraints.Ok,
})
}
}

View File

@ -0,0 +1,13 @@
package actions
import (
"github.com/fthvgb1/wp-go/internal/theme"
"github.com/fthvgb1/wp-go/internal/theme/common"
"github.com/gin-gonic/gin"
)
func ThemeHook(scene int) func(*gin.Context) {
return func(ctx *gin.Context) {
theme.Hook(theme.GetTemplateName(), common.NewHandle(ctx, scene))
}
}

View File

@ -62,18 +62,18 @@ func SetupRouter() (*gin.Engine, func()) {
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("go-wp", store))
sl, slRload := middleware.SearchLimit(c.SingleIpSearchNum)
r.GET("/", sl, actions.Index(constraints.Home))
r.GET("/page/:page", actions.Index(constraints.Home))
r.GET("/p/category/:category", actions.Index(constraints.Category))
r.GET("/p/category/:category/page/:page", actions.Index(constraints.Category))
r.GET("/p/tag/:tag", actions.Index(constraints.Tag))
r.GET("/p/tag/:tag/page/:page", actions.Index(constraints.Tag))
r.GET("/p/date/:year/:month", actions.Index(constraints.Archive))
r.GET("/p/date/:year/:month/page/:page", actions.Index(constraints.Archive))
r.GET("/p/author/:author", actions.Index(constraints.Author))
r.GET("/p/author/:author/page/:page", actions.Index(constraints.Author))
r.GET("/", sl, actions.ThemeHook(constraints.Home))
r.GET("/page/:page", actions.ThemeHook(constraints.Home))
r.GET("/p/category/:category", actions.ThemeHook(constraints.Category))
r.GET("/p/category/:category/page/:page", actions.ThemeHook(constraints.Category))
r.GET("/p/tag/:tag", actions.ThemeHook(constraints.Tag))
r.GET("/p/tag/:tag/page/:page", actions.ThemeHook(constraints.Tag))
r.GET("/p/date/:year/:month", actions.ThemeHook(constraints.Archive))
r.GET("/p/date/:year/:month/page/:page", actions.ThemeHook(constraints.Archive))
r.GET("/p/author/:author", actions.ThemeHook(constraints.Author))
r.GET("/p/author/:author/page/:page", actions.ThemeHook(constraints.Author))
r.POST("/login", actions.Login)
r.GET("/p/:id", actions.Detail)
r.GET("/p/:id", actions.ThemeHook(constraints.Detail))
r.GET("/p/:id/feed", actions.PostFeed)
r.GET("/feed", actions.Feed)
r.GET("/comments/feed", actions.CommentsFeed)

View File

@ -7,11 +7,13 @@ import (
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/internal/pkg/config"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"net/http"
)
type Handle struct {
@ -22,47 +24,35 @@ type Handle struct {
Scene int
Code int
Stats int
Templ string
}
func (h *Handle) Detail() {
func NewHandle(c *gin.Context, scene int) *Handle {
return &Handle{
C: c,
Session: sessions.Default(c),
GinH: gin.H{},
Scene: scene,
Code: http.StatusOK,
Stats: constraints.Ok,
}
}
func (h *Handle) Index() {
func (h *Handle) GetPassword() {
pw := h.Session.Get("post_password")
if pw != nil {
h.Password = pw.(string)
}
}
func (h *Handle) ExecListPagePlugin(m map[string]Plugin[models.Posts], calls ...func(*models.Posts)) {
func (i *IndexHandle) ExecListPagePlugin(m map[string]Plugin[models.Posts], calls ...func(*models.Posts)) {
pluginConf := config.GetConfig().ListPagePlugins
plugin := GetListPostPlugins(pluginConf, m)
posts, ok := maps.GetStrMapAnyVal[[]models.Posts](h.GinH, "posts")
i.GinH["posts"] = slice.Map(i.Posts, PluginFn[models.Posts](plugin, i.Handle, Defaults(calls...)))
if ok {
h.GinH["posts"] = slice.Map(posts, PluginFn[models.Posts](plugin, h, Defaults(calls...)))
}
}
/*func (h *Handle) Pagination(paginate pagination) {
}*/
type Fn[T any] func(T) T
type Plugin[T any] func(next Fn[T], h *Handle, t T) T
func PluginFn[T any](a []Plugin[T], h *Handle, fn Fn[T]) Fn[T] {
return slice.ReverseReduce(a, func(next Plugin[T], forward Fn[T]) Fn[T] {
return func(t T) T {
return next(forward, h, t)
}
}, fn)
}
var pluginFns = map[string]Plugin[models.Posts]{
"passwordProject": PasswordProject,
"digest": Digest,
}
func ListPostPlugins() map[string]Plugin[models.Posts] {
@ -82,6 +72,13 @@ func Default[T any](t T) T {
return t
}
func ProjectTitle(t models.Posts) models.Posts {
if t.PostPassword != "" {
plugins.PasswordProjectTitle(&t)
}
return t
}
func GetListPostPlugins(name []string, m map[string]Plugin[models.Posts]) []Plugin[models.Posts] {
return slice.FilterAndMap(name, func(t string) (Plugin[models.Posts], bool) {
v, ok := m[t]
@ -93,37 +90,6 @@ func GetListPostPlugins(name []string, m map[string]Plugin[models.Posts]) []Plug
})
}
// PasswordProject 标题和内容密码保护
func PasswordProject(next Fn[models.Posts], h *Handle, post models.Posts) (r models.Posts) {
r = post
if post.PostPassword != "" {
plugins.PasswordProjectTitle(&r)
if h.Password != post.PostPassword {
plugins.PasswdProjectContent(&r)
return
}
}
r = next(r)
return
}
func ProjectTitle(t models.Posts) models.Posts {
if t.PostPassword != "" {
plugins.PasswordProjectTitle(&t)
}
return t
}
// Digest 生成摘要
func Digest(next Fn[models.Posts], h *Handle, post models.Posts) models.Posts {
if post.PostExcerpt != "" {
plugins.PostExcerpt(&post)
} else {
plugins.Digest(h.C, &post, config.GetConfig().DigestWordCount)
}
return next(post)
}
func DigestsAndOthers(ctx context.Context, calls ...func(*models.Posts)) Fn[models.Posts] {
return func(post models.Posts) models.Posts {
if post.PostExcerpt != "" {

View File

@ -0,0 +1,95 @@
package common
import (
"fmt"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/internal/pkg/cache"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
"github.com/fthvgb1/wp-go/internal/wpconfig"
)
type DetailHandle struct {
*Handle
CommentRender plugins.CommentHtml
Comments []models.Comments
Post models.Posts
}
func NewDetailHandle(handle *Handle) *DetailHandle {
return &DetailHandle{Handle: handle}
}
func (d *DetailHandle) BuildDetailData() (err error) {
d.GinH["title"] = wpconfig.Options.Value("blogname")
err = d.CheckAndGetPost()
if err != nil {
d.Scene = constraints.Error404
return
}
d.WidgetAreaData()
d.GetPassword()
d.Comment()
d.ContextPost()
return
}
func (d *DetailHandle) CheckAndGetPost() (err error) {
id := str.ToInteger[uint64](d.C.Param("id"), 0)
maxId, err := cache.GetMaxPostId(d.C)
logs.ErrPrintln(err, "get max post id")
if id > maxId || id <= 0 || err != nil {
d.Stats = constraints.Error404
return
}
post, err := cache.GetPostById(d.C, id)
if post.Id == 0 || err != nil || post.PostStatus != "publish" {
d.Stats = constraints.Error404
return
}
d.GinH["post"] = post
d.Post = post
d.GinH["user"] = cache.GetUserById(d.C, post.PostAuthor)
d.GinH["title"] = fmt.Sprintf("%s-%s", post.PostTitle, wpconfig.Options.Value("blogname"))
return
}
func (d *DetailHandle) PasswordProject() {
if d.Post.PostPassword != "" {
plugins.PasswordProjectTitle(&d.Post)
if d.Password != d.Post.PostPassword {
plugins.PasswdProjectContent(&d.Post)
}
d.GinH["post"] = d.Post
}
}
func (d *DetailHandle) Comment() {
comments, err := cache.PostComments(d.C, d.Post.Id)
logs.ErrPrintln(err, "get d.Post comment", d.Post.Id)
d.GinH["comments"] = comments
d.Comments = comments
}
func (d *DetailHandle) RenderComment() {
ableComment := true
if d.Post.CommentStatus != "open" ||
(d.Post.PostPassword != "" && d.Password != d.Post.PostPassword) {
ableComment = false
}
d.GinH["showComment"] = ableComment
if len(d.Comments) > 0 && ableComment {
dep := str.ToInteger(wpconfig.Options.Value("thread_comments_depth"), 5)
d.GinH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep)
}
}
func (d *DetailHandle) ContextPost() {
prev, next, err := cache.GetContextPost(d.C, d.Post.Id, d.Post.PostDate)
logs.ErrPrintln(err, "get pre and next post", d.Post.Id, d.Post.PostDate)
d.GinH["next"] = next
d.GinH["prev"] = prev
}

View File

@ -2,307 +2,109 @@ package common
import (
"database/sql"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/helper/maps"
"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"
"github.com/fthvgb1/wp-go/internal/pkg/config"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/dao"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/wpconfig"
"github.com/fthvgb1/wp-go/model"
"github.com/fthvgb1/wp-go/plugin/pagination"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
"strings"
"sync/atomic"
"time"
)
type IndexParams struct {
c *gin.Context
Page int
PageSize int
Title string
titleL string
titleR string
search string
author string
totalPage int
category string
categoryType string
Where model.SqlBuilder
OrderBy string
Order string
Month string
Year string
Join model.SqlBuilder
PostType []any
PostStatus []any
header string
PaginationStep int
scene int
stats int
CacheKey string
blogName string
}
var months = slice.SimpleToMap(number.Range(1, 12, 1), func(v int) int {
return v
})
var orders = map[string]struct{}{"asc": {}, "desc": {}}
func (i *IndexParams) setTitleLR(l, r string) {
i.titleL = l
i.titleR = r
}
func (i *IndexParams) getTitle() string {
i.Title = fmt.Sprintf("%s-%s", i.titleL, i.titleR)
return i.Title
}
func (i *IndexParams) getSearchKey() string {
return fmt.Sprintf("action:%s|%s|%s|%s|%s|%s|%d|%d", i.author, i.search, i.OrderBy, i.Order, i.category, i.categoryType, i.Page, i.PageSize)
}
func newIndexHandle(ctx *gin.Context) *IndexParams {
blogName := wpconfig.Options.Value("blogname")
size := str.ToInteger(wpconfig.Options.Value("posts_per_page"), 10)
return &IndexParams{
c: ctx,
Page: 1,
PageSize: size,
PaginationStep: number.Max(1, config.GetConfig().PaginationStep),
titleL: blogName,
titleR: wpconfig.Options.Value("blogdescription"),
Where: model.SqlBuilder{
{"post_type", "in", ""},
{"post_status", "in", ""},
},
OrderBy: "post_date",
Join: model.SqlBuilder{},
PostType: []any{"post"},
PostStatus: []any{"publish"},
scene: constraints.Home,
stats: constraints.Ok,
blogName: wpconfig.Options.Value("blogname"),
}
}
func (i *IndexParams) ParseSearch() {
s := i.c.Query("s")
if s != "" && strings.Replace(s, " ", "", -1) != "" {
q := str.Join("%", s, "%")
i.Where = append(i.Where, []string{
"and", "post_title", "like", q, "",
"or", "post_content", "like", q, "",
"or", "post_excerpt", "like", q, "",
}, []string{"post_password", ""})
i.PostType = append(i.PostType, "Page", "attachment")
i.header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.blogName)
i.search = s
i.scene = constraints.Search
}
}
func (i *IndexParams) ParseArchive() error {
year := i.c.Param("year")
if year != "" {
y := str.ToInteger(year, -1)
if y > time.Now().Year() || y <= 1970 {
return errors.New(str.Join("year err : ", year))
}
i.Where = append(i.Where, []string{
"year(post_date)", year,
})
i.Year = year
}
month := i.c.Param("month")
if month != "" {
m := str.ToInteger(month, -1)
if !maps.IsExists(months, m) {
return errors.New(str.Join("months err ", month))
}
i.Where = append(i.Where, []string{
"month(post_date)", month,
})
ss := fmt.Sprintf("%s年%s月", year, strings.TrimLeft(month, "0"))
i.header = fmt.Sprintf("月度归档: <span>%s</span>", ss)
i.setTitleLR(ss, i.blogName)
i.scene = constraints.Archive
i.Month = month
}
return nil
}
func (i *IndexParams) ParseCategory() error {
category := i.c.Param("category")
if category != "" {
i.scene = constraints.Category
if !maps.IsExists(cache.AllCategoryTagsNames(i.c, constraints.Category), category) {
return errors.New(str.Join("not exists category ", category))
}
i.categoryType = "category"
i.header = fmt.Sprintf("分类: <span>%s</span>", category)
i.category = category
i.CategoryCondition()
}
return nil
}
func (i *IndexParams) ParseTag() error {
tag := i.c.Param("tag")
if tag != "" {
i.scene = constraints.Tag
if !maps.IsExists(cache.AllCategoryTagsNames(i.c, constraints.Tag), tag) {
return errors.New(str.Join("not exists tag ", tag))
}
i.categoryType = "post_tag"
i.header = fmt.Sprintf("标签: <span>%s</span>", tag)
i.category = tag
i.CategoryCondition()
}
return nil
}
func (i *IndexParams) CategoryCondition() {
if i.category != "" {
i.Where = append(i.Where, []string{
"d.name", i.category,
}, []string{"taxonomy", i.categoryType})
i.Join = append(i.Join, []string{
"a", "left Join", "wp_term_relationships b", "a.Id=b.object_id",
}, []string{
"left Join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id",
}, []string{
"left Join", "wp_terms d", "c.term_id=d.term_id",
})
i.setTitleLR(i.category, i.blogName)
}
}
func (i *IndexParams) ParseAuthor() (err error) {
username := i.c.Param("author")
if username != "" {
allUsername, er := cache.GetAllUsername(i.c)
if err != nil {
err = er
return
}
if !maps.IsExists(allUsername, username) {
err = errors.New(str.Join("user ", username, " is not exists"))
return
}
user, er := cache.GetUserByName(i.c, username)
if er != nil {
return
}
i.author = username
i.Where = append(i.Where, []string{
"post_author", "=", strconv.FormatUint(user.Id, 10), "int",
})
}
return
}
func (i *IndexParams) parseParams() {
i.Order = i.c.Query("Order")
if !maps.IsExists(orders, i.Order) {
order := config.GetConfig().PostOrder
i.Order = "asc"
if order != "" && maps.IsExists(orders, order) {
i.Order = order
}
}
i.Page = str.ToInteger(i.c.Param("page"), 1)
total := int(atomic.LoadInt64(&dao.TotalRaw))
if total > 0 && total < (i.Page-1)*i.PageSize {
i.Page = 1
}
if i.Page > 1 && (i.category != "" || i.search != "" || i.Month != "") {
i.setTitleLR(fmt.Sprintf("%s-第%d页", i.titleL, i.Page), i.blogName)
}
return
}
func (h *Handle) ParseIndex() (i *IndexParams, err error) {
i = newIndexHandle(h.C)
switch h.Scene {
func (i *IndexHandle) ParseIndex(parm *IndexParams) (err error) {
i.Param = parm
switch i.Scene {
case constraints.Home, constraints.Search:
i.ParseSearch()
i.Param.ParseSearch()
case constraints.Category:
err = i.ParseCategory()
err = i.Param.ParseCategory()
case constraints.Tag:
err = i.ParseTag()
err = i.Param.ParseTag()
case constraints.Archive:
err = i.ParseArchive()
err = i.Param.ParseArchive()
case constraints.Author:
err = i.ParseAuthor()
err = i.Param.ParseAuthor()
}
if err != nil {
h.Stats = constraints.ParamError
i.Stats = constraints.ParamError
return
}
i.CacheKey = i.getSearchKey()
i.parseParams()
h.GinH["title"] = i.getTitle()
h.GinH["search"] = i.search
h.GinH["header"] = i.header
i.Param.CacheKey = i.Param.getSearchKey()
i.Param.ParseParams()
i.GinH["title"] = i.Param.getTitle()
i.GinH["search"] = i.Param.Search
i.GinH["header"] = i.Param.Header
return
}
func (h *Handle) GetIndexData(i *IndexParams) (posts []models.Posts, totalRaw int, err error) {
func (i *IndexHandle) GetIndexData() (posts []models.Posts, totalRaw int, err error) {
switch h.Scene {
switch i.Scene {
case constraints.Home, constraints.Category, constraints.Tag, constraints.Author:
posts, totalRaw, err = cache.PostLists(h.C, i.CacheKey, h.C, i.Where, i.Page, i.PageSize,
model.SqlBuilder{{i.OrderBy, i.Order}}, i.Join, i.PostType, i.PostStatus)
posts, totalRaw, err = cache.PostLists(i.C, i.Param.CacheKey, i.C, i.Param.Where, i.Param.Page, i.Param.PageSize,
model.SqlBuilder{{i.Param.OrderBy, i.Param.Order}}, i.Param.Join, i.Param.PostType, i.Param.PostStatus)
case constraints.Search:
posts, totalRaw, err = cache.SearchPost(h.C, i.CacheKey, h.C, i.Where, i.Page, i.PageSize,
model.SqlBuilder{{i.OrderBy, i.Order}}, i.Join, i.PostType, i.PostStatus)
posts, totalRaw, err = cache.SearchPost(i.C, i.Param.CacheKey, i.C, i.Param.Where, i.Param.Page, i.Param.PageSize,
model.SqlBuilder{{i.Param.OrderBy, i.Param.Order}}, i.Param.Join, i.Param.PostType, i.Param.PostStatus)
case constraints.Archive:
posts, totalRaw, err = cache.GetMonthPostIds(h.C, i.Year, i.Month, i.Page, i.PageSize, i.Order)
posts, totalRaw, err = cache.GetMonthPostIds(i.C, i.Param.Year, i.Param.Month, i.Param.Page, i.Param.PageSize, i.Param.Order)
}
return
}
func (h *Handle) Indexs() (err error) {
i, err := h.ParseIndex()
if err != nil {
h.Stats = constraints.ParamError
h.Code = http.StatusNotFound
return
}
posts, totalRows, err := h.GetIndexData(i)
if err != nil && err != sql.ErrNoRows {
h.Scene = constraints.InternalErr
h.Code = http.StatusInternalServerError
return
}
pw := h.Session.Get("post_password")
if pw != nil {
h.Password = pw.(string)
}
h.GinH["posts"] = posts
h.GinH["totalPage"] = number.CalTotalPage(totalRows, i.PageSize)
q := h.C.Request.URL.Query().Encode()
func (i *IndexHandle) Pagination() {
q := i.C.Request.URL.Query().Encode()
if q != "" {
q = fmt.Sprintf("?%s", q)
}
h.GinH["pagination"] = pagination.NewParsePagination(totalRows, i.PageSize, i.Page, i.PaginationStep, q, h.C.Request.URL.Path)
paginations := pagination.NewParsePagination(i.TotalRows, i.Param.PageSize, i.Param.Page, i.Param.PaginationStep, q, i.C.Request.URL.Path)
i.GinH["pagination"] = pagination.Paginate(i.PageEle, paginations)
}
func (i *IndexHandle) BuildIndexData(parm *IndexParams) (err error) {
err = i.ParseIndex(parm)
if err != nil {
i.Stats = constraints.ParamError
i.Code = http.StatusNotFound
return
}
posts, totalRows, err := i.GetIndexData()
if err != nil && err != sql.ErrNoRows {
i.Scene = constraints.InternalErr
i.Code = http.StatusInternalServerError
return
}
i.GinH["posts"] = posts
i.Posts = posts
i.TotalRows = totalRows
i.GinH["totalPage"] = number.CalTotalPage(totalRows, i.Param.PageSize)
return
}
func (i *IndexHandle) ExecPostsPlugin(calls ...func(*models.Posts)) {
pluginConf := config.GetConfig().ListPagePlugins
postsPlugins := i.PostsPlugins
if postsPlugins == nil {
postsPlugins = pluginFns
}
plugin := GetListPostPlugins(pluginConf, postsPlugins)
i.GinH["posts"] = slice.Map(i.Posts, PluginFn[models.Posts](plugin, i.Handle, Defaults(calls...)))
}

View File

@ -0,0 +1,251 @@
package common
import (
"errors"
"fmt"
"github.com/fthvgb1/wp-go/helper/maps"
"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"
"github.com/fthvgb1/wp-go/internal/pkg/config"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/dao"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/wpconfig"
"github.com/fthvgb1/wp-go/model"
"github.com/fthvgb1/wp-go/plugin/pagination"
"github.com/gin-gonic/gin"
"strconv"
"strings"
"sync/atomic"
"time"
)
type IndexParams struct {
ParseSearch func()
ParseArchive func() error
ParseCategory func() error
ParseTag func() error
ParseAuthor func() error
CategoryCondition func()
ParseParams func()
Ctx *gin.Context
Page int
PageSize int
Title string
TitleL string
TitleR string
Search string
Author string
TotalPage int
Category string
CategoryType string
Where model.SqlBuilder
OrderBy string
Order string
Month string
Year string
Join model.SqlBuilder
PostType []any
PostStatus []any
Header string
PaginationStep int
CacheKey string
BlogName string
}
type IndexHandle struct {
*Handle
Param *IndexParams
Posts []models.Posts
PageEle pagination.Elements
TotalRows int
PostsPlugins map[string]Plugin[models.Posts]
}
func NewIndexHandle(handle *Handle) *IndexHandle {
return &IndexHandle{Handle: handle}
}
var months = slice.SimpleToMap(number.Range(1, 12, 1), func(v int) int {
return v
})
var orders = map[string]struct{}{"asc": {}, "desc": {}}
func (i *IndexParams) setTitleLR(l, r string) {
i.TitleL = l
i.TitleR = r
}
func (i *IndexParams) getTitle() string {
i.Title = fmt.Sprintf("%s-%s", i.TitleL, i.TitleR)
return i.Title
}
func (i *IndexParams) getSearchKey() string {
return fmt.Sprintf("action:%s|%s|%s|%s|%s|%s|%d|%d", i.Author, i.Search, i.OrderBy, i.Order, i.Category, i.CategoryType, i.Page, i.PageSize)
}
func NewIndexParams(ctx *gin.Context) *IndexParams {
blogName := wpconfig.Options.Value("blogname")
size := str.ToInteger(wpconfig.Options.Value("posts_per_page"), 10)
i := &IndexParams{
Ctx: ctx,
Page: 1,
PageSize: size,
PaginationStep: number.Max(1, config.GetConfig().PaginationStep),
TitleL: blogName,
TitleR: wpconfig.Options.Value("blogdescription"),
Where: model.SqlBuilder{
{"post_type", "in", ""},
{"post_status", "in", ""},
},
OrderBy: "post_date",
Join: model.SqlBuilder{},
PostType: []any{"post"},
PostStatus: []any{"publish"},
BlogName: wpconfig.Options.Value("blogname"),
}
i.ParseSearch = i.parseSearch
i.ParseArchive = i.parseArchive
i.ParseCategory = i.parseCategory
i.ParseTag = i.parseTag
i.CategoryCondition = i.categoryCondition
i.ParseAuthor = i.parseAuthor
i.ParseParams = i.parseParams
return i
}
func (i *IndexParams) parseSearch() {
s := i.Ctx.Query("s")
if s != "" && strings.Replace(s, " ", "", -1) != "" {
q := str.Join("%", s, "%")
i.Where = append(i.Where, []string{
"and", "post_title", "like", q, "",
"or", "post_content", "like", q, "",
"or", "post_excerpt", "like", q, "",
}, []string{"post_password", ""})
i.PostType = append(i.PostType, "Page", "attachment")
i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
i.Search = s
}
}
func (i *IndexParams) parseArchive() error {
year := i.Ctx.Param("year")
if year != "" {
y := str.ToInteger(year, -1)
if y > time.Now().Year() || y <= 1970 {
return errors.New(str.Join("year err : ", year))
}
i.Where = append(i.Where, []string{
"year(post_date)", year,
})
i.Year = year
}
month := i.Ctx.Param("month")
if month != "" {
m := str.ToInteger(month, -1)
if !maps.IsExists(months, m) {
return errors.New(str.Join("months err ", month))
}
i.Where = append(i.Where, []string{
"month(post_date)", month,
})
ss := fmt.Sprintf("%s年%s月", year, strings.TrimLeft(month, "0"))
i.Header = fmt.Sprintf("月度归档: <span>%s</span>", ss)
i.setTitleLR(ss, i.BlogName)
i.Month = month
}
return nil
}
func (i *IndexParams) parseCategory() error {
category := i.Ctx.Param("category")
if category != "" {
if !maps.IsExists(cache.AllCategoryTagsNames(i.Ctx, constraints.Category), category) {
return errors.New(str.Join("not exists category ", category))
}
i.CategoryType = "category"
i.Header = fmt.Sprintf("分类: <span>%s</span>", category)
i.Category = category
i.CategoryCondition()
}
return nil
}
func (i *IndexParams) parseTag() error {
tag := i.Ctx.Param("tag")
if tag != "" {
if !maps.IsExists(cache.AllCategoryTagsNames(i.Ctx, constraints.Tag), tag) {
return errors.New(str.Join("not exists tag ", tag))
}
i.CategoryType = "post_tag"
i.Header = fmt.Sprintf("标签: <span>%s</span>", tag)
i.Category = tag
i.CategoryCondition()
}
return nil
}
func (i *IndexParams) categoryCondition() {
if i.Category != "" {
i.Where = append(i.Where, []string{
"d.name", i.Category,
}, []string{"taxonomy", i.CategoryType})
i.Join = append(i.Join, []string{
"a", "left Join", "wp_term_relationships b", "a.Id=b.object_id",
}, []string{
"left Join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id",
}, []string{
"left Join", "wp_terms d", "c.term_id=d.term_id",
})
i.setTitleLR(i.Category, i.BlogName)
}
}
func (i *IndexParams) parseAuthor() (err error) {
username := i.Ctx.Param("author")
if username != "" {
allUsername, er := cache.GetAllUsername(i.Ctx)
if err != nil {
err = er
return
}
if !maps.IsExists(allUsername, username) {
err = errors.New(str.Join("user ", username, " is not exists"))
return
}
user, er := cache.GetUserByName(i.Ctx, username)
if er != nil {
return
}
i.Author = username
i.Where = append(i.Where, []string{
"post_author", "=", strconv.FormatUint(user.Id, 10), "int",
})
}
return
}
func (i *IndexParams) parseParams() {
i.Order = i.Ctx.Query("Order")
if !maps.IsExists(orders, i.Order) {
order := config.GetConfig().PostOrder
i.Order = "asc"
if order != "" && maps.IsExists(orders, order) {
i.Order = order
}
}
i.Page = str.ToInteger(i.Ctx.Param("page"), 1)
total := int(atomic.LoadInt64(&dao.TotalRaw))
if total > 0 && total < (i.Page-1)*i.PageSize {
i.Page = 1
}
if i.Page > 1 && (i.Category != "" || i.Search != "" || i.Month != "") {
i.setTitleLR(fmt.Sprintf("%s-第%d页", i.TitleL, i.Page), i.BlogName)
}
return
}

View File

@ -0,0 +1,48 @@
package common
import (
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/internal/pkg/config"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
)
type Fn[T any] func(T) T
type Plugin[T any] func(initialFn Fn[T], h *Handle, t T) T
func PluginFn[T any](a []Plugin[T], h *Handle, fn Fn[T]) Fn[T] {
return slice.ReverseReduce(a, func(next Plugin[T], fn Fn[T]) Fn[T] {
return func(t T) T {
return next(fn, h, t)
}
}, fn)
}
var pluginFns = map[string]Plugin[models.Posts]{
"passwordProject": PasswordProject,
"digest": Digest,
}
// PasswordProject 标题和内容密码保护
func PasswordProject(next Fn[models.Posts], h *Handle, post models.Posts) (r models.Posts) {
r = post
if post.PostPassword != "" {
plugins.PasswordProjectTitle(&r)
if h.Password != post.PostPassword {
plugins.PasswdProjectContent(&r)
return
}
}
r = next(r)
return
}
// Digest 生成摘要
func Digest(next Fn[models.Posts], h *Handle, post models.Posts) models.Posts {
if post.PostExcerpt != "" {
plugins.PostExcerpt(&post)
} else {
plugins.Digest(h.C, &post, config.GetConfig().DigestWordCount)
}
return next(post)
}

View File

@ -5,16 +5,16 @@ import (
"github.com/fthvgb1/wp-go/internal/theme/twentyfifteen"
)
var themeMap = map[string]func(handle common.Handle){}
var themeMap = map[string]func(handle *common.Handle){}
func addThemeHookFunc(name string, fn func(handle common.Handle)) {
func addThemeHookFunc(name string, fn func(handle *common.Handle)) {
if _, ok := themeMap[name]; ok {
panic("exists same name theme")
}
themeMap[name] = fn
}
func Hook(themeName string, handle common.Handle) {
func Hook(themeName string, handle *common.Handle) {
fn, ok := themeMap[themeName]
if ok && fn != nil {
fn(handle)

View File

@ -1,67 +1,64 @@
package twentyfifteen
import (
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
"github.com/fthvgb1/wp-go/internal/theme/common"
"github.com/fthvgb1/wp-go/plugin/pagination"
"github.com/gin-contrib/sessions"
)
const ThemeName = "twentyfifteen"
type handle struct {
common.Handle
templ string
type indexHandle struct {
*common.IndexHandle
}
func Hook(cHandle common.Handle) {
h := handle{
Handle: cHandle,
templ: "twentyfifteen/posts/index.gohtml",
func newIndexHandle(iHandle *common.IndexHandle) *indexHandle {
return &indexHandle{iHandle}
}
type detailHandle struct {
*common.DetailHandle
}
func newDetailHandle(dHandle *common.DetailHandle) *detailHandle {
return &detailHandle{DetailHandle: dHandle}
}
func Hook(h *common.Handle) {
h.WidgetAreaData()
h.Session = sessions.Default(h.C)
if h.Stats == constraints.Error404 {
h.C.HTML(h.Code, h.templ, h.GinH)
return
}
h.GetPassword()
if h.Scene == constraints.Detail {
h.Detail()
newDetailHandle(common.NewDetailHandle(h)).Detail()
return
}
h.Index()
newIndexHandle(common.NewIndexHandle(h)).Index()
}
var plugin = common.ListPostPlugins()
func (i *indexHandle) Index() {
i.Templ = "twentyfifteen/posts/index.gohtml"
func (h handle) Index() {
err := h.Indexs()
err := i.BuildIndexData(common.NewIndexParams(i.C))
if err != nil {
h.C.HTML(h.Code, h.templ, h.GinH)
i.C.HTML(i.Code, i.Templ, i.GinH)
return
}
h.ExecListPagePlugin(plugin)
page, ok := maps.GetStrMapAnyVal[pagination.ParsePagination](h.GinH, "pagination")
if ok {
h.GinH["pagination"] = pagination.Paginate(plugins.TwentyFifteenPagination(), page)
}
h.C.HTML(h.Code, h.templ, h.GinH)
i.ExecPostsPlugin()
i.PageEle = plugins.TwentyFifteenPagination()
i.Pagination()
i.C.HTML(i.Code, i.Templ, i.GinH)
}
func (h handle) Detail() {
//h.GinH["bodyClass"] = h.bodyClass()
//host, _ := wpconfig.Options.Load("siteurl")
if h.GinH["comments"] != nil {
comments := h.GinH["comments"].([]models.Comments)
dep := h.GinH["maxDep"].(int)
h.GinH["comments"] = plugins.FormatComments(h.C, plugins.CommentRender(), comments, dep)
}
func (d *detailHandle) Detail() {
d.Templ = "twentyfifteen/posts/detail.gohtml"
h.templ = "twentyfifteen/posts/detail.gohtml"
h.C.HTML(h.Code, h.templ, h.GinH)
err := d.BuildDetailData()
if err != nil {
d.Stats = constraints.Error404
d.C.HTML(d.Code, d.Templ, d.GinH)
return
}
d.PasswordProject()
d.CommentRender = plugins.CommentRender()
d.RenderComment()
d.C.HTML(d.Code, d.Templ, d.GinH)
}

View File

@ -11,9 +11,8 @@ import (
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/plugins"
"github.com/fthvgb1/wp-go/internal/theme/common"
"github.com/fthvgb1/wp-go/plugin/pagination"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
@ -30,23 +29,40 @@ var paginate = func() plugins.PageEle {
}()
type handle struct {
common.Handle
templ string
*common.Handle
}
func Hook(cHandle common.Handle) {
h := handle{
Handle: cHandle,
templ: "twentyseventeen/posts/index.gohtml",
func newHandle(h *common.Handle) *handle {
return &handle{Handle: h}
}
type indexHandle struct {
*common.IndexHandle
h *handle
}
func newIndexHandle(iHandle *common.IndexHandle) *indexHandle {
return &indexHandle{IndexHandle: iHandle, h: newHandle(iHandle.Handle)}
}
type detailHandle struct {
*common.DetailHandle
h *handle
}
func newDetailHandle(dHandle *common.DetailHandle) *detailHandle {
return &detailHandle{DetailHandle: dHandle, h: newHandle(dHandle.Handle)}
}
func Hook(h *common.Handle) {
h.WidgetAreaData()
h.Session = sessions.Default(h.C)
h.GinH["HeaderImage"] = h.getHeaderImage(h.C)
h.GetPassword()
h.GinH["HeaderImage"] = getHeaderImage(h.C)
if h.Scene == constraints.Detail {
h.Detail()
newDetailHandle(common.NewDetailHandle(h)).Detail()
return
}
h.Index()
newIndexHandle(common.NewIndexHandle(h)).Index()
}
var pluginFns = func() map[string]common.Plugin[models.Posts] {
@ -55,44 +71,41 @@ var pluginFns = func() map[string]common.Plugin[models.Posts] {
})
}()
func (h *handle) Index() {
err := h.Indexs()
h.GinH["bodyClass"] = h.bodyClass()
func (i *indexHandle) Index() {
i.Templ = "twentyseventeen/posts/index.gohtml"
p := common.NewIndexParams(i.C)
err := i.BuildIndexData(p)
i.GinH["bodyClass"] = i.h.bodyClass()
if err != nil {
h.C.HTML(h.Code, h.templ, h.GinH)
i.C.HTML(i.Code, i.Templ, i.GinH)
return
}
h.ExecListPagePlugin(pluginFns)
page, ok := maps.GetStrMapAnyVal[pagination.ParsePagination](h.GinH, "pagination")
if ok {
h.GinH["pagination"] = pagination.Paginate(paginate, page)
}
h.C.HTML(h.Code, h.templ, h.GinH)
i.PageEle = paginate
i.ExecListPagePlugin(pluginFns)
i.Pagination()
i.C.HTML(i.Code, i.Templ, i.GinH)
}
func (h *handle) Detail() {
post := h.GinH["post"].(models.Posts)
h.GinH["bodyClass"] = h.bodyClass()
if h.Stats == constraints.Error404 {
h.C.HTML(h.Code, h.templ, h.GinH)
func (d *detailHandle) Detail() {
err := d.BuildDetailData()
d.GinH["bodyClass"] = d.h.bodyClass()
if err != nil {
d.Code = http.StatusNotFound
d.C.HTML(d.Code, d.Templ, d.GinH)
return
}
//host, _ := wpconfig.Options.Load("siteurl")
host := ""
img := plugins.Thumbnail(post.Thumbnail.OriginAttachmentData, "thumbnail", host, "thumbnail", "post-thumbnail")
img := plugins.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "thumbnail", "", "thumbnail", "post-thumbnail")
img.Width = img.OriginAttachmentData.Width
img.Height = img.OriginAttachmentData.Height
img.Sizes = "100vw"
img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset)
post.Thumbnail = img
h.GinH["post"] = post
if h.GinH["comments"] != nil {
comments := h.GinH["comments"].([]models.Comments)
dep := h.GinH["maxDep"].(int)
h.GinH["comments"] = plugins.FormatComments(h.C, commentFormat, comments, dep)
}
h.templ = "twentyseventeen/posts/detail.gohtml"
h.C.HTML(h.Code, h.templ, h.GinH)
d.Post.Thumbnail = img
d.GinH["post"] = d.Post
d.CommentRender = commentFormat
d.RenderComment()
d.PasswordProject()
d.Templ = "twentyseventeen/posts/detail.gohtml"
d.C.HTML(d.Code, d.Templ, d.GinH)
}
var commentFormat = comment{}
@ -125,7 +138,7 @@ func postThumbnail(next common.Fn[models.Posts], h *common.Handle, t models.Post
return next(t)
}
func (h *handle) getHeaderImage(c *gin.Context) (r models.PostThumbnail) {
func getHeaderImage(c *gin.Context) (r models.PostThumbnail) {
r.Path = "/wp-content/themes/twentyseventeen/assets/images/header.jpg"
r.Width = 2000
r.Height = 1200
@ -140,23 +153,23 @@ func (h *handle) getHeaderImage(c *gin.Context) (r models.PostThumbnail) {
return
}
func (h *handle) bodyClass() string {
func (i *handle) bodyClass() string {
s := ""
if constraints.Ok != h.Stats {
if constraints.Ok != i.Stats {
return "error404"
}
switch h.Scene {
switch i.Scene {
case constraints.Search:
s = "search-no-results"
if len(h.GinH["posts"].([]models.Posts)) > 0 {
if len(i.GinH["posts"].([]models.Posts)) > 0 {
s = "search-results"
}
case constraints.Category, constraints.Tag:
cat := h.C.Param("category")
cat := i.C.Param("category")
if cat == "" {
cat = h.C.Param("tag")
cat = i.C.Param("tag")
}
_, cate := slice.SearchFirst(cache.CategoriesTags(h.C, h.Scene), func(my models.TermsMy) bool {
_, cate := slice.SearchFirst(cache.CategoriesTags(i.C, i.Scene), func(my models.TermsMy) bool {
return my.Name == cat
})
if cate.Slug[0] != '%' {
@ -164,9 +177,9 @@ func (h *handle) bodyClass() string {
}
s = fmt.Sprintf("category-%d %v", cate.Terms.TermId, s)
case constraints.Detail:
s = fmt.Sprintf("postid-%d", h.GinH["post"].(models.Posts).Id)
s = fmt.Sprintf("postid-%d", i.GinH["post"].(models.Posts).Id)
}
return str.Join(class[h.Scene], s)
return str.Join(class[i.Scene], s)
}
var class = map[int]string{