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

1
.gitignore vendored
View File

@ -4,4 +4,3 @@
err.log
/plugins/
/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
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
WORKDIR /opt/wp-go

View File

@ -20,11 +20,6 @@
- kill -SIGUSR1 PID 更新配置和清空缓存
- kill -SIGUSR2 PID 清空缓存
#### 运行
```
go run app/cmd/main.go [-c configpath] [-p port]
```
#### 数据显示支持程度
| 页表 | 支持程度 |
@ -53,9 +48,7 @@ go run app/cmd/main.go [-c configpath] [-p port]
- 博客页面至多显示数量
- Feed中显示最近数量
- 讨论
- 其他评论设置
- `启用|禁止`评论嵌套,最多嵌套层数
- 分页显示评论,每页显示评论条数,默认显示`最前/后`页
- 其他评论设置-启用评论嵌套,最多嵌套层数
- 在每个页面顶部显示 `新旧`评论
#### 主题支持程度
@ -80,8 +73,3 @@ go run app/cmd/main.go [-c configpath] [-p port]
#### 其它
用的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/wpconfig"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"io"
@ -78,7 +77,7 @@ func PostComment(c *gin.Context) {
}
req.Host = home.Host
res, err := cli.Do(req)
if err != nil && !errors.Is(err, http.ErrUseLastResponse) {
if err != nil && err != http.ErrUseLastResponse {
logs.Error(err, "请求评论接口错误")
return
}
@ -111,9 +110,6 @@ func PostComment(c *gin.Context) {
}
cc := c.Copy()
go func() {
if gin.Mode() != gin.ReleaseMode {
return
}
id := comment.CommentPostId
if id <= 0 {
logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId)
@ -134,15 +130,8 @@ func PostComment(c *gin.Context) {
err = er
return
}
uuu := ""
uu, _ := url.Parse(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)
c.Redirect(http.StatusFound, res.Header.Get("Location"))
return
}
var r io.Reader

View File

@ -10,30 +10,6 @@ import (
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 {
eTag := str.Md5(lastTime.Format(tmp))
since := c.Request.Header.Get("If-Modified-Since")
@ -48,21 +24,20 @@ func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
return true
}
func SiteFeed(c *gin.Context) {
feed := cache.FeedCache()
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
func Feed(c *gin.Context) {
if !isCacheExpired(c, cache.FeedCache().GetLastSetTime()) {
c.Status(http.StatusNotModified)
return
}
r, err := feed.GetCache(c, time.Second, c)
r, err := cache.FeedCache().GetCache(c, time.Second, c)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Abort()
c.Error(err)
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) {
@ -76,33 +51,31 @@ func setFeed(s string, c *gin.Context, t time.Time) {
func PostFeed(c *gin.Context) {
id := c.Param("id")
postFeed := cache.PostFeedCache()
if !isCacheExpired(c, postFeed.GetLastSetTime(c, id)) {
if !isCacheExpired(c, cache.PostFeedCache().GetLastSetTime(c, id)) {
c.Status(http.StatusNotModified)
return
}
s, err := postFeed.GetCache(c, id, time.Second)
s, err := cache.PostFeedCache().GetCache(c, id, time.Second, c, id)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Abort()
c.Error(err)
return
}
setFeed(s, c, postFeed.GetLastSetTime(c, id))
setFeed(s, c, cache.PostFeedCache().GetLastSetTime(c, id))
}
func CommentsFeed(c *gin.Context) {
feed := cache.CommentsFeedCache()
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
if !isCacheExpired(c, cache.CommentsFeedCache().GetLastSetTime()) {
c.Status(http.StatusNotModified)
return
}
r, err := feed.GetCache(c, time.Second, c)
r, err := cache.CommentsFeedCache().GetCache(c, time.Second, c)
if err != nil {
c.Status(http.StatusInternalServerError)
c.Abort()
c.Error(err)
return
}
setFeed(r[0], c, feed.GetLastSetTime(c))
setFeed(r[0], c, cache.CommentsFeedCache().GetLastSetTime())
}

View File

@ -1,6 +1,7 @@
package actions
import (
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/gin-gonic/gin"
@ -8,8 +9,18 @@ import (
func ThemeHook(scene string) func(*gin.Context) {
return func(c *gin.Context) {
t := theme.GetCurrentTheme()
h := wp.NewHandle(c, scene, t)
s := scene
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)
}
}

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 (
"flag"
"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/config"
"github.com/fthvgb1/wp-go/app/pkg/db"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/plugins"
"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/wp/scriptloader"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/model"
"log"
"os"
"os/signal"
"regexp"
"strings"
"syscall"
"time"
)
@ -25,7 +30,13 @@ var confPath string
var address string
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(&address, "p", "", "listen address and port")
flag.Parse()
@ -39,6 +50,7 @@ func inits() {
if err != nil {
panic(err)
}
scriptloader.InitDefaultScriptSetting()
cache.InitActionsCommonCache()
plugins.InitDigestCache()
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() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
os.Exit(1)
os.Exit(-1)
}
}()
inits()
ossigns.SetConfPath(confPath)
go ossigns.SignalNotify()
go signalNotify()
Gin := route.SetupRouter()
c := config.GetConfig()
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.Store(sleepTime)
fn := func(sleepNum, maxNum int64, st []time.Duration) {
atomic.StoreInt64(&maxRequestSleepNum, sleepNum)
atomic.StoreInt64(&maxRequestNum, maxNum)
fn := func(msn, mn int64, st []time.Duration) {
atomic.StoreInt64(&maxRequestSleepNum, msn)
atomic.StoreInt64(&maxRequestNum, mn)
s.Store(st)
}
return func(c *gin.Context) {

View File

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

View File

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

View File

@ -1,8 +1,8 @@
package middleware
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/gin-gonic/gin"
"net/http"
@ -19,7 +19,7 @@ func ValidateServerNames() func(ctx *gin.Context) {
}
}
return m
}, "site-names")
})
return func(c *gin.Context) {
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 (
"context"
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
"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/pkg/models"
"github.com/fthvgb1/wp-go/cache/cachemanager"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/cache"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/safety"
"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() {
c := config.GetConfig()
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds", func() time.Duration {
return config.GetConfig().CacheTime.SearchPostCacheTime
})
searchPostIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.SearchPostCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
return config.GetConfig().CacheTime.PostListCacheTime
})
postListIdsCache = cachemanager.MapCacheBy[string](dao.SearchPostIds, c.CacheTime.PostListCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds", func() time.Duration {
return config.GetConfig().CacheTime.MonthPostCacheTime
})
monthPostsCache = cachemanager.MapCacheBy[string](dao.MonthPost, c.CacheTime.MonthPostCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext", func() time.Duration {
return config.GetConfig().CacheTime.ContextPostCacheTime
})
postContextCache = cachemanager.MapCacheBy[uint64](dao.GetPostContext, c.CacheTime.ContextPostCacheTime)
cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData", func() time.Duration {
return config.GetConfig().CacheTime.PostDataCacheTime
})
postsCache = cachemanager.MapBatchCacheBy(dao.GetPostsByIds, c.CacheTime.PostDataCacheTime)
cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData", func() time.Duration {
return config.GetConfig().CacheTime.PostDataCacheTime
})
postMetaCache = cachemanager.MapBatchCacheBy(dao.GetPostMetaByPostIds, c.CacheTime.PostDataCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData", func() time.Duration {
return config.GetConfig().CacheTime.CategoryCacheTime
})
categoryAndTagsCaches = cachemanager.MapCacheBy[string](dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime)
cachemanager.NewVarMemoryCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime, "recentPosts", func() time.Duration {
return config.GetConfig().CacheTime.RecentPostCacheTime
})
recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime)
cachemanager.NewVarMemoryCache(RecentComment, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration {
return config.GetConfig().CacheTime.RecentCommentsCacheTime
})
recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
postCommentCaches = cachemanager.MapCacheBy[uint64](dao.PostComments, c.CacheTime.PostCommentsCacheTime)
cachemanager.NewMemoryMapCache(nil, PostTopComments, 30*time.Second, "PostCommentsIds", func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
cachemanager.NewMemoryMapCache(nil, dao.PostTopCommentNum, 30*time.Second, "postTopCommentsNum", func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime)
cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, time.Hour, "postCommentData", func() time.Duration {
return config.GetConfig().CacheTime.CommentsCacheTime
})
usersCache = cachemanager.MapCacheBy[uint64](dao.GetUserById, c.CacheTime.UserInfoCacheTime)
cachemanager.NewMemoryMapCache(dao.CommentChildren, nil, time.Minute, "commentChildren", func() time.Duration {
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
})
usersNameCache = cachemanager.MapCacheBy[string](dao.GetUserByName, c.CacheTime.UserInfoCacheTime)
cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration {
return config.GetConfig().CacheTime.MaxPostIdCacheTime
})
commentsCache = cachemanager.MapBatchCacheBy(dao.GetCommentByIds, c.CacheTime.CommentsCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime
})
allUsernameCache = cache.NewVarCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameToUserData", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime
})
feedCache = cache.NewVarCache(feed, time.Hour)
cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime
})
postFeedCache = cachemanager.MapCacheBy[string](postFeed, time.Hour)
cachemanager.NewVarMemoryCache(SiteFeed, time.Hour, "siteFeed")
commentsFeedCache = cache.NewVarCache(commentsFeed, time.Hour)
cachemanager.NewMemoryMapCache(nil, PostFeed, time.Hour, "postFeed")
cachemanager.NewVarMemoryCache(CommentsFeed, time.Hour, "commentsFeed")
cachemanager.NewMemoryMapCache[string, string](nil, nil, 15*time.Minute, "NewComment")
newCommentCache = cachemanager.MapCacheBy[string, string](nil, 15*time.Minute)
InitFeed()
}
@ -102,19 +91,19 @@ type Arch struct {
month time.Month
}
var arch = reload.Vars(Arch{
var arch = safety.NewVar(Arch{
fn: dao.Archives,
}, "archives-year-month-data")
})
func Archives(ctx context.Context) []models.PostArchive {
a := arch.Load()
data := a.data
l := len(data)
m := time.Now().Month()
if l < 1 || a.month != m {
if l > 0 && a.month != m || l < 1 {
r, err := a.fn(ctx)
if err != nil {
logs.Error(err, "set cache Archives fail")
logs.Error(err, "set cache fail")
return nil
}
a.month = m
@ -124,3 +113,30 @@ func Archives(ctx context.Context) []models.PostArchive {
}
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 (
"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/models"
"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"
"github.com/fthvgb1/wp-go/helper/number"
str "github.com/fthvgb1/wp-go/helper/strings"
"time"
)
// RecentComments query func see RecentComment
func RecentComments(ctx context.Context, n int) (r []models.Comments) {
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 {
r = r[0:n]
}
@ -26,99 +19,22 @@ func RecentComments(ctx context.Context, n int) (r []models.Comments) {
return
}
// PostTopLevelCommentIds query func see PostTopComments
func PostTopLevelCommentIds(ctx context.Context, postId uint64, page, limit, total int, order string, a ...any) ([]uint64, error) {
var key string
if len(a) > 0 {
key = helper.ParseArgs("", a...)
func PostComments(ctx context.Context, Id uint64) ([]models.Comments, error) {
ids, err := postCommentCaches.GetCache(ctx, Id, time.Second, ctx, Id)
if err != nil {
return nil, err
}
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)
return GetCommentByIds(ctx, ids)
}
// 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)
return commentsCache.GetCache(ctx, id, time.Second, ctx, id)
}
// 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 GetCommentByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
return commentsCache.GetCacheBatch(ctx, ids, time.Second, ctx, ids)
}
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 {
return nil, err
}
return v, nil
}
func RecentComment(ctx context.Context, a ...any) (r []models.Comments, err error) {
r, err = dao.RecentComments(ctx, a...)
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) {
if wpconfig.GetOption("page_comments") != "1" {
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) {
comment, err := GetCommentById(ctx, commentId)
if err != nil {
return 0, err
}
if comment.CommentParent == 0 {
return comment.CommentId, nil
}
return AncestorCommentId(ctx, comment.CommentParent)
return newCommentCache
}

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

@ -1,8 +1,6 @@
package cache
import (
"context"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"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/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"
"github.com/fthvgb1/wp-go/plugin/digest"
"github.com/fthvgb1/wp-go/rss2"
"github.com/gin-gonic/gin"
"strings"
"time"
)
@ -35,25 +33,20 @@ func InitFeed() {
}
}
// CommentsFeedCache query func see CommentsFeed
func CommentsFeedCache() *cache.VarCache[[]string] {
r, _ := cachemanager.GetVarCache[[]string]("commentsFeed")
return r
return commentsFeedCache
}
// FeedCache query func see SiteFeed
func FeedCache() *cache.VarCache[[]string] {
r, _ := cachemanager.GetVarCache[[]string]("siteFeed")
return r
return feedCache
}
// PostFeedCache query func see PostFeed
func PostFeedCache() *cache.MapCache[string, string] {
r, _ := cachemanager.GetMapCache[string, string]("postFeed")
return r
return postFeedCache
}
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)
ids := slice.Map(r, func(t models.Posts) uint64 {
return t.Id
@ -99,7 +92,9 @@ func SiteFeed(c context.Context, _ ...any) (xml []string, err error) {
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)
maxId, err := GetMaxPostId(c)
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 {
return
}
limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10)
ids, err := PostTopLevelCommentIds(c, ID, 1, limit, 0, "desc", "latest-comment")
if err != nil {
return
}
comments, err := GetCommentDataByIds(c, ids)
comments, err := PostComments(c, post.Id)
if err != nil {
return
}
@ -131,14 +121,10 @@ func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
wpposts.PasswdProjectContent(&post)
if len(comments) > 0 {
t := comments[len(comments)-1]
u, err := GetCommentUrl(c, t.CommentId, t.CommentPostId)
if err != nil {
return "", err
}
rs.Items = []rss2.Item{
{
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,
PubDate: t.CommentDateGmt.Format(timeFormat),
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 {
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{
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,
PubDate: t.CommentDateGmt.Format(timeFormat),
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
}
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)
rs := templateRss
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
rs.LastBuildDate = time.Now().Format(timeFormat)
site := wpconfig.GetOption("siteurl")
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
}))
if nil != err {
@ -193,14 +175,9 @@ func CommentsFeed(c context.Context, _ ...any) (r []string, err error) {
} else {
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{
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,
Description: desc,
PubDate: t.CommentDateGmt.Format(timeFormat),

View File

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

View File

@ -3,30 +3,26 @@ package cache
import (
"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/models"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin"
"time"
)
// GetPostById query func see dao.GetPostsByIds
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) {
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) {
ids, err := cachemanager.GetBy[dao.PostIds]("searchPostIds", ctx, key, time.Second, args...)
ids, err := searchPostIdsCache.GetCache(ctx, key, time.Second, args...)
if err != nil {
return
}
@ -35,9 +31,8 @@ func SearchPost(ctx context.Context, key string, args ...any) (r []models.Posts,
return
}
// PostLists query func see dao.SearchPostIds
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 {
return
}
@ -46,17 +41,15 @@ func PostLists(ctx context.Context, key string, args ...any) (r []models.Posts,
return
}
// GetMaxPostId query func see dao.GetMaxPostId
func GetMaxPostId(ctx context.Context) (uint64, error) {
return cachemanager.GetVarVal[uint64]("maxPostId", ctx, time.Second)
func GetMaxPostId(ctx *gin.Context) (uint64, error) {
return maxPostIdCache.GetCache(ctx, time.Second, ctx)
}
// RecentPosts query func see dao.RecentPosts
func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
nn := n
feedNum := str.ToInteger(wpconfig.GetOption("posts_per_rss"), 10)
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) {
r = r[:n]
}
@ -64,9 +57,8 @@ func RecentPosts(ctx context.Context, n int) (r []models.Posts) {
return
}
// GetContextPost query func see dao.GetPostContext
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 {
return models.Posts{}, models.Posts{}, err
}
@ -75,9 +67,8 @@ func GetContextPost(ctx context.Context, id uint64, date time.Time) (prev, next
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) {
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 {
return
}

View File

@ -4,23 +4,27 @@ 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/model"
"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) {
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]uint64, error) {
return cachemanager.GetVarVal[map[string]uint64]("allUsername", ctx, time.Second)
func GetAllUsername(ctx context.Context) (map[string]struct{}, error) {
return allUsernameCache.GetCache(ctx, time.Second, ctx)
}
// GetUserById query func see dao.GetUserById
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)
return r
}

View File

@ -63,7 +63,6 @@ type CacheTime struct {
UserInfoCacheTime time.Duration `yaml:"userInfoCacheTime" json:"userInfoCacheTime,omitempty"`
CommentsCacheTime time.Duration `yaml:"commentsCacheTime" json:"commentsCacheTime,omitempty"`
SleepTime []time.Duration `yaml:"sleepTime" json:"sleepTime,omitempty"`
CommentsIncreaseUpdateTime time.Duration `yaml:"commentsIncreaseUpdateTime" json:"commentsIncreaseUpdateTime"`
}
type Ssl struct {
@ -85,14 +84,6 @@ type Mysql struct {
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 {
if conf == "" {
conf = "config.yaml"
@ -104,7 +95,6 @@ func InitConfig(conf string) error {
if err != nil {
return err
}
defer get.Body.Close()
file, err = io.ReadAll(get.Body)
} else {
file, err = os.ReadFile(conf)
@ -112,7 +102,6 @@ func InitConfig(conf string) error {
if err != nil {
return err
}
fileData.Store(file)
var c Config
err = yaml.Unmarshal(file, &c)
if err != nil {

View File

@ -2,21 +2,17 @@ package dao
import (
"context"
"database/sql"
"errors"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model"
)
// RecentComments
// param context.Context
func RecentComments(ctx context.Context, a ...any) (r []models.Comments, err error) {
n := helper.ParseArgs(10, a...)
func RecentComments(a ...any) (r []models.Comments, err error) {
ctx := a[0].(context.Context)
n := a[1].(int)
return model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{
{"comment_approved", "1"},
@ -32,8 +28,10 @@ func RecentComments(ctx context.Context, a ...any) (r []models.Comments, err err
// PostComments
// param1 context.Context
// param2 postId
func PostComments(ctx context.Context, postId uint64, _ ...any) ([]uint64, error) {
r, err := model.ChunkFind[models.Comments](ctx, 300, model.Conditions(
func PostComments(args ...any) ([]uint64, error) {
ctx := args[0].(context.Context)
postId := args[1].(uint64)
r, err := model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{
{"comment_approved", "1"},
{"comment_post_ID", "=", number.IntToString(postId), "int"},
@ -52,132 +50,17 @@ func PostComments(ctx context.Context, postId uint64, _ ...any) ([]uint64, error
}), err
}
func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]models.Comments, error) {
if len(ids) < 1 {
return nil, nil
}
func GetCommentByIds(args ...any) (map[uint64]models.Comments, error) {
ctx := args[0].(context.Context)
ids := args[1].([]uint64)
m := make(map[uint64]models.Comments)
off := 0
for {
id := slice.Slice(ids, off, 500)
if len(id) < 1 {
break
}
r, err := model.Finds[models.Comments](ctx, model.Conditions(
model.Where(model.SqlBuilder{
r, err := model.SimpleFind[models.Comments](ctx, model.SqlBuilder{
{"comment_ID", "in", ""}, {"comment_approved", "1"},
}),
model.Fields("*"),
model.In(slice.ToAnySlice(id)),
))
}, "*", slice.ToAnySlice(ids))
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 {
return 0, err
}
return str.ToInteger(n, 0), err
}
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 slice.SimpleToMap(r, func(t models.Comments) uint64 {
return t.CommentId
}), total, 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
}), err
}

View File

@ -21,14 +21,18 @@ type PostContext struct {
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"}
if ok {
switch t {
case constraints.Category:
in = []any{"category"}
case constraints.Tag:
in = []any{"post_tag"}
}
}
w := model.SqlBuilder{
{"tt.taxonomy", "in", ""},
}

View File

@ -11,8 +11,10 @@ import (
"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)
ctx := args[0].(context.Context)
ids := args[1].([]uint64)
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
model.In(slice.ToAnySlice(ids)),

View File

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

View File

@ -7,26 +7,31 @@ import (
"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)
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{
{"user_status", "=", "0", "int"},
}, "display_name,ID")
}, "user_login")
if err != nil {
return nil, err
}
return slice.ToMap(r, func(t models.Users) (string, uint64) {
return t.DisplayName, t.Id
return slice.ToMap(r, func(t models.Users) (string, struct{}) {
return t.UserLogin, struct{}{}
}, 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{{
"display_name", u,
"user_login", u,
}}, "*", nil)
return
}

View File

@ -3,21 +3,14 @@ package db
import (
"context"
"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/safety"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
"log"
"runtime"
)
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) {
c := config.GetConfig()
@ -43,11 +36,6 @@ func InitDb() (*safety.Var[*sqlx.DB], error) {
if preDb != nil {
_ = preDb.Close()
}
if showQuerySql == nil {
showQuerySql = reload.BuildFnVal("showQuerySql", false, func() bool {
return config.GetConfig().ShowQuerySql
})
}
return safeDb, err
}
@ -56,20 +44,14 @@ func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
nil,
nil))
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() {
_, f, l, _ := runtime.Caller(5)
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
if config.GetConfig().ShowQuerySql {
go log.Println(model.FormatSql(s, args...))
}
return query.Selects(ctx, a, s, args...)
})
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
if showQuerySql() {
_, f, l, _ := runtime.Caller(5)
go func() {
log.Printf("%v:%v %v\n", f, l, model.FormatSql(s, args...))
}()
if config.GetConfig().ShowQuerySql {
go log.Println(model.FormatSql(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)
func InitLogger() error {
c := config.GetConfig()
return SetLogger(c.LogOutput)
}
func SetLogger(loggerFile string) error {
if loggerFile == "" {
loggerFile = "stderr"
}
preFD := logFile.Load()
l := &log.Logger{}
c := config.GetConfig()
if c.LogOutput == "" {
c.LogOutput = "stderr"
}
var out io.Writer
switch loggerFile {
switch c.LogOutput {
case "stdout":
out = os.Stdout
case "stderr":
out = os.Stderr
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 {
return err
}

View File

@ -20,7 +20,6 @@ type Comments struct {
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
//扩展字段
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 {
@ -30,8 +29,3 @@ func (w Comments) PrimaryKey() string {
func (w Comments) Table() string {
return "wp_comments"
}
type PostComments struct {
Comments
Children []uint64
}

View File

@ -1,10 +1,8 @@
package plugins
import (
"context"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/gin-gonic/gin"
@ -19,7 +17,6 @@ type CommentHandler struct {
depth int
isTls bool
i CommentHtml
isThreadComments bool
}
type Comments struct {
@ -28,8 +25,8 @@ type Comments struct {
}
type CommentHtml interface {
FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string
FloorOrder(i, j models.Comments) bool
Sort(i, j *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 {
@ -49,21 +46,15 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m
isTls: isTls,
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()
if d.depth >= d.maxDepth {
if d.depth > d.maxDepth {
comments = d.findComments(comments)
}
order := wpconfig.GetOption("comment_order")
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
})
slice.Sort(comments, d.i.Sort)
for i, comment := range comments {
eo := "even"
if (i+1)%2 == 0 {
@ -75,11 +66,13 @@ func (d CommentHandler) formatComment(comments []*Comments) (html string) {
parent = "parent"
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 {
d.depth++
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`)
d.depth--
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
if isTop {
d.depth = 1
}
}
s.WriteString("</li><!-- #comment-## -->")
}
@ -92,7 +85,7 @@ func (d CommentHandler) findComments(comments []*Comments) []*Comments {
var r []*Comments
for _, comment := range comments {
tmp := *comment
tmp.Children = nil
comment.Children = nil
r = append(r, &tmp)
if len(comment.Children) > 0 {
t := d.findComments(comment.Children)
@ -142,35 +135,16 @@ func CommentRender() CommonCommentFormat {
type CommonCommentFormat struct {
}
func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
return FormatLi(li, m, respondsFn, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent)
func (c CommonCommentFormat) Sort(i, j *Comments) bool {
order := wpconfig.GetOption("comment_order")
if order == "asc" {
return i.CommentDate.UnixNano() < j.CommentDate.UnixNano()
}
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,
})
}
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 = `
@ -182,11 +156,14 @@ var li = `
src="{{Gravatar}}"
srcset="{{Gravatar}} 2x"
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 -->
<div class="comment-metadata">
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}">
<a href="/p/{{PostId}}#comment-{{CommentId}}">
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
</a></div><!-- .comment-metadata -->
@ -196,34 +173,30 @@ var li = `
<p>{{CommentContent}}</p>
</div><!-- .comment-content -->
{{respond}}
</article><!-- .comment-body -->
`
var respondTml = `<div class="reply">
<div class="reply">
<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}}">回复</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{
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
"{{Depth}}": strconv.Itoa(currentDepth),
"{{Depth}}": strconv.Itoa(depth),
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
"{{CommentAuthor}}": comments.CommentAuthor,
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
"{{page}}": strconv.Itoa(page),
"{{CommentDateGmt}}": comments.CommentDateGmt.String(),
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
"{{CommentContent}}": comments.CommentContent,
"{{eo}}": eo,
"{{parent}}": parent,
"{{respond}}": respond(comments, currentDepth, maxDepth, isThreadComments),
} {
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 (
"context"
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/cachemanager"
"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/cache/cachemanager"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/cache"
"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/safety"
"regexp"
"strings"
"time"
"unicode/utf8"
)
var digestCache *cache.MapCache[uint64, string]
var more = regexp.MustCompile("<!--more(.*?)?-->")
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() {
cachemanager.NewMemoryMapCache(nil, digestRaw, config.GetConfig().CacheTime.DigestCacheTime, "digestPlugin", func() time.Duration {
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
digestCache = cachemanager.MapCacheBy[uint64](digestRaw, config.GetConfig().CacheTime.DigestCacheTime)
}
func RemoveWpBlock(s string) string {
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)
id := arg[2].(uint64)
limit := arg[3].(int)
if limit < 0 {
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 {
closeTag := ""
content = RemoveWpBlock(content)
c := digestConfig.Load()
tag := c.DigestAllowTag
tag := config.GetConfig().DigestAllowTag
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>"
}
content = digest.StripTags(content, tag)
length := utf8.RuneCountInString(content) + 1
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 {
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) {
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
}

View File

@ -2,12 +2,8 @@ package plugins
import (
"fmt"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings"
"net/url"
"regexp"
"strconv"
"strings"
)
@ -22,13 +18,6 @@ type PageEle struct {
func TwentyFifteenPagination() PageEle {
return twentyFifteen
}
func TwentyFifteenCommentPagination() CommentPageEle {
return twentyFifteenComment
}
type CommentPageEle struct {
PageEle
}
var twentyFifteen = PageEle{
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">
<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 {
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 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/") {
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 == "" {
path = "/"
}
if 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)
return str.Join(path, query)
}

View File

@ -2,29 +2,12 @@ package apply
import "github.com/fthvgb1/wp-go/safety"
var contains = safety.NewMap[string, any]()
var fn safety.Var[any]
func SetVal(key string, val any) {
contains.Store(key, val)
func SetFn(f any) {
fn.Store(f)
}
func DelVal(key string) {
contains.Delete(key)
}
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
func UsePlugins() any {
return fn.Load()
}

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import (
)
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
})
/*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 (
"embed"
"github.com/fthvgb1/wp-go/multipTemplate"
"github.com/fthvgb1/wp-go/safety"
"html/template"
"io/fs"
"path/filepath"
@ -13,36 +12,20 @@ import (
//go:embed *[^.go]
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 BuildTemplate() *multipTemplate.MultipleFsTemplate {
if multiple != nil {
tt := multipTemplate.NewFsTemplate(TemplateFs)
commonTemplate(tt)
for k, v := range map[string]*template.Template(any(tt.Template).(multipTemplate.TemplateMaps)) {
multiple.Template.Store(k, v)
}
} 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 Template() *multipTemplate.MultipleFsTemplate {
t := multipTemplate.NewFsTemplate(TemplateFs)
templates = t.Template
t.FuncMap = FuncMap()
commonTemplate(t)
/*t.AddTemplate("twentyfifteen/*[^layout]/*.gohtml", FuncMap(), "twentyfifteen/layout/*.gohtml"). //单个主题设置
AddTemplate("twentyseventeen/*[^layout]/*.gohtml", FuncMap(), "twentyseventeen/layout/*.gohtml")*/
return t
}
func GetTemplate(name string) (*template.Template, bool) {
t, ok := templates.Load(name)
t, ok := templates[name]
return t, ok
}
@ -52,11 +35,10 @@ func commonTemplate(t *multipTemplate.MultipleFsTemplate) {
if err != nil {
panic(err)
}
funMap := FuncMap()
for _, main := range m {
file := filepath.Base(main)
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)
}
}
@ -73,10 +55,6 @@ func IsTemplateDirExists(tml string) bool {
}
func IsTemplateExists(tml string) bool {
t, ok := templates.Load(tml)
t, ok := templates[tml]
return ok && t != nil
}
func SetTemplate(name string, val *template.Template) {
templates.Store(name, val)
}

View File

@ -1,35 +1,25 @@
package theme
import (
"github.com/fthvgb1/wp-go/app/theme/twentyfifteen"
"github.com/fthvgb1/wp-go/app/theme/wp"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
)
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)
}
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) {
fn, ok := themeMap.Load(themeName)
if ok && fn != nil {
fn(h)
return
}
panic(str.Join("theme ", themeName, " don't exist"))
twentyfifteen.Hook(h)
}

View File

@ -7,12 +7,7 @@ import (
"time"
)
func postsFn(fn func(models.Posts) string, a models.Posts) string {
return fn(a)
}
func FuncMap() template.FuncMap {
return template.FuncMap{
var comFn = template.FuncMap{
"unescaped": func(s string) any {
return template.HTML(s)
},
@ -30,8 +25,19 @@ func FuncMap() template.FuncMap {
"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))
},
}
func postsFn(fn func(models.Posts) string, a models.Posts) string {
return fn(a)
}
func FuncMap() template.FuncMap {
return comFn
}
func addTemplateFunc(fnName string, fn any) {
if _, ok := comFn[fnName]; ok {
panic("exists same name func")
}
comFn[fnName] = fn
}

View File

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

View File

@ -1,9 +1,9 @@
package twentyfifteen
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/theme/wp"
"github.com/fthvgb1/wp-go/cache/reload"
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) {
img, rand := h.GetCustomHeaderImg()

View File

@ -31,7 +31,11 @@
{{end}}
</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>
{{template "layout/footer" .}}

View File

@ -52,30 +52,14 @@
{{ if .showComment}}
<div id="comments" class="comments-area">
{{ if ne .comments ""}}
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </h2>
{{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}}
{{ if gt .post.CommentCount 0}}
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
<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}}
{{if eq .post.CommentStatus "open"}}
{{if and (eq .post.CommentStatus "open") (eq ( "thread_comments" |getOption ) "1")}}
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">发表回复
<small>

View File

@ -6,7 +6,7 @@ import (
"github.com/fthvgb1/wp-go/app/plugins"
"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/middleware"
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
"github.com/fthvgb1/wp-go/app/wpconfig"
"strings"
)
@ -22,13 +22,16 @@ func configs(h *wp.Handle) {
return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`)
})
wp.InitPipe(h)
middleware.CommonMiddleware(h)
setPaginationAndRender(h)
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
wp.NewHandleFn(widget.CheckCategory, 100, "widget.CheckCategory"))
h.Index.SetPageEle(plugins.TwentyFifteenPagination())
h.PushCacheGroupHeadScript(constraints.AllScene, "CalCustomBackGround", 10.005, CalCustomBackGround)
h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss)
h.CommonComponents()
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"))
h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender"))
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"))
}
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) {
d := h.GetDetailHandle()
if d.Post.Thumbnail.Path != "" {
d.Post.Thumbnail = wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "")
if h.Detail.Post.Thumbnail.Path != "" {
h.Detail.Post.Thumbnail = wpconfig.Thumbnail(h.Detail.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}}
<div id="comments" class="comments-area">
{{ if ne .comments ""}}
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.totalCommentNum}}个回复 </h2>
{{ if gt .post.CommentCount 0}}
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.post.CommentCount}}个回复 </h2>
<ol class="comment-list">
{{.comments|unescaped}}
</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}}
{{if eq .post.CommentStatus "open"}}
{{template "respond" .}}
@ -132,7 +123,7 @@
</div>
</div>
{{template "common/colophon" .}}
{{template "layout/colophon"}}
</div>

View File

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

View File

@ -4,10 +4,56 @@ import (
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"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/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) {
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 {
head := headScript
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/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>`
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
import (
"context"
"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/widgets"
"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/theme/wp"
"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/cache/reload"
"github.com/fthvgb1/wp-go/helper"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/plugin/pagination"
"github.com/gin-gonic/gin"
"strings"
)
@ -28,33 +27,23 @@ var paginate = func() plugins.PageEle {
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>`, 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
}()
var commentPageEle pagination.Render
func Hook(h *wp.Handle) {
wp.Run(h, configs)
}
func configs(h *wp.Handle) {
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.PushCacheGroupHeadScript(constraints.AllScene, "colorScheme-customHeader", 10, colorScheme, customHeader)
components.WidgetArea(h)
pushScripts(h)
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">`,
"{$after_widget}": `</section>`,
})
@ -63,11 +52,12 @@ func configs(h *wp.Handle) {
wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"),
)
videoHeader(h)
h.SetData("colophon", colophon)
setPaginationAndRender(h)
h.Detail.CommentRender = commentFormat
h.CommonComponents()
h.Index.SetPageEle(paginate)
wp.ReplyCommentJs(h)
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"))
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"))
@ -75,19 +65,6 @@ func configs(h *wp.Handle) {
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="/">
<label for="search-form-1">
<span class="screen-reader-text">{$label}</span>
@ -108,7 +85,7 @@ func errorsHandle(h *wp.Handle) {
}
func postThumb(h *wp.Handle) {
d := h.GetDetailHandle()
d := h.Detail
if d.Post.Thumbnail.Path != "" {
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
img.Sizes = "100vw"
@ -123,27 +100,19 @@ type comment struct {
plugins.CommonCommentFormat
}
var commentLi = plugins.CommonLi()
var respondFn = plugins.Responds(respondStr)
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"
func (c comment) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
templ := plugins.CommonLi()
templ = strings.ReplaceAll(templ, `<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>`
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) {
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) {
h.SetData("HeaderImage", getHeaderImage(h))

View File

@ -33,14 +33,14 @@ func (h *Handle) BodyClass() string {
case constraints.Search:
s := "search-no-results"
if len(h.GetIndexHandle().Posts) > 0 {
if len(h.Index.Posts) > 0 {
s = "search-results"
}
class = append(class, "search", s)
case constraints.Category, constraints.Tag:
class = append(class, "archive", "category")
cat := h.GetIndexHandle().Param.Category
cat := h.Index.Param.Category
if cat == "" {
break
}
@ -54,16 +54,16 @@ func (h *Handle) BodyClass() string {
case constraints.Author:
class = append(class, "archive", "author")
author := h.GetIndexHandle().Param.Author
author := h.Index.Param.Author
user, _ := cache.GetUserByName(h.C, author)
class = append(class, str.Join("author-", number.IntToString(user.Id)))
if user.DisplayName[0] != '%' {
class = append(class, str.Join("author-", user.DisplayName))
if user.UserLogin[0] != '%' {
class = append(class, str.Join("author-", user.UserLogin))
}
case constraints.Detail:
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 {
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
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety"
"strings"
)
var handleComponents = safety.NewMap[string, map[string][]Components[string]]()
var handleComponentHook = safety.NewMap[string, map[string][]func(Components[string]) (Components[string], bool)]()
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
func (h *Handle) DeleteComponents(scene, name string) {
h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
return c, c.Name != name
})
handleComponentHook.Store(scene, v)
}
func (h *Handle) ReplaceComponents(scene, componentKey, componentName string, components Components[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) {
if c.Name == componentName {
func (h *Handle) ReplaceComponents(scene, name string, components Components[string]) {
h.componentHook[scene] = append(h.componentHook[scene], func(c Components[string]) (Components[string], bool) {
if c.Name == name {
c = components
}
return c, true
})
handleComponentHook.Store(scene, v)
}
func (h *Handle) PushComponentHooks(scene, componentKey string, fn func(Components[string]) (Components[string], bool)) {
v, ok := handleComponentHook.Load(scene)
if !ok {
v = make(map[string][]func(Components[string]) (Components[string], bool))
}
v[componentKey] = append(v[componentKey], fn)
handleComponentHook.Store(scene, v)
func (h *Handle) HookComponents(scene string, fn func(Components[string]) (Components[string], bool)) {
h.componentHook[scene] = append(h.componentHook[scene], fn)
}
var GetAndHookComponents = reload.BuildMapFnWithAnyParams[string]("calComponents", HookComponent)
func HookComponent(a ...any) []Components[string] {
componentKey := a[0].(string)
scene := a[1].(string)
mut := reload.GetGlobeMutex()
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
func CalComponents(h *Handle) {
componentss := reload.GetAnyValMapBy("scene-components", str.Join("allScene-", h.scene), h, func(h *Handle) map[string][]Components[string] {
return maps.MergeBy(func(k string, v1, v2 []Components[string]) ([]Components[string], bool) {
vv := append(v1, v2...)
return vv, vv != nil
}, nil, h.components[h.scene], h.components[constraints.AllScene])
})
slice.SimpleSort(r, slice.DESC, func(t Components[string]) float64 {
return t.Order
for k, components := range componentss {
key := str.Join("calComponents-", h.scene, "-", k)
ss := reload.GetAnyValMapBy("calComponents", key, h, func(h *Handle) []Components[string] {
r := slice.FilterAndMap(components, func(t Components[string]) (Components[string], bool) {
fns, ok := h.componentHook[k]
if !ok {
return t, true
}
for _, fn := range fns {
c, ok := fn(t)
if !ok {
return c, false
}
t = c
}
return t, true
})
slice.Sort(r, func(i, j Components[string]) bool {
return i.Order > j.Order
})
return r
}
func GetComponents(scene, key string) (r []Components[string]) {
sceneComponents, _ := handleComponents.Load(scene)
allSceneComponents, _ := handleComponents.Load(constraints.AllScene)
r = append(sceneComponents[key], allSceneComponents[key]...)
return
}
var CacheComponent = reload.BuildMapFnWithAnyParams[string]("cacheComponents", cacheComponentFn)
func cacheComponentFn(a ...any) string {
return a[0].(Components[string]).Fn(a[1].(*Handle))
}
func CalComponent(h *Handle) func(string) string {
return func(componentKey string) string {
cacheKey := str.Join("get-hook-Components-", h.scene, "-", componentKey)
hookedComponents := GetAndHookComponents(cacheKey, componentKey, h.scene)
var s = make([]string, 0, len(hookedComponents))
for _, component := range hookedComponents {
})
var s = make([]string, 0, len(ss))
for _, component := range ss {
if component.Val != "" {
s = append(s, component.Val)
continue
@ -105,8 +64,7 @@ func CalComponent(h *Handle) func(string) string {
if component.Fn != nil {
v := ""
if component.Cached {
key := str.Join(h.scene, "-", componentKey, "-", component.Name)
v = CacheComponent(key, component, h)
v = reload.GetAnyValMapBy("cacheComponents", component.Name, h, component.Fn)
} else {
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]) {
c, ok := handleComponents.Load(scene)
c, ok := h.components[scene]
if !ok {
c = make(map[string][]Components[string])
h.components[scene] = c
}
c[componentType] = append(c[componentType], components...)
handleComponents.Store(scene, c)
}
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...)
}
func GetComponentsArgs[T any](k string, defaults T) T {
v, ok := componentsArgs.Load(k)
func GetComponentsArgs[T any](h *Handle, k string, defaults T) T {
v, ok := h.componentsArgs[k]
if ok {
vv, ok := v.(T)
if ok {
@ -192,61 +150,60 @@ func GetComponentsArgs[T any](k string, defaults T) T {
return defaults
}
func PushComponentsArgsForSlice[T any](name string, v ...T) {
val, ok := componentsArgs.Load(name)
func PushComponentsArgsForSlice[T any](h *Handle, name string, v ...T) {
val, ok := h.componentsArgs[name]
if !ok {
var vv []T
vv = append(vv, v...)
componentsArgs.Store(name, vv)
h.componentsArgs[name] = vv
return
}
vv, ok := val.([]T)
if ok {
vv = append(vv, v...)
componentsArgs.Store(name, vv)
h.componentsArgs[name] = vv
}
}
func SetComponentsArgsForMap[K comparable, V any](name string, key K, v V) {
val, ok := componentsArgs.Load(name)
func SetComponentsArgsForMap[K comparable, V any](h *Handle, name string, key K, v V) {
val, ok := h.componentsArgs[name]
if !ok {
vv := make(map[K]V)
vv[key] = v
componentsArgs.Store(name, vv)
h.componentsArgs[name] = vv
return
}
vv, ok := val.(map[K]V)
if ok {
vv[key] = v
componentsArgs.Store(name, vv)
h.componentsArgs[name] = vv
}
}
func MergeComponentsArgsForMap[K comparable, V any](name string, m map[K]V) {
val, ok := componentsArgs.Load(name)
func MergeComponentsArgsForMap[K comparable, V any](h *Handle, name string, m map[K]V) {
val, ok := h.componentsArgs[name]
if !ok {
componentsArgs.Store(name, m)
h.componentsArgs[name] = m
return
}
vv, ok := val.(map[K]V)
if ok {
componentsArgs.Store(name, maps.Merge(vv, m))
h.componentsArgs[name] = maps.Merge(vv, m)
}
}
func SetComponentsArgs(key string, value any) {
componentsArgs.Store(key, value)
func SetComponentsArgs(h *Handle, key string, value any) {
h.componentsArgs[key] = value
}
func (h *Handle) GetComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
return componentFilterFns.Load(name)
func (h *Handle) ComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
fn, ok := h.componentFilterFn[name]
return fn, ok
}
func (h *Handle) AddActionFilter(name string, fns ...func(*Handle, string, ...any) string) {
v, _ := componentFilterFns.Load(name)
v = append(v, fns...)
componentFilterFns.Store(name, v)
h.componentFilterFn[name] = append(h.componentFilterFn[name], fns...)
}
func (h *Handle) DoActionFilter(name, s string, args ...any) string {
calls, ok := componentFilterFns.Load(name)
calls, ok := h.componentFilterFn[name]
if ok {
return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string {
return fn(h, r, args...)

View File

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

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
constraints2 "github.com/fthvgb1/wp-go/app/pkg/constraints"
"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/theme/wp"
"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/number"
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")
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)
if !ok {
return "", "", false
@ -71,16 +71,15 @@ func parseAttr(attr map[any]any) string {
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) {
func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) {
counter := number.Counters[int]()
var err error
conf := reload.GetAnyValBys("block-category-conf", h, func(h *wp.Handle) map[any]any {
var con any
err := json.Unmarshal([]byte(blockParser.Attrs), &con)
err = json.Unmarshal([]byte(blockParser.Attrs), &con)
if err != nil {
logs.Error(err, "解析category attr错误", blockParser.Attrs)
return nil, false
return nil
}
var conf map[any]any
switch con.(type) {
@ -112,20 +111,8 @@ func categoryConfFn(blockParser ParserBlock) (map[any]any, bool) {
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) {
counter := number.Counters[int]()
var err error
conf := GetCategoryConf(blockParser)
return conf
})
if err != nil {
return nil, err
@ -140,7 +127,10 @@ func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string,
if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") {
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 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}"])
}
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()
li := widget.CategoryLi(h, confAttr, categories)
attrs := GetCategoryAttr(confAttr)
li := widget.CategoryLi(h, conf, categories)
attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
s.Sprintf(`<ul %s>%s</ul>`, attrs, li)
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()
ids := fmt.Sprintf(`wp-block-categories-%v`, id)
args = maps.Copy(args)
args["{$selectId}"] = ids
attrs := GetCategoryAttr(confAttr)
selects := widget.DropdownCategories(h, args, confAttr, categories)
attrs := reload.GetAnyValBys("block-category-attr", conf, parseAttr)
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))
return s.String()
}

View File

@ -2,11 +2,11 @@ package widget
import (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
@ -40,16 +40,12 @@ var archivesConfig = map[any]any{
"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)
func Archive(h *wp.Handle, id string) string {
conf := configs(archivesConfig, "widget_archives", int64(2))
args := reload.GetAnyValBys("widget-archive-args", h, func(h *wp.Handle) map[string]string {
archiveArgs := archiveArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, CommonArgs())
args := wp.GetComponentsArgs(widgets.Archive, 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}"])
@ -58,11 +54,7 @@ func archiveArgsFn(a ...any) map[string]string {
args["{$navCloser}"] = "</nav>"
}
return args
}
func Archive(h *wp.Handle, id string) string {
conf := GetArchiveConf()
args := GetArchiveArgs(h, conf, id)
})
s := archiveTemplate
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 {
option := str.NewBuilder()
option.Sprintf(`<option value="">%s</option>`, args["{$dropdown_label}"])
i := h.GetIndexHandle()
month := strings.TrimLeft(i.Param.Month, "0")
month := strings.TrimLeft(h.Index.Param.Month, "0")
showCount := conf["count"].(int64)
for _, archive := range archives {
sel := ""
if i.Param.Year == archive.Year && month == archive.Month {
if h.Index.Param.Year == archive.Year && month == archive.Month {
sel = "selected"
}
count := ""

View File

@ -2,16 +2,17 @@ package widget
import (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/helper/tree"
"net/http"
"strings"
)
@ -44,16 +45,12 @@ func categoryArgs() map[string]string {
}
}
var GetCategoryConf = BuildconfigFn(categoryConfig, "widget_categories", int64(2))
func Category(h *wp.Handle, id string) string {
conf := configs(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 := 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}"])
@ -62,11 +59,8 @@ func categoryArgsFn(a ...any) map[string]string {
args["{$navCloser}"] = "</nav>"
}
return args
}
})
func Category(h *wp.Handle, id string) string {
conf := GetCategoryConf()
args := GetCategoryArgs(h, conf, id)
t := categoryTemplate
dropdown := conf["dropdown"].(int64)
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>
`, args["{$show_option_none}"])
currentCategory := ""
i := h.GetIndexHandle()
if h.Scene() == constraints.Category {
currentCategory = i.Param.Category
currentCategory = h.Index.Param.Category
}
showCount := conf["count"].(int64)
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())
}
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) {
cate := wp.GetComponentsArgs[map[string]string](widgets.Categories, categoryArgs())
cate := wp.GetComponentsArgs[map[string]string](h, widgets.Categories, categoryArgs())
name, ok := cate["{$name}"]
if !ok || name == "" {
return
@ -266,12 +292,3 @@ func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) {
category = cc
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
import (
"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/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps"
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 {
return func(_ ...any) map[K]V {
c := wpconfig.GetPHPArrayVal[map[K]V](key, nil, a...)
func configs[M ~map[K]V, K comparable, V any](m M, key string, a ...any) M {
return reload.GetAnyValBys(str.Join("widget-config-", key), key, func(_ string) M {
c := wpconfig.GetPHPArrayVal[M](key, nil, a...)
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 (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/theme/wp"
"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"
str "github.com/fthvgb1/wp-go/helper/strings"
@ -21,21 +21,18 @@ var metaTemplate = `{$before_widget}
{$navCloser}
{$after_widget}`
func defaultMetaArgs() map[string]string {
func metaArgs() map[string]string {
return map[string]string{
"{$aria_label}": "",
"{$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)
func Meta(h *wp.Handle, id string) string {
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")
@ -50,10 +47,8 @@ func ParseMetaArgs(a ...any) map[string]string {
args["{$navCloser}"] = "</nav>"
}
return args
}
})
func Meta(h *wp.Handle, id string) string {
args := GetMetaArgs(h, id)
ss := str.NewBuilder()
if str.ToInteger(wpconfig.GetOption("users_can_register"), 0) > 0 {
ss.Sprintf(`<li><a href="/wp-login.php?action=register">注册</li>`)

View File

@ -2,11 +2,11 @@ package widget
import (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models"
"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/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
@ -39,17 +39,13 @@ var recentCommentsTemplate = `{$before_widget}
{$after_widget}
`
var GetRecentCommentConf = BuildconfigFn(recentCommentConf, "widget_recent-comments", int64(2))
func RecentComments(h *wp.Handle, id string) string {
conf := configs(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)
args := reload.GetAnyValBys("widget-recent-comment-args", h, func(h *wp.Handle) map[string]string {
commentsArgs := recentCommentsArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.RecentComments, commentsArgs)
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}"])
@ -58,17 +54,14 @@ func RecentCommentArgs(a ...any) map[string]string {
args["{$navCloser}"] = "</nav>"
}
return args
}
func RecentComments(h *wp.Handle, id string) string {
conf := GetRecentCommentConf()
args := GetRecentCommentArgs(h, conf, id)
})
comments := slice.Map(cache.RecentComments(h.C, int(conf["number"].(int64))), func(t models.Comments) string {
return fmt.Sprintf(` <li>
<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"))
return h.DoActionFilter(widgets.RecentComments, str.Replace(s, args))

View File

@ -2,13 +2,13 @@ package widget
import (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/constraints"
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/theme/wp"
"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"
str "github.com/fthvgb1/wp-go/helper/strings"
@ -25,7 +25,7 @@ var recentPostsTemplate = `{$before_widget}
{$after_widget}
`
func DefaultRecentPostsArgs() map[string]string {
func recentPostsArgs() map[string]string {
return map[string]string{
"{$before_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{
"number": int64(5),
"show_date": false,
@ -43,24 +43,18 @@ func DefaultRecentConf() map[any]any {
}
}
var GetRecentPostConf = reload.BuildValFnWithAnyParams("widget-recent-posts-conf", RecentPostConf)
func RecentPostConf(_ ...any) map[any]any {
recent := DefaultRecentConf()
func RecentPosts(h *wp.Handle, id string) string {
conf := reload.GetAnyValBys("widget-recent-posts-conf", h, func(h *wp.Handle) map[any]any {
recent := recentConf()
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 := 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}"])
@ -69,11 +63,8 @@ func ParseRecentPostArgs(a ...any) map[string]string {
args["{$navCloser}"] = "</nav>"
}
return args
}
})
func RecentPosts(h *wp.Handle, id string) string {
conf := GetRecentPostConf()
args := GetRecentPostArgs(h, conf, id)
currentPostId := uint64(0)
if h.Scene() == constraints.Detail {
currentPostId = str.ToInteger(h.C.Param("id"), uint64(0))

View File

@ -2,11 +2,11 @@ package widget
import (
"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/widgets"
"github.com/fthvgb1/wp-go/app/theme/wp"
"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/maps"
"github.com/fthvgb1/wp-go/helper/slice"
@ -47,14 +47,11 @@ func searchArgs() map[string]string {
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)
func Search(h *wp.Handle, id string) string {
args := reload.GetAnyValBys("widget-search-args", h, func(h *wp.Handle) map[string]string {
search := searchArgs()
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
args := wp.GetComponentsArgs(widgets.Search, search)
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}"] == "" {
@ -73,14 +70,11 @@ func ParseSearchArgs(a ...any) map[string]string {
}
return args
}
func Search(h *wp.Handle, id string) string {
args := GetSearchArgs(h, id)
})
s := strings.ReplaceAll(searchTemplate, "{$form}", form)
val := ""
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)
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 (
"encoding/json"
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/pkg/logs"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/model"
"regexp"
"strings"
)
func (h *Handle) DisplayHeaderText() bool {
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) {
var err error
img := GetCustomHeaderImgFn(h)
err = h.Err()
if err != nil && strings.Contains(err.Error(), "get customheadimage err") {
img := reload.GetAnyValBys("headerImages", h.theme, func(theme string) []models.PostThumbnail {
hs, er := h.GetHeaderImages(h.theme)
if er != nil {
err = er
return nil
}
return hs
})
if err != nil {
logs.Error(err, "获取页眉背景图失败")
return
}
@ -128,7 +122,7 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
logs.Error(err, "get headerVideo fail", mod.HeaderVideo)
return
}
scripts := []string{
scriptss := []string{
"/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js",
"/wp-includes/js/dist/vendor/regenerator-runtime.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/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>
`, t, str.Replaces(t, []string{
"/wp-includes/js/dist/vendor/",
"/wp-includes/js/dist/",
"/wp-includes/js/",
".min.js",
".js",
"wp-",
"",
"/wp-includes/js/dist/vendor/", "/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]{
NewComponent("wp-a11y-js-translations", tr, true, 10.0065, 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 {
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...)
}
ok = true

View File

@ -2,9 +2,9 @@ package wp
import (
"fmt"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/cache"
"github.com/fthvgb1/wp-go/app/wpconfig"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper/maps"
str "github.com/fthvgb1/wp-go/helper/strings"
)
@ -42,10 +42,10 @@ func CalCustomLogo(h *Handle) (r string) {
return
}
var GetCustomLog = reload.BuildValFn("customLogo", CalCustomLogo)
func customLogo(h *Handle) 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/wpposts"
"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"
"github.com/fthvgb1/wp-go/plugin/pagination"
"strings"
"time"
)
type DetailHandle struct {
*Handle
CommentRender plugins.CommentHtml
Comments []uint64
Page int
Limit int
Comments []models.Comments
Post models.Posts
CommentPageEle pagination.Render
TotalRaw int
TotalPage int
CommentStep int
}
func NewDetailHandle(handle *Handle) *DetailHandle {
return &DetailHandle{
Handle: handle,
Page: 1,
Limit: 5,
CommentStep: 1,
}
return &DetailHandle{Handle: handle}
}
func (d *DetailHandle) BuildDetailData() (err error) {
@ -46,7 +30,7 @@ func (d *DetailHandle) BuildDetailData() (err error) {
if err != nil {
return
}
d.CommentData()
d.Comment()
d.ContextPost()
return
}
@ -84,66 +68,12 @@ func (d *DetailHandle) PasswordProject() {
}
}
}
func (h *Handle) GetDetailHandle() *DetailHandle {
v, ok := h.C.Get("detailHandle")
if !ok {
vv := NewDetailHandle(h)
h.C.Set("detailHandle", vv)
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) Comment() {
comments, err := cache.PostComments(d.C, d.Post.Id)
logs.IfError(err, "get d.Post comment", d.Post.Id)
d.ginH["comments"] = comments
d.Comments = comments
}
func (d *DetailHandle) RenderComment() {
@ -156,21 +86,9 @@ func (d *DetailHandle) RenderComment() {
ableComment = false
}
d.ginH["showComment"] = ableComment
d.ginH["comments"] = ""
if len(d.Comments) < 0 || !ableComment {
return
}
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())
if len(d.Comments) > 0 && ableComment {
dep := str.ToInteger(wpconfig.GetOption("thread_comments_depth"), 5)
d.ginH["comments"] = plugins.FormatComments(d.C, d.CommentRender, d.Comments, dep)
}
}
@ -185,17 +103,16 @@ func DetailRender(h *Handle) {
if h.Stats != constraints.Ok {
return
}
d := h.GetDetailHandle()
d := h.Detail
d.PasswordProject()
d.RenderComment()
d.ginH["post"] = d.Post
}
func Detail(h *Handle) {
d := h.GetDetailHandle()
err := d.BuildDetailData()
err := h.Detail.BuildDetailData()
if err != nil {
d.SetErr(err, High)
h.Detail.SetErr(err)
}
h.SetData("scene", h.Scene())
}
@ -203,7 +120,7 @@ func Detail(h *Handle) {
func ReplyCommentJs(h *Handle) {
h.PushFooterScript(constraints.Detail, NewComponent("comment-reply.js", "", false, 10, func(h *Handle) string {
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>`
}
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 (
"errors"
"github.com/fthvgb1/wp-go/safety"
)
var fnMap = safety.NewMap[string, map[string]any]()
var fnHook = safety.NewMap[string, map[string]any]()
var fnMap map[string]map[string]any
var fnHook map[string]map[string]any
func GetFn[T any](fnType string, name string) []T {
v, ok := fnMap.Load(fnType)
v, ok := fnMap[fnType]
if !ok {
return nil
}
@ -20,7 +19,7 @@ func GetFn[T any](fnType string, name string) []T {
return vv.([]T)
}
func GetFnHook[T any](fnType string, name string) []T {
v, ok := fnHook.Load(fnType)
v, ok := fnHook[fnType]
if !ok {
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 {
v, ok := fnMap.Load(fnType)
v, ok := fnMap[fnType]
if !ok {
v = make(map[string]any)
fnMap.Store(fnType, v)
fnMap[fnType] = v
v[name] = fns
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 {
v, ok := fnHook.Load(fnType)
v, ok := fnHook[fnType]
if !ok {
v = make(map[string]any)
fnHook.Store(fnType, v)
fnHook[fnType] = v
v[name] = fns
return nil
}

View File

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

View File

@ -105,6 +105,7 @@ func NewIndexParams(ctx *gin.Context) *IndexParams {
func (i *IndexParams) ParseSearchs() {
s := i.Ctx.Query("s")
if s != "" {
q := str.Join("%", s, "%")
i.Where = append(i.Where, []string{
"and", "post_title", "like", q, "",
@ -116,6 +117,7 @@ func (i *IndexParams) ParseSearchs() {
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
i.Search = s
}
}
func (i *IndexParams) ParseArchives() error {
year := i.Ctx.Param("year")
if year != "" {

View File

@ -1,11 +1,11 @@
package wp
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/app/pkg/config"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/app/plugins"
"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/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){
"passwordProject": PasswordProject,
"digest": Digest,
}, "list-post-plugins-fns")
})
func (h *Handle) PushPostsPlugin(name string, fn func(PostsPlugin, *Handle, *models.Posts)) {
m := pluginFns.Load()
@ -53,7 +53,7 @@ func Digest(next PostsPlugin, h *Handle, post *models.Posts) {
next(h, post)
}
var ordinaryPlugin = reload.Vars([]PostsPlugin{}, "ordinaryPlugin")
var ordinaryPlugin = reload.Vars([]PostsPlugin{})
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
p := ordinaryPlugin.Load()
@ -69,7 +69,7 @@ func PostPlugin(calls ...PostsPlugin) PostsPlugin {
}
}
func UsePostsPlugins(_ ...any) PostsPlugin {
func UsePostsPlugins() PostsPlugin {
m := pluginFns.Load()
pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
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
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"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"
str "github.com/fthvgb1/wp-go/helper/strings"
)
@ -21,7 +22,7 @@ func NewPipe(name string, order float64, fn HandlePipeFn[*Handle]) Pipe {
// HandlePipe 方便把功能写在其它包里
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) {
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) {
v, ok := handlerss.Load(pipScene)
if !ok {
v = make(map[string][]HandleCall)
if _, ok := h.handlers[pipScene]; !ok {
h.handlers[pipScene] = make(map[string][]HandleCall)
}
v[scene] = append(v[scene], fns...)
handlerss.Store(pipScene, v)
h.handlers[pipScene][scene] = append(h.handlers[pipScene][scene], fns...)
}
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...)
}
func BuildHandlers(pipeScene string, keyFn func(*Handle, string) string,
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))
func BuildPipe(pipeScene string, keyFn func(*Handle, string) string, fn func(*Handle, map[string][]HandleCall, string) []HandleCall) func(HandleFn[*Handle], *Handle) {
return func(next HandleFn[*Handle], h *Handle) {
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 {
handler.Fn(h)
if h.abort {
@ -88,63 +98,22 @@ 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 {
key := str.Join("pipekey", "-", pipScene, "-", h.scene, "-", h.Stats)
return h.DoActionFilter("pipeKey", key, pipScene)
}
var pipeInitFn = reload.BuildMapFn[string]("pipeInit", BuildPipe)
func Run(h *Handle, conf func(*Handle)) {
if !h.isInited {
if !helper.GetContextVal(h.C, "inited", false) {
InitHandle(conf, h)
}
pipeInitFn(h.scene, h.scene)(h)
}
func BuildPipe(scene string) func(*Handle) {
pipees := GetFn[Pipe]("pipe", constraints.AllScene)
pipees = append(pipees, GetFn[Pipe]("pipe", scene)...)
pipes := slice.FilterAndMap(pipees, func(pipe Pipe) (Pipe, bool) {
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)...)
pipes := slice.FilterAndMap(p, func(pipe Pipe) (Pipe, bool) {
var ok bool
mut := reload.GetGlobeMutex()
mut.Lock()
hooks := GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", constraints.AllScene)
hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", scene)...)
mut.Unlock()
hooks = append(hooks, GetFnHook[func(Pipe) (Pipe, bool)]("pipeHook", h.scene)...)
for _, fn := range hooks {
pipe, ok = fn(pipe)
if !ok {
@ -153,76 +122,63 @@ func BuildPipe(scene string) func(*Handle) {
}
return pipe, pipe.Fn != nil
})
slice.SimpleSort(pipes, slice.DESC, func(t Pipe) float64 {
return t.Order
slice.Sort(pipes, func(i, j Pipe) bool {
return i.Order > j.Order
})
arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] {
return t.Fn
})
return HandlePipe(NothingToDo, arr...)
})(h)
}
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,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
return h.PipeHandleHook("PipeMiddlewareHandle", hookedHandles, handlers, key)
func PipeMiddlewareHandle(h *Handle, middlewares map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, middlewares[h.scene]...)
handlers = append(handlers, middlewares[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeMiddlewareHandle", handlers, middlewares, key)
return
}
func PipeDataHandle(h *Handle, key string,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, constraints.AllScene)
return h.PipeHandleHook("PipeDataHandle", hookedHandles, handlers, key)
func PipeDataHandle(h *Handle, dataHandlers map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, dataHandlers[h.scene]...)
handlers = append(handlers, dataHandlers[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeDataHandle", handlers, dataHandlers, key)
return
}
func PipeRender(h *Handle, key string,
hooks map[string][]func(HandleCall) (HandleCall, bool),
handlers map[string][]HandleCall) []HandleCall {
hookedHandles := HookHandles(hooks, handlers, h.scene, h.Stats, constraints.AllScene, constraints.AllStats)
return h.PipeHandleHook("PipeRender", hookedHandles, handlers, key)
func PipeRender(h *Handle, renders map[string][]HandleCall, key string) (handlers []HandleCall) {
handlers = append(handlers, renders[h.Stats]...)
handlers = append(handlers, renders[h.scene]...)
handlers = append(handlers, renders[constraints.AllStats]...)
handlers = append(handlers, renders[constraints.AllScene]...)
handlers = h.PipeHandleHook("PipeRender", handlers, renders, key)
return
}
// DeleteHandle 写插件的时候用
func (h *Handle) DeleteHandle(pipeScene, scene, name string) {
v, ok := handleHooks.Load(pipeScene)
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
func (h *Handle) DeleteHandle(pipeScene string, name string) {
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
return call, name != call.Name
})
handleHooks.Store(pipeScene, v)
}
// ReplaceHandle 写插件的时候用
func (h *Handle) ReplaceHandle(pipeScene, scene, name string, fn HandleFn[*Handle]) {
v, ok := handleHooks.Load(pipeScene)
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], func(call HandleCall) (HandleCall, bool) {
func (h *Handle) ReplaceHandle(pipeScene, name string, fn HandleFn[*Handle]) {
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], func(call HandleCall) (HandleCall, bool) {
if name == call.Name {
call.Fn = fn
}
return call, true
})
handleHooks.Store(pipeScene, v)
}
// HookHandle 写插件的时候用
func (h *Handle) HookHandle(pipeScene, scene string, hook func(HandleCall) (HandleCall, bool)) {
v, ok := handleHooks.Load(pipeScene)
if !ok {
v = make(map[string][]func(HandleCall) (HandleCall, bool))
}
v[scene] = append(v[scene], hook)
handleHooks.Store(pipeScene, v)
func (h *Handle) HookHandle(pipeScene string, hook func(HandleCall) (HandleCall, bool)) {
h.handleHook[pipeScene] = append(h.handleHook[pipeScene], hook)
}
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) {
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeMiddleware, 300,
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
h.PushPipe(constraints.Home, NewPipe(constraints.PipeMiddleware, 300,
BuildPipe(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
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,
BuildHandlers(constraints.PipeRender, PipeKey, PipeRender)))
BuildPipe(constraints.PipeRender, PipeKey, PipeRender)))
}

View File

@ -1,16 +1,13 @@
package route
import (
"github.com/fthvgb1/wp-go/app/cmd/reload"
"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/safety"
"net/http"
"regexp"
)
// Route
// Type value equal const or reg
type Route struct {
Path string
Scene string
@ -23,18 +20,18 @@ var routeHook []func(Route) (Route, bool)
var regRoutes *safety.Map[string, *regexp.Regexp]
var routes = func() *safety.Map[string, Route] {
r := safety.NewMap[string, Route]()
reload.Append(func() {
reload.Push(func() {
r.Flush()
regRoutes.Flush()
}, "wp-routers")
})
regRoutes = safety.NewMap[string, *regexp.Regexp]()
return r
}()
// PushRoute path can be const or regex string
//
// eg: `(?P<controller>\w+)/(?P<method>\w+)`, route.Route{
// Path: `(?P<controller>\w+)/(?P<method>\w+)`,
// eg: `(?P<control>\w+)/(?P<method>\w+)`, route.Route{
// Path: `(?P<control>\w+)/(?P<method>\w+)`,
// Scene: constraints.Home,
// Method: []string{"GET"},
// Type: "reg",
@ -73,10 +70,11 @@ func Hook(path string, fn func(Route) Route) {
return r, path == r.Path
})
}
var RegRouteFn = reload.BuildValFnWithAnyParams("regexRoute", RegRouteHook)
func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp) {
func ResolveRoute(h *wp.Handle) {
requestURI := h.C.Request.RequestURI
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 {
@ -84,13 +82,9 @@ func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp)
if len(routeHook) > 0 {
for _, fn := range routeHook {
v, ok := fn(value)
if !ok {
continue
}
if ok {
m[v.Path] = v
if v.Type != "reg" {
continue
}
if v.Type == "reg" {
if v.Path != key {
vvv, err := regexp.Compile(v.Path)
if err != nil {
@ -100,47 +94,36 @@ func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp)
}
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) {
requestURI := h.C.Request.RequestURI
rs, rrs := RegRouteFn()()
})()
v, ok := rs[requestURI]
if ok && slice.IsContained(v.Method, h.C.Request.Method) {
h.SetScene(v.Scene)
wp.Run(h, nil)
h.Abort()
return
}
for path, reg := range rrs {
r := reg.FindStringSubmatch(requestURI)
r := reg.FindAllStringSubmatch(requestURI, -1)
if len(r) < 1 {
return
}
rr := rs[path]
if slice.IsContained(rr.Method, h.C.Request.Method) {
h.SetScene(rr.Scene)
for i, name := range reg.SubexpNames() {
if name == "" {
continue
}
h.C.AddParam(name, r[i])
}
h.C.Set("regRoute", reg)
h.C.Set("regRouteRes", r)
h.C.Set("route", r)
wp.Run(h, nil)
h.Abort()
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,18 +3,18 @@ package wp
import (
"fmt"
"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/logs"
"github.com/fthvgb1/wp-go/app/pkg/models"
"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"
str "github.com/fthvgb1/wp-go/helper/strings"
)
var GetStickPosts = reload.BuildValFnWithConfirm("stickPostsSlice", ParseStickPosts)
func ParseStickPosts(h *Handle) (r []models.Posts, ok bool) {
func (h *Handle) StickPosts() []models.Posts {
return reload.GetAnyValBys("stickPostsSlice", h, func(h *Handle) (r []models.Posts) {
v := wpconfig.GetOption("sticky_posts")
if v == "" {
return
@ -30,19 +30,18 @@ func ParseStickPosts(h *Handle) (r []models.Posts, ok bool) {
post.IsSticky = true
return post, err == nil
})
ok = true
return
})
}
var GetStickMapPosts = reload.BuildValFn("stickPostsMap", StickMapPosts)
func StickMapPosts(h *Handle) map[uint64]models.Posts {
return slice.SimpleToMap(GetStickPosts(h), func(v models.Posts) uint64 {
func (h *Handle) StickMapPosts() map[uint64]models.Posts {
return reload.GetAnyValBys("stickPostsMap", h, func(h *Handle) map[uint64]models.Posts {
return slice.SimpleToMap(h.StickPosts(), func(v models.Posts) uint64 {
return v.Id
})
})
}
func (h *Handle) IsStick(id uint64) bool {
_, ok := GetStickMapPosts(h)[id]
return ok
return maps.IsExists(h.StickMapPosts(), id)
}

View File

@ -1,6 +1,8 @@
{{define "common/head"}}
{{ callFuncString .calComponent "headScript"}}
{{if .headScript}}
{{.headScript|unescaped}}
{{end}}
{{if .externHead}}
{{.externHead|unescaped}}
@ -8,7 +10,9 @@
{{end}}
{{define "common/footer"}}
{{ callFuncString .calComponent "footerScript"}}
{{if .footerScript}}
{{.footerScript|unescaped}}
{{end}}
{{if .externFooter}}
{{.externFooter|unescaped}}
{{end}}
@ -16,17 +20,7 @@
{{define "common/sidebarWidget"}}
{{ callFuncString .calComponent "sidebarsWidgets"}}
{{end}}
{{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>
{{if .sidebarsWidgets}}
{{.sidebarsWidgets|unescaped}}
{{end}}
{{end}}

View File

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

View File

@ -2,11 +2,15 @@ package wpconfig
import (
"context"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"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/helper/maps"
"github.com/fthvgb1/wp-go/model"
"github.com/fthvgb1/wp-go/safety"
"os"
"path/filepath"
"strings"
)
@ -31,19 +35,41 @@ func InitOptions() error {
for _, option := range ops {
options.Store(option["k"], option["v"])
}
themeJson = reload.VarsBy(hasThemeJson)
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 {
v, ok := options.Load(k)
if ok {
return v
}
vv, err := model.GetField[models.Options](ctx, "option_value",
model.Conditions(
vv, err := model.GetField[models.Options](ctx, "option_value", model.Conditions(
model.Where(
model.SqlBuilder{{"option_name", k}},
),
model.SqlBuilder{{"option_name", k}}),
),
)
options.Store(k, vv)

View File

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

44
cache/cache.go vendored
View File

@ -2,49 +2,15 @@ package cache
import (
"context"
"sync"
"time"
)
type Cache[K comparable, V any] interface {
Get(ctx context.Context, key K) (V, bool)
Set(ctx context.Context, key K, val V)
GetExpireTime(ctx context.Context) time.Duration
Ttl(ctx context.Context, key K) time.Duration
Set(ctx context.Context, key K, val V, expire time.Duration)
Ttl(ctx context.Context, key K, expire time.Duration) time.Duration
Ver(ctx context.Context, key K) int
Flush(ctx context.Context)
Del(ctx context.Context, key ...K)
ClearExpired(ctx context.Context)
}
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)
Delete(ctx context.Context, key K)
ClearExpired(ctx context.Context, expire time.Duration)
}

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
}

580
cache/map.go vendored
View File

@ -4,200 +4,54 @@ import (
"context"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/cache/reload"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice"
"sync"
"time"
)
type MapCache[K comparable, V any] struct {
Cache[K, V]
mux *sync.Mutex
muFn func(ctx context.Context, gMut *sync.Mutex, k ...K) *sync.Mutex
cacheFunc MapSingleFn[K, V]
batchCacheFn MapBatchFn[K, V]
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)
data Cache[K, V]
mux sync.Mutex
cacheFunc func(...any) (V, error)
batchCacheFn func(...any) (map[K]V, error)
expireTime time.Duration
}
func (m *MapCache[K, V]) Get(ctx context.Context, key K) (V, bool) {
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]) {
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
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) {
tt := m.Ttl(ctx, k)
tt := m.data.Ttl(ctx, k, m.expireTime)
if tt <= 0 {
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
if m.cacheFunc == nil {
m.setDefaultCacheFn(fn)
m.setCacheFn(fn)
}
}
func (m *MapCache[K, V]) setDefaultCacheFn(fn MapBatchFn[K, V]) {
m.cacheFunc = func(ctx context.Context, k K, a ...any) (V, error) {
func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
m.cacheFunc = func(a ...any) (V, error) {
var err error
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 {
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) {
m.mux.Lock()
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) {
var err error
nowTime := time.Now()
if nowTime.Sub(m.GetLastSetTime(c, key)) < m.increaseUpdate.CycleTime() {
return data, err
func (m *MapCache[K, V]) Get(ctx context.Context, k K) (V, bool) {
return m.data.Get(ctx, k)
}
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]) Set(ctx context.Context, k K, v V) {
m.data.Set(ctx, k, v, m.expireTime)
}
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
if ok {
if m.increaseUpdate == nil || m.refresh == nil {
return data, err
}
return m.increaseUpdates(c, timeout, data, key, params...)
}
if !ok || m.data.Ttl(c, key, m.expireTime) <= 0 {
ver := m.data.Ver(c, key)
call := func() {
l := m.muFn(c, m.mux, key)
l.Lock()
defer l.Unlock()
if data, ok = m.Get(c, key); ok {
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(c, key, params...)
data, err = m.cacheFunc(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
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()))
case <-done:
}
} else {
call()
}
}
return data, err
}
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) {
if _, ok := m.data.Get(c, k); !ok {
return k, true
}
ver += m.data.Ver(c, k)
return
})
func (m *MapCache[K, V]) GetBatchToMap(c context.Context, key []K, timeout time.Duration, params ...any) (map[K]V, error) {
return m.getCacheBatchToMap(c, key, timeout, params...)
}
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
}
}
if len(needFlush) > 0 {
call := func() {
m.mux.Lock()
defer m.mux.Unlock()
mmm, er := e.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[k] = v
delete(needIndex, k)
}
if len(needIndex) < 1 {
vers := slice.Reduce(needFlush, func(t K, r int) int {
return r + m.data.Ver(c, t)
}, 0)
if vers > ver {
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)
}
}
}
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)
r, er := m.batchCacheFn(params...)
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
}
for k, v := range r {
m.Set(c, k, v)
}
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(ctx, timeout)
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
done := make(chan struct{}, 1)
go func() {
@ -569,13 +178,20 @@ func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, ke
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done:
}
} else {
call()
}
}
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 fn MapSingleFn[string, string]
var batchFn MapBatchFn[string, string]
var fn func(a ...any) (string, error)
var batchFn func(a ...any) (map[string]string, error)
var ct context.Context
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
}
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)
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
}), 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) {
type args struct {
@ -67,9 +72,7 @@ func TestMapCache_Flush(t *testing.T) {
m MapCache[K, V]
args args
}
ca := *NewMapCache[string, string](NewMemoryMapCache[string, string](func() time.Duration {
return time.Second
}), fn, nil, nil, nil)
ca := *NewMemoryMapCacheByFn[string, string](fn, time.Second)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
tests := []testCase[string, string]{
{
@ -290,7 +293,7 @@ func TestMapCache_Set(t *testing.T) {
func TestMapCache_SetCacheBatchFn(t *testing.T) {
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 {
name string
@ -312,19 +315,19 @@ func TestMapCache_SetCacheBatchFn(t *testing.T) {
}
func TestMapCache_SetCacheFunc(t *testing.T) {
type args[K comparable, V any] struct {
fn MapSingleFn[K, V]
type args[V any] struct {
fn func(...any) (V, error)
}
type testCase[K comparable, V any] struct {
name string
m MapCache[K, V]
args args[K, V]
args args[V]
}
tests := []testCase[string, string]{
{
name: "t1",
m: ca,
args: args[string, string]{fn: fn},
args: args[string]{fn: fn},
},
}
for _, tt := range tests {
@ -352,12 +355,12 @@ func TestMapCache_Ttl(t *testing.T) {
name: "t1",
m: ca,
args: args[string]{ct, "aa"},
want: ca.GetExpireTime(ct) - tx.Sub(txx),
want: ca.expireTime - tx.Sub(txx),
},
}
for _, tt := range tests {
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 {
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) {
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 {
name string
@ -384,7 +387,7 @@ func TestMapCache_setCacheFn(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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"))
})
}

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