Compare commits

..

9 Commits

Author SHA1 Message Date
b8112bb970 print head 2023-08-26 21:29:55 +08:00
cd5a12c9fd todo ... 2023-08-02 23:19:03 +08:00
74c88b1385 🤮🤮 todo ..... 2023-07-17 22:43:15 +08:00
6137502ef9 MaybeInlineStyles 2023-07-13 22:59:06 +08:00
25bf1dd1f7 scriptloader for themeseventeen 2023-07-12 22:43:59 +08:00
b247b8640d finish getPresetClasses 2023-07-08 21:39:47 +08:00
e44caae780 get_layout_styles finish 2023-07-06 21:36:59 +08:00
d81b484916 🤮🤮 2023-07-05 23:23:47 +08:00
1d837d4bf8 scriptloader half init 2023-07-04 23:36:25 +08:00
140 changed files with 6385 additions and 6719 deletions

3
.gitignore vendored
View File

@ -3,5 +3,4 @@
/config.yaml /config.yaml
err.log err.log
/plugins/ /plugins/
/config.json /config.json
go.sum

View File

@ -1,7 +1,8 @@
FROM golang:1.22.2-alpine as gobulidIso FROM golang:latest as gobulidIso
COPY ./ /go/src/wp-go COPY ./ /go/src/wp-go
WORKDIR /go/src/wp-go WORKDIR /go/src/wp-go
RUN go build -ldflags "-w" -tags netgo -o wp-go app/cmd/main.go ENV GOPROXY="https://goproxy.cn"
RUN go build -ldflags "-w" -tags netgo -o wp-go internal/cmd/main.go
FROM alpine:latest FROM alpine:latest
WORKDIR /opt/wp-go WORKDIR /opt/wp-go

View File

@ -20,11 +20,6 @@
- kill -SIGUSR1 PID 更新配置和清空缓存 - kill -SIGUSR1 PID 更新配置和清空缓存
- kill -SIGUSR2 PID 清空缓存 - kill -SIGUSR2 PID 清空缓存
#### 运行
```
go run app/cmd/main.go [-c configpath] [-p port]
```
#### 数据显示支持程度 #### 数据显示支持程度
| 页表 | 支持程度 | | 页表 | 支持程度 |
@ -53,10 +48,8 @@ go run app/cmd/main.go [-c configpath] [-p port]
- 博客页面至多显示数量 - 博客页面至多显示数量
- Feed中显示最近数量 - Feed中显示最近数量
- 讨论 - 讨论
- 其他评论设置 - 其他评论设置-启用评论嵌套,最多嵌套层数
- `启用|禁止`评论嵌套,最多嵌套层数 - 在每个页面顶部显示 `新旧`评论
- 分页显示评论,每页显示评论条数,默认显示`最前/后`页
- 在每个页面顶部显示 `新旧`评论
#### 主题支持程度 #### 主题支持程度
@ -79,9 +72,4 @@ go run app/cmd/main.go [-c configpath] [-p port]
#### 其它 #### 其它
用的gin框架和sqlx,在外面封装了层查询的方法。 用的gin框架和sqlx,在外面封装了层查询的方法。
#### 鸣谢
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." width="30%"></a>

View File

@ -11,7 +11,6 @@ import (
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"io" "io"
@ -78,7 +77,7 @@ func PostComment(c *gin.Context) {
} }
req.Host = home.Host req.Host = home.Host
res, err := cli.Do(req) res, err := cli.Do(req)
if err != nil && !errors.Is(err, http.ErrUseLastResponse) { if err != nil && err != http.ErrUseLastResponse {
logs.Error(err, "请求评论接口错误") logs.Error(err, "请求评论接口错误")
return return
} }
@ -111,9 +110,6 @@ func PostComment(c *gin.Context) {
} }
cc := c.Copy() cc := c.Copy()
go func() { go func() {
if gin.Mode() != gin.ReleaseMode {
return
}
id := comment.CommentPostId id := comment.CommentPostId
if id <= 0 { if id <= 0 {
logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId) logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId)
@ -134,15 +130,8 @@ func PostComment(c *gin.Context) {
err = er err = er
return return
} }
uuu := "" cache.NewCommentCache().Set(c, up.RawQuery, string(s))
uu, _ := url.Parse(res.Header.Get("Location")) c.Redirect(http.StatusFound, res.Header.Get("Location"))
if up.RawQuery != "" {
cache.NewCommentCache().Set(c, up.RawQuery, string(s))
uuu = str.Join(uu.Path, "?", uu.RawQuery)
} else {
uuu = str.Join(uu.Path)
}
c.Redirect(http.StatusFound, uuu)
return return
} }
var r io.Reader var r io.Reader

View File

@ -10,30 +10,6 @@ import (
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT" var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
func Feed(c *gin.Context) {
v, ok := c.GetQuery("feed")
if !ok || v == "" {
c.Next()
return
}
switch v {
case "rss2":
p, ok := c.GetQuery("p")
if ok && p != "" {
c.AddParam("id", p)
PostFeed(c)
} else {
SiteFeed(c)
}
c.Abort()
return
case "comments-rss2":
CommentsFeed(c)
c.Abort()
return
}
}
func isCacheExpired(c *gin.Context, lastTime time.Time) bool { func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
eTag := str.Md5(lastTime.Format(tmp)) eTag := str.Md5(lastTime.Format(tmp))
since := c.Request.Header.Get("If-Modified-Since") since := c.Request.Header.Get("If-Modified-Since")
@ -48,21 +24,20 @@ func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
return true return true
} }
func SiteFeed(c *gin.Context) { func Feed(c *gin.Context) {
feed := cache.FeedCache() if !isCacheExpired(c, cache.FeedCache().GetLastSetTime()) {
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
c.Status(http.StatusNotModified) c.Status(http.StatusNotModified)
return return
} }
r, err := feed.GetCache(c, time.Second, c) r, err := cache.FeedCache().GetCache(c, time.Second, c)
if err != nil { if err != nil {
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
c.Error(err) c.Error(err)
return return
} }
setFeed(r[0], c, feed.GetLastSetTime(c)) setFeed(r[0], c, cache.FeedCache().GetLastSetTime())
} }
func setFeed(s string, c *gin.Context, t time.Time) { func setFeed(s string, c *gin.Context, t time.Time) {
@ -76,33 +51,31 @@ func setFeed(s string, c *gin.Context, t time.Time) {
func PostFeed(c *gin.Context) { func PostFeed(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
postFeed := cache.PostFeedCache() if !isCacheExpired(c, cache.PostFeedCache().GetLastSetTime(c, id)) {
if !isCacheExpired(c, postFeed.GetLastSetTime(c, id)) {
c.Status(http.StatusNotModified) c.Status(http.StatusNotModified)
return return
} }
s, err := postFeed.GetCache(c, id, time.Second) s, err := cache.PostFeedCache().GetCache(c, id, time.Second, c, id)
if err != nil { if err != nil {
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
c.Error(err) c.Error(err)
return return
} }
setFeed(s, c, postFeed.GetLastSetTime(c, id)) setFeed(s, c, cache.PostFeedCache().GetLastSetTime(c, id))
} }
func CommentsFeed(c *gin.Context) { func CommentsFeed(c *gin.Context) {
feed := cache.CommentsFeedCache() if !isCacheExpired(c, cache.CommentsFeedCache().GetLastSetTime()) {
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
c.Status(http.StatusNotModified) c.Status(http.StatusNotModified)
return return
} }
r, err := feed.GetCache(c, time.Second, c) r, err := cache.CommentsFeedCache().GetCache(c, time.Second, c)
if err != nil { if err != nil {
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
c.Error(err) c.Error(err)
return return
} }
setFeed(r[0], c, feed.GetLastSetTime(c)) setFeed(r[0], c, cache.CommentsFeedCache().GetLastSetTime())
} }

View File

@ -1,6 +1,7 @@
package actions package actions
import ( import (
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme" "github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -8,8 +9,18 @@ import (
func ThemeHook(scene string) func(*gin.Context) { func ThemeHook(scene string) func(*gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
t := theme.GetCurrentTheme() s := scene
h := wp.NewHandle(c, scene, t) if scene == constraints.Home {
if _, ok := c.GetQuery("s"); ok {
s = constraints.Search
}
}
t := theme.GetCurrentTemplateName()
h := wp.NewHandle(c, s, t)
h.Index = wp.NewIndexHandle(h)
h.Detail = wp.NewDetailHandle(h)
templ, _ := theme.GetTemplate(t)
h.SetTemplate(templ)
theme.Hook(t, h) theme.Hook(t, h)
} }
} }

View File

@ -0,0 +1,53 @@
package cachemanager
import (
"context"
"github.com/fthvgb1/wp-go/cache"
"time"
)
var ctx = context.Background()
type flush interface {
Flush(ctx context.Context)
}
type clear interface {
ClearExpired(ctx context.Context)
}
var clears []clear
var flushes []flush
func Flush() {
for _, f := range flushes {
f.Flush(ctx)
}
}
func MapCacheBy[K comparable, V any](fn func(...any) (V, error), expireTime time.Duration) *cache.MapCache[K, V] {
m := cache.NewMemoryMapCacheByFn[K, V](fn, expireTime)
FlushPush(m)
ClearPush(m)
return m
}
func MapBatchCacheBy[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *cache.MapCache[K, V] {
m := cache.NewMemoryMapCacheByBatchFn[K, V](fn, expireTime)
FlushPush(m)
ClearPush(m)
return m
}
func FlushPush(f ...flush) {
flushes = append(flushes, f...)
}
func ClearPush(c ...clear) {
clears = append(clears, c...)
}
func ClearExpired() {
for _, c := range clears {
c.ClearExpired(ctx)
}
}

View File

@ -3,21 +3,26 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/ossigns" "github.com/fthvgb1/wp-go/app/cmd/cachemanager"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/cmd/route"
"github.com/fthvgb1/wp-go/app/mail"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/db" "github.com/fthvgb1/wp-go/app/pkg/db"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/plugins/wphandle" "github.com/fthvgb1/wp-go/app/plugins/wphandle"
"github.com/fthvgb1/wp-go/app/route"
"github.com/fthvgb1/wp-go/app/theme" "github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/theme/wp/scriptloader"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
"log"
"os" "os"
"os/signal"
"regexp" "regexp"
"strings" "strings"
"syscall"
"time" "time"
) )
@ -25,7 +30,13 @@ var confPath string
var address string var address string
var intReg = regexp.MustCompile(`^\d`) var intReg = regexp.MustCompile(`^\d`)
func inits() { func init() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
os.Exit(-1)
}
}()
flag.StringVar(&confPath, "c", "config.yaml", "config file support json,yaml or url") flag.StringVar(&confPath, "c", "config.yaml", "config file support json,yaml or url")
flag.StringVar(&address, "p", "", "listen address and port") flag.StringVar(&address, "p", "", "listen address and port")
flag.Parse() flag.Parse()
@ -39,6 +50,7 @@ func inits() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
scriptloader.InitDefaultScriptSetting()
cache.InitActionsCommonCache() cache.InitActionsCommonCache()
plugins.InitDigestCache() plugins.InitDigestCache()
theme.InitTheme() theme.InitTheme()
@ -85,16 +97,61 @@ func cronClearCache() {
} }
} }
func flushCache() {
defer func() {
if r := recover(); r != nil {
err := mail.SendMail([]string{config.GetConfig().Mail.User}, "清空缓存失败", fmt.Sprintf("err:[%s]", r))
logs.IfError(err, "发邮件失败")
}
}()
cachemanager.Flush()
log.Println("all cache flushed")
}
func reloads() {
defer func() {
if r := recover(); r != nil {
log.Println(r)
}
}()
err := config.InitConfig(confPath)
logs.IfError(err, "获取配置文件失败", confPath)
err = logs.InitLogger()
logs.IfError(err, "日志配置错误")
_, err = db.InitDb()
logs.IfError(err, "重新读取db失败", config.GetConfig().Mysql)
err = wpconfig.InitOptions()
logs.IfError(err, "获取网站设置WpOption失败")
err = wpconfig.InitTerms()
logs.IfError(err, "获取WpTerms表失败")
scriptloader.InitDefaultScriptSetting()
wphandle.LoadPlugins()
reload.Reload()
flushCache()
log.Println("reload complete")
}
func signalNotify() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGUSR1, syscall.SIGUSR2)
for {
switch <-c {
case syscall.SIGUSR1:
go reloads()
case syscall.SIGUSR2:
go flushCache()
}
}
}
func main() { func main() {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
fmt.Println(r) fmt.Println(r)
os.Exit(1) os.Exit(-1)
} }
}() }()
inits() go signalNotify()
ossigns.SetConfPath(confPath)
go ossigns.SignalNotify()
Gin := route.SetupRouter() Gin := route.SetupRouter()
c := config.GetConfig() c := config.GetConfig()
if c.Ssl.Key != "" && c.Ssl.Cert != "" { if c.Ssl.Key != "" && c.Ssl.Cert != "" {

204
app/cmd/reload/reload.go Normal file
View File

@ -0,0 +1,204 @@
package reload
import (
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/safety"
"sync"
)
var calls []func()
var anyMap = safety.NewMap[string, any]()
type safetyVar[T, A any] struct {
Val *safety.Var[val[T]]
mutex sync.Mutex
}
type val[T any] struct {
v T
ok bool
counter number.Counter[int]
}
type safetyMap[K comparable, V, A any] struct {
val *safety.Map[K, V]
mutex sync.Mutex
}
var safetyMaps = safety.NewMap[string, any]()
var safetyMapLock = sync.Mutex{}
func GetAnyMapFnBys[K comparable, V, A any](namespace string, fn func(A) V) func(key K, args A) V {
m := safetyMapFn[K, V, A](namespace)
return func(key K, a A) V {
v, ok := m.val.Load(key)
if ok {
return v
}
m.mutex.Lock()
v, ok = m.val.Load(key)
if ok {
m.mutex.Unlock()
return v
}
v = fn(a)
m.val.Store(key, v)
m.mutex.Unlock()
return v
}
}
func safetyMapFn[K comparable, V, A any](namespace string) *safetyMap[K, V, A] {
vv, ok := safetyMaps.Load(namespace)
var m *safetyMap[K, V, A]
if ok {
m = vv.(*safetyMap[K, V, A])
} else {
safetyMapLock.Lock()
vv, ok = safetyMaps.Load(namespace)
if ok {
m = vv.(*safetyMap[K, V, A])
} else {
m = &safetyMap[K, V, A]{safety.NewMap[K, V](), sync.Mutex{}}
Push(func() {
m.val.Flush()
})
safetyMaps.Store(namespace, m)
}
safetyMapLock.Unlock()
}
return m
}
func GetAnyValMapBy[K comparable, V, A any](namespace string, key K, a A, fn func(A) V) V {
m := safetyMapFn[K, V, A](namespace)
v, ok := m.val.Load(key)
if ok {
return v
}
m.mutex.Lock()
v, ok = m.val.Load(key)
if ok {
m.mutex.Unlock()
return v
}
v = fn(a)
m.val.Store(key, v)
m.mutex.Unlock()
return v
}
func anyVal[T, A any](namespace string, counter bool) *safetyVar[T, A] {
var vv *safetyVar[T, A]
vvv, ok := safetyMaps.Load(namespace)
if ok {
vv = vvv.(*safetyVar[T, A])
} else {
safetyMapLock.Lock()
vvv, ok = safetyMaps.Load(namespace)
if ok {
vv = vvv.(*safetyVar[T, A])
} else {
v := val[T]{}
if counter {
v.counter = number.Counters[int]()
}
vv = &safetyVar[T, A]{safety.NewVar(v), sync.Mutex{}}
Push(func() {
vv.Val.Flush()
})
safetyMaps.Store(namespace, vv)
}
safetyMapLock.Unlock()
}
return vv
}
func GetAnyValBy[T, A any](namespace string, tryTimes int, a A, fn func(A) (T, bool)) T {
var vv = anyVal[T, A](namespace, true)
var ok bool
v := vv.Val.Load()
if v.ok {
return v.v
}
vv.mutex.Lock()
v = vv.Val.Load()
if v.ok {
vv.mutex.Unlock()
return v.v
}
v.v, ok = fn(a)
times := v.counter()
if ok || times >= tryTimes {
v.ok = true
vv.Val.Store(v)
}
vv.mutex.Unlock()
return v.v
}
func GetAnyValBys[T, A any](namespace string, a A, fn func(A) T) T {
var vv = anyVal[T, A](namespace, false)
v := vv.Val.Load()
if v.ok {
return v.v
}
vv.mutex.Lock()
v = vv.Val.Load()
if v.ok {
vv.mutex.Unlock()
return v.v
}
v.v = fn(a)
v.ok = true
vv.Val.Store(v)
vv.mutex.Unlock()
return v.v
}
func Vars[T any](defaults T) *safety.Var[T] {
ss := safety.NewVar(defaults)
calls = append(calls, func() {
ss.Store(defaults)
})
return ss
}
func VarsBy[T any](fn func() T) *safety.Var[T] {
ss := safety.NewVar(fn())
calls = append(calls, func() {
ss.Store(fn())
})
return ss
}
func MapBy[K comparable, T any](fn func(*safety.Map[K, T])) *safety.Map[K, T] {
m := safety.NewMap[K, T]()
if fn != nil {
fn(m)
}
calls = append(calls, func() {
m.Flush()
if fn != nil {
fn(m)
}
})
return m
}
func SafeMap[K comparable, T any]() *safety.Map[K, T] {
m := safety.NewMap[K, T]()
calls = append(calls, func() {
m.Flush()
})
return m
}
func Push(fn ...func()) {
calls = append(calls, fn...)
}
func Reload() {
for _, call := range calls {
call()
}
anyMap.Flush()
safetyMaps.Flush()
}

97
app/cmd/route/route.go Normal file
View File

@ -0,0 +1,97 @@
package route
import (
"github.com/fthvgb1/wp-go/app/actions"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/middleware"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/wpconfig"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
var hooker []func(r *gin.Engine)
// Hook 方便插件在init时使用
func Hook(fn ...func(r *gin.Engine)) {
hooker = append(hooker, fn...)
}
func SetupRouter() *gin.Engine {
// Disable Console Color
// gin.DisableConsoleColor()
r := gin.New()
c := config.GetConfig()
if len(c.TrustIps) > 0 {
err := r.SetTrustedProxies(c.TrustIps)
if err != nil {
panic(err)
}
}
r.HTMLRender = theme.Template()
wpconfig.SetTemplateFs(theme.TemplateFs)
siteFlowLimitMiddleware, siteFlow := middleware.FlowLimit(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
r.Use(
gin.Logger(),
middleware.ValidateServerNames(),
middleware.RecoverAndSendMail(gin.DefaultErrorWriter),
siteFlowLimitMiddleware,
middleware.SetStaticFileCache,
)
//gzip 因为一般会用nginx做反代时自动使用gzip,所以go这边本身可以不用
if c.Gzip {
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{
"/wp-includes/", "/wp-content/",
})))
}
if c.WpDir == "" {
panic("wordpress path can't be empty")
}
r.Static("/wp-content/uploads", str.Join(c.WpDir, "/wp-content/uploads"))
r.Static("/wp-content/themes", str.Join(c.WpDir, "/wp-content/themes"))
r.Static("/wp-content/plugins", str.Join(c.WpDir, "/wp-content/plugins"))
r.Static("/wp-includes/css", str.Join(c.WpDir, "/wp-includes/css"))
r.Static("/wp-includes/fonts", str.Join(c.WpDir, "/wp-includes/fonts"))
r.Static("/wp-includes/js", str.Join(c.WpDir, "/wp-includes/js"))
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("go-wp", store))
r.GET("/", middleware.SearchLimit(c.SingleIpSearchNum), 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.ThemeHook(constraints.Detail))
r.GET("/p/:id/feed", actions.PostFeed)
r.GET("/feed", actions.Feed)
r.GET("/comments/feed", actions.CommentsFeed)
//r.NoRoute(actions.ThemeHook(constraints.NoRoute))
commentMiddleWare, _ := middleware.FlowLimit(c.MaxRequestSleepNum, 5, c.CacheTime.SleepTime)
r.POST("/comment", commentMiddleWare, actions.PostComment)
if c.Pprof != "" {
pprof.Register(r, c.Pprof)
}
for _, fn := range hooker {
fn(r)
}
reload.Push(func() {
c := config.GetConfig()
siteFlow(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
})
return r
}

View File

@ -19,9 +19,9 @@ func FlowLimit(maxRequestSleepNum, maxRequestNum int64, sleepTime []time.Duratio
} }
s := safety.Var[[]time.Duration]{} s := safety.Var[[]time.Duration]{}
s.Store(sleepTime) s.Store(sleepTime)
fn := func(sleepNum, maxNum int64, st []time.Duration) { fn := func(msn, mn int64, st []time.Duration) {
atomic.StoreInt64(&maxRequestSleepNum, sleepNum) atomic.StoreInt64(&maxRequestSleepNum, msn)
atomic.StoreInt64(&maxRequestNum, maxNum) atomic.StoreInt64(&maxRequestNum, mn)
s.Store(st) s.Store(st)
} }
return func(c *gin.Context) { return func(c *gin.Context) {

View File

@ -25,25 +25,14 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
fn(num) fn(num)
return func(c *gin.Context) { return func(c *gin.Context) {
if atomic.LoadInt64(m.limitNum) <= 0 {
c.Next()
return
}
ip := c.ClientIP() ip := c.ClientIP()
s := false
m.mux.RLock() m.mux.RLock()
i, ok := m.m[ip] i, ok := m.m[ip]
m.mux.RUnlock() m.mux.RUnlock()
if !ok {
m.mux.Lock()
i = new(int64)
m.m[ip] = i
m.mux.Unlock()
}
defer func() { defer func() {
ii := atomic.LoadInt64(i) ii := atomic.LoadInt64(i)
if ii > 0 { if s && ii > 0 {
atomic.AddInt64(i, -1) atomic.AddInt64(i, -1)
if atomic.LoadInt64(i) == 0 { if atomic.LoadInt64(i) == 0 {
m.mux.Lock() m.mux.Lock()
@ -53,12 +42,20 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
} }
}() }()
if atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) { if !ok {
m.mux.Lock()
i = new(int64)
m.m[ip] = i
m.mux.Unlock()
}
if atomic.LoadInt64(m.limitNum) > 0 && atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) {
c.Status(http.StatusForbidden) c.Status(http.StatusForbidden)
c.Abort() c.Abort()
return return
} }
atomic.AddInt64(i, 1) atomic.AddInt64(i, 1)
s = true
c.Next() c.Next()
}, fn }, fn
} }

View File

@ -1,16 +1,16 @@
package middleware package middleware
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func SearchLimit(num int64) func(ctx *gin.Context) { func SearchLimit(num int64) func(ctx *gin.Context) {
fn, reFn := IpLimit(num) fn, reFn := IpLimit(num)
reload.Append(func() { reload.Push(func() {
reFn(config.GetConfig().SingleIpSearchNum) reFn(config.GetConfig().SingleIpSearchNum)
}, "search-ip-limit-number") })
return func(c *gin.Context) { return func(c *gin.Context) {
if c.Query("s") != "" { if c.Query("s") != "" {
fn(c) fn(c)

View File

@ -1,8 +1,8 @@
package middleware package middleware
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
@ -19,7 +19,7 @@ func ValidateServerNames() func(ctx *gin.Context) {
} }
} }
return m return m
}, "site-names") })
return func(c *gin.Context) { return func(c *gin.Context) {
m := sites.Load() m := sites.Load()

View File

@ -1,69 +0,0 @@
package ossigns
import (
"fmt"
"github.com/fthvgb1/wp-go/app/mail"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/db"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/plugins/wphandle"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/signs"
"log"
"syscall"
)
var confPath string
func SetConfPath(path string) {
confPath = path
}
func FlushCache() {
defer func() {
if r := recover(); r != nil {
err := mail.SendMail([]string{config.GetConfig().Mail.User}, "清空缓存失败", fmt.Sprintf("err:[%s]", r))
logs.IfError(err, "发邮件失败")
}
}()
cachemanager.Flush()
log.Println("all cache flushed")
}
func Reloads() {
defer func() {
if r := recover(); r != nil {
log.Println(r)
}
}()
err := config.InitConfig(confPath)
logs.IfError(err, "获取配置文件失败", confPath)
err = logs.InitLogger()
logs.IfError(err, "日志配置错误")
_, err = db.InitDb()
logs.IfError(err, "重新读取db失败", config.GetConfig().Mysql)
err = wpconfig.InitOptions()
logs.IfError(err, "获取网站设置WpOption失败")
err = wpconfig.InitTerms()
logs.IfError(err, "获取WpTerms表失败")
wphandle.LoadPlugins()
reload.Reloads("themeArgAndConfig")
FlushCache()
log.Println("reload complete")
}
func SignalNotify() {
rel := func() bool {
go Reloads()
return true
}
flu := func() bool {
go FlushCache()
return true
}
signs.Install(syscall.SIGUSR1, rel, "reload")
signs.Install(syscall.SIGUSR2, flu, "flush")
signs.Wait()
}

148
app/pkg/cache/cache.go vendored
View File

@ -2,96 +2,85 @@ package cache
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/dao" "github.com/fthvgb1/wp-go/app/pkg/dao"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache/cachemanager" "github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/safety"
"time" "time"
) )
var postContextCache *cache.MapCache[uint64, dao.PostContext]
var categoryAndTagsCaches *cache.MapCache[string, []models.TermsMy]
var recentPostsCaches *cache.VarCache[[]models.Posts]
var recentCommentsCaches *cache.VarCache[[]models.Comments]
var postCommentCaches *cache.MapCache[uint64, []uint64]
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, dao.PostIds]
var searchPostIdsCache *cache.MapCache[string, dao.PostIds]
var maxPostIdCache *cache.VarCache[uint64]
var usersCache *cache.MapCache[uint64, models.Users]
var usersNameCache *cache.MapCache[string, models.Users]
var commentsCache *cache.MapCache[uint64, models.Comments]
var feedCache *cache.VarCache[[]string]
var postFeedCache *cache.MapCache[string, string]
var commentsFeedCache *cache.VarCache[[]string]
var newCommentCache *cache.MapCache[string, string]
var allUsernameCache *cache.VarCache[map[string]struct{}]
func InitActionsCommonCache() { func InitActionsCommonCache() {
c := config.GetConfig() c := config.GetConfig()
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds", func() time.Duration { searchPostIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.SearchPostCacheTime)
return config.GetConfig().CacheTime.SearchPostCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds", func() time.Duration { postListIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.PostListCacheTime)
return config.GetConfig().CacheTime.PostListCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds", func() time.Duration { monthPostsCache = cachemanager.MapCacheBy[string](dao.MonthPost, c.CacheTime.MonthPostCacheTime)
return config.GetConfig().CacheTime.MonthPostCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext", func() time.Duration { postContextCache = cachemanager.MapCacheBy[uint64](dao.GetPostContext, c.CacheTime.ContextPostCacheTime)
return config.GetConfig().CacheTime.ContextPostCacheTime
})
cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData", func() time.Duration { postsCache = cachemanager.MapBatchCacheBy(dao.GetPostsByIds, c.CacheTime.PostDataCacheTime)
return config.GetConfig().CacheTime.PostDataCacheTime
})
cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData", func() time.Duration { postMetaCache = cachemanager.MapBatchCacheBy(dao.GetPostMetaByPostIds, c.CacheTime.PostDataCacheTime)
return config.GetConfig().CacheTime.PostDataCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData", func() time.Duration { categoryAndTagsCaches = cachemanager.MapCacheBy[string](dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime)
return config.GetConfig().CacheTime.CategoryCacheTime
})
cachemanager.NewVarMemoryCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime, "recentPosts", func() time.Duration { recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime)
return config.GetConfig().CacheTime.RecentPostCacheTime
})
cachemanager.NewVarMemoryCache(RecentComment, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration { recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime)
return config.GetConfig().CacheTime.RecentCommentsCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration { postCommentCaches = cachemanager.MapCacheBy[uint64](dao.PostComments, c.CacheTime.PostCommentsCacheTime)
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
cachemanager.NewMemoryMapCache(nil, PostTopComments, 30*time.Second, "PostCommentsIds", func() time.Duration { maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime)
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
cachemanager.NewMemoryMapCache(nil, dao.PostTopCommentNum, 30*time.Second, "postTopCommentsNum", func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, time.Hour, "postCommentData", func() time.Duration { usersCache = cachemanager.MapCacheBy[uint64](dao.GetUserById, c.CacheTime.UserInfoCacheTime)
return config.GetConfig().CacheTime.CommentsCacheTime
})
cachemanager.NewMemoryMapCache(dao.CommentChildren, nil, time.Minute, "commentChildren", func() time.Duration { usersNameCache = cachemanager.MapCacheBy[string](dao.GetUserByName, c.CacheTime.UserInfoCacheTime)
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration { commentsCache = cachemanager.MapBatchCacheBy(dao.GetCommentByIds, c.CacheTime.CommentsCacheTime)
return config.GetConfig().CacheTime.MaxPostIdCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData", func() time.Duration { allUsernameCache = cache.NewVarCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime)
return config.GetConfig().CacheTime.UserInfoCacheTime
})
cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameToUserData", func() time.Duration { feedCache = cache.NewVarCache(feed, time.Hour)
return config.GetConfig().CacheTime.UserInfoCacheTime
})
cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration { postFeedCache = cachemanager.MapCacheBy[string](postFeed, time.Hour)
return config.GetConfig().CacheTime.UserInfoCacheTime
})
cachemanager.NewVarMemoryCache(SiteFeed, time.Hour, "siteFeed") commentsFeedCache = cache.NewVarCache(commentsFeed, time.Hour)
cachemanager.NewMemoryMapCache(nil, PostFeed, time.Hour, "postFeed") newCommentCache = cachemanager.MapCacheBy[string, string](nil, 15*time.Minute)
cachemanager.NewVarMemoryCache(CommentsFeed, time.Hour, "commentsFeed")
cachemanager.NewMemoryMapCache[string, string](nil, nil, 15*time.Minute, "NewComment")
InitFeed() InitFeed()
} }
@ -102,19 +91,19 @@ type Arch struct {
month time.Month month time.Month
} }
var arch = reload.Vars(Arch{ var arch = safety.NewVar(Arch{
fn: dao.Archives, fn: dao.Archives,
}, "archives-year-month-data") })
func Archives(ctx context.Context) []models.PostArchive { func Archives(ctx context.Context) []models.PostArchive {
a := arch.Load() a := arch.Load()
data := a.data data := a.data
l := len(data) l := len(data)
m := time.Now().Month() m := time.Now().Month()
if l < 1 || a.month != m { if l > 0 && a.month != m || l < 1 {
r, err := a.fn(ctx) r, err := a.fn(ctx)
if err != nil { if err != nil {
logs.Error(err, "set cache Archives fail") logs.Error(err, "set cache fail")
return nil return nil
} }
a.month = m a.month = m
@ -124,3 +113,30 @@ func Archives(ctx context.Context) []models.PostArchive {
} }
return data return data
} }
// CategoriesTags categories or tags
//
// t is constraints.Tag or constraints.Category
func CategoriesTags(ctx context.Context, t ...string) []models.TermsMy {
tt := ""
if len(t) > 0 {
tt = t[0]
}
r, err := categoryAndTagsCaches.GetCache(ctx, tt, time.Second, ctx, tt)
logs.IfError(err, "get category fail")
return r
}
func AllCategoryTagsNames(ctx context.Context, t ...string) map[string]struct{} {
tt := ""
if len(t) > 0 {
tt = t[0]
}
r, err := categoryAndTagsCaches.GetCache(ctx, tt, time.Second, ctx, tt)
if err != nil {
logs.Error(err, "get category fail")
return nil
}
return slice.ToMap(r, func(t models.TermsMy) (string, struct{}) {
return t.Name, struct{}{}
}, true)
}

View File

@ -1,39 +0,0 @@
package cache
import (
"context"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper/slice"
"time"
)
// CategoriesTags get all categories or tags
//
// query func see dao.CategoriesAndTags
//
// t is constraints.Tag or constraints.Category
func CategoriesTags(ctx context.Context, t ...string) []models.TermsMy {
tt := ""
if len(t) > 0 {
tt = t[0]
}
r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second)
logs.IfError(err, "get category fail")
return r
}
func AllCategoryTagsNames(ctx context.Context, t ...string) map[string]struct{} {
tt := ""
if len(t) > 0 {
tt = t[0]
}
r, err := cachemanager.GetBy[[]models.TermsMy]("categoryAndTagsData", ctx, tt, time.Second)
if err != nil {
logs.Error(err, "get category fail")
return nil
}
return slice.ToMap(r, func(t models.TermsMy) (string, struct{}) {
return t.Name, struct{}{}
}, true)
}

View File

@ -2,23 +2,16 @@ package cache
import ( import (
"context" "context"
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/dao"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings"
"time" "time"
) )
// RecentComments query func see RecentComment
func RecentComments(ctx context.Context, n int) (r []models.Comments) { func RecentComments(ctx context.Context, n int) (r []models.Comments) {
nn := number.Max(n, 10) nn := number.Max(n, 10)
r, err := cachemanager.GetVarVal[[]models.Comments]("recentComments", ctx, time.Second, ctx, nn) r, err := recentCommentsCaches.GetCache(ctx, time.Second, ctx, nn)
if len(r) > n { if len(r) > n {
r = r[0:n] r = r[0:n]
} }
@ -26,99 +19,22 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) {
return return
} }
// PostTopLevelCommentIds query func see PostTopComments func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) {
func PostTopLevelCommentIds(ctx context.Context, postId uint64, page, limit, total int, order string, a ...any) ([]uint64, error) { ids, err := postCommentCaches.GetCache(ctx, Id, time.Second, ctx, Id)
var key string
if len(a) > 0 {
key = helper.ParseArgs("", a...)
}
if key == "" {
key = fmt.Sprintf("%d-%d-%d-%d-%s", postId, page, limit, total, order)
}
return cachemanager.GetBy[[]uint64]("PostCommentsIds", ctx,
key, time.Second, postId, page, limit, 0, order)
}
// GetCommentById query func see dao.GetCommentByIds
func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
return cachemanager.GetBy[models.Comments]("postCommentData", ctx, id, time.Second)
}
// GetCommentDataByIds query func see dao.GetCommentByIds
func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
return cachemanager.GetBatchBy[models.Comments]("postCommentData", ctx, ids, time.Second)
}
func NewCommentCache() *cache.MapCache[string, string] {
r, _ := cachemanager.GetMapCache[string, string]("NewComment")
return r
}
func PostTopComments(ctx context.Context, _ string, a ...any) ([]uint64, error) {
postId := a[0].(uint64)
page := a[1].(int)
limit := a[2].(int)
total := a[3].(int)
order := helper.ParseArgs("", a...)
if order == "" {
order = wpconfig.GetOption("comment_order")
}
v, _, err := dao.PostCommentsIds(ctx, postId, page, limit, total, order)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return v, nil return GetCommentByIds(ctx, ids)
} }
func RecentComment(ctx context.Context, a ...any) (r []models.Comments, err error) { func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
r, err = dao.RecentComments(ctx, a...) return commentsCache.GetCache(ctx, id, time.Second, ctx, id)
if err != nil {
return r, err
}
for i, comment := range r {
r[i].CommentAuthorUrl, err = GetCommentUrl(ctx, comment.CommentId, comment.CommentPostId)
if err != nil {
return nil, err
}
}
return
} }
func GetCommentUrl(ctx context.Context, commentId, postId uint64) (string, error) { func GetCommentByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
if wpconfig.GetOption("page_comments") != "1" { return commentsCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil
}
commentsPerPage := str.ToInteger(wpconfig.GetOption("comments_per_page"), 5)
topCommentId, err := AncestorCommentId(ctx, commentId)
if err != nil {
return "", err
}
totalNum, err := cachemanager.GetBy[int]("postTopCommentsNum", ctx, postId, time.Second)
if err != nil {
return "", err
}
if totalNum <= commentsPerPage {
return fmt.Sprintf("/p/%d#comment-%d", postId, commentId), nil
}
num, err := dao.PreviousCommentNum(ctx, topCommentId, postId)
if err != nil {
return "", err
}
order := wpconfig.GetOption("comment_order")
page := number.DivideCeil(num+1, commentsPerPage)
if order == "desc" {
page = number.DivideCeil(totalNum-num, commentsPerPage)
}
return fmt.Sprintf("/p/%d/comment-page-%d/#comment-%d", postId, page, commentId), nil
} }
func AncestorCommentId(ctx context.Context, commentId uint64) (uint64, error) { func NewCommentCache() *cache.MapCache[string, string] {
comment, err := GetCommentById(ctx, commentId) return newCommentCache
if err != nil {
return 0, err
}
if comment.CommentParent == 0 {
return comment.CommentId, nil
}
return AncestorCommentId(ctx, comment.CommentParent)
} }

55
app/pkg/cache/feed.go vendored
View File

@ -1,8 +1,6 @@
package cache package cache
import ( import (
"context"
"errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
@ -10,11 +8,11 @@ import (
"github.com/fthvgb1/wp-go/app/plugins/wpposts" "github.com/fthvgb1/wp-go/app/plugins/wpposts"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/digest" "github.com/fthvgb1/wp-go/plugin/digest"
"github.com/fthvgb1/wp-go/rss2" "github.com/fthvgb1/wp-go/rss2"
"github.com/gin-gonic/gin"
"strings" "strings"
"time" "time"
) )
@ -35,25 +33,20 @@ func InitFeed() {
} }
} }
// CommentsFeedCache query func see CommentsFeed
func CommentsFeedCache() *cache.VarCache[[]string] { func CommentsFeedCache() *cache.VarCache[[]string] {
r, _ := cachemanager.GetVarCache[[]string]("commentsFeed") return commentsFeedCache
return r
} }
// FeedCache query func see SiteFeed
func FeedCache() *cache.VarCache[[]string] { func FeedCache() *cache.VarCache[[]string] {
r, _ := cachemanager.GetVarCache[[]string]("siteFeed") return feedCache
return r
} }
// PostFeedCache query func see PostFeed
func PostFeedCache() *cache.MapCache[string, string] { func PostFeedCache() *cache.MapCache[string, string] {
r, _ := cachemanager.GetMapCache[string, string]("postFeed") return postFeedCache
return r
} }
func SiteFeed(c context.Context, _ ...any) (xml []string, err error) { func feed(arg ...any) (xml []string, err error) {
c := arg[0].(*gin.Context)
r := RecentPosts(c, 10) r := RecentPosts(c, 10)
ids := slice.Map(r, func(t models.Posts) uint64 { ids := slice.Map(r, func(t models.Posts) uint64 {
return t.Id return t.Id
@ -99,7 +92,9 @@ func SiteFeed(c context.Context, _ ...any) (xml []string, err error) {
return return
} }
func PostFeed(c context.Context, id string, _ ...any) (x string, err error) { func postFeed(arg ...any) (x string, err error) {
c := arg[0].(*gin.Context)
id := arg[1].(string)
ID := str.ToInteger[uint64](id, 0) ID := str.ToInteger[uint64](id, 0)
maxId, err := GetMaxPostId(c) maxId, err := GetMaxPostId(c)
logs.IfError(err, "get max post id") logs.IfError(err, "get max post id")
@ -110,12 +105,7 @@ func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
if post.Id == 0 || err != nil { if post.Id == 0 || err != nil {
return return
} }
limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10) comments, err := PostComments(c, post.Id)
ids, err := PostTopLevelCommentIds(c, ID, 1, limit, 0, "desc", "latest-comment")
if err != nil {
return
}
comments, err := GetCommentDataByIds(c, ids)
if err != nil { if err != nil {
return return
} }
@ -131,14 +121,10 @@ func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
wpposts.PasswdProjectContent(&post) wpposts.PasswdProjectContent(&post)
if len(comments) > 0 { if len(comments) > 0 {
t := comments[len(comments)-1] t := comments[len(comments)-1]
u, err := GetCommentUrl(c, t.CommentId, t.CommentPostId)
if err != nil {
return "", err
}
rs.Items = []rss2.Item{ rs.Items = []rss2.Item{
{ {
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor), Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
Link: fmt.Sprintf("%s%s", site, u), Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
Creator: t.CommentAuthor, Creator: t.CommentAuthor,
PubDate: t.CommentDateGmt.Format(timeFormat), PubDate: t.CommentDateGmt.Format(timeFormat),
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId), Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
@ -149,14 +135,9 @@ func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
} }
} else { } else {
rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item { rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
if er != nil {
err = errors.Join(err, er)
return rss2.Item{}
}
return rss2.Item{ return rss2.Item{
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor), Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
Link: fmt.Sprintf("%s%s", site, u), Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
Creator: t.CommentAuthor, Creator: t.CommentAuthor,
PubDate: t.CommentDateGmt.Format(timeFormat), PubDate: t.CommentDateGmt.Format(timeFormat),
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId), Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
@ -169,14 +150,15 @@ func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
return return
} }
func CommentsFeed(c context.Context, _ ...any) (r []string, err error) { func commentsFeed(args ...any) (r []string, err error) {
c := args[0].(*gin.Context)
commens := RecentComments(c, 10) commens := RecentComments(c, 10)
rs := templateRss rs := templateRss
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname")) rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
rs.LastBuildDate = time.Now().Format(timeFormat) rs.LastBuildDate = time.Now().Format(timeFormat)
site := wpconfig.GetOption("siteurl") site := wpconfig.GetOption("siteurl")
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site) rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
com, err := GetCommentDataByIds(c, slice.Map(commens, func(t models.Comments) uint64 { com, err := GetCommentByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
return t.CommentId return t.CommentId
})) }))
if nil != err { if nil != err {
@ -193,14 +175,9 @@ func CommentsFeed(c context.Context, _ ...any) (r []string, err error) {
} else { } else {
content = digest.StripTags(t.CommentContent, "") content = digest.StripTags(t.CommentContent, "")
} }
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
if er != nil {
errors.Join(err, er)
}
u = str.Join(site, u)
return rss2.Item{ return rss2.Item{
Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle), Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle),
Link: u, Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
Creator: t.CommentAuthor, Creator: t.CommentAuthor,
Description: desc, Description: desc,
PubDate: t.CommentDateGmt.Format(timeFormat), PubDate: t.CommentDateGmt.Format(timeFormat),

View File

@ -2,16 +2,14 @@ package cache
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"time" "time"
) )
// GetPostMetaByPostIds query func see dao.GetPostMetaByPostIds func GetPostMetaByPostIds(ctx context.Context, ids []uint64) (r []map[string]any, err error) {
func GetPostMetaByPostIds(ctx context.Context, ids []uint64) ([]map[string]any, error) { r, err = postMetaCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
return cachemanager.GetBatchBy[map[string]any]("postMetaData", ctx, ids, time.Second) return
} }
func GetPostMetaByPostId(ctx context.Context, id uint64) (r map[string]any, err error) {
// GetPostMetaByPostId query func see dao.GetPostMetaByPostIds r, err = postMetaCache.GetCache(ctx, id, time.Second, ctx, id)
func GetPostMetaByPostId(ctx context.Context, id uint64) (map[string]any, error) { return
return cachemanager.GetBy[map[string]any]("postMetaData", ctx, id, time.Second)
} }

View File

@ -3,30 +3,26 @@ package cache
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/dao"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin"
"time" "time"
) )
// GetPostById query func see dao.GetPostsByIds
func GetPostById(ctx context.Context, id uint64) (models.Posts, error) { func GetPostById(ctx context.Context, id uint64) (models.Posts, error) {
return cachemanager.GetBy[models.Posts]("postData", ctx, id, time.Second) return postsCache.GetCache(ctx, id, time.Second, ctx, id)
} }
// GetPostsByIds query func see dao.GetPostsByIds
func GetPostsByIds(ctx context.Context, ids []uint64) ([]models.Posts, error) { func GetPostsByIds(ctx context.Context, ids []uint64) ([]models.Posts, error) {
return cachemanager.GetBatchBy[models.Posts]("postData", ctx, ids, time.Second) return postsCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
} }
// SearchPost query func see dao.SearchPostIds
func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) { func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
ids, err := cachemanager.GetBy[dao.PostIds]("searchPostIds", ctx, key, time.Second, args...) ids, err := searchPostIdsCache.GetCache(ctx, key, time.Second, args...)
if err != nil { if err != nil {
return return
} }
@ -35,9 +31,8 @@ func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts,
return return
} }
// PostLists query func see dao.SearchPostIds
func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) { func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts, total int, err error) {
ids, err := cachemanager.GetBy[dao.PostIds]("listPostIds", ctx, key, time.Second, args...) ids, err := postListIdsCache.GetCache(ctx, key, time.Second, args...)
if err != nil { if err != nil {
return return
} }
@ -46,17 +41,15 @@ func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts,
return return
} }
// GetMaxPostId query func see dao.GetMaxPostId func GetMaxPostId(ctx *gin.Context) (uint64, error) {
func GetMaxPostId(ctx context.Context) (uint64, error) { return maxPostIdCache.GetCache(ctx, time.Second, ctx)
return cachemanager.GetVarVal[uint64]("maxPostId", ctx, time.Second)
} }
// RecentPosts query func see dao.RecentPosts
func RecentPosts(ctx context.Context, n int) (r []models.Posts) { func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
nn := n nn := n
feedNum := str.ToInteger(wpconfig.GetOption("posts_per_rss"), 10) feedNum := str.ToInteger(wpconfig.GetOption("posts_per_rss"), 10)
nn = number.Max(n, feedNum) nn = number.Max(n, feedNum)
r, err := cachemanager.GetVarVal[[]models.Posts]("recentPosts", ctx, time.Second, nn) r, err := recentPostsCaches.GetCache(ctx, time.Second, ctx, nn)
if n < len(r) { if n < len(r) {
r = r[:n] r = r[:n]
} }
@ -64,9 +57,8 @@ func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
return return
} }
// GetContextPost query func see dao.GetPostContext
func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.Posts, err error) { func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next models.Posts, err error) {
postCtx, err := cachemanager.GetBy[dao.PostContext]("postContext", ctx, id, time.Second, date) postCtx, err := postContextCache.GetCache(ctx, id, time.Second, ctx, date)
if err != nil { if err != nil {
return models.Posts{}, models.Posts{}, err return models.Posts{}, models.Posts{}, err
} }
@ -75,9 +67,8 @@ func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next
return return
} }
// GetMonthPostIds query func see dao.MonthPost
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.Posts, total int, err error) { func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.Posts, total int, err error) {
res, err := cachemanager.GetBy[[]uint64]("monthPostIds", ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month) res, err := monthPostsCache.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, ctx, year, month)
if err != nil { if err != nil {
return return
} }

View File

@ -4,23 +4,27 @@ import (
"context" "context"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache/cachemanager" "github.com/fthvgb1/wp-go/model"
"time" "time"
) )
// GetUserByName query func see dao.GetUserByName func getUserById(a ...any) (r models.Users, err error) {
ctx := a[0].(context.Context)
uid := a[1].(uint64)
r, err = model.FindOneById[models.Users](ctx, uid)
return
}
func GetUserByName(ctx context.Context, username string) (models.Users, error) { func GetUserByName(ctx context.Context, username string) (models.Users, error) {
return cachemanager.GetBy[models.Users]("usernameToUserData", ctx, username, time.Second) return usersNameCache.GetCache(ctx, username, time.Second, ctx, username)
} }
// GetAllUsername query func see dao.AllUsername func GetAllUsername(ctx context.Context) (map[string]struct{}, error) {
func GetAllUsername(ctx context.Context) (map[string]uint64, error) { return allUsernameCache.GetCache(ctx, time.Second, ctx)
return cachemanager.GetVarVal[map[string]uint64]("allUsername", ctx, time.Second)
} }
// GetUserById query func see dao.GetUserById
func GetUserById(ctx context.Context, uid uint64) models.Users { func GetUserById(ctx context.Context, uid uint64) models.Users {
r, err := cachemanager.GetBy[models.Users]("userData", ctx, uid, time.Second) r, err := usersCache.GetCache(ctx, uid, time.Second, ctx, uid)
logs.IfError(err, "get user", uid) logs.IfError(err, "get user", uid)
return r return r
} }

View File

@ -46,24 +46,23 @@ type Config struct {
} }
type CacheTime struct { type CacheTime struct {
CacheControl time.Duration `yaml:"cacheControl" json:"cacheControl,omitempty"` CacheControl time.Duration `yaml:"cacheControl" json:"cacheControl,omitempty"`
RecentPostCacheTime time.Duration `yaml:"recentPostCacheTime" json:"recentPostCacheTime,omitempty"` RecentPostCacheTime time.Duration `yaml:"recentPostCacheTime" json:"recentPostCacheTime,omitempty"`
CategoryCacheTime time.Duration `yaml:"categoryCacheTime" json:"categoryCacheTime,omitempty"` CategoryCacheTime time.Duration `yaml:"categoryCacheTime" json:"categoryCacheTime,omitempty"`
ArchiveCacheTime time.Duration `yaml:"archiveCacheTime" json:"archiveCacheTime,omitempty"` ArchiveCacheTime time.Duration `yaml:"archiveCacheTime" json:"archiveCacheTime,omitempty"`
ContextPostCacheTime time.Duration `yaml:"contextPostCacheTime" json:"contextPostCacheTime,omitempty"` ContextPostCacheTime time.Duration `yaml:"contextPostCacheTime" json:"contextPostCacheTime,omitempty"`
RecentCommentsCacheTime time.Duration `yaml:"recentCommentsCacheTime" json:"recentCommentsCacheTime,omitempty"` RecentCommentsCacheTime time.Duration `yaml:"recentCommentsCacheTime" json:"recentCommentsCacheTime,omitempty"`
DigestCacheTime time.Duration `yaml:"digestCacheTime" json:"digestCacheTime,omitempty"` DigestCacheTime time.Duration `yaml:"digestCacheTime" json:"digestCacheTime,omitempty"`
PostListCacheTime time.Duration `yaml:"postListCacheTime" json:"postListCacheTime,omitempty"` PostListCacheTime time.Duration `yaml:"postListCacheTime" json:"postListCacheTime,omitempty"`
SearchPostCacheTime time.Duration `yaml:"searchPostCacheTime" json:"searchPostCacheTime,omitempty"` SearchPostCacheTime time.Duration `yaml:"searchPostCacheTime" json:"searchPostCacheTime,omitempty"`
MonthPostCacheTime time.Duration `yaml:"monthPostCacheTime" json:"monthPostCacheTime,omitempty"` MonthPostCacheTime time.Duration `yaml:"monthPostCacheTime" json:"monthPostCacheTime,omitempty"`
PostDataCacheTime time.Duration `yaml:"postDataCacheTime" json:"postDataCacheTime,omitempty"` PostDataCacheTime time.Duration `yaml:"postDataCacheTime" json:"postDataCacheTime,omitempty"`
PostCommentsCacheTime time.Duration `yaml:"postCommentsCacheTime" json:"postCommentsCacheTime,omitempty"` PostCommentsCacheTime time.Duration `yaml:"postCommentsCacheTime" json:"postCommentsCacheTime,omitempty"`
CrontabClearCacheTime time.Duration `yaml:"crontabClearCacheTime" json:"crontabClearCacheTime,omitempty"` CrontabClearCacheTime time.Duration `yaml:"crontabClearCacheTime" json:"crontabClearCacheTime,omitempty"`
MaxPostIdCacheTime time.Duration `yaml:"maxPostIdCacheTime" json:"maxPostIdCacheTime,omitempty"` MaxPostIdCacheTime time.Duration `yaml:"maxPostIdCacheTime" json:"maxPostIdCacheTime,omitempty"`
UserInfoCacheTime time.Duration `yaml:"userInfoCacheTime" json:"userInfoCacheTime,omitempty"` UserInfoCacheTime time.Duration `yaml:"userInfoCacheTime" json:"userInfoCacheTime,omitempty"`
CommentsCacheTime time.Duration `yaml:"commentsCacheTime" json:"commentsCacheTime,omitempty"` CommentsCacheTime time.Duration `yaml:"commentsCacheTime" json:"commentsCacheTime,omitempty"`
SleepTime []time.Duration `yaml:"sleepTime" json:"sleepTime,omitempty"` SleepTime []time.Duration `yaml:"sleepTime" json:"sleepTime,omitempty"`
CommentsIncreaseUpdateTime time.Duration `yaml:"commentsIncreaseUpdateTime" json:"commentsIncreaseUpdateTime"`
} }
type Ssl struct { type Ssl struct {
@ -85,14 +84,6 @@ type Mysql struct {
Pool Pool `yaml:"pool" json:"pool"` Pool Pool `yaml:"pool" json:"pool"`
} }
func GetCustomizedConfig[T any]() (T, error) {
var r T
err := yaml.Unmarshal(fileData.Load(), &r)
return r, err
}
var fileData = safety.NewVar([]byte{})
func InitConfig(conf string) error { func InitConfig(conf string) error {
if conf == "" { if conf == "" {
conf = "config.yaml" conf = "config.yaml"
@ -104,7 +95,6 @@ func InitConfig(conf string) error {
if err != nil { if err != nil {
return err return err
} }
defer get.Body.Close()
file, err = io.ReadAll(get.Body) file, err = io.ReadAll(get.Body)
} else { } else {
file, err = os.ReadFile(conf) file, err = os.ReadFile(conf)
@ -112,7 +102,6 @@ func InitConfig(conf string) error {
if err != nil { if err != nil {
return err return err
} }
fileData.Store(file)
var c Config var c Config
err = yaml.Unmarshal(file, &c) err = yaml.Unmarshal(file, &c)
if err != nil { if err != nil {

View File

@ -2,21 +2,17 @@ package dao
import ( import (
"context" "context"
"database/sql"
"errors"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
) )
// RecentComments // RecentComments
// param context.Context // param context.Context
func RecentComments(ctx context.Context, a ...any) (r []models.Comments, err error) { func RecentComments(a ...any) (r []models.Comments, err error) {
n := helper.ParseArgs(10, a...) ctx := a[0].(context.Context)
n := a[1].(int)
return model.Finds[models.Comments](ctx, model.Conditions( return model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{ model.Where(model.SqlBuilder{
{"comment_approved", "1"}, {"comment_approved", "1"},
@ -32,8 +28,10 @@ func RecentComments(ctx context.Context, a ...any) (r []models.Comments, err err
// PostComments // PostComments
// param1 context.Context // param1 context.Context
// param2 postId // param2 postId
func PostComments(ctx context.Context, postId uint64, _ ...any) ([]uint64, error) { func PostComments(args ...any) ([]uint64, error) {
r, err := model.ChunkFind[models.Comments](ctx, 300, model.Conditions( ctx := args[0].(context.Context)
postId := args[1].(uint64)
r, err := model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{ model.Where(model.SqlBuilder{
{"comment_approved", "1"}, {"comment_approved", "1"},
{"comment_post_ID", "=", number.IntToString(postId), "int"}, {"comment_post_ID", "=", number.IntToString(postId), "int"},
@ -52,132 +50,17 @@ func PostComments(ctx context.Context, postId uint64, _ ...any) ([]uint64, error
}), err }), err
} }
func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]models.Comments, error) { func GetCommentByIds(args ...any) (map[uint64]models.Comments, error) {
if len(ids) < 1 { ctx := args[0].(context.Context)
return nil, nil ids := args[1].([]uint64)
}
m := make(map[uint64]models.Comments) m := make(map[uint64]models.Comments)
off := 0 r, err := model.SimpleFind[models.Comments](ctx, model.SqlBuilder{
for { {"comment_ID", "in", ""}, {"comment_approved", "1"},
id := slice.Slice(ids, off, 500) }, "*", slice.ToAnySlice(ids))
if len(id) < 1 {
break
}
r, err := model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{
{"comment_ID", "in", ""}, {"comment_approved", "1"},
}),
model.Fields("*"),
model.In(slice.ToAnySlice(id)),
))
if err != nil {
return m, err
}
for _, comments := range r {
m[comments.CommentId] = comments
}
off += 500
}
return m, nil
}
func CommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) {
n, err := model.GetField[models.Posts](ctx, "comment_count", model.Conditions(
model.Where(model.SqlBuilder{{"ID", "=", number.IntToString(postId), "int"}})))
if err != nil { if err != nil {
return 0, err return m, err
} }
return str.ToInteger(n, 0), err return slice.SimpleToMap(r, func(t models.Comments) uint64 {
}
func PostTopCommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) {
v, err := model.GetField[models.Comments](ctx, "count(*) num", model.Conditions(
model.Where(postTopCommentNumWhere(postId)),
))
if err != nil {
return 0, err
}
return str.ToInteger(v, 0), nil
}
func postTopCommentNumWhere(postId uint64) model.SqlBuilder {
threadComments := wpconfig.GetOption("thread_comments")
pageComments := wpconfig.GetOption("page_comments")
where := model.SqlBuilder{
{"comment_approved", "1"},
{"comment_post_ID", "=", number.IntToString(postId), "int"},
}
if pageComments != "1" || threadComments == "1" || "1" == wpconfig.GetOption("thread_comments_depth") {
where = append(where, []string{"comment_parent", "0"})
}
return where
}
func PostCommentsIds(ctx context.Context, postId uint64, page, limit, totalRaw int, order string) ([]uint64, int, error) {
condition := model.Conditions(
model.Where(postTopCommentNumWhere(postId)),
model.TotalRaw(totalRaw),
model.Fields("comment_ID"),
model.Order(model.SqlBuilder{
{"comment_date_gmt", order},
{"comment_ID", "asc"},
}),
)
var r []models.Comments
var total int
var err error
if limit < 1 {
r, err = model.ChunkFind[models.Comments](ctx, 300, condition)
total = len(r)
} else {
r, total, err = model.Pagination[models.Comments](ctx, condition, page, limit)
}
if err != nil && errors.Is(err, sql.ErrNoRows) {
err = nil
}
return slice.Map(r, func(t models.Comments) uint64 {
return t.CommentId return t.CommentId
}), total, err }), err
}
func CommentChildren(ctx context.Context, commentIds []uint64, _ ...any) (r map[uint64][]uint64, err error) {
rr, err := model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{
{"comment_parent", "in", ""},
{"comment_approved", "1"},
}),
model.In(slice.ToAnySlice(commentIds)),
model.Fields("comment_ID,comment_parent"),
))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return
}
rrr := slice.GroupBy(rr, func(v models.Comments) (uint64, uint64) {
return v.CommentParent, v.CommentId
})
r = make(map[uint64][]uint64)
for _, id := range commentIds {
r[id] = rrr[id]
}
return
}
func PreviousCommentNum(ctx context.Context, commentId, postId uint64) (int, error) {
v, err := model.GetField[models.Comments](ctx, "count(*)", model.Conditions(
model.Where(model.SqlBuilder{
{"comment_approved", "1"},
{"comment_post_ID", "=", number.IntToString(postId), "int"},
{"comment_ID", "<", number.IntToString(commentId), "int"},
{"comment_parent", "=", "0", "int"},
}),
))
if err != nil {
return 0, err
}
return str.ToInteger(v, 0), nil
} }

View File

@ -21,13 +21,17 @@ type PostContext struct {
Next models.Posts Next models.Posts
} }
func CategoriesAndTags(ctx context.Context, t string, _ ...any) (terms []models.TermsMy, err error) { func CategoriesAndTags(a ...any) (terms []models.TermsMy, err error) {
ctx := a[0].(context.Context)
t, ok := a[1].(string)
var in = []any{"category", "post_tag"} var in = []any{"category", "post_tag"}
switch t { if ok {
case constraints.Category: switch t {
in = []any{"category"} case constraints.Category:
case constraints.Tag: in = []any{"category"}
in = []any{"post_tag"} case constraints.Tag:
in = []any{"post_tag"}
}
} }
w := model.SqlBuilder{ w := model.SqlBuilder{
{"tt.taxonomy", "in", ""}, {"tt.taxonomy", "in", ""},

View File

@ -11,8 +11,10 @@ import (
"strconv" "strconv"
) )
func GetPostMetaByPostIds(ctx context.Context, ids []uint64, _ ...any) (r map[uint64]map[string]any, err error) { func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) {
r = make(map[uint64]map[string]any) r = make(map[uint64]map[string]any)
ctx := args[0].(context.Context)
ids := args[1].([]uint64)
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions( rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
model.Where(model.SqlBuilder{{"post_id", "in", ""}}), model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
model.In(slice.ToAnySlice(ids)), model.In(slice.ToAnySlice(ids)),

View File

@ -3,7 +3,6 @@ package dao
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/pkg/models/relation" "github.com/fthvgb1/wp-go/app/pkg/models/relation"
@ -16,8 +15,10 @@ import (
"time" "time"
) )
func GetPostsByIds(ctx context.Context, ids []uint64, _ ...any) (m map[uint64]models.Posts, err error) { func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
ctx := a[0].(context.Context)
m = make(map[uint64]models.Posts) m = make(map[uint64]models.Posts)
ids := a[1].([]uint64)
q := model.Conditions( q := model.Conditions(
model.Where(model.SqlBuilder{{"Id", "in", ""}}), model.Where(model.SqlBuilder{{"Id", "in", ""}}),
model.Join(model.SqlBuilder{ model.Join(model.SqlBuilder{
@ -98,10 +99,11 @@ func GetPostsByIds(ctx context.Context, ids []uint64, _ ...any) (m map[uint64]mo
return return
} }
func SearchPostIds(ctx context.Context, _ string, args ...any) (ids PostIds, err error) { func SearchPostIds(args ...any) (ids PostIds, err error) {
q := args[0].(*model.QueryCondition) ctx := args[0].(context.Context)
page := args[1].(int) q := args[1].(*model.QueryCondition)
pageSize := args[2].(int) page := args[2].(int)
pageSize := args[3].(int)
q.Fields = "ID" q.Fields = "ID"
res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize) res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize)
for _, posts := range res { for _, posts := range res {
@ -116,19 +118,11 @@ func SearchPostIds(ctx context.Context, _ string, args ...any) (ids PostIds, err
return return
} }
func GetMaxPostId(ctx context.Context, _ ...any) (uint64, error) { func GetMaxPostId(a ...any) (uint64, error) {
r, err := model.Finds[models.Posts](ctx, ctx := a[0].(context.Context)
model.Conditions( r, err := model.SimpleFind[models.Posts](ctx,
model.Where(model.SqlBuilder{ model.SqlBuilder{{"post_type", "post"}, {"post_status", "publish"}},
{"post_type", "post"}, "max(ID) ID",
{"post_status", "publish"}},
),
model.Fields("ID"),
model.Order(model.SqlBuilder{
{"ID", "desc"},
}),
model.Limit(1),
),
) )
var id uint64 var id uint64
if len(r) > 0 { if len(r) > 0 {
@ -137,8 +131,9 @@ func GetMaxPostId(ctx context.Context, _ ...any) (uint64, error) {
return id, err return id, err
} }
func RecentPosts(ctx context.Context, a ...any) (r []models.Posts, err error) { func RecentPosts(a ...any) (r []models.Posts, err error) {
num := helper.ParseArgs(10, a...) ctx := a[0].(context.Context)
num := a[1].(int)
r, err = model.Finds[models.Posts](ctx, model.Conditions( r, err = model.Finds[models.Posts](ctx, model.Conditions(
model.Where(model.SqlBuilder{ model.Where(model.SqlBuilder{
{"post_type", "post"}, {"post_type", "post"},
@ -151,14 +146,15 @@ func RecentPosts(ctx context.Context, a ...any) (r []models.Posts, err error) {
return return
} }
func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, err error) { func GetPostContext(arg ...any) (r PostContext, err error) {
t := arg[0].(time.Time) ctx := arg[0].(context.Context)
t := arg[1].(time.Time)
next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{ next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{
{"post_date", ">", t.Format("2006-01-02 15:04:05")}, {"post_date", ">", t.Format("2006-01-02 15:04:05")},
{"post_status", "in", ""}, {"post_status", "in", ""},
{"post_type", "post"}, {"post_type", "post"},
}, "ID,post_title,post_password", nil, []any{"publish"}) }, "ID,post_title,post_password", nil, []any{"publish"})
if errors.Is(err, sql.ErrNoRows) { if err == sql.ErrNoRows {
err = nil err = nil
} }
if err != nil { if err != nil {
@ -169,7 +165,7 @@ func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, e
{"post_status", "in", ""}, {"post_status", "in", ""},
{"post_type", "post"}, {"post_type", "post"},
}, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"}) }, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
if errors.Is(err, sql.ErrNoRows) { if err == sql.ErrNoRows {
err = nil err = nil
} }
if err != nil { if err != nil {
@ -182,8 +178,9 @@ func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, e
return return
} }
func MonthPost(ctx context.Context, _ string, args ...any) (r []uint64, err error) { func MonthPost(args ...any) (r []uint64, err error) {
year, month := args[0].(string), args[1].(string) ctx := args[0].(context.Context)
year, month := args[1].(string), args[2].(string)
where := model.SqlBuilder{ where := model.SqlBuilder{
{"post_type", "post"}, {"post_type", "post"},
{"post_status", "publish"}, {"post_status", "publish"},

View File

@ -7,26 +7,31 @@ import (
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
) )
func GetUserById(ctx context.Context, uid uint64, _ ...any) (r models.Users, err error) { func GetUserById(a ...any) (r models.Users, err error) {
ctx := a[0].(context.Context)
uid := a[1].(uint64)
r, err = model.FindOneById[models.Users](ctx, uid) r, err = model.FindOneById[models.Users](ctx, uid)
return return
} }
func AllUsername(ctx context.Context, _ ...any) (map[string]uint64, error) { func AllUsername(a ...any) (map[string]struct{}, error) {
ctx := a[0].(context.Context)
r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{ r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{
{"user_status", "=", "0", "int"}, {"user_status", "=", "0", "int"},
}, "display_name,ID") }, "user_login")
if err != nil { if err != nil {
return nil, err return nil, err
} }
return slice.ToMap(r, func(t models.Users) (string, uint64) { return slice.ToMap(r, func(t models.Users) (string, struct{}) {
return t.DisplayName, t.Id return t.UserLogin, struct{}{}
}, true), nil }, true), nil
} }
func GetUserByName(ctx context.Context, u string, _ ...any) (r models.Users, err error) { func GetUserByName(a ...any) (r models.Users, err error) {
u := a[1].(string)
ctx := a[0].(context.Context)
r, err = model.FirstOne[models.Users](ctx, model.SqlBuilder{{ r, err = model.FirstOne[models.Users](ctx, model.SqlBuilder{{
"display_name", u, "user_login", u,
}}, "*", nil) }}, "*", nil)
return return
} }

View File

@ -3,21 +3,14 @@ package db
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"log" "log"
"runtime"
) )
var safeDb = safety.NewVar[*sqlx.DB](nil) var safeDb = safety.NewVar[*sqlx.DB](nil)
var showQuerySql func() bool
func GetSqlxDB() *sqlx.DB {
return safeDb.Load()
}
func InitDb() (*safety.Var[*sqlx.DB], error) { func InitDb() (*safety.Var[*sqlx.DB], error) {
c := config.GetConfig() c := config.GetConfig()
@ -43,11 +36,6 @@ func InitDb() (*safety.Var[*sqlx.DB], error) {
if preDb != nil { if preDb != nil {
_ = preDb.Close() _ = preDb.Close()
} }
if showQuerySql == nil {
showQuerySql = reload.BuildFnVal("showQuerySql", false, func() bool {
return config.GetConfig().ShowQuerySql
})
}
return safeDb, err return safeDb, err
} }
@ -56,20 +44,14 @@ func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
nil, nil,
nil)) nil))
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error { model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() { if config.GetConfig().ShowQuerySql {
_, f, l, _ := runtime.Caller(5) go log.Println(model.FormatSql(s, args...))
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
} }
return query.Selects(ctx, a, s, args...) return query.Selects(ctx, a, s, args...)
}) })
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error { model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() { if config.GetConfig().ShowQuerySql {
_, f, l, _ := runtime.Caller(5) go log.Println(model.FormatSql(s, args...))
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
} }
return query.Gets(ctx, a, s, args...) return query.Gets(ctx, a, s, args...)
}) })

View File

@ -15,24 +15,20 @@ var logs = safety.NewVar[*log.Logger](nil)
var logFile = safety.NewVar[*os.File](nil) var logFile = safety.NewVar[*os.File](nil)
func InitLogger() error { func InitLogger() error {
c := config.GetConfig()
return SetLogger(c.LogOutput)
}
func SetLogger(loggerFile string) error {
if loggerFile == "" {
loggerFile = "stderr"
}
preFD := logFile.Load() preFD := logFile.Load()
l := &log.Logger{} l := &log.Logger{}
c := config.GetConfig()
if c.LogOutput == "" {
c.LogOutput = "stderr"
}
var out io.Writer var out io.Writer
switch loggerFile { switch c.LogOutput {
case "stdout": case "stdout":
out = os.Stdout out = os.Stdout
case "stderr": case "stderr":
out = os.Stderr out = os.Stderr
default: default:
file, err := os.OpenFile(loggerFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0777) file, err := os.OpenFile(c.LogOutput, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0777)
if err != nil { if err != nil {
return err return err
} }

View File

@ -19,8 +19,7 @@ type Comments struct {
CommentParent uint64 `gorm:"column:comment_parent" db:"comment_parent" json:"comment_parent" form:"comment_parent"` CommentParent uint64 `gorm:"column:comment_parent" db:"comment_parent" json:"comment_parent" form:"comment_parent"`
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"` UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
//扩展字段 //扩展字段
PostTitle string `db:"post_title"` PostTitle string `db:"post_title"`
UpdateTime time.Time `gorm:"update_time" form:"update_time" json:"update_time" db:"update_time"`
} }
func (w Comments) PrimaryKey() string { func (w Comments) PrimaryKey() string {
@ -30,8 +29,3 @@ func (w Comments) PrimaryKey() string {
func (w Comments) Table() string { func (w Comments) Table() string {
return "wp_comments" return "wp_comments"
} }
type PostComments struct {
Comments
Children []uint64
}

View File

@ -1,10 +1,8 @@
package plugins package plugins
import ( import (
"context"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -14,12 +12,11 @@ import (
type CommentHandler struct { type CommentHandler struct {
*gin.Context *gin.Context
comments []*Comments comments []*Comments
maxDepth int maxDepth int
depth int depth int
isTls bool isTls bool
i CommentHtml i CommentHtml
isThreadComments bool
} }
type Comments struct { type Comments struct {
@ -28,8 +25,8 @@ type Comments struct {
} }
type CommentHtml interface { type CommentHtml interface {
FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string Sort(i, j *Comments) bool
FloorOrder(i, j models.Comments) bool FormatLi(c *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string
} }
func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string { func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
@ -49,21 +46,15 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m
isTls: isTls, isTls: isTls,
i: i, i: i,
} }
return h.formatComment(h.comments) return h.formatComment(h.comments, true)
} }
func (d CommentHandler) formatComment(comments []*Comments) (html string) { func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html string) {
s := str.NewBuilder() s := str.NewBuilder()
if d.depth >= d.maxDepth { if d.depth > d.maxDepth {
comments = d.findComments(comments) comments = d.findComments(comments)
} }
order := wpconfig.GetOption("comment_order") slice.Sort(comments, d.i.Sort)
slice.Sort(comments, func(i, j *Comments) bool {
if order == "asc" {
return i.CommentDate.Sub(j.CommentDate) < 0
}
return i.CommentDate.Sub(j.CommentDate) > 0
})
for i, comment := range comments { for i, comment := range comments {
eo := "even" eo := "even"
if (i+1)%2 == 0 { if (i+1)%2 == 0 {
@ -75,11 +66,13 @@ func (d CommentHandler) formatComment(comments []*Comments) (html string) {
parent = "parent" parent = "parent"
fl = true fl = true
} }
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.maxDepth, 1, d.isTls, d.isThreadComments, eo, parent)) s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.isTls, eo, parent))
if fl { if fl {
d.depth++ d.depth++
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`) s.WriteString(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
d.depth-- if isTop {
d.depth = 1
}
} }
s.WriteString("</li><!-- #comment-## -->") s.WriteString("</li><!-- #comment-## -->")
} }
@ -92,7 +85,7 @@ func (d CommentHandler) findComments(comments []*Comments) []*Comments {
var r []*Comments var r []*Comments
for _, comment := range comments { for _, comment := range comments {
tmp := *comment tmp := *comment
tmp.Children = nil comment.Children = nil
r = append(r, &tmp) r = append(r, &tmp)
if len(comment.Children) > 0 { if len(comment.Children) > 0 {
t := d.findComments(comment.Children) t := d.findComments(comment.Children)
@ -142,35 +135,16 @@ func CommentRender() CommonCommentFormat {
type CommonCommentFormat struct { type CommonCommentFormat struct {
} }
func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { func (c CommonCommentFormat) Sort(i, j *Comments) bool {
return FormatLi(li, m, respondsFn, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent) order := wpconfig.GetOption("comment_order")
} if order == "asc" {
return i.CommentDate.UnixNano() < j.CommentDate.UnixNano()
func (c CommonCommentFormat) FloorOrder(i, j models.Comments) bool {
return i.CommentId > j.CommentId
}
type RespondFn func(m models.Comments, depth, maxDepth int, isThreadComments bool) string
var respondsFn = Responds(respondTml)
func RespondsFn() RespondFn {
return respondsFn
}
func Responds(respondTml string) RespondFn {
return func(m models.Comments, depth, maxDepth int, isThreadComments bool) string {
if !isThreadComments || depth >= maxDepth {
return ""
}
pId := number.IntToString(m.CommentPostId)
cId := number.IntToString(m.CommentId)
return str.Replace(respondTml, map[string]string{
"{{PostId}}": pId,
"{{CommentId}}": cId,
"{{CommentAuthor}}": m.CommentAuthor,
})
} }
return i.CommentDate.UnixNano() > j.CommentDate.UnixNano()
}
func (c CommonCommentFormat) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
return FormatLi(CommonLi(), ctx, m, depth, isTls, eo, parent)
} }
var li = ` var li = `
@ -182,11 +156,14 @@ var li = `
src="{{Gravatar}}" src="{{Gravatar}}"
srcset="{{Gravatar}} 2x" srcset="{{Gravatar}} 2x"
class="avatar avatar-56 photo" height="56" width="56" loading="lazy"> class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
<b class="fn">{{CommentAuthor}}</b> <b class="fn">
<a href="{{CommentAuthorUrl}}" rel="external nofollow ugc"
class="url">{{CommentAuthor}}</a>
</b>
<span class="says">说道</span></div><!-- .comment-author --> <span class="says">说道</span></div><!-- .comment-author -->
<div class="comment-metadata"> <div class="comment-metadata">
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}"> <a href="/p/{{PostId}}#comment-{{CommentId}}">
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time> <time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
</a></div><!-- .comment-metadata --> </a></div><!-- .comment-metadata -->
@ -196,34 +173,30 @@ var li = `
<p>{{CommentContent}}</p> <p>{{CommentContent}}</p>
</div><!-- .comment-content --> </div><!-- .comment-content -->
{{respond}} <div class="reply">
</article><!-- .comment-body -->
`
var respondTml = `<div class="reply">
<a rel="nofollow" class="comment-reply-link" <a rel="nofollow" class="comment-reply-link"
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}" href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond" data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
data-replyto="回复给{{CommentAuthor}}" data-replyto="回复给{{CommentAuthor}}"
aria-label="回复给{{CommentAuthor}}">回复</a> aria-label="回复给{{CommentAuthor}}">回复</a>
</div>` </div>
</article><!-- .comment-body -->
func FormatLi(li string, comments models.Comments, respond RespondFn, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string { `
func FormatLi(li string, c *gin.Context, comments models.Comments, depth int, isTls bool, eo, parent string) string {
for k, v := range map[string]string{ for k, v := range map[string]string{
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10), "{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
"{{Depth}}": strconv.Itoa(currentDepth), "{{Depth}}": strconv.Itoa(depth),
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls), "{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl, "{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
"{{CommentAuthor}}": comments.CommentAuthor, "{{CommentAuthor}}": comments.CommentAuthor,
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10), "{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
"{{page}}": strconv.Itoa(page),
"{{CommentDateGmt}}": comments.CommentDateGmt.String(), "{{CommentDateGmt}}": comments.CommentDateGmt.String(),
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"), "{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
"{{CommentContent}}": comments.CommentContent, "{{CommentContent}}": comments.CommentContent,
"{{eo}}": eo, "{{eo}}": eo,
"{{parent}}": parent, "{{parent}}": parent,
"{{respond}}": respond(comments, currentDepth, maxDepth, isThreadComments),
} { } {
li = strings.Replace(li, k, v, -1) li = strings.Replace(li, k, v, -1)
} }

View File

@ -1,141 +0,0 @@
package main
import (
"context"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/dao"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/helper/strings"
"github.com/redis/go-redis/v9"
"strconv"
str "strings"
"time"
)
type RdmCache[K comparable, V any] struct {
expired func() time.Duration
rdb *redis.Client
keyFn func(K) string
name string
resFn func(map[string]string) V
saveData func(V) map[string]string
}
func (r *RdmCache[K, V]) SetExpiredTime(f func() time.Duration) {
r.expired = f
}
func (r *RdmCache[K, V]) Get(ctx context.Context, key K) (V, bool) {
var re V
result, err := r.rdb.Exists(ctx, r.keyFn(key)).Result()
if result <= 0 || err != nil {
return re, false
}
rr, err := r.rdb.HGetAll(ctx, r.keyFn(key)).Result()
if errors.Is(err, redis.Nil) {
return re, false
}
if err != nil {
return re, false
}
return r.resFn(rr), true
}
func (r *RdmCache[K, V]) Set(ctx context.Context, key K, val V) {
k := r.keyFn(key)
result, err := r.rdb.HSet(ctx, k, r.saveData(val)).Result()
b, err := r.rdb.Expire(ctx, k, r.expired()).Result()
if err != nil {
fmt.Println(result, b, err)
return
}
fmt.Println(result, err)
}
func (r *RdmCache[K, V]) GetExpireTime(ctx context.Context) time.Duration {
return r.expired()
}
func (r *RdmCache[K, V]) Ttl(ctx context.Context, key K) time.Duration {
result, err := r.rdb.TTL(ctx, r.keyFn(key)).Result()
if err != nil {
return 0
}
return result
}
func (r *RdmCache[K, V]) Flush(ctx context.Context) {
fmt.Println("flush redis cache")
}
func (r *RdmCache[K, V]) Del(ctx context.Context, key ...K) {
r.rdb.Del(ctx, slice.Map(key, r.keyFn)...)
}
func (r *RdmCache[K, V]) ClearExpired(ctx context.Context) {
fmt.Println("clear expired redis cache")
}
// RedisCache use step:
// 1 go build -gcflags all="-N -l" --race -buildmode=plugin -o redisCache.so main.go && cp ./redisCache.so ../wp-go/plugins/
// 2 wp-go config add redisCache plugin
func RedisCache(h *wp.Handle) {
vv, ok := cachemanager.GetMapCache[string, dao.PostIds]("listPostIds")
if ok {
_, ok := any(vv.Cache).(*RdmCache[string, dao.PostIds])
if ok {
return
}
}
reload.AppendOnceFn(func() {
err := cachemanager.SetMapCache("listPostIds", vv)
if err != nil {
logs.Error(err, "set recovery listPostIds cache err")
} else {
cachemanager.PushOrSetFlush(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush})
cachemanager.PushOrSetClearExpired(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush})
fmt.Println("recovery listPostIds cache ok")
}
})
rdm := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
r := RdmCache[string, dao.PostIds]{
expired: func() time.Duration {
return time.Minute
},
keyFn: func(u string) string {
return strings.Join("postIds:", u)
},
rdb: rdm,
name: "",
resFn: func(m map[string]string) dao.PostIds {
return dao.PostIds{
Ids: slice.Map(str.Split(m["ids"], ","), strings.ToInt[uint64]),
Length: strings.ToInt[int](m["length"]),
}
},
saveData: func(ids dao.PostIds) map[string]string {
t := slice.Map(ids.Ids, number.IntToString[uint64])
return map[string]string{
"ids": str.Join(t, ","),
"length": strconv.Itoa(ids.Length),
}
},
}
cachemanager.NewMapCache[string, dao.PostIds](&r, nil, dao.SearchPostIds, config.GetConfig().CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
return config.GetConfig().CacheTime.PostListCacheTime
})
fmt.Println("redis cache inited ok")
}

View File

@ -1,50 +0,0 @@
module redisCache
go 1.21
require (
github.com/fthvgb1/wp-go v0.0.0-20231210111549-d72bed0c8c4e
github.com/redis/go-redis/v9 v9.3.0
)
replace github.com/fthvgb1/wp-go v0.0.0-20231210111549-d72bed0c8c4e => ../wp-go
require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/elliotchance/phpserialize v1.3.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sessions v0.0.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -3,134 +3,35 @@ package plugins
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache/cachemanager" "github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/digest" "github.com/fthvgb1/wp-go/plugin/digest"
"github.com/fthvgb1/wp-go/safety"
"regexp" "regexp"
"strings" "strings"
"time" "time"
"unicode/utf8"
) )
var digestCache *cache.MapCache[uint64, string]
var more = regexp.MustCompile("<!--more(.*?)?-->") var more = regexp.MustCompile("<!--more(.*?)?-->")
var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->") var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->")
type DigestConfig struct {
DigestWordCount int `yaml:"digestWordCount"`
DigestAllowTag string `yaml:"digestAllowTag"`
DigestRegex string `yaml:"digestRegex"`
DigestTagOccupyNum []struct {
Tag string `yaml:"tag"`
Num int `yaml:"num"`
ChuckOvered bool `yaml:"chuckOvered"`
EscapeCharacter []struct {
Tags string `yaml:"tags"`
Character []string `yaml:"character"`
Num int `yaml:"num"`
ChuckOvered bool `yaml:"chuckOvered"`
} `yaml:"escapeCharacter"`
} `yaml:"digestTagOccupyNum"`
specialSolve map[string]digest.SpecialSolveConf
}
var digestConfig *safety.Var[DigestConfig]
func InitDigestCache() { func InitDigestCache() {
cachemanager.NewMemoryMapCache(nil, digestRaw, config.GetConfig().CacheTime.DigestCacheTime, "digestPlugin", func() time.Duration { digestCache = cachemanager.MapCacheBy[uint64](digestRaw, config.GetConfig().CacheTime.DigestCacheTime)
return config.GetConfig().CacheTime.DigestCacheTime
})
digestConfig = reload.VarsBy(func() DigestConfig {
c, err := config.GetCustomizedConfig[DigestConfig]()
if err != nil {
logs.Error(err, "get digest config fail")
c.DigestWordCount = config.GetConfig().DigestWordCount
c.DigestAllowTag = config.GetConfig().DigestAllowTag
return c
}
if c.DigestRegex != "" {
digest.SetQutos(c.DigestRegex)
}
if len(c.DigestTagOccupyNum) <= 1 {
return c
}
c.specialSolve = ParseDigestConf(c)
return c
}, "digestConfig")
}
func ParseDigestConf(c DigestConfig) map[string]digest.SpecialSolveConf {
specialSolve := map[string]digest.SpecialSolveConf{}
for _, item := range c.DigestTagOccupyNum {
tags := strings.Split(strings.ReplaceAll(item.Tag, " ", ""), "<")
for _, tag := range tags {
if tag == "" {
continue
}
ec := make(map[rune]digest.SpecialSolve)
specialTags := make(map[string]digest.SpecialSolve)
tag = str.Join("<", tag)
if len(item.EscapeCharacter) > 0 {
for _, esc := range item.EscapeCharacter {
for _, i := range esc.Character {
s := []rune(i)
if len(s) == 1 {
ec[s[0]] = digest.SpecialSolve{
Num: esc.Num,
ChuckOvered: esc.ChuckOvered,
}
}
}
if esc.Tags == "" {
continue
}
tagss := strings.Split(strings.ReplaceAll(esc.Tags, " ", ""), "<")
for _, t := range tagss {
if t == "" {
continue
}
t = str.Join("<", t)
specialTags[t] = digest.SpecialSolve{
Num: esc.Num,
ChuckOvered: esc.ChuckOvered,
}
}
}
}
v, ok := specialSolve[tag]
if !ok {
specialSolve[tag] = digest.SpecialSolveConf{
Num: item.Num,
ChuckOvered: item.ChuckOvered,
EscapeCharacter: ec,
Tags: specialTags,
}
continue
}
v.Num = item.Num
v.ChuckOvered = item.ChuckOvered
v.EscapeCharacter = maps.Merge(v.EscapeCharacter, ec)
v.Tags = maps.Merge(v.Tags, specialTags)
specialSolve[tag] = v
}
}
return specialSolve
} }
func RemoveWpBlock(s string) string { func RemoveWpBlock(s string) string {
return removeWpBlock.ReplaceAllString(s, "") return removeWpBlock.ReplaceAllString(s, "")
} }
func digestRaw(ctx context.Context, id uint64, arg ...any) (string, error) { func digestRaw(arg ...any) (string, error) {
ctx := arg[0].(context.Context)
s := arg[1].(string) s := arg[1].(string)
id := arg[2].(uint64)
limit := arg[3].(int) limit := arg[3].(int)
if limit < 0 { if limit < 0 {
return s, nil return s, nil
@ -146,22 +47,12 @@ func digestRaw(ctx context.Context, id uint64, arg ...any) (string, error) {
func Digests(content string, id uint64, limit int, fn func(id uint64, content, closeTag string) string) string { func Digests(content string, id uint64, limit int, fn func(id uint64, content, closeTag string) string) string {
closeTag := "" closeTag := ""
content = RemoveWpBlock(content) content = RemoveWpBlock(content)
c := digestConfig.Load() tag := config.GetConfig().DigestAllowTag
tag := c.DigestAllowTag
if tag == "" { if tag == "" {
tag = "<a><b><blockquote><br><cite><code><dd><del><div><dl><dt><em><h1><h2><h3><h4><h5><h6><i><img><li><ol><p><pre><span><strong><ul>" tag = "<a><b><blockquote><br><cite><code><dd><del><div><dl><dt><em><h1><h2><h3><h4><h5><h6><i><img><li><ol><p><pre><span><strong><ul>"
} }
content = digest.StripTags(content, tag) content = digest.StripTags(content, tag)
length := utf8.RuneCountInString(content) + 1 content, closeTag = digest.Html(content, limit)
if length <= limit {
return content
}
if len(c.specialSolve) > 0 {
content, closeTag = digest.CustomizeHtml(content, limit, c.specialSolve)
} else {
content, closeTag = digest.Html(content, limit)
}
if fn == nil { if fn == nil {
return PostsMore(id, content, closeTag) return PostsMore(id, content, closeTag)
} }
@ -178,7 +69,7 @@ func PostsMore(id uint64, content, closeTag string) string {
} }
func Digest(ctx context.Context, post *models.Posts, limit int) { func Digest(ctx context.Context, post *models.Posts, limit int) {
content, _ := cachemanager.GetBy[string]("digestPlugin", ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit) content, _ := digestCache.GetCache(ctx, post.Id, time.Second, ctx, post.PostContent, post.Id, limit)
post.PostContent = content post.PostContent = content
} }

View File

@ -2,12 +2,8 @@ package plugins
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"net/url"
"regexp" "regexp"
"strconv"
"strings" "strings"
) )
@ -22,13 +18,6 @@ type PageEle struct {
func TwentyFifteenPagination() PageEle { func TwentyFifteenPagination() PageEle {
return twentyFifteen return twentyFifteen
} }
func TwentyFifteenCommentPagination() CommentPageEle {
return twentyFifteenComment
}
type CommentPageEle struct {
PageEle
}
var twentyFifteen = PageEle{ var twentyFifteen = PageEle{
PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`, PrevEle: `<a class="prev page-numbers" href="%s">上一页</a>`,
@ -39,12 +28,6 @@ var twentyFifteen = PageEle{
CurrentEle: `<span aria-current="page" class="page-numbers current"> CurrentEle: `<span aria-current="page" class="page-numbers current">
<span class="meta-nav screen-reader-text"> </span>%d</span>`, <span class="meta-nav screen-reader-text"> </span>%d</span>`,
} }
var twentyFifteenComment = CommentPageEle{
PageEle{
PrevEle: `<div class="nav-previous"><a href="%s">%s</a></div>`,
NextEle: `<div class="nav-next"><a href="%s">%s</a></div>`,
},
}
func (p PageEle) Current(page, totalPage, totalRow int) string { func (p PageEle) Current(page, totalPage, totalRow int) string {
return fmt.Sprintf(p.CurrentEle, page) return fmt.Sprintf(p.CurrentEle, page)
@ -67,22 +50,8 @@ func (p PageEle) Middle(page int, url string) string {
} }
var reg = regexp.MustCompile(`(/page)/(\d+)`) var reg = regexp.MustCompile(`(/page)/(\d+)`)
var commentReg = regexp.MustCompile(`/comment-page-(\d+)`)
var queryParam = []string{"paged", "cat", "m", "author", "tag"}
func (p PageEle) Urls(u url.URL, page int, isTLS bool) string {
var path, query = u.Path, u.RawQuery
if path == "/" {
v := u.Query()
for _, q := range queryParam {
if v.Get(q) != "" {
v.Set("paged", strconv.Itoa(page))
return str.Join(path, "?", v.Encode())
}
}
}
func (p PageEle) Url(path, query string, page int) string {
if !strings.Contains(path, "/page/") { if !strings.Contains(path, "/page/") {
path = fmt.Sprintf("%s%s", path, "/page/1") path = fmt.Sprintf("%s%s", path, "/page/1")
} }
@ -96,81 +65,5 @@ func (p PageEle) Urls(u url.URL, page int, isTLS bool) string {
if path == "" { if path == "" {
path = "/" path = "/"
} }
if query != "" { return str.Join(path, query)
return str.Join(path, "?", query)
}
return path
}
func (p CommentPageEle) Urls(u url.URL, page int, isTLS bool) string {
var path, query = u.Path, u.RawQuery
if path == "/" {
v := u.Query()
if v.Get("p") != "" {
v.Set("cpage", strconv.Itoa(page))
return str.Join(path, "?", v.Encode(), "#comments")
}
}
if !strings.Contains(path, "/comment-page-") {
path = fmt.Sprintf("%s%s", path, "/comment-page-1")
}
path = commentReg.ReplaceAllString(path, fmt.Sprintf("/comment-page-%d", page))
path = strings.Replace(path, "//", "/", -1)
ur := path
if query != "" {
ur = str.Join(path, "?", query)
}
ur = str.Join(ur, "#comments")
return ur
}
func (p CommentPageEle) Middle(page int, url string) string {
return ""
}
func (p CommentPageEle) Dots() string {
return ""
}
func (p CommentPageEle) Current(page, totalPage, totalRow int) string {
return ""
}
func (p CommentPageEle) Prev(url string) string {
return fmt.Sprintf(p.PrevEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较早评论", "较新评论"))
}
func (p CommentPageEle) Next(url string) string {
return fmt.Sprintf(p.NextEle, url, helper.Or(wpconfig.GetOption("comment_order") == "asc", "较新评论", "较早评论"))
}
type PaginationNav struct {
Currents func(page, totalPage, totalRows int) string
Prevs func(url string) string
Nexts func(url string) string
Dotss func() string
Middles func(page int, url string) string
Urlss func(u url.URL, page int, isTLS bool) string
}
func (p PaginationNav) Current(page, totalPage, totalRows int) string {
return p.Currents(page, totalPage, totalRows)
}
func (p PaginationNav) Prev(url string) string {
return p.Prevs(url)
}
func (p PaginationNav) Next(url string) string {
return p.Nexts(url)
}
func (p PaginationNav) Dots() string {
return p.Dotss()
}
func (p PaginationNav) Middle(page int, url string) string {
return p.Middles(page, url)
}
func (p PaginationNav) Urls(u url.URL, page int, isTLS bool) string {
return p.Urlss(u, page, isTLS)
} }

View File

@ -2,29 +2,12 @@ package apply
import "github.com/fthvgb1/wp-go/safety" import "github.com/fthvgb1/wp-go/safety"
var contains = safety.NewMap[string, any]() var fn safety.Var[any]
func SetVal(key string, val any) { func SetFn(f any) {
contains.Store(key, val) fn.Store(f)
} }
func DelVal(key string) { func UsePlugins() any {
contains.Delete(key) return fn.Load()
}
func GetVal[V any](key string) (V, bool) {
v, ok := contains.Load(key)
if !ok {
var vv V
return vv, ok
}
return v.(V), ok
}
func GetRawVal(key string) (any, bool) {
return contains.Load(key)
}
func GetPlugins() any {
v, _ := contains.Load("wp-plugins")
return v
} }

View File

@ -22,7 +22,7 @@ type Options struct {
Linehover bool `json:"linehover,omitempty"` Linehover bool `json:"linehover,omitempty"`
RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"` RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"`
TextOverflow string `json:"textOverflow,omitempty"` TextOverflow string `json:"textOverflow,omitempty"`
Linenumbers bool `json:"linenumbers,omitempty"` Linenumbers int64 `json:"linenumbers,omitempty"`
Theme string `json:"theme,omitempty"` Theme string `json:"theme,omitempty"`
Language string `json:"language,omitempty"` Language string `json:"language,omitempty"`
RetainCssClasses bool `json:"retainCssClasses,omitempty"` RetainCssClasses bool `json:"retainCssClasses,omitempty"`
@ -58,7 +58,7 @@ func EnlighterJS(h *wp.Handle) {
Linehover: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linehover", true), Linehover: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linehover", true),
RawcodeDbclick: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-rawcodedbclick", true), RawcodeDbclick: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-rawcodedbclick", true),
TextOverflow: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-textoverflow", "break"), TextOverflow: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-textoverflow", "break"),
Linenumbers: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-linenumbers", true), Linenumbers: maps.GetStrAnyValWithDefaults[int64](opp, "enlighterjs-linenumbers", 1),
Theme: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-theme", "enlighter"), Theme: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-theme", "enlighter"),
Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"), Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"),
RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false), RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false),

View File

@ -66,7 +66,7 @@ func LoadPlugins() {
} }
RegisterPlugin(name, plu) RegisterPlugin(name, plu)
} }
apply.SetVal("wp-plugins", func(h *wp.Handle) { apply.SetFn(func(h *wp.Handle) {
UsePlugins(h) UsePlugins(h)
}) })
} }

View File

@ -7,7 +7,7 @@ import (
) )
func Tt(h *wp.Handle) { func Tt(h *wp.Handle) {
h.HookHandle(constraints.PipeMiddleware, constraints.AllScene, func(call wp.HandleCall) (wp.HandleCall, bool) { h.HookHandle(constraints.PipeMiddleware, func(call wp.HandleCall) (wp.HandleCall, bool) {
return call, false return call, false
}) })
/*h.PushPipeHook(constraints.Home, func(pipe wp.Pipe) (wp.Pipe, bool) { /*h.PushPipeHook(constraints.Home, func(pipe wp.Pipe) (wp.Pipe, bool) {

View File

@ -1,147 +0,0 @@
package route
import (
"github.com/fthvgb1/wp-go/app/actions"
"github.com/fthvgb1/wp-go/app/middleware"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
type GinSetter func(*gin.Engine)
var setters mockmap.Map[string, GinSetter]
var setterHooks []func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool)
// SetGinAction 方便插件在init时使用
func SetGinAction(name string, hook GinSetter, orders ...float64) {
setters.Set(name, hook, orders...)
}
func HookGinSetter(fn func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool)) {
setterHooks = append(setterHooks, fn)
}
// DelGinSetter 方便插件在init时使用
func DelGinSetter(name string) {
setterHooks = append(setterHooks, func(item mockmap.Item[string, GinSetter]) (mockmap.Item[string, GinSetter], bool) {
return item, item.Name != name
})
}
func SetupRouter() *gin.Engine {
// Disable Console Color
// gin.DisableConsoleColor()
r := gin.New()
c := config.GetConfig()
SetGinAction("initTrustIp", func(r *gin.Engine) {
if len(c.TrustIps) > 0 {
err := r.SetTrustedProxies(c.TrustIps)
if err != nil {
panic(err)
}
}
}, 99.5)
SetGinAction("setTemplate", func(r *gin.Engine) {
r.HTMLRender = theme.BuildTemplate()
wpconfig.SetTemplateFs(theme.TemplateFs)
}, 90.5)
siteFlowLimitMiddleware, siteFlow := middleware.FlowLimit(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
reload.Append(func() {
c = config.GetConfig()
siteFlow(c.MaxRequestSleepNum, c.MaxRequestNum, c.CacheTime.SleepTime)
}, "site-flowLimit-config")
SetGinAction("setGlobalMiddleware", func(r *gin.Engine) {
r.Use(
gin.Logger(),
middleware.ValidateServerNames(),
middleware.RecoverAndSendMail(gin.DefaultErrorWriter),
siteFlowLimitMiddleware,
middleware.SetStaticFileCache,
)
}, 88.5)
SetGinAction("setGzip", func(r *gin.Engine) {
//gzip 因为一般会用nginx做反代时自动使用gzip,所以go这边本身可以不用
if c.Gzip {
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{
"/wp-includes/", "/wp-content/",
})))
}
}, 87.6)
SetGinAction("setWpDir", func(r *gin.Engine) {
if c.WpDir == "" {
panic("wordpress path can't be empty")
}
r.Static("/wp-content/uploads", str.Join(c.WpDir, "/wp-content/uploads"))
r.Static("/wp-content/themes", str.Join(c.WpDir, "/wp-content/themes"))
r.Static("/wp-content/plugins", str.Join(c.WpDir, "/wp-content/plugins"))
r.Static("/wp-includes/css", str.Join(c.WpDir, "/wp-includes/css"))
r.Static("/wp-includes/fonts", str.Join(c.WpDir, "/wp-includes/fonts"))
r.Static("/wp-includes/js", str.Join(c.WpDir, "/wp-includes/js"))
}, 86.1)
SetGinAction("setSession", func(r *gin.Engine) {
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("go-wp", store))
}, 85.1)
SetGinAction("setRoute", func(r *gin.Engine) {
r.GET("/", actions.Feed, middleware.SearchLimit(c.SingleIpSearchNum),
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.ThemeHook(constraints.Detail))
r.GET("/p/:id/comment-page-:page", actions.ThemeHook(constraints.Detail))
r.GET("/p/:id/feed", actions.PostFeed)
r.GET("/feed", actions.SiteFeed)
r.GET("/comments/feed", actions.CommentsFeed)
commentMiddleWare, _ := middleware.FlowLimit(c.MaxRequestSleepNum, 5, c.CacheTime.SleepTime)
r.POST("/comment", commentMiddleWare, actions.PostComment)
r.NoRoute(actions.ThemeHook(constraints.NoRoute))
}, 84.6)
SetGinAction("setpprof", func(r *gin.Engine) {
if c.Pprof != "" {
pprof.Register(r, c.Pprof)
}
}, 80.8)
for _, hook := range setterHooks {
setters = slice.FilterAndMap(setters, hook)
}
slice.SimpleSort(setters, slice.DESC, func(t mockmap.Item[string, GinSetter]) float64 {
return t.Order
})
for _, fn := range setters {
fn.Value(r)
}
return r
}

View File

@ -3,7 +3,6 @@ package theme
import ( import (
"embed" "embed"
"github.com/fthvgb1/wp-go/multipTemplate" "github.com/fthvgb1/wp-go/multipTemplate"
"github.com/fthvgb1/wp-go/safety"
"html/template" "html/template"
"io/fs" "io/fs"
"path/filepath" "path/filepath"
@ -13,36 +12,20 @@ import (
//go:embed *[^.go] //go:embed *[^.go]
var TemplateFs embed.FS var TemplateFs embed.FS
var templates = safety.NewMap[string, *template.Template]() //方便外部获取模板render后的字符串不然在gin中获取不了 var templates map[string]*template.Template //方便外部获取模板render后的字符串不然在gin中获取不了
var multiple *multipTemplate.MultipleFsTemplate func Template() *multipTemplate.MultipleFsTemplate {
t := multipTemplate.NewFsTemplate(TemplateFs)
func BuildTemplate() *multipTemplate.MultipleFsTemplate { templates = t.Template
if multiple != nil { t.FuncMap = FuncMap()
tt := multipTemplate.NewFsTemplate(TemplateFs) commonTemplate(t)
commonTemplate(tt) /*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml"). //单个主题设置
for k, v := range map[string]*template.Template(any(tt.Template).(multipTemplate.TemplateMaps)) { AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml")*/
multiple.Template.Store(k, v) return t
}
} else {
multiple = multipTemplate.NewFsTemplates(TemplateFs, templates)
commonTemplate(multiple)
}
/*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml","wp/template.gohtml"). //单个主题设置
AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml","wp/template.gohtml")*/
return multiple
}
func GetMultipleTemplate() *multipTemplate.MultipleFsTemplate {
if multiple == nil {
BuildTemplate()
}
return multiple
} }
func GetTemplate(name string) (*template.Template, bool) { func GetTemplate(name string) (*template.Template, bool) {
t, ok := templates.Load(name) t, ok := templates[name]
return t, ok return t, ok
} }
@ -52,11 +35,10 @@ func commonTemplate(t *multipTemplate.MultipleFsTemplate) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
funMap := FuncMap()
for _, main := range m { for _, main := range m {
file := filepath.Base(main) file := filepath.Base(main)
dir := strings.Split(main, "/")[0] dir := strings.Split(main, "/")[0]
templ := template.Must(template.New(file).Funcs(funMap).ParseFS(t.Fs, main, filepath.Join(dir, "layout/*.gohtml"), "wp/template.gohtml")) templ := template.Must(template.New(file).Funcs(t.FuncMap).ParseFS(t.Fs, main, filepath.Join(dir, "layout/*.gohtml"), "wp/template.gohtml"))
t.SetTemplate(main, templ) t.SetTemplate(main, templ)
} }
} }
@ -73,10 +55,6 @@ func IsTemplateDirExists(tml string) bool {
} }
func IsTemplateExists(tml string) bool { func IsTemplateExists(tml string) bool {
t, ok := templates.Load(tml) t, ok := templates[tml]
return ok && t != nil return ok && t != nil
} }
func SetTemplate(name string, val *template.Template) {
templates.Store(name, val)
}

View File

@ -1,35 +1,25 @@
package theme package theme
import ( import (
"github.com/fthvgb1/wp-go/app/theme/twentyfifteen"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
) )
var themeMap = safety.NewMap[string, func(*wp.Handle)]() var themeMap = safety.NewMap[string, func(*wp.Handle)]()
func AddTheme(name string, fn func(handle *wp.Handle)) { func AddThemeHookFunc(name string, fn func(handle *wp.Handle)) {
if _, ok := themeMap.Load(name); ok {
panic("exists same name theme")
}
themeMap.Store(name, fn) themeMap.Store(name, fn)
} }
func DelTheme(name string) {
themeMap.Delete(name)
}
func GetTheme(name string) (func(*wp.Handle), bool) {
return themeMap.Load(name)
}
func IsThemeHookFuncExist(name string) bool {
_, ok := themeMap.Load(name)
return ok
}
func Hook(themeName string, h *wp.Handle) { func Hook(themeName string, h *wp.Handle) {
fn, ok := themeMap.Load(themeName) fn, ok := themeMap.Load(themeName)
if ok && fn != nil { if ok && fn != nil {
fn(h) fn(h)
return return
} }
panic(str.Join("theme ", themeName, " don't exist")) twentyfifteen.Hook(h)
} }

View File

@ -7,31 +7,37 @@ import (
"time" "time"
) )
var comFn = template.FuncMap{
"unescaped": func(s string) any {
return template.HTML(s)
},
"dateCh": func(t time.Time) any {
return t.Format("2006年 01月 02日")
},
"timeFormat": func(t time.Time, format string) any {
return t.Format(format)
},
"getOption": func(k string) string {
return wpconfig.GetOption(k)
},
"getLang": wpconfig.GetLang,
"postsFn": postsFn,
"exec": func(fn func() string) template.HTML {
return template.HTML(fn())
},
}
func postsFn(fn func(models.Posts) string, a models.Posts) string { func postsFn(fn func(models.Posts) string, a models.Posts) string {
return fn(a) return fn(a)
} }
func FuncMap() template.FuncMap { func FuncMap() template.FuncMap {
return template.FuncMap{ return comFn
"unescaped": func(s string) any { }
return template.HTML(s)
}, func addTemplateFunc(fnName string, fn any) {
"dateCh": func(t time.Time) any { if _, ok := comFn[fnName]; ok {
return t.Format("2006年 01月 02日") panic("exists same name func")
}, }
"timeFormat": func(t time.Time, format string) any { comFn[fnName] = fn
return t.Format(format)
},
"getOption": func(k string) string {
return wpconfig.GetOption(k)
},
"getLang": wpconfig.GetLang,
"postsFn": postsFn,
"exec": func(fn func() string) template.HTML {
return template.HTML(fn())
},
"callFuncString": func(fn func(string) string, s string) template.HTML {
return template.HTML(fn(s))
},
}
} }

View File

@ -8,17 +8,17 @@ import (
) )
func InitTheme() { func InitTheme() {
AddTheme(twentyfifteen.ThemeName, twentyfifteen.Hook) AddThemeHookFunc(twentyfifteen.ThemeName, twentyfifteen.Hook)
AddTheme(twentyseventeen.ThemeName, twentyseventeen.Hook) AddThemeHookFunc(twentyseventeen.ThemeName, twentyseventeen.Hook)
} }
func GetCurrentTheme() string { func GetCurrentTemplateName() string {
themeName := config.GetConfig().Theme tmlp := config.GetConfig().Theme
if themeName == "" { if tmlp == "" {
themeName = wpconfig.GetOption("template") tmlp = wpconfig.GetOption("template")
} }
if !IsTemplateDirExists(themeName) { if !IsTemplateDirExists(tmlp) {
themeName = "twentyfifteen" tmlp = "twentyfifteen"
} }
return themeName return tmlp
} }

View File

@ -1,9 +1,9 @@
package twentyfifteen package twentyfifteen
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
) )
@ -81,7 +81,7 @@ var imgStyle = `.site-header {
} }
}` }`
var header = reload.Vars(constraints.Defaults, "twentyfifteen-customheader") var header = reload.Vars(constraints.Defaults)
func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) { func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) {
img, rand := h.GetCustomHeaderImg() img, rand := h.GetCustomHeaderImg()

View File

@ -31,7 +31,11 @@
{{end}} {{end}}
</div> </div>
{{template "common/colophon" .}} <footer id="colophon" class="site-footer">
<div class="site-info">
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
</div>
</footer>
</div> </div>
{{template "layout/footer" .}} {{template "layout/footer" .}}

View File

@ -52,30 +52,14 @@
{{ if .showComment}} {{ if .showComment}}
<div id="comments" class="comments-area"> <div id="comments" class="comments-area">
{{ if ne .comments ""}} {{ if gt .post.CommentCount 0}}
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </h2> <h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
{{if gt .totalCommentPage 1}} <ol class="comment-list">
<nav class="navigation comment-navigation"> {{.comments|unescaped}}
<h2 class="screen-reader-text">评论导航</h2> </ol>
<div class="nav-links">
{{ .commentPageNav|unescaped}}
</div><!-- .nav-links -->
</nav>
{{end}}
<ol class="comment-list">
{{.comments|unescaped}}
</ol>
{{if gt .totalCommentPage 1}}
<nav class="navigation comment-navigation">
<h2 class="screen-reader-text">评论导航</h2>
<div class="nav-links">
{{ .commentPageNav|unescaped}}
</div><!-- .nav-links -->
</nav>
{{end}}
{{end}} {{end}}
{{if eq .post.CommentStatus "open"}} {{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}}
<div id="respond" class="comment-respond"> <div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">发表回复 <h3 id="reply-title" class="comment-reply-title">发表回复
<small> <small>

View File

@ -6,7 +6,7 @@ import (
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components" "github.com/fthvgb1/wp-go/app/theme/wp/components"
"github.com/fthvgb1/wp-go/app/theme/wp/middleware" "github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"strings" "strings"
) )
@ -22,13 +22,16 @@ func configs(h *wp.Handle) {
return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`) return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`)
}) })
wp.InitPipe(h) wp.InitPipe(h)
middleware.CommonMiddleware(h) h.PushHandler(constraints.PipeMiddleware, constraints.Home,
setPaginationAndRender(h) wp.NewHandleFn(widget.CheckCategory, 100, "widget.CheckCategory"))
h.Index.SetPageEle(plugins.TwentyFifteenPagination())
h.PushCacheGroupHeadScript(constraints.AllScene, "CalCustomBackGround", 10.005, CalCustomBackGround) h.PushCacheGroupHeadScript(constraints.AllScene, "CalCustomBackGround", 10.005, CalCustomBackGround)
h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss) h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss)
h.CommonComponents() h.CommonComponents()
components.WidgetArea(h) components.WidgetArea(h)
h.PushRender(constraints.AllScene, wp.NewHandleFn(renderCustomHeader, 20.5, "renderCustomHeader")) wp.ReplyCommentJs(h)
h.SetData("customHeader", customHeader(h))
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 50.005, "wp.IndexRender")) wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 50.005, "wp.IndexRender"))
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender")) h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender"))
h.PushRender(constraints.Detail, wp.NewHandleFn(postThumb, 60.005, "postThumb")) h.PushRender(constraints.Detail, wp.NewHandleFn(postThumb, 60.005, "postThumb"))
@ -38,26 +41,8 @@ func configs(h *wp.Handle) {
h.PushRender(constraints.AllScene, wp.NewHandleFn(wp.PreTemplate, 70.005, "wp.PreTemplate")) h.PushRender(constraints.AllScene, wp.NewHandleFn(wp.PreTemplate, 70.005, "wp.PreTemplate"))
} }
func setPaginationAndRender(h *wp.Handle) {
h.PushHandler(constraints.PipeRender, constraints.Detail, wp.NewHandleFn(func(hh *wp.Handle) {
d := hh.GetDetailHandle()
d.CommentRender = plugins.CommentRender()
d.CommentPageEle = plugins.TwentyFifteenCommentPagination()
}, 150, "setPaginationAndRender"))
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(func(hh *wp.Handle) {
i := hh.GetIndexHandle()
i.SetPageEle(plugins.TwentyFifteenPagination())
}, 150, "setPaginationAndRender"))
}
func postThumb(h *wp.Handle) { func postThumb(h *wp.Handle) {
d := h.GetDetailHandle() if h.Detail.Post.Thumbnail.Path != "" {
if d.Post.Thumbnail.Path != "" { h.Detail.Post.Thumbnail = wpconfig.Thumbnail(h.Detail.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
d.Post.Thumbnail = wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
} }
} }
func renderCustomHeader(h *wp.Handle) {
h.SetData("customHeader", customHeader(h))
}

View File

@ -0,0 +1,9 @@
{{define "layout/colophon"}}
<footer id="colophon" class="site-footer">
<div class="wrap">
<div class="site-info">
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
</div>
</div>
</footer>
{{end}}

View File

@ -64,21 +64,12 @@
{{ if .showComment}} {{ if .showComment}}
<div id="comments" class="comments-area"> <div id="comments" class="comments-area">
{{ if ne .comments ""}} {{ if gt .post.CommentCount 0}}
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.totalCommentNum}}个回复 </h2> <h2 class="comments-title">“{{.post.PostTitle}}”的{{.post.CommentCount}}个回复 </h2>
<ol class="comment-list"> <ol class="comment-list">
{{.comments|unescaped}} {{.comments|unescaped}}
</ol> </ol>
{{if gt .totalCommentPage 1}}
<nav class="navigation comments-pagination" aria-label="评论">
<h2 class="screen-reader-text">评论导航</h2>
<div class="nav-links">
{{ .commentPageNav|unescaped}}
</div><!-- .nav-links -->
</nav>
{{end}}
{{end}} {{end}}
{{if eq .post.CommentStatus "open"}} {{if eq .post.CommentStatus "open"}}
{{template "respond" .}} {{template "respond" .}}
@ -132,7 +123,7 @@
</div> </div>
</div> </div>
{{template "common/colophon" .}} {{template "layout/colophon"}}
</div> </div>

View File

@ -77,6 +77,6 @@
{{end}} {{end}}
</div> </div>
{{template "common/colophon" .}} {{template "layout/colophon"}}
</div> </div>
{{end}} {{end}}

View File

@ -4,10 +4,56 @@ import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/scriptloader"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings"
) )
func pushScripts(h *wp.Handle) { func pushScripts(h *wp.Handle) {
scriptloader.EnqueueStyle("twentyseventeen-style", scriptloader.GetStylesheetUri(), nil, "20230328", "")
scriptloader.EnqueueStyles("twentyseventeen-block-style", "/assets/css/blocks.css", []string{"twentyseventeen-style"}, "20220912", "")
if "dark" == wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") {
scriptloader.EnqueueStyles("twentyseventeen-colors-dark", "/assets/css/colors-dark.css",
[]string{"twentyseventeen-style"}, "20191025", "")
}
scriptloader.AddScriptData("twentyseventeen-ie8", "conditional", "lt IE 9")
scriptloader.EnqueueScripts("html5", "/assets/js/html5.js", nil, "20161020", false)
scriptloader.AddScriptData("html5", "conditional", "lt IE 9")
scriptloader.EnqueueScripts("twentyseventeen-skip-link-focus-fix", "/assets/js/skip-link-focus-fix.js",
nil, "20161114", true)
l10n := map[string]any{
"quote": svg(h, map[string]string{"icon": "quote-right"}),
}
scriptloader.EnqueueScripts("twentyseventeen-global", "/assets/js/global.js",
[]string{"jquery"}, "20211130", true)
scriptloader.EnqueueScripts("jquery-scrollto", "/assets/js/jquery.scrollTo.js",
[]string{"jquery"}, "2.1.3", true)
scriptloader.EnqueueScripts("comment-reply", "", nil, "", false)
//todo menu top
scriptloader.AddStaticLocalize("twentyseventeen-skip-link-focus-fix", "twentyseventeenScreenReaderText", l10n)
scriptloader.AddStaticLocalize("wp-custom-header", "_wpCustomHeaderSettings", map[string]any{
"mimeType": `video/mp4`,
"posterUrl": `/wp-content/uploads/2023/01/cropped-wallhaven-9dm7dd-1.png`,
"videoUrl": `/wp-content/uploads/2023/06/BloodMoon_GettyRM_495644264_1080_HD_ZH-CN.mp4`,
"width": `2000`,
"height": `1199`,
"minWidth": `900`,
"minHeight": `500`,
"l10n": map[string]any{
"pause": `<span class="screen-reader-text">暂停背景视频</span><svg class="icon icon-pause" aria-hidden="true" role="img"> <use href="#icon-pause" xlink:href="#icon-pause"></use> </svg>`, "play": `<span class="screen-reader-text">播放背景视频</span><svg class="icon icon-play" aria-hidden="true" role="img"> <use href="#icon-play" xlink:href="#icon-play"></use> </svg>`, "pauseSpeak": `视频已暂停。`, "playSpeak": `视频正在播放。`,
},
})
h.PushCacheGroupHeadScript(constraints.AllScene, "{theme}.head", 30, func(h *wp.Handle) string { h.PushCacheGroupHeadScript(constraints.AllScene, "{theme}.head", 30, func(h *wp.Handle) string {
head := headScript head := headScript
if "dark" == wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") { if "dark" == wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") {
@ -41,3 +87,37 @@ var footerScript = `<script id="twentyseventeen-skip-link-focus-fix-js-extra">
<script src="/wp-content/themes/twentyseventeen/assets/js/skip-link-focus-fix.js?ver=20161114" id="twentyseventeen-skip-link-focus-fix-js"></script> <script src="/wp-content/themes/twentyseventeen/assets/js/skip-link-focus-fix.js?ver=20161114" id="twentyseventeen-skip-link-focus-fix-js"></script>
<script src="/wp-content/themes/twentyseventeen/assets/js/global.js?ver=20211130" id="twentyseventeen-global-js"></script> <script src="/wp-content/themes/twentyseventeen/assets/js/global.js?ver=20211130" id="twentyseventeen-global-js"></script>
<script src="/wp-content/themes/twentyseventeen/assets/js/jquery.scrollTo.js?ver=2.1.3" id="jquery-scrollto-js"></script>` <script src="/wp-content/themes/twentyseventeen/assets/js/jquery.scrollTo.js?ver=2.1.3" id="jquery-scrollto-js"></script>`
func svg(h *wp.Handle, m map[string]string) string {
if !maps.IsExists(m, "icon") {
return ""
}
ariaHidden := ` aria-hidden="true"`
ariaLabelledby := ""
uniqueId := ""
if m["title"] != "" {
ariaHidden = ""
id := helper.GetContextVal(h.C, "svg", 0)
uniqueId = number.IntToString(id)
id++
h.C.Set("svg", id)
ariaLabelledby = str.Join(" aria-labelledby=\"title-", uniqueId, "\"")
if m["desc"] != "" {
ariaLabelledby = str.Join(" aria-labelledby=\"title-", uniqueId, " desc-", uniqueId, "\"")
}
}
s := str.NewBuilder()
s.WriteString("<svg class=\"icon icon-", m["icon"], "\"", ariaHidden, ariaLabelledby, " role=\"img\">")
if m["title"] != "" {
s.WriteString(`<title id="title-`, uniqueId, `">`, m["title"], "</title>")
if m["desc"] != "" {
s.WriteString(`<desc id="desc-`, uniqueId, `">`, m["desc"], `</desc>`)
}
}
s.WriteString(` <use href="#icon-`, m["icon"], `" xlink:href="#icon-`, m["icon"], `"></use> `)
if m["fallback"] != "" {
s.WriteString(`<span class="svg-fallback icon-' . esc_attr( $args['icon'] ) . '"></span>`)
}
s.WriteString(`<span class="svg-fallback icon-`, m["icon"], `"></span></svg>`)
return s.String()
}

View File

@ -1,8 +1,8 @@
package twentyseventeen package twentyseventeen
import ( import (
"context"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
@ -10,12 +10,11 @@ import (
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components" "github.com/fthvgb1/wp-go/app/theme/wp/components"
"github.com/fthvgb1/wp-go/app/theme/wp/middleware" "github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/pagination" "github.com/gin-gonic/gin"
"strings" "strings"
) )
@ -28,33 +27,23 @@ var paginate = func() plugins.PageEle {
p.NextEle = strings.Replace(p.NextEle, "下一页", `<span class="screen-reader-text">下一页</span> p.NextEle = strings.Replace(p.NextEle, "下一页", `<span class="screen-reader-text">下一页</span>
<svg class="icon icon-arrow-right" aria-hidden="true" role="img"> <use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use> <svg class="icon icon-arrow-right" aria-hidden="true" role="img"> <use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use>
</svg>`, 1) </svg>`, 1)
commentPageEle = plugins.PaginationNav{
Currents: p.Current,
Prevs: p.Prev,
Nexts: p.Next,
Dotss: p.Dots,
Middles: p.Middle,
Urlss: plugins.TwentyFifteenCommentPagination().Urls,
}
return p return p
}() }()
var commentPageEle pagination.Render
func Hook(h *wp.Handle) { func Hook(h *wp.Handle) {
wp.Run(h, configs) wp.Run(h, configs)
} }
func configs(h *wp.Handle) { func configs(h *wp.Handle) {
wp.InitPipe(h) wp.InitPipe(h)
middleware.CommonMiddleware(h) h.PushHandler(constraints.PipeMiddleware, constraints.Home,
wp.NewHandleFn(widget.CheckCategory, 100.006, "widget.CheckCategory"))
h.AddActionFilter("bodyClass", calClass) h.AddActionFilter("bodyClass", calClass)
h.PushCacheGroupHeadScript(constraints.AllScene, "colorScheme-customHeader", 10, colorScheme, customHeader) h.PushCacheGroupHeadScript(constraints.AllScene, "colorScheme-customHeader", 10, colorScheme, customHeader)
components.WidgetArea(h) components.WidgetArea(h)
pushScripts(h) pushScripts(h)
h.PushRender(constraints.AllStats, wp.NewHandleFn(calCustomHeader, 10.005, "calCustomHeader")) h.PushRender(constraints.AllStats, wp.NewHandleFn(calCustomHeader, 10.005, "calCustomHeader"))
wp.SetComponentsArgs(widgets.Widget, map[string]string{ wp.SetComponentsArgs(h, widgets.Widget, map[string]string{
"{$before_widget}": `<section id="%s" class="%s">`, "{$before_widget}": `<section id="%s" class="%s">`,
"{$after_widget}": `</section>`, "{$after_widget}": `</section>`,
}) })
@ -63,11 +52,12 @@ func configs(h *wp.Handle) {
wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"), wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"),
) )
videoHeader(h) videoHeader(h)
h.SetData("colophon", colophon) h.Detail.CommentRender = commentFormat
setPaginationAndRender(h)
h.CommonComponents() h.CommonComponents()
h.Index.SetPageEle(paginate)
wp.ReplyCommentJs(h)
h.PushPostPlugin(postThumbnail) h.PushPostPlugin(postThumbnail)
wp.SetComponentsArgsForMap(widgets.Search, "{$form}", searchForm) wp.SetComponentsArgsForMap(h, widgets.Search, "{$form}", searchForm)
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 10.005, "wp.IndexRender")) wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 10.005, "wp.IndexRender"))
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 10.005, "wp.DetailRender")) h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 10.005, "wp.DetailRender"))
h.PushDataHandler(constraints.Detail, wp.NewHandleFn(wp.Detail, 100.005, "wp.Detail"), wp.NewHandleFn(postThumb, 90.005, "{theme}.postThumb")) h.PushDataHandler(constraints.Detail, wp.NewHandleFn(wp.Detail, 100.005, "wp.Detail"), wp.NewHandleFn(postThumb, 90.005, "{theme}.postThumb"))
@ -75,19 +65,6 @@ func configs(h *wp.Handle) {
h.PushDataHandler(constraints.AllScene, wp.NewHandleFn(wp.PreCodeAndStats, 90.005, "wp.PreCodeAndStats")) h.PushDataHandler(constraints.AllScene, wp.NewHandleFn(wp.PreCodeAndStats, 90.005, "wp.PreCodeAndStats"))
} }
func setPaginationAndRender(h *wp.Handle) {
h.PushHandler(constraints.PipeRender, constraints.Detail, wp.NewHandleFn(func(hh *wp.Handle) {
d := hh.GetDetailHandle()
d.CommentRender = commentFormat
d.CommentPageEle = commentPageEle
d.CommentStep = 2
}, 150, "setPaginationAndRender"))
wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(func(hh *wp.Handle) {
i := hh.GetIndexHandle()
i.SetPageEle(paginate)
}, 150, "setPaginationAndRender"))
}
var searchForm = `<form role="search" method="get" class="search-form" action="/"> var searchForm = `<form role="search" method="get" class="search-form" action="/">
<label for="search-form-1"> <label for="search-form-1">
<span class="screen-reader-text">{$label}</span> <span class="screen-reader-text">{$label}</span>
@ -108,7 +85,7 @@ func errorsHandle(h *wp.Handle) {
} }
func postThumb(h *wp.Handle) { func postThumb(h *wp.Handle) {
d := h.GetDetailHandle() d := h.Detail
if d.Post.Thumbnail.Path != "" { if d.Post.Thumbnail.Path != "" {
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail") img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
img.Sizes = "100vw" img.Sizes = "100vw"
@ -123,27 +100,19 @@ type comment struct {
plugins.CommonCommentFormat plugins.CommonCommentFormat
} }
var commentLi = plugins.CommonLi() func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
templ := plugins.CommonLi()
var respondFn = plugins.Responds(respondStr) templ = strings.ReplaceAll(templ, `<a rel="nofollow" class="comment-reply-link"
func (c comment) FormatLi(_ context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
return plugins.FormatLi(commentLi, m, respondFn, depth, maxDepth, page, isTls, isThreadComments, eo, parent)
}
var colophon = `<footer id="colophon" class="site-footer">
<div class="wrap">
<div class="site-info">
<a href="https://github.com/fthvgb1/wp-go" class="imprint">自豪地采用 wp-go</a>
</div>
</div>
</footer>`
var respondStr = `<a rel="nofollow" class="comment-reply-link"
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}" href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond" data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
data-replyto="回复给{{CommentAuthor}}" data-replyto="回复给{{CommentAuthor}}"
aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>` aria-label="回复给{{CommentAuthor}}">回复</a>`, `<a rel="nofollow" class="comment-reply-link"
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
data-replyto="回复给{{CommentAuthor}}"
aria-label="回复给{{CommentAuthor}}"><svg class="icon icon-mail-reply" aria-hidden="true" role="img"> <use href="#icon-mail-reply" xlink:href="#icon-mail-reply"></use> </svg>回复</a>`)
return plugins.FormatLi(templ, ctx, m, depth, isTls, eo, parent)
}
func postThumbnail(h *wp.Handle, posts *models.Posts) { func postThumbnail(h *wp.Handle, posts *models.Posts) {
if posts.Thumbnail.Path != "" { if posts.Thumbnail.Path != "" {
@ -154,7 +123,7 @@ func postThumbnail(h *wp.Handle, posts *models.Posts) {
} }
} }
var header = reload.Vars(models.PostThumbnail{}, "twentyseventeen-headerImage") var header = reload.Vars(models.PostThumbnail{})
func calCustomHeader(h *wp.Handle) { func calCustomHeader(h *wp.Handle) {
h.SetData("HeaderImage", getHeaderImage(h)) h.SetData("HeaderImage", getHeaderImage(h))

View File

@ -33,14 +33,14 @@ func (h *Handle) BodyClass() string {
case constraints.Search: case constraints.Search:
s := "search-no-results" s := "search-no-results"
if len(h.GetIndexHandle().Posts) > 0 { if len(h.Index.Posts) > 0 {
s = "search-results" s = "search-results"
} }
class = append(class, "search", s) class = append(class, "search", s)
case constraints.Category, constraints.Tag: case constraints.Category, constraints.Tag:
class = append(class, "archive", "category") class = append(class, "archive", "category")
cat := h.GetIndexHandle().Param.Category cat := h.Index.Param.Category
if cat == "" { if cat == "" {
break break
} }
@ -54,16 +54,16 @@ func (h *Handle) BodyClass() string {
case constraints.Author: case constraints.Author:
class = append(class, "archive", "author") class = append(class, "archive", "author")
author := h.GetIndexHandle().Param.Author author := h.Index.Param.Author
user, _ := cache.GetUserByName(h.C, author) user, _ := cache.GetUserByName(h.C, author)
class = append(class, str.Join("author-", number.IntToString(user.Id))) class = append(class, str.Join("author-", number.IntToString(user.Id)))
if user.DisplayName[0] != '%' { if user.UserLogin[0] != '%' {
class = append(class, str.Join("author-", user.DisplayName)) class = append(class, str.Join("author-", user.UserLogin))
} }
case constraints.Detail: case constraints.Detail:
class = append(class, "post-template-default", "single", "single-post") class = append(class, "post-template-default", "single", "single-post")
class = append(class, str.Join("postid-", number.IntToString(h.GetDetailHandle().Post.Id))) class = append(class, str.Join("postid-", number.IntToString(h.Detail.Post.Id)))
if len(h.themeMods.ThemeSupport.PostFormats) > 0 { if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
class = append(class, "single-format-standard") class = append(class, "single-format-standard")
} }

View File

@ -1,126 +0,0 @@
package wp
import (
"context"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"time"
)
func RenderComment(ctx context.Context, page int, render plugins.CommentHtml, ids []uint64, timeout time.Duration, isTLS bool) (string, error) {
ca, _ := cachemanager.GetMapCache[uint64, models.Comments]("postCommentData")
children, _ := cachemanager.GetMapCache[uint64, []uint64]("commentChildren")
h := CommentHandle{
maxDepth: str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5),
depth: 1,
isTls: isTLS,
html: render,
order: wpconfig.GetOption("comment_order"),
ca: ca,
children: children,
threadComments: wpconfig.GetOption("thread_comments") == "1",
page: page,
}
return h.formatComments(ctx, ids, timeout)
}
type CommentHandle struct {
maxDepth int
depth int
isTls bool
html plugins.CommentHtml
order string
page int
ca *cache.MapCache[uint64, models.Comments]
children *cache.MapCache[uint64, []uint64]
threadComments bool
}
func (c CommentHandle) findGrandchildComments(ctx context.Context, timeout time.Duration, comments []models.Comments) ([]models.Comments, error) {
parentIds := slice.Map(comments, func(t models.Comments) uint64 {
return t.CommentId
})
children, err := c.children.GetCacheBatch(ctx, parentIds, timeout)
if err != nil {
return nil, err
}
rr := slice.FilterAndMap(children, func(t []uint64) ([]uint64, bool) {
return t, len(t) > 0
})
if len(rr) < 1 {
return comments, nil
}
ids := slice.Decompress(rr)
r, err := c.ca.GetCacheBatch(ctx, ids, timeout)
if err != nil {
return nil, err
}
rrr, err := c.findGrandchildComments(ctx, timeout, r)
if err != nil {
return nil, err
}
comments = append(comments, rrr...)
slice.Sort(comments, func(i, j models.Comments) bool {
return c.html.FloorOrder(i, j)
})
return comments, nil
}
func (c CommentHandle) formatComments(ctx context.Context, ids []uint64, timeout time.Duration) (html string, err error) {
comments, err := c.ca.GetCacheBatch(ctx, ids, timeout)
if err != nil {
return "", err
}
if c.depth > 1 && c.depth < c.maxDepth {
slice.Sort(comments, func(i, j models.Comments) bool {
return c.html.FloorOrder(i, j)
})
}
fixChildren := false
if c.depth >= c.maxDepth {
comments, err = c.findGrandchildComments(ctx, timeout, comments)
if err != nil {
return "", err
}
fixChildren = true
}
s := str.NewBuilder()
for i, comment := range comments {
eo := "even"
if (i+1)%2 == 0 {
eo = "odd"
}
parent := ""
fl := false
var children []uint64
if !fixChildren {
children, err = c.children.GetCache(ctx, comment.CommentId, timeout)
if err != nil {
return "", err
}
}
if c.threadComments && len(children) > 0 && c.depth < c.maxDepth+1 {
parent = "parent"
fl = true
}
s.WriteString(c.html.FormatLi(ctx, comment, c.depth, c.maxDepth, c.page, c.isTls, c.threadComments, eo, parent))
if fl {
c.depth++
ss, err := c.formatComments(ctx, children, timeout)
if err != nil {
return "", err
}
s.WriteString(`<ol class="children">`, ss, `</ol>`)
c.depth--
}
s.WriteString("</li><!-- #comment-## -->")
}
html = s.String()
return
}

View File

@ -1,103 +1,62 @@
package wp package wp
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"strings" "strings"
) )
var handleComponents = safety.NewMap[string, map[string][]Components[string]]() func (h *Handle) DeleteComponents(scene, name string) {
var handleComponentHook = safety.NewMap[string, map[string][]func(Components[string]) (Components[string], bool)]() h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
return c, c.Name != name
var componentsArgs = safety.NewMap[string, any]()
var componentFilterFns = safety.NewMap[string, []func(*Handle, string, ...any) string]()
func (h *Handle) DeleteComponents(scene, componentKey, componentName string) {
v, ok := handleComponentHook.Load(scene)
if !ok {
v = make(map[string][]func(Components[string]) (Components[string], bool))
}
v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) {
return c, c.Name != componentName
}) })
handleComponentHook.Store(scene, v)
} }
func (h *Handle) ReplaceComponents(scene, componentKey, componentName string, components Components[string]) { func (h *Handle) ReplaceComponents(scene, name string, components Components[string]) {
v, ok := handleComponentHook.Load(scene) h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
if !ok { if c.Name == name {
v = make(map[string][]func(Components[string]) (Components[string], bool))
}
v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) {
if c.Name == componentName {
c = components c = components
} }
return c, true return c, true
}) })
handleComponentHook.Store(scene, v)
} }
func (h *Handle) PushComponentHooks(scene, componentKey string, fn func(Components[string]) (Components[string], bool)) { func (h *Handle) HookComponents(scene string, fn func(Components[string]) (Components[string], bool)) {
v, ok := handleComponentHook.Load(scene) h.componentHook[scene] = append(h.componentHook[scene], fn)
if !ok {
v = make(map[string][]func(Components[string]) (Components[string], bool))
}
v[componentKey] = append(v[componentKey], fn)
handleComponentHook.Store(scene, v)
} }
var GetAndHookComponents = reload.BuildMapFnWithAnyParams[string]("calComponents", HookComponent) func CalComponents(h *Handle) {
componentss := reload.GetAnyValMapBy("scene-components", str.Join("allScene-", h.scene), h, func(h *Handle) map[string][]Components[string] {
func HookComponent(a ...any) []Components[string] { return maps.MergeBy(func(k string, v1, v2 []Components[string]) ([]Components[string], bool) {
componentKey := a[0].(string) vv := append(v1, v2...)
scene := a[1].(string) return vv, vv != nil
mut := reload.GetGlobeMutex() }, nil, h.components[h.scene], h.components[constraints.AllScene])
mut.Lock()
defer mut.Unlock()
components := GetComponents(scene, componentKey)
r := slice.FilterAndMap(components, func(component Components[string]) (Components[string], bool) {
keyHooks, ok := handleComponentHook.Load(scene)
if !ok {
return component, true
}
hooks := keyHooks[componentKey]
for _, fn := range hooks {
hookedComponent, ok := fn(component)
if !ok { // DeleteComponents fn
return hookedComponent, false
}
component = hookedComponent // ReplaceComponents fn
}
return component, true
}) })
slice.SimpleSort(r, slice.DESC, func(t Components[string]) float64 { for k, components := range componentss {
return t.Order key := str.Join("calComponents-", h.scene, "-", k)
}) ss := reload.GetAnyValMapBy("calComponents", key, h, func(h *Handle) []Components[string] {
return r r := slice.FilterAndMap(components, func(t Components[string]) (Components[string], bool) {
} fns, ok := h.componentHook[k]
if !ok {
func GetComponents(scene, key string) (r []Components[string]) { return t, true
sceneComponents, _ := handleComponents.Load(scene) }
allSceneComponents, _ := handleComponents.Load(constraints.AllScene) for _, fn := range fns {
r = append(sceneComponents[key], allSceneComponents[key]...) c, ok := fn(t)
return if !ok {
} return c, false
}
var CacheComponent = reload.BuildMapFnWithAnyParams[string]("cacheComponents", cacheComponentFn) t = c
}
func cacheComponentFn(a ...any) string { return t, true
return a[0].(Components[string]).Fn(a[1].(*Handle)) })
} slice.Sort(r, func(i, j Components[string]) bool {
return i.Order > j.Order
func CalComponent(h *Handle) func(string) string { })
return func(componentKey string) string { return r
cacheKey := str.Join("get-hook-Components-", h.scene, "-", componentKey) })
hookedComponents := GetAndHookComponents(cacheKey, componentKey, h.scene) var s = make([]string, 0, len(ss))
var s = make([]string, 0, len(hookedComponents)) for _, component := range ss {
for _, component := range hookedComponents {
if component.Val != "" { if component.Val != "" {
s = append(s, component.Val) s = append(s, component.Val)
continue continue
@ -105,8 +64,7 @@ func CalComponent(h *Handle) func(string) string {
if component.Fn != nil { if component.Fn != nil {
v := "" v := ""
if component.Cached { if component.Cached {
key := str.Join(h.scene, "-", componentKey, "-", component.Name) v = reload.GetAnyValMapBy("cacheComponents", component.Name, h, component.Fn)
v = CacheComponent(key, component, h)
} else { } else {
v = component.Fn(h) v = component.Fn(h)
} }
@ -115,17 +73,17 @@ func CalComponent(h *Handle) func(string) string {
} }
} }
} }
return strings.Join(s, "\n") h.ginH[k] = strings.Join(s, "\n")
} }
} }
func (h *Handle) PushComponents(scene, componentType string, components ...Components[string]) { func (h *Handle) PushComponents(scene, componentType string, components ...Components[string]) {
c, ok := handleComponents.Load(scene) c, ok := h.components[scene]
if !ok { if !ok {
c = make(map[string][]Components[string]) c = make(map[string][]Components[string])
h.components[scene] = c
} }
c[componentType] = append(c[componentType], components...) c[componentType] = append(c[componentType], components...)
handleComponents.Store(scene, c)
} }
func (h *Handle) PushGroupComponentStr(scene, componentType, name string, order float64, strs ...string) { func (h *Handle) PushGroupComponentStr(scene, componentType, name string, order float64, strs ...string) {
@ -181,8 +139,8 @@ func (h *Handle) PushGroupHeadScript(scene, name string, order float64, str ...s
h.PushGroupComponentStr(scene, constraints.HeadScript, name, order, str...) h.PushGroupComponentStr(scene, constraints.HeadScript, name, order, str...)
} }
func GetComponentsArgs[T any](k string, defaults T) T { func GetComponentsArgs[T any](h *Handle, k string, defaults T) T {
v, ok := componentsArgs.Load(k) v, ok := h.componentsArgs[k]
if ok { if ok {
vv, ok := v.(T) vv, ok := v.(T)
if ok { if ok {
@ -192,61 +150,60 @@ func GetComponentsArgs[T any](k string, defaults T) T {
return defaults return defaults
} }
func PushComponentsArgsForSlice[T any](name string, v ...T) { func PushComponentsArgsForSlice[T any](h *Handle, name string, v ...T) {
val, ok := componentsArgs.Load(name) val, ok := h.componentsArgs[name]
if !ok { if !ok {
var vv []T var vv []T
vv = append(vv, v...) vv = append(vv, v...)
componentsArgs.Store(name, vv) h.componentsArgs[name] = vv
return return
} }
vv, ok := val.([]T) vv, ok := val.([]T)
if ok { if ok {
vv = append(vv, v...) vv = append(vv, v...)
componentsArgs.Store(name, vv) h.componentsArgs[name] = vv
} }
} }
func SetComponentsArgsForMap[K comparable, V any](name string, key K, v V) { func SetComponentsArgsForMap[K comparable, V any](h *Handle, name string, key K, v V) {
val, ok := componentsArgs.Load(name) val, ok := h.componentsArgs[name]
if !ok { if !ok {
vv := make(map[K]V) vv := make(map[K]V)
vv[key] = v vv[key] = v
componentsArgs.Store(name, vv) h.componentsArgs[name] = vv
return return
} }
vv, ok := val.(map[K]V) vv, ok := val.(map[K]V)
if ok { if ok {
vv[key] = v vv[key] = v
componentsArgs.Store(name, vv) h.componentsArgs[name] = vv
} }
} }
func MergeComponentsArgsForMap[K comparable, V any](name string, m map[K]V) { func MergeComponentsArgsForMap[K comparable, V any](h *Handle, name string, m map[K]V) {
val, ok := componentsArgs.Load(name) val, ok := h.componentsArgs[name]
if !ok { if !ok {
componentsArgs.Store(name, m) h.componentsArgs[name] = m
return return
} }
vv, ok := val.(map[K]V) vv, ok := val.(map[K]V)
if ok { if ok {
componentsArgs.Store(name, maps.Merge(vv, m)) h.componentsArgs[name] = maps.Merge(vv, m)
} }
} }
func SetComponentsArgs(key string, value any) { func SetComponentsArgs(h *Handle, key string, value any) {
componentsArgs.Store(key, value) h.componentsArgs[key] = value
} }
func (h *Handle) GetComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) { func (h *Handle) ComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
return componentFilterFns.Load(name) fn, ok := h.componentFilterFn[name]
return fn, ok
} }
func (h *Handle) AddActionFilter(name string, fns ...func(*Handle, string, ...any) string) { func (h *Handle) AddActionFilter(name string, fns ...func(*Handle, string, ...any) string) {
v, _ := componentFilterFns.Load(name) h.componentFilterFn[name] = append(h.componentFilterFn[name], fns...)
v = append(v, fns...)
componentFilterFns.Store(name, v)
} }
func (h *Handle) DoActionFilter(name, s string, args ...any) string { func (h *Handle) DoActionFilter(name, s string, args ...any) string {
calls, ok := componentFilterFns.Load(name) calls, ok := h.componentFilterFn[name]
if ok { if ok {
return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string { return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string {
return fn(h, r, args...) return fn(h, r, args...)

View File

@ -1,7 +1,6 @@
package components package components
import ( import (
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components/block" "github.com/fthvgb1/wp-go/app/theme/wp/components/block"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
@ -30,7 +29,6 @@ func Block(id string) (func(*wp.Handle) string, string) {
if ok { if ok {
s, err := fn(h, id, parserBlock) s, err := fn(h, id, parserBlock)
if err != nil { if err != nil {
logs.Error(err, str.Join("parse block", parserBlock.Name, " fail "), parserBlock)
continue continue
} }
out = append(out, s()) out = append(out, s())

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
constraints2 "github.com/fthvgb1/wp-go/app/pkg/constraints" constraints2 "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
@ -11,7 +12,6 @@ import (
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget" "github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
@ -50,7 +50,7 @@ func parseAttr(attr map[any]any) string {
} }
style := maps.GetAnyAnyValWithDefaults[map[any]any](attr, nil, "style", "typography") style := maps.GetAnyAnyValWithDefaults[map[any]any](attr, nil, "style", "typography")
if len(style) > 0 { if len(style) > 0 {
styless := maps.AnyAnyMapTo(style, func(k, v any) (string, string, bool) { styless := maps.AnyAnyMap(style, func(k, v any) (string, string, bool) {
kk, ok := k.(string) kk, ok := k.(string)
if !ok { if !ok {
return "", "", false return "", "", false
@ -71,61 +71,48 @@ func parseAttr(attr map[any]any) string {
return strings.Join(attrs, " ") return strings.Join(attrs, " ")
} }
var GetCategoryAttr = reload.BuildValFn("block-category-attr", parseAttr)
var GetCategoryConf = reload.BuildValFnWithConfirm("block-category-conf", categoryConfFn, 5)
func categoryConfFn(blockParser ParserBlock) (map[any]any, bool) {
var con any
err := json.Unmarshal([]byte(blockParser.Attrs), &con)
if err != nil {
logs.Error(err, "解析category attr错误", blockParser.Attrs)
return nil, false
}
var conf map[any]any
switch con.(type) {
case map[any]any:
conf = con.(map[any]any)
case map[string]any:
conf = maps.StrAnyToAnyAny(con.(map[string]any))
}
conf = maps.FilterZeroMerge(categoryConf(), conf)
if maps.GetAnyAnyValWithDefaults(conf, false, "showPostCounts") {
conf["count"] = int64(1)
}
if maps.GetAnyAnyValWithDefaults(conf, false, "displayAsDropdown") {
conf["dropdown"] = int64(1)
}
if maps.GetAnyAnyValWithDefaults(conf, false, "showHierarchy") {
conf["hierarchical"] = int64(1)
}
class := maps.GetAnyAnyValWithDefaults(conf, "", "className")
classes := strings.Split(class, " ")
classes = append(classes, "wp-block-categories")
if conf["dropdown"].(int64) == 1 {
classes = append(classes, "wp-block-categories-dropdown")
conf["className"] = strings.Join(classes, " ")
} else {
classes = append(classes, "wp-block-categories-list")
conf["className"] = strings.Join(classes, " ")
}
return conf, true
}
var GetCategoryArgs = reload.BuildValFnWithAnyParams("block-category-args", categoryArgs)
func categoryArgs(_ ...any) map[string]string {
args := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
return maps.FilterZeroMerge(categoryDefaultArgs(), args)
}
func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) { func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) {
counter := number.Counters[int]() counter := number.Counters[int]()
var err error var err error
conf := GetCategoryConf(blockParser) conf := reload.GetAnyValBys("block-category-conf", h, func(h *wp.Handle) map[any]any {
var con any
err = json.Unmarshal([]byte(blockParser.Attrs), &con)
if err != nil {
logs.Error(err, "解析category attr错误", blockParser.Attrs)
return nil
}
var conf map[any]any
switch con.(type) {
case map[any]any:
conf = con.(map[any]any)
case map[string]any:
conf = maps.StrAnyToAnyAny(con.(map[string]any))
}
conf = maps.FilterZeroMerge(categoryConf(), conf)
if maps.GetAnyAnyValWithDefaults(conf, false, "showPostCounts") {
conf["count"] = int64(1)
}
if maps.GetAnyAnyValWithDefaults(conf, false, "displayAsDropdown") {
conf["dropdown"] = int64(1)
}
if maps.GetAnyAnyValWithDefaults(conf, false, "showHierarchy") {
conf["hierarchical"] = int64(1)
}
class := maps.GetAnyAnyValWithDefaults(conf, "", "className")
classes := strings.Split(class, " ")
classes = append(classes, "wp-block-categories")
if conf["dropdown"].(int64) == 1 {
classes = append(classes, "wp-block-categories-dropdown")
conf["className"] = strings.Join(classes, " ")
} else {
classes = append(classes, "wp-block-categories-list")
conf["className"] = strings.Join(classes, " ")
}
return conf
})
if err != nil { if err != nil {
return nil, err return nil, err
@ -140,7 +127,10 @@ func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string,
if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") { if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") {
h.C.Set("showOnlyTopLevel", true) h.C.Set("showOnlyTopLevel", true)
} }
args := GetCategoryArgs() args := reload.GetAnyValBys("block-category-args", h, func(h *wp.Handle) map[string]string {
args := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
return maps.FilterZeroMerge(categoryDefaultArgs(), args)
})
return func() string { return func() string {
return category(h, id, counter, args, conf) return category(h, id, counter, args, conf)
@ -160,21 +150,21 @@ func category(h *wp.Handle, id string, counter number.Counter[int], args map[str
return str.Join(before, out, args["{$after_widget}"]) return str.Join(before, out, args["{$after_widget}"])
} }
func categoryUl(h *wp.Handle, categories []models.TermsMy, confAttr map[any]any) string { func categoryUl(h *wp.Handle, categories []models.TermsMy, conf map[any]any) string {
s := str.NewBuilder() s := str.NewBuilder()
li := widget.CategoryLi(h, confAttr, categories) li := widget.CategoryLi(h, conf, categories)
attrs := GetCategoryAttr(confAttr) attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
s.Sprintf(`<ul %s>%s</ul>`, attrs, li) s.Sprintf(`<ul %s>%s</ul>`, attrs, li)
return s.String() return s.String()
} }
func dropdown(h *wp.Handle, categories []models.TermsMy, id int, args map[string]string, confAttr map[any]any) string { func dropdown(h *wp.Handle, categories []models.TermsMy, id int, args map[string]string, conf map[any]any) string {
s := str.NewBuilder() s := str.NewBuilder()
ids := fmt.Sprintf(`wp-block-categories-%v`, id) ids := fmt.Sprintf(`wp-block-categories-%v`, id)
args = maps.Copy(args) args = maps.Copy(args)
args["{$selectId}"] = ids args["{$selectId}"] = ids
attrs := GetCategoryAttr(confAttr) attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
selects := widget.DropdownCategories(h, args, confAttr, categories) selects := widget.DropdownCategories(h, args, conf, categories)
s.Sprintf(`<div %s><label class="screen-reader-text" for="%s">%s</label>%s%s</div>`, attrs, ids, args["{$title}"], selects, strings.ReplaceAll(categoryDropdownScript, "{$id}", ids)) s.Sprintf(`<div %s><label class="screen-reader-text" for="%s">%s</label>%s%s</div>`, attrs, ids, args["{$title}"], selects, strings.ReplaceAll(categoryDropdownScript, "{$id}", ids))
return s.String() return s.String()
} }

View File

@ -2,11 +2,11 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
@ -40,29 +40,21 @@ var archivesConfig = map[any]any{
"title": "归档", "title": "归档",
} }
var GetArchiveConf = BuildconfigFn(archivesConfig, "widget_archives", int64(2))
var GetArchiveArgs = reload.BuildValFnWithAnyParams("widget_archive-args", archiveArgsFn)
func archiveArgsFn(a ...any) map[string]string {
h := a[0].(*wp.Handle)
conf := a[1].(map[any]any)
id := a[2].(string)
archiveArgs := archiveArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, CommonArgs())
args := wp.GetComponentsArgs(widgets.Archive, archiveArgs)
args = maps.FilterZeroMerge(archiveArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("archives-", id), str.Join("widget widget_", "archive"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"].(string))
args["{$navCloser}"] = "</nav>"
}
return args
}
func Archive(h *wp.Handle, id string) string { func Archive(h *wp.Handle, id string) string {
conf := GetArchiveConf() conf := configs(archivesConfig, "widget_archives", int64(2))
args := GetArchiveArgs(h, conf, id) args := reload.GetAnyValBys("widget-archive-args", h, func(h *wp.Handle) map[string]string {
archiveArgs := archiveArgs()
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, CommonArgs())
args := wp.GetComponentsArgs(h, widgets.Archive, archiveArgs)
args = maps.FilterZeroMerge(archiveArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("archives-", id), str.Join("widget widget_", "archive"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"].(string))
args["{$navCloser}"] = "</nav>"
}
return args
})
s := archiveTemplate s := archiveTemplate
if int64(1) == conf["dropdown"].(int64) { if int64(1) == conf["dropdown"].(int64) {
@ -91,12 +83,11 @@ var dropdownScript = `
func archiveDropDown(h *wp.Handle, conf map[any]any, args map[string]string, archives []models.PostArchive) string { func archiveDropDown(h *wp.Handle, conf map[any]any, args map[string]string, archives []models.PostArchive) string {
option := str.NewBuilder() option := str.NewBuilder()
option.Sprintf(`<option value="">%s</option>`, args["{$dropdown_label}"]) option.Sprintf(`<option value="">%s</option>`, args["{$dropdown_label}"])
i := h.GetIndexHandle() month := strings.TrimLeft(h.Index.Param.Month, "0")
month := strings.TrimLeft(i.Param.Month, "0")
showCount := conf["count"].(int64) showCount := conf["count"].(int64)
for _, archive := range archives { for _, archive := range archives {
sel := "" sel := ""
if i.Param.Year == archive.Year && month == archive.Month { if h.Index.Param.Year == archive.Year && month == archive.Month {
sel = "selected" sel = "selected"
} }
count := "" count := ""

View File

@ -2,16 +2,17 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/helper/tree" "github.com/fthvgb1/wp-go/helper/tree"
"net/http"
"strings" "strings"
) )
@ -44,29 +45,22 @@ func categoryArgs() map[string]string {
} }
} }
var GetCategoryConf = BuildconfigFn(categoryConfig, "widget_categories", int64(2))
var GetCategoryArgs = reload.BuildValFnWithAnyParams("widget-category-args", categoryArgsFn)
func categoryArgsFn(a ...any) map[string]string {
h := a[0].(*wp.Handle)
conf := a[1].(map[any]any)
id := a[2].(string)
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.Categories, categoryArgs())
args = maps.FilterZeroMerge(categoryArgs(), CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("categories-", id), str.Join("widget widget_", "categories"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{title}"])
args["{$navCloser}"] = "</nav>"
}
return args
}
func Category(h *wp.Handle, id string) string { func Category(h *wp.Handle, id string) string {
conf := GetCategoryConf() conf := configs(categoryConfig, "widget_categories", int64(2))
args := GetCategoryArgs(h, conf, id)
args := reload.GetAnyValBys("widget-category-args", h, func(h *wp.Handle) map[string]string {
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(h, widgets.Categories, categoryArgs())
args = maps.FilterZeroMerge(categoryArgs(), CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("categories-", id), str.Join("widget widget_", "categories"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{title}"])
args["{$navCloser}"] = "</nav>"
}
return args
})
t := categoryTemplate t := categoryTemplate
dropdown := conf["dropdown"].(int64) dropdown := conf["dropdown"].(int64)
categories := cache.CategoriesTags(h.C, constraints.Category) categories := cache.CategoriesTags(h.C, constraints.Category)
@ -208,9 +202,8 @@ func DropdownCategories(h *wp.Handle, args map[string]string, conf map[any]any,
s.Sprintf(` <option value="-1">%s</option> s.Sprintf(` <option value="-1">%s</option>
`, args["{$show_option_none}"]) `, args["{$show_option_none}"])
currentCategory := "" currentCategory := ""
i := h.GetIndexHandle()
if h.Scene() == constraints.Category { if h.Scene() == constraints.Category {
currentCategory = i.Param.Category currentCategory = h.Index.Param.Category
} }
showCount := conf["count"].(int64) showCount := conf["count"].(int64)
fn := func(category models.TermsMy, deep int) { fn := func(category models.TermsMy, deep int) {
@ -242,8 +235,41 @@ func DropdownCategories(h *wp.Handle, args map[string]string, conf map[any]any,
return h.DoActionFilter("wp_dropdown_cats", s.String()) return h.DoActionFilter("wp_dropdown_cats", s.String())
} }
func CheckCategory(h *wp.Handle) {
name, ok := parseDropdownCate(h)
if ok {
h.C.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/p/category/%s", name))
h.Abort()
}
}
func parseDropdownCate(h *wp.Handle) (cateName string, r bool) {
cate := wp.GetComponentsArgs[map[string]string](h, widgets.Categories, categoryArgs())
name, ok := cate["{$name}"]
if !ok || name == "" {
return
}
cat := h.C.Query(name)
if cat == "" {
return
}
id := str.ToInteger[uint64](cat, 0)
if id < 1 {
return
}
i, cc := slice.SearchFirst(cache.CategoriesTags(h.C, constraints.Category), func(my models.TermsMy) bool {
return id == my.Terms.TermId
})
if i < 0 {
return
}
r = true
cateName = cc.Name
return
}
func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) { func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) {
cate := wp.GetComponentsArgs[map[string]string](widgets.Categories, categoryArgs()) cate := wp.GetComponentsArgs[map[string]string](h, widgets.Categories, categoryArgs())
name, ok := cate["{$name}"] name, ok := cate["{$name}"]
if !ok || name == "" { if !ok || name == "" {
return return
@ -266,12 +292,3 @@ func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) {
category = cc category = cc
return return
} }
func CategoryQueryName(h *wp.Handle) string {
cate := wp.GetComponentsArgs[map[string]string](widgets.Categories, categoryArgs())
name, ok := cate["{$name}"]
if ok {
return name
}
return ""
}

View File

@ -1,9 +1,9 @@
package widget package widget
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
) )
@ -14,13 +14,9 @@ func Fn(id string, fn func(*wp.Handle, string) string) func(h *wp.Handle) string
} }
} }
func configFns[K comparable, V any](m map[K]V, key string, a ...any) func(_ ...any) map[K]V { func configs[M ~map[K]V, K comparable, V any](m M, key string, a ...any) M {
return func(_ ...any) map[K]V { return reload.GetAnyValBys(str.Join("widget-config-", key), key, func(_ string) M {
c := wpconfig.GetPHPArrayVal[map[K]V](key, nil, a...) c := wpconfig.GetPHPArrayVal[M](key, nil, a...)
return maps.FilterZeroMerge(maps.Copy(m), c) return maps.FilterZeroMerge(maps.Copy(m), c)
} })
}
func BuildconfigFn[K comparable, V any](m map[K]V, key string, a ...any) func(_ ...any) map[K]V {
return reload.BuildValFnWithAnyParams(str.Join("widget-config-", key), configFns(m, key, a...))
} }

View File

@ -2,10 +2,10 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
@ -21,39 +21,34 @@ var metaTemplate = `{$before_widget}
{$navCloser} {$navCloser}
{$after_widget}` {$after_widget}`
func defaultMetaArgs() map[string]string { func metaArgs() map[string]string {
return map[string]string{ return map[string]string{
"{$aria_label}": "", "{$aria_label}": "",
"{$title}": "", "{$title}": "",
} }
} }
var GetMetaArgs = reload.BuildValFnWithAnyParams("widget-meta-args", ParseMetaArgs)
func ParseMetaArgs(a ...any) map[string]string {
h := a[0].(*wp.Handle)
id := a[1].(string)
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
metaArgs := defaultMetaArgs()
args := wp.GetComponentsArgs(widgets.Meta, metaArgs)
args = maps.FilterZeroMerge(metaArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("meta-", id), str.Join("widget widget_", "meta"))
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_meta", "其它操作", int64(2), "title")
if args["{$title}"] == "" {
args["{$title}"] = "其他操作"
}
if args["{$title}"] != "" {
args["{$h2title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
}
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{$title}"])
args["{$navCloser}"] = "</nav>"
}
return args
}
func Meta(h *wp.Handle, id string) string { func Meta(h *wp.Handle, id string) string {
args := GetMetaArgs(h, id) args := reload.GetAnyValBys("widget-meta-args", h, func(h *wp.Handle) map[string]string {
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
metaArgs := metaArgs()
args := wp.GetComponentsArgs(h, widgets.Meta, metaArgs)
args = maps.FilterZeroMerge(metaArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("meta-", id), str.Join("widget widget_", "meta"))
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_meta", "其它操作", int64(2), "title")
if args["{$title}"] == "" {
args["{$title}"] = "其他操作"
}
if args["{$title}"] != "" {
args["{$h2title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
}
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{$title}"])
args["{$navCloser}"] = "</nav>"
}
return args
})
ss := str.NewBuilder() ss := str.NewBuilder()
if str.ToInteger(wpconfig.GetOption("users_can_register"), 0) > 0 { if str.ToInteger(wpconfig.GetOption("users_can_register"), 0) > 0 {
ss.Sprintf(`<li><a href="/wp-login.php?action=register">注册</li>`) ss.Sprintf(`<li><a href="/wp-login.php?action=register">注册</li>`)

View File

@ -2,11 +2,11 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
@ -39,36 +39,29 @@ var recentCommentsTemplate = `{$before_widget}
{$after_widget} {$after_widget}
` `
var GetRecentCommentConf = BuildconfigFn(recentCommentConf, "widget_recent-comments", int64(2))
var GetRecentCommentArgs = reload.BuildValFnWithAnyParams("widget-recent-comment-args", RecentCommentArgs)
func RecentCommentArgs(a ...any) map[string]string {
h := a[0].(*wp.Handle)
conf := a[1].(map[any]any)
id := a[2].(string)
commentsArgs := recentCommentsArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.RecentComments, commentsArgs)
args = maps.FilterZeroMerge(commentsArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-comments-", id), str.Join("widget widget_", "recent_comments"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
args["{$navCloser}"] = "</nav>"
}
return args
}
func RecentComments(h *wp.Handle, id string) string { func RecentComments(h *wp.Handle, id string) string {
conf := GetRecentCommentConf() conf := configs(recentCommentConf, "widget_recent-comments", int64(2))
args := GetRecentCommentArgs(h, conf, id)
args := reload.GetAnyValBys("widget-recent-comment-args", h, func(h *wp.Handle) map[string]string {
commentsArgs := recentCommentsArgs()
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(h, widgets.RecentComments, commentsArgs)
args = maps.FilterZeroMerge(commentsArgs, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-comments-", id), str.Join("widget widget_", "recent_comments"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
args["{$navCloser}"] = "</nav>"
}
return args
})
comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string { comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string {
return fmt.Sprintf(` <li> return fmt.Sprintf(` <li>
<span class="comment-author-link">%s</span>发表在 <span class="comment-author-link">%s</span>发表在
<a href="%s">%s</a> <a href="/p/%v#comment-%v">%s</a>
</li>`, t.CommentAuthor, t.CommentAuthorUrl, t.PostTitle) </li>`, t.CommentAuthor, t.CommentPostId, t.CommentId, t.PostTitle)
}) })
s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n")) s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n"))
return h.DoActionFilter(widgets.RecentComments, str.Replace(s, args)) return h.DoActionFilter(widgets.RecentComments, str.Replace(s, args))

View File

@ -2,13 +2,13 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
@ -25,7 +25,7 @@ var recentPostsTemplate = `{$before_widget}
{$after_widget} {$after_widget}
` `
func DefaultRecentPostsArgs() map[string]string { func recentPostsArgs() map[string]string {
return map[string]string{ return map[string]string{
"{$before_sidebar}": "", "{$before_sidebar}": "",
"{$after_sidebar}": "", "{$after_sidebar}": "",
@ -35,7 +35,7 @@ func DefaultRecentPostsArgs() map[string]string {
} }
} }
func DefaultRecentConf() map[any]any { func recentConf() map[any]any {
return map[any]any{ return map[any]any{
"number": int64(5), "number": int64(5),
"show_date": false, "show_date": false,
@ -43,37 +43,28 @@ func DefaultRecentConf() map[any]any {
} }
} }
var GetRecentPostConf = reload.BuildValFnWithAnyParams("widget-recent-posts-conf", RecentPostConf)
func RecentPostConf(_ ...any) map[any]any {
recent := DefaultRecentConf()
conf := wpconfig.GetPHPArrayVal[map[any]any]("widget_recent-posts", recent, int64(2))
conf = maps.FilterZeroMerge(recent, conf)
return conf
}
var GetRecentPostArgs = reload.BuildValFnWithAnyParams("widget-recent-posts-args", ParseRecentPostArgs)
func ParseRecentPostArgs(a ...any) map[string]string {
h := a[0].(*wp.Handle)
conf := a[1].(map[any]any)
id := a[2].(string)
recent := DefaultRecentPostsArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.RecentPosts, recent)
args = maps.FilterZeroMerge(recent, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-posts-", id), str.Join("widget widget_", "recent_entries"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
args["{$navCloser}"] = "</nav>"
}
return args
}
func RecentPosts(h *wp.Handle, id string) string { func RecentPosts(h *wp.Handle, id string) string {
conf := GetRecentPostConf() conf := reload.GetAnyValBys("widget-recent-posts-conf", h, func(h *wp.Handle) map[any]any {
args := GetRecentPostArgs(h, conf, id) recent := recentConf()
conf := wpconfig.GetPHPArrayVal[map[any]any]("widget_recent-posts", recent, int64(2))
conf = maps.FilterZeroMerge(recent, conf)
return conf
})
args := reload.GetAnyValBys("widget-recent-posts-args", h, func(h *wp.Handle) map[string]string {
recent := recentPostsArgs()
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(h, widgets.RecentPosts, recent)
args = maps.FilterZeroMerge(recent, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-posts-", id), str.Join("widget widget_", "recent_entries"))
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
args["{$navCloser}"] = "</nav>"
}
return args
})
currentPostId := uint64(0) currentPostId := uint64(0)
if h.Scene() == constraints.Detail { if h.Scene() == constraints.Detail {
currentPostId = str.ToInteger(h.C.Param("id"), uint64(0)) currentPostId = str.ToInteger(h.C.Param("id"), uint64(0))

View File

@ -2,11 +2,11 @@ package widget
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/html" "github.com/fthvgb1/wp-go/helper/html"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
@ -47,40 +47,34 @@ func searchArgs() map[string]string {
var form = html5SearchForm var form = html5SearchForm
var GetSearchArgs = reload.BuildValFnWithAnyParams("widget-search-args", ParseSearchArgs)
func ParseSearchArgs(a ...any) map[string]string {
h := a[0].(*wp.Handle)
id := a[1].(string)
search := searchArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.Search, search)
args = maps.FilterZeroMerge(search, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("search-", id), str.Join("widget widget_", "search"))
if args["{$title}"] == "" {
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_search", "", int64(2), "title")
}
if args["{$title}"] != "" {
args["{$title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
}
if args["{$form}"] != "" {
form = args["{$form}"]
delete(args, "{$form}")
}
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
form = xmlSearchForm
}
return args
}
func Search(h *wp.Handle, id string) string { func Search(h *wp.Handle, id string) string {
args := GetSearchArgs(h, id) args := reload.GetAnyValBys("widget-search-args", h, func(h *wp.Handle) map[string]string {
search := searchArgs()
commonArgs := wp.GetComponentsArgs(h, widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(h, widgets.Search, search)
args = maps.FilterZeroMerge(search, CommonArgs(), commonArgs, args)
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("search-", id), str.Join("widget widget_", "search"))
if args["{$title}"] == "" {
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_search", "", int64(2), "title")
}
if args["{$title}"] != "" {
args["{$title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
}
if args["{$form}"] != "" {
form = args["{$form}"]
delete(args, "{$form}")
}
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
form = xmlSearchForm
}
return args
})
s := strings.ReplaceAll(searchTemplate, "{$form}", form) s := strings.ReplaceAll(searchTemplate, "{$form}", form)
val := "" val := ""
if h.Scene() == constraints.Search { if h.Scene() == constraints.Search {
val = html.SpecialChars(h.GetIndexHandle().Param.Search) val = html.SpecialChars(h.Index.Param.Search)
} }
s = strings.ReplaceAll(s, "{$value}", val) s = strings.ReplaceAll(s, "{$value}", val)
return h.DoActionFilter(widgets.Search, str.Replace(s, args)) return h.DoActionFilter(widgets.Search, str.Replace(s, args))

View File

@ -0,0 +1,23 @@
package widget
import (
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
)
func IsTag(h *wp.Handle) (models.TermsMy, bool) {
if h.Scene() == constraints.Tag {
id := str.ToInt[uint64](h.C.Query("tag"))
i, t := slice.SearchFirst(cache.CategoriesTags(h.C, constraints.Tag), func(my models.TermsMy) bool {
return id == my.Terms.TermId
})
if i > 0 {
return t, true
}
}
return models.TermsMy{}, false
}

View File

@ -3,38 +3,32 @@ package wp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
"regexp" "regexp"
"strings"
) )
func (h *Handle) DisplayHeaderText() bool { func (h *Handle) DisplayHeaderText() bool {
return h.themeMods.ThemeSupport.CustomHeader.HeaderText && "blank" != h.themeMods.HeaderTextcolor return h.themeMods.ThemeSupport.CustomHeader.HeaderText && "blank" != h.themeMods.HeaderTextcolor
} }
var GetCustomHeaderImgFn = reload.BuildValFnWithConfirm("headerImages", customHeadImag, 5)
func customHeadImag(h *Handle) ([]models.PostThumbnail, bool) {
hs, err := h.GetHeaderImages(h.theme)
if err != nil {
h.SetErr(fmt.Errorf("get customheadimage err: %v", err), Low)
return nil, false
}
return hs, true
}
func (h *Handle) GetCustomHeaderImg() (r models.PostThumbnail, isRand bool) { func (h *Handle) GetCustomHeaderImg() (r models.PostThumbnail, isRand bool) {
var err error var err error
img := GetCustomHeaderImgFn(h) img := reload.GetAnyValBys("headerImages", h.theme, func(theme string) []models.PostThumbnail {
err = h.Err() hs, er := h.GetHeaderImages(h.theme)
if err != nil && strings.Contains(err.Error(), "get customheadimage err") { if er != nil {
err = er
return nil
}
return hs
})
if err != nil {
logs.Error(err, "获取页眉背景图失败") logs.Error(err, "获取页眉背景图失败")
return return
} }
@ -128,7 +122,7 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
logs.Error(err, "get headerVideo fail", mod.HeaderVideo) logs.Error(err, "get headerVideo fail", mod.HeaderVideo)
return return
} }
scripts := []string{ scriptss := []string{
"/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js", "/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js",
"/wp-includes/js/dist/vendor/regenerator-runtime.min.js", "/wp-includes/js/dist/vendor/regenerator-runtime.min.js",
"/wp-includes/js/dist/vendor/wp-polyfill.min.js", "/wp-includes/js/dist/vendor/wp-polyfill.min.js",
@ -138,16 +132,10 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
"/wp-includes/js/dist/a11y.min.js", "/wp-includes/js/dist/a11y.min.js",
"/wp-includes/js/wp-custom-header.min.js", "/wp-includes/js/wp-custom-header.min.js",
} }
scripts = slice.Map(scripts, func(t string) string { scriptss = slice.Map(scriptss, func(t string) string {
return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script> return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script>
`, t, str.Replaces(t, []string{ `, t, str.Replaces(t, []string{
"/wp-includes/js/dist/vendor/", "/wp-includes/js/dist/vendor/", "/wp-includes/js/dist/", "/wp-includes/js/", ".min.js", ".js", "wp-", "",
"/wp-includes/js/dist/",
"/wp-includes/js/",
".min.js",
".js",
"wp-",
"",
})) }))
}) })
@ -166,10 +154,10 @@ wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );
c := []Components[string]{ c := []Components[string]{
NewComponent("wp-a11y-js-translations", tr, true, 10.0065, nil), NewComponent("wp-a11y-js-translations", tr, true, 10.0065, nil),
NewComponent("VideoSetting", hs, true, 10.0064, nil), NewComponent("VideoSetting", hs, true, 10.0064, nil),
NewComponent("header-script", scripts[len(scripts)-1], true, 10.0063, nil), NewComponent("header-script", scriptss[len(scriptss)-1], true, 10.0063, nil),
} }
for _, s := range scene { for _, s := range scene {
h.PushGroupFooterScript(s, "wp-custom-header", 10.0066, scripts[0:len(scripts)-2]...) h.PushGroupFooterScript(s, "wp-custom-header", 10.0066, scriptss[0:len(scriptss)-2]...)
h.PushFooterScript(s, c...) h.PushFooterScript(s, c...)
} }
ok = true ok = true

View File

@ -2,9 +2,9 @@ package wp
import ( import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
) )
@ -42,10 +42,10 @@ func CalCustomLogo(h *Handle) (r string) {
return return
} }
var GetCustomLog = reload.BuildValFn("customLogo", CalCustomLogo)
func customLogo(h *Handle) func() string { func customLogo(h *Handle) func() string {
return func() string { return func() string {
return GetCustomLog(h) return reload.GetAnyValBys("customLogo", h, func(h *Handle) string {
return CalCustomLogo(h)
})
} }
} }

View File

@ -0,0 +1,106 @@
<?php
/**
* wordpress/wp-includes/script-loader.php:632
* @var $scripts
*/
$con = 'package wp
import "github.com/fthvgb1/wp-go/safety"
func defaultScripts(m *safety.Map[string, *Script], suffix string){
';
foreach ($scripts->registered as $handle => $h) {
$dep = 'nil';
if ($h->deps) {
$dep = '[]string{';
$dep .= implode(',', array_map(fn($v) => '"' . $v . '"', $h->deps));
$dep .= '}';
}
$con .= sprintf('m.Store("%s", NewScript("%s", "%s"+suffix+".js", %s, "%s", %s))
', $handle, $h->handle, str_replace('.min.js', '', $h->src), $dep, $h->ver, $h->args ?: 'nil');
}
$con .= '}';
file_put_contents('/tmp/scriptLoad.go', $con);
/**
* put code to wordpress/wp-includes/class-wp-scripts.php:504
* @param $handle
* @param $object_name
* @param $l10n
* @return void
*/
function parseLocalize($handle, $object_name, $l10n): void
{
if ('utils' != $handle) {
$s = array_map('parseArr', array_keys($l10n), array_values($l10n));
$m = implode("\n", $s);
$x = sprintf('AddStaticLocalize("%s","%s",map[string]any{
%s
})
', $handle, $object_name, $m);
file_put_contents('/tmp/bb.go', $x, FILE_APPEND);
}
}
function parseArr($k, $v): string
{
/**
* @var $this object
*/
if (is_array($v)) {
if (array_diff_key(array_values($v), $v)) {
$x = '';
foreach ($v as $kk => $vv) {
$x .= parseArr($kk, $vv);
}
return sprintf('"%s":map[string]any{
%s
},', $k, $x);
} else {
$s = array_map(fn($ss) => sprintf('"%s"', $ss), $v);
return sprintf('"%s":[]string{%s},', $k, implode(',', $s));
}
} else {
return sprintf('"%s":`%s`,', $k, $v);
}
}
/**
* /var/www/html/wordpress/wp-includes/class-wp-theme-json.php:1712
* @return void
*/
function presetsMetadata()
{
$ss = <<<go
{
path: []string{%s},
preventOverride: []string{%s},
useDefaultNames: %s,
valueKey: "%s",
valueFunc: %s,
cssVars: "%s",
classes: map[string]string{
%s
},
properties: []string{%s},
},
go;
$s = '';
foreach (static::PRESETS_METADATA as $val) {
$arr = [];
$arr[] = implode(',', array_map(fn($v) => '"' . $v . '"', $val['path']));
$arr[] = (!$val['prevent_override']) ? '' : implode(',', array_map(fn($v) => '"' . $v . '"', $val['prevent_override']));
$arr[] = $val['use_default_names'] ? 'true' : 'false';
$arr[] = $val['value_key'] ?? '';
$arr[] = $val['value_func'] ?? 'nil';
$arr[] = $val['css_vars'];
$arr[] = implode(",\n", array_map(fn($k, $v) => sprintf('"%s":"%s"', $k, $v), array_keys($val['classes']), array_values($val['classes'])));
$arr[] = implode(',', array_map(fn($v) => '"' . $v . '"', $val['properties']));
$s .= sprintf($ss, ...$arr);
}
echo $s;
}

View File

@ -10,34 +10,18 @@ import (
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/plugins/wpposts" "github.com/fthvgb1/wp-go/app/plugins/wpposts"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/pagination"
"strings"
"time"
) )
type DetailHandle struct { type DetailHandle struct {
*Handle *Handle
CommentRender plugins.CommentHtml CommentRender plugins.CommentHtml
Comments []uint64 Comments []models.Comments
Page int Post models.Posts
Limit int
Post models.Posts
CommentPageEle pagination.Render
TotalRaw int
TotalPage int
CommentStep int
} }
func NewDetailHandle(handle *Handle) *DetailHandle { func NewDetailHandle(handle *Handle) *DetailHandle {
return &DetailHandle{ return &DetailHandle{Handle: handle}
Handle: handle,
Page: 1,
Limit: 5,
CommentStep: 1,
}
} }
func (d *DetailHandle) BuildDetailData() (err error) { func (d *DetailHandle) BuildDetailData() (err error) {
@ -46,7 +30,7 @@ func (d *DetailHandle) BuildDetailData() (err error) {
if err != nil { if err != nil {
return return
} }
d.CommentData() d.Comment()
d.ContextPost() d.ContextPost()
return return
} }
@ -84,66 +68,12 @@ func (d *DetailHandle) PasswordProject() {
} }
} }
} }
func (h *Handle) GetDetailHandle() *DetailHandle { func (d *DetailHandle) Comment() {
v, ok := h.C.Get("detailHandle") comments, err := cache.PostComments(d.C, d.Post.Id)
if !ok { logs.IfError(err, "get d.Post comment", d.Post.Id)
vv := NewDetailHandle(h) d.ginH["comments"] = comments
h.C.Set("detailHandle", vv) d.Comments = comments
return vv
}
return v.(*DetailHandle)
}
func (d *DetailHandle) CommentData() {
d.ginH["totalCommentNum"] = 0
d.ginH["totalCommentPage"] = 1
d.ginH["commentPageNav"] = ""
order := wpconfig.GetOption("comment_order")
d.ginH["commentOrder"] = order
d.Limit = str.ToInteger(wpconfig.GetOption("comments_per_page"), 5)
pageComments := wpconfig.GetOption("page_comments")
num, err := cachemanager.GetBy[int]("commentNumber", d.C, d.Post.Id, time.Second)
if err != nil {
d.SetErr(err, Low)
return
}
if num < 1 {
return
}
topNum, err := cachemanager.GetBy[int]("postTopCommentsNum", d.C, d.Post.Id, time.Second)
if err != nil {
d.SetErr(err, Low)
return
}
d.TotalPage = number.DivideCeil(topNum, d.Limit)
if !strings.Contains(d.C.Request.URL.Path, "comment-page") {
defaultCommentsPage := wpconfig.GetOption("default_comments_page")
if order == "desc" && defaultCommentsPage == "oldest" || order == "asc" && defaultCommentsPage == "newest" {
d.C.AddParam("page", number.IntToString(d.TotalPage))
}
}
d.Page = str.ToInteger(d.C.Param("page"), 1)
d.ginH["currentPage"] = d.Page
var key string
if pageComments != "1" {
key = number.IntToString(d.Post.Id)
d.Limit = 0
} else {
key = fmt.Sprintf("%d-%d-%d", d.Post.Id, d.Page, d.Limit)
}
d.ginH["page_comments"] = pageComments
d.ginH["totalCommentPage"] = d.TotalPage
if d.TotalPage < d.Page {
d.SetErr(errors.New("curren page above total page"), High)
return
}
data, err := cache.PostTopLevelCommentIds(d.C, d.Post.Id, d.Page, d.Limit, topNum, order, key)
if err != nil {
d.SetErr(err, Low)
return
}
d.TotalRaw = topNum
d.ginH["totalCommentNum"] = num
d.Comments = data
} }
func (d *DetailHandle) RenderComment() { func (d *DetailHandle) RenderComment() {
@ -156,21 +86,9 @@ func (d *DetailHandle) RenderComment() {
ableComment = false ableComment = false
} }
d.ginH["showComment"] = ableComment d.ginH["showComment"] = ableComment
d.ginH["comments"] = "" if len(d.Comments) > 0 && ableComment {
if len(d.Comments) < 0 || !ableComment { dep := str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5)
return d.ginH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep)
}
var err error
d.ginH["comments"], err = RenderComment(d.C, d.Page, d.CommentRender, d.Comments, 2*time.Second, d.IsHttps())
if err != nil {
d.SetErr(err, High)
return
}
if d.CommentPageEle == nil {
d.CommentPageEle = plugins.TwentyFifteenCommentPagination()
}
if wpconfig.GetOption("page_comments") == "1" && d.TotalPage > 1 {
d.ginH["commentPageNav"] = pagination.Paginate(d.CommentPageEle, d.TotalRaw, d.Limit, d.Page, d.CommentStep, *d.C.Request.URL, d.IsHttps())
} }
} }
@ -185,17 +103,16 @@ func DetailRender(h *Handle) {
if h.Stats != constraints.Ok { if h.Stats != constraints.Ok {
return return
} }
d := h.GetDetailHandle() d := h.Detail
d.PasswordProject() d.PasswordProject()
d.RenderComment() d.RenderComment()
d.ginH["post"] = d.Post d.ginH["post"] = d.Post
} }
func Detail(h *Handle) { func Detail(h *Handle) {
d := h.GetDetailHandle() err := h.Detail.BuildDetailData()
err := d.BuildDetailData()
if err != nil { if err != nil {
d.SetErr(err, High) h.Detail.SetErr(err)
} }
h.SetData("scene", h.Scene()) h.SetData("scene", h.Scene())
} }
@ -203,7 +120,7 @@ func Detail(h *Handle) {
func ReplyCommentJs(h *Handle) { func ReplyCommentJs(h *Handle) {
h.PushFooterScript(constraints.Detail, NewComponent("comment-reply.js", "", false, 10, func(h *Handle) string { h.PushFooterScript(constraints.Detail, NewComponent("comment-reply.js", "", false, 10, func(h *Handle) string {
reply := "" reply := ""
if h.GetDetailHandle().Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" { if h.Detail.Post.CommentStatus == "open" && wpconfig.GetOption("thread_comments") == "1" {
reply = `<script src='/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>` reply = `<script src='/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>`
} }
return reply return reply

View File

@ -1,8 +0,0 @@
package wp
const (
None = iota
Low
High
Fatal
)

View File

@ -2,14 +2,13 @@ package wp
import ( import (
"errors" "errors"
"github.com/fthvgb1/wp-go/safety"
) )
var fnMap = safety.NewMap[string, map[string]any]() var fnMap map[string]map[string]any
var fnHook = safety.NewMap[string, map[string]any]() var fnHook map[string]map[string]any
func GetFn[T any](fnType string, name string) []T { func GetFn[T any](fnType string, name string) []T {
v, ok := fnMap.Load(fnType) v, ok := fnMap[fnType]
if !ok { if !ok {
return nil return nil
} }
@ -20,7 +19,7 @@ func GetFn[T any](fnType string, name string) []T {
return vv.([]T) return vv.([]T)
} }
func GetFnHook[T any](fnType string, name string) []T { func GetFnHook[T any](fnType string, name string) []T {
v, ok := fnHook.Load(fnType) v, ok := fnHook[fnType]
if !ok { if !ok {
return nil return nil
} }
@ -32,10 +31,10 @@ func GetFnHook[T any](fnType string, name string) []T {
} }
func PushFn[T any](fnType string, name string, fns ...T) error { func PushFn[T any](fnType string, name string, fns ...T) error {
v, ok := fnMap.Load(fnType) v, ok := fnMap[fnType]
if !ok { if !ok {
v = make(map[string]any) v = make(map[string]any)
fnMap.Store(fnType, v) fnMap[fnType] = v
v[name] = fns v[name] = fns
return nil return nil
} }
@ -53,10 +52,10 @@ func PushFn[T any](fnType string, name string, fns ...T) error {
} }
func PushFnHook[T any](fnType string, name string, fns ...T) error { func PushFnHook[T any](fnType string, name string, fns ...T) error {
v, ok := fnHook.Load(fnType) v, ok := fnHook[fnType]
if !ok { if !ok {
v = make(map[string]any) v = make(map[string]any)
fnHook.Store(fnType, v) fnHook[fnType] = v
v[name] = fns v[name] = fns
return nil return nil
} }

View File

@ -2,13 +2,12 @@ package wp
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/number" "github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
@ -20,21 +19,11 @@ type IndexHandle struct {
*Handle *Handle
Param *IndexParams Param *IndexParams
Posts []models.Posts Posts []models.Posts
pageEle pagination.Render pageEle pagination.Elements
TotalRows int TotalRows int
postsPlugin PostsPlugin postsPlugin PostsPlugin
} }
func (h *Handle) GetIndexHandle() *IndexHandle {
v, ok := h.C.Get("indexHandle")
if !ok {
vv := NewIndexHandle(h)
h.C.Set("indexHandle", vv)
return vv
}
return v.(*IndexHandle)
}
func (i *IndexHandle) ListPlugin() func(*Handle, *models.Posts) { func (i *IndexHandle) ListPlugin() func(*Handle, *models.Posts) {
return i.postsPlugin return i.postsPlugin
} }
@ -43,11 +32,11 @@ func (i *IndexHandle) SetListPlugin(listPlugin func(*Handle, *models.Posts)) {
i.postsPlugin = listPlugin i.postsPlugin = listPlugin
} }
func (i *IndexHandle) PageEle() pagination.Render { func (i *IndexHandle) PageEle() pagination.Elements {
return i.pageEle return i.pageEle
} }
func (i *IndexHandle) SetPageEle(pageEle pagination.Render) { func (i *IndexHandle) SetPageEle(pageEle pagination.Elements) {
i.pageEle = pageEle i.pageEle = pageEle
} }
@ -99,14 +88,14 @@ func (i *IndexHandle) GetIndexData() (posts []models.Posts, totalRaw int, err er
switch i.scene { switch i.scene {
case constraints.Home, constraints.Category, constraints.Tag, constraints.Author: case constraints.Home, constraints.Category, constraints.Tag, constraints.Author:
posts, totalRaw, err = cache.PostLists(i.C, i.Param.CacheKey, q, i.Param.Page, i.Param.PageSize) posts, totalRaw, err = cache.PostLists(i.C, i.Param.CacheKey, i.C, q, i.Param.Page, i.Param.PageSize)
if i.scene == constraints.Home && i.Param.Page == 1 { if i.scene == constraints.Home && i.Param.Page == 1 {
i.MarkSticky(&posts) i.MarkSticky(&posts)
} }
case constraints.Search: case constraints.Search:
posts, totalRaw, err = cache.SearchPost(i.C, i.Param.CacheKey, q, i.Param.Page, i.Param.PageSize) posts, totalRaw, err = cache.SearchPost(i.C, i.Param.CacheKey, i.C, q, i.Param.Page, i.Param.PageSize)
case constraints.Archive: case constraints.Archive:
i.ginH["archiveYear"] = i.Param.Year i.ginH["archiveYear"] = i.Param.Year
@ -126,36 +115,34 @@ func (i *IndexHandle) Pagination() {
if q != "" { if q != "" {
q = fmt.Sprintf("?%s", q) q = fmt.Sprintf("?%s", q)
} }
i.ginH["pagination"] = pagination.Paginate(i.pageEle, i.TotalRows, i.Param.PageSize, i.Param.Page, i.Param.PaginationStep, *i.C.Request.URL, i.IsHttps()) 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() (err error) { func (i *IndexHandle) BuildIndexData(parm *IndexParams) (err error) {
if i.Param == nil { err = i.ParseIndex(parm)
i.Param = NewIndexParams(i.C)
}
err = i.ParseIndex(i.Param)
if err != nil { if err != nil {
i.Stats = constraints.ParamError i.Stats = constraints.ParamError
return return
} }
posts, totalRows, err := i.GetIndexData() posts, totalRows, err := i.GetIndexData()
if err != nil && !errors.Is(err, sql.ErrNoRows) { if err != nil && err != sql.ErrNoRows {
i.Stats = constraints.Error404 i.Stats = constraints.Error404
return return
} }
i.Posts = posts i.Posts = posts
i.TotalRows = totalRows i.TotalRows = totalRows
i.ginH["totalPage"] = number.DivideCeil(totalRows, i.Param.PageSize) i.ginH["totalPage"] = number.CalTotalPage(totalRows, i.Param.PageSize)
return return
} }
var GetPostsPlugin = reload.BuildValFnWithAnyParams("postPlugins", UsePostsPlugins)
func (i *IndexHandle) ExecPostsPlugin() { func (i *IndexHandle) ExecPostsPlugin() {
fn := i.postsPlugin fn := i.postsPlugin
if fn == nil { if fn == nil {
fn = GetPostsPlugin() fn = reload.GetAnyValBys("postPlugins", i, func(a *IndexHandle) PostsPlugin {
return UsePostsPlugins()
})
} }
for j := range i.Posts { for j := range i.Posts {
fn(i.Handle, &i.Posts[j]) fn(i.Handle, &i.Posts[j])
@ -163,27 +150,27 @@ func (i *IndexHandle) ExecPostsPlugin() {
} }
func IndexRender(h *Handle) { func IndexRender(h *Handle) {
i := h.GetIndexHandle() i := h.Index
i.ExecPostsPlugin() i.ExecPostsPlugin()
i.Pagination() i.Pagination()
i.ginH["posts"] = i.Posts i.ginH["posts"] = i.Posts
} }
func Index(h *Handle) { func Index(h *Handle) {
i := h.GetIndexHandle() i := h.Index
err := i.BuildIndexData() err := i.BuildIndexData(NewIndexParams(i.C))
if err != nil { if err != nil {
i.SetErr(err, High) i.SetErr(err)
} }
h.SetData("scene", h.Scene()) h.SetData("scene", h.Scene())
} }
func (i *IndexHandle) MarkSticky(posts *[]models.Posts) { func (i *IndexHandle) MarkSticky(posts *[]models.Posts) {
a := GetStickPosts(i.Handle) a := i.StickPosts()
if len(a) < 1 { if len(a) < 1 {
return return
} }
m := GetStickMapPosts(i.Handle) m := i.StickMapPosts()
*posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool { *posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool {
_, ok := m[post.Id] _, ok := m[post.Id]
return !ok return !ok

View File

@ -105,16 +105,18 @@ func NewIndexParams(ctx *gin.Context) *IndexParams {
func (i *IndexParams) ParseSearchs() { func (i *IndexParams) ParseSearchs() {
s := i.Ctx.Query("s") s := i.Ctx.Query("s")
q := str.Join("%", s, "%") if s != "" {
i.Where = append(i.Where, []string{ q := str.Join("%", s, "%")
"and", "post_title", "like", q, "", i.Where = append(i.Where, []string{
"or", "post_content", "like", q, "", "and", "post_title", "like", q, "",
"or", "post_excerpt", "like", q, "", "or", "post_content", "like", q, "",
}, []string{"post_password", ""}) "or", "post_excerpt", "like", q, "",
i.PostType = append(i.PostType, "Page", "attachment") }, []string{"post_password", ""})
i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s) i.PostType = append(i.PostType, "Page", "attachment")
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName) i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
i.Search = s i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
i.Search = s
}
} }
func (i *IndexParams) ParseArchives() error { func (i *IndexParams) ParseArchives() error {
year := i.Ctx.Param("year") year := i.Ctx.Param("year")

View File

@ -1,11 +1,11 @@
package wp package wp
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins"
"github.com/fthvgb1/wp-go/app/plugins/wpposts" "github.com/fthvgb1/wp-go/app/plugins/wpposts"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
) )
@ -23,7 +23,7 @@ func PostsPlugins(initial PostsPlugin, calls ...func(PostsPlugin, *Handle, *mode
var pluginFns = reload.Vars(map[string]func(PostsPlugin, *Handle, *models.Posts){ var pluginFns = reload.Vars(map[string]func(PostsPlugin, *Handle, *models.Posts){
"passwordProject": PasswordProject, "passwordProject": PasswordProject,
"digest": Digest, "digest": Digest,
}, "list-post-plugins-fns") })
func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) { func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) {
m := pluginFns.Load() m := pluginFns.Load()
@ -53,7 +53,7 @@ func Digest(next PostsPlugin, h *Handle, post *models.Posts) {
next(h, post) next(h, post)
} }
var ordinaryPlugin = reload.Vars([]PostsPlugin{}, "ordinaryPlugin") var ordinaryPlugin = reload.Vars([]PostsPlugin{})
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) { func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
p := ordinaryPlugin.Load() p := ordinaryPlugin.Load()
@ -69,7 +69,7 @@ func PostPlugin(calls ...PostsPlugin) PostsPlugin {
} }
} }
func UsePostsPlugins(_ ...any) PostsPlugin { func UsePostsPlugins() PostsPlugin {
m := pluginFns.Load() m := pluginFns.Load()
pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) { pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
f, ok := m[t] f, ok := m[t]

View File

@ -1,167 +0,0 @@
package middleware
import (
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/theme/wp/route"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings"
"net/http"
"strconv"
"time"
)
var plainRouteParam = reload.Vars([]Plain{
{
Action: "p",
Param: map[string]string{
"p": "id",
"cpage": "page",
},
Scene: constraints.Detail,
},
{
Action: "s",
Scene: constraints.Search,
},
{
Scene: constraints.Category,
Fn: func(h *wp.Handle) bool {
c, ok := widget.IsCategory(h)
if !ok {
return false
}
h.C.AddParam("category", c.Name)
h.C.AddParam("page", h.C.Query("paged"))
return true
},
},
{
Scene: constraints.Tag,
Action: "tag",
Param: map[string]string{
"tag": "tag",
"paged": "page",
},
},
{
Scene: constraints.Archive,
Fn: func(h *wp.Handle) bool {
m := h.C.Query("m")
if m == "" {
return false
}
t, err := time.Parse("200601", m)
if err != nil {
return false
}
h.C.AddParam("year", strconv.Itoa(t.Year()))
h.C.AddParam("month", number.IntToString(t.Month()))
h.C.AddParam("page", h.C.Query("paged"))
return true
},
},
{
Scene: constraints.Author,
Fn: func(h *wp.Handle) bool {
u := h.C.Query("author")
if u == "" {
return false
}
users := GetUsersIds(h)
name, ok := users[str.ToInteger[uint64](u, 0)]
if !ok {
return false
}
h.C.AddParam("author", name)
h.C.AddParam("page", h.C.Query("paged"))
return true
},
},
}, "plainRouteParam")
var GetUsersIds = reload.BuildValFnWithConfirm("usersIds", func(h *wp.Handle) (map[uint64]string, bool) {
users, err := cache.GetAllUsername(h.C)
if err != nil {
return nil, false
}
return maps.Flip(users), true
}, 10)
func SetExplainRouteParam(p []Plain) {
plainRouteParam.Store(p)
}
func GetExplainRouteParam() []Plain {
return plainRouteParam.Load()
}
func PushExplainRouteParam(explain ...Plain) {
v := plainRouteParam.Load()
v = append(v, explain...)
plainRouteParam.Store(v)
}
type Plain struct {
Action string
Param map[string]string
Scene string
Fn func(h *wp.Handle) bool
}
func MixWithPlain(h *wp.Handle) {
for _, explain := range plainRouteParam.Load() {
if explain.Action == "" && explain.Fn == nil {
continue
}
if explain.Fn != nil {
if !explain.Fn(h) {
continue
}
if explain.Scene != "" {
h.SetScene(explain.Scene)
}
wp.Run(h, nil)
h.Abort()
return
}
if explain.Scene == "" {
continue
}
q := h.C.Query(explain.Action)
if q == "" {
continue
}
h.SetScene(explain.Scene)
for query, param := range explain.Param {
h.C.AddParam(param, h.C.Query(query))
}
wp.Run(h, nil)
h.Abort()
return
}
}
func ShowPreComment(h *wp.Handle) {
v, ok := cache.NewCommentCache().Get(h.C, h.C.Request.URL.RawQuery)
if ok {
h.C.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
h.C.Writer.WriteHeader(http.StatusOK)
_, _ = h.C.Writer.Write([]byte(v))
h.Abort()
}
}
func CommonMiddleware(h *wp.Handle) {
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
wp.NewHandleFn(MixWithPlain, 100, "middleware.MixWithPlain"),
)
h.PushHandler(constraints.PipeMiddleware, constraints.Detail,
wp.NewHandleFn(ShowPreComment, 100, "middleware.ShowPreComment"),
)
h.PushHandler(constraints.PipeMiddleware, constraints.NoRoute,
wp.NewHandleFn(route.ResolveRoute, 100, "route.ResolveRoute"),
)
}

View File

@ -1,8 +1,9 @@
package wp package wp
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
) )
@ -21,7 +22,7 @@ func NewPipe(name string, order float64, fn HandlePipeFn[*Handle]) Pipe {
// HandlePipe 方便把功能写在其它包里 // HandlePipe 方便把功能写在其它包里
func HandlePipe[T any](initial func(T), fns ...HandlePipeFn[T]) HandleFn[T] { func HandlePipe[T any](initial func(T), fns ...HandlePipeFn[T]) HandleFn[T] {
return slice.ReverseReduce(fns, func(next HandlePipeFn[T], f HandleFn[T]) HandleFn[T] { return slice.ReverseReduce(fns, func(next HandlePipeFn[T], f func(t T)) func(t T) {
return func(t T) { return func(t T) {
next(f, t) next(f, t)
} }
@ -51,12 +52,10 @@ func (h *Handle) ReplacePipe(scene, pipeName string, pipe Pipe) error {
} }
func (h *Handle) PushHandler(pipScene string, scene string, fns ...HandleCall) { func (h *Handle) PushHandler(pipScene string, scene string, fns ...HandleCall) {
v, ok := handlerss.Load(pipScene) if _, ok := h.handlers[pipScene]; !ok {
if !ok { h.handlers[pipScene] = make(map[string][]HandleCall)
v = make(map[string][]HandleCall)
} }
v[scene] = append(v[scene], fns...) h.handlers[pipScene][scene] = append(h.handlers[pipScene][scene], fns...)
handlerss.Store(pipScene, v)
} }
func (h *Handle) PushRender(statsOrScene string, fns ...HandleCall) { func (h *Handle) PushRender(statsOrScene string, fns ...HandleCall) {
@ -66,16 +65,27 @@ func (h *Handle) PushDataHandler(scene string, fns ...HandleCall) {
h.PushHandler(constraints.PipeData, scene, fns...) h.PushHandler(constraints.PipeData, scene, fns...)
} }
func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string, func BuildPipe(pipeScene string, keyFn func(*Handle, string) string, fn func(*Handle, map[string][]HandleCall, string) []HandleCall) func(HandleFn[*Handle], *Handle) {
handleHookFn func(*Handle, string,
map[string][]func(HandleCall) (HandleCall, bool),
map[string][]HandleCall) []HandleCall) func(HandleFn[*Handle], *Handle) {
pipeHandlerFn := reload.BuildMapFn[string]("pipeHandlers", BuildHandler(pipeScene, keyFn, handleHookFn))
return func(next HandleFn[*Handle], h *Handle) { return func(next HandleFn[*Handle], h *Handle) {
key := keyFn(h, pipeScene) key := keyFn(h, pipeScene)
handlers := pipeHandlerFn(key, h) handlers := reload.GetAnyValMapBy("pipeHandlers", key, h, func(h *Handle) []HandleCall {
conf := h.handleHook[pipeScene]
calls := fn(h, h.handlers[pipeScene], key)
calls = slice.FilterAndMap(calls, func(call HandleCall) (HandleCall, bool) {
ok := true
for _, hook := range conf {
call, ok = hook(call)
if !ok {
break
}
}
return call, ok
})
slice.Sort(calls, func(i, j HandleCall) bool {
return i.Order > j.Order
})
return calls
})
for _, handler := range handlers { for _, handler := range handlers {
handler.Fn(h) handler.Fn(h)
if h.abort { if h.abort {
@ -88,141 +98,87 @@ func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string,
} }
} }
func BuildHandler(pipeScene string, keyFn func(*Handle, string) string,
handleHook func(*Handle, string,
map[string][]func(HandleCall) (HandleCall, bool),
map[string][]HandleCall) []HandleCall) func(*Handle) []HandleCall {
return func(h *Handle) []HandleCall {
key := keyFn(h, pipeScene)
mut := reload.GetGlobeMutex()
mut.Lock()
pipeHookers, _ := handleHooks.Load(pipeScene)
pipeHandlers, _ := handlerss.Load(pipeScene)
mut.Unlock()
calls := handleHook(h, key, pipeHookers, pipeHandlers)
slice.SimpleSort(calls, slice.DESC, func(t HandleCall) float64 {
return t.Order
})
return calls
}
}
func HookHandles(hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall, sceneOrStats ...string) []HandleCall {
hookedHandlers := slice.FilterAndMap(sceneOrStats, func(k string) ([]HandleCall, bool) {
r := handlers[k]
for _, hook := range hooks[k] {
r = slice.FilterAndMap(r, hook)
}
return r, true
})
return slice.Decompress(hookedHandlers)
}
func PipeKey(h *Handle, pipScene string) string { func PipeKey(h *Handle, pipScene string) string {
key := str.Join("pipekey", "-", pipScene, "-", h.scene, "-", h.Stats) key := str.Join("pipekey", "-", pipScene, "-", h.scene, "-", h.Stats)
return h.DoActionFilter("pipeKey", key, pipScene) return h.DoActionFilter("pipeKey", key, pipScene)
} }
var pipeInitFn = reload.BuildMapFn[string]("pipeInit", BuildPipe)
func Run(h *Handle, conf func(*Handle)) { func Run(h *Handle, conf func(*Handle)) {
if !h.isInited { if !helper.GetContextVal(h.C, "inited", false) {
InitHandle(conf, h) InitHandle(conf, h)
} }
pipeInitFn(h.scene, h.scene)(h) reload.GetAnyValBys(str.Join("pipeInit-", h.scene), h, func(h *Handle) func(*Handle) {
} p := GetFn[Pipe]("pipe", constraints.AllScene)
p = append(p, GetFn[Pipe]("pipe", h.scene)...)
func BuildPipe(scene string) func(*Handle) { pipes := slice.FilterAndMap(p, func(pipe Pipe) (Pipe, bool) {
pipees := GetFn[Pipe]("pipe", constraints.AllScene) var ok bool
pipees = append(pipees, GetFn[Pipe]("pipe", scene)...) hooks := GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", constraints.AllScene)
pipes := slice.FilterAndMap(pipees, func(pipe Pipe) (Pipe, bool) { hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", h.scene)...)
var ok bool for _, fn := range hooks {
mut := reload.GetGlobeMutex() pipe, ok = fn(pipe)
mut.Lock() if !ok {
hooks := GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", constraints.AllScene) return pipe, false
hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", scene)...) }
mut.Unlock()
for _, fn := range hooks {
pipe, ok = fn(pipe)
if !ok {
return pipe, false
} }
} return pipe, pipe.Fn != nil
return pipe, pipe.Fn != nil })
}) slice.Sort(pipes, func(i, j Pipe) bool {
slice.SimpleSort(pipes, slice.DESC, func(t Pipe) float64 { return i.Order > j.Order
return t.Order })
}) arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] {
return t.Fn
arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] { })
return t.Fn return HandlePipe(NothingToDo, arr...)
}) })(h)
return HandlePipe(NothingToDo, arr...)
} }
func MiddlewareKey(h *Handle, pipScene string) string { func MiddlewareKey(h *Handle, pipScene string) string {
return h.DoActionFilter("middleware", str.Join("pipe-middleware-", h.scene), pipScene) return h.DoActionFilter("middleware", "middleware", pipScene)
} }
func PipeMiddlewareHandle(h *Handle, key string, func PipeMiddlewareHandle(h *Handle, middlewares map[string][]HandleCall, key string) (handlers []HandleCall) {
hooks map[string][]func(HandleCall) (HandleCall, bool), handlers = append(handlers, middlewares[h.scene]...)
handlers map[string][]HandleCall) []HandleCall { handlers = append(handlers, middlewares[constraints.AllScene]...)
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene) handlers = h.PipeHandleHook("PipeMiddlewareHandle", handlers, middlewares, key)
return h.PipeHandleHook("PipeMiddlewareHandle", hookedHandles, handlers, key) return
} }
func PipeDataHandle(h *Handle, key string, func PipeDataHandle(h *Handle, dataHandlers map[string][]HandleCall, key string) (handlers []HandleCall) {
hooks map[string][]func(HandleCall) (HandleCall, bool), handlers = append(handlers, dataHandlers[h.scene]...)
handlers map[string][]HandleCall) []HandleCall { handlers = append(handlers, dataHandlers[constraints.AllScene]...)
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene) handlers = h.PipeHandleHook("PipeDataHandle", handlers, dataHandlers, key)
return h.PipeHandleHook("PipeDataHandle", hookedHandles, handlers, key) return
} }
func PipeRender(h *Handle, key string, func PipeRender(h *Handle, renders map[string][]HandleCall, key string) (handlers []HandleCall) {
hooks map[string][]func(HandleCall) (HandleCall, bool), handlers = append(handlers, renders[h.Stats]...)
handlers map[string][]HandleCall) []HandleCall { handlers = append(handlers, renders[h.scene]...)
hookedHandles := HookHandles(hooks, handlers, h.scene, h.Stats, constraints.AllScene, constraints.AllStats) handlers = append(handlers, renders[constraints.AllStats]...)
return h.PipeHandleHook("PipeRender", hookedHandles, handlers, key) handlers = append(handlers, renders[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeRender", handlers, renders, key)
return
} }
// DeleteHandle 写插件的时候用 // DeleteHandle 写插件的时候用
func (h *Handle) DeleteHandle(pipeScene, scene, name string) { func (h *Handle) DeleteHandle(pipeScene string, name string) {
v, ok := handleHooks.Load(pipeScene) h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
return call, name != call.Name return call, name != call.Name
}) })
handleHooks.Store(pipeScene, v)
} }
// ReplaceHandle 写插件的时候用 // ReplaceHandle 写插件的时候用
func (h *Handle) ReplaceHandle(pipeScene, scene, name string, fn HandleFn[*Handle]) { func (h *Handle) ReplaceHandle(pipeScene, name string, fn HandleFn[*Handle]) {
v, ok := handleHooks.Load(pipeScene) h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
if name == call.Name { if name == call.Name {
call.Fn = fn call.Fn = fn
} }
return call, true return call, true
}) })
handleHooks.Store(pipeScene, v)
} }
// HookHandle 写插件的时候用 // HookHandle 写插件的时候用
func (h *Handle) HookHandle(pipeScene, scene string, hook func(HandleCall) (HandleCall, bool)) { func (h *Handle) HookHandle(pipeScene string, hook func(HandleCall) (HandleCall, bool)) {
v, ok := handleHooks.Load(pipeScene) h.handleHook[pipeScene] = append(h.handleHook[pipeScene], hook)
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], hook)
handleHooks.Store(pipeScene, v)
} }
func (h *Handle) PushPipeHandleHook(name string, fn ...func([]HandleCall) []HandleCall) error { func (h *Handle) PushPipeHandleHook(name string, fn ...func([]HandleCall) []HandleCall) error {
@ -237,11 +193,11 @@ func (h *Handle) PipeHandleHook(name string, calls []HandleCall, m map[string][]
} }
func InitPipe(h *Handle) { func InitPipe(h *Handle) {
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeMiddleware, 300, h.PushPipe(constraints.Home, NewPipe(constraints.PipeMiddleware, 300,
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle))) BuildPipe(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200, h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200,
BuildHandlers(constraints.PipeData, PipeKey, PipeDataHandle))) BuildPipe(constraints.PipeData, PipeKey, PipeDataHandle)))
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100, h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100,
BuildHandlers(constraints.PipeRender, PipeKey, PipeRender))) BuildPipe(constraints.PipeRender, PipeKey, PipeRender)))
} }

View File

@ -1,16 +1,13 @@
package route package route
import ( import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"net/http"
"regexp" "regexp"
) )
// Route
// Type value equal const or reg
type Route struct { type Route struct {
Path string Path string
Scene string Scene string
@ -23,18 +20,18 @@ var routeHook []func(Route) (Route, bool)
var regRoutes *safety.Map[string, *regexp.Regexp] var regRoutes *safety.Map[string, *regexp.Regexp]
var routes = func() *safety.Map[string, Route] { var routes = func() *safety.Map[string, Route] {
r := safety.NewMap[string, Route]() r := safety.NewMap[string, Route]()
reload.Append(func() { reload.Push(func() {
r.Flush() r.Flush()
regRoutes.Flush() regRoutes.Flush()
}, "wp-routers") })
regRoutes = safety.NewMap[string, *regexp.Regexp]() regRoutes = safety.NewMap[string, *regexp.Regexp]()
return r return r
}() }()
// PushRoute path can be const or regex string // PushRoute path can be const or regex string
// //
// eg: `(?P<controller>\w+)/(?P<method>\w+)`, route.Route{ // eg: `(?P<control>\w+)/(?P<method>\w+)`, route.Route{
// Path: `(?P<controller>\w+)/(?P<method>\w+)`, // Path: `(?P<control>\w+)/(?P<method>\w+)`,
// Scene: constraints.Home, // Scene: constraints.Home,
// Method: []string{"GET"}, // Method: []string{"GET"},
// Type: "reg", // Type: "reg",
@ -73,74 +70,60 @@ func Hook(path string, fn func(Route) Route) {
return r, path == r.Path return r, path == r.Path
}) })
} }
var RegRouteFn = reload.BuildValFnWithAnyParams("regexRoute", RegRouteHook)
func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp) {
m := map[string]Route{}
rrs := map[string]*regexp.Regexp{}
routes.Range(func(key string, value Route) bool {
vv, _ := regRoutes.Load(key)
if len(routeHook) > 0 {
for _, fn := range routeHook {
v, ok := fn(value)
if !ok {
continue
}
m[v.Path] = v
if v.Type != "reg" {
continue
}
if v.Path != key {
vvv, err := regexp.Compile(v.Path)
if err != nil {
panic(err)
}
vv = vvv
}
rrs[v.Path] = vv
}
} else {
m[key] = value
rrs[key] = vv
}
return true
})
return func() (map[string]Route, map[string]*regexp.Regexp) {
return m, rrs
}
}
func ResolveRoute(h *wp.Handle) { func ResolveRoute(h *wp.Handle) {
requestURI := h.C.Request.RequestURI requestURI := h.C.Request.RequestURI
rs, rrs := RegRouteFn()() rs, rrs := reload.GetAnyValBys("route",
struct{}{},
func(_ struct{}) func() (map[string]Route, map[string]*regexp.Regexp) {
m := map[string]Route{}
rrs := map[string]*regexp.Regexp{}
routes.Range(func(key string, value Route) bool {
vv, _ := regRoutes.Load(key)
if len(routeHook) > 0 {
for _, fn := range routeHook {
v, ok := fn(value)
if ok {
m[v.Path] = v
if v.Type == "reg" {
if v.Path != key {
vvv, err := regexp.Compile(v.Path)
if err != nil {
panic(err)
}
vv = vvv
}
rrs[v.Path] = vv
}
}
}
} else {
m[key] = value
rrs[key] = vv
}
return true
})
return func() (map[string]Route, map[string]*regexp.Regexp) {
return m, rrs
}
})()
v, ok := rs[requestURI] v, ok := rs[requestURI]
if ok && slice.IsContained(v.Method, h.C.Request.Method) { if ok && slice.IsContained(v.Method, h.C.Request.Method) {
h.SetScene(v.Scene) h.SetScene(v.Scene)
wp.Run(h, nil) wp.Run(h, nil)
h.Abort()
return return
} }
for path, reg := range rrs { for path, reg := range rrs {
r := reg.FindStringSubmatch(requestURI) r := reg.FindAllStringSubmatch(requestURI, -1)
if len(r) < 1 { if len(r) < 1 {
return return
} }
rr := rs[path] rr := rs[path]
if slice.IsContained(rr.Method, h.C.Request.Method) { if slice.IsContained(rr.Method, h.C.Request.Method) {
h.SetScene(rr.Scene) h.SetScene(rr.Scene)
for i, name := range reg.SubexpNames() { h.C.Set("route", r)
if name == "" {
continue
}
h.C.AddParam(name, r[i])
}
h.C.Set("regRoute", reg)
h.C.Set("regRouteRes", r)
wp.Run(h, nil) wp.Run(h, nil)
h.Abort()
return return
} }
} }
h.C.Status(http.StatusNotFound)
h.Abort()
} }

View File

@ -0,0 +1,739 @@
package scriptloader
import (
"encoding/json"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"os"
"path/filepath"
"regexp"
"strings"
)
func defaultScripts(m *safety.Map[string, *Script], suffix string) {
m.Store("utils", NewScript("utils", "/wp-includes/js/utils"+suffix+".js", nil, "", nil))
m.Store("common", NewScript("common", "/wp-admin/js/common"+suffix+".js", []string{"jquery", "hoverIntent", "utils", "wp-i18n"}, "", 1))
m.Store("wp-sanitize", NewScript("wp-sanitize", "/wp-includes/js/wp-sanitize"+suffix+".js", nil, "", 1))
m.Store("sack", NewScript("sack", "/wp-includes/js/tw-sack"+suffix+".js", nil, "1.6.1", 1))
m.Store("quicktags", NewScript("quicktags", "/wp-includes/js/quicktags"+suffix+".js", nil, "", 1))
m.Store("colorpicker", NewScript("colorpicker", "/wp-includes/js/colorpicker"+suffix+".js", []string{"prototype"}, "3517m", nil))
m.Store("editor", NewScript("editor", "/wp-admin/js/editor"+suffix+".js", []string{"utils", "jquery"}, "", 1))
m.Store("clipboard", NewScript("clipboard", "/wp-includes/js/clipboard"+suffix+".js", nil, "2.0.11", 1))
m.Store("wp-ajax-response", NewScript("wp-ajax-response", "/wp-includes/js/wp-ajax-response"+suffix+".js", []string{"jquery", "wp-a11y"}, "", 1))
m.Store("wp-api-request", NewScript("wp-api-request", "/wp-includes/js/api-request"+suffix+".js", []string{"jquery"}, "", 1))
m.Store("wp-pointer", NewScript("wp-pointer", "/wp-includes/js/wp-pointer"+suffix+".js", []string{"jquery-ui-core", "wp-i18n"}, "", 1))
m.Store("autosave", NewScript("autosave", "/wp-includes/js/autosave"+suffix+".js", []string{"heartbeat"}, "", 1))
m.Store("heartbeat", NewScript("heartbeat", "/wp-includes/js/heartbeat"+suffix+".js", []string{"jquery", "wp-hooks"}, "", 1))
m.Store("wp-auth-check", NewScript("wp-auth-check", "/wp-includes/js/wp-auth-check"+suffix+".js", []string{"heartbeat", "wp-i18n"}, "", 1))
m.Store("wp-lists", NewScript("wp-lists", "/wp-includes/js/wp-lists"+suffix+".js", []string{"wp-ajax-response", "jquery-color"}, "", 1))
m.Store("prototype", NewScript("prototype", "https://ajax.googleapis.com/ajax/libs/prototype/1.7.1.0/prototype.js"+suffix+".js", nil, "1.7.1", nil))
m.Store("scriptaculous-root", NewScript("scriptaculous-root", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/scriptaculous.js"+suffix+".js", []string{"prototype"}, "1.9.0", nil))
m.Store("scriptaculous-builder", NewScript("scriptaculous-builder", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/builder.js"+suffix+".js", []string{"scriptaculous-root"}, "1.9.0", nil))
m.Store("scriptaculous-dragdrop", NewScript("scriptaculous-dragdrop", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/dragdrop.js"+suffix+".js", []string{"scriptaculous-builder", "scriptaculous-effects"}, "1.9.0", nil))
m.Store("scriptaculous-effects", NewScript("scriptaculous-effects", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/effects.js"+suffix+".js", []string{"scriptaculous-root"}, "1.9.0", nil))
m.Store("scriptaculous-slider", NewScript("scriptaculous-slider", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/slider.js"+suffix+".js", []string{"scriptaculous-effects"}, "1.9.0", nil))
m.Store("scriptaculous-sound", NewScript("scriptaculous-sound", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/sound.js"+suffix+".js", []string{"scriptaculous-root"}, "1.9.0", nil))
m.Store("scriptaculous-controls", NewScript("scriptaculous-controls", "https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/controls.js"+suffix+".js", []string{"scriptaculous-root"}, "1.9.0", nil))
m.Store("scriptaculous", NewScript("scriptaculous", ""+suffix+".js", []string{"scriptaculous-dragdrop", "scriptaculous-slider", "scriptaculous-controls"}, "", nil))
m.Store("cropper", NewScript("cropper", "/wp-includes/js/crop/cropper.js"+suffix+".js", []string{"scriptaculous-dragdrop"}, "", nil))
m.Store("jquery", NewScript("jquery", ""+suffix+".js", []string{"jquery-core", "jquery-migrate"}, "3.6.4", nil))
m.Store("jquery-core", NewScript("jquery-core", "/wp-includes/js/jquery/jquery"+suffix+".js", nil, "3.6.4", nil))
m.Store("jquery-migrate", NewScript("jquery-migrate", "/wp-includes/js/jquery/jquery-migrate"+suffix+".js", nil, "3.4.0", nil))
m.Store("jquery-ui-core", NewScript("jquery-ui-core", "/wp-includes/js/jquery/ui/core"+suffix+".js", []string{"jquery"}, "1.13.2", 1))
m.Store("jquery-effects-core", NewScript("jquery-effects-core", "/wp-includes/js/jquery/ui/effect"+suffix+".js", []string{"jquery"}, "1.13.2", 1))
m.Store("jquery-effects-blind", NewScript("jquery-effects-blind", "/wp-includes/js/jquery/ui/effect-blind"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-bounce", NewScript("jquery-effects-bounce", "/wp-includes/js/jquery/ui/effect-bounce"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-clip", NewScript("jquery-effects-clip", "/wp-includes/js/jquery/ui/effect-clip"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-drop", NewScript("jquery-effects-drop", "/wp-includes/js/jquery/ui/effect-drop"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-explode", NewScript("jquery-effects-explode", "/wp-includes/js/jquery/ui/effect-explode"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-fade", NewScript("jquery-effects-fade", "/wp-includes/js/jquery/ui/effect-fade"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-fold", NewScript("jquery-effects-fold", "/wp-includes/js/jquery/ui/effect-fold"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-highlight", NewScript("jquery-effects-highlight", "/wp-includes/js/jquery/ui/effect-highlight"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-puff", NewScript("jquery-effects-puff", "/wp-includes/js/jquery/ui/effect-puff"+suffix+".js", []string{"jquery-effects-core", "jquery-effects-scale"}, "1.13.2", 1))
m.Store("jquery-effects-pulsate", NewScript("jquery-effects-pulsate", "/wp-includes/js/jquery/ui/effect-pulsate"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-scale", NewScript("jquery-effects-scale", "/wp-includes/js/jquery/ui/effect-scale"+suffix+".js", []string{"jquery-effects-core", "jquery-effects-size"}, "1.13.2", 1))
m.Store("jquery-effects-shake", NewScript("jquery-effects-shake", "/wp-includes/js/jquery/ui/effect-shake"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-size", NewScript("jquery-effects-size", "/wp-includes/js/jquery/ui/effect-size"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-slide", NewScript("jquery-effects-slide", "/wp-includes/js/jquery/ui/effect-slide"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-effects-transfer", NewScript("jquery-effects-transfer", "/wp-includes/js/jquery/ui/effect-transfer"+suffix+".js", []string{"jquery-effects-core"}, "1.13.2", 1))
m.Store("jquery-ui-accordion", NewScript("jquery-ui-accordion", "/wp-includes/js/jquery/ui/accordion"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-autocomplete", NewScript("jquery-ui-autocomplete", "/wp-includes/js/jquery/ui/autocomplete"+suffix+".js", []string{"jquery-ui-menu", "wp-a11y"}, "1.13.2", 1))
m.Store("jquery-ui-button", NewScript("jquery-ui-button", "/wp-includes/js/jquery/ui/button"+suffix+".js", []string{"jquery-ui-core", "jquery-ui-controlgroup", "jquery-ui-checkboxradio"}, "1.13.2", 1))
m.Store("jquery-ui-datepicker", NewScript("jquery-ui-datepicker", "/wp-includes/js/jquery/ui/datepicker"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-dialog", NewScript("jquery-ui-dialog", "/wp-includes/js/jquery/ui/dialog"+suffix+".js", []string{"jquery-ui-resizable", "jquery-ui-draggable", "jquery-ui-button"}, "1.13.2", 1))
m.Store("jquery-ui-menu", NewScript("jquery-ui-menu", "/wp-includes/js/jquery/ui/menu"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-mouse", NewScript("jquery-ui-mouse", "/wp-includes/js/jquery/ui/mouse"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-progressbar", NewScript("jquery-ui-progressbar", "/wp-includes/js/jquery/ui/progressbar"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-selectmenu", NewScript("jquery-ui-selectmenu", "/wp-includes/js/jquery/ui/selectmenu"+suffix+".js", []string{"jquery-ui-menu"}, "1.13.2", 1))
m.Store("jquery-ui-slider", NewScript("jquery-ui-slider", "/wp-includes/js/jquery/ui/slider"+suffix+".js", []string{"jquery-ui-mouse"}, "1.13.2", 1))
m.Store("jquery-ui-spinner", NewScript("jquery-ui-spinner", "/wp-includes/js/jquery/ui/spinner"+suffix+".js", []string{"jquery-ui-button"}, "1.13.2", 1))
m.Store("jquery-ui-tabs", NewScript("jquery-ui-tabs", "/wp-includes/js/jquery/ui/tabs"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-tooltip", NewScript("jquery-ui-tooltip", "/wp-includes/js/jquery/ui/tooltip"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-checkboxradio", NewScript("jquery-ui-checkboxradio", "/wp-includes/js/jquery/ui/checkboxradio"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-controlgroup", NewScript("jquery-ui-controlgroup", "/wp-includes/js/jquery/ui/controlgroup"+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-draggable", NewScript("jquery-ui-draggable", "/wp-includes/js/jquery/ui/draggable"+suffix+".js", []string{"jquery-ui-mouse"}, "1.13.2", 1))
m.Store("jquery-ui-droppable", NewScript("jquery-ui-droppable", "/wp-includes/js/jquery/ui/droppable"+suffix+".js", []string{"jquery-ui-draggable"}, "1.13.2", 1))
m.Store("jquery-ui-resizable", NewScript("jquery-ui-resizable", "/wp-includes/js/jquery/ui/resizable"+suffix+".js", []string{"jquery-ui-mouse"}, "1.13.2", 1))
m.Store("jquery-ui-selectable", NewScript("jquery-ui-selectable", "/wp-includes/js/jquery/ui/selectable"+suffix+".js", []string{"jquery-ui-mouse"}, "1.13.2", 1))
m.Store("jquery-ui-sortable", NewScript("jquery-ui-sortable", "/wp-includes/js/jquery/ui/sortable"+suffix+".js", []string{"jquery-ui-mouse"}, "1.13.2", 1))
m.Store("jquery-ui-position", NewScript("jquery-ui-position", ""+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-ui-widget", NewScript("jquery-ui-widget", ""+suffix+".js", []string{"jquery-ui-core"}, "1.13.2", 1))
m.Store("jquery-form", NewScript("jquery-form", "/wp-includes/js/jquery/jquery.form"+suffix+".js", []string{"jquery"}, "4.3.0", 1))
m.Store("jquery-color", NewScript("jquery-color", "/wp-includes/js/jquery/jquery.color"+suffix+".js", []string{"jquery"}, "2.2.0", 1))
m.Store("schedule", NewScript("schedule", "/wp-includes/js/jquery/jquery.schedule.js"+suffix+".js", []string{"jquery"}, "20m", 1))
m.Store("jquery-query", NewScript("jquery-query", "/wp-includes/js/jquery/jquery.query.js"+suffix+".js", []string{"jquery"}, "2.2.3", 1))
m.Store("jquery-serialize-object", NewScript("jquery-serialize-object", "/wp-includes/js/jquery/jquery.serialize-object.js"+suffix+".js", []string{"jquery"}, "0.2-wp", 1))
m.Store("jquery-hotkeys", NewScript("jquery-hotkeys", "/wp-includes/js/jquery/jquery.hotkeys"+suffix+".js", []string{"jquery"}, "0.0.2m", 1))
m.Store("jquery-table-hotkeys", NewScript("jquery-table-hotkeys", "/wp-includes/js/jquery/jquery.table-hotkeys"+suffix+".js", []string{"jquery", "jquery-hotkeys"}, "", 1))
m.Store("jquery-touch-punch", NewScript("jquery-touch-punch", "/wp-includes/js/jquery/jquery.ui.touch-punch.js"+suffix+".js", []string{"jquery-ui-core", "jquery-ui-mouse"}, "0.2.2", 1))
m.Store("suggest", NewScript("suggest", "/wp-includes/js/jquery/suggest"+suffix+".js", []string{"jquery"}, "1.1-20110113", 1))
m.Store("imagesloaded", NewScript("imagesloaded", "/wp-includes/js/imagesloaded"+suffix+".js", nil, "4.1.4", 1))
m.Store("masonry", NewScript("masonry", "/wp-includes/js/masonry"+suffix+".js", []string{"imagesloaded"}, "4.2.2", 1))
m.Store("jquery-masonry", NewScript("jquery-masonry", "/wp-includes/js/jquery/jquery.masonry"+suffix+".js", []string{"jquery", "masonry"}, "3.1.2b", 1))
m.Store("thickbox", NewScript("thickbox", "/wp-includes/js/thickbox/thickbox.js"+suffix+".js", []string{"jquery"}, "3.1-20121105", 1))
m.Store("jcrop", NewScript("jcrop", "/wp-includes/js/jcrop/jquery.Jcrop"+suffix+".js", []string{"jquery"}, "0.9.15", nil))
m.Store("swfobject", NewScript("swfobject", "/wp-includes/js/swfobject.js"+suffix+".js", nil, "2.2-20120417", nil))
m.Store("moxiejs", NewScript("moxiejs", "/wp-includes/js/plupload/moxie"+suffix+".js", nil, "1.3.5", nil))
m.Store("plupload", NewScript("plupload", "/wp-includes/js/plupload/plupload"+suffix+".js", []string{"moxiejs"}, "2.1.9", nil))
m.Store("plupload-all", NewScript("plupload-all", ""+suffix+".js", []string{"plupload"}, "2.1.1", nil))
m.Store("plupload-html5", NewScript("plupload-html5", ""+suffix+".js", []string{"plupload"}, "2.1.1", nil))
m.Store("plupload-flash", NewScript("plupload-flash", ""+suffix+".js", []string{"plupload"}, "2.1.1", nil))
m.Store("plupload-silverlight", NewScript("plupload-silverlight", ""+suffix+".js", []string{"plupload"}, "2.1.1", nil))
m.Store("plupload-html4", NewScript("plupload-html4", ""+suffix+".js", []string{"plupload"}, "2.1.1", nil))
m.Store("plupload-handlers", NewScript("plupload-handlers", "/wp-includes/js/plupload/handlers"+suffix+".js", []string{"clipboard", "jquery", "plupload", "underscore", "wp-a11y", "wp-i18n"}, "", nil))
m.Store("wp-plupload", NewScript("wp-plupload", "/wp-includes/js/plupload/wp-plupload"+suffix+".js", []string{"plupload", "jquery", "json2", "media-models"}, "", 1))
m.Store("swfupload", NewScript("swfupload", "/wp-includes/js/swfupload/swfupload.js"+suffix+".js", nil, "2201-20110113", nil))
m.Store("swfupload-all", NewScript("swfupload-all", ""+suffix+".js", []string{"swfupload"}, "2201", nil))
m.Store("swfupload-handlers", NewScript("swfupload-handlers", "/wp-includes/js/swfupload/handlers"+suffix+".js", []string{"swfupload-all", "jquery"}, "2201-20110524", nil))
m.Store("comment-reply", NewScript("comment-reply", "/wp-includes/js/comment-reply"+suffix+".js", nil, "", 1))
m.Store("json2", NewScript("json2", "/wp-includes/js/json2"+suffix+".js", nil, "2015-05-03", nil))
m.Store("underscore", NewScript("underscore", "/wp-includes/js/underscore"+suffix+".js", nil, "1.13.4", 1))
m.Store("backbone", NewScript("backbone", "/wp-includes/js/backbone"+suffix+".js", []string{"underscore", "jquery"}, "1.4.1", 1))
m.Store("wp-util", NewScript("wp-util", "/wp-includes/js/wp-util"+suffix+".js", []string{"underscore", "jquery"}, "", 1))
m.Store("wp-backbone", NewScript("wp-backbone", "/wp-includes/js/wp-backbone"+suffix+".js", []string{"backbone", "wp-util"}, "", 1))
m.Store("revisions", NewScript("revisions", "/wp-admin/js/revisions"+suffix+".js", []string{"wp-backbone", "jquery-ui-slider", "hoverIntent"}, "", 1))
m.Store("imgareaselect", NewScript("imgareaselect", "/wp-includes/js/imgareaselect/jquery.imgareaselect"+suffix+".js", []string{"jquery"}, "", 1))
m.Store("mediaelement", NewScript("mediaelement", ""+suffix+".js", []string{"jquery", "mediaelement-core", "mediaelement-migrate"}, "4.2.17", 1))
m.Store("mediaelement-core", NewScript("mediaelement-core", "/wp-includes/js/mediaelement/mediaelement-and-player"+suffix+".js", nil, "4.2.17", 1))
m.Store("mediaelement-migrate", NewScript("mediaelement-migrate", "/wp-includes/js/mediaelement/mediaelement-migrate"+suffix+".js", nil, "", 1))
m.Store("mediaelement-vimeo", NewScript("mediaelement-vimeo", "/wp-includes/js/mediaelement/renderers/vimeo"+suffix+".js", []string{"mediaelement"}, "4.2.17", 1))
m.Store("wp-mediaelement", NewScript("wp-mediaelement", "/wp-includes/js/mediaelement/wp-mediaelement"+suffix+".js", []string{"mediaelement"}, "", 1))
m.Store("wp-codemirror", NewScript("wp-codemirror", "/wp-includes/js/codemirror/codemirror"+suffix+".js", nil, "5.29.1-alpha-ee20357", nil))
m.Store("csslint", NewScript("csslint", "/wp-includes/js/codemirror/csslint.js"+suffix+".js", nil, "1.0.5", nil))
m.Store("esprima", NewScript("esprima", "/wp-includes/js/codemirror/esprima.js"+suffix+".js", nil, "4.0.0", nil))
m.Store("jshint", NewScript("jshint", "/wp-includes/js/codemirror/fakejshint.js"+suffix+".js", []string{"esprima"}, "2.9.5", nil))
m.Store("jsonlint", NewScript("jsonlint", "/wp-includes/js/codemirror/jsonlint.js"+suffix+".js", nil, "1.6.2", nil))
m.Store("htmlhint", NewScript("htmlhint", "/wp-includes/js/codemirror/htmlhint.js"+suffix+".js", nil, "0.9.14-xwp", nil))
m.Store("htmlhint-kses", NewScript("htmlhint-kses", "/wp-includes/js/codemirror/htmlhint-kses.js"+suffix+".js", []string{"htmlhint"}, "", nil))
m.Store("code-editor", NewScript("code-editor", "/wp-admin/js/code-editor"+suffix+".js", []string{"jquery", "wp-codemirror", "underscore"}, "", nil))
m.Store("wp-theme-plugin-editor", NewScript("wp-theme-plugin-editor", "/wp-admin/js/theme-plugin-editor"+suffix+".js", []string{"common", "wp-util", "wp-sanitize", "jquery", "jquery-ui-core", "wp-a11y", "underscore", "wp-i18n"}, "", 1))
m.Store("wp-playlist", NewScript("wp-playlist", "/wp-includes/js/mediaelement/wp-playlist"+suffix+".js", []string{"wp-util", "backbone", "mediaelement"}, "", 1))
m.Store("zxcvbn-async", NewScript("zxcvbn-async", "/wp-includes/js/zxcvbn-async"+suffix+".js", nil, "1.0", nil))
m.Store("password-strength-meter", NewScript("password-strength-meter", "/wp-admin/js/password-strength-meter"+suffix+".js", []string{"jquery", "zxcvbn-async", "wp-i18n"}, "", 1))
m.Store("application-passwords", NewScript("application-passwords", "/wp-admin/js/application-passwords"+suffix+".js", []string{"jquery", "wp-util", "wp-api-request", "wp-date", "wp-i18n", "wp-hooks"}, "", 1))
m.Store("auth-app", NewScript("auth-app", "/wp-admin/js/auth-app"+suffix+".js", []string{"jquery", "wp-api-request", "wp-i18n", "wp-hooks"}, "", 1))
m.Store("user-profile", NewScript("user-profile", "/wp-admin/js/user-profile"+suffix+".js", []string{"jquery", "password-strength-meter", "wp-util", "wp-i18n"}, "", 1))
m.Store("language-chooser", NewScript("language-chooser", "/wp-admin/js/language-chooser"+suffix+".js", []string{"jquery"}, "", 1))
m.Store("user-suggest", NewScript("user-suggest", "/wp-admin/js/user-suggest"+suffix+".js", []string{"jquery-ui-autocomplete"}, "", 1))
m.Store("admin-bar", NewScript("admin-bar", "/wp-includes/js/admin-bar"+suffix+".js", []string{"hoverintent-js"}, "", 1))
m.Store("wplink", NewScript("wplink", "/wp-includes/js/wplink"+suffix+".js", []string{"jquery", "wp-a11y"}, "", 1))
m.Store("wpdialogs", NewScript("wpdialogs", "/wp-includes/js/wpdialog"+suffix+".js", []string{"jquery-ui-dialog"}, "", 1))
m.Store("word-count", NewScript("word-count", "/wp-admin/js/word-count"+suffix+".js", nil, "", 1))
m.Store("media-upload", NewScript("media-upload", "/wp-admin/js/media-upload"+suffix+".js", []string{"thickbox", "shortcode"}, "", 1))
m.Store("hoverIntent", NewScript("hoverIntent", "/wp-includes/js/hoverIntent"+suffix+".js", []string{"jquery"}, "1.10.2", 1))
m.Store("hoverintent-js", NewScript("hoverintent-js", "/wp-includes/js/hoverintent-js"+suffix+".js", nil, "2.2.1", 1))
m.Store("customize-base", NewScript("customize-base", "/wp-includes/js/customize-base"+suffix+".js", []string{"jquery", "json2", "underscore"}, "", 1))
m.Store("customize-loader", NewScript("customize-loader", "/wp-includes/js/customize-loader"+suffix+".js", []string{"customize-base"}, "", 1))
m.Store("customize-preview", NewScript("customize-preview", "/wp-includes/js/customize-preview"+suffix+".js", []string{"wp-a11y", "customize-base"}, "", 1))
m.Store("customize-models", NewScript("customize-models", "/wp-includes/js/customize-models.js"+suffix+".js", []string{"underscore", "backbone"}, "", 1))
m.Store("customize-views", NewScript("customize-views", "/wp-includes/js/customize-views.js"+suffix+".js", []string{"jquery", "underscore", "imgareaselect", "customize-models", "media-editor", "media-views"}, "", 1))
m.Store("customize-controls", NewScript("customize-controls", "/wp-admin/js/customize-controls"+suffix+".js", []string{"customize-base", "wp-a11y", "wp-util", "jquery-ui-core"}, "", 1))
m.Store("customize-selective-refresh", NewScript("customize-selective-refresh", "/wp-includes/js/customize-selective-refresh"+suffix+".js", []string{"jquery", "wp-util", "customize-preview"}, "", 1))
m.Store("customize-widgets", NewScript("customize-widgets", "/wp-admin/js/customize-widgets"+suffix+".js", []string{"jquery", "jquery-ui-sortable", "jquery-ui-droppable", "wp-backbone", "customize-controls"}, "", 1))
m.Store("customize-preview-widgets", NewScript("customize-preview-widgets", "/wp-includes/js/customize-preview-widgets"+suffix+".js", []string{"jquery", "wp-util", "customize-preview", "customize-selective-refresh"}, "", 1))
m.Store("customize-nav-menus", NewScript("customize-nav-menus", "/wp-admin/js/customize-nav-menus"+suffix+".js", []string{"jquery", "wp-backbone", "customize-controls", "accordion", "nav-menu", "wp-sanitize"}, "", 1))
m.Store("customize-preview-nav-menus", NewScript("customize-preview-nav-menus", "/wp-includes/js/customize-preview-nav-menus"+suffix+".js", []string{"jquery", "wp-util", "customize-preview", "customize-selective-refresh"}, "", 1))
m.Store("wp-custom-header", NewScript("wp-custom-header", "/wp-includes/js/wp-custom-header"+suffix+".js", []string{"wp-a11y"}, "", 1))
m.Store("accordion", NewScript("accordion", "/wp-admin/js/accordion"+suffix+".js", []string{"jquery"}, "", 1))
m.Store("shortcode", NewScript("shortcode", "/wp-includes/js/shortcode"+suffix+".js", []string{"underscore"}, "", 1))
m.Store("media-models", NewScript("media-models", "/wp-includes/js/media-models"+suffix+".js", []string{"wp-backbone"}, "", 1))
m.Store("wp-embed", NewScript("wp-embed", "/wp-includes/js/wp-embed"+suffix+".js", nil, "", 1))
m.Store("media-views", NewScript("media-views", "/wp-includes/js/media-views"+suffix+".js", []string{"utils", "media-models", "wp-plupload", "jquery-ui-sortable", "wp-mediaelement", "wp-api-request", "wp-a11y", "clipboard", "wp-i18n"}, "", 1))
m.Store("media-editor", NewScript("media-editor", "/wp-includes/js/media-editor"+suffix+".js", []string{"shortcode", "media-views", "wp-i18n"}, "", 1))
m.Store("media-audiovideo", NewScript("media-audiovideo", "/wp-includes/js/media-audiovideo"+suffix+".js", []string{"media-editor"}, "", 1))
m.Store("mce-view", NewScript("mce-view", "/wp-includes/js/mce-view"+suffix+".js", []string{"shortcode", "jquery", "media-views", "media-audiovideo"}, "", 1))
m.Store("wp-api", NewScript("wp-api", "/wp-includes/js/wp-api"+suffix+".js", []string{"jquery", "backbone", "underscore", "wp-api-request"}, "", 1))
m.Store("react", NewScript("react", "/wp-includes/js/dist/vendor/react"+suffix+".js", []string{"wp-polyfill"}, "18.2.0", 1))
m.Store("react-dom", NewScript("react-dom", "/wp-includes/js/dist/vendor/react-dom"+suffix+".js", []string{"react"}, "18.2.0", 1))
m.Store("regenerator-runtime", NewScript("regenerator-runtime", "/wp-includes/js/dist/vendor/regenerator-runtime"+suffix+".js", nil, "0.13.11", 1))
m.Store("moment", NewScript("moment", "/wp-includes/js/dist/vendor/moment"+suffix+".js", nil, "2.29.4", 1))
m.Store("lodash", NewScript("lodash", "/wp-includes/js/dist/vendor/lodash"+suffix+".js", nil, "4.17.19", 1))
m.Store("wp-polyfill-fetch", NewScript("wp-polyfill-fetch", "/wp-includes/js/dist/vendor/wp-polyfill-fetch"+suffix+".js", nil, "3.6.2", 1))
m.Store("wp-polyfill-formdata", NewScript("wp-polyfill-formdata", "/wp-includes/js/dist/vendor/wp-polyfill-formdata"+suffix+".js", nil, "4.0.10", 1))
m.Store("wp-polyfill-node-contains", NewScript("wp-polyfill-node-contains", "/wp-includes/js/dist/vendor/wp-polyfill-node-contains"+suffix+".js", nil, "4.6.0", 1))
m.Store("wp-polyfill-url", NewScript("wp-polyfill-url", "/wp-includes/js/dist/vendor/wp-polyfill-url"+suffix+".js", nil, "3.6.4", 1))
m.Store("wp-polyfill-dom-rect", NewScript("wp-polyfill-dom-rect", "/wp-includes/js/dist/vendor/wp-polyfill-dom-rect"+suffix+".js", nil, "4.6.0", 1))
m.Store("wp-polyfill-element-closest", NewScript("wp-polyfill-element-closest", "/wp-includes/js/dist/vendor/wp-polyfill-element-closest"+suffix+".js", nil, "3.0.2", 1))
m.Store("wp-polyfill-object-fit", NewScript("wp-polyfill-object-fit", "/wp-includes/js/dist/vendor/wp-polyfill-object-fit"+suffix+".js", nil, "2.3.5", 1))
m.Store("wp-polyfill-inert", NewScript("wp-polyfill-inert", "/wp-includes/js/dist/vendor/wp-polyfill-inert"+suffix+".js", nil, "3.1.2", 1))
m.Store("wp-polyfill", NewScript("wp-polyfill", "/wp-includes/js/dist/vendor/wp-polyfill"+suffix+".js", []string{"wp-polyfill-inert", "regenerator-runtime"}, "3.15.0", 1))
m.Store("wp-tinymce-root", NewScript("wp-tinymce-root", "http://wp.test/wp-includes/js/tinymce/tinymce"+suffix+".js", nil, "49110-20201110", nil))
m.Store("wp-tinymce", NewScript("wp-tinymce", "http://wp.test/wp-includes/js/tinymce/plugins/compat3x/plugin"+suffix+".js", []string{"wp-tinymce-root"}, "49110-20201110", nil))
m.Store("wp-tinymce-lists", NewScript("wp-tinymce-lists", "http://wp.test/wp-includes/js/tinymce/plugins/lists/plugin"+suffix+".js", []string{"wp-tinymce"}, "49110-20201110", nil))
m.Store("wp-a11y", NewScript("wp-a11y", "/wp-includes/js/dist/a11y"+suffix+".js", []string{"wp-dom-ready", "wp-i18n", "wp-polyfill"}, "ecce20f002eda4c19664", 1))
m.Store("wp-annotations", NewScript("wp-annotations", "/wp-includes/js/dist/annotations"+suffix+".js", []string{"wp-data", "wp-hooks", "wp-i18n", "wp-polyfill", "wp-rich-text"}, "1720fc5d5c76f53a1740", 1))
m.Store("wp-api-fetch", NewScript("wp-api-fetch", "/wp-includes/js/dist/api-fetch"+suffix+".js", []string{"wp-i18n", "wp-polyfill", "wp-url"}, "bc0029ca2c943aec5311", 1))
m.Store("wp-autop", NewScript("wp-autop", "/wp-includes/js/dist/autop"+suffix+".js", []string{"wp-polyfill"}, "43197d709df445ccf849", 1))
m.Store("wp-blob", NewScript("wp-blob", "/wp-includes/js/dist/blob"+suffix+".js", []string{"wp-polyfill"}, "e7b4ea96175a89b263e2", 1))
m.Store("wp-block-directory", NewScript("wp-block-directory", "/wp-includes/js/dist/block-directory"+suffix+".js", []string{"wp-a11y", "wp-api-fetch", "wp-block-editor", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-editor", "wp-element", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-notices", "wp-plugins", "wp-polyfill", "wp-primitives", "wp-url"}, "9c45b8d28fc867ceed45", 1))
m.Store("wp-block-editor", NewScript("wp-block-editor", "/wp-includes/js/dist/block-editor"+suffix+".js", []string{"lodash", "react", "react-dom", "wp-a11y", "wp-api-fetch", "wp-blob", "wp-blocks", "wp-components", "wp-compose", "wp-data", "wp-date", "wp-deprecated", "wp-dom", "wp-element", "wp-escape-html", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-is-shallow-equal", "wp-keyboard-shortcuts", "wp-keycodes", "wp-notices", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-rich-text", "wp-shortcode", "wp-style-engine", "wp-token-list", "wp-url", "wp-warning", "wp-wordcount"}, "43e40e04f77d598ede94", 1))
m.Store("wp-block-library", NewScript("wp-block-library", "/wp-includes/js/dist/block-library"+suffix+".js", []string{"lodash", "wp-a11y", "wp-api-fetch", "wp-autop", "wp-blob", "wp-block-editor", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-date", "wp-deprecated", "wp-dom", "wp-element", "wp-escape-html", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-keycodes", "wp-notices", "wp-polyfill", "wp-primitives", "wp-private-apis", "wp-reusable-blocks", "wp-rich-text", "wp-server-side-render", "wp-url", "wp-viewport", "editor"}, "3115f0b5551a55bb6d3b", 1))
m.Store("wp-block-serialization-default-parser", NewScript("wp-block-serialization-default-parser", "/wp-includes/js/dist/block-serialization-default-parser"+suffix+".js", []string{"wp-polyfill"}, "30ffd7e7e199f10b2a6d", 1))
m.Store("wp-blocks", NewScript("wp-blocks", "/wp-includes/js/dist/blocks"+suffix+".js", []string{"lodash", "wp-autop", "wp-blob", "wp-block-serialization-default-parser", "wp-compose", "wp-data", "wp-deprecated", "wp-dom", "wp-element", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-is-shallow-equal", "wp-polyfill", "wp-shortcode"}, "639e14271099dc3d85bf", 1))
m.Store("wp-components", NewScript("wp-components", "/wp-includes/js/dist/components"+suffix+".js", []string{"lodash", "react", "react-dom", "wp-a11y", "wp-compose", "wp-date", "wp-deprecated", "wp-dom", "wp-element", "wp-escape-html", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-is-shallow-equal", "wp-keycodes", "wp-polyfill", "wp-primitives", "wp-private-apis", "wp-rich-text", "wp-warning"}, "bf6e0ec3089253604b52", 1))
m.Store("wp-compose", NewScript("wp-compose", "/wp-includes/js/dist/compose"+suffix+".js", []string{"react", "wp-deprecated", "wp-dom", "wp-element", "wp-is-shallow-equal", "wp-keycodes", "wp-polyfill", "wp-priority-queue"}, "7d5916e3b2ef0ea01400", 1))
m.Store("wp-core-data", NewScript("wp-core-data", "/wp-includes/js/dist/core-data"+suffix+".js", []string{"lodash", "wp-api-fetch", "wp-blocks", "wp-compose", "wp-data", "wp-deprecated", "wp-element", "wp-html-entities", "wp-i18n", "wp-is-shallow-equal", "wp-polyfill", "wp-url"}, "fc0de6bb17aa25caf698", 1))
m.Store("wp-customize-widgets", NewScript("wp-customize-widgets", "/wp-includes/js/dist/customize-widgets"+suffix+".js", []string{"wp-block-editor", "wp-block-library", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-deprecated", "wp-dom", "wp-element", "wp-hooks", "wp-i18n", "wp-is-shallow-equal", "wp-keyboard-shortcuts", "wp-keycodes", "wp-media-utils", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-widgets"}, "7ae69cc350436c0cf301", 1))
m.Store("wp-data", NewScript("wp-data", "/wp-includes/js/dist/data"+suffix+".js", []string{"lodash", "wp-compose", "wp-deprecated", "wp-element", "wp-is-shallow-equal", "wp-polyfill", "wp-priority-queue", "wp-private-apis", "wp-redux-routine"}, "90cebfec01d1a3f0368e", 1))
m.Store("wp-data-controls", NewScript("wp-data-controls", "/wp-includes/js/dist/data-controls"+suffix+".js", []string{"wp-api-fetch", "wp-data", "wp-deprecated", "wp-polyfill"}, "e10d473d392daa8501e8", 1))
m.Store("wp-date", NewScript("wp-date", "/wp-includes/js/dist/date"+suffix+".js", []string{"moment", "wp-deprecated", "wp-polyfill"}, "f8550b1212d715fbf745", 1))
m.Store("wp-deprecated", NewScript("wp-deprecated", "/wp-includes/js/dist/deprecated"+suffix+".js", []string{"wp-hooks", "wp-polyfill"}, "6c963cb9494ba26b77eb", 1))
m.Store("wp-dom", NewScript("wp-dom", "/wp-includes/js/dist/dom"+suffix+".js", []string{"wp-deprecated", "wp-polyfill"}, "e03c89e1dd68aee1cb3a", 1))
m.Store("wp-dom-ready", NewScript("wp-dom-ready", "/wp-includes/js/dist/dom-ready"+suffix+".js", []string{"wp-polyfill"}, "392bdd43726760d1f3ca", 1))
m.Store("wp-edit-post", NewScript("wp-edit-post", "/wp-includes/js/dist/edit-post"+suffix+".js", []string{"lodash", "wp-a11y", "wp-api-fetch", "wp-block-editor", "wp-block-library", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-deprecated", "wp-editor", "wp-element", "wp-hooks", "wp-i18n", "wp-keyboard-shortcuts", "wp-keycodes", "wp-media-utils", "wp-notices", "wp-plugins", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-url", "wp-viewport", "wp-warning", "wp-widgets", "media-models", "media-views", "postbox", "wp-dom-ready"}, "d098b8ee5bdffa238c03", 1))
m.Store("wp-edit-site", NewScript("wp-edit-site", "/wp-includes/js/dist/edit-site"+suffix+".js", []string{"lodash", "react", "wp-a11y", "wp-api-fetch", "wp-block-editor", "wp-block-library", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-deprecated", "wp-editor", "wp-element", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-keyboard-shortcuts", "wp-keycodes", "wp-media-utils", "wp-notices", "wp-plugins", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-reusable-blocks", "wp-url", "wp-viewport", "wp-widgets"}, "fcf81e803ab1af60d4f8", 1))
m.Store("wp-edit-widgets", NewScript("wp-edit-widgets", "/wp-includes/js/dist/edit-widgets"+suffix+".js", []string{"wp-api-fetch", "wp-block-editor", "wp-block-library", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-deprecated", "wp-dom", "wp-element", "wp-hooks", "wp-i18n", "wp-keyboard-shortcuts", "wp-keycodes", "wp-media-utils", "wp-notices", "wp-plugins", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-reusable-blocks", "wp-url", "wp-viewport", "wp-widgets"}, "d683d5fc75e655fdf974", 1))
m.Store("wp-editor", NewScript("wp-editor", "/wp-includes/js/dist/editor"+suffix+".js", []string{"lodash", "react", "wp-a11y", "wp-api-fetch", "wp-blob", "wp-block-editor", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-date", "wp-deprecated", "wp-dom", "wp-element", "wp-hooks", "wp-html-entities", "wp-i18n", "wp-keyboard-shortcuts", "wp-keycodes", "wp-media-utils", "wp-notices", "wp-polyfill", "wp-preferences", "wp-primitives", "wp-private-apis", "wp-reusable-blocks", "wp-rich-text", "wp-server-side-render", "wp-url", "wp-wordcount"}, "1fb5fcf129627da4939e", 1))
m.Store("wp-element", NewScript("wp-element", "/wp-includes/js/dist/element"+suffix+".js", []string{"react", "react-dom", "wp-escape-html", "wp-polyfill"}, "b3bda690cfc516378771", 1))
m.Store("wp-escape-html", NewScript("wp-escape-html", "/wp-includes/js/dist/escape-html"+suffix+".js", []string{"wp-polyfill"}, "03e27a7b6ae14f7afaa6", 1))
m.Store("wp-format-library", NewScript("wp-format-library", "/wp-includes/js/dist/format-library"+suffix+".js", []string{"wp-a11y", "wp-block-editor", "wp-components", "wp-data", "wp-element", "wp-html-entities", "wp-i18n", "wp-polyfill", "wp-primitives", "wp-rich-text", "wp-url"}, "cd4a10ec005e2f001978", 1))
m.Store("wp-hooks", NewScript("wp-hooks", "/wp-includes/js/dist/hooks"+suffix+".js", []string{"wp-polyfill"}, "4169d3cf8e8d95a3d6d5", 1))
m.Store("wp-html-entities", NewScript("wp-html-entities", "/wp-includes/js/dist/html-entities"+suffix+".js", []string{"wp-polyfill"}, "36a4a255da7dd2e1bf8e", 1))
m.Store("wp-i18n", NewScript("wp-i18n", "/wp-includes/js/dist/i18n"+suffix+".js", []string{"wp-hooks", "wp-polyfill"}, "9e794f35a71bb98672ae", 1))
m.Store("wp-is-shallow-equal", NewScript("wp-is-shallow-equal", "/wp-includes/js/dist/is-shallow-equal"+suffix+".js", []string{"wp-polyfill"}, "20c2b06ecf04afb14fee", 1))
m.Store("wp-keyboard-shortcuts", NewScript("wp-keyboard-shortcuts", "/wp-includes/js/dist/keyboard-shortcuts"+suffix+".js", []string{"wp-data", "wp-element", "wp-keycodes", "wp-polyfill"}, "b696c16720133edfc065", 1))
m.Store("wp-keycodes", NewScript("wp-keycodes", "/wp-includes/js/dist/keycodes"+suffix+".js", []string{"wp-i18n", "wp-polyfill"}, "184b321fa2d3bc7fd173", 1))
m.Store("wp-list-reusable-blocks", NewScript("wp-list-reusable-blocks", "/wp-includes/js/dist/list-reusable-blocks"+suffix+".js", []string{"wp-api-fetch", "wp-components", "wp-compose", "wp-element", "wp-i18n", "wp-polyfill"}, "6ba78be26d660b6af113", 1))
m.Store("wp-media-utils", NewScript("wp-media-utils", "/wp-includes/js/dist/media-utils"+suffix+".js", []string{"wp-api-fetch", "wp-blob", "wp-element", "wp-i18n", "wp-polyfill"}, "f837b6298c83612cd6f6", 1))
m.Store("wp-notices", NewScript("wp-notices", "/wp-includes/js/dist/notices"+suffix+".js", []string{"wp-data", "wp-polyfill"}, "9c1575b7a31659f45a45", 1))
m.Store("wp-nux", NewScript("wp-nux", "/wp-includes/js/dist/nux"+suffix+".js", []string{"wp-components", "wp-compose", "wp-data", "wp-deprecated", "wp-element", "wp-i18n", "wp-polyfill", "wp-primitives"}, "038c48e26a91639ae8ab", 1))
m.Store("wp-plugins", NewScript("wp-plugins", "/wp-includes/js/dist/plugins"+suffix+".js", []string{"wp-compose", "wp-element", "wp-hooks", "wp-polyfill", "wp-primitives"}, "0d1b90278bae7df6ecf9", 1))
m.Store("wp-preferences", NewScript("wp-preferences", "/wp-includes/js/dist/preferences"+suffix+".js", []string{"wp-a11y", "wp-components", "wp-data", "wp-element", "wp-i18n", "wp-polyfill", "wp-primitives", "wp-preferences-persistence"}, "c66e137a7e588dab54c3", 1))
m.Store("wp-preferences-persistence", NewScript("wp-preferences-persistence", "/wp-includes/js/dist/preferences-persistence"+suffix+".js", []string{"wp-api-fetch", "wp-polyfill"}, "c5543628aa7ff5bd5be4", 1))
m.Store("wp-primitives", NewScript("wp-primitives", "/wp-includes/js/dist/primitives"+suffix+".js", []string{"wp-element", "wp-polyfill"}, "dfac1545e52734396640", 1))
m.Store("wp-priority-queue", NewScript("wp-priority-queue", "/wp-includes/js/dist/priority-queue"+suffix+".js", []string{"wp-polyfill"}, "422e19e9d48b269c5219", 1))
m.Store("wp-private-apis", NewScript("wp-private-apis", "/wp-includes/js/dist/private-apis"+suffix+".js", []string{"wp-polyfill"}, "6f247ed2bc3571743bba", 1))
m.Store("wp-redux-routine", NewScript("wp-redux-routine", "/wp-includes/js/dist/redux-routine"+suffix+".js", []string{"wp-polyfill"}, "d86e7e9f062d7582f76b", 1))
m.Store("wp-reusable-blocks", NewScript("wp-reusable-blocks", "/wp-includes/js/dist/reusable-blocks"+suffix+".js", []string{"wp-block-editor", "wp-blocks", "wp-components", "wp-core-data", "wp-data", "wp-element", "wp-i18n", "wp-notices", "wp-polyfill", "wp-primitives", "wp-url"}, "a7367a6154c724b51b31", 1))
m.Store("wp-rich-text", NewScript("wp-rich-text", "/wp-includes/js/dist/rich-text"+suffix+".js", []string{"wp-a11y", "wp-compose", "wp-data", "wp-deprecated", "wp-element", "wp-escape-html", "wp-i18n", "wp-keycodes", "wp-polyfill"}, "9307ec04c67d79b6e813", 1))
m.Store("wp-server-side-render", NewScript("wp-server-side-render", "/wp-includes/js/dist/server-side-render"+suffix+".js", []string{"wp-api-fetch", "wp-blocks", "wp-components", "wp-compose", "wp-data", "wp-element", "wp-i18n", "wp-polyfill", "wp-url"}, "d1bc93277666143a3f5e", 1))
m.Store("wp-shortcode", NewScript("wp-shortcode", "/wp-includes/js/dist/shortcode"+suffix+".js", []string{"wp-polyfill"}, "7539044b04e6bca57f2e", 1))
m.Store("wp-style-engine", NewScript("wp-style-engine", "/wp-includes/js/dist/style-engine"+suffix+".js", []string{"lodash", "wp-polyfill"}, "528e6cf281ffc9b7bd3c", 1))
m.Store("wp-token-list", NewScript("wp-token-list", "/wp-includes/js/dist/token-list"+suffix+".js", []string{"wp-polyfill"}, "f2cf0bb3ae80de227e43", 1))
m.Store("wp-url", NewScript("wp-url", "/wp-includes/js/dist/url"+suffix+".js", []string{"wp-polyfill"}, "16185fce2fb043a0cfed", 1))
m.Store("wp-viewport", NewScript("wp-viewport", "/wp-includes/js/dist/viewport"+suffix+".js", []string{"wp-compose", "wp-data", "wp-element", "wp-polyfill"}, "4f6bd168b2b8b45c8a6b", 1))
m.Store("wp-warning", NewScript("wp-warning", "/wp-includes/js/dist/warning"+suffix+".js", []string{"wp-polyfill"}, "4acee5fc2fd9a24cefc2", 1))
m.Store("wp-widgets", NewScript("wp-widgets", "/wp-includes/js/dist/widgets"+suffix+".js", []string{"wp-api-fetch", "wp-block-editor", "wp-blocks", "wp-components", "wp-compose", "wp-core-data", "wp-data", "wp-element", "wp-i18n", "wp-notices", "wp-polyfill", "wp-primitives"}, "040ac8be5e0cfc4b52df", 1))
m.Store("wp-wordcount", NewScript("wp-wordcount", "/wp-includes/js/dist/wordcount"+suffix+".js", []string{"wp-polyfill"}, "feb9569307aec24292f2", 1))
}
func defaultLocalize() {
/*AddDynamicLocalize(h, "utils", "userSettings", map[string]any{
"url": h.C.Request.RequestURI,
"uid": "0",
"time": number.IntToString(time.Now().Unix()),
"secure": h.IsHttps(),
})*/
AddStaticLocalize("wp-ajax-response", "wpAjax", map[string]any{
"noPerm": `抱歉,您不能这么做。`,
"broken": `出现了问题。`,
})
AddStaticLocalize("wp-api-request", "wpApiSettings", map[string]any{
"root": `/wp-json/`,
"nonce": `9a3ce0a5d7`,
"versionString": `wp/v2/`,
})
AddStaticLocalize("jquery-ui-autocomplete", "uiAutocompleteL10n", map[string]any{
"noResults": `未找到结果。`,
"oneResult": `找到1个结果。使用上下方向键来导航。`,
"manyResults": `找到%d个结果。使用上下方向键来导航。`,
"itemSelected": `已选择项目。`,
})
AddStaticLocalize("thickbox", "thickboxL10n", map[string]any{
"next": `下一页 &gt;`,
"prev": `&lt; 上一页`,
"image": `图片`,
"of": `/`,
"close": `关闭`,
"noiframes": `这个功能需要iframe的支持。您可能禁止了iframe的显示或您的浏览器不支持此功能。`,
"loadingAnimation": `/wp-includes/js/thickbox/loadingAnimation.gif`,
})
AddStaticLocalize("mediaelement", "_wpmejsSettings", map[string]any{
"pluginPath": `/wp-includes/js/mediaelement/`,
"classPrefix": `mejs-`,
"stretching": `responsive`,
"audioShortcodeLibrary": `mediaelement`,
"videoShortcodeLibrary": `mediaelement`,
})
AddStaticLocalize("zxcvbn-async", "_zxcvbnSettings", map[string]any{
"src": `/wp-includes/js/zxcvbn.min.js`,
})
AddStaticLocalize("mce-view", "mceViewL10n", map[string]any{
"shortcodes": []string{"wp_caption", "caption", "gallery", "playlist", "audio", "video", "embed"},
})
AddStaticLocalize("word-count", "wordCountL10n", map[string]any{
"type": `characters_excluding_spaces`,
"shortcodes": []string{"wp_caption", "caption", "gallery", "playlist", "audio", "video", "embed"},
})
}
func defaultTranslate() {
SetTranslation("common", "default", "")
SetTranslation("wp-pointer", "default", "")
SetTranslation("wp-auth-check", "default", "")
SetTranslation("wp-theme-plugin-editor", "default", "")
SetTranslation("password-strength-meter", "default", "")
SetTranslation("application-passwords", "default", "")
SetTranslation("auth-app", "default", "")
SetTranslation("user-profile", "default", "")
SetTranslation("media-views", "default", "")
SetTranslation("media-editor", "default", "")
SetTranslation("wp-a11y", "default", "")
SetTranslation("wp-annotations", "default", "")
SetTranslation("wp-api-fetch", "default", "")
SetTranslation("wp-block-directory", "default", "")
SetTranslation("wp-block-editor", "default", "")
SetTranslation("wp-block-library", "default", "")
SetTranslation("wp-blocks", "default", "")
SetTranslation("wp-components", "default", "")
SetTranslation("wp-core-data", "default", "")
SetTranslation("wp-customize-widgets", "default", "")
SetTranslation("wp-edit-post", "default", "")
SetTranslation("wp-edit-site", "default", "")
SetTranslation("wp-edit-widgets", "default", "")
SetTranslation("wp-editor", "default", "")
SetTranslation("wp-format-library", "default", "")
SetTranslation("wp-keycodes", "default", "")
SetTranslation("wp-list-reusable-blocks", "default", "")
SetTranslation("wp-media-utils", "default", "")
SetTranslation("wp-nux", "default", "")
SetTranslation("wp-preferences", "default", "")
SetTranslation("wp-reusable-blocks", "default", "")
SetTranslation("wp-rich-text", "default", "")
SetTranslation("wp-server-side-render", "default", "")
SetTranslation("wp-widgets", "default", "")
SetTranslation("common", "default", "")
SetTranslation("wp-pointer", "default", "")
SetTranslation("wp-auth-check", "default", "")
SetTranslation("wp-theme-plugin-editor", "default", "")
SetTranslation("password-strength-meter", "default", "")
SetTranslation("application-passwords", "default", "")
SetTranslation("auth-app", "default", "")
SetTranslation("user-profile", "default", "")
SetTranslation("media-views", "default", "")
SetTranslation("media-editor", "default", "")
SetTranslation("wp-a11y", "default", "")
SetTranslation("wp-annotations", "default", "")
SetTranslation("wp-api-fetch", "default", "")
SetTranslation("wp-block-directory", "default", "")
SetTranslation("wp-block-editor", "default", "")
SetTranslation("wp-block-library", "default", "")
SetTranslation("wp-blocks", "default", "")
SetTranslation("wp-components", "default", "")
SetTranslation("wp-core-data", "default", "")
SetTranslation("wp-customize-widgets", "default", "")
SetTranslation("wp-edit-post", "default", "")
SetTranslation("wp-edit-site", "default", "")
SetTranslation("wp-edit-widgets", "default", "")
SetTranslation("wp-editor", "default", "")
SetTranslation("wp-format-library", "default", "")
SetTranslation("wp-keycodes", "default", "")
SetTranslation("wp-list-reusable-blocks", "default", "")
SetTranslation("wp-media-utils", "default", "")
SetTranslation("wp-nux", "default", "")
SetTranslation("wp-preferences", "default", "")
SetTranslation("wp-reusable-blocks", "default", "")
SetTranslation("wp-rich-text", "default", "")
SetTranslation("wp-server-side-render", "default", "")
SetTranslation("wp-widgets", "default", "")
}
func defaultAddData() {
AddScriptData("json2", "conditional", `lt IE 8`)
AddScriptData("wp-embed-template-ie", "conditional", `lte IE 8`)
AddScriptData("wp-block-library-theme", "path", `wp-includes/css/dist/block-library/theme.min.css`)
AddScriptData("wp-block-editor", "path", `/wp-includes/css/dist/block-editor/style.min.css`)
AddScriptData("wp-block-library", "path", `/wp-includes/css/dist/block-library/style.min.css`)
AddScriptData("wp-block-directory", "path", `/wp-includes/css/dist/block-directory/style.min.css`)
AddScriptData("wp-components", "path", `/wp-includes/css/dist/components/style.min.css`)
AddScriptData("wp-edit-post", "path", `/wp-includes/css/dist/edit-post/style.min.css`)
AddScriptData("wp-editor", "path", `/wp-includes/css/dist/editor/style.min.css`)
AddScriptData("wp-format-library", "path", `/wp-includes/css/dist/format-library/style.min.css`)
AddScriptData("wp-list-reusable-blocks", "path", `/wp-includes/css/dist/list-reusable-blocks/style.min.css`)
AddScriptData("wp-reusable-blocks", "path", `/wp-includes/css/dist/reusable-blocks/style.min.css`)
AddScriptData("wp-nux", "path", `/wp-includes/css/dist/nux/style.min.css`)
AddScriptData("wp-widgets", "path", `/wp-includes/css/dist/widgets/style.min.css`)
AddScriptData("wp-edit-widgets", "path", `/wp-includes/css/dist/edit-widgets/style.min.css`)
AddScriptData("wp-customize-widgets", "path", `/wp-includes/css/dist/customize-widgets/style.min.css`)
AddScriptData("wp-edit-site", "path", `/wp-includes/css/dist/edit-site/style.min.css`)
AddScriptData("common", "rtl", `replace`)
AddScriptData("common", "suffix", `.min`)
AddScriptData("forms", "rtl", `replace`)
AddScriptData("forms", "suffix", `.min`)
AddScriptData("admin-menu", "rtl", `replace`)
AddScriptData("admin-menu", "suffix", `.min`)
AddScriptData("dashboard", "rtl", `replace`)
AddScriptData("dashboard", "suffix", `.min`)
AddScriptData("list-tables", "rtl", `replace`)
AddScriptData("list-tables", "suffix", `.min`)
AddScriptData("edit", "rtl", `replace`)
AddScriptData("edit", "suffix", `.min`)
AddScriptData("revisions", "rtl", `replace`)
AddScriptData("revisions", "suffix", `.min`)
AddScriptData("media", "rtl", `replace`)
AddScriptData("media", "suffix", `.min`)
AddScriptData("themes", "rtl", `replace`)
AddScriptData("themes", "suffix", `.min`)
AddScriptData("about", "rtl", `replace`)
AddScriptData("about", "suffix", `.min`)
AddScriptData("nav-menus", "rtl", `replace`)
AddScriptData("nav-menus", "suffix", `.min`)
AddScriptData("widgets", "rtl", `replace`)
AddScriptData("widgets", "suffix", `.min`)
AddScriptData("site-icon", "rtl", `replace`)
AddScriptData("site-icon", "suffix", `.min`)
AddScriptData("l10n", "rtl", `replace`)
AddScriptData("l10n", "suffix", `.min`)
AddScriptData("install", "rtl", `replace`)
AddScriptData("install", "suffix", `.min`)
AddScriptData("wp-color-picker", "rtl", `replace`)
AddScriptData("wp-color-picker", "suffix", `.min`)
AddScriptData("customize-controls", "rtl", `replace`)
AddScriptData("customize-controls", "suffix", `.min`)
AddScriptData("customize-widgets", "rtl", `replace`)
AddScriptData("customize-widgets", "suffix", `.min`)
AddScriptData("customize-nav-menus", "rtl", `replace`)
AddScriptData("customize-nav-menus", "suffix", `.min`)
AddScriptData("customize-preview", "rtl", `replace`)
AddScriptData("customize-preview", "suffix", `.min`)
AddScriptData("login", "rtl", `replace`)
AddScriptData("login", "suffix", `.min`)
AddScriptData("site-health", "rtl", `replace`)
AddScriptData("site-health", "suffix", `.min`)
AddScriptData("buttons", "rtl", `replace`)
AddScriptData("buttons", "suffix", `.min`)
AddScriptData("admin-bar", "rtl", `replace`)
AddScriptData("admin-bar", "suffix", `.min`)
AddScriptData("wp-auth-check", "rtl", `replace`)
AddScriptData("wp-auth-check", "suffix", `.min`)
AddScriptData("editor-buttons", "rtl", `replace`)
AddScriptData("editor-buttons", "suffix", `.min`)
AddScriptData("media-views", "rtl", `replace`)
AddScriptData("media-views", "suffix", `.min`)
AddScriptData("wp-pointer", "rtl", `replace`)
AddScriptData("wp-pointer", "suffix", `.min`)
AddScriptData("wp-jquery-ui-dialog", "rtl", `replace`)
AddScriptData("wp-jquery-ui-dialog", "suffix", `.min`)
AddScriptData("wp-reset-editor-styles", "rtl", `replace`)
AddScriptData("wp-reset-editor-styles", "suffix", `.min`)
AddScriptData("wp-editor-classic-layout-styles", "rtl", `replace`)
AddScriptData("wp-editor-classic-layout-styles", "suffix", `.min`)
AddScriptData("wp-block-library-theme", "rtl", `replace`)
AddScriptData("wp-block-library-theme", "suffix", `.min`)
AddScriptData("wp-edit-blocks", "rtl", `replace`)
AddScriptData("wp-edit-blocks", "suffix", `.min`)
AddScriptData("wp-block-editor", "rtl", `replace`)
AddScriptData("wp-block-editor", "suffix", `.min`)
AddScriptData("wp-block-library", "rtl", `replace`)
AddScriptData("wp-block-library", "suffix", `.min`)
AddScriptData("wp-block-directory", "rtl", `replace`)
AddScriptData("wp-block-directory", "suffix", `.min`)
AddScriptData("wp-components", "rtl", `replace`)
AddScriptData("wp-components", "suffix", `.min`)
AddScriptData("wp-customize-widgets", "rtl", `replace`)
AddScriptData("wp-customize-widgets", "suffix", `.min`)
AddScriptData("wp-edit-post", "rtl", `replace`)
AddScriptData("wp-edit-post", "suffix", `.min`)
AddScriptData("wp-edit-site", "rtl", `replace`)
AddScriptData("wp-edit-site", "suffix", `.min`)
AddScriptData("wp-edit-widgets", "rtl", `replace`)
AddScriptData("wp-edit-widgets", "suffix", `.min`)
AddScriptData("wp-editor", "rtl", `replace`)
AddScriptData("wp-editor", "suffix", `.min`)
AddScriptData("wp-format-library", "rtl", `replace`)
AddScriptData("wp-format-library", "suffix", `.min`)
AddScriptData("wp-list-reusable-blocks", "rtl", `replace`)
AddScriptData("wp-list-reusable-blocks", "suffix", `.min`)
AddScriptData("wp-reusable-blocks", "rtl", `replace`)
AddScriptData("wp-reusable-blocks", "suffix", `.min`)
AddScriptData("wp-nux", "rtl", `replace`)
AddScriptData("wp-nux", "suffix", `.min`)
AddScriptData("wp-widgets", "rtl", `replace`)
AddScriptData("wp-widgets", "suffix", `.min`)
AddScriptData("deprecated-media", "rtl", `replace`)
AddScriptData("deprecated-media", "suffix", `.min`)
AddScriptData("farbtastic", "rtl", `replace`)
AddScriptData("farbtastic", "suffix", `.min`)
}
func defaultAddInLineScript() {
AddInlineScript("mediaelement-core", `var mejsL10n = {"language":"zh","strings":{"mejs.download-file":"\u4e0b\u8f7d\u6587\u4ef6","mejs.install-flash":"\u60a8\u6b63\u5728\u4f7f\u7528\u7684\u6d4f\u89c8\u5668\u672a\u5b89\u88c5\u6216\u542f\u7528Flash\u64ad\u653e\u5668\uff0c\u8bf7\u542f\u7528\u60a8\u7684Flash\u64ad\u653e\u5668\u63d2\u4ef6\uff0c\u6216\u4ece https:\/\/get.adobe.com\/flashplayer\/ \u4e0b\u8f7d\u6700\u65b0\u7248\u3002","mejs.fullscreen":"\u5168\u5c4f","mejs.play":"\u64ad\u653e","mejs.pause":"\u6682\u505c","mejs.time-slider":"\u65f6\u95f4\u8f74","mejs.time-help-text":"\u4f7f\u7528\u5de6\/\u53f3\u7bad\u5934\u952e\u6765\u524d\u8fdb\u4e00\u79d2\uff0c\u4e0a\/\u4e0b\u7bad\u5934\u952e\u6765\u524d\u8fdb\u5341\u79d2\u3002","mejs.live-broadcast":"\u73b0\u573a\u76f4\u64ad","mejs.volume-help-text":"\u4f7f\u7528\u4e0a\/\u4e0b\u7bad\u5934\u952e\u6765\u589e\u9ad8\u6216\u964d\u4f4e\u97f3\u91cf\u3002","mejs.unmute":"\u53d6\u6d88\u9759\u97f3","mejs.mute":"\u9759\u97f3","mejs.volume-slider":"\u97f3\u91cf","mejs.video-player":"\u89c6\u9891\u64ad\u653e\u5668","mejs.audio-player":"\u97f3\u9891\u64ad\u653e\u5668","mejs.captions-subtitles":"\u8bf4\u660e\u6587\u5b57\u6216\u5b57\u5e55","mejs.captions-chapters":"\u7ae0\u8282","mejs.none":"\u65e0","mejs.afrikaans":"\u5357\u975e\u8377\u5170\u8bed","mejs.albanian":"\u963f\u5c14\u5df4\u5c3c\u4e9a\u8bed","mejs.arabic":"\u963f\u62c9\u4f2f\u8bed","mejs.belarusian":"\u767d\u4fc4\u7f57\u65af\u8bed","mejs.bulgarian":"\u4fdd\u52a0\u5229\u4e9a\u8bed","mejs.catalan":"\u52a0\u6cf0\u7f57\u5c3c\u4e9a\u8bed","mejs.chinese":"\u4e2d\u6587","mejs.chinese-simplified":"\u4e2d\u6587\uff08\u7b80\u4f53\uff09","mejs.chinese-traditional":"\u4e2d\u6587(\uff08\u7e41\u4f53\uff09","mejs.croatian":"\u514b\u7f57\u5730\u4e9a\u8bed","mejs.czech":"\u6377\u514b\u8bed","mejs.danish":"\u4e39\u9ea6\u8bed","mejs.dutch":"\u8377\u5170\u8bed","mejs.english":"\u82f1\u8bed","mejs.estonian":"\u7231\u6c99\u5c3c\u4e9a\u8bed","mejs.filipino":"\u83f2\u5f8b\u5bbe\u8bed","mejs.finnish":"\u82ac\u5170\u8bed","mejs.french":"\u6cd5\u8bed","mejs.galician":"\u52a0\u5229\u897f\u4e9a\u8bed","mejs.german":"\u5fb7\u8bed","mejs.greek":"\u5e0c\u814a\u8bed","mejs.haitian-creole":"\u6d77\u5730\u514b\u91cc\u5965\u5c14\u8bed","mejs.hebrew":"\u5e0c\u4f2f\u6765\u8bed","mejs.hindi":"\u5370\u5730\u8bed","mejs.hungarian":"\u5308\u7259\u5229\u8bed","mejs.icelandic":"\u51b0\u5c9b\u8bed","mejs.indonesian":"\u5370\u5ea6\u5c3c\u897f\u4e9a\u8bed","mejs.irish":"\u7231\u5c14\u5170\u8bed","mejs.italian":"\u610f\u5927\u5229\u8bed","mejs.japanese":"\u65e5\u8bed","mejs.korean":"\u97e9\u8bed","mejs.latvian":"\u62c9\u8131\u7ef4\u4e9a\u8bed","mejs.lithuanian":"\u7acb\u9676\u5b9b\u8bed","mejs.macedonian":"\u9a6c\u5176\u987f\u8bed","mejs.malay":"\u9a6c\u6765\u8bed","mejs.maltese":"\u9a6c\u8033\u4ed6\u8bed","mejs.norwegian":"\u632a\u5a01\u8bed","mejs.persian":"\u6ce2\u65af\u8bed","mejs.polish":"\u6ce2\u5170\u8bed","mejs.portuguese":"\u8461\u8404\u7259\u8bed","mejs.romanian":"\u7f57\u9a6c\u5c3c\u4e9a\u8bed","mejs.russian":"\u4fc4\u8bed","mejs.serbian":"\u585e\u5c14\u7ef4\u4e9a\u8bed","mejs.slovak":"\u65af\u6d1b\u4f10\u514b\u8bed","mejs.slovenian":"\u65af\u6d1b\u6587\u5c3c\u4e9a\u8bed","mejs.spanish":"\u897f\u73ed\u7259\u8bed","mejs.swahili":"\u65af\u74e6\u5e0c\u91cc\u8bed","mejs.swedish":"\u745e\u5178\u8bed","mejs.tagalog":"\u4ed6\u52a0\u7984\u8bed","mejs.thai":"\u6cf0\u8bed","mejs.turkish":"\u571f\u8033\u5176\u8bed","mejs.ukrainian":"\u4e4c\u514b\u5170\u8bed","mejs.vietnamese":"\u8d8a\u5357\u8bed","mejs.welsh":"\u5a01\u5c14\u58eb\u8bed","mejs.yiddish":"\u610f\u7b2c\u7eea\u8bed"}};`, "before")
AddInlineScript("lodash", `window.lodash = _.noConflict();`, "after")
AddInlineScript("moment", `moment.updateLocale( 'zh_CN', {"months":["1\u6708","2\u6708","3\u6708","4\u6708","5\u6708","6\u6708","7\u6708","8\u6708","9\u6708","10\u6708","11\u6708","12\u6708"],"monthsShort":["1\u6708","2\u6708","3\u6708","4\u6708","5\u6708","6\u6708","7\u6708","8\u6708","9\u6708","10\u6708","11\u6708","12\u6708"],"weekdays":["\u661f\u671f\u65e5","\u661f\u671f\u4e00","\u661f\u671f\u4e8c","\u661f\u671f\u4e09","\u661f\u671f\u56db","\u661f\u671f\u4e94","\u661f\u671f\u516d"],"weekdaysShort":["\u5468\u65e5","\u5468\u4e00","\u5468\u4e8c","\u5468\u4e09","\u5468\u56db","\u5468\u4e94","\u5468\u516d"],"week":{"dow":1},"longDateFormat":{"LT":"ag:i","LTS":null,"L":null,"LL":"Y\u5e74n\u6708j\u65e5","LLL":"Y\u5e74n\u6708j\u65e5a g:i","LLLL":null}} );`, "after")
AddInlineScript("wp-i18n", `wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );`, "after")
AddInlineScript("text-widgets", `wp.textWidgets.idBases.push( "text" );`, "after")
AddInlineScript("custom-html-widgets", `wp.customHtmlWidgets.idBases.push( "custom_html" );`, "after")
}
func defaultAddInLineStyle() {
AddInlineStyle("global-styles", GetGlobalStyletSheet())
AddInlineStyle("global-styles", `.wp-block-navigation a:where(:not(.wp-element-button)){color: inherit;}`)
AddInlineStyle("global-styles", `:where(.wp-block-columns.is-layout-flex){gap: 2em;}`)
AddInlineStyle("global-styles", `.wp-block-pullquote{font-size: 1.5em;line-height: 1.6;}`)
}
var re = regexp.MustCompile(`(?is:\([A-Za-z0-9'.:\-/, ]+\))`)
var rea = regexp.MustCompile(`array\(array\(.*?\)\)`)
func InitDefaultScriptSetting() {
initThemeJson()
defaultLocalize()
defaultTranslate()
defaultAddData()
defaultAddInLineScript()
defaultAddInLineStyle()
}
func initThemeJson() {
blocksData := __blocksData()
path := config.GetConfig().WpDir
f, err := os.ReadFile(filepath.Join(path, "wp-includes/theme.json"))
if err != nil {
logs.Error(err, "can't open theme json", path)
return
}
var j map[string]any
err = json.Unmarshal(f, &j)
if err != nil {
logs.Error(err, "can't parse theme json")
return
}
t := ThemeJson{blocksData, j}
setThemeJson(j)
setSpacingSizes(t)
__themeJson.Store(t)
}
func setThemeJson(m map[string]any) {
blocks, ok := maps.GetStrAnyVal[map[string]any](m, "settings.blocks")
if !ok {
return
}
var paths = [][]string{{"settings"}}
for name := range blocks {
paths = append(paths, []string{"settings", "blocks", name})
}
for _, path := range paths {
for _, metadatum := range presetsMetadata {
pathx := append(path, metadatum.path...)
key := strings.Join(pathx, ".")
preset, ok := maps.GetStrAnyVal[[]any](m, key)
if !ok || len(preset) < 1 {
continue
}
var presets []map[string]string
for _, a := range preset {
v, ok := a.(map[string]any)
if !ok {
continue
}
mm := map[string]string{}
for kk, vv := range v {
val, ok := vv.(string)
if !ok {
continue
}
mm[kk] = val
}
presets = append(presets, mm)
}
maps.SetStrAnyVal(m, key, map[string]any{
"default": presets,
})
}
}
}
var __propertyMappings = map[string]string{
"apiVersion": "api_version",
"title": "title",
"category": "category",
"parent": "parent",
"ancestor": "ancestor",
"icon": "icon",
"description": "description",
"keywords": "keywords",
"attributes": "attributes",
"providesContext": "provides_context",
"usesContext": "uses_context",
"supports": "supports",
"styles": "styles",
"variations": "variations",
"example": "example",
}
func __propertyMap(m map[string]any) {
for k, mappedKey := range __propertyMappings {
vv, ok := m[k]
if ok {
m[mappedKey] = vv
}
}
}
func __blocksData() map[string]any {
path := config.GetConfig().WpDir
//path := "/var/www/html/wordpress"
b, err := os.ReadFile(filepath.Join(path, "wp-includes/blocks/blocks-json.php"))
if err != nil {
logs.Error(err, "can't open block json", path)
return nil
}
bb := re.ReplaceAllStringFunc(string(b), func(s string) string {
return str.Replace(s, map[string]string{
"(": "[",
")": "]",
})
})
bb = strings.ReplaceAll(bb, "\"", `\"`)
bb = rea.ReplaceAllStringFunc(bb, func(s string) string {
s = strings.ReplaceAll(s, "array(array", "[")
ss := []rune(s)
ss[len(ss)-1] = ']'
s = string(ss)
return s
})
bb = str.Replace(bb, map[string]string{
"<?php": "",
"return": "",
"array": "",
"()": "[]",
"(": "{",
")": "}",
"=>": ":",
";": "",
"'": `"`,
})
var blocks map[string]any
err = json.Unmarshal([]byte(bb), &blocks)
if err != nil {
logs.Error(err, "can't parse block json")
return nil
}
c := map[string]any{
"version": int64(2),
}
for k, v := range blocks {
m, ok := v.(map[string]any)
if !ok {
continue
}
_, ok = m["style"]
if !ok {
m["style"] = str.Join("wp-block-", k)
}
_, ok = m["editorStyle"]
if !ok {
m["editorStyle"] = str.Join("wp-block-", k, "-editor")
}
__propertyMap(m)
name := maps.GetStrAnyValWithDefaults(m, "name", str.Join("core/", k))
blocks[name] = v
if name != k {
delete(blocks, k)
}
__blockSelectors(m)
s, ok := maps.GetStrAnyVal[map[string]any](m, "supports.__experimentalStyle")
if ok {
__removeComment(s)
maps.SetStrAnyVal(c, str.Join("styles.blocks.", name), s)
}
_, ok = maps.GetStrAnyVal[string](m, "supports.spacing.blockGap.__experimentalDefault")
if ok {
key := str.Join("styles.blocks.", name, ".spacing.blockGap")
_, ok := maps.GetStrAnyVal[string](c, key)
if !ok {
maps.SetStrAnyVal[map[string]any](c, key, nil)
}
}
}
return map[string]any{
"blocks_metadata": blocks,
"theme_json": c,
}
}
func __blockSelectors(m map[string]any) {
selector, ok := maps.GetStrAnyVal[string](m, "supports.__experimentalSelector")
if !ok {
vv, _ := maps.GetStrAnyVal[string](m, "name")
selector = str.Join(".wp-block-", str.Replaces(vv,
[]string{"core/", ""},
[]string{"/", "-"},
))
}
var features = map[string]string{}
maps.SetStrAnyVal(m, "supports.selector", selector)
color, ok := maps.GetStrAnyVal[string](m, "supports.color.__experimentalDuotone")
if ok {
maps.SetStrAnyVal(m, "duotone", color)
}
for k, v := range blockSupportFeatureLevelSelectors {
key := str.Join("supports.", k, ".__experimentalSelector")
vv, ok := maps.GetStrAnyVal[string](m, key)
if ok && vv != "" {
features[v] = scopeSelector(selector, vv)
}
}
if len(features) > 0 {
m["features"] = features
}
blockSelector := strings.Split(selector, ",")
for name, selor := range __elements {
var els []string
for _, s := range blockSelector {
if s == selor {
els = append(els, selor)
break
}
els = append(els, appendToSelector(selor, str.Join(s, " "), "left"))
}
maps.SetStrAnyVal(m, str.Join("elements.", name), strings.Join(els, ","))
}
styles, ok := maps.GetStrAnyVal[[]any](m, "styles")
if ok {
var styleSelectors = map[string]string{}
for _, ss := range styles {
s, ok := ss.(map[string]any)
if !ok {
continue
}
name, ok := maps.GetStrAnyVal[string](s, "name")
if !ok {
continue
}
styleSelectors[name] = appendToSelector(str.Join(".is-style-", name, ".is-style-", name), selector, "")
}
m["styleVariations"] = styleSelectors
}
}

View File

@ -0,0 +1,20 @@
package scriptloader
import (
"testing"
)
func Test_themeJson(t *testing.T) {
tests := []struct {
name string
}{
{
name: "t1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
initThemeJson()
})
}
}

View File

@ -0,0 +1,7 @@
package scriptloader
import "github.com/fthvgb1/wp-go/safety"
func defaultStyles(m *safety.Map[string, *Style], suffix string) {
}

View File

@ -0,0 +1,218 @@
package scriptloader
import (
"encoding/json"
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"os"
"path/filepath"
)
type _style struct {
handle string
src string
path string
size int64
}
func MaybeInlineStyles(h *wp.Handle) {
totalInlineLimit := int64(0)
var styles []_style
ss := styleQueues.Load()
for _, que := range ss.Queue {
p, ok := __styles.Load(que)
if !ok {
continue
}
f, ok := p.Extra["path"]
if !ok || f == nil {
continue
}
ff := f[0]
stat, err := os.Stat(ff)
if err != nil {
return
}
styles = append(styles, _style{
handle: que,
src: p.Src,
path: ff,
size: stat.Size(),
})
}
if len(styles) < 1 {
return
}
slice.Sort(styles, func(i, j _style) bool {
return i.size > j.size
})
totalInlineSize := int64(0)
for _, i := range styles {
if totalInlineSize+i.size > totalInlineLimit {
break
}
path := filepath.Join(i.path)
css := reload.GetAnyValMapBy("script-loader-MaybeInlineStyles", i.handle, path, func(a string) string {
css, err := os.ReadFile(i.path)
if err != nil {
logs.Error(err, "read file ", i.path)
return ""
}
return string(css)
})
s, _ := __styles.Load(i.handle)
s.Src = ""
a := s.Extra["after"]
if a == nil {
a = []string{}
}
slice.Unshift(&a, css)
s.Extra["after"] = a
}
}
func emojiDetectionScript(h *wp.Handle) {
settings := map[string]any{
"baseUrl": "https://s.w.org/images/core/emoji/14.0.0/72x72/",
"ext": ".png",
"svgUrl": "https://s.w.org/images/core/emoji/14.0.0/svg/", "svgExt": ".svg",
"source": map[string]any{
"concatemoji": "/wp-includes/js/wp-emoji-release.min.js?ver=6.2.2",
},
}
setting, _ := json.Marshal(settings)
dir := config.GetConfig().WpDir
emotion := reload.GetAnyValBys("script-loader-emoji", struct{}{}, func(_ struct{}) string {
f, err := os.ReadFile(dir)
if err != nil {
logs.Error(err, "load emoji css fail", dir)
return ""
}
return string(f)
})
s := str.Join("window._wpemojiSettings = ", string(setting), "\n", emotion)
PrintInlineScriptTag(h, s, nil)
}
func PrintInlineScriptTag(h *wp.Handle, script string, attr map[string]string) {
ss := wp.GetComponentsArgs(h, "inlineScript", "")
s := str.NewBuilder()
s.WriteString(ss)
s.WriteString("<script")
for k, v := range attr {
s.Sprintf(` %s="%s"`, k, v)
}
s.Sprintf(">%s</script>\n", script)
wp.SetComponentsArgs(h, "inlineScript", s.String())
}
func PrintInlineStyles(handle string) string {
o, _ := __styles.Load(handle)
out := o.getData("after")
if out == "" {
return ""
}
return fmt.Sprintf("<style id='%s-inline-css'%s>\n%s\n</style>\n", handle, "", out)
}
func PrintStyle(h *wp.Handle, s ...string) {
out := wp.GetComponentsArgs(h, "wp_style_out", str.NewBuilder())
out.WriteString(s...)
}
func PrintHead(h *wp.Handle, s ...string) {
out := wp.GetComponentsArgs(h, "wp_head", str.NewBuilder())
out.WriteString(s...)
}
func LinkHead(h *wp.Handle) {
PrintHead(h, "<link rel=\"https://api.w.org/\" href=\"/wp-json\" />")
if s := restGetQueriedResourceRoute(h); s != "" {
PrintHead(h, "<link rel=\"alternate\" type=\"application/json\" href=", s, " />")
}
}
func restGetQueriedResourceRoute(h *wp.Handle) string {
if cate, ok := widget.IsCategory(h); ok {
return fmt.Sprintf("/wp/v2/categories/%d", cate.Terms.TermId)
}
if tag, ok := widget.IsTag(h); ok {
return fmt.Sprintf("/wp/v2/tags/%d", tag.Terms.TermId)
}
return ""
}
func RsdLink(h *wp.Handle) {
PrintHead(h, fmt.Sprintf("<link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"%s\" />\n", "xmlrpc.php?rsd"))
}
func WlwmanifestLink(h *wp.Handle) {
PrintHead(h, fmt.Sprintf("<link rel=\"wlwmanifest\" type=\"application/wlwmanifest+xml\" href=\"%s\" />\n", "/wp-includes/wlwmanifest.xml"))
}
func LocaleStylesheet(h *wp.Handle) {
uri := reload.GetAnyValBys("printHead-localStylesheet", h, func(a *wp.Handle) string {
ur := str.Join("wp-content/themes", h.Theme(), str.Join(wpconfig.GetLang(), ".css"))
path := filepath.Join(config.GetConfig().WpDir, ur)
if helper.FileExist(path) {
return str.Join("/", ur)
}
return ""
})
if uri != "" {
PrintHead(h, fmt.Sprintf("<link rel=\"stylesheet\" href=\"%s\"%s media=\"screen\" />", uri, ""))
}
}
func TheGenerator(h *wp.Handle) {
PrintHead(h, fmt.Sprintf(`<meta name="generator" content="WordPress %s"/>`, "6.2.2"))
}
func ShortLinkWpHead(h *wp.Handle) {
if h.Scene() != constraints.Detail || h.Detail.Post.Id < 1 {
return
}
shortlink := ""
post := h.Detail.Post
if post.PostType == "page" && wpconfig.GetOption("page_on_front") == number.IntToString(post.Id) &&
wpconfig.GetOption("show_on_front") == "page" {
shortlink = "/"
} else {
shortlink = str.Join("/p/", number.IntToString(post.Id))
}
if shortlink != "" {
PrintHead(h, fmt.Sprintf(`<link rel='shortlink' href="%s" />`, shortlink))
}
}
func customLogoHeaderStyles(h *wp.Handle) {
mod := h.CommonThemeMods()
if !mod.ThemeSupport.CustomHeader.HeaderText && mod.ThemeSupport.CustomLogo.HeaderText != "" {
class := mod.ThemeSupport.CustomLogo.HeaderText
attr := ""
if !slice.IsContained(mod.ThemeSupport.HTML5, "style") {
attr = ` type="text/css"`
}
PrintHead(h, fmt.Sprintf(`<style id="custom-logo-css"%s>
.%s {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
</style>`, attr, class))
}
}
func PrintHeadToStr(h *wp.Handle) string {
h.DoActionFilter("wp_head", "", h)
return wp.GetComponentsArgs(h, "wp_head", str.NewBuilder()).String()
}

View File

@ -0,0 +1,409 @@
package scriptloader
import (
"encoding/json"
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"net/url"
"path/filepath"
"strings"
)
var __styles = reload.MapBy(func(m *safety.Map[string, *Style]) {
defaultStyles(m, ".css")
})
var __scripts = reload.MapBy[string, *Script](func(m *safety.Map[string, *Script]) {
suffix := ".min"
defaultScripts(m, suffix)
})
type Style struct {
Dependencies
}
type Script struct {
Dependencies
}
func addScript(handle string, src string, deps []string, ver string, args any) {
script := NewScript(handle, src, deps, ver, args)
__scripts.Store(handle, script)
}
func localize(handle, objectname string, l10n map[string]any) string {
if "jquery" == handle {
handle = "jquery-core"
}
after, ok := maps.GetStrAnyVal[string](l10n, "l10n_print_after")
if ok {
delete(l10n, "l10n_print_after")
}
v, _ := json.Marshal(l10n)
script := fmt.Sprintf("var %s = %s;", objectname, string(v))
if after != "" {
script = str.Join(script, "\n", after, ";")
}
return script
}
func AddStaticLocalize(handle, objectname string, l10n map[string]any) {
AddScriptData(handle, "data", localize(handle, objectname, l10n))
}
func AddDynamicLocalize(h *wp.Handle, handle, objectname string, l10n map[string]any) {
AddDynamicData(h, handle, "data", localize(handle, objectname, l10n))
}
func (d *Dependencies) getData(key string) string {
return strings.Join(d.Extra[key], "\n")
}
func GetData(h *wp.Handle, handle, key string) string {
hh, ok := __scripts.Load(handle)
if !ok {
return ""
}
d := hh.Extra[key]
d = append(d, GetDynamicData(h, handle, key))
return strings.Join(d, "\n")
}
func AddScriptData(handle, key, data string) {
var s *Script
var ok bool
s, ok = __scripts.Load(handle)
if !ok {
s = NewScript(handle, "", nil, "", nil)
}
if s.Extra == nil {
s.Extra = make(map[string][]string)
}
s.Extra[key] = append(s.Extra[key], data)
}
func AddStyleData(handle, key, data string) {
var s *Style
var ok bool
s, ok = __styles.Load(handle)
if !ok {
s = NewStyle(handle, "", nil, "", nil)
}
if s.Extra == nil {
s.Extra = make(map[string][]string)
}
s.Extra[key] = append(s.Extra[key], data)
}
func AddInlineScript(handle, data, position string) {
if handle == "" || data == "" {
return
}
if position != "after" {
position = "before"
}
AddScriptData(handle, position, data)
}
func AddInlineStyle(handle, data string) {
if handle == "" || data == "" {
return
}
AddStyleData(handle, "after", data)
}
func InlineScripts(handle, position string, display bool) string {
v, _ := __scripts.Load(handle)
ss := v.getData(position)
if ss == "" {
return ""
}
scp := strings.Trim(ss, "\n")
if display {
return fmt.Sprintf("<script id='%s-js-%s'>\n%s\n</script>\n", handle, position, scp)
}
return scp
}
func AddScript(handle string, src string, deps []string, ver string, args any) {
script := NewScript(handle, src, deps, ver, args)
__scripts.Store(handle, script)
}
const (
style = iota
script
)
var scriptQueues = reload.Vars(scriptQueue{})
var styleQueues = reload.Vars(scriptQueue{})
type scriptQueue struct {
Register map[string]struct{}
Queue []string
Args map[string]string
queuedBeforeRegister map[string]string
}
func EnqueueStyle(handle, src string, deps []string, ver, media string) {
if media == "" {
media = "all"
}
h := strings.Split(handle, "?")
if src != "" {
AddScript(h[0], src, deps, ver, media)
}
enqueue(handle, style)
}
func EnqueueStyles(handle, src string, deps []string, ver, media string) {
if src != "" {
src = GetThemeFileUri(src)
}
EnqueueStyle(handle, src, deps, ver, media)
}
func EnqueueScript(handle, src string, deps []string, ver string, inFooter bool) {
h := strings.Split(handle, "?")
if src != "" {
AddScript(h[0], src, deps, ver, nil)
}
if inFooter {
AddScriptData(h[0], "group", "1")
}
enqueue(handle, script)
}
func EnqueueScripts(handle, src string, deps []string, ver string, inFooter bool) {
if src != "" {
src = GetThemeFileUri(src)
}
EnqueueScript(handle, src, deps, ver, inFooter)
}
func enqueue(handle string, t int) {
h := strings.Split(handle, "?")
ss := styleQueues.Load()
if t == 1 {
ss = scriptQueues.Load()
}
if slice.IsContained(ss.Queue, h[0]) && maps.IsExists(ss.Register, h[0]) {
ss.Queue = append(ss.Queue, h[0])
} else if maps.IsExists(ss.Register, h[0]) {
ss.queuedBeforeRegister[h[0]] = ""
if len(h) > 1 {
ss.queuedBeforeRegister[h[0]] = h[1]
}
}
}
func GetStylesheetUri() string {
return GetThemeFileUri("/styles.css")
}
func GetThemeFileUri(file string) string {
return filepath.Join("/wp-content/themes", wpconfig.GetOption("template"), file)
}
type Dependencies struct {
Handle string `json:"handle,omitempty"`
Src string `json:"src,omitempty"`
Deps []string `json:"deps,omitempty"`
Ver string `json:"ver,omitempty"`
Args any `json:"args,omitempty"`
Extra map[string][]string `json:"extra,omitempty"`
Textdomain string `json:"textdomain,omitempty"`
TranslationsPath string `json:"translations_path,omitempty"`
}
func NewScript(handle string, src string, deps []string, ver string, args any) *Script {
return &Script{Dependencies{Handle: handle, Src: src, Deps: deps, Ver: ver, Args: args}}
}
func NewStyle(handle string, src string, deps []string, ver string, args any) *Style {
return &Style{Dependencies{Handle: handle, Src: src, Deps: deps, Ver: ver, Args: args}}
}
func AddDynamicData(h *wp.Handle, handle, key, data string) {
da := helper.GetContextVal(h.C, "__scriptDynamicData__", map[string]map[string][]string{})
m, ok := da[handle]
if !ok {
m = map[string][]string{}
}
m[key] = append(m[key], data)
da[handle] = m
}
func GetDynamicData(h *wp.Handle, handle, key string) string {
da := helper.GetContextVal(h.C, "__scriptDynamicData__", map[string]map[string][]string{})
if len(da) < 1 {
return ""
}
m, ok := da[handle]
if !ok {
return ""
}
mm, ok := m[key]
if !ok {
return ""
}
return strings.Join(mm, "\n")
}
func SetTranslation(handle, domain, path string) {
hh, ok := __scripts.Load(handle)
if !ok {
return
}
if !slice.IsContained(hh.Deps, handle) {
hh.Deps = append(hh.Deps, "wp-i18n")
}
if domain == "" {
domain = "default"
}
hh.Textdomain = domain
hh.TranslationsPath = path
}
func (item *__parseLoadItem) allDeps(handles []string, recursion bool, group int) bool {
for _, handle := range handles {
parts := strings.Split(handle, "?")
queued := slice.IsContained(item.todo, parts[0])
handle = parts[0]
moved := item.setGroup(handle, group)
if queued || !moved {
continue
}
newGroup := item.groups[handle]
keepGoing := true
h, ok := __styles.Load(handle)
if !ok {
keepGoing = false
}
if len(h.Deps) > 0 && len(slice.Diff(h.Deps, __styles.Keys())) > 0 {
keepGoing = false
}
if len(h.Deps) > 0 && item.allDeps(h.Deps, true, newGroup) {
keepGoing = false
}
if !keepGoing {
if recursion {
return false
} else {
continue
}
}
if len(parts) > 1 {
item.args[handle] = parts[1]
}
item.todo = append(item.todo, handle)
}
return true
}
type __parseLoadItem struct {
todo []string
done []string
groups map[string]int
args map[string]string
textDirection string
concat string
doConcat bool
}
func newParseLoadItem() *__parseLoadItem {
return &__parseLoadItem{
groups: map[string]int{},
}
}
func (item *__parseLoadItem) setGroup(handle string, group int) bool {
if v, ok := item.groups[handle]; ok && v <= group {
return false
}
item.groups[handle] = group
return true
}
func DoStyleItems(h *wp.Handle, handles []string, group int) []string {
item := newParseLoadItem()
item.allDeps(handles, false, 0)
for i, handle := range item.todo {
_, ok := __styles.Load(handle)
if !slice.IsContained(item.done, handle) && ok {
if DoStyleItem(h, item, handle, group) {
item.done = append(item.done, handle)
}
slice.Delete(&item.todo, i)
}
}
return item.done
}
func (s *Style) DoHeadItems() {
}
func (s *Style) DoItems(handle string) {
}
func DoStyleItem(h *wp.Handle, item *__parseLoadItem, handle string, group int) bool {
obj, _ := __styles.Load(handle)
ver := obj.Ver
if item.args[handle] != "" {
str.Join(ver, "&amp;", item.args[handle])
}
src := obj.Src
var condBefore, condAfter, conditional, _ string
if v, ok := obj.Extra["conditional"]; ok && v != nil {
conditional = v[0]
}
if conditional != "" {
condBefore = str.Join("<!==[if ", conditional, "]>\n")
condAfter = "<![endif]-->\n"
}
inlineStyle := item.PrintInline(handle)
if inlineStyle != "" {
_ = fmt.Sprintf("<style id='%s-inline-css'%s>\n%s\n</style>\n", handle, "", inlineStyle)
}
href := item.CssHref(src, ver)
ref := "stylesheet"
if v, ok := obj.Extra["alt"]; ok && v != nil {
ref = "alternate stylesheet"
}
title := ""
if v, ok := obj.Extra["title"]; ok && v != nil {
title = str.Join(" title='", v[len(v)-1], "'")
}
tag := fmt.Sprintf("<link rel='%s' id='%s-css'%s href='%s'%s media='%s' />\n", ref, handle, title, href, "", item.args)
if !item.doConcat {
PrintStyle(h, condBefore, tag, PrintInlineStyles(handle), condAfter)
}
return true
}
func (item *__parseLoadItem) CssHref(src, ver string) string {
if ver != "" {
u, _ := url.Parse(src)
v := u.Query()
v.Set("ver", ver)
u.RawQuery = v.Encode()
src = u.String()
}
return src
}
func (item *__parseLoadItem) PrintInline(handle string) string {
sty, _ := __styles.Load(handle)
out := sty.getData("after")
if out == "" {
return ""
}
s := str.NewBuilder()
s.Sprintf("<style id='%s-inline-css'%s>\n%s\n</style>\n", handle)
return s.String()
}

File diff suppressed because it is too large Load Diff

1605
app/theme/wp/scripts.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,46 +3,45 @@ package wp
import ( import (
"fmt" "fmt"
"github.com/elliotchance/phpserialize" "github.com/elliotchance/phpserialize"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
) )
var GetStickPosts = reload.BuildValFnWithConfirm("stickPostsSlice", ParseStickPosts) func (h *Handle) StickPosts() []models.Posts {
return reload.GetAnyValBys("stickPostsSlice", h, func(h *Handle) (r []models.Posts) {
func ParseStickPosts(h *Handle) (r []models.Posts, ok bool) { v := wpconfig.GetOption("sticky_posts")
v := wpconfig.GetOption("sticky_posts") if v == "" {
if v == "" { return
}
array, err := phpserialize.UnmarshalIndexedArray([]byte(v))
if err != nil {
logs.Error(err, "解析option sticky_posts错误", v)
return
}
r = slice.FilterAndMap(array, func(t any) (models.Posts, bool) {
id := str.ToInt[uint64](fmt.Sprintf("%v", t))
post, err := cache.GetPostById(h.C, id)
post.IsSticky = true
return post, err == nil
})
return return
}
array, err := phpserialize.UnmarshalIndexedArray([]byte(v))
if err != nil {
logs.Error(err, "解析option sticky_posts错误", v)
return
}
r = slice.FilterAndMap(array, func(t any) (models.Posts, bool) {
id := str.ToInt[uint64](fmt.Sprintf("%v", t))
post, err := cache.GetPostById(h.C, id)
post.IsSticky = true
return post, err == nil
}) })
ok = true
return
} }
var GetStickMapPosts = reload.BuildValFn("stickPostsMap", StickMapPosts) func (h *Handle) StickMapPosts() map[uint64]models.Posts {
return reload.GetAnyValBys("stickPostsMap", h, func(h *Handle) map[uint64]models.Posts {
func StickMapPosts(h *Handle) map[uint64]models.Posts { return slice.SimpleToMap(h.StickPosts(), func(v models.Posts) uint64 {
return slice.SimpleToMap(GetStickPosts(h), func(v models.Posts) uint64 { return v.Id
return v.Id })
}) })
} }
func (h *Handle) IsStick(id uint64) bool { func (h *Handle) IsStick(id uint64) bool {
_, ok := GetStickMapPosts(h)[id] return maps.IsExists(h.StickMapPosts(), id)
return ok
} }

View File

@ -1,6 +1,8 @@
{{define "common/head"}} {{define "common/head"}}
{{ callFuncString .calComponent "headScript"}} {{if .headScript}}
{{.headScript|unescaped}}
{{end}}
{{if .externHead}} {{if .externHead}}
{{.externHead|unescaped}} {{.externHead|unescaped}}
@ -8,7 +10,9 @@
{{end}} {{end}}
{{define "common/footer"}} {{define "common/footer"}}
{{ callFuncString .calComponent "footerScript"}} {{if .footerScript}}
{{.footerScript|unescaped}}
{{end}}
{{if .externFooter}} {{if .externFooter}}
{{.externFooter|unescaped}} {{.externFooter|unescaped}}
{{end}} {{end}}
@ -16,17 +20,7 @@
{{define "common/sidebarWidget"}} {{define "common/sidebarWidget"}}
{{ callFuncString .calComponent "sidebarsWidgets"}} {{if .sidebarsWidgets}}
{{end}} {{.sidebarsWidgets|unescaped}}
{{define "common/colophon"}}
{{if .colophon}}
{{.colophon|unescaped}}
{{else}}
<footer id="colophon" class="site-footer">
<div class="site-info">
<a href="https://github.com/fthvgb1/wp-go" class="imprint">自豪地采用 wp-go</a>
</div>
</footer>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -2,15 +2,14 @@ package wp
import ( import (
"errors" "errors"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/plugins/wphandle/apply" "github.com/fthvgb1/wp-go/app/plugins/wphandle/apply"
"github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"html/template" "html/template"
@ -20,26 +19,30 @@ import (
) )
type Handle struct { type Handle struct {
C *gin.Context Index *IndexHandle
theme string Detail *DetailHandle
isInited bool C *gin.Context
Session sessions.Session theme string
ginH gin.H Session sessions.Session
password string ginH gin.H
scene string password string
Code int scene string
Stats string Code int
templ string Stats string
themeMods wpconfig.ThemeMods templ string
err error components map[string]map[string][]Components[string]
errLevel int8 componentHook map[string][]func(Components[string]) (Components[string], bool)
abort bool themeMods wpconfig.ThemeMods
stopPipe bool handlers map[string]map[string][]HandleCall
handleHook map[string][]func(HandleCall) (HandleCall, bool)
err error
abort bool
stopPipe bool
componentsArgs map[string]any
componentFilterFn map[string][]func(*Handle, string, ...any) string
template *template.Template
} }
var handlerss = safety.NewMap[string, map[string][]HandleCall]()
var handleHooks = safety.NewMap[string, map[string][]func(HandleCall) (HandleCall, bool)]()
func (h *Handle) Theme() string { func (h *Handle) Theme() string {
return h.theme return h.theme
} }
@ -52,20 +55,28 @@ func (h *Handle) SetScene(scene string) {
h.scene = scene h.scene = scene
} }
func (h *Handle) Components() *safety.Map[string, map[string][]Components[string]] { func (h *Handle) Components() map[string]map[string][]Components[string] {
return handleComponents return h.components
} }
func (h *Handle) ComponentHook() *safety.Map[string, map[string][]func(Components[string]) (Components[string], bool)] { func (h *Handle) ComponentHook() map[string][]func(Components[string]) (Components[string], bool) {
return handleComponentHook return h.componentHook
} }
func (h *Handle) Handlers() *safety.Map[string, map[string][]HandleCall] { func (h *Handle) Handlers() map[string]map[string][]HandleCall {
return handlerss return h.handlers
} }
func (h *Handle) HandleHook() *safety.Map[string, map[string][]func(HandleCall) (HandleCall, bool)] { func (h *Handle) HandleHook() map[string][]func(HandleCall) (HandleCall, bool) {
return handleHooks return h.handleHook
}
func (h *Handle) SetTemplate(template *template.Template) {
h.template = template
}
func (h *Handle) Template() *template.Template {
return h.template
} }
type HandlePlugins map[string]HandleFn[*Handle] type HandlePlugins map[string]HandleFn[*Handle]
@ -87,50 +98,44 @@ type HandleCall struct {
Name string Name string
} }
var isFirstRequest = true func InitHandle(fn func(*Handle), h *Handle) {
var inited = false
func SetConfigHandle(a ...any) Handle { hh := reload.GetAnyValBys("themeArgAndConfig", h, func(h *Handle) Handle {
configFn := a[0].(func(*Handle)) h.components = make(map[string]map[string][]Components[string])
hh := a[1].(*Handle) h.componentsArgs = make(map[string]any)
h := &Handle{} h.componentFilterFn = make(map[string][]func(*Handle, string, ...any) string)
handleComponents.Flush() h.handlers = make(map[string]map[string][]HandleCall)
componentsArgs.Flush() h.handleHook = make(map[string][]func(HandleCall) (HandleCall, bool))
handleComponentHook.Flush() h.ginH = gin.H{}
componentFilterFns.Flush() fnMap = map[string]map[string]any{}
handlerss.Flush() fnHook = map[string]map[string]any{}
handleHooks.Flush() fn(h)
h.ginH = gin.H{} v := apply.UsePlugins()
fnMap.Flush() pluginFn, ok := v.(func(*Handle))
fnHook.Flush() if ok {
if isFirstRequest { pluginFn(h)
isFirstRequest = false }
} else { h.C.Set("inited", true)
reload.Reload() inited = true
} return *h
h.C = hh.C })
h.theme = hh.theme
configFn(h)
v := apply.GetPlugins()
pluginFn, ok := v.(func(*Handle))
if ok {
pluginFn(h)
}
return *h
}
var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, false)
func InitHandle(configFn func(*Handle), h *Handle) {
hh := GetInitHandleFn(configFn, h)
mods, err := wpconfig.GetThemeMods(h.theme)
logs.IfError(err, "获取mods失败")
h.themeMods = mods
h.ginH = maps.Copy(hh.ginH) h.ginH = maps.Copy(hh.ginH)
h.ginH["calPostClass"] = postClass(h) h.ginH["calPostClass"] = postClass(h)
h.ginH["calBodyClass"] = bodyClass(h) h.ginH["calBodyClass"] = bodyClass(h)
h.ginH["customLogo"] = customLogo(h) h.ginH["customLogo"] = customLogo(h)
h.ginH["calComponent"] = CalComponent(h) if inited {
h.isInited = true return
}
h.components = hh.components
h.Index.postsPlugin = hh.Index.postsPlugin
h.Index.pageEle = hh.Index.pageEle
h.Detail.CommentRender = hh.Detail.CommentRender
h.handlers = hh.handlers
h.handleHook = hh.handleHook
h.componentHook = hh.componentHook
h.componentsArgs = hh.componentsArgs
h.componentFilterFn = hh.componentFilterFn
h.C.Set("inited", true)
} }
func (h *Handle) Abort() { func (h *Handle) Abort() {
@ -152,25 +157,13 @@ func (h *Handle) Err() error {
return h.err return h.err
} }
func (h *Handle) SetErr(err error, level int8) { func (h *Handle) SetErr(err error) {
h.err = errors.Join(err) h.err = errors.Join(err)
h.errLevel = level
}
func (h *Handle) ErrLevel() int8 {
return h.errLevel
}
func (h *Handle) SetErrLevel(errLevel int8) {
h.errLevel = errLevel
} }
func (h *Handle) SetTempl(templ string) { func (h *Handle) SetTempl(templ string) {
h.templ = templ h.templ = templ
} }
func (h *Handle) GetTempl() string {
return h.templ
}
func (h *Handle) Scene() string { func (h *Handle) Scene() string {
return h.scene return h.scene
@ -184,12 +177,15 @@ func (h *Handle) SetData(k string, v any) {
} }
func NewHandle(c *gin.Context, scene string, theme string) *Handle { func NewHandle(c *gin.Context, scene string, theme string) *Handle {
mods, err := wpconfig.GetThemeMods(theme)
logs.IfError(err, "获取mods失败")
return &Handle{ return &Handle{
C: c, C: c,
theme: theme, theme: theme,
Session: sessions.Default(c), Session: sessions.Default(c),
scene: scene, scene: scene,
Stats: constraints.Ok, Stats: constraints.Ok,
themeMods: mods,
} }
} }
@ -234,12 +230,7 @@ func (h *Handle) RenderHtml(t *template.Template, statsCode int, name string) {
header["Content-Type"] = htmlContentType header["Content-Type"] = htmlContentType
} }
h.C.Status(statsCode) h.C.Status(statsCode)
var err error err := t.ExecuteTemplate(h.C.Writer, name, h.ginH)
if name == "" {
err = t.Execute(h.C.Writer, h.ginH)
} else {
err = t.ExecuteTemplate(h.C.Writer, name, h.ginH)
}
h.Abort() h.Abort()
if err != nil { if err != nil {
panic(err) panic(err)
@ -254,8 +245,8 @@ func (h *Handle) PushHandlers(pipeScene string, call HandleCall, statsOrScene ..
func (h *Handle) CommonComponents() { func (h *Handle) CommonComponents() {
h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss) h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss)
h.PushRender(constraints.AllStats, NewHandleFn(CalComponents, 10.001, "wp.CalComponents"))
h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate")) h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate"))
ReplyCommentJs(h)
AdditionScript(h) AdditionScript(h)
} }

View File

@ -2,11 +2,15 @@ package wpconfig
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/phphelper" "github.com/fthvgb1/wp-go/app/phphelper"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/model" "github.com/fthvgb1/wp-go/model"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"os"
"path/filepath"
"strings" "strings"
) )
@ -31,20 +35,42 @@ func InitOptions() error {
for _, option := range ops { for _, option := range ops {
options.Store(option["k"], option["v"]) options.Store(option["k"], option["v"])
} }
themeJson = reload.VarsBy(hasThemeJson)
return nil return nil
} }
var themeJson *safety.Var[bool]
func HasThemeJson() bool {
if themeJson == nil {
return false
}
return themeJson.Load()
}
func hasThemeJson() bool {
styleSheet := GetOption("stylesheet")
rootDir := config.GetConfig().WpDir
dir := filepath.Join(rootDir, "wp-content/themes", styleSheet, "theme.json")
_, err := os.Stat(dir)
if err == nil {
return true
}
template := GetOption("template")
dir = filepath.Join(rootDir, "wp-content/themes", template, "theme.json")
_, err = os.Stat(dir)
return err == nil
}
func GetOption(k string) string { func GetOption(k string) string {
v, ok := options.Load(k) v, ok := options.Load(k)
if ok { if ok {
return v return v
} }
vv, err := model.GetField[models.Options](ctx, "option_value", vv, err := model.GetField[models.Options](ctx, "option_value", model.Conditions(
model.Conditions( model.Where(
model.Where( model.SqlBuilder{{"option_name", k}}),
model.SqlBuilder{{"option_name", k}}, ),
),
),
) )
options.Store(k, vv) options.Store(k, vv)
if err != nil { if err != nil {

View File

@ -3,10 +3,10 @@ package wpconfig
import ( import (
"embed" "embed"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/phphelper" "github.com/fthvgb1/wp-go/app/phphelper"
"github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps" "github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"path/filepath" "path/filepath"
@ -73,7 +73,6 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
up := strings.Split(metadata.File, "/") up := strings.Split(metadata.File, "/")
if metadata.File != "" && Type == "full" { if metadata.File != "" && Type == "full" {
mimeType := metadata.Sizes["thumbnail"].MimeType mimeType := metadata.Sizes["thumbnail"].MimeType
metadata.Sizes = maps.Copy(metadata.Sizes)
metadata.Sizes["full"] = models.MetaDataFileSize{ metadata.Sizes["full"] = models.MetaDataFileSize{
File: filepath.Base(metadata.File), File: filepath.Base(metadata.File),
Width: metadata.Width, Width: metadata.Width,
@ -112,10 +111,10 @@ func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except .
var themeModes = func() *safety.Map[string, ThemeMods] { var themeModes = func() *safety.Map[string, ThemeMods] {
m := safety.NewMap[string, ThemeMods]() m := safety.NewMap[string, ThemeMods]()
themeModsRaw = safety.NewMap[string, map[string]any]() themeModsRaw = safety.NewMap[string, map[string]any]()
reload.Append(func() { reload.Push(func() {
m.Flush() m.Flush()
themeModsRaw.Flush() themeModsRaw.Flush()
}, "theme-modes") })
return m return m
}() }()

44
cache/cache.go vendored
View File

@ -2,49 +2,15 @@ package cache
import ( import (
"context" "context"
"sync"
"time" "time"
) )
type Cache[K comparable, V any] interface { type Cache[K comparable, V any] interface {
Get(ctx context.Context, key K) (V, bool) Get(ctx context.Context, key K) (V, bool)
Set(ctx context.Context, key K, val V) Set(ctx context.Context, key K, val V, expire time.Duration)
GetExpireTime(ctx context.Context) time.Duration Ttl(ctx context.Context, key K, expire time.Duration) time.Duration
Ttl(ctx context.Context, key K) time.Duration Ver(ctx context.Context, key K) int
Flush(ctx context.Context) Flush(ctx context.Context)
Del(ctx context.Context, key ...K) Delete(ctx context.Context, key K)
ClearExpired(ctx context.Context) ClearExpired(ctx context.Context, expire time.Duration)
}
type Expend[K comparable, V any] interface {
Gets(ctx context.Context, k []K) (map[K]V, error)
Sets(ctx context.Context, m map[K]V)
}
type SetTime interface {
SetExpiredTime(func() time.Duration)
}
type AnyCache[T any] interface {
Get(ctx context.Context) (T, bool)
Set(ctx context.Context, v T)
Flush(ctx context.Context)
GetLastSetTime(ctx context.Context) time.Time
}
type Refresh[K comparable, V any] interface {
Refresh(ctx context.Context, k K, a ...any)
}
type RefreshVar[T any] interface {
Refresh(ctx context.Context, a ...any)
}
type Lockss[K comparable] interface {
GetLock(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
}
type LockFn[K comparable] func(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
type LocksNum interface {
SetLockNum(num int)
} }

View File

@ -1,163 +0,0 @@
package cachemanager
import (
"context"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"runtime"
"sync"
"time"
)
var mutex sync.Mutex
type Fn func(context.Context)
type clearExpired interface {
ClearExpired(ctx context.Context)
}
var clears = safety.NewVar(mockmap.Map[string, Fn]{})
var flushes = safety.NewVar(mockmap.Map[string, Fn]{})
func Flush() {
ctx := context.WithValue(context.Background(), "execFlushBy", "mangerFlushFn")
for _, f := range flushes.Load() {
f.Value(ctx)
}
}
func Flushes(ctx context.Context, names ...string) {
execute(ctx, flushes, names...)
}
func execute(ctx context.Context, q *safety.Var[mockmap.Map[string, Fn]], names ...string) {
queues := q.Load()
for _, name := range names {
queue := queues.Get(name)
if queue.Value != nil {
queue.Value(ctx)
}
}
}
func parseArgs(args ...any) (string, func() time.Duration) {
var name string
var fn func() time.Duration
for _, arg := range args {
v, ok := arg.(string)
if ok {
name = v
continue
}
vv, ok := arg.(func() time.Duration)
if ok {
fn = vv
}
}
return name, fn
}
func buildLockFn[K comparable](args ...any) cache.LockFn[K] {
lockFn := helper.ParseArgs(cache.LockFn[K](nil), args...)
name := helper.ParseArgs("", args...)
num := helper.ParseArgs(runtime.NumCPU(), args...)
loFn := func() int {
return num
}
loFn = helper.ParseArgs(loFn, args...)
if name != "" {
loFn = reload.BuildFnVal(str.Join("cachesLocksNum-", name), num, loFn)
}
if lockFn == nil {
looo := helper.ParseArgs(cache.Lockss[K](nil), args...)
if looo != nil {
lockFn = looo.GetLock
loo, ok := any(looo).(cache.LocksNum)
if ok && loo != nil {
loo.SetLockNum(num)
}
} else {
lo := cache.NewLocks[K](loFn)
lockFn = lo.GetLock
PushOrSetFlush(mockmap.Item[string, Fn]{
Name: name,
Value: lo.Flush,
})
}
}
return lockFn
}
func SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expireTimeFn func() time.Duration) {
if name == "" {
return
}
fn := reload.BuildFnVal(str.Join("cacheManger-", name, "-expiredTime"), expireTime, expireTimeFn)
c.SetExpiredTime(fn)
}
func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) {
for _, s := range name {
reload.SetFnVal(s, t, coverConf)
}
}
func pushOrSet(q *safety.Var[mockmap.Map[string, Fn]], queues ...mockmap.Item[string, Fn]) {
mutex.Lock()
defer mutex.Unlock()
qu := q.Load()
for _, queue := range queues {
v := qu.Get(queue.Name)
if v.Value != nil {
qu.Set(queue.Name, queue.Value)
} else {
qu = append(qu, queue)
}
}
q.Store(qu)
}
// PushOrSetFlush will execute flush func when call Flush or Flushes
func PushOrSetFlush(queues ...mockmap.Item[string, Fn]) {
pushOrSet(flushes, queues...)
}
// PushOrSetClearExpired will execute clearExpired func when call ClearExpired or ClearExpireds
func PushOrSetClearExpired(queues ...mockmap.Item[string, Fn]) {
pushOrSet(clears, queues...)
}
func del(q *safety.Var[mockmap.Map[string, Fn]], names ...string) {
mutex.Lock()
defer mutex.Unlock()
queues := q.Load()
for _, name := range names {
queues.Del(name)
}
q.Store(queues)
}
func DelFlush(names ...string) {
del(flushes, names...)
}
func DelClearExpired(names ...string) {
del(clears, names...)
}
func ClearExpireds(ctx context.Context, names ...string) {
execute(ctx, clears, names...)
}
func ClearExpired() {
ctx := context.WithValue(context.Background(), "execClearExpired", "mangerClearExpiredFn")
for _, queue := range clears.Load() {
queue.Value(ctx)
}
}

View File

@ -1,193 +0,0 @@
package cachemanager
import (
"context"
"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/taskPools"
"reflect"
"strings"
"testing"
"time"
)
var ctx = context.Background()
func TestFlushMapVal(t *testing.T) {
_ = number.Range(1, 5, 0)
t.Run("t1", func(t *testing.T) {
count := 0
vv := NewMemoryMapCache(func(ctx2 context.Context, ks []int, a ...any) (map[int]int, error) {
r := make(map[int]int)
for _, k := range ks {
r[k] = k * k
}
count++
return r, nil
}, nil, time.Second, "test")
gets, err := GetBatchBy[int]("test", ctx, number.Range(1, 10), time.Second)
if err != nil {
t.Fatal(t, "err:", err)
}
p := taskPools.NewPools(10)
for i := 0; i < 20; i++ {
i := i
p.Execute(func() {
if i%2 == 0 {
vv.Get(ctx, 5)
} else {
vv.Set(ctx, i, i)
}
})
}
p.Wait()
fmt.Println(gets, count)
DelMapCacheVal("test", 3, 4)
fmt.Println(vv.Get(ctx, 3))
fmt.Println(vv.Get(ctx, 4))
get, err := GetBy[int]("test", ctx, 3, time.Second)
if err != nil {
t.Fatal(t, "err", err)
}
fmt.Println(get, count)
fmt.Println(vv.Get(ctx, 5))
Flushes(ctx, "test")
fmt.Println(vv.Get(ctx, 5))
fmt.Println(vv.Get(ctx, 6))
//fmt.Println(GetVarCache("test"))
})
}
func TestSetExpireTime(t *testing.T) {
t.Run("t1", func(t *testing.T) {
c := NewMemoryMapCache[string, string](func(ctx2 context.Context, strings []string, a ...any) (map[string]string, error) {
return slice.ToMap(strings, func(v string) (string, string) {
return v, str.Join(v, "__", v)
}, false), nil
}, nil, time.Second, "xx")
c.Set(ctx, "xx", "yy")
fmt.Println(c.Get(ctx, "xx"))
time.Sleep(time.Second)
fmt.Println(c.Get(ctx, "xx"))
ChangeExpireTime(3*time.Second, true, "xx")
c.Set(ctx, "xx", "yyy")
time.Sleep(time.Second)
fmt.Println(c.Get(ctx, "xx"))
time.Sleep(3 * time.Second)
fmt.Println(c.Get(ctx, "xx"))
cc, _ := GetMapCache[string, string]("xx")
fmt.Println(reflect.DeepEqual(c, cc))
cc.Set(ctx, "fff", "xxxx")
cc.Set(ctx, "ffx", "eex")
cc.Set(ctx, "ww", "vv")
m, err := cc.GetBatchToMap(ctx, []string{"fff", "ffx", "ww", "kkkk"}, time.Second)
fmt.Println(m, err)
fmt.Println(GetBatchByToMap[string]("xx", ctx, []string{"fff", "ffx", "ww", "kkkk"}, time.Second))
v := NewVarMemoryCache(func(ct context.Context, a ...any) (string, error) {
return "ssss", nil
}, 3*time.Second, "ff")
vv, _ := GetVarCache[string]("ff")
fmt.Println(reflect.DeepEqual(v, vv))
})
}
func TestSetMapCache(t *testing.T) {
t.Run("t1", func(t *testing.T) {
x := NewMemoryMapCache(nil, func(ctx2 context.Context, k string, a ...any) (string, error) {
fmt.Println("memory cache")
return strings.Repeat(k, 2), nil
}, time.Hour, "test")
fmt.Println(GetBy[string]("test", ctx, "test", time.Second))
NewMapCache[string, string](xx[string, string]{m: map[string]string{}}, nil, func(ctx2 context.Context, k string, a ...any) (string, error) {
fmt.Println("other cache drives. eg: redis,file.....")
return strings.Repeat(k, 2), nil
}, "test", time.Hour)
if err := SetMapCache("kkk", x); err != nil {
t.Errorf("SetMapCache() error = %v, wantErr %v", err, nil)
}
fmt.Println(GetBy[string]("test", ctx, "test", time.Second))
})
}
type xx[K comparable, V any] struct {
m map[K]V
}
func (x xx[K, V]) Get(ctx context.Context, key K) (V, bool) {
v, ok := x.m[key]
return v, ok
}
func (x xx[K, V]) Set(ctx context.Context, key K, val V) {
x.m[key] = val
}
func (x xx[K, V]) GetExpireTime(ctx context.Context) time.Duration {
//TODO implement me
panic("implement me")
}
func (x xx[K, V]) Ttl(ctx context.Context, key K) time.Duration {
//TODO implement me
panic("implement me")
}
func (x xx[K, V]) Flush(ctx context.Context) {
//TODO implement me
panic("implement me")
}
func (x xx[K, V]) Del(ctx context.Context, key ...K) {
//TODO implement me
panic("implement me")
}
func (x xx[K, V]) ClearExpired(ctx context.Context) {
//TODO implement me
panic("implement me")
}
func TestSetVarCache(t *testing.T) {
t.Run("t1", func(t *testing.T) {
bak := NewVarMemoryCache(func(ctx2 context.Context, a ...any) (string, error) {
fmt.Println("memory cache")
return "xxx", nil
}, time.Hour, "test")
fmt.Println(GetVarVal[string]("test", ctx, time.Second))
NewVarCache[string](oo[string]{}, func(ctx2 context.Context, a ...any) (string, error) {
fmt.Println("other cache drives. eg: redis,file.....")
return "ooo", nil
}, "test")
if err := SetVarCache("xx", bak); err != nil {
t.Errorf("SetVarCache() error = %v, wantErr %v", err, nil)
}
fmt.Println(GetVarVal[string]("test", ctx, time.Second))
})
}
type oo[T any] struct {
val T
}
func (o oo[T]) Get(ctx context.Context) (T, bool) {
return o.val, false
}
func (o oo[T]) Set(ctx context.Context, v T) {
o.val = v
}
func (o oo[T]) Flush(ctx context.Context) {
//TODO implement me
panic("implement me")
}
func (o oo[T]) GetLastSetTime(ctx context.Context) time.Time {
//TODO implement me
panic("implement me")
}

View File

@ -1,148 +0,0 @@
package cachemanager
import (
"context"
"errors"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"time"
)
var mapDelFuncs = safety.NewMap[string, func(any)]()
var mapCache = safety.NewMap[string, any]()
func SetMapCache[K comparable, V any](name string, ca *cache.MapCache[K, V]) error {
v, ok := mapCache.Load(name)
if !ok {
mapCache.Store(name, ca)
return nil
}
_, ok = v.(*cache.MapCache[K, V])
if !ok {
return errors.New(str.Join("cache ", name, " type err"))
}
mapCache.Store(name, ca)
return nil
}
// PushMangerMap will del mapCache val with name When call DelMapCacheVal
func PushMangerMap[K comparable, V any](name string, m *cache.MapCache[K, V]) {
if name == "" {
return
}
mapCache.Store(name, m)
mapDelFuncs.Store(name, func(a any) {
k, ok := a.([]K)
if ok && len(k) > 0 {
mm, ok := mapCache.Load(name)
if !ok {
return
}
c, ok := mm.(*cache.MapCache[K, V])
if !ok {
return
}
ctx := context.WithValue(context.Background(), "ctx", "registerFlush")
c.Del(ctx, k...)
}
})
}
func GetBy[T any, K comparable](name string, ct context.Context, key K, timeout time.Duration, params ...any) (r T, err error) {
ct = context.WithValue(ct, "getCache", name)
ca, err := getMap[K, T](name)
if err != nil {
return r, err
}
vv, err := ca.GetCache(ct, key, timeout, params...)
if err != nil {
return r, err
}
r = vv
return
}
func getMap[K comparable, T any](name string) (*cache.MapCache[K, T], error) {
m, ok := mapCache.Load(name)
if !ok {
return nil, errors.New(str.Join("cache ", name, " doesn't exist"))
}
vk, ok := m.(*cache.MapCache[K, T])
if !ok {
return nil, errors.New(str.Join("cache ", name, " type error"))
}
return vk, nil
}
func GetBatchBy[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r []T, err error) {
ct = context.WithValue(ct, "getCache", name)
ca, err := getMap[K, T](name)
if err != nil {
return r, err
}
vv, err := ca.GetCacheBatch(ct, key, timeout, params...)
if err != nil {
return r, err
}
r = vv
return
}
func GetBatchByToMap[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r map[K]T, err error) {
ct = context.WithValue(ct, "getCache", name)
ca, err := getMap[K, T](name)
if err != nil {
return r, err
}
vv, err := ca.GetBatchToMap(ct, key, timeout, params...)
if err != nil {
return r, err
}
r = vv
return
}
func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] {
inc := helper.ParseArgs((*cache.IncreaseUpdate[K, V])(nil), args...)
m := cache.NewMapCache[K, V](data, fn, batchFn, inc, buildLockFn[K](args...), args...)
name, f := parseArgs(args...)
if name != "" {
PushMangerMap(name, m)
}
PushOrSetFlush(mockmap.Item[string, Fn]{
Name: name,
Value: m.Flush,
})
PushOrSetClearExpired(mockmap.Item[string, Fn]{
Name: name,
Value: m.ClearExpired,
})
if f != nil && name != "" {
SetExpireTime(any(data).(cache.SetTime), name, 0, f)
}
return m
}
func NewMemoryMapCache[K comparable, V any](batchFn cache.MapBatchFn[K, V],
fn cache.MapSingleFn[K, V], expireTime time.Duration, args ...any) *cache.MapCache[K, V] {
c := NewMapCache[K, V](cache.NewMemoryMapCache[K, V](func() time.Duration {
return expireTime
}), batchFn, fn, args...)
return c
}
func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) {
vv, err := getMap[K, V](name)
return vv, err == nil
}
func DelMapCacheVal[T any](name string, keys ...T) {
v, ok := mapDelFuncs.Load(name)
if !ok || len(keys) < 1 {
return
}
v(keys)
}

View File

@ -1,57 +0,0 @@
package cachemanager
import (
"context"
"errors"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings"
"time"
)
func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.PaginationData[V]], maxNum int,
dbFn cache.DbFn[K, V], localFn cache.LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, fetchNum int, name string, a ...any) *cache.Pagination[K, V] {
fn := helper.ParseArgs([]func() int(nil), a...)
var ma, fet func() int
if len(fn) > 0 {
ma = fn[0]
if len(fn) > 1 {
fet = fn[1]
}
}
if ma == nil {
ma = reload.BuildFnVal(str.Join("paginationCache-", name, "-maxNum"), maxNum, nil)
}
if fet == nil {
fet = reload.BuildFnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil)
}
p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name)
mapCache.Store(name, p)
return p
}
func GetPaginationCache[K comparable, V any](name string) (*cache.Pagination[K, V], bool) {
v, err := getPagination[K, V](name)
return v, err == nil
}
func Pagination[V any, K comparable](name string, ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) {
v, err := getPagination[K, V](name)
if err != nil {
return nil, 0, err
}
return v.Pagination(ctx, timeout, k, page, limit, a...)
}
func getPagination[K comparable, T any](name string) (*cache.Pagination[K, T], error) {
m, ok := mapCache.Load(name)
if !ok {
return nil, errors.New(str.Join("cache ", name, " doesn't exist"))
}
vk, ok := m.(*cache.Pagination[K, T])
if !ok {
return nil, errors.New(str.Join("cache ", name, " type error"))
}
return vk, nil
}

View File

@ -1,83 +0,0 @@
package cachemanager
import (
"context"
"errors"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice/mockmap"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"time"
)
var varCache = safety.NewMap[string, any]()
func SetVarCache[T any](name string, v *cache.VarCache[T]) error {
vv, ok := varCache.Load(name)
if !ok {
varCache.Store(name, v)
return nil
}
_, ok = vv.(*cache.VarCache[T])
if ok {
varCache.Store(name, v)
return nil
}
return errors.New(str.Join("cache ", name, " type err"))
}
func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T, error), a ...any) *cache.VarCache[T] {
inc := helper.ParseArgs((*cache.IncreaseUpdateVar[T])(nil), a...)
ref := helper.ParseArgs(cache.RefreshVar[T](nil), a...)
v := cache.NewVarCache(c, fn, inc, ref, a...)
name, _ := parseArgs(a...)
if name != "" {
varCache.Store(name, v)
}
PushOrSetFlush(mockmap.Item[string, Fn]{
Name: name,
Value: v.Flush,
})
cc, ok := any(c).(clearExpired)
if ok {
PushOrSetClearExpired(mockmap.Item[string, Fn]{
Name: name,
Value: cc.ClearExpired,
})
}
return v
}
func GetVarVal[T any](name string, ctx context.Context, duration time.Duration, a ...any) (r T, err error) {
ctx = context.WithValue(ctx, "getCache", name)
ca, ok := GetVarCache[T](name)
if !ok {
err = errors.New(str.Join("cache ", name, " is not exist"))
return
}
v, err := ca.GetCache(ctx, duration, a...)
if err != nil {
return
}
r = v
return
}
func NewVarMemoryCache[T any](fn func(context.Context, ...any) (T, error), expired time.Duration, a ...any) *cache.VarCache[T] {
c := cache.NewVarMemoryCache[T](nil)
name, e := parseArgs(a...)
SetExpireTime(c, name, expired, e)
v := NewVarCache[T](c, fn, a...)
return v
}
func GetVarCache[T any](name string) (*cache.VarCache[T], bool) {
v, ok := varCache.Load(name)
if !ok {
return nil, false
}
vv, ok := v.(*cache.VarCache[T])
return vv, ok
}

78
cache/locks.go vendored
View File

@ -1,78 +0,0 @@
package cache
import (
"context"
"github.com/fthvgb1/wp-go/safety"
"sync"
"sync/atomic"
)
type Locks[K comparable] struct {
numFn func() int
locks []*sync.Mutex
m *safety.Map[K, *sync.Mutex]
counter *int64
}
func (l *Locks[K]) Flush(_ context.Context) {
l.m.Flush()
atomic.StoreInt64(l.counter, 0)
}
func (l *Locks[K]) SetNumFn(numFn func() int) {
l.numFn = numFn
}
func NewLocks[K comparable](num func() int) *Locks[K] {
var i int64
return &Locks[K]{numFn: num, m: safety.NewMap[K, *sync.Mutex](), counter: &i}
}
func (l *Locks[K]) SetLockNum(num int) {
if num > 0 {
l.locks = make([]*sync.Mutex, num)
for i := 0; i < num; i++ {
l.locks[i] = &sync.Mutex{}
}
}
}
func (l *Locks[K]) GetLock(ctx context.Context, gMut *sync.Mutex, keys ...K) *sync.Mutex {
k := keys[0]
lo, ok := l.m.Load(k)
if ok {
return lo
}
num := l.numFn()
if num == 1 {
return gMut
}
gMut.Lock()
defer gMut.Unlock()
lo, ok = l.m.Load(k)
if ok {
return lo
}
if num <= 0 {
lo = &sync.Mutex{}
l.m.Store(k, lo)
return lo
}
if len(l.locks) == 0 {
l.SetLockNum(num)
}
counter := int(atomic.LoadInt64(l.counter))
if counter > len(l.locks)-1 {
atomic.StoreInt64(l.counter, 0)
counter = 0
}
lo = l.locks[counter]
l.m.Store(k, lo)
atomic.AddInt64(l.counter, 1)
if len(l.locks) < num {
for i := 0; i < num-len(l.locks); i++ {
l.locks = append(l.locks, &sync.Mutex{})
}
}
return lo
}

614
cache/map.go vendored
View File

@ -4,200 +4,54 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
"sync" "sync"
"time" "time"
) )
type MapCache[K comparable, V any] struct { type MapCache[K comparable, V any] struct {
Cache[K, V] data Cache[K, V]
mux *sync.Mutex mux sync.Mutex
muFn func(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex cacheFunc func(...any) (V, error)
cacheFunc MapSingleFn[K, V] batchCacheFn func(...any) (map[K]V, error)
batchCacheFn MapBatchFn[K, V] expireTime time.Duration
getCacheBatch func(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error)
getCacheBatchToMap func(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error)
increaseUpdate *IncreaseUpdate[K, V]
refresh Refresh[K, V]
gets func(ctx context.Context, key K) (V, bool)
sets func(ctx context.Context, key K, val V)
getExpireTimes func(ctx context.Context) time.Duration
ttl func(ctx context.Context, key K) time.Duration
flush func(ctx context.Context)
del func(ctx context.Context, key ...K)
clearExpired func(ctx context.Context)
} }
func (m *MapCache[K, V]) Get(ctx context.Context, key K) (V, bool) { func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
return m.gets(ctx, key)
}
func (m *MapCache[K, V]) Set(ctx context.Context, key K, val V) {
m.sets(ctx, key, val)
}
func (m *MapCache[K, V]) Ttl(ctx context.Context, key K) time.Duration {
return m.ttl(ctx, key)
}
func (m *MapCache[K, V]) GetExpireTime(ctx context.Context) time.Duration {
return m.getExpireTimes(ctx)
}
func (m *MapCache[K, V]) Del(ctx context.Context, key ...K) {
m.del(ctx, key...)
}
func (m *MapCache[K, V]) ClearExpired(ctx context.Context) {
m.clearExpired(ctx)
}
type IncreaseUpdate[K comparable, V any] struct {
CycleTime func() time.Duration
Fn IncreaseFn[K, V]
}
func NewIncreaseUpdate[K comparable, V any](name string, fn IncreaseFn[K, V], cycleTime time.Duration, tFn func() time.Duration) *IncreaseUpdate[K, V] {
tFn = reload.BuildFnVal(name, cycleTime, tFn)
return &IncreaseUpdate[K, V]{CycleTime: tFn, Fn: fn}
}
type MapSingleFn[K, V any] func(context.Context, K, ...any) (V, error)
type MapBatchFn[K comparable, V any] func(context.Context, []K, ...any) (map[K]V, error)
type IncreaseFn[K comparable, V any] func(c context.Context, currentData V, k K, t time.Time, a ...any) (data V, save bool, refresh bool, err error)
func NewMapCache[K comparable, V any](ca Cache[K, V], cacheFunc MapSingleFn[K, V], batchCacheFn MapBatchFn[K, V], inc *IncreaseUpdate[K, V], lockFn LockFn[K], a ...any) *MapCache[K, V] {
r := &MapCache[K, V]{
Cache: ca,
mux: &sync.Mutex{},
cacheFunc: cacheFunc,
batchCacheFn: batchCacheFn,
increaseUpdate: inc,
muFn: lockFn,
}
if cacheFunc == nil && batchCacheFn != nil {
r.setDefaultCacheFn(batchCacheFn)
} else if batchCacheFn == nil && cacheFunc != nil {
r.SetDefaultBatchFunc(cacheFunc)
}
ex, ok := any(ca).(Expend[K, V])
if !ok {
r.getCacheBatch = r.getCacheBatchs
r.getCacheBatchToMap = r.getBatchToMapes
} else {
r.getCacheBatch = r.getBatches(ex)
r.getCacheBatchToMap = r.getBatchToMap(ex)
}
re, ok := any(ca).(Refresh[K, V])
if ok {
r.refresh = re
}
initCache(r, a...)
return r
}
func initCache[K comparable, V any](r *MapCache[K, V], a ...any) {
gets := helper.ParseArgs[func(Cache[K, V], context.Context, K) (V, bool)](nil, a...)
if gets == nil {
r.gets = r.Cache.Get
} else {
r.gets = func(ctx context.Context, key K) (V, bool) {
return gets(r.Cache, ctx, key)
}
}
sets := helper.ParseArgs[func(Cache[K, V], context.Context, K, V)](nil, a...)
if sets == nil {
r.sets = r.Cache.Set
} else {
r.sets = func(ctx context.Context, key K, val V) {
sets(r.Cache, ctx, key, val)
}
}
getExpireTimes := helper.ParseArgs[func(Cache[K, V], context.Context) time.Duration](nil, a...)
if getExpireTimes == nil {
r.getExpireTimes = r.Cache.GetExpireTime
} else {
r.getExpireTimes = func(ctx context.Context) time.Duration {
return getExpireTimes(r.Cache, ctx)
}
}
ttl := helper.ParseArgs[func(Cache[K, V], context.Context, K) time.Duration](nil, a...)
if ttl == nil {
r.ttl = r.Cache.Ttl
} else {
r.ttl = func(ctx context.Context, k K) time.Duration {
return ttl(r.Cache, ctx, k)
}
}
del := helper.ParseArgs[func(Cache[K, V], context.Context, ...K)](nil, a...)
if del == nil {
r.del = r.Cache.Del
} else {
r.del = func(ctx context.Context, key ...K) {
del(r.Cache, ctx, key...)
}
}
flushAndClearExpired := helper.ParseArgs[[]func(Cache[K, V], context.Context)](nil, a...)
if flushAndClearExpired == nil {
r.flush = r.Cache.Flush
r.clearExpired = r.Cache.ClearExpired
} else {
r.flush = func(ctx context.Context) {
flushAndClearExpired[0](r.Cache, ctx)
}
if len(flushAndClearExpired) > 1 {
r.clearExpired = func(ctx context.Context) {
flushAndClearExpired[1](r.Cache, ctx)
}
} else {
r.clearExpired = r.Cache.ClearExpired
}
}
}
func (m *MapCache[K, V]) SetDefaultBatchFunc(fn MapSingleFn[K, V]) {
m.batchCacheFn = func(ctx context.Context, ids []K, a ...any) (map[K]V, error) {
var err error
rr := make(map[K]V)
for _, id := range ids {
v, er := fn(ctx, id, a...)
if er != nil {
err = errors.Join(er)
continue
}
rr[id] = v
}
return rr, err
}
}
func (m *MapCache[K, V]) SetCacheFunc(fn MapSingleFn[K, V]) {
m.cacheFunc = fn m.cacheFunc = fn
} }
func (m *MapCache[K, V]) Ttl(ctx context.Context, k K) time.Duration {
return m.data.Ttl(ctx, k, m.expireTime)
}
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) { func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
tt := m.Ttl(ctx, k) tt := m.data.Ttl(ctx, k, m.expireTime)
if tt <= 0 { if tt <= 0 {
return return
} }
return time.Now().Add(m.Ttl(ctx, k)).Add(-m.GetExpireTime(ctx)) return time.Now().Add(m.data.Ttl(ctx, k, m.expireTime)).Add(-m.expireTime)
} }
func (m *MapCache[K, V]) SetCacheBatchFn(fn MapBatchFn[K, V]) { func (m *MapCache[K, V]) SetCacheBatchFn(fn func(...any) (map[K]V, error)) {
m.batchCacheFn = fn m.batchCacheFn = fn
if m.cacheFunc == nil { if m.cacheFunc == nil {
m.setDefaultCacheFn(fn) m.setCacheFn(fn)
} }
} }
func (m *MapCache[K, V]) setDefaultCacheFn(fn MapBatchFn[K, V]) { func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
m.cacheFunc = func(ctx context.Context, k K, a ...any) (V, error) { m.cacheFunc = func(a ...any) (V, error) {
var err error var err error
var r map[K]V var r map[K]V
r, err = fn(ctx, []K{k}, a...) var k K
ctx, ok := a[0].(context.Context)
if ok {
k, ok = a[1].(K)
if ok {
r, err = fn(ctx, []K{k})
}
}
if err != nil { if err != nil {
var rr V var rr V
@ -207,359 +61,114 @@ func (m *MapCache[K, V]) setDefaultCacheFn(fn MapBatchFn[K, V]) {
} }
} }
func NewMapCacheByFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
return &MapCache[K, V]{
mux: sync.Mutex{},
cacheFunc: fn,
expireTime: expireTime,
data: cacheType,
}
}
func NewMapCacheByBatchFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
r := &MapCache[K, V]{
mux: sync.Mutex{},
batchCacheFn: fn,
expireTime: expireTime,
data: cacheType,
}
r.setCacheFn(fn)
return r
}
func (m *MapCache[K, V]) Flush(ctx context.Context) { func (m *MapCache[K, V]) Flush(ctx context.Context) {
m.mux.Lock() m.mux.Lock()
defer m.mux.Unlock() defer m.mux.Unlock()
m.flush(ctx) m.data.Flush(ctx)
} }
func (m *MapCache[K, V]) increaseUpdates(c context.Context, timeout time.Duration, data V, key K, params ...any) (V, error) { func (m *MapCache[K, V]) Get(ctx context.Context, k K) (V, bool) {
var err error return m.data.Get(ctx, k)
nowTime := time.Now() }
if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() {
return data, err func (m *MapCache[K, V]) Set(ctx context.Context, k K, v V) {
} m.data.Set(ctx, k, v, m.expireTime)
fn := func() {
l := m.muFn(c, m.mux, key)
l.Lock()
defer l.Unlock()
if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() {
return
}
dat, save, refresh, er := m.increaseUpdate.Fn(c, data, key, m.GetLastSetTime(c, key), params...)
if er != nil {
err = er
return
}
if refresh {
m.refresh.Refresh(c, key, params...)
}
if save {
m.Set(c, key, dat)
data = dat
}
}
if timeout > 0 {
er := helper.RunFnWithTimeout(c, timeout, fn)
if err == nil && er != nil {
return data, fmt.Errorf("increateUpdate cache %v err:[%s]", key, er)
}
} else {
fn()
}
return data, err
} }
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
data, ok := m.Get(c, key) data, ok := m.data.Get(c, key)
var err error var err error
if ok { if !ok || m.data.Ttl(c, key, m.expireTime) <= 0 {
if m.increaseUpdate == nil || m.refresh == nil { ver := m.data.Ver(c, key)
return data, err call := func() {
m.mux.Lock()
defer m.mux.Unlock()
if m.data.Ver(c, key) > ver {
data, _ = m.data.Get(c, key)
return
}
data, err = m.cacheFunc(params...)
if err != nil {
return
}
m.Set(c, key, data)
} }
return m.increaseUpdates(c, timeout, data, key, params...) if timeout > 0 {
} ctx, cancel := context.WithTimeout(c, timeout)
call := func() { defer cancel()
l := m.muFn(c, m.mux, key) done := make(chan struct{}, 1)
l.Lock() go func() {
defer l.Unlock() call()
if data, ok = m.Get(c, key); ok { done <- struct{}{}
return }()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
case <-done:
}
} else {
call()
} }
data, err = m.cacheFunc(c, key, params...)
if err != nil {
return
}
m.Set(c, key, data)
}
if timeout > 0 {
er := helper.RunFnWithTimeout(c, timeout, call, fmt.Sprintf("get cache %v ", key))
if err == nil && er != nil {
err = er
}
} else {
call()
} }
return data, err return data, err
} }
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) { func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
return m.getCacheBatch(c, key, timeout, params...) var res []V
} ver := 0
needFlush := slice.FilterAndMap(key, func(k K) (r K, ok bool) {
func (m *MapCache[K, V]) GetBatchToMap(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) { if _, ok := m.data.Get(c, k); !ok {
return m.getCacheBatchToMap(c, key, timeout, params...) return k, true
}
func (m *MapCache[K, V]) getBatchToMap(e Expend[K, V]) func(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
return func(ctx context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
var res map[K]V
var err error
mm, err := e.Gets(ctx, key)
if err != nil || len(key) == len(mm) {
return mm, err
}
var needIndex = make(map[K]int)
res = mm
var flushKeys []K
for i, k := range key {
_, ok := mm[k]
if !ok {
flushKeys = append(flushKeys, k)
needIndex[k] = i
}
} }
ver += m.data.Ver(c, k)
return
})
var err error
if len(needFlush) > 0 {
call := func() { call := func() {
m.mux.Lock() m.mux.Lock()
defer m.mux.Unlock() defer m.mux.Unlock()
mmm, er := e.Gets(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, true vers := slice.Reduce(needFlush, func(t K, r int) int {
})) return r + m.data.Ver(c, t)
if er != nil { }, 0)
if vers > ver {
return
}
r, er := m.batchCacheFn(params...)
if err != nil {
err = er err = er
return return
} }
for k, v := range mmm { for k, v := range r {
res[k] = v
delete(needIndex, k)
}
if len(needIndex) < 1 {
return
}
r, er := m.batchCacheFn(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, true
}), params...)
if er != nil {
err = er
return
}
e.Sets(ctx, r)
for k := range needIndex {
v, ok := r[k]
if ok {
res[k] = v
}
}
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
done := make(chan struct{}, 1)
go func() {
call()
done <- struct{}{}
}()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done:
}
} else {
call()
}
return res, err
}
}
func (m *MapCache[K, V]) getBatchToMapes(c context.Context, key []K, timeout time.Duration, params ...any) (r map[K]V, err error) {
r = make(map[K]V)
var needIndex = make(map[K]int)
for i, k := range key {
v, ok := m.Get(c, k)
if !ok {
needIndex[k] = i
} else {
r[k] = v
}
}
if len(needIndex) < 1 {
return
}
call := func() {
l := m.muFn(c, m.mux, key...)
l.Lock()
defer l.Unlock()
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
vv, ok := m.Get(c, k)
if ok {
r[k] = vv
delete(needIndex, k)
return k, false
}
return k, true
})
if len(needFlushs) < 1 {
return
}
rr, er := m.batchCacheFn(c, needFlushs, params...)
if er != nil {
err = er
return
}
for k := range needIndex {
v, ok := rr[k]
if ok {
r[k] = v
}
m.Set(c, k, v)
}
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
done := make(chan struct{}, 1)
go func() {
call()
done <- struct{}{}
}()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done:
}
} else {
call()
}
return
}
func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
var res = make([]V, 0, len(key))
var needIndex = make(map[K]int)
for i, k := range key {
v, ok := m.Get(c, k)
if !ok {
needIndex[k] = i
}
res = append(res, v)
}
if len(needIndex) < 1 {
return res, nil
}
var err error
call := func() {
l := m.muFn(c, m.mux, key...)
l.Lock()
defer l.Unlock()
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
vv, ok := m.Get(c, k)
if ok {
res[needIndex[k]] = vv
delete(needIndex, k)
return k, false
}
return k, true
})
if len(needFlushs) < 1 {
return
}
r, er := m.batchCacheFn(c, needFlushs, params...)
if er != nil {
err = er
return
}
for k, i := range needIndex {
v, ok := r[k]
if ok {
res[i] = v
m.Set(c, k, v) m.Set(c, k, v)
} }
} }
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
done := make(chan struct{}, 1)
go func() {
call()
done <- struct{}{}
}()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done:
}
} else {
call()
}
return res, err
}
func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
cc := e
return func(ctx context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
var res = make([]V, 0, len(key))
var needIndex = make(map[K]int)
var err error
mm, err := cc.Gets(ctx, key)
if err != nil {
return nil, err
}
var flushKeys []K
for i, k := range key {
v, ok := mm[k]
if !ok {
flushKeys = append(flushKeys, k)
needIndex[k] = i
var vv V
v = vv
}
res = append(res, v)
}
if len(needIndex) < 1 {
return res, nil
}
call := func() {
m.mux.Lock()
defer m.mux.Unlock()
mmm, er := cc.Gets(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, true
}))
if er != nil {
err = er
return
}
for k, v := range mmm {
res[needIndex[k]] = v
delete(needIndex, k)
}
if len(needIndex) < 1 {
return
}
r, er := m.batchCacheFn(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, true
}), params...)
if er != nil {
err = er
return
}
cc.Sets(ctx, r)
for k, i := range needIndex {
v, ok := r[k]
if ok {
res[i] = v
}
}
}
if timeout > 0 { if timeout > 0 {
ctx, cancel := context.WithTimeout(ctx, timeout) ctx, cancel := context.WithTimeout(c, timeout)
defer cancel() defer cancel()
done := make(chan struct{}, 1) done := make(chan struct{}, 1)
go func() { go func() {
@ -569,13 +178,20 @@ func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, ke
select { select {
case <-ctx.Done(): case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error())) err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done: case <-done:
} }
} else { } else {
call() call()
} }
return res, err
} }
res = slice.FilterAndMap(key, func(k K) (V, bool) {
return m.data.Get(c, k)
})
return res, err
}
func (m *MapCache[K, V]) ClearExpired(ctx context.Context) {
m.mux.Lock()
defer m.mux.Unlock()
m.data.ClearExpired(ctx, m.expireTime)
} }

39
cache/map_test.go vendored
View File

@ -12,22 +12,27 @@ import (
) )
var ca MapCache[string, string] var ca MapCache[string, string]
var fn MapSingleFn[string, string] var fn func(a ...any) (string, error)
var batchFn MapBatchFn[string, string] var batchFn func(a ...any) (map[string]string, error)
var ct context.Context var ct context.Context
func init() { func init() {
fn = func(ctx context.Context, aa string, a ...any) (string, error) { fn = func(a ...any) (string, error) {
aa := a[1].(string)
return strings.Repeat(aa, 2), nil return strings.Repeat(aa, 2), nil
} }
ct = context.Background() ct = context.Background()
batchFn = func(ctx context.Context, arr []string, a ...any) (map[string]string, error) { batchFn = func(a ...any) (map[string]string, error) {
fmt.Println(a) fmt.Println(a)
return slice.FilterAndToMap(arr, func(t string, _ int) (string, string, bool) { arr := a[1].([]string)
return slice.FilterAndToMap(arr, func(t string) (string, string, bool) {
return t, strings.Repeat(t, 2), true return t, strings.Repeat(t, 2), true
}), nil }), nil
} }
ca = *NewMemoryMapCacheByFn[string, string](fn, time.Second*2)
ca.SetCacheBatchFn(batchFn)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
_, _ = ca.GetCache(ct, "bb", time.Second, ct, "bb")
} }
func TestMapCache_ClearExpired(t *testing.T) { func TestMapCache_ClearExpired(t *testing.T) {
type args struct { type args struct {
@ -67,9 +72,7 @@ func TestMapCache_Flush(t *testing.T) {
m MapCache[K, V] m MapCache[K, V]
args args args args
} }
ca := *NewMapCache[string, string](NewMemoryMapCache[string, string](func() time.Duration { ca := *NewMemoryMapCacheByFn[string, string](fn, time.Second)
return time.Second
}), fn, nil, nil, nil)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa") _, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
tests := []testCase[string, string]{ tests := []testCase[string, string]{
{ {
@ -290,7 +293,7 @@ func TestMapCache_Set(t *testing.T) {
func TestMapCache_SetCacheBatchFn(t *testing.T) { func TestMapCache_SetCacheBatchFn(t *testing.T) {
type args[K comparable, V any] struct { type args[K comparable, V any] struct {
fn MapBatchFn[K, V] fn func(...any) (map[K]V, error)
} }
type testCase[K comparable, V any] struct { type testCase[K comparable, V any] struct {
name string name string
@ -312,19 +315,19 @@ func TestMapCache_SetCacheBatchFn(t *testing.T) {
} }
func TestMapCache_SetCacheFunc(t *testing.T) { func TestMapCache_SetCacheFunc(t *testing.T) {
type args[K comparable, V any] struct { type args[V any] struct {
fn MapSingleFn[K, V] fn func(...any) (V, error)
} }
type testCase[K comparable, V any] struct { type testCase[K comparable, V any] struct {
name string name string
m MapCache[K, V] m MapCache[K, V]
args args[K, V] args args[V]
} }
tests := []testCase[string, string]{ tests := []testCase[string, string]{
{ {
name: "t1", name: "t1",
m: ca, m: ca,
args: args[string, string]{fn: fn}, args: args[string]{fn: fn},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -352,12 +355,12 @@ func TestMapCache_Ttl(t *testing.T) {
name: "t1", name: "t1",
m: ca, m: ca,
args: args[string]{ct, "aa"}, args: args[string]{ct, "aa"},
want: ca.GetExpireTime(ct) - tx.Sub(txx), want: ca.expireTime - tx.Sub(txx),
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
fmt.Printf("过期时间=%v \nttl=%v \n当前时间 =%v\n最后设置时间=%v\n当时时间-最后设置时间=%v ", ca.GetExpireTime(ct), ca.Ttl(ct, "aa"), tx, txx, tx.Sub(txx)) fmt.Printf("过期时间=%v \nttl=%v \n当前时间 =%v\n最后设置时间=%v\n当时时间-最后设置时间=%v ", ca.expireTime, ca.Ttl(ct, "aa"), tx, txx, tx.Sub(txx))
if got := tt.m.Ttl(tt.args.ct, tt.args.k); got != tt.want { if got := tt.m.Ttl(tt.args.ct, tt.args.k); got != tt.want {
t.Errorf("Ttl() = %v, want %v", got, tt.want) t.Errorf("Ttl() = %v, want %v", got, tt.want)
} }
@ -367,7 +370,7 @@ func TestMapCache_Ttl(t *testing.T) {
func TestMapCache_setCacheFn(t *testing.T) { func TestMapCache_setCacheFn(t *testing.T) {
type args[K comparable, V any] struct { type args[K comparable, V any] struct {
fn MapBatchFn[K, V] fn func(...any) (map[K]V, error)
} }
type testCase[K comparable, V any] struct { type testCase[K comparable, V any] struct {
name string name string
@ -384,7 +387,7 @@ func TestMapCache_setCacheFn(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ca.cacheFunc = nil ca.cacheFunc = nil
tt.m.setDefaultCacheFn(tt.args.fn) tt.m.setCacheFn(tt.args.fn)
fmt.Println(ca.GetCache(ct, "xx", time.Second, ct, "xx")) fmt.Println(ca.GetCache(ct, "xx", time.Second, ct, "xx"))
}) })
} }

Some files were not shown because too many files have changed in this diff Show More