diff --git a/internal/actions/detail.go b/internal/actions/detail.go deleted file mode 100644 index 2f63e17..0000000 --- a/internal/actions/detail.go +++ /dev/null @@ -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 -} diff --git a/internal/actions/index.go b/internal/actions/index.go deleted file mode 100644 index 9967e27..0000000 --- a/internal/actions/index.go +++ /dev/null @@ -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, - }) - } -} diff --git a/internal/actions/themehook.go b/internal/actions/themehook.go new file mode 100644 index 0000000..c1d3a7d --- /dev/null +++ b/internal/actions/themehook.go @@ -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)) + } +} diff --git a/internal/cmd/route/route.go b/internal/cmd/route/route.go index a2f1f49..e9dab53 100644 --- a/internal/cmd/route/route.go +++ b/internal/cmd/route/route.go @@ -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) diff --git a/internal/theme/common/common.go b/internal/theme/common/common.go index 922a8f3..a616eff 100644 --- a/internal/theme/common/common.go +++ b/internal/theme/common/common.go @@ -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 != "" { diff --git a/internal/theme/common/detail.go b/internal/theme/common/detail.go new file mode 100644 index 0000000..0c0df21 --- /dev/null +++ b/internal/theme/common/detail.go @@ -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 +} diff --git a/internal/theme/common/index.go b/internal/theme/common/index.go index 16ebc4f..2cdeb8c 100644 --- a/internal/theme/common/index.go +++ b/internal/theme/common/index.go @@ -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("%s的搜索结果", 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("月度归档: %s", 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("分类: %s", 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("标签: %s", 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...))) + +} diff --git a/internal/theme/common/indexparams.go b/internal/theme/common/indexparams.go new file mode 100644 index 0000000..b174f5a --- /dev/null +++ b/internal/theme/common/indexparams.go @@ -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("%s的搜索结果", 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("月度归档: %s", 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("分类: %s", 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("标签: %s", 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 +} diff --git a/internal/theme/common/listpostplugins.go b/internal/theme/common/listpostplugins.go new file mode 100644 index 0000000..2a961df --- /dev/null +++ b/internal/theme/common/listpostplugins.go @@ -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) +} diff --git a/internal/theme/hook.go b/internal/theme/hook.go index 5795881..40c83e7 100644 --- a/internal/theme/hook.go +++ b/internal/theme/hook.go @@ -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) diff --git a/internal/theme/twentyfifteen/twentyfifteen.go b/internal/theme/twentyfifteen/twentyfifteen.go index 7c65657..4eabddb 100644 --- a/internal/theme/twentyfifteen/twentyfifteen.go +++ b/internal/theme/twentyfifteen/twentyfifteen.go @@ -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) } diff --git a/internal/theme/twentyseventeen/twentyseventeen.go b/internal/theme/twentyseventeen/twentyseventeen.go index 5324825..7f11740 100644 --- a/internal/theme/twentyseventeen/twentyseventeen.go +++ b/internal/theme/twentyseventeen/twentyseventeen.go @@ -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{