Compare commits
No commits in common. "master" and "newcache" have entirely different histories.
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,7 +1,3 @@
|
|||
.idea
|
||||
/wp-go.iml
|
||||
/config.yaml
|
||||
err.log
|
||||
/plugins/
|
||||
/config.json
|
||||
go.sum
|
||||
wp-go.iml
|
||||
config.yaml
|
|
@ -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
|
||||
|
|
84
README.md
84
README.md
|
@ -1,87 +1,19 @@
|
|||
## wp-go
|
||||
a simple front of WordPress build with golang.
|
||||
|
||||
[en readme](https://github.com/fthvgb1/wp-go/blob/master/readme_en.md)
|
||||
|
||||
一个go写的WordPress的前端,功能比较简单,只有列表页和详情页,rss2,主题只有twentyfifteen和twentyseventeen两套主题,插件的话只有一个简单的列表页的摘要生成和enlighter代码高亮。本身只用于展示文章及评论。要求go的版本在1.20以上,越新越好。。。
|
||||
一个go写的WordPress的前端,功能比较简单,只有列表页和详情页,rss2,主题只有twentyfifteen和twentyseventeen两套主题,插件的话只有一个简单的列表页的摘要生成和enlighter代码高亮。本身只用于展示文章,添加评论走的转发请求到php的WordPress。因为大量用了泛型功能,所以要求go的版本在1.18及以上。
|
||||
|
||||
#### 特色功能
|
||||
|
||||
- 基本实现全站缓存,并且可防止缓存击穿
|
||||
- 列表页也可以高亮语法格式化显示代码
|
||||
- 简易插件扩展开发机制、配置后支持热加载更新
|
||||
- 使用.so扩展主题、插件、路由等
|
||||
- 丰富繁杂的配置,呃,配置是有点儿多,虽然大部分都是可选项。。。
|
||||
- 多种缓存配置
|
||||
- 添加评论或panic时发邮件通知,包涵栈调用和请求信息
|
||||
- 简单的流量限制中间件,可以限制全瞬时最大请求数量
|
||||
- 简单的流量限制中间件
|
||||
- 除配置文件外将所有静态资源都打包到执行文件中
|
||||
- 支持密码查看,且cookie信息可被php版所验证
|
||||
- 支持rss2订阅
|
||||
- 热更新配置、切换主题、清空缓存
|
||||
- kill -SIGUSR1 PID 更新配置和清空缓存
|
||||
- kill -SIGUSR2 PID 清空缓存
|
||||
|
||||
#### 运行
|
||||
```
|
||||
go run app/cmd/main.go [-c configpath] [-p port]
|
||||
```
|
||||
|
||||
#### 数据显示支持程度
|
||||
|
||||
| 页表 | 支持程度 |
|
||||
|-----|---------------------------------------------|
|
||||
| 列表页 | 首页/搜索/归档/分类/标签/作者 分页列表 |
|
||||
| 详情页 | 显示内容、评论并可以添加评论(转发的php处理,需要配置php版的添加评论的url) |
|
||||
| 侧边栏 | 支持旧版 近期文章、近期评论、规档、分类、其它操作 显示及设置, 支持新版 分类 |
|
||||
|
||||
#### 后台设置支持程度
|
||||
|
||||
- 仪表盘
|
||||
- 外观
|
||||
- 小工具
|
||||
- 搜索
|
||||
- 规档
|
||||
- 近期文章
|
||||
- 近期评论
|
||||
- 分类
|
||||
- 其它操作
|
||||
|
||||
- 设置-
|
||||
- 常规
|
||||
- 站点标题
|
||||
- 副标题
|
||||
- 阅读
|
||||
- 博客页面至多显示数量
|
||||
- Feed中显示最近数量
|
||||
- 讨论
|
||||
- 其他评论设置
|
||||
- `启用|禁止`评论嵌套,最多嵌套层数
|
||||
- 分页显示评论,每页显示评论条数,默认显示`最前/后`页
|
||||
- 在每个页面顶部显示 `新旧`评论
|
||||
|
||||
#### 主题支持程度
|
||||
|
||||
| twentyfifteen | twentyseventeen |
|
||||
|---------------|-----------------|
|
||||
| 站点身份 | 站点身份 |
|
||||
| 颜色 | 颜色 |
|
||||
| 页眉图片 | 页眉媒体 |
|
||||
| 背景图片 | 额外css |
|
||||
| 额外css | |
|
||||
|
||||
#### 插件机制
|
||||
|
||||
分为对列表页文章数据的修改的插件和对影响整个程序表现的插件
|
||||
|
||||
| 列表页文章数据插件 | 整个程序表现的插件 |
|
||||
|---------------------|--------------------------------------|
|
||||
| digest 自动生成指定长度的摘要 | enlighter 代码高亮(需要在后台安装enlighterjs插件) |
|
||||
| | hiddenLogin 隐藏登录入口 |
|
||||
- 热更新配置、清空缓存
|
||||
- kill -SIGUSR1 PID 更新配置和清空缓存
|
||||
- kill -SIGUSR2 PID 清空缓存
|
||||
|
||||
#### 其它
|
||||
|
||||
用的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>
|
||||
|
||||
用的gin框架和sqlx,在外面封装了层查询的方法。后台可以设置的比较少,大部分设置还没打通。
|
|
@ -1,293 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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/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"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CommentForm struct {
|
||||
CommentPostId uint64 `form:"comment_post_ID" binding:"required" json:"comment_post_ID"`
|
||||
Author string `form:"author" binding:"required" label:"显示名称" json:"author"`
|
||||
Email string `form:"email" binding:"required,email"`
|
||||
Comment string `form:"comment" binding:"required" label:"评论" json:"comment"`
|
||||
}
|
||||
|
||||
func PostComment(c *gin.Context) {
|
||||
cli := &http.Client{
|
||||
Timeout: time.Second * 3,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
data, err := c.GetRawData()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.Writer.WriteHeader(http.StatusConflict)
|
||||
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
var v validator.ValidationErrors
|
||||
if errors.As(err, &v) {
|
||||
e := v.Translate(config.GetZh())
|
||||
for _, v := range e {
|
||||
fmt.Fprintf(c.Writer, fail, v)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Writer.WriteString("评论出错,请联系管理员或稍后再度")
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
conf := config.GetConfig()
|
||||
if err != nil {
|
||||
logs.Error(err, "获取评论数据错误")
|
||||
return
|
||||
}
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(data))
|
||||
var comment CommentForm
|
||||
if err = c.ShouldBind(&comment); err != nil {
|
||||
return
|
||||
}
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(data))
|
||||
req, err := http.NewRequest("POST", conf.PostCommentUrl, strings.NewReader(c.Request.PostForm.Encode()))
|
||||
if err != nil {
|
||||
logs.Error(err, "创建评论请求错误")
|
||||
return
|
||||
}
|
||||
defer req.Body.Close()
|
||||
req.Header = c.Request.Header.Clone()
|
||||
home, err := url.Parse(wpconfig.GetOption("siteurl"))
|
||||
if err != nil {
|
||||
logs.Error(err, "解析评论接口错误")
|
||||
return
|
||||
}
|
||||
req.Host = home.Host
|
||||
res, err := cli.Do(req)
|
||||
if err != nil && !errors.Is(err, http.ErrUseLastResponse) {
|
||||
logs.Error(err, "请求评论接口错误")
|
||||
return
|
||||
}
|
||||
if res.StatusCode == http.StatusFound {
|
||||
for _, cookie := range res.Cookies() {
|
||||
c.SetCookie(cookie.Name, cookie.Value, cookie.MaxAge, cookie.Path, cookie.Domain, cookie.Secure, cookie.HttpOnly)
|
||||
}
|
||||
u := res.Header.Get("Location")
|
||||
up, er := url.Parse(u)
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
cu, er := url.Parse(conf.PostCommentUrl)
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
up.Host = cu.Host
|
||||
up.Scheme = "http"
|
||||
newReq, _ := http.NewRequest("GET", up.String(), nil)
|
||||
newReq.Host = home.Host
|
||||
newReq.Header.Set("Cookie", strings.Join(slice.Map(c.Request.Cookies(), func(t *http.Cookie) string {
|
||||
return fmt.Sprintf("%s=%s", t.Name, t.Value)
|
||||
}), "; "))
|
||||
ress, er := http.DefaultClient.Do(newReq)
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
cc := c.Copy()
|
||||
go func() {
|
||||
if gin.Mode() != gin.ReleaseMode {
|
||||
return
|
||||
}
|
||||
id := comment.CommentPostId
|
||||
if id <= 0 {
|
||||
logs.Error(errors.New("获取文档id错误"), "", comment.CommentPostId)
|
||||
return
|
||||
}
|
||||
post, err := cache.GetPostById(cc, id)
|
||||
if err != nil {
|
||||
logs.Error(err, "获取文档错误", id)
|
||||
return
|
||||
}
|
||||
su := fmt.Sprintf("%s: %s[%s]发表了评论对文档[%v]的评论", wpconfig.GetOption("siteurl"), comment.Author, comment.Email, post.PostTitle)
|
||||
err = mail.SendMail([]string{conf.Mail.User}, su, comment.Comment)
|
||||
logs.IfError(err, "发送邮件", conf.Mail.User, su, comment)
|
||||
}()
|
||||
|
||||
s, er := io.ReadAll(ress.Body)
|
||||
if er != nil {
|
||||
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)
|
||||
return
|
||||
}
|
||||
var r io.Reader
|
||||
if res.Header.Get("Content-Encoding") == "gzip" {
|
||||
r, err = gzip.NewReader(res.Body)
|
||||
if err != nil {
|
||||
logs.Error(err, "gzip解压错误")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
r = res.Body
|
||||
}
|
||||
s, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
logs.Error(err, "读取结果错误")
|
||||
return
|
||||
}
|
||||
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
c.Writer.WriteHeader(res.StatusCode)
|
||||
_, _ = c.Writer.Write(s)
|
||||
|
||||
}
|
||||
|
||||
var fail = `
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name='robots' content='max-image-preview:large, noindex, follow' />
|
||||
<title>评论提交失败</title>
|
||||
<style type="text/css">
|
||||
html {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
color: #444;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
margin: 2em auto;
|
||||
padding: 1em 2em;
|
||||
max-width: 700px;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
|
||||
}
|
||||
h1 {
|
||||
border-bottom: 1px solid #dadada;
|
||||
clear: both;
|
||||
color: #666;
|
||||
font-size: 24px;
|
||||
margin: 30px 0 0 0;
|
||||
padding: 0;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
#error-page {
|
||||
margin-top: 50px;
|
||||
}
|
||||
#error-page p,
|
||||
#error-page .wp-die-message {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
margin: 25px 0 20px;
|
||||
}
|
||||
#error-page code {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
}
|
||||
ul li {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px ;
|
||||
}
|
||||
a {
|
||||
color: #0073aa;
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
color: #006799;
|
||||
}
|
||||
a:focus {
|
||||
color: #124964;
|
||||
-webkit-box-shadow:
|
||||
0 0 0 1px #5b9dd9,
|
||||
0 0 2px 1px rgba(30, 140, 190, 0.8);
|
||||
box-shadow:
|
||||
0 0 0 1px #5b9dd9,
|
||||
0 0 2px 1px rgba(30, 140, 190, 0.8);
|
||||
outline: none;
|
||||
}
|
||||
.button {
|
||||
background: #f3f5f6;
|
||||
border: 1px solid #016087;
|
||||
color: #016087;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
height: 28px;
|
||||
margin: 0;
|
||||
padding: 0 10px 1px;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 3px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.button.button-large {
|
||||
line-height: 2.30769231;
|
||||
min-height: 32px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.button:focus {
|
||||
background: #f3f5f6;
|
||||
border-color: #007cba;
|
||||
-webkit-box-shadow: 0 0 0 1px #007cba;
|
||||
box-shadow: 0 0 0 1px #007cba;
|
||||
color: #016087;
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 0;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
background: #f3f5f6;
|
||||
border-color: #7e8993;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body id="error-page">
|
||||
<div class="wp-die-message"><p><strong>错误:</strong>%s</p></div>
|
||||
<p><a href='javascript:history.back()'>« 返回</a></p></body>
|
||||
</html>
|
||||
|
||||
`
|
|
@ -1,108 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
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")
|
||||
cTag := c.Request.Header.Get("If-None-Match")
|
||||
if since != "" && cTag != "" {
|
||||
cGMT, err := time.Parse(tmp, since)
|
||||
if err == nil && lastTime.Unix() <= cGMT.Unix() && eTag == cTag {
|
||||
c.Status(http.StatusNotModified)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SiteFeed(c *gin.Context) {
|
||||
feed := cache.FeedCache()
|
||||
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||
c.Status(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
|
||||
r, err := feed.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))
|
||||
}
|
||||
|
||||
func setFeed(s string, c *gin.Context, t time.Time) {
|
||||
lastTimeGMT := t.Format(tmp)
|
||||
eTag := str.Md5(lastTimeGMT)
|
||||
c.Header("Content-Type", "application/rss+xml; charset=UTF-8")
|
||||
c.Header("Last-Modified", lastTimeGMT)
|
||||
c.Header("ETag", eTag)
|
||||
c.String(http.StatusOK, s)
|
||||
}
|
||||
|
||||
func PostFeed(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
postFeed := cache.PostFeedCache()
|
||||
if !isCacheExpired(c, postFeed.GetLastSetTime(c, id)) {
|
||||
c.Status(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
s, err := postFeed.GetCache(c, id, time.Second)
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.Abort()
|
||||
c.Error(err)
|
||||
return
|
||||
}
|
||||
setFeed(s, c, postFeed.GetLastSetTime(c, id))
|
||||
}
|
||||
|
||||
func CommentsFeed(c *gin.Context) {
|
||||
feed := cache.CommentsFeedCache()
|
||||
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||
c.Status(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
r, err := feed.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))
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/theme"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ThemeHook(scene string) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
t := theme.GetCurrentTheme()
|
||||
h := wp.NewHandle(c, scene, t)
|
||||
theme.Hook(t, h)
|
||||
}
|
||||
}
|
111
app/cmd/main.go
111
app/cmd/main.go
|
@ -1,111 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/ossigns"
|
||||
"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/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var confPath string
|
||||
var address string
|
||||
var intReg = regexp.MustCompile(`^\d`)
|
||||
|
||||
func inits() {
|
||||
flag.StringVar(&confPath, "c", "config.yaml", "config file support json,yaml or url")
|
||||
flag.StringVar(&address, "p", "", "listen address and port")
|
||||
flag.Parse()
|
||||
if address == "" && os.Getenv("PORT") == "" {
|
||||
address = "80"
|
||||
}
|
||||
if intReg.MatchString(address) && !strings.Contains(address, ":") {
|
||||
address = ":" + address
|
||||
}
|
||||
err := initConf(confPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cache.InitActionsCommonCache()
|
||||
plugins.InitDigestCache()
|
||||
theme.InitTheme()
|
||||
go cronClearCache()
|
||||
}
|
||||
|
||||
func initConf(c string) (err error) {
|
||||
err = config.InitConfig(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = config.InitTrans()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = logs.InitLogger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
database, err := db.InitDb()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
model.InitDB(db.QueryDb(database))
|
||||
err = wpconfig.InitOptions()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = wpconfig.InitTerms()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wphandle.LoadPlugins()
|
||||
return
|
||||
}
|
||||
|
||||
func cronClearCache() {
|
||||
t := time.NewTicker(config.GetConfig().CacheTime.CrontabClearCacheTime)
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
cachemanager.ClearExpired()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println(r)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
inits()
|
||||
ossigns.SetConfPath(confPath)
|
||||
go ossigns.SignalNotify()
|
||||
Gin := route.SetupRouter()
|
||||
c := config.GetConfig()
|
||||
if c.Ssl.Key != "" && c.Ssl.Cert != "" {
|
||||
err := Gin.RunTLS(address, c.Ssl.Cert, c.Ssl.Key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
err := Gin.Run(address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"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() {
|
||||
reFn(config.GetConfig().SingleIpSearchNum)
|
||||
}, "search-ip-limit-number")
|
||||
return func(c *gin.Context) {
|
||||
if c.Query("s") != "" {
|
||||
fn(c)
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var path = map[string]struct{}{
|
||||
"wp-includes": {},
|
||||
"wp-content": {},
|
||||
"favicon.ico": {},
|
||||
}
|
||||
|
||||
func SetStaticFileCache(c *gin.Context) {
|
||||
f := strings.Split(strings.TrimLeft(c.FullPath(), "/"), "/")
|
||||
if _, ok := path[f[0]]; ok {
|
||||
if ".php" == filepath.Ext(c.Request.URL.Path) {
|
||||
c.Abort()
|
||||
c.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
t := config.GetConfig().CacheTime.CacheControl
|
||||
if t > 0 {
|
||||
c.Header("Cache-Control", fmt.Sprintf("private, max-age=%d", int(t.Seconds())))
|
||||
}
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ValidateServerNames() func(ctx *gin.Context) {
|
||||
sites := reload.VarsBy(func() map[string]struct{} {
|
||||
r := config.GetConfig().TrustServerNames
|
||||
m := map[string]struct{}{}
|
||||
if len(r) > 0 {
|
||||
for _, name := range r {
|
||||
m[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}, "site-names")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
m := sites.Load()
|
||||
if len(m) > 0 && !maps.IsExists(m, strings.Split(c.Request.Host, ":")[0]) {
|
||||
c.Status(http.StatusForbidden)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package phphelper
|
||||
|
||||
import (
|
||||
"github.com/elliotchance/phpserialize"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
)
|
||||
|
||||
// UnPHPSerializeToStruct 使用 json tag
|
||||
func UnPHPSerializeToStruct[T any](s string) (r T, err error) {
|
||||
var rr map[any]any
|
||||
err = phpserialize.Unmarshal([]byte(s), &rr)
|
||||
if err == nil {
|
||||
rx := maps.AnyAnyToStrAny(rr)
|
||||
r, err = maps.StrAnyMapToStruct[T](rx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func UnPHPSerializeToStrAnyMap(s string) (map[string]any, error) {
|
||||
m := map[string]any{}
|
||||
var r map[any]any
|
||||
err := phpserialize.Unmarshal([]byte(s), &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m = maps.AnyAnyToStrAny(r)
|
||||
return m, err
|
||||
}
|
||||
func UnPHPSerializeToAnyAnyMap(s string) (map[any]any, error) {
|
||||
var r map[any]any
|
||||
err := phpserialize.Unmarshal([]byte(s), &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, err
|
||||
}
|
126
app/pkg/cache/cache.go
vendored
126
app/pkg/cache/cache.go
vendored
|
@ -1,126 +0,0 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InitActionsCommonCache() {
|
||||
c := config.GetConfig()
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.SearchPostCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.PostListCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.MonthPostCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.ContextPostCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.PostDataCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.PostDataCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.CategoryCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewVarMemoryCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime, "recentPosts", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.RecentPostCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewVarMemoryCache(RecentComment, c.CacheTime.RecentCommentsCacheTime, "recentComments", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.RecentCommentsCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.CommentNum, 30*time.Second, "commentNumber", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, time.Hour, "postCommentData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.CommentsCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(dao.CommentChildren, nil, time.Minute, "commentChildren", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.CommentsIncreaseUpdateTime
|
||||
})
|
||||
|
||||
cachemanager.NewVarMemoryCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime, "maxPostId", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.MaxPostIdCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameToUserData", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewVarMemoryCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime, "allUsername", func() time.Duration {
|
||||
return config.GetConfig().CacheTime.UserInfoCacheTime
|
||||
})
|
||||
|
||||
cachemanager.NewVarMemoryCache(SiteFeed, time.Hour, "siteFeed")
|
||||
|
||||
cachemanager.NewMemoryMapCache(nil, PostFeed, time.Hour, "postFeed")
|
||||
|
||||
cachemanager.NewVarMemoryCache(CommentsFeed, time.Hour, "commentsFeed")
|
||||
|
||||
cachemanager.NewMemoryMapCache[string, string](nil, nil, 15*time.Minute, "NewComment")
|
||||
|
||||
InitFeed()
|
||||
}
|
||||
|
||||
type Arch struct {
|
||||
data []models.PostArchive
|
||||
fn func(context.Context) ([]models.PostArchive, error)
|
||||
month time.Month
|
||||
}
|
||||
|
||||
var arch = reload.Vars(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 {
|
||||
r, err := a.fn(ctx)
|
||||
if err != nil {
|
||||
logs.Error(err, "set cache Archives fail")
|
||||
return nil
|
||||
}
|
||||
a.month = m
|
||||
a.data = r
|
||||
arch.Store(a)
|
||||
data = r
|
||||
}
|
||||
return data
|
||||
}
|
39
app/pkg/cache/categoryandtag.go
vendored
39
app/pkg/cache/categoryandtag.go
vendored
|
@ -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)
|
||||
}
|
124
app/pkg/cache/comments.go
vendored
124
app/pkg/cache/comments.go
vendored
|
@ -1,124 +0,0 @@
|
|||
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)
|
||||
if len(r) > n {
|
||||
r = r[0:n]
|
||||
}
|
||||
logs.IfError(err, "get recent comment fail")
|
||||
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...)
|
||||
}
|
||||
if key == "" {
|
||||
key = fmt.Sprintf("%d-%d-%d-%d-%s", postId, page, limit, total, order)
|
||||
}
|
||||
return cachemanager.GetBy[[]uint64]("PostCommentsIds", ctx,
|
||||
key, time.Second, postId, page, limit, 0, order)
|
||||
}
|
||||
|
||||
// GetCommentById query func see dao.GetCommentByIds
|
||||
func GetCommentById(ctx context.Context, id uint64) (models.Comments, error) {
|
||||
return cachemanager.GetBy[models.Comments]("postCommentData", ctx, id, time.Second)
|
||||
}
|
||||
|
||||
// GetCommentDataByIds query func see dao.GetCommentByIds
|
||||
func GetCommentDataByIds(ctx context.Context, ids []uint64) ([]models.Comments, error) {
|
||||
return cachemanager.GetBatchBy[models.Comments]("postCommentData", ctx, ids, time.Second)
|
||||
}
|
||||
|
||||
func NewCommentCache() *cache.MapCache[string, string] {
|
||||
r, _ := cachemanager.GetMapCache[string, string]("NewComment")
|
||||
return r
|
||||
}
|
||||
|
||||
func PostTopComments(ctx context.Context, _ string, a ...any) ([]uint64, error) {
|
||||
postId := a[0].(uint64)
|
||||
page := a[1].(int)
|
||||
limit := a[2].(int)
|
||||
total := a[3].(int)
|
||||
order := helper.ParseArgs("", a...)
|
||||
if order == "" {
|
||||
order = wpconfig.GetOption("comment_order")
|
||||
}
|
||||
v, _, err := dao.PostCommentsIds(ctx, postId, page, limit, total, order)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
17
app/pkg/cache/postmeta.go
vendored
17
app/pkg/cache/postmeta.go
vendored
|
@ -1,17 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
91
app/pkg/cache/posts.go
vendored
91
app/pkg/cache/posts.go
vendored
|
@ -1,91 +0,0 @@
|
|||
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"
|
||||
"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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
total = ids.Length
|
||||
r, err = GetPostsByIds(ctx, ids.Ids)
|
||||
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...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
total = ids.Length
|
||||
r, err = GetPostsByIds(ctx, ids.Ids)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMaxPostId query func see dao.GetMaxPostId
|
||||
func GetMaxPostId(ctx context.Context) (uint64, error) {
|
||||
return cachemanager.GetVarVal[uint64]("maxPostId", ctx, time.Second)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if n < len(r) {
|
||||
r = r[:n]
|
||||
}
|
||||
logs.IfError(err, "get recent post")
|
||||
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)
|
||||
if err != nil {
|
||||
return models.Posts{}, models.Posts{}, err
|
||||
}
|
||||
prev = postCtx.Prev
|
||||
next = postCtx.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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if order == "desc" {
|
||||
res = slice.Reverse(res)
|
||||
}
|
||||
total = len(res)
|
||||
rr := slice.Pagination(res, page, limit)
|
||||
r, err = GetPostsByIds(ctx, rr)
|
||||
return
|
||||
}
|
26
app/pkg/cache/users.go
vendored
26
app/pkg/cache/users.go
vendored
|
@ -1,26 +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"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetUserByName query func see dao.GetUserByName
|
||||
func GetUserByName(ctx context.Context, username string) (models.Users, error) {
|
||||
return cachemanager.GetBy[models.Users]("usernameToUserData", ctx, username, time.Second)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
logs.IfError(err, "get user", uid)
|
||||
return r
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var config safety.Var[Config]
|
||||
|
||||
func GetConfig() Config {
|
||||
return config.Load()
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Ssl Ssl `yaml:"ssl" json:"ssl"`
|
||||
Mysql Mysql `yaml:"mysql" json:"mysql"`
|
||||
Mail Mail `yaml:"mail" json:"mail"`
|
||||
CacheTime CacheTime `yaml:"cacheTime" json:"cacheTime"`
|
||||
PluginPath string `yaml:"pluginPath" json:"pluginPath"`
|
||||
ExternScript []string `json:"externScript" yaml:"externScript"`
|
||||
DigestWordCount int `yaml:"digestWordCount" json:"digestWordCount,omitempty"`
|
||||
DigestAllowTag string `yaml:"digestAllowTag" json:"digestAllowTag"`
|
||||
MaxRequestSleepNum int64 `yaml:"maxRequestSleepNum" json:"maxRequestSleepNum,omitempty"`
|
||||
MaxRequestNum int64 `yaml:"maxRequestNum" json:"maxRequestNum,omitempty"`
|
||||
SingleIpSearchNum int64 `yaml:"singleIpSearchNum" json:"singleIpSearchNum,omitempty"`
|
||||
Gzip bool `yaml:"gzip" json:"gzip,omitempty"`
|
||||
PostCommentUrl string `yaml:"postCommentUrl" json:"postCommentUrl,omitempty"`
|
||||
TrustIps []string `yaml:"trustIps" json:"trustIps,omitempty"`
|
||||
TrustServerNames []string `yaml:"trustServerNames" json:"trustServerNames,omitempty"`
|
||||
Theme string `yaml:"theme" json:"theme,omitempty"`
|
||||
PostOrder string `yaml:"postOrder" json:"postOrder,omitempty"`
|
||||
UploadDir string `yaml:"uploadDir" json:"uploadDir,omitempty"`
|
||||
Pprof string `yaml:"pprof" json:"pprof,omitempty"`
|
||||
ListPagePlugins []string `yaml:"listPagePlugins" json:"listPagePlugins,omitempty"`
|
||||
PaginationStep int `yaml:"paginationStep" json:"paginationStep,omitempty"`
|
||||
ShowQuerySql bool `yaml:"showQuerySql" json:"showQuerySql,omitempty"`
|
||||
Plugins []string `yaml:"plugins" json:"plugins,omitempty"`
|
||||
LogOutput string `yaml:"logOutput" json:"logOutput,omitempty"`
|
||||
WpDir string `yaml:"wpDir" json:"wpDir"`
|
||||
}
|
||||
|
||||
type CacheTime struct {
|
||||
CacheControl time.Duration `yaml:"cacheControl" json:"cacheControl,omitempty"`
|
||||
RecentPostCacheTime time.Duration `yaml:"recentPostCacheTime" json:"recentPostCacheTime,omitempty"`
|
||||
CategoryCacheTime time.Duration `yaml:"categoryCacheTime" json:"categoryCacheTime,omitempty"`
|
||||
ArchiveCacheTime time.Duration `yaml:"archiveCacheTime" json:"archiveCacheTime,omitempty"`
|
||||
ContextPostCacheTime time.Duration `yaml:"contextPostCacheTime" json:"contextPostCacheTime,omitempty"`
|
||||
RecentCommentsCacheTime time.Duration `yaml:"recentCommentsCacheTime" json:"recentCommentsCacheTime,omitempty"`
|
||||
DigestCacheTime time.Duration `yaml:"digestCacheTime" json:"digestCacheTime,omitempty"`
|
||||
PostListCacheTime time.Duration `yaml:"postListCacheTime" json:"postListCacheTime,omitempty"`
|
||||
SearchPostCacheTime time.Duration `yaml:"searchPostCacheTime" json:"searchPostCacheTime,omitempty"`
|
||||
MonthPostCacheTime time.Duration `yaml:"monthPostCacheTime" json:"monthPostCacheTime,omitempty"`
|
||||
PostDataCacheTime time.Duration `yaml:"postDataCacheTime" json:"postDataCacheTime,omitempty"`
|
||||
PostCommentsCacheTime time.Duration `yaml:"postCommentsCacheTime" json:"postCommentsCacheTime,omitempty"`
|
||||
CrontabClearCacheTime time.Duration `yaml:"crontabClearCacheTime" json:"crontabClearCacheTime,omitempty"`
|
||||
MaxPostIdCacheTime time.Duration `yaml:"maxPostIdCacheTime" json:"maxPostIdCacheTime,omitempty"`
|
||||
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 {
|
||||
Cert string `yaml:"cert" json:"cert,omitempty"`
|
||||
Key string `yaml:"key" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
type Mail struct {
|
||||
User string `yaml:"user" json:"user,omitempty"`
|
||||
Alias string `yaml:"alias" json:"alias,omitempty"`
|
||||
Pass string `yaml:"pass" json:"pass,omitempty"`
|
||||
Host string `yaml:"host" json:"host,omitempty"`
|
||||
Port int `yaml:"port" json:"port,omitempty"`
|
||||
InsecureSkipVerify bool `yaml:"insecureSkipVerify" json:"insecureSkipVerify,omitempty"`
|
||||
}
|
||||
|
||||
type Mysql struct {
|
||||
Dsn Dsn `yaml:"dsn" json:"dsn"`
|
||||
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"
|
||||
}
|
||||
var file []byte
|
||||
var err error
|
||||
if strings.Contains(conf, "http") {
|
||||
get, err := http.Get(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer get.Body.Close()
|
||||
file, err = io.ReadAll(get.Body)
|
||||
} else {
|
||||
file, err = os.ReadFile(conf)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileData.Store(file)
|
||||
var c Config
|
||||
err = yaml.Unmarshal(file, &c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Store(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Dsn struct {
|
||||
Host string `yaml:"host" json:"host,omitempty"`
|
||||
Port string `yaml:"port" json:"port,omitempty"`
|
||||
Db string `yaml:"db" json:"db,omitempty"`
|
||||
User string `yaml:"user" json:"user,omitempty"`
|
||||
Password string `yaml:"password" json:"password,omitempty"`
|
||||
Charset string `yaml:"charset" json:"charset,omitempty"`
|
||||
}
|
||||
|
||||
func (m Dsn) GetDsn() string {
|
||||
if m.Charset == "" {
|
||||
m.Charset = "utf8"
|
||||
}
|
||||
t := "%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local"
|
||||
return fmt.Sprintf(t, m.User, m.Password, m.Host, m.Port, m.Db, m.Charset)
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
ConnMaxIdleTime time.Duration `yaml:"connMaxIdleTime" json:"connMaxIdleTime,omitempty"`
|
||||
MaxOpenConn int `yaml:"maxOpenConn" json:"maxOpenConn,omitempty"`
|
||||
MaxIdleConn int `yaml:"maxIdleConn" json:"maxIdleConn,omitempty"`
|
||||
ConnMaxLifetime time.Duration `yaml:"connMaxLifetime" json:"connMaxLifetime,omitempty"`
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-playground/locales/en"
|
||||
"github.com/go-playground/locales/zh"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
enTrans "github.com/go-playground/validator/v10/translations/en"
|
||||
zhTrans "github.com/go-playground/validator/v10/translations/zh"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var enT ut.Translator
|
||||
var zhT ut.Translator
|
||||
|
||||
func GetZh() ut.Translator {
|
||||
return zhT
|
||||
}
|
||||
func GetEn() ut.Translator {
|
||||
return enT
|
||||
}
|
||||
|
||||
func InitTrans() error {
|
||||
if validate, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
ens := en.New()
|
||||
uni := ut.New(ens, zh.New(), ens)
|
||||
zhT, _ = uni.GetTranslator("zh")
|
||||
enT, _ = uni.GetTranslator("en")
|
||||
err := enTrans.RegisterDefaultTranslations(validate, enT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
return field.Tag.Get("label")
|
||||
})
|
||||
err = zhTrans.RegisterDefaultTranslations(validate, zhT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package blocks
|
||||
|
||||
const (
|
||||
Search = "block-search"
|
||||
RecentPosts = "block-recent-posts"
|
||||
RecentComments = "block-recent-comments"
|
||||
Archive = "block-archives"
|
||||
Categories = "block-categories"
|
||||
Meta = "block-meta"
|
||||
)
|
|
@ -1,30 +0,0 @@
|
|||
package constraints
|
||||
|
||||
const (
|
||||
Home = "Home"
|
||||
Archive = "Archive"
|
||||
Category = "Category"
|
||||
Tag = "Tag"
|
||||
Search = "Search"
|
||||
Author = "Author"
|
||||
Detail = "Detail"
|
||||
|
||||
NoRoute = "NoRoute"
|
||||
|
||||
Ok = "Ok"
|
||||
Error404 = "Error404"
|
||||
ParamError = "ParamError"
|
||||
InternalErr = "InternalErr"
|
||||
AllStats = "AllStats"
|
||||
AllScene = "AllScene"
|
||||
|
||||
PipeData = "PipeData"
|
||||
PipeMiddleware = "PipeMiddleware"
|
||||
PipeRender = "PipeRender"
|
||||
|
||||
Defaults = "default"
|
||||
|
||||
HeadScript = "headScript"
|
||||
FooterScript = "footerScript"
|
||||
SidebarsWidgets = "sidebarsWidgets"
|
||||
)
|
|
@ -1,12 +0,0 @@
|
|||
package widgets
|
||||
|
||||
const (
|
||||
Search = "widget-search"
|
||||
RecentPosts = "widget-recent-posts"
|
||||
RecentComments = "widget-recent-comments"
|
||||
Archive = "widget-archives"
|
||||
Categories = "widget-categories"
|
||||
Meta = "widget-meta"
|
||||
|
||||
Widget = "widget"
|
||||
)
|
|
@ -1,183 +0,0 @@
|
|||
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...)
|
||||
return model.Finds[models.Comments](ctx, model.Conditions(
|
||||
model.Where(model.SqlBuilder{
|
||||
{"comment_approved", "1"},
|
||||
{"post_status", "publish"},
|
||||
}),
|
||||
model.Fields("comment_ID,comment_author,comment_post_ID,post_title"),
|
||||
model.Order(model.SqlBuilder{{"comment_date_gmt", "desc"}}),
|
||||
model.Join(model.SqlBuilder{{"a", "left join", "wp_posts b", "a.comment_post_ID=b.ID"}}),
|
||||
model.Limit(n),
|
||||
))
|
||||
}
|
||||
|
||||
// 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(
|
||||
model.Where(model.SqlBuilder{
|
||||
{"comment_approved", "1"},
|
||||
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
||||
}),
|
||||
model.Fields("comment_ID"),
|
||||
model.Order(model.SqlBuilder{
|
||||
{"comment_date_gmt", "asc"},
|
||||
{"comment_ID", "asc"},
|
||||
})),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return slice.Map(r, func(t models.Comments) uint64 {
|
||||
return t.CommentId
|
||||
}), err
|
||||
}
|
||||
|
||||
func GetCommentByIds(ctx context.Context, ids []uint64, _ ...any) (map[uint64]models.Comments, error) {
|
||||
if len(ids) < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
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{
|
||||
{"comment_ID", "in", ""}, {"comment_approved", "1"},
|
||||
}),
|
||||
model.Fields("*"),
|
||||
model.In(slice.ToAnySlice(id)),
|
||||
))
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
for _, comments := range r {
|
||||
m[comments.CommentId] = comments
|
||||
}
|
||||
off += 500
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func CommentNum(ctx context.Context, postId uint64, _ ...any) (int, error) {
|
||||
n, err := model.GetField[models.Posts](ctx, "comment_count", model.Conditions(
|
||||
model.Where(model.SqlBuilder{{"ID", "=", number.IntToString(postId), "int"}})))
|
||||
if err != nil {
|
||||
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 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
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"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/model"
|
||||
)
|
||||
|
||||
var TotalRaw int64
|
||||
|
||||
type PostIds struct {
|
||||
Ids []uint64
|
||||
Length int
|
||||
}
|
||||
|
||||
type PostContext struct {
|
||||
Prev models.Posts
|
||||
Next models.Posts
|
||||
}
|
||||
|
||||
func CategoriesAndTags(ctx context.Context, t string, _ ...any) (terms []models.TermsMy, err error) {
|
||||
var in = []any{"category", "post_tag"}
|
||||
switch t {
|
||||
case constraints.Category:
|
||||
in = []any{"category"}
|
||||
case constraints.Tag:
|
||||
in = []any{"post_tag"}
|
||||
}
|
||||
w := model.SqlBuilder{
|
||||
{"tt.taxonomy", "in", ""},
|
||||
}
|
||||
if helper.GetContextVal(ctx, "showOnlyTopLevel", false) {
|
||||
w = append(w, []string{"tt.parent", "=", "0", "int"})
|
||||
}
|
||||
if !helper.GetContextVal(ctx, "showEmpty", false) {
|
||||
w = append(w, []string{"tt.count", ">", "0", "int"})
|
||||
}
|
||||
order := []string{"name", "asc"}
|
||||
ord := helper.GetContextVal[[]string](ctx, "order", nil)
|
||||
if ord != nil {
|
||||
order = ord
|
||||
}
|
||||
terms, err = model.Finds[models.TermsMy](ctx, model.Conditions(
|
||||
model.Where(w),
|
||||
model.Fields("t.term_id"),
|
||||
model.Order(model.SqlBuilder{order}),
|
||||
model.Join(model.SqlBuilder{
|
||||
{"t", "inner join", "wp_term_taxonomy tt", "t.term_id = tt.term_id"},
|
||||
}),
|
||||
model.In(in),
|
||||
))
|
||||
for i := 0; i < len(terms); i++ {
|
||||
if v, ok := wpconfig.GetTerm(terms[i].Terms.TermId); ok {
|
||||
terms[i].Terms = v
|
||||
}
|
||||
if v, ok := wpconfig.GetTermTaxonomy(terms[i].Terms.TermId); ok {
|
||||
terms[i].TermTaxonomy = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Archives(ctx context.Context) ([]models.PostArchive, error) {
|
||||
return model.Finds[models.PostArchive](ctx, model.Conditions(
|
||||
model.Where(model.SqlBuilder{
|
||||
{"post_type", "post"},
|
||||
{"post_status", "publish"},
|
||||
}),
|
||||
model.Fields("YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts"),
|
||||
model.Group("year,month"),
|
||||
model.Order(model.SqlBuilder{{"year", "desc"}, {"month", "desc"}}),
|
||||
))
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
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"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetPostsByIds(ctx context.Context, ids []uint64, _ ...any) (m map[uint64]models.Posts, err error) {
|
||||
m = make(map[uint64]models.Posts)
|
||||
q := model.Conditions(
|
||||
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
||||
model.Join(model.SqlBuilder{
|
||||
{"a", "left join", "wp_term_relationships b", "a.Id=b.object_id"},
|
||||
{"left join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id"},
|
||||
{"left join", "wp_terms d", "c.term_id=d.term_id"},
|
||||
}),
|
||||
model.Fields("a.*,ifnull(d.name,'') category_name,ifnull(c.term_id,0) terms_id,ifnull(taxonomy,'') `taxonomy`"),
|
||||
model.In(slice.ToAnySlice(ids)),
|
||||
)
|
||||
if helper.GetContextVal(ctx, "getPostAuthor", false) {
|
||||
q.RelationFn = append(q.RelationFn, model.AddRelationFn(true, false, helper.GetContextVal[*model.QueryCondition](ctx, "postAuthorQueryCondition", nil), relation.PostsWithAuthor))
|
||||
}
|
||||
rawPosts, err := model.Finds[models.Posts](ctx, q)
|
||||
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
postsMap := make(map[uint64]models.Posts)
|
||||
for i, post := range rawPosts {
|
||||
v, ok := postsMap[post.Id]
|
||||
if !ok {
|
||||
v = rawPosts[i]
|
||||
}
|
||||
if post.Taxonomy == "category" {
|
||||
v.Categories = append(v.Categories, post.CategoryName)
|
||||
} else if post.Taxonomy == "post_tag" {
|
||||
v.Tags = append(v.Tags, post.CategoryName)
|
||||
}
|
||||
if post.TermsId > 0 {
|
||||
v.TermIds = append(v.TermIds, post.TermsId)
|
||||
}
|
||||
postsMap[post.Id] = v
|
||||
}
|
||||
//host, _ := wpconfig.Options.Load("siteurl")
|
||||
host := ""
|
||||
meta, _ := GetPostMetaByPostIds(ctx, ids)
|
||||
for k, pp := range postsMap {
|
||||
if len(pp.Categories) > 0 {
|
||||
t := make([]string, 0, len(pp.Categories))
|
||||
for _, cat := range pp.Categories {
|
||||
t = append(t, fmt.Sprintf(`<a href="/p/category/%s" rel="category tag">%s</a>`, cat, cat))
|
||||
}
|
||||
pp.CategoriesHtml = strings.Join(t, "、")
|
||||
}
|
||||
mm, ok := meta[pp.Id]
|
||||
if ok {
|
||||
pp.Metas = mm
|
||||
attMeta, ok := mm["_wp_attachment_metadata"]
|
||||
if ok {
|
||||
att, ok := attMeta.(models.WpAttachmentMetadata)
|
||||
if ok {
|
||||
pp.AttachmentMetadata = att
|
||||
}
|
||||
}
|
||||
if pp.PostType != "attachment" {
|
||||
thumb := ToPostThumb(ctx, mm, host)
|
||||
if thumb.Path != "" {
|
||||
pp.Thumbnail = thumb
|
||||
}
|
||||
} else if pp.PostType == "attachment" && pp.AttachmentMetadata.File != "" {
|
||||
thumb := wpconfig.Thumbnail(pp.AttachmentMetadata, "thumbnail", host, "thumbnail", "post-thumbnail")
|
||||
if thumb.Path != "" {
|
||||
pp.Thumbnail = thumb
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if len(pp.Tags) > 0 {
|
||||
t := make([]string, 0, len(pp.Tags))
|
||||
for _, cat := range pp.Tags {
|
||||
t = append(t, fmt.Sprintf(`<a href="/p/tag/%s" rel="tag">%s</a>`, cat, cat))
|
||||
}
|
||||
pp.TagsHtml = strings.Join(t, "、")
|
||||
}
|
||||
m[k] = pp
|
||||
}
|
||||
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)
|
||||
q.Fields = "ID"
|
||||
res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize)
|
||||
for _, posts := range res {
|
||||
ids.Ids = append(ids.Ids, posts.Id)
|
||||
}
|
||||
ids.Length = total
|
||||
totalR := int(atomic.LoadInt64(&TotalRaw))
|
||||
if total > totalR {
|
||||
tt := int64(total)
|
||||
atomic.StoreInt64(&TotalRaw, tt)
|
||||
}
|
||||
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),
|
||||
),
|
||||
)
|
||||
var id uint64
|
||||
if len(r) > 0 {
|
||||
id = r[0].Id
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
func RecentPosts(ctx context.Context, a ...any) (r []models.Posts, err error) {
|
||||
num := helper.ParseArgs(10, a...)
|
||||
r, err = model.Finds[models.Posts](ctx, model.Conditions(
|
||||
model.Where(model.SqlBuilder{
|
||||
{"post_type", "post"},
|
||||
{"post_status", "publish"},
|
||||
}),
|
||||
model.Fields("ID,post_title,post_password,post_date_gmt"),
|
||||
model.Order([][]string{{"post_date", "desc"}}),
|
||||
model.Limit(num),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, err error) {
|
||||
t := arg[0].(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) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
prev, 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", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r = PostContext{
|
||||
Prev: prev,
|
||||
Next: next,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MonthPost(ctx context.Context, _ string, args ...any) (r []uint64, err error) {
|
||||
year, month := args[0].(string), args[1].(string)
|
||||
where := model.SqlBuilder{
|
||||
{"post_type", "post"},
|
||||
{"post_status", "publish"},
|
||||
{"year(post_date)", year},
|
||||
{"month(post_date)", month},
|
||||
}
|
||||
r, err = model.Column[models.Posts, uint64](ctx, func(v models.Posts) (uint64, bool) {
|
||||
return v.Id, true
|
||||
}, model.Conditions(
|
||||
model.Fields("ID"),
|
||||
model.Where(where),
|
||||
))
|
||||
l := int64(len(r))
|
||||
if l > atomic.LoadInt64(&TotalRaw) {
|
||||
atomic.StoreInt64(&TotalRaw, l)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
)
|
||||
|
||||
func GetUserById(ctx context.Context, uid uint64, _ ...any) (r models.Users, err error) {
|
||||
r, err = model.FindOneById[models.Users](ctx, uid)
|
||||
return
|
||||
}
|
||||
|
||||
func AllUsername(ctx context.Context, _ ...any) (map[string]uint64, error) {
|
||||
r, err := model.SimpleFind[models.Users](ctx, model.SqlBuilder{
|
||||
{"user_status", "=", "0", "int"},
|
||||
}, "display_name,ID")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return slice.ToMap(r, func(t models.Users) (string, uint64) {
|
||||
return t.DisplayName, t.Id
|
||||
}, true), nil
|
||||
}
|
||||
|
||||
func GetUserByName(ctx context.Context, u string, _ ...any) (r models.Users, err error) {
|
||||
r, err = model.FirstOne[models.Users](ctx, model.SqlBuilder{{
|
||||
"display_name", u,
|
||||
}}, "*", nil)
|
||||
return
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
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()
|
||||
dsn := c.Mysql.Dsn.GetDsn()
|
||||
db, err := sqlx.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
preDb := safeDb.Load()
|
||||
if c.Mysql.Pool.ConnMaxIdleTime != 0 {
|
||||
db.SetConnMaxIdleTime(c.Mysql.Pool.ConnMaxLifetime)
|
||||
}
|
||||
if c.Mysql.Pool.MaxIdleConn != 0 {
|
||||
db.SetMaxIdleConns(c.Mysql.Pool.MaxIdleConn)
|
||||
}
|
||||
if c.Mysql.Pool.MaxOpenConn != 0 {
|
||||
db.SetMaxOpenConns(c.Mysql.Pool.MaxOpenConn)
|
||||
}
|
||||
if c.Mysql.Pool.ConnMaxLifetime != 0 {
|
||||
db.SetConnMaxLifetime(c.Mysql.Pool.ConnMaxLifetime)
|
||||
}
|
||||
safeDb.Store(db)
|
||||
if preDb != nil {
|
||||
_ = preDb.Close()
|
||||
}
|
||||
if showQuerySql == nil {
|
||||
showQuerySql = reload.BuildFnVal("showQuerySql", false, func() bool {
|
||||
return config.GetConfig().ShowQuerySql
|
||||
})
|
||||
}
|
||||
return safeDb, err
|
||||
}
|
||||
|
||||
func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
|
||||
query := model.NewSqlxQuery(db, model.NewUniversalDb(
|
||||
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...))
|
||||
}()
|
||||
}
|
||||
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...))
|
||||
}()
|
||||
}
|
||||
return query.Gets(ctx, a, s, args...)
|
||||
})
|
||||
return query
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package logs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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{}
|
||||
var out io.Writer
|
||||
switch loggerFile {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out = file
|
||||
logFile.Store(file)
|
||||
}
|
||||
logs.Store(l)
|
||||
if preFD != nil {
|
||||
_ = preFD.Close()
|
||||
}
|
||||
l.SetFlags(log.Ldate | log.Ltime)
|
||||
l.SetOutput(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Errs(err error, depth int, desc string, args ...any) {
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(depth, pcs[:])
|
||||
f := runtime.CallersFrames([]uintptr{pcs[0]})
|
||||
ff, _ := f.Next()
|
||||
s := strings.Builder{}
|
||||
_, _ = fmt.Fprintf(&s, "%s:%d %s err:[%s]", ff.File, ff.Line, desc, err)
|
||||
if len(args) > 0 {
|
||||
s.WriteString(" args:")
|
||||
for _, arg := range args {
|
||||
_, _ = fmt.Fprintf(&s, "%v", arg)
|
||||
}
|
||||
}
|
||||
logs.Load().Println(s.String())
|
||||
}
|
||||
|
||||
func Error(err error, desc string, args ...any) {
|
||||
Errs(err, 3, desc, args...)
|
||||
}
|
||||
|
||||
func IfError(err error, desc string, args ...any) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
Errs(err, 3, desc, args...)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package relation
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
)
|
||||
|
||||
var PostsWithAuthor = model.RelationHasOne(func(m *models.Posts) uint64 {
|
||||
return m.PostAuthor
|
||||
}, func(p *models.Users) uint64 {
|
||||
return p.Id
|
||||
}, func(m *models.Posts, p *models.Users) {
|
||||
m.Author = p
|
||||
}, model.Relationship{
|
||||
RelationType: model.HasOne,
|
||||
Table: "wp_users user",
|
||||
ForeignKey: "ID",
|
||||
Local: "post_author",
|
||||
})
|
|
@ -1,9 +0,0 @@
|
|||
<div>
|
||||
{{.aa}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ range $k,$v := .posts}}
|
||||
<h2>{{$v.PostTitle}} </h2>
|
||||
{{end}}
|
||||
</div>
|
|
@ -1,81 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/fthvgb1/wp-go/app/cmd/route"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle"
|
||||
"github.com/fthvgb1/wp-go/app/theme"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp/components"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp/components/widget"
|
||||
route2 "github.com/fthvgb1/wp-go/app/theme/wp/route"
|
||||
"github.com/gin-gonic/gin"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"plugintt/xx"
|
||||
)
|
||||
|
||||
//go:embed a.gohtml
|
||||
var em embed.FS
|
||||
var tt *template.Template
|
||||
|
||||
func init() {
|
||||
// register as theme
|
||||
theme.AddThemeHookFunc("themename", hook)
|
||||
|
||||
//use the local template
|
||||
//note: must use embed.FS
|
||||
t, err := template.ParseFS(em, "a.gohtml")
|
||||
if err != nil {
|
||||
logs.Error(err, "")
|
||||
}
|
||||
tt = t
|
||||
|
||||
// register gin route. it will be effecting when server restart.
|
||||
route.Hook(func(r *gin.Engine) {
|
||||
r.GET("xx", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "xxoo")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func hook(h *wp.Handle) {
|
||||
wp.Run(h, config)
|
||||
}
|
||||
|
||||
func config(h *wp.Handle) {
|
||||
// same theme config
|
||||
wphandle.UsePlugins(h)
|
||||
wp.InitPipe(h)
|
||||
h.PushHandler(constraints.PipeMiddleware, constraints.Home,
|
||||
wp.NewHandleFn(widget.IsCategory, 100, "widget.IsCategory"))
|
||||
components.WidgetArea(h)
|
||||
h.PushHandler(constraints.PipeRender, constraints.Home, wp.NewHandleFn(func(h *wp.Handle) {
|
||||
h.SetData("aa", "xyxxxx")
|
||||
h.RenderHtml(tt, http.StatusOK, "a.gohtml")
|
||||
h.Abort()
|
||||
h.StopPipe()
|
||||
}, 10, "renderHome"))
|
||||
|
||||
// use simple reg route
|
||||
route2.PushRoute(`(?P<control>\w+)/(?P<method>\w+)`, route2.Route{
|
||||
Path: `(?P<control>\w+)/(?P<method>\w+)`,
|
||||
Scene: constraints.Home,
|
||||
Method: []string{"GET"},
|
||||
Type: "reg",
|
||||
})
|
||||
//...
|
||||
}
|
||||
|
||||
// Xo to be a func when theme init
|
||||
func Xo(h *wp.Handle) {
|
||||
xx.Xo()
|
||||
route2.Delete(`(?P<control>\w+)/(?P<method>\w+)`)
|
||||
h.ReplaceHandle(constraints.PipeRender, "wp.RenderTemplate", func(h *wp.Handle) {
|
||||
h.SetData("aa", "xyxxxx")
|
||||
h.RenderHtml(tt, http.StatusOK, "a.gohtml")
|
||||
h.StopPipe()
|
||||
})
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#/bin/bash
|
||||
|
||||
# copy plugintt to other dir and remove .dev suffix
|
||||
# note the go version and build tool flag must same to server build
|
||||
# eg: -gcflags all="-N -l" --race may used in ide debug
|
||||
go mod tidy
|
||||
go build -buildmode=plugin -o xx.so main.go
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -1,11 +0,0 @@
|
|||
package xx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func Xo() {
|
||||
fmt.Println("xxoo")
|
||||
fmt.Println(decimal.Max(decimal.NewFromFloat(32.3333333333333), decimal.NewFromFloat(32.33333333333331)).String())
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
package plugins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/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 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
|
||||
}
|
||||
|
||||
func RemoveWpBlock(s string) string {
|
||||
return removeWpBlock.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
func digestRaw(ctx context.Context, id uint64, arg ...any) (string, error) {
|
||||
s := arg[1].(string)
|
||||
limit := arg[3].(int)
|
||||
if limit < 0 {
|
||||
return s, nil
|
||||
} else if limit == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
s = more.ReplaceAllString(s, "")
|
||||
fn := helper.GetContextVal(ctx, "postMoreFn", PostsMore)
|
||||
return Digests(s, id, limit, fn), nil
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
return fn(id, content, closeTag)
|
||||
}
|
||||
|
||||
func PostsMore(id uint64, content, closeTag string) string {
|
||||
tmp := `%s......%s<p class="read-more"><a href="/p/%d">继续阅读</a></p>`
|
||||
if strings.Contains(closeTag, "pre") || strings.Contains(closeTag, "code") {
|
||||
tmp = `%s%s......<p class="read-more"><a href="/p/%d">继续阅读</a></p>`
|
||||
}
|
||||
content = fmt.Sprintf(tmp, content, closeTag, id)
|
||||
return content
|
||||
}
|
||||
|
||||
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)
|
||||
post.PostContent = content
|
||||
}
|
||||
|
||||
func PostExcerpt(post *models.Posts) {
|
||||
post.PostContent = strings.Replace(post.PostExcerpt, "\n", "<br/>", -1)
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
type PageEle struct {
|
||||
PrevEle string
|
||||
NextEle string
|
||||
DotsEle string
|
||||
MiddleEle string
|
||||
CurrentEle string
|
||||
}
|
||||
|
||||
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>`,
|
||||
NextEle: `<a class="next page-numbers" href="%s">下一页</a>`,
|
||||
DotsEle: `<span class="page-numbers dots">…</span>`,
|
||||
MiddleEle: `<a class="page-numbers" href="%s"><span class="meta-nav screen-reader-text">页 </span>%d</a>
|
||||
`,
|
||||
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)
|
||||
}
|
||||
|
||||
func (p PageEle) Prev(url string) string {
|
||||
return fmt.Sprintf(p.PrevEle, url)
|
||||
}
|
||||
|
||||
func (p PageEle) Next(url string) string {
|
||||
return fmt.Sprintf(p.NextEle, url)
|
||||
}
|
||||
|
||||
func (p PageEle) Dots() string {
|
||||
return p.DotsEle
|
||||
}
|
||||
|
||||
func (p PageEle) Middle(page int, url string) string {
|
||||
return fmt.Sprintf(p.MiddleEle, url, page)
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(path, "/page/") {
|
||||
path = fmt.Sprintf("%s%s", path, "/page/1")
|
||||
}
|
||||
if page == 1 {
|
||||
path = reg.ReplaceAllString(path, "")
|
||||
} else {
|
||||
s := fmt.Sprintf("$1/%d", page)
|
||||
path = reg.ReplaceAllString(path, s)
|
||||
}
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
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)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package apply
|
||||
|
||||
import "github.com/fthvgb1/wp-go/safety"
|
||||
|
||||
var contains = safety.NewMap[string, any]()
|
||||
|
||||
func SetVal(key string, val any) {
|
||||
contains.Store(key, val)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package enlightjs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/phphelper"
|
||||
"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/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Selectors Selectors `json:"selectors"`
|
||||
Options Options `json:"options"`
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Indent int64 `json:"indent,omitempty"`
|
||||
AmpersandCleanup bool `json:"ampersandCleanup,omitempty"`
|
||||
Linehover bool `json:"linehover,omitempty"`
|
||||
RawcodeDbclick bool `json:"rawcodeDbclick,omitempty"`
|
||||
TextOverflow string `json:"textOverflow,omitempty"`
|
||||
Linenumbers bool `json:"linenumbers,omitempty"`
|
||||
Theme string `json:"theme,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
RetainCssClasses bool `json:"retainCssClasses,omitempty"`
|
||||
Collapse bool `json:"collapse,omitempty"`
|
||||
ToolbarOuter string `json:"toolbarOuter,omitempty"`
|
||||
ToolbarTop string `json:"toolbarTop,omitempty"`
|
||||
ToolbarBottom string `json:"toolbarBottom,omitempty"`
|
||||
}
|
||||
|
||||
type Selectors struct {
|
||||
Block string `json:"block,omitempty"`
|
||||
Inline string `json:"inline,omitempty"`
|
||||
}
|
||||
|
||||
func EnlighterJS(h *wp.Handle) {
|
||||
h.PushGroupHeadScript(constraints.AllScene, "enlighterjs-css", 20, `<link rel='stylesheet' id='enlighterjs-css' href='/wp-content/plugins/enlighter/cache/enlighterjs.min.css' media='all' />`)
|
||||
|
||||
h.PushCacheGroupFooterScript(constraints.AllScene, "enlighterJs", 10, func(h *wp.Handle) string {
|
||||
op := wpconfig.GetOption("enlighter-options")
|
||||
opp, err := phphelper.UnPHPSerializeToStrAnyMap(op)
|
||||
if err != nil {
|
||||
logs.Error(err, "获取enlighter-option失败", op)
|
||||
return ""
|
||||
}
|
||||
v := Config{
|
||||
Selectors: Selectors{
|
||||
Block: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-selector-block", "pre.EnlighterJSRAW"),
|
||||
Inline: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-selector-inline", "code.EnlighterJSRAW"),
|
||||
},
|
||||
Options: Options{
|
||||
Indent: maps.GetStrAnyValWithDefaults[int64](opp, "enlighterjs-indent", 4),
|
||||
AmpersandCleanup: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-ampersandcleanup", true),
|
||||
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),
|
||||
Theme: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-theme", "enlighter"),
|
||||
Language: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-language", "generic"),
|
||||
RetainCssClasses: maps.GetStrAnyValWithDefaults(opp, "enlighterjs-retaincss", false),
|
||||
Collapse: false,
|
||||
ToolbarOuter: "",
|
||||
ToolbarTop: "{BTN_RAW}{BTN_COPY}{BTN_WINDOW}{BTN_WEBSITE}",
|
||||
ToolbarBottom: "",
|
||||
},
|
||||
}
|
||||
conf, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
logs.Error(err, "json化enlighterjs配置失败")
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(enlighterjs, conf)
|
||||
})
|
||||
}
|
||||
|
||||
var enlighterjs = `<script src='/wp-content/plugins/enlighter/cache/enlighterjs.min.js?ver=0A0B0C' id='enlighterjs-js'></script>
|
||||
<script id='enlighterjs-js-after'>
|
||||
!function(e,n){if("undefined"!=typeof EnlighterJS){var o=%s;(e.EnlighterJSINIT=function(){EnlighterJS.init(o.selectors.block,o.selectors.inline,o.options)})()}else{(n&&(n.error||n.log)||function(){})("Error: EnlighterJS resources not loaded yet!")}}(window,console);
|
||||
</script>`
|
|
@ -1,72 +0,0 @@
|
|||
package wphandle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle/apply"
|
||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle/enlightjs"
|
||||
"github.com/fthvgb1/wp-go/app/plugins/wphandle/hiddenlogin"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
)
|
||||
|
||||
var plugins = func() *safety.Map[string, func(*wp.Handle)] {
|
||||
m := safety.NewMap[string, func(*wp.Handle)]()
|
||||
m.Store("Enlightjs", enlightjs.EnlighterJS)
|
||||
m.Store("HiddenLogin", hiddenlogin.HiddenLogin)
|
||||
return m
|
||||
}()
|
||||
|
||||
func RegisterPlugin(name string, fn func(*wp.Handle)) {
|
||||
plugins.Store(name, fn)
|
||||
}
|
||||
|
||||
func UsePlugins(h *wp.Handle, calls ...string) {
|
||||
calls = append(calls, config.GetConfig().Plugins...)
|
||||
for _, call := range calls {
|
||||
call = str.FirstUpper(call)
|
||||
if fn, ok := plugins.Load(call); ok {
|
||||
fn(h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func LoadPlugins() {
|
||||
dirPath := config.GetConfig().PluginPath
|
||||
if dirPath == "" {
|
||||
return
|
||||
}
|
||||
glob, err := filepath.Glob(filepath.Join(dirPath, "*.so"))
|
||||
if err != nil {
|
||||
logs.Error(err, "读取插件目录错误", dirPath)
|
||||
return
|
||||
}
|
||||
for _, entry := range glob {
|
||||
p, err := plugin.Open(entry)
|
||||
if err != nil {
|
||||
logs.Error(err, "读取插件错误", entry)
|
||||
continue
|
||||
}
|
||||
name := filepath.Ext(entry)
|
||||
name = filepath.Base(entry[0 : len(entry)-len(name)])
|
||||
name = str.FirstUpper(name)
|
||||
pl, err := p.Lookup(name)
|
||||
if err != nil {
|
||||
logs.Error(err, "插件lookup错误", entry)
|
||||
continue
|
||||
}
|
||||
plu, ok := pl.(func(*wp.Handle))
|
||||
if !ok {
|
||||
logs.Error(errors.New("switch func(*wp.Handle) fail"), "插件转换错误", entry)
|
||||
continue
|
||||
}
|
||||
RegisterPlugin(name, plu)
|
||||
}
|
||||
apply.SetVal("wp-plugins", func(h *wp.Handle) {
|
||||
UsePlugins(h)
|
||||
})
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package hiddenlogin
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
)
|
||||
|
||||
func HiddenLogin(h *wp.Handle) {
|
||||
h.AddActionFilter(widgets.Meta, func(h *wp.Handle, s string, args ...any) string {
|
||||
return str.Replace(s, map[string]string{
|
||||
`<li><a href="/wp-login.php">登录</a></li>`: "",
|
||||
`<li><a href="/feed">登录</a></li>`: "",
|
||||
`<li><a href="/comments/feed">登录</a></li>`: "",
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
)
|
||||
|
||||
func Tt(h *wp.Handle) {
|
||||
h.HookHandle(constraints.PipeMiddleware, constraints.AllScene, func(call wp.HandleCall) (wp.HandleCall, bool) {
|
||||
return call, false
|
||||
})
|
||||
/*h.PushPipeHook(constraints.Home, func(pipe wp.Pipe) (wp.Pipe, bool) {
|
||||
return wp.Pipe{}, false
|
||||
})*/
|
||||
//h.DeletePipe(constraints.Home, constraints.PipeMiddleware)
|
||||
h.ReplacePipe(constraints.Home, constraints.PipeMiddleware, wp.NewPipe("log", 500, func(next wp.HandleFn[*wp.Handle], h *wp.Handle) {
|
||||
fmt.Println("ffff")
|
||||
next(h)
|
||||
fmt.Println("iiiii")
|
||||
}))
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package wpposts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
)
|
||||
|
||||
func PasswordProjectTitle(post *models.Posts) {
|
||||
post.PostTitle = fmt.Sprintf("密码保护:%s", post.PostTitle)
|
||||
}
|
||||
|
||||
func PasswdProjectContent(post *models.Posts) {
|
||||
format := `
|
||||
<form action="/login" class="post-password-form" method="post">
|
||||
<p>此内容受密码保护。如需查阅,请在下列字段中输入您的密码。</p>
|
||||
<p><label for="pwbox-%d">密码: <input name="post_password" id="pwbox-%d" type="password" size="20"></label> <input type="submit" name="Submit" value="提交"></p>
|
||||
</form>`
|
||||
post.PostContent = fmt.Sprintf(format, post.Id, post.Id)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/fthvgb1/wp-go/multipTemplate"
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed *[^.go]
|
||||
var TemplateFs embed.FS
|
||||
|
||||
var templates = safety.NewMap[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 GetTemplate(name string) (*template.Template, bool) {
|
||||
t, ok := templates.Load(name)
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// 所有主题模板通用设置
|
||||
func commonTemplate(t *multipTemplate.MultipleFsTemplate) {
|
||||
m, err := fs.Glob(t.Fs, "*/posts/*.gohtml")
|
||||
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"))
|
||||
t.SetTemplate(main, templ)
|
||||
}
|
||||
}
|
||||
|
||||
func IsTemplateDirExists(tml string) bool {
|
||||
arr, err := TemplateFs.ReadDir(tml)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(arr) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsTemplateExists(tml string) bool {
|
||||
t, ok := templates.Load(tml)
|
||||
return ok && t != nil
|
||||
}
|
||||
|
||||
func SetTemplate(name string, val *template.Template) {
|
||||
templates.Store(name, val)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"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)) {
|
||||
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"))
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
"html/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
func postsFn(fn func(models.Posts) string, a models.Posts) string {
|
||||
return fn(a)
|
||||
}
|
||||
|
||||
func FuncMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"unescaped": func(s string) any {
|
||||
return template.HTML(s)
|
||||
},
|
||||
"dateCh": func(t time.Time) any {
|
||||
return t.Format("2006年 01月 02日")
|
||||
},
|
||||
"timeFormat": func(t time.Time, format string) any {
|
||||
return t.Format(format)
|
||||
},
|
||||
"getOption": func(k string) string {
|
||||
return wpconfig.GetOption(k)
|
||||
},
|
||||
"getLang": wpconfig.GetLang,
|
||||
"postsFn": postsFn,
|
||||
"exec": func(fn func() string) template.HTML {
|
||||
return template.HTML(fn())
|
||||
},
|
||||
"callFuncString": func(fn func(string) string, s string) template.HTML {
|
||||
return template.HTML(fn(s))
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||
"github.com/fthvgb1/wp-go/app/theme/twentyfifteen"
|
||||
"github.com/fthvgb1/wp-go/app/theme/twentyseventeen"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
)
|
||||
|
||||
func InitTheme() {
|
||||
AddTheme(twentyfifteen.ThemeName, twentyfifteen.Hook)
|
||||
AddTheme(twentyseventeen.ThemeName, twentyseventeen.Hook)
|
||||
}
|
||||
|
||||
func GetCurrentTheme() string {
|
||||
themeName := config.GetConfig().Theme
|
||||
if themeName == "" {
|
||||
themeName = wpconfig.GetOption("template")
|
||||
}
|
||||
if !IsTemplateDirExists(themeName) {
|
||||
themeName = "twentyfifteen"
|
||||
}
|
||||
return themeName
|
||||
}
|
|
@ -1,614 +0,0 @@
|
|||
package twentyfifteen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func colorSchemeCss(h *wp.Handle) string {
|
||||
s := slice.Filter([]string{calColorSchemeCss(h), calSidebarTextColorCss(h), calHeaderBackgroundColorCss(h)}, func(s string, i int) bool {
|
||||
return s != ""
|
||||
})
|
||||
if len(s) < 1 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(`<style id='%s-inline-css'%s>\n%s\n</style>`, "twentyfifteen-style", "", strings.Join(s, "\n"))
|
||||
}
|
||||
func calColorSchemeCss(h *wp.Handle) (r string) {
|
||||
color := getColorScheme(h)
|
||||
if "default" == h.CommonThemeMods().ColorScheme || len(color) < 1 {
|
||||
return
|
||||
}
|
||||
textColorRgb := slice.ToAnySlice(Hex2RgbUint8(color[3]))
|
||||
sidebarTextColorRgb := Hex2RgbUint8(color[4])
|
||||
sidebarTextColorRgbs := slice.ToAnySlice(sidebarTextColorRgb)
|
||||
colors := map[string]string{
|
||||
"background_color": color[0],
|
||||
"header_background_color": color[1],
|
||||
"box_background_color": color[2],
|
||||
"textcolor": color[3],
|
||||
"secondary_textcolor": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", textColorRgb...),
|
||||
"border_color": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", textColorRgb...),
|
||||
"border_focus_color": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", textColorRgb...),
|
||||
"sidebar_textcolor": color[4],
|
||||
"sidebar_border_color": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", sidebarTextColorRgbs...),
|
||||
"sidebar_border_focus_color": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", sidebarTextColorRgbs...),
|
||||
"secondary_sidebar_textcolor": fmt.Sprintf("rgba(%d, %d, %d, 0.7)", sidebarTextColorRgbs...),
|
||||
"meta_box_background_color": color[5],
|
||||
}
|
||||
r = cssTemplate
|
||||
for k, v := range colors {
|
||||
r = strings.ReplaceAll(r, fmt.Sprintf(`{$colors['%s']}`, k), v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func calSidebarTextColorCss(h *wp.Handle) (r string) {
|
||||
colors := getColorScheme(h)
|
||||
themeMods := h.CommonThemeMods()
|
||||
if themeMods.SidebarTextcolor == "" || themeMods.SidebarTextcolor == colors[4] {
|
||||
return
|
||||
}
|
||||
linkColorRgb := Hex2RgbUint8(themeMods.SidebarTextcolor)
|
||||
color := slice.ToAnySlice(linkColorRgb)
|
||||
textColor := fmt.Sprintf(`rgba( %[1]v, %[2]v, %[3]v, 0.7)`, color...)
|
||||
borderColor := fmt.Sprintf(`rgba( %[1]v, %[2]v, %[3]v, 0.1)`, color...)
|
||||
borderFocusColor := fmt.Sprintf(`rgba( %[1]v, %[2]v, %[3]v, 0.3)`, color...)
|
||||
r = fmt.Sprintf(sidebarTextColorTemplate, themeMods.SidebarTextcolor, textColor, borderColor, borderFocusColor)
|
||||
return
|
||||
}
|
||||
|
||||
func calHeaderBackgroundColorCss(h *wp.Handle) (r string) {
|
||||
colors := getColorScheme(h)
|
||||
themeMods := h.CommonThemeMods()
|
||||
if themeMods.HeaderBackgroundColor == "" || themeMods.HeaderBackgroundColor == colors[1] {
|
||||
return
|
||||
}
|
||||
r = fmt.Sprintf(headerBackgroundColorCssTemplate, themeMods.HeaderBackgroundColor, themeMods.HeaderBackgroundColor)
|
||||
return
|
||||
}
|
||||
|
||||
func getColorScheme(h *wp.Handle) (r []string) {
|
||||
x, ok := colorscheme[h.CommonThemeMods().ColorScheme]
|
||||
if ok {
|
||||
r = x.Colors
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ColorScheme struct {
|
||||
Label string `json:"label,omitempty"`
|
||||
Colors []string `json:"colors,omitempty"`
|
||||
}
|
||||
|
||||
func Hex2RgbUint8(color string) []uint8 {
|
||||
var r []uint8
|
||||
color = strings.TrimLeft(color, "#")
|
||||
fn := func(s string) uint8 {
|
||||
n, _ := strconv.ParseInt(s, 16, 0)
|
||||
return uint8(n)
|
||||
}
|
||||
switch len(color) {
|
||||
case 3:
|
||||
r = []uint8{color[0], color[1], color[2]}
|
||||
case 6:
|
||||
r = []uint8{fn(color[:2]), fn(color[2:4]), fn(color[4:])}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
var cssTemplate = `
|
||||
/* Color Scheme */
|
||||
|
||||
/* Background Color */
|
||||
body {
|
||||
background-color: {$colors['background_color']};
|
||||
}
|
||||
|
||||
/* Sidebar Background Color */
|
||||
body:before,
|
||||
.site-header {
|
||||
background-color: {$colors['header_background_color']};
|
||||
}
|
||||
|
||||
/* Box Background Color */
|
||||
.post-navigation,
|
||||
.pagination,
|
||||
.secondary,
|
||||
.site-footer,
|
||||
.hentry,
|
||||
.page-header,
|
||||
.page-content,
|
||||
.comments-area,
|
||||
.widecolumn {
|
||||
background-color: {$colors['box_background_color']};
|
||||
}
|
||||
|
||||
/* Box Background Color */
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"],
|
||||
.pagination .prev,
|
||||
.pagination .next,
|
||||
.widget_calendar tbody a,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus,
|
||||
.page-links a,
|
||||
.page-links a:hover,
|
||||
.page-links a:focus,
|
||||
.sticky-post {
|
||||
color: {$colors['box_background_color']};
|
||||
}
|
||||
|
||||
/* Main Text Color */
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"],
|
||||
.pagination .prev,
|
||||
.pagination .next,
|
||||
.widget_calendar tbody a,
|
||||
.page-links a,
|
||||
.sticky-post {
|
||||
background-color: {$colors['textcolor']};
|
||||
}
|
||||
|
||||
/* Main Text Color */
|
||||
body,
|
||||
blockquote cite,
|
||||
blockquote small,
|
||||
a,
|
||||
.dropdown-toggle:after,
|
||||
.image-navigation a:hover,
|
||||
.image-navigation a:focus,
|
||||
.comment-navigation a:hover,
|
||||
.comment-navigation a:focus,
|
||||
.widget-title,
|
||||
.entry-footer a:hover,
|
||||
.entry-footer a:focus,
|
||||
.comment-metadata a:hover,
|
||||
.comment-metadata a:focus,
|
||||
.pingback .edit-link a:hover,
|
||||
.pingback .edit-link a:focus,
|
||||
.comment-list .reply a:hover,
|
||||
.comment-list .reply a:focus,
|
||||
.site-info a:hover,
|
||||
.site-info a:focus {
|
||||
color: {$colors['textcolor']};
|
||||
}
|
||||
|
||||
/* Main Text Color */
|
||||
.entry-content a,
|
||||
.entry-summary a,
|
||||
.page-content a,
|
||||
.comment-content a,
|
||||
.pingback .comment-body > a,
|
||||
.author-description a,
|
||||
.taxonomy-description a,
|
||||
.textwidget a,
|
||||
.entry-footer a:hover,
|
||||
.comment-metadata a:hover,
|
||||
.pingback .edit-link a:hover,
|
||||
.comment-list .reply a:hover,
|
||||
.site-info a:hover {
|
||||
border-color: {$colors['textcolor']};
|
||||
}
|
||||
|
||||
/* Secondary Text Color */
|
||||
button:hover,
|
||||
button:focus,
|
||||
input[type="button"]:hover,
|
||||
input[type="button"]:focus,
|
||||
input[type="reset"]:hover,
|
||||
input[type="reset"]:focus,
|
||||
input[type="submit"]:hover,
|
||||
input[type="submit"]:focus,
|
||||
.pagination .prev:hover,
|
||||
.pagination .prev:focus,
|
||||
.pagination .next:hover,
|
||||
.pagination .next:focus,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus,
|
||||
.page-links a:hover,
|
||||
.page-links a:focus {
|
||||
background-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
background-color: {$colors['secondary_textcolor']};
|
||||
}
|
||||
|
||||
/* Secondary Text Color */
|
||||
blockquote,
|
||||
a:hover,
|
||||
a:focus,
|
||||
.main-navigation .menu-item-description,
|
||||
.post-navigation .meta-nav,
|
||||
.post-navigation a:hover .post-title,
|
||||
.post-navigation a:focus .post-title,
|
||||
.image-navigation,
|
||||
.image-navigation a,
|
||||
.comment-navigation,
|
||||
.comment-navigation a,
|
||||
.widget,
|
||||
.author-heading,
|
||||
.entry-footer,
|
||||
.entry-footer a,
|
||||
.taxonomy-description,
|
||||
.page-links > .page-links-title,
|
||||
.entry-caption,
|
||||
.comment-author,
|
||||
.comment-metadata,
|
||||
.comment-metadata a,
|
||||
.pingback .edit-link,
|
||||
.pingback .edit-link a,
|
||||
.post-password-form label,
|
||||
.comment-form label,
|
||||
.comment-notes,
|
||||
.comment-awaiting-moderation,
|
||||
.logged-in-as,
|
||||
.form-allowed-tags,
|
||||
.no-comments,
|
||||
.site-info,
|
||||
.site-info a,
|
||||
.wp-caption-text,
|
||||
.gallery-caption,
|
||||
.comment-list .reply a,
|
||||
.widecolumn label,
|
||||
.widecolumn .mu_register label {
|
||||
color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
color: {$colors['secondary_textcolor']};
|
||||
}
|
||||
|
||||
/* Secondary Text Color */
|
||||
blockquote,
|
||||
.logged-in-as a:hover,
|
||||
.comment-author a:hover {
|
||||
border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
border-color: {$colors['secondary_textcolor']};
|
||||
}
|
||||
|
||||
/* Border Color */
|
||||
hr,
|
||||
.dropdown-toggle:hover,
|
||||
.dropdown-toggle:focus {
|
||||
background-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
background-color: {$colors['border_color']};
|
||||
}
|
||||
|
||||
/* Border Color */
|
||||
pre,
|
||||
abbr[title],
|
||||
table,
|
||||
th,
|
||||
td,
|
||||
input,
|
||||
textarea,
|
||||
.main-navigation ul,
|
||||
.main-navigation li,
|
||||
.post-navigation,
|
||||
.post-navigation div + div,
|
||||
.pagination,
|
||||
.comment-navigation,
|
||||
.widget li,
|
||||
.widget_categories .children,
|
||||
.widget_nav_menu .sub-menu,
|
||||
.widget_pages .children,
|
||||
.site-header,
|
||||
.site-footer,
|
||||
.hentry + .hentry,
|
||||
.author-info,
|
||||
.entry-content .page-links a,
|
||||
.page-links > span,
|
||||
.page-header,
|
||||
.comments-area,
|
||||
.comment-list + .comment-respond,
|
||||
.comment-list article,
|
||||
.comment-list .pingback,
|
||||
.comment-list .trackback,
|
||||
.comment-list .reply a,
|
||||
.no-comments {
|
||||
border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
border-color: {$colors['border_color']};
|
||||
}
|
||||
|
||||
/* Border Focus Color */
|
||||
a:focus,
|
||||
button:focus,
|
||||
input:focus {
|
||||
outline-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
outline-color: {$colors['border_focus_color']};
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
border-color: {$colors['border_focus_color']};
|
||||
}
|
||||
|
||||
/* Sidebar Link Color */
|
||||
.secondary-toggle:before {
|
||||
color: {$colors['sidebar_textcolor']};
|
||||
}
|
||||
|
||||
.site-title a,
|
||||
.site-description {
|
||||
color: {$colors['sidebar_textcolor']};
|
||||
}
|
||||
|
||||
/* Sidebar Text Color */
|
||||
.site-title a:hover,
|
||||
.site-title a:focus {
|
||||
color: {$colors['secondary_sidebar_textcolor']};
|
||||
}
|
||||
|
||||
/* Sidebar Border Color */
|
||||
.secondary-toggle {
|
||||
border-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
border-color: {$colors['sidebar_border_color']};
|
||||
}
|
||||
|
||||
/* Sidebar Border Focus Color */
|
||||
.secondary-toggle:hover,
|
||||
.secondary-toggle:focus {
|
||||
border-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
border-color: {$colors['sidebar_border_focus_color']};
|
||||
}
|
||||
|
||||
.site-title a {
|
||||
outline-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */
|
||||
outline-color: {$colors['sidebar_border_focus_color']};
|
||||
}
|
||||
|
||||
/* Meta Background Color */
|
||||
.entry-footer {
|
||||
background-color: {$colors['meta_box_background_color']};
|
||||
}
|
||||
|
||||
@media screen and (min-width: 38.75em) {
|
||||
/* Main Text Color */
|
||||
.page-header {
|
||||
border-color: {$colors['textcolor']};
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 59.6875em) {
|
||||
/* Make sure its transparent on desktop */
|
||||
.site-header,
|
||||
.secondary {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Sidebar Background Color */
|
||||
.widget button,
|
||||
.widget input[type="button"],
|
||||
.widget input[type="reset"],
|
||||
.widget input[type="submit"],
|
||||
.widget_calendar tbody a,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus {
|
||||
color: {$colors['header_background_color']};
|
||||
}
|
||||
|
||||
/* Sidebar Link Color */
|
||||
.secondary a,
|
||||
.dropdown-toggle:after,
|
||||
.widget-title,
|
||||
.widget blockquote cite,
|
||||
.widget blockquote small {
|
||||
color: {$colors['sidebar_textcolor']};
|
||||
}
|
||||
|
||||
.widget button,
|
||||
.widget input[type="button"],
|
||||
.widget input[type="reset"],
|
||||
.widget input[type="submit"],
|
||||
.widget_calendar tbody a {
|
||||
background-color: {$colors['sidebar_textcolor']};
|
||||
}
|
||||
|
||||
.textwidget a {
|
||||
border-color: {$colors['sidebar_textcolor']};
|
||||
}
|
||||
|
||||
/* Sidebar Text Color */
|
||||
.secondary a:hover,
|
||||
.secondary a:focus,
|
||||
.main-navigation .menu-item-description,
|
||||
.widget,
|
||||
.widget blockquote,
|
||||
.widget .wp-caption-text,
|
||||
.widget .gallery-caption {
|
||||
color: {$colors['secondary_sidebar_textcolor']};
|
||||
}
|
||||
|
||||
.widget button:hover,
|
||||
.widget button:focus,
|
||||
.widget input[type="button"]:hover,
|
||||
.widget input[type="button"]:focus,
|
||||
.widget input[type="reset"]:hover,
|
||||
.widget input[type="reset"]:focus,
|
||||
.widget input[type="submit"]:hover,
|
||||
.widget input[type="submit"]:focus,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus {
|
||||
background-color: {$colors['secondary_sidebar_textcolor']};
|
||||
}
|
||||
|
||||
.widget blockquote {
|
||||
border-color: {$colors['secondary_sidebar_textcolor']};
|
||||
}
|
||||
|
||||
/* Sidebar Border Color */
|
||||
.main-navigation ul,
|
||||
.main-navigation li,
|
||||
.widget input,
|
||||
.widget textarea,
|
||||
.widget table,
|
||||
.widget th,
|
||||
.widget td,
|
||||
.widget pre,
|
||||
.widget li,
|
||||
.widget_categories .children,
|
||||
.widget_nav_menu .sub-menu,
|
||||
.widget_pages .children,
|
||||
.widget abbr[title] {
|
||||
border-color: {$colors['sidebar_border_color']};
|
||||
}
|
||||
|
||||
.dropdown-toggle:hover,
|
||||
.dropdown-toggle:focus,
|
||||
.widget hr {
|
||||
background-color: {$colors['sidebar_border_color']};
|
||||
}
|
||||
|
||||
.widget input:focus,
|
||||
.widget textarea:focus {
|
||||
border-color: {$colors['sidebar_border_focus_color']};
|
||||
}
|
||||
|
||||
.sidebar a:focus,
|
||||
.dropdown-toggle:focus {
|
||||
outline-color: {$colors['sidebar_border_focus_color']};
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var headerBackgroundColorCssTemplate = `
|
||||
/* Custom Header Background Color */
|
||||
body:before,
|
||||
.site-header {
|
||||
background-color: %s;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 59.6875em) {
|
||||
.site-header,
|
||||
.secondary {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.widget button,
|
||||
.widget input[type="button"],
|
||||
.widget input[type="reset"],
|
||||
.widget input[type="submit"],
|
||||
.widget_calendar tbody a,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus {
|
||||
color: %s;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var sidebarTextColorTemplate = `
|
||||
/* Custom Sidebar Text Color */
|
||||
.site-title a,
|
||||
.site-description,
|
||||
.secondary-toggle:before {
|
||||
color: %[1]v;
|
||||
}
|
||||
|
||||
.site-title a:hover,
|
||||
.site-title a:focus {
|
||||
color: %[1]v; /* Fallback for IE7 and IE8 */
|
||||
color: %[2]v;
|
||||
}
|
||||
|
||||
.secondary-toggle {
|
||||
border-color: %[1]v; /* Fallback for IE7 and IE8 */
|
||||
border-color: %[3]v;
|
||||
}
|
||||
|
||||
.secondary-toggle:hover,
|
||||
.secondary-toggle:focus {
|
||||
border-color: %[1]v; /* Fallback for IE7 and IE8 */
|
||||
border-color: %[4]v;
|
||||
}
|
||||
|
||||
.site-title a {
|
||||
outline-color: %[1]v; /* Fallback for IE7 and IE8 */
|
||||
outline-color: %[4]v;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 59.6875em) {
|
||||
.secondary a,
|
||||
.dropdown-toggle:after,
|
||||
.widget-title,
|
||||
.widget blockquote cite,
|
||||
.widget blockquote small {
|
||||
color: %[1]v;
|
||||
}
|
||||
|
||||
.widget button,
|
||||
.widget input[type="button"],
|
||||
.widget input[type="reset"],
|
||||
.widget input[type="submit"],
|
||||
.widget_calendar tbody a {
|
||||
background-color: %[1]v;
|
||||
}
|
||||
|
||||
.textwidget a {
|
||||
border-color: %[1]v;
|
||||
}
|
||||
|
||||
.secondary a:hover,
|
||||
.secondary a:focus,
|
||||
.main-navigation .menu-item-description,
|
||||
.widget,
|
||||
.widget blockquote,
|
||||
.widget .wp-caption-text,
|
||||
.widget .gallery-caption {
|
||||
color: %[2]v;
|
||||
}
|
||||
|
||||
.widget button:hover,
|
||||
.widget button:focus,
|
||||
.widget input[type="button"]:hover,
|
||||
.widget input[type="button"]:focus,
|
||||
.widget input[type="reset"]:hover,
|
||||
.widget input[type="reset"]:focus,
|
||||
.widget input[type="submit"]:hover,
|
||||
.widget input[type="submit"]:focus,
|
||||
.widget_calendar tbody a:hover,
|
||||
.widget_calendar tbody a:focus {
|
||||
background-color: %[2]v;
|
||||
}
|
||||
|
||||
.widget blockquote {
|
||||
border-color: %[2]v;
|
||||
}
|
||||
|
||||
.main-navigation ul,
|
||||
.main-navigation li,
|
||||
.secondary-toggle,
|
||||
.widget input,
|
||||
.widget textarea,
|
||||
.widget table,
|
||||
.widget th,
|
||||
.widget td,
|
||||
.widget pre,
|
||||
.widget li,
|
||||
.widget_categories .children,
|
||||
.widget_nav_menu .sub-menu,
|
||||
.widget_pages .children,
|
||||
.widget abbr[title] {
|
||||
border-color: %[3]v;
|
||||
}
|
||||
|
||||
.dropdown-toggle:hover,
|
||||
.dropdown-toggle:focus,
|
||||
.widget hr {
|
||||
background-color: %[3]v;
|
||||
}
|
||||
|
||||
.widget input:focus,
|
||||
.widget textarea:focus {
|
||||
border-color: %[4]v;
|
||||
}
|
||||
|
||||
.sidebar a:focus,
|
||||
.dropdown-toggle:focus {
|
||||
outline-color: %[4]v;
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1,65 +0,0 @@
|
|||
package twentyfifteen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/fthvgb1/wp-go/helper"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
)
|
||||
|
||||
var postx = map[string]string{
|
||||
"left": "left",
|
||||
"right": "right",
|
||||
"center": "center",
|
||||
}
|
||||
var posty = map[string]string{
|
||||
"top": "top",
|
||||
"bottom": "bottom",
|
||||
"center": "center",
|
||||
}
|
||||
var size = map[string]string{
|
||||
"auto": "auto",
|
||||
"contain": "contain",
|
||||
"cover": "cover",
|
||||
}
|
||||
var repeat = map[string]string{
|
||||
"repeat-x": "repeat-x",
|
||||
"repeat-y": "repeat-y",
|
||||
"repeat": "repeat",
|
||||
"no-repeat": "no-repeat",
|
||||
}
|
||||
|
||||
func CalCustomBackGround(h *wp.Handle) (r string) {
|
||||
themeMods := h.CommonThemeMods()
|
||||
if themeMods.BackgroundImage == "" && (themeMods.BackgroundColor == "" || themeMods.BackgroundColor == themesupport.CustomBackground.DefaultColor) {
|
||||
return
|
||||
}
|
||||
s := str.NewBuilder()
|
||||
if themeMods.BackgroundImage != "" {
|
||||
s.Sprintf(` background-image: url("%s");`, helper.CutUrlHost(themeMods.BackgroundImage))
|
||||
}
|
||||
backgroundPositionX := helper.Defaults(themeMods.BackgroundPositionX, themesupport.CustomBackground.DefaultPositionX)
|
||||
backgroundPositionX = maps.WithDefaultVal(postx, backgroundPositionX, "left")
|
||||
|
||||
backgroundPositionY := helper.Defaults(themeMods.BackgroundPositionY, themesupport.CustomBackground.DefaultPositionY)
|
||||
backgroundPositionY = maps.WithDefaultVal(posty, backgroundPositionY, "top")
|
||||
positon := fmt.Sprintf(" background-position: %s %s;", backgroundPositionX, backgroundPositionY)
|
||||
|
||||
siz := helper.DefaultVal(themeMods.BackgroundSize, themesupport.CustomBackground.DefaultSize)
|
||||
siz = maps.WithDefaultVal(size, siz, "auto")
|
||||
siz = fmt.Sprintf(" background-size: %s;", siz)
|
||||
|
||||
repeats := helper.Defaults(themeMods.BackgroundRepeat, themesupport.CustomBackground.DefaultRepeat)
|
||||
repeats = maps.WithDefaultVal(repeat, repeats, "repeat")
|
||||
repeats = fmt.Sprintf(" background-repeat: %s;", repeats)
|
||||
|
||||
attachment := helper.Defaults(themeMods.BackgroundAttachment, themesupport.CustomBackground.DefaultAttachment)
|
||||
attachment = helper.Or(attachment == "fixed", "fixed", "scroll")
|
||||
attachment = fmt.Sprintf(" background-attachment: %s;", attachment)
|
||||
s.WriteString(positon, siz, repeats, attachment)
|
||||
r = fmt.Sprintf(`<style id="custom-background-css">
|
||||
body.custom-background {%s}
|
||||
</style>`, s.String())
|
||||
return
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
package twentyfifteen
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
var style = `<style type="text/css" id="twentyfifteen-header-css">`
|
||||
var defaultTextStyle = `.site-header {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.site-branding {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 46.25em) {
|
||||
.site-header {
|
||||
padding-top: 21px;
|
||||
padding-bottom: 21px;
|
||||
}
|
||||
.site-branding {
|
||||
min-height: 56px;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 55em) {
|
||||
.site-header {
|
||||
padding-top: 25px;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
.site-branding {
|
||||
min-height: 62px;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 59.6875em) {
|
||||
.site-header {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.site-branding {
|
||||
min-height: 0;
|
||||
}
|
||||
}`
|
||||
var imgStyle = `.site-header {
|
||||
|
||||
/*
|
||||
* No shorthand so the Customizer can override individual properties.
|
||||
* @see https://core.trac.wordpress.org/ticket/31460
|
||||
*/
|
||||
background-image: url("%s");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 59.6875em) {
|
||||
body:before {
|
||||
|
||||
/*
|
||||
* No shorthand so the Customizer can override individual properties.
|
||||
* @see https://core.trac.wordpress.org/ticket/31460
|
||||
*/
|
||||
background-image: url("%s");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 100% 50%;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
background: transparent;
|
||||
}
|
||||
}`
|
||||
|
||||
var header = reload.Vars(constraints.Defaults, "twentyfifteen-customheader")
|
||||
|
||||
func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) {
|
||||
img, rand := h.GetCustomHeaderImg()
|
||||
if img.Path == "" && h.DisplayHeaderText() {
|
||||
return
|
||||
}
|
||||
ss := str.NewBuilder()
|
||||
ss.WriteString(style)
|
||||
if img.Path == "" && !h.DisplayHeaderText() {
|
||||
ss.WriteString(defaultTextStyle)
|
||||
}
|
||||
if img.Path != "" {
|
||||
ss.Sprintf(imgStyle, img.Path, img.Path)
|
||||
}
|
||||
if !h.DisplayHeaderText() {
|
||||
ss.WriteString(`.site-title,
|
||||
.site-description {
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
position: absolute;
|
||||
}`)
|
||||
}
|
||||
ss.WriteString("</style>")
|
||||
r = ss.String()
|
||||
return
|
||||
}
|
||||
|
||||
func customHeader(h *wp.Handle) func() string {
|
||||
return func() string {
|
||||
headers := header.Load()
|
||||
if headers == constraints.Defaults {
|
||||
headerss, rand := calCustomHeaderImg(h)
|
||||
headers = headerss
|
||||
if !rand {
|
||||
header.Store(headers)
|
||||
}
|
||||
}
|
||||
return headers
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{{define "layout/sidebar" }}
|
||||
<div id="widget-area" class="widget-area" role="complementary">
|
||||
{{template "common/sidebarWidget" .}}
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,295 +0,0 @@
|
|||
package twentyfifteen
|
||||
|
||||
import "github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
|
||||
type themeSupport struct {
|
||||
CustomBackground customBackground `json:"custom-background"`
|
||||
EditorColorPalette []EditorColorPalette `json:"editor-color-palette"`
|
||||
EditorGradientPresets []EditorGradientPresets `json:"editor-gradient-presets"`
|
||||
}
|
||||
type customBackground struct {
|
||||
DefaultImage string `json:"default-image"`
|
||||
DefaultPreset string `json:"default-preset"`
|
||||
DefaultPositionX string `json:"default-position-x"`
|
||||
DefaultPositionY string `json:"default-position-y"`
|
||||
DefaultSize string `json:"default-size"`
|
||||
DefaultRepeat string `json:"default-repeat"`
|
||||
DefaultAttachment string `json:"default-attachment"`
|
||||
DefaultColor string `json:"default-color"`
|
||||
WpHeadCallback string `json:"wp-head-callback"`
|
||||
AdminHeadCallback string `json:"admin-head-callback"`
|
||||
AdminPreviewCallback string `json:"admin-preview-callback"`
|
||||
}
|
||||
|
||||
type EditorColorPalette struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
type EditorGradientPresets struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Gradient string `json:"gradient"`
|
||||
}
|
||||
|
||||
var themesupport = themeSupport{
|
||||
CustomBackground: customBackground{
|
||||
DefaultImage: "",
|
||||
DefaultPreset: "default",
|
||||
DefaultPositionX: "left",
|
||||
DefaultPositionY: "top",
|
||||
DefaultSize: "auto",
|
||||
DefaultRepeat: "repeat",
|
||||
DefaultAttachment: "fixed",
|
||||
DefaultColor: "f1f1f1",
|
||||
WpHeadCallback: "_custom_background_cb",
|
||||
AdminHeadCallback: "",
|
||||
AdminPreviewCallback: "",
|
||||
},
|
||||
EditorColorPalette: []EditorColorPalette{
|
||||
{
|
||||
Name: "暗灰色",
|
||||
Slug: "dark-gray",
|
||||
Color: "#111",
|
||||
},
|
||||
{
|
||||
Name: "亮灰色",
|
||||
Slug: "light-gray",
|
||||
Color: "#f1f1f1",
|
||||
},
|
||||
{
|
||||
Name: "白色",
|
||||
Slug: "white",
|
||||
Color: "#fff",
|
||||
},
|
||||
{
|
||||
Name: "黄色",
|
||||
Slug: "yellow",
|
||||
Color: "#f4ca16",
|
||||
},
|
||||
{
|
||||
Name: "暗棕色",
|
||||
Slug: "dark-brown",
|
||||
Color: "#352712",
|
||||
},
|
||||
{
|
||||
Name: "粉色",
|
||||
Slug: "medium-pink",
|
||||
Color: "#e53b51",
|
||||
},
|
||||
{
|
||||
Name: "浅粉色",
|
||||
Slug: "light-pink",
|
||||
Color: "#ffe5d1",
|
||||
},
|
||||
{
|
||||
Name: "暗紫色",
|
||||
Slug: "dark-purple",
|
||||
Color: "#2e2256",
|
||||
},
|
||||
{
|
||||
Name: "紫色",
|
||||
Slug: "purple",
|
||||
Color: "#674970",
|
||||
},
|
||||
{
|
||||
Name: "蓝灰色",
|
||||
Slug: "blue-gray",
|
||||
Color: "#22313f",
|
||||
},
|
||||
{
|
||||
Name: "亮蓝色",
|
||||
Slug: "bright-blue",
|
||||
Color: "#55c3dc",
|
||||
},
|
||||
{
|
||||
Name: "浅蓝色",
|
||||
Slug: "light-blue",
|
||||
Color: "#e9f2f9",
|
||||
},
|
||||
},
|
||||
EditorGradientPresets: []EditorGradientPresets{
|
||||
{
|
||||
Name: "Dark Gray Gradient",
|
||||
Slug: "dark-gray-gradient-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(17,17,17,1) 0%, rgba(42,42,42,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Light Gray Gradient",
|
||||
Slug: "light-gray-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(241,241,241,1) 0%, rgba(215,215,215,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "White Gradient",
|
||||
Slug: "white-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(230,230,230,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Yellow Gradient",
|
||||
Slug: "yellow-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(244,202,22,1) 0%, rgba(205,168,10,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Dark Brown Gradient",
|
||||
Slug: "dark-brown-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(53,39,18,1) 0%, rgba(91,67,31,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Medium Pink Gradient",
|
||||
Slug: "medium-pink-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(229,59,81,1) 0%, rgba(209,28,51,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Light Pink Gradient",
|
||||
Slug: "light-pink-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(255,229,209,1) 0%, rgba(255,200,158,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Dark Purple Gradient",
|
||||
Slug: "dark-purple-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(46,34,86,1) 0%, rgba(66,48,123,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Purple Gradient",
|
||||
Slug: "purple-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(103,73,112,1) 0%, rgba(131,93,143,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Blue Gray Gradient",
|
||||
Slug: "blue-gray-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(34,49,63,1) 0%, rgba(52,75,96,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Bright Blue Gradient",
|
||||
Slug: "bright-blue-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(85,195,220,1) 0%, rgba(43,180,211,1) 100%)",
|
||||
},
|
||||
{
|
||||
Name: "Light Blue Gradient",
|
||||
Slug: "light-blue-gradient",
|
||||
Gradient: "linear-gradient(90deg, rgba(233,242,249,1) 0%, rgba(193,218,238,1) 100%)",
|
||||
},
|
||||
},
|
||||
}
|
||||
var colorscheme = map[string]ColorScheme{
|
||||
"default": {
|
||||
Label: "Default",
|
||||
Colors: []string{
|
||||
"#f1f1f1",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#333333",
|
||||
"#333333",
|
||||
"#f7f7f7",
|
||||
},
|
||||
},
|
||||
"dark": {
|
||||
Label: "Dark",
|
||||
Colors: []string{
|
||||
"#111111",
|
||||
"#202020",
|
||||
"#202020",
|
||||
"#bebebe",
|
||||
"#bebebe",
|
||||
"#1b1b1b",
|
||||
},
|
||||
},
|
||||
|
||||
"pink": {
|
||||
Label: "Pink",
|
||||
Colors: []string{
|
||||
"#ffe5d1",
|
||||
"#e53b51",
|
||||
"#ffffff",
|
||||
"#352712",
|
||||
"#ffffff",
|
||||
"#f1f1f1",
|
||||
},
|
||||
},
|
||||
"purple": {
|
||||
Label: "Purple",
|
||||
Colors: []string{
|
||||
"#674970",
|
||||
"#2e2256",
|
||||
"#ffffff",
|
||||
"#2e2256",
|
||||
"#ffffff",
|
||||
"#f1f1f1",
|
||||
},
|
||||
},
|
||||
"blue": {
|
||||
Label: "Blue",
|
||||
Colors: []string{
|
||||
"#e9f2f9",
|
||||
"#55c3dc",
|
||||
"#ffffff",
|
||||
"#22313f",
|
||||
"#ffffff",
|
||||
"#f1f1f1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var _ = func() struct{} {
|
||||
v := wpconfig.ThemeSupport{
|
||||
CoreBlockPatterns: true,
|
||||
WidgetsBlockEditor: true,
|
||||
AutomaticFeedLinks: true,
|
||||
TitleTag: true,
|
||||
PostThumbnails: true,
|
||||
Menus: true,
|
||||
HTML5: []string{
|
||||
"search-form",
|
||||
"comment-form",
|
||||
"comment-list",
|
||||
"gallery",
|
||||
"caption",
|
||||
"script",
|
||||
"style",
|
||||
"navigation-widgets",
|
||||
},
|
||||
PostFormats: []string{
|
||||
"aside",
|
||||
"image",
|
||||
"video",
|
||||
"quote",
|
||||
"link",
|
||||
"gallery",
|
||||
"status",
|
||||
"audio",
|
||||
"chat",
|
||||
},
|
||||
CustomLogo: wpconfig.CustomLogo{
|
||||
Width: 248,
|
||||
Height: 248,
|
||||
FlexWidth: false,
|
||||
FlexHeight: true,
|
||||
HeaderText: "",
|
||||
UnlinkHomepageLogo: false,
|
||||
},
|
||||
CustomizeSelectiveRefreshWidgets: true,
|
||||
EditorStyle: true,
|
||||
EditorStyles: true,
|
||||
WpBlockStyles: true,
|
||||
ResponsiveEmbeds: true,
|
||||
CustomHeader: wpconfig.CustomHeader{
|
||||
DefaultImage: "",
|
||||
RandomDefault: false,
|
||||
Width: 954,
|
||||
Height: 1300,
|
||||
FlexHeight: false,
|
||||
FlexWidth: false,
|
||||
DefaultTextColor: "333333",
|
||||
HeaderText: true,
|
||||
Uploads: true,
|
||||
WpHeadCallback: "twentyfifteen_header_style",
|
||||
AdminHeadCallback: "",
|
||||
AdminPreviewCallback: "",
|
||||
Video: false,
|
||||
VideoActiveCallback: "is_front_page",
|
||||
},
|
||||
Widgets: true,
|
||||
}
|
||||
wpconfig.SetThemeSupport(ThemeName, v)
|
||||
return struct{}{}
|
||||
}()
|
|
@ -1,63 +0,0 @@
|
|||
package twentyfifteen
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints/widgets"
|
||||
"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/wpconfig"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ThemeName = "twentyfifteen"
|
||||
|
||||
func Hook(h *wp.Handle) {
|
||||
wp.Run(h, configs)
|
||||
}
|
||||
|
||||
func configs(h *wp.Handle) {
|
||||
h.AddActionFilter(widgets.Search, func(h *wp.Handle, s string, args ...any) string {
|
||||
return strings.ReplaceAll(s, `class="search-submit"`, `class="search-submit screen-reader-text"`)
|
||||
})
|
||||
wp.InitPipe(h)
|
||||
middleware.CommonMiddleware(h)
|
||||
setPaginationAndRender(h)
|
||||
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.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"))
|
||||
h.PushDataHandler(constraints.Detail, wp.NewHandleFn(wp.Detail, 100.005, "wp.Detail"))
|
||||
wp.PushIndexHandler(constraints.PipeData, h, wp.NewHandleFn(wp.Index, 100.005, "wp.Index"))
|
||||
h.PushDataHandler(constraints.AllScene, wp.NewHandleFn(wp.PreCodeAndStats, 80.005, "wp.PreCodeAndStats"))
|
||||
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", "")
|
||||
}
|
||||
}
|
||||
|
||||
func renderCustomHeader(h *wp.Handle) {
|
||||
h.SetData("customHeader", customHeader(h))
|
||||
}
|
|
@ -1,574 +0,0 @@
|
|||
package twentyseventeen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/number"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
)
|
||||
|
||||
func colorScheme(h *wp.Handle) (r string) {
|
||||
if "custom" != wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") {
|
||||
return
|
||||
}
|
||||
s := str.NewBuilder()
|
||||
hue := number.ToString(wpconfig.GetThemeModsVal[int64](ThemeName, "colorscheme_hue", 250))
|
||||
reducedSaturation := fmt.Sprintf("%d%%", int(.8*50))
|
||||
saturation := fmt.Sprintf("%d%%", 50)
|
||||
css := str.Replace(customCss, map[string]string{
|
||||
"' . $hue . '": hue,
|
||||
"' . esc_attr( $hue ) . '": hue,
|
||||
"' . $saturation . '": saturation,
|
||||
"' . esc_attr( $saturation ) .' ": saturation,
|
||||
"' . $reduced_saturation . '": reducedSaturation,
|
||||
})
|
||||
s.Sprintf(`<style type="text/css" id="custom-theme-colors">%s</style>`, css)
|
||||
r = s.String()
|
||||
return
|
||||
}
|
||||
|
||||
var customCss = `
|
||||
/**
|
||||
* Twenty Seventeen: Color Patterns
|
||||
*
|
||||
* Colors are ordered from dark to light.
|
||||
*/
|
||||
|
||||
.colors-custom a:hover,
|
||||
.colors-custom a:active,
|
||||
.colors-custom .entry-content a:focus,
|
||||
.colors-custom .entry-content a:hover,
|
||||
.colors-custom .entry-summary a:focus,
|
||||
.colors-custom .entry-summary a:hover,
|
||||
.colors-custom .comment-content a:focus,
|
||||
.colors-custom .comment-content a:hover,
|
||||
.colors-custom .widget a:focus,
|
||||
.colors-custom .widget a:hover,
|
||||
.colors-custom .site-footer .widget-area a:focus,
|
||||
.colors-custom .site-footer .widget-area a:hover,
|
||||
.colors-custom .posts-navigation a:focus,
|
||||
.colors-custom .posts-navigation a:hover,
|
||||
.colors-custom .comment-metadata a:focus,
|
||||
.colors-custom .comment-metadata a:hover,
|
||||
.colors-custom .comment-metadata a.comment-edit-link:focus,
|
||||
.colors-custom .comment-metadata a.comment-edit-link:hover,
|
||||
.colors-custom .comment-reply-link:focus,
|
||||
.colors-custom .comment-reply-link:hover,
|
||||
.colors-custom .widget_authors a:focus strong,
|
||||
.colors-custom .widget_authors a:hover strong,
|
||||
.colors-custom .entry-title a:focus,
|
||||
.colors-custom .entry-title a:hover,
|
||||
.colors-custom .entry-meta a:focus,
|
||||
.colors-custom .entry-meta a:hover,
|
||||
.colors-custom.blog .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.blog .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom.archive .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.archive .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom.search .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.search .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom .page-links a:focus .page-number,
|
||||
.colors-custom .page-links a:hover .page-number,
|
||||
.colors-custom .entry-footer a:focus,
|
||||
.colors-custom .entry-footer a:hover,
|
||||
.colors-custom .entry-footer .cat-links a:focus,
|
||||
.colors-custom .entry-footer .cat-links a:hover,
|
||||
.colors-custom .entry-footer .tags-links a:focus,
|
||||
.colors-custom .entry-footer .tags-links a:hover,
|
||||
.colors-custom .post-navigation a:focus,
|
||||
.colors-custom .post-navigation a:hover,
|
||||
.colors-custom .pagination a:not(.prev):not(.next):focus,
|
||||
.colors-custom .pagination a:not(.prev):not(.next):hover,
|
||||
.colors-custom .comments-pagination a:not(.prev):not(.next):focus,
|
||||
.colors-custom .comments-pagination a:not(.prev):not(.next):hover,
|
||||
.colors-custom .logged-in-as a:focus,
|
||||
.colors-custom .logged-in-as a:hover,
|
||||
.colors-custom a:focus .nav-title,
|
||||
.colors-custom a:hover .nav-title,
|
||||
.colors-custom .edit-link a:focus,
|
||||
.colors-custom .edit-link a:hover,
|
||||
.colors-custom .site-info a:focus,
|
||||
.colors-custom .site-info a:hover,
|
||||
.colors-custom .widget .widget-title a:focus,
|
||||
.colors-custom .widget .widget-title a:hover,
|
||||
.colors-custom .widget ul li a:focus,
|
||||
.colors-custom .widget ul li a:hover {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 0% ); /* base: #000; */
|
||||
}
|
||||
|
||||
.colors-custom .entry-content a,
|
||||
.colors-custom .entry-summary a,
|
||||
.colors-custom .comment-content a,
|
||||
.colors-custom .widget a,
|
||||
.colors-custom .site-footer .widget-area a,
|
||||
.colors-custom .posts-navigation a,
|
||||
.colors-custom .widget_authors a strong {
|
||||
-webkit-box-shadow: inset 0 -1px 0 hsl( ' . $hue . ', ' . $saturation . ', 6% ); /* base: rgba(15, 15, 15, 1); */
|
||||
box-shadow: inset 0 -1px 0 hsl( ' . $hue . ', ' . $saturation . ', 6% ); /* base: rgba(15, 15, 15, 1); */
|
||||
}
|
||||
|
||||
.colors-custom button,
|
||||
.colors-custom input[type="button"],
|
||||
.colors-custom input[type="submit"],
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link {
|
||||
background-color: hsl( ' . $hue . ', ' . $saturation . ', 13% ); /* base: #222; */
|
||||
}
|
||||
|
||||
.colors-custom input[type="text"]:focus,
|
||||
.colors-custom input[type="email"]:focus,
|
||||
.colors-custom input[type="url"]:focus,
|
||||
.colors-custom input[type="password"]:focus,
|
||||
.colors-custom input[type="search"]:focus,
|
||||
.colors-custom input[type="number"]:focus,
|
||||
.colors-custom input[type="tel"]:focus,
|
||||
.colors-custom input[type="range"]:focus,
|
||||
.colors-custom input[type="date"]:focus,
|
||||
.colors-custom input[type="month"]:focus,
|
||||
.colors-custom input[type="week"]:focus,
|
||||
.colors-custom input[type="time"]:focus,
|
||||
.colors-custom input[type="datetime"]:focus,
|
||||
.colors-custom .colors-custom input[type="datetime-local"]:focus,
|
||||
.colors-custom input[type="color"]:focus,
|
||||
.colors-custom textarea:focus,
|
||||
.colors-custom button.secondary,
|
||||
.colors-custom input[type="reset"],
|
||||
.colors-custom input[type="button"].secondary,
|
||||
.colors-custom input[type="reset"].secondary,
|
||||
.colors-custom input[type="submit"].secondary,
|
||||
.colors-custom a,
|
||||
.colors-custom .site-title,
|
||||
.colors-custom .site-title a,
|
||||
.colors-custom .navigation-top a,
|
||||
.colors-custom .dropdown-toggle,
|
||||
.colors-custom .menu-toggle,
|
||||
.colors-custom .page .panel-content .entry-title,
|
||||
.colors-custom .page-title,
|
||||
.colors-custom.page:not(.twentyseventeen-front-page) .entry-title,
|
||||
.colors-custom .page-links a .page-number,
|
||||
.colors-custom .comment-metadata a.comment-edit-link,
|
||||
.colors-custom .comment-reply-link .icon,
|
||||
.colors-custom h2.widget-title,
|
||||
.colors-custom mark,
|
||||
.colors-custom .post-navigation a:focus .icon,
|
||||
.colors-custom .post-navigation a:hover .icon,
|
||||
.colors-custom .site-content .site-content-light,
|
||||
.colors-custom .twentyseventeen-panel .recent-posts .entry-header .edit-link {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 13% ); /* base: #222; */
|
||||
}
|
||||
|
||||
.colors-custom .entry-content a:focus,
|
||||
.colors-custom .entry-content a:hover,
|
||||
.colors-custom .entry-summary a:focus,
|
||||
.colors-custom .entry-summary a:hover,
|
||||
.colors-custom .comment-content a:focus,
|
||||
.colors-custom .comment-content a:hover,
|
||||
.colors-custom .widget a:focus,
|
||||
.colors-custom .widget a:hover,
|
||||
.colors-custom .site-footer .widget-area a:focus,
|
||||
.colors-custom .site-footer .widget-area a:hover,
|
||||
.colors-custom .posts-navigation a:focus,
|
||||
.colors-custom .posts-navigation a:hover,
|
||||
.colors-custom .comment-metadata a:focus,
|
||||
.colors-custom .comment-metadata a:hover,
|
||||
.colors-custom .comment-metadata a.comment-edit-link:focus,
|
||||
.colors-custom .comment-metadata a.comment-edit-link:hover,
|
||||
.colors-custom .comment-reply-link:focus,
|
||||
.colors-custom .comment-reply-link:hover,
|
||||
.colors-custom .widget_authors a:focus strong,
|
||||
.colors-custom .widget_authors a:hover strong,
|
||||
.colors-custom .entry-title a:focus,
|
||||
.colors-custom .entry-title a:hover,
|
||||
.colors-custom .entry-meta a:focus,
|
||||
.colors-custom .entry-meta a:hover,
|
||||
.colors-custom.blog .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.blog .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom.archive .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.archive .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom.search .entry-meta a.post-edit-link:focus,
|
||||
.colors-custom.search .entry-meta a.post-edit-link:hover,
|
||||
.colors-custom .page-links a:focus .page-number,
|
||||
.colors-custom .page-links a:hover .page-number,
|
||||
.colors-custom .entry-footer .cat-links a:focus,
|
||||
.colors-custom .entry-footer .cat-links a:hover,
|
||||
.colors-custom .entry-footer .tags-links a:focus,
|
||||
.colors-custom .entry-footer .tags-links a:hover,
|
||||
.colors-custom .post-navigation a:focus,
|
||||
.colors-custom .post-navigation a:hover,
|
||||
.colors-custom .pagination a:not(.prev):not(.next):focus,
|
||||
.colors-custom .pagination a:not(.prev):not(.next):hover,
|
||||
.colors-custom .comments-pagination a:not(.prev):not(.next):focus,
|
||||
.colors-custom .comments-pagination a:not(.prev):not(.next):hover,
|
||||
.colors-custom .logged-in-as a:focus,
|
||||
.colors-custom .logged-in-as a:hover,
|
||||
.colors-custom a:focus .nav-title,
|
||||
.colors-custom a:hover .nav-title,
|
||||
.colors-custom .edit-link a:focus,
|
||||
.colors-custom .edit-link a:hover,
|
||||
.colors-custom .site-info a:focus,
|
||||
.colors-custom .site-info a:hover,
|
||||
.colors-custom .widget .widget-title a:focus,
|
||||
.colors-custom .widget .widget-title a:hover,
|
||||
.colors-custom .widget ul li a:focus,
|
||||
.colors-custom .widget ul li a:hover {
|
||||
-webkit-box-shadow: inset 0 0 0 hsl( ' . $hue . ', ' . $saturation . ', 13% ), 0 3px 0 hsl( ' . $hue . ', ' . $saturation . ', 13% );
|
||||
box-shadow: inset 0 0 0 hsl( ' . $hue . ', ' . $saturation . ' , 13% ), 0 3px 0 hsl( ' . $hue . ', ' . $saturation . ', 13% );
|
||||
}
|
||||
|
||||
body.colors-custom,
|
||||
.colors-custom button,
|
||||
.colors-custom input,
|
||||
.colors-custom select,
|
||||
.colors-custom textarea,
|
||||
.colors-custom h3,
|
||||
.colors-custom h4,
|
||||
.colors-custom h6,
|
||||
.colors-custom label,
|
||||
.colors-custom .entry-title a,
|
||||
.colors-custom.twentyseventeen-front-page .panel-content .recent-posts article,
|
||||
.colors-custom .entry-footer .cat-links a,
|
||||
.colors-custom .entry-footer .tags-links a,
|
||||
.colors-custom .format-quote blockquote,
|
||||
.colors-custom .nav-title,
|
||||
.colors-custom .comment-body,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-current-item .wp-playlist-item-album {
|
||||
color: hsl( ' . $hue . ', ' . $reduced_saturation . ', 20% ); /* base: #333; */
|
||||
}
|
||||
|
||||
.colors-custom .social-navigation a:hover,
|
||||
.colors-custom .social-navigation a:focus {
|
||||
background: hsl( ' . $hue . ', ' . $reduced_saturation . ', 20% ); /* base: #333; */
|
||||
}
|
||||
|
||||
.colors-custom input[type="text"]:focus,
|
||||
.colors-custom input[type="email"]:focus,
|
||||
.colors-custom input[type="url"]:focus,
|
||||
.colors-custom input[type="password"]:focus,
|
||||
.colors-custom input[type="search"]:focus,
|
||||
.colors-custom input[type="number"]:focus,
|
||||
.colors-custom input[type="tel"]:focus,
|
||||
.colors-custom input[type="range"]:focus,
|
||||
.colors-custom input[type="date"]:focus,
|
||||
.colors-custom input[type="month"]:focus,
|
||||
.colors-custom input[type="week"]:focus,
|
||||
.colors-custom input[type="time"]:focus,
|
||||
.colors-custom input[type="datetime"]:focus,
|
||||
.colors-custom input[type="datetime-local"]:focus,
|
||||
.colors-custom input[type="color"]:focus,
|
||||
.colors-custom textarea:focus,
|
||||
.bypostauthor > .comment-body > .comment-meta > .comment-author .avatar {
|
||||
border-color: hsl( ' . $hue . ', ' . $reduced_saturation . ', 20% ); /* base: #333; */
|
||||
}
|
||||
|
||||
.colors-custom h2,
|
||||
.colors-custom blockquote,
|
||||
.colors-custom input[type="text"],
|
||||
.colors-custom input[type="email"],
|
||||
.colors-custom input[type="url"],
|
||||
.colors-custom input[type="password"],
|
||||
.colors-custom input[type="search"],
|
||||
.colors-custom input[type="number"],
|
||||
.colors-custom input[type="tel"],
|
||||
.colors-custom input[type="range"],
|
||||
.colors-custom input[type="date"],
|
||||
.colors-custom input[type="month"],
|
||||
.colors-custom input[type="week"],
|
||||
.colors-custom input[type="time"],
|
||||
.colors-custom input[type="datetime"],
|
||||
.colors-custom input[type="datetime-local"],
|
||||
.colors-custom input[type="color"],
|
||||
.colors-custom textarea,
|
||||
.colors-custom .site-description,
|
||||
.colors-custom .entry-content blockquote.alignleft,
|
||||
.colors-custom .entry-content blockquote.alignright,
|
||||
.colors-custom .colors-custom .taxonomy-description,
|
||||
.colors-custom .site-info a,
|
||||
.colors-custom .wp-caption,
|
||||
.colors-custom .gallery-caption {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 40% ); /* base: #666; */
|
||||
}
|
||||
|
||||
.colors-custom abbr,
|
||||
.colors-custom acronym {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 40% ); /* base: #666; */
|
||||
}
|
||||
|
||||
.colors-custom h5,
|
||||
.colors-custom .entry-meta,
|
||||
.colors-custom .entry-meta a,
|
||||
.colors-custom.blog .entry-meta a.post-edit-link,
|
||||
.colors-custom.archive .entry-meta a.post-edit-link,
|
||||
.colors-custom.search .entry-meta a.post-edit-link,
|
||||
.colors-custom .nav-subtitle,
|
||||
.colors-custom .comment-metadata,
|
||||
.colors-custom .comment-metadata a,
|
||||
.colors-custom .no-comments,
|
||||
.colors-custom .comment-awaiting-moderation,
|
||||
.colors-custom .page-numbers.current,
|
||||
.colors-custom .page-links .page-number,
|
||||
.colors-custom .navigation-top .current-menu-item > a,
|
||||
.colors-custom .navigation-top .current_page_item > a,
|
||||
.colors-custom .main-navigation a:hover,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-current-item .wp-playlist-item-artist {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 46% ); /* base: #767676; */
|
||||
}
|
||||
|
||||
.colors-custom :not( .mejs-button ) > button:hover,
|
||||
.colors-custom :not( .mejs-button ) > button:focus,
|
||||
.colors-custom input[type="button"]:hover,
|
||||
.colors-custom input[type="button"]:focus,
|
||||
.colors-custom input[type="submit"]:hover,
|
||||
.colors-custom input[type="submit"]:focus,
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link:hover,
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link:focus,
|
||||
.colors-custom .social-navigation a,
|
||||
.colors-custom .prev.page-numbers:focus,
|
||||
.colors-custom .prev.page-numbers:hover,
|
||||
.colors-custom .next.page-numbers:focus,
|
||||
.colors-custom .next.page-numbers:hover,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:hover,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:focus {
|
||||
background: hsl( ' . esc_attr( $hue ) . ', ' . esc_attr( $saturation ) . ', 46% ); /* base: #767676; */
|
||||
}
|
||||
|
||||
.colors-custom button.secondary:hover,
|
||||
.colors-custom button.secondary:focus,
|
||||
.colors-custom input[type="reset"]:hover,
|
||||
.colors-custom input[type="reset"]:focus,
|
||||
.colors-custom input[type="button"].secondary:hover,
|
||||
.colors-custom input[type="button"].secondary:focus,
|
||||
.colors-custom input[type="reset"].secondary:hover,
|
||||
.colors-custom input[type="reset"].secondary:focus,
|
||||
.colors-custom input[type="submit"].secondary:hover,
|
||||
.colors-custom input[type="submit"].secondary:focus,
|
||||
.colors-custom hr {
|
||||
background: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
}
|
||||
|
||||
.colors-custom input[type="text"],
|
||||
.colors-custom input[type="email"],
|
||||
.colors-custom input[type="url"],
|
||||
.colors-custom input[type="password"],
|
||||
.colors-custom input[type="search"],
|
||||
.colors-custom input[type="number"],
|
||||
.colors-custom input[type="tel"],
|
||||
.colors-custom input[type="range"],
|
||||
.colors-custom input[type="date"],
|
||||
.colors-custom input[type="month"],
|
||||
.colors-custom input[type="week"],
|
||||
.colors-custom input[type="time"],
|
||||
.colors-custom input[type="datetime"],
|
||||
.colors-custom input[type="datetime-local"],
|
||||
.colors-custom input[type="color"],
|
||||
.colors-custom textarea,
|
||||
.colors-custom select,
|
||||
.colors-custom fieldset,
|
||||
.colors-custom .widget .tagcloud a:hover,
|
||||
.colors-custom .widget .tagcloud a:focus,
|
||||
.colors-custom .widget.widget_tag_cloud a:hover,
|
||||
.colors-custom .widget.widget_tag_cloud a:focus,
|
||||
.colors-custom .wp_widget_tag_cloud a:hover,
|
||||
.colors-custom .wp_widget_tag_cloud a:focus {
|
||||
border-color: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
}
|
||||
|
||||
.colors-custom thead th {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
}
|
||||
|
||||
.colors-custom .entry-footer .cat-links .icon,
|
||||
.colors-custom .entry-footer .tags-links .icon {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
}
|
||||
|
||||
.colors-custom button.secondary,
|
||||
.colors-custom input[type="reset"],
|
||||
.colors-custom input[type="button"].secondary,
|
||||
.colors-custom input[type="reset"].secondary,
|
||||
.colors-custom input[type="submit"].secondary,
|
||||
.colors-custom .prev.page-numbers,
|
||||
.colors-custom .next.page-numbers {
|
||||
background-color: hsl( ' . $hue . ', ' . $saturation . ', 87% ); /* base: #ddd; */
|
||||
}
|
||||
|
||||
.colors-custom .widget .tagcloud a,
|
||||
.colors-custom .widget.widget_tag_cloud a,
|
||||
.colors-custom .wp_widget_tag_cloud a {
|
||||
border-color: hsl( ' . $hue . ', ' . $saturation . ', 87% ); /* base: #ddd; */
|
||||
}
|
||||
|
||||
.colors-custom.twentyseventeen-front-page article:not(.has-post-thumbnail):not(:first-child),
|
||||
.colors-custom .widget ul li {
|
||||
border-top-color: hsl( ' . $hue . ', ' . $saturation . ', 87% ); /* base: #ddd; */
|
||||
}
|
||||
|
||||
.colors-custom .widget ul li {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 87% ); /* base: #ddd; */
|
||||
}
|
||||
|
||||
.colors-custom pre,
|
||||
.colors-custom mark,
|
||||
.colors-custom ins {
|
||||
background: hsl( ' . $hue . ', ' . $saturation . ', 93% ); /* base: #eee; */
|
||||
}
|
||||
|
||||
.colors-custom .navigation-top,
|
||||
.colors-custom .main-navigation > div > ul,
|
||||
.colors-custom .pagination,
|
||||
.colors-custom .comments-pagination,
|
||||
.colors-custom .entry-footer,
|
||||
.colors-custom .site-footer {
|
||||
border-top-color: hsl( ' . $hue . ', ' . $saturation . ', 93% ); /* base: #eee; */
|
||||
}
|
||||
|
||||
.colors-custom .navigation-top,
|
||||
.colors-custom .main-navigation li,
|
||||
.colors-custom .entry-footer,
|
||||
.colors-custom .single-featured-image-header,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item,
|
||||
.colors-custom tr {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 93% ); /* base: #eee; */
|
||||
}
|
||||
|
||||
.colors-custom .site-content .wp-playlist-light {
|
||||
border-color: hsl( ' . $hue . ', ' . $saturation . ', 93% ); /* base: #eee; */
|
||||
}
|
||||
|
||||
.colors-custom .site-header,
|
||||
.colors-custom .single-featured-image-header {
|
||||
background-color: hsl( ' . $hue . ', ' . $saturation . ', 98% ); /* base: #fafafa; */
|
||||
}
|
||||
|
||||
.colors-custom button,
|
||||
.colors-custom input[type="button"],
|
||||
.colors-custom input[type="submit"],
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link,
|
||||
.colors-custom .social-navigation a,
|
||||
.colors-custom .site-content .wp-playlist-light a.wp-playlist-caption:hover,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:hover a,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:focus a,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:hover,
|
||||
.colors-custom .site-content .wp-playlist-light .wp-playlist-item:focus,
|
||||
.colors-custom .prev.page-numbers:focus,
|
||||
.colors-custom .prev.page-numbers:hover,
|
||||
.colors-custom .next.page-numbers:focus,
|
||||
.colors-custom .next.page-numbers:hover,
|
||||
.colors-custom.has-header-image .site-title,
|
||||
.colors-custom.has-header-video .site-title,
|
||||
.colors-custom.has-header-image .site-title a,
|
||||
.colors-custom.has-header-video .site-title a,
|
||||
.colors-custom.has-header-image .site-description,
|
||||
.colors-custom.has-header-video .site-description {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: #fff; */
|
||||
}
|
||||
|
||||
body.colors-custom,
|
||||
.colors-custom .navigation-top,
|
||||
.colors-custom .main-navigation ul {
|
||||
background: hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: #fff; */
|
||||
}
|
||||
|
||||
.colors-custom .widget ul li a,
|
||||
.colors-custom .site-footer .widget-area ul li a {
|
||||
-webkit-box-shadow: inset 0 -1px 0 hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: rgba(255, 255, 255, 1); */
|
||||
box-shadow: inset 0 -1px 0 hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: rgba(255, 255, 255, 1); */
|
||||
}
|
||||
|
||||
.colors-custom .menu-toggle,
|
||||
.colors-custom .menu-toggle:hover,
|
||||
.colors-custom .menu-toggle:focus,
|
||||
.colors-custom .menu .dropdown-toggle,
|
||||
.colors-custom .menu-scroll-down,
|
||||
.colors-custom .menu-scroll-down:hover,
|
||||
.colors-custom .menu-scroll-down:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.colors-custom .widget .tagcloud a,
|
||||
.colors-custom .widget .tagcloud a:focus,
|
||||
.colors-custom .widget .tagcloud a:hover,
|
||||
.colors-custom .widget.widget_tag_cloud a,
|
||||
.colors-custom .widget.widget_tag_cloud a:focus,
|
||||
.colors-custom .widget.widget_tag_cloud a:hover,
|
||||
.colors-custom .wp_widget_tag_cloud a,
|
||||
.colors-custom .wp_widget_tag_cloud a:focus,
|
||||
.colors-custom .wp_widget_tag_cloud a:hover,
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link:focus,
|
||||
.colors-custom .entry-footer .edit-link a.post-edit-link:hover {
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Reset non-customizable hover styling for links */
|
||||
.colors-custom .entry-content a:hover,
|
||||
.colors-custom .entry-content a:focus,
|
||||
.colors-custom .entry-summary a:hover,
|
||||
.colors-custom .entry-summary a:focus,
|
||||
.colors-custom .comment-content a:focus,
|
||||
.colors-custom .comment-content a:hover,
|
||||
.colors-custom .widget a:hover,
|
||||
.colors-custom .widget a:focus,
|
||||
.colors-custom .site-footer .widget-area a:hover,
|
||||
.colors-custom .site-footer .widget-area a:focus,
|
||||
.colors-custom .posts-navigation a:hover,
|
||||
.colors-custom .posts-navigation a:focus,
|
||||
.colors-custom .widget_authors a:hover strong,
|
||||
.colors-custom .widget_authors a:focus strong {
|
||||
-webkit-box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 3px 0 rgba(0, 0, 0, 1);
|
||||
box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 3px 0 rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.colors-custom .gallery-item a,
|
||||
.colors-custom .gallery-item a:hover,
|
||||
.colors-custom .gallery-item a:focus {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 48em) {
|
||||
|
||||
.colors-custom .nav-links .nav-previous .nav-title .icon,
|
||||
.colors-custom .nav-links .nav-next .nav-title .icon {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 20% ); /* base: #222; */
|
||||
}
|
||||
|
||||
.colors-custom .main-navigation li li:hover,
|
||||
.colors-custom .main-navigation li li.focus {
|
||||
background: hsl( ' . $hue . ', ' . $saturation . ', 46% ); /* base: #767676; */
|
||||
}
|
||||
|
||||
.colors-custom .navigation-top .menu-scroll-down {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 46% ); /* base: #767676; */;
|
||||
}
|
||||
|
||||
.colors-custom abbr[title] {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 46% ); /* base: #767676; */;
|
||||
}
|
||||
|
||||
.colors-custom .main-navigation ul ul {
|
||||
border-color: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
background: hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: #fff; */
|
||||
}
|
||||
|
||||
.colors-custom .main-navigation ul li.menu-item-has-children:before,
|
||||
.colors-custom .main-navigation ul li.page_item_has_children:before {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 73% ); /* base: #bbb; */
|
||||
}
|
||||
|
||||
.colors-custom .main-navigation ul li.menu-item-has-children:after,
|
||||
.colors-custom .main-navigation ul li.page_item_has_children:after {
|
||||
border-bottom-color: hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: #fff; */
|
||||
}
|
||||
|
||||
.colors-custom .main-navigation li li.focus > a,
|
||||
.colors-custom .main-navigation li li:focus > a,
|
||||
.colors-custom .main-navigation li li:hover > a,
|
||||
.colors-custom .main-navigation li li a:hover,
|
||||
.colors-custom .main-navigation li li a:focus,
|
||||
.colors-custom .main-navigation li li.current_page_item a:hover,
|
||||
.colors-custom .main-navigation li li.current-menu-item a:hover,
|
||||
.colors-custom .main-navigation li li.current_page_item a:focus,
|
||||
.colors-custom .main-navigation li li.current-menu-item a:focus {
|
||||
color: hsl( ' . $hue . ', ' . $saturation . ', 100% ); /* base: #fff; */
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1,48 +0,0 @@
|
|||
package twentyseventeen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
)
|
||||
|
||||
func customHeader(h *wp.Handle) (r string) {
|
||||
themeMods := h.CommonThemeMods()
|
||||
headerTextColor := themeMods.HeaderTextcolor
|
||||
if headerTextColor == "" || headerTextColor == themeMods.ThemeSupport.CustomHeader.DefaultTextColor {
|
||||
return
|
||||
}
|
||||
css := `
|
||||
.site-title,
|
||||
.site-description {
|
||||
position: absolute;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
}`
|
||||
if headerTextColor != "blank" {
|
||||
css = fmt.Sprintf(customHeaderCss, headerTextColor)
|
||||
}
|
||||
r = fmt.Sprintf(`<style id="twentyseventeen-custom-header-styles" type="text/css">%s</style>`, css)
|
||||
return
|
||||
}
|
||||
|
||||
var customHeaderCss = `
|
||||
.site-title a,
|
||||
.colors-dark .site-title a,
|
||||
.colors-custom .site-title a,
|
||||
body.has-header-image .site-title a,
|
||||
body.has-header-video .site-title a,
|
||||
body.has-header-image.colors-dark .site-title a,
|
||||
body.has-header-video.colors-dark .site-title a,
|
||||
body.has-header-image.colors-custom .site-title a,
|
||||
body.has-header-video.colors-custom .site-title a,
|
||||
.site-description,
|
||||
.colors-dark .site-description,
|
||||
.colors-custom .site-description,
|
||||
body.has-header-image .site-description,
|
||||
body.has-header-video .site-description,
|
||||
body.has-header-image.colors-dark .site-description,
|
||||
body.has-header-video.colors-dark .site-description,
|
||||
body.has-header-image.colors-custom .site-description,
|
||||
body.has-header-video.colors-custom .site-description {
|
||||
color: #%s;
|
||||
}
|
||||
`
|
|
@ -1,48 +0,0 @@
|
|||
{{define "layout/empty"}}
|
||||
<div class="wrap">
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main">
|
||||
<section class="error-404 not-found">
|
||||
<header class="page-header">
|
||||
<h1 class="page-title">
|
||||
{{if .search}}
|
||||
未找到
|
||||
{{else}}
|
||||
有点尴尬诶!该页无法显示。
|
||||
{{end}}
|
||||
</h1>
|
||||
</header><!-- .page-header -->
|
||||
|
||||
<div class="page-content">
|
||||
|
||||
<p>{{if .search}}
|
||||
抱歉,没有符合您搜索条件的结果。请换其它关键词再试。
|
||||
{{else}}
|
||||
这儿似乎什么都没有,试试搜索?
|
||||
{{end}}
|
||||
</p>
|
||||
<form role="search" method="get" class="search-form" action="/">
|
||||
<label for="search-form-1">
|
||||
<span class="screen-reader-text">搜索:</span>
|
||||
</label>
|
||||
<input type="search" id="search-form-1" class="search-field" placeholder="搜索…" value="{{.search}}" name="s">
|
||||
<button type="submit" class="search-submit">
|
||||
<svg class="icon icon-search" aria-hidden="true" role="img"> <use href="#icon-search" xlink:href="#icon-search"></use> </svg>
|
||||
<span class="screen-reader-text">搜索</span>
|
||||
</button>
|
||||
</form>
|
||||
</div><!-- .page-content -->
|
||||
</section><!-- .no-results -->
|
||||
|
||||
</main><!-- .site-main -->
|
||||
|
||||
</div>
|
||||
|
||||
{{if .search }}
|
||||
<aside id="secondary" class="widget-area" aria-label="博客边栏">
|
||||
{{template "layout/sidebar" .}}
|
||||
</aside>
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,5 +0,0 @@
|
|||
{{define "layout/footer"}}
|
||||
{{template "common/footer" .}}
|
||||
{{ block "footer" .}}
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -1,3 +0,0 @@
|
|||
{{define "layout/sidebar" }}
|
||||
{{template "common/sidebarWidget" .}}
|
||||
{{end}}
|
|
@ -1,16 +0,0 @@
|
|||
{{template "layout/base" .}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="site-content-contain">
|
||||
<div id="content" class="site-content">
|
||||
<div class="wrap">
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main">
|
||||
{{template "layout/empty"}}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package twentyseventeen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
)
|
||||
|
||||
func pushScripts(h *wp.Handle) {
|
||||
h.PushCacheGroupHeadScript(constraints.AllScene, "{theme}.head", 30, func(h *wp.Handle) string {
|
||||
head := headScript
|
||||
if "dark" == wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light") {
|
||||
head = fmt.Sprintf("%s\n%s", headScript, ` <link rel="stylesheet" id="twentyseventeen-colors-dark-css" href="/wp-content/themes/twentyseventeen/assets/css/colors-dark.css?ver=20191025" media="all">`)
|
||||
}
|
||||
return head
|
||||
})
|
||||
h.PushGroupFooterScript(constraints.AllScene, "{theme}.footer", 20.005, footerScript)
|
||||
|
||||
}
|
||||
|
||||
var headScript = `<link rel='stylesheet' id='twentyseventeen-style-css' href='/wp-content/themes/twentyseventeen/style.css?ver=20221101' media='all' />
|
||||
<link rel='stylesheet' id='twentyseventeen-block-style-css' href='/wp-content/themes/twentyseventeen/assets/css/blocks.css?ver=20220912' media='all' />
|
||||
<!--[if lt IE 9]>
|
||||
<link rel='stylesheet' id='twentyseventeen-ie8-css' href='/wp-content/themes/twentyseventeen/assets/css/ie8.css?ver=20161202' media='all' />
|
||||
<![endif]-->
|
||||
<!--[if lt IE 9]>
|
||||
<script src='/wp-content/themes/twentyseventeen/assets/js/html5.js?ver=20161020' id='html5-js'></script>
|
||||
<![endif]-->
|
||||
<script src='/wp-includes/js/jquery/jquery.min.js?ver=3.6.0' id='jquery-core-js'></script>
|
||||
<script src='/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.3.2' id='jquery-migrate-js'></script>
|
||||
<link rel="https://api.w.org/" href="/wp-json/" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="/xmlrpc.php?rsd" />
|
||||
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="/wp-includes/wlwmanifest.xml" />
|
||||
<meta name="generator" content="WordPress 6.1.1" />
|
||||
<style>.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>`
|
||||
|
||||
var footerScript = `<script id="twentyseventeen-skip-link-focus-fix-js-extra">
|
||||
var twentyseventeenScreenReaderText = {"quote":"<svg class=\"icon icon-quote-right\" aria-hidden=\"true\" role=\"img\"> <use href=\"#icon-quote-right\" xlink:href=\"#icon-quote-right\"><\/use> <\/svg>"};
|
||||
</script>
|
||||
|
||||
<script src="/wp-content/themes/twentyseventeen/assets/js/skip-link-focus-fix.js?ver=20161114" id="twentyseventeen-skip-link-focus-fix-js"></script>
|
||||
<script src="/wp-content/themes/twentyseventeen/assets/js/global.js?ver=20211130" id="twentyseventeen-global-js"></script>
|
||||
<script src="/wp-content/themes/twentyseventeen/assets/js/jquery.scrollTo.js?ver=2.1.3" id="jquery-scrollto-js"></script>`
|
|
@ -1,212 +0,0 @@
|
|||
package twentyseventeen
|
||||
|
||||
import "github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
|
||||
type themeSupport struct {
|
||||
CustomLineHeight bool `json:"custom-line-height"`
|
||||
StarterContent StarterContent `json:"starter-content"`
|
||||
}
|
||||
|
||||
type Widgets struct {
|
||||
Sidebar1 []string `json:"sidebar-1"`
|
||||
Sidebar2 []string `json:"sidebar-2"`
|
||||
Sidebar3 []string `json:"sidebar-3"`
|
||||
}
|
||||
type About struct {
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
}
|
||||
type Contact struct {
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
}
|
||||
type Blog struct {
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
}
|
||||
type HomepageSection struct {
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
}
|
||||
|
||||
type ImageEspresso struct {
|
||||
PostTitle string `json:"post_title"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type ImageSandwich struct {
|
||||
PostTitle string `json:"post_title"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type ImageCoffee struct {
|
||||
PostTitle string `json:"post_title"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type Attachments struct {
|
||||
ImageEspresso ImageEspresso `json:"image-espresso"`
|
||||
ImageSandwich ImageSandwich `json:"image-sandwich"`
|
||||
ImageCoffee ImageCoffee `json:"image-coffee"`
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
PostTitle string `json:"post_title"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
type Options struct {
|
||||
ShowOnFront string `json:"show_on_front"`
|
||||
PageOnFront string `json:"page_on_front"`
|
||||
PageForPosts string `json:"page_for_posts"`
|
||||
}
|
||||
type ThemeMods struct {
|
||||
Panel1 string `json:"panel_1"`
|
||||
Panel2 string `json:"panel_2"`
|
||||
Panel3 string `json:"panel_3"`
|
||||
Panel4 string `json:"panel_4"`
|
||||
}
|
||||
type Menus struct {
|
||||
Name string `json:"name"`
|
||||
Items []string `json:"items"`
|
||||
}
|
||||
|
||||
type NavMenus struct {
|
||||
Top Menus `json:"top"`
|
||||
Social Menus `json:"social"`
|
||||
}
|
||||
type StarterContent struct {
|
||||
Widgets Widgets `json:"widgets"`
|
||||
Posts map[string]map[string]string `json:"posts"`
|
||||
Attachments map[string]Image `json:"attachments"`
|
||||
Options Options `json:"options"`
|
||||
ThemeMods ThemeMods `json:"theme_mods"`
|
||||
NavMenus NavMenus `json:"nav_menus"`
|
||||
}
|
||||
|
||||
var themesupport = themeSupport{
|
||||
CustomLineHeight: true,
|
||||
StarterContent: StarterContent{
|
||||
Widgets: Widgets{
|
||||
Sidebar1: []string{"text_business_info", "search", "text_about"},
|
||||
Sidebar2: []string{"text_business_info"},
|
||||
Sidebar3: []string{"text_about", "search"},
|
||||
},
|
||||
Posts: map[string]map[string]string{
|
||||
"0": {
|
||||
"home": "home",
|
||||
},
|
||||
"about": {
|
||||
"thumbnail": "{{image-sandwich}}",
|
||||
},
|
||||
"contact": {
|
||||
"thumbnail": "{{image-espresso}}",
|
||||
},
|
||||
"blog": {
|
||||
"thumbnail": "{{image-coffee}}",
|
||||
},
|
||||
"homepage-section": {
|
||||
"thumbnail": "{{image-espresso}}",
|
||||
},
|
||||
},
|
||||
Attachments: map[string]Image{
|
||||
"image-espresso": {
|
||||
PostTitle: "浓缩咖啡",
|
||||
File: "assets/images/espresso.jpg",
|
||||
},
|
||||
"image-sandwich": {
|
||||
PostTitle: "三明治",
|
||||
File: "assets/images/sandwich.jpg",
|
||||
},
|
||||
"image-coffee": {
|
||||
PostTitle: "咖啡",
|
||||
File: "assets/images/coffee.jpg",
|
||||
},
|
||||
},
|
||||
Options: Options{
|
||||
ShowOnFront: "page",
|
||||
PageOnFront: "{{home}}",
|
||||
PageForPosts: "{{blog}}",
|
||||
},
|
||||
ThemeMods: ThemeMods{
|
||||
Panel1: "{{homepage-section}}",
|
||||
Panel2: "{{about}}",
|
||||
Panel3: "{{blog}}",
|
||||
Panel4: "{{contact}}",
|
||||
},
|
||||
NavMenus: NavMenus{
|
||||
Top: Menus{
|
||||
Name: "顶部菜单",
|
||||
Items: []string{
|
||||
"link_home",
|
||||
"page_about",
|
||||
"page_blog",
|
||||
"page_contact",
|
||||
},
|
||||
},
|
||||
Social: Menus{
|
||||
Name: "社交网络链接菜单",
|
||||
Items: []string{
|
||||
"link_yelp",
|
||||
"link_facebook",
|
||||
"link_twitter",
|
||||
"link_instagram",
|
||||
"link_email",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var _ = func() struct{} {
|
||||
v := wpconfig.ThemeSupport{
|
||||
CoreBlockPatterns: true,
|
||||
WidgetsBlockEditor: true,
|
||||
AutomaticFeedLinks: true,
|
||||
TitleTag: true,
|
||||
PostThumbnails: true,
|
||||
Menus: true,
|
||||
HTML5: []string{
|
||||
"comment-form",
|
||||
"comment-list",
|
||||
"gallery",
|
||||
"caption",
|
||||
"script",
|
||||
"style",
|
||||
"navigation-widgets",
|
||||
},
|
||||
PostFormats: []string{
|
||||
"aside",
|
||||
"image",
|
||||
"video",
|
||||
"quote",
|
||||
"link",
|
||||
"gallery",
|
||||
"audio",
|
||||
},
|
||||
CustomLogo: wpconfig.CustomLogo{
|
||||
Width: 250,
|
||||
Height: 250,
|
||||
FlexWidth: true,
|
||||
FlexHeight: false,
|
||||
HeaderText: "",
|
||||
UnlinkHomepageLogo: false,
|
||||
},
|
||||
CustomizeSelectiveRefreshWidgets: true,
|
||||
EditorStyle: true,
|
||||
EditorStyles: true,
|
||||
WpBlockStyles: true,
|
||||
ResponsiveEmbeds: true,
|
||||
CustomHeader: wpconfig.CustomHeader{
|
||||
DefaultImage: "http://wp.test/wp-content/themes/twentyseventeen/assets/images/header.jpg",
|
||||
RandomDefault: false,
|
||||
Width: 2000,
|
||||
Height: 1200,
|
||||
FlexHeight: true,
|
||||
FlexWidth: false,
|
||||
DefaultTextColor: "",
|
||||
HeaderText: true,
|
||||
Uploads: true,
|
||||
WpHeadCallback: "twentyseventeen_header_style",
|
||||
AdminHeadCallback: "",
|
||||
AdminPreviewCallback: "",
|
||||
Video: true,
|
||||
VideoActiveCallback: "is_front_page",
|
||||
},
|
||||
Widgets: true,
|
||||
}
|
||||
wpconfig.SetThemeSupport(ThemeName, v)
|
||||
return struct{}{}
|
||||
}()
|
|
@ -1,229 +0,0 @@
|
|||
package twentyseventeen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
"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/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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ThemeName = "twentyseventeen"
|
||||
|
||||
var paginate = func() plugins.PageEle {
|
||||
p := plugins.TwentyFifteenPagination()
|
||||
p.PrevEle = `<a class="prev page-numbers" href="%s"><svg class="icon icon-arrow-left" aria-hidden="true" role="img"> <use href="#icon-arrow-left" xlink:href="#icon-arrow-left"></use> </svg>
|
||||
<span class="screen-reader-text">上一页</span></a>`
|
||||
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.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{
|
||||
"{$before_widget}": `<section id="%s" class="%s">`,
|
||||
"{$after_widget}": `</section>`,
|
||||
})
|
||||
h.PushRender(constraints.AllStats,
|
||||
wp.NewHandleFn(wp.PreTemplate, 70.005, "wp.PreTemplate"),
|
||||
wp.NewHandleFn(errorsHandle, 80.005, "errorsHandle"),
|
||||
)
|
||||
videoHeader(h)
|
||||
h.SetData("colophon", colophon)
|
||||
setPaginationAndRender(h)
|
||||
h.CommonComponents()
|
||||
h.PushPostPlugin(postThumbnail)
|
||||
wp.SetComponentsArgsForMap(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"))
|
||||
wp.PushIndexHandler(constraints.PipeData, h, wp.NewHandleFn(wp.Index, 100.005, "wp.Index"))
|
||||
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>
|
||||
</label>
|
||||
<input type="search" id="search-form-1" class="search-field" placeholder="{$placeholder}…" value="{$value}" name="s">
|
||||
<button type="submit" class="search-submit">
|
||||
<svg class="icon icon-search" aria-hidden="true" role="img"> <use href="#icon-search" xlink:href="#icon-search"></use> </svg>
|
||||
<span class="screen-reader-text">{$button}</span>
|
||||
</button>
|
||||
</form>`
|
||||
|
||||
func errorsHandle(h *wp.Handle) {
|
||||
switch h.Stats {
|
||||
case constraints.Error404, constraints.InternalErr, constraints.ParamError:
|
||||
logs.IfError(h.Err(), "报错:")
|
||||
h.SetTempl("twentyseventeen/posts/error.gohtml")
|
||||
}
|
||||
}
|
||||
|
||||
func postThumb(h *wp.Handle) {
|
||||
d := h.GetDetailHandle()
|
||||
if d.Post.Thumbnail.Path != "" {
|
||||
img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail")
|
||||
img.Sizes = "100vw"
|
||||
img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset)
|
||||
d.Post.Thumbnail = img
|
||||
}
|
||||
}
|
||||
|
||||
var commentFormat = comment{}
|
||||
|
||||
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"
|
||||
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>`
|
||||
|
||||
func postThumbnail(h *wp.Handle, posts *models.Posts) {
|
||||
if posts.Thumbnail.Path != "" {
|
||||
posts.Thumbnail.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||
if h.Scene() == constraints.Detail {
|
||||
posts.Thumbnail.Sizes = "100vw"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var header = reload.Vars(models.PostThumbnail{}, "twentyseventeen-headerImage")
|
||||
|
||||
func calCustomHeader(h *wp.Handle) {
|
||||
h.SetData("HeaderImage", getHeaderImage(h))
|
||||
}
|
||||
|
||||
func getHeaderImage(h *wp.Handle) (r models.PostThumbnail) {
|
||||
img := header.Load()
|
||||
if img.Path != "" {
|
||||
return img
|
||||
}
|
||||
image, rand := h.GetCustomHeaderImg()
|
||||
if image.Path != "" {
|
||||
r = image
|
||||
r.Sizes = "100vw"
|
||||
if !rand {
|
||||
header.Store(r)
|
||||
}
|
||||
return
|
||||
}
|
||||
r.Path = helper.CutUrlHost(h.CommonThemeMods().ThemeSupport.CustomHeader.DefaultImage)
|
||||
r.Width = 2000
|
||||
r.Height = 1200
|
||||
header.Store(r)
|
||||
return
|
||||
}
|
||||
|
||||
func calClass(h *wp.Handle, s string, _ ...any) string {
|
||||
class := strings.Split(s, " ")
|
||||
themeMods := h.CommonThemeMods()
|
||||
u := wpconfig.GetThemeModsVal(ThemeName, "header_image", themeMods.ThemeSupport.CustomHeader.DefaultImage)
|
||||
if u != "" && u != "remove-header" {
|
||||
class = append(class, "has-header-image")
|
||||
}
|
||||
if len(themeMods.SidebarsWidgets.Data.Sidebar1) > 0 {
|
||||
class = append(class, "has-sidebar")
|
||||
}
|
||||
if themeMods.HeaderTextcolor == "blank" {
|
||||
class = append(class, "title-tagline-hidden")
|
||||
}
|
||||
class = append(class, "hfeed")
|
||||
class = append(class, str.Join("colors-", wpconfig.GetThemeModsVal(ThemeName, "colorscheme", "light")))
|
||||
if h.Scene() == constraints.Archive {
|
||||
if "one-column" == wpconfig.GetThemeModsVal(ThemeName, "page_layout", "") {
|
||||
class = append(class, "page-one-column")
|
||||
} else {
|
||||
class = append(class, "page-two-column")
|
||||
}
|
||||
}
|
||||
return strings.Join(class, " ")
|
||||
}
|
||||
|
||||
func videoHeader(h *wp.Handle) {
|
||||
h.AddActionFilter("videoSetting", videoPlay)
|
||||
wp.CustomVideo(h, constraints.Home)
|
||||
}
|
||||
|
||||
func videoPlay(h *wp.Handle, _ string, a ...any) string {
|
||||
if len(a) < 1 {
|
||||
return ""
|
||||
}
|
||||
v, ok := a[0].(*wp.VideoSetting)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
img := getHeaderImage(h)
|
||||
v.Width = img.Width
|
||||
v.Height = img.Height
|
||||
v.PosterUrl = img.Path
|
||||
v.L10n.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>`
|
||||
v.L10n.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>`
|
||||
return ""
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/number"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func bodyClass(h *Handle) func() string {
|
||||
return func() string {
|
||||
return h.BodyClass()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handle) BodyClass() string {
|
||||
var class []string
|
||||
if constraints.Ok != h.Stats {
|
||||
class = append(class, "error404")
|
||||
}
|
||||
switch h.scene {
|
||||
case constraints.Home:
|
||||
class = append(class, "home", "blog")
|
||||
|
||||
case constraints.Archive:
|
||||
class = append(class, "archive", "date")
|
||||
|
||||
case constraints.Search:
|
||||
s := "search-no-results"
|
||||
if len(h.GetIndexHandle().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
|
||||
if cat == "" {
|
||||
break
|
||||
}
|
||||
_, cate := slice.SearchFirst(cache.CategoriesTags(h.C, h.scene), func(my models.TermsMy) bool {
|
||||
return my.Name == cat
|
||||
})
|
||||
if cate.Slug[0] != '%' {
|
||||
class = append(class, str.Join("category-", cate.Slug))
|
||||
}
|
||||
class = append(class, str.Join("category-", number.IntToString(cate.Terms.TermId)))
|
||||
|
||||
case constraints.Author:
|
||||
class = append(class, "archive", "author")
|
||||
author := h.GetIndexHandle().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))
|
||||
}
|
||||
|
||||
case constraints.Detail:
|
||||
class = append(class, "post-template-default", "single", "single-post")
|
||||
class = append(class, str.Join("postid-", number.IntToString(h.GetDetailHandle().Post.Id)))
|
||||
if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
|
||||
class = append(class, "single-format-standard")
|
||||
}
|
||||
}
|
||||
if wpconfig.IsCustomBackground(h.theme) {
|
||||
class = append(class, "custom-background")
|
||||
}
|
||||
if h.themeMods.CustomLogo > 0 || str.ToInteger(wpconfig.GetOption("site_logo"), 0) > 0 {
|
||||
class = append(class, "wp-custom-logo")
|
||||
}
|
||||
if h.themeMods.ThemeSupport.ResponsiveEmbeds {
|
||||
class = append(class, "wp-embed-responsive")
|
||||
}
|
||||
return h.DoActionFilter("bodyClass", strings.Join(class, " "))
|
||||
}
|
||||
|
||||
func postClass(h *Handle) func(posts models.Posts) string {
|
||||
return func(posts models.Posts) string {
|
||||
return h.PostClass(posts)
|
||||
}
|
||||
}
|
||||
func (h *Handle) PostClass(posts models.Posts) string {
|
||||
var class []string
|
||||
class = append(class, fmt.Sprintf("post-%d", posts.Id), posts.PostType,
|
||||
str.Join("type-", posts.PostType), str.Join("status-", posts.PostStatus),
|
||||
"hentry", "format-standard")
|
||||
if h.CommonThemeMods().ThemeSupport.PostThumbnails && posts.Thumbnail.Path != "" {
|
||||
class = append(class, "has-post-thumbnail")
|
||||
}
|
||||
|
||||
if posts.PostPassword != "" {
|
||||
if h.GetPassword() != posts.PostPassword {
|
||||
class = append(class, "post-password-required")
|
||||
} else {
|
||||
class = append(class, "post-password-projected")
|
||||
}
|
||||
}
|
||||
|
||||
if h.scene == constraints.Home && h.IsStick(posts.Id) {
|
||||
class = append(class, "sticky")
|
||||
}
|
||||
for _, id := range posts.TermIds {
|
||||
term, ok := wpconfig.GetTermMy(id)
|
||||
if !ok || term.Slug == "" {
|
||||
continue
|
||||
}
|
||||
class = append(class, TermClass(term))
|
||||
}
|
||||
|
||||
return h.DoActionFilter("postClass", strings.Join(class, " "))
|
||||
}
|
||||
|
||||
func TermClass(term models.TermsMy) string {
|
||||
termClass := term.Slug
|
||||
if strings.Contains(term.Slug, "%") {
|
||||
termClass = strconv.FormatUint(term.TermTaxonomy.TermId, 10)
|
||||
}
|
||||
switch term.Taxonomy {
|
||||
case "post_tag":
|
||||
return str.Join("tag-", termClass)
|
||||
case "post_format":
|
||||
return fmt.Sprintf("format-%s", strings.ReplaceAll(term.Slug, "post-format-", ""))
|
||||
}
|
||||
return str.Join(term.Taxonomy, "-", termClass)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"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
|
||||
})
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
slice.SimpleSort(r, slice.DESC, func(t Components[string]) float64 {
|
||||
return t.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 {
|
||||
if component.Val != "" {
|
||||
s = append(s, component.Val)
|
||||
continue
|
||||
}
|
||||
if component.Fn != nil {
|
||||
v := ""
|
||||
if component.Cached {
|
||||
key := str.Join(h.scene, "-", componentKey, "-", component.Name)
|
||||
v = CacheComponent(key, component, h)
|
||||
} else {
|
||||
v = component.Fn(h)
|
||||
}
|
||||
if v != "" {
|
||||
s = append(s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(s, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handle) PushComponents(scene, componentType string, components ...Components[string]) {
|
||||
c, ok := handleComponents.Load(scene)
|
||||
if !ok {
|
||||
c = make(map[string][]Components[string])
|
||||
}
|
||||
c[componentType] = append(c[componentType], components...)
|
||||
handleComponents.Store(scene, c)
|
||||
}
|
||||
|
||||
func (h *Handle) PushGroupComponentStr(scene, componentType, name string, order float64, strs ...string) {
|
||||
var component = Components[string]{
|
||||
Val: strings.Join(slice.FilterAndMap(strs, func(t string) (string, bool) {
|
||||
t = strings.Trim(t, " \n\r\t\v\x00")
|
||||
if t == "" {
|
||||
return "", false
|
||||
}
|
||||
return t, true
|
||||
}), "\n"),
|
||||
Order: order,
|
||||
Name: name,
|
||||
}
|
||||
h.PushComponents(scene, componentType, component)
|
||||
}
|
||||
|
||||
func (h *Handle) PushCacheGroupHeadScript(scene, name string, order float64, fns ...func(*Handle) string) {
|
||||
h.PushGroupCacheComponentFn(scene, constraints.HeadScript, name, order, fns...)
|
||||
}
|
||||
|
||||
func (h *Handle) PushFooterScript(scene string, components ...Components[string]) {
|
||||
h.PushComponents(scene, constraints.FooterScript, components...)
|
||||
}
|
||||
|
||||
func (h *Handle) PushGroupFooterScript(scene, name string, order float64, strs ...string) {
|
||||
h.PushGroupComponentStr(scene, constraints.FooterScript, name, order, strs...)
|
||||
}
|
||||
|
||||
func (h *Handle) PushCacheGroupFooterScript(scene, name string, order float64, fns ...func(*Handle) string) {
|
||||
h.PushGroupCacheComponentFn(scene, constraints.FooterScript, name, order, fns...)
|
||||
}
|
||||
func (h *Handle) PushGroupCacheComponentFn(scene, componentType, name string, order float64, fns ...func(*Handle) string) {
|
||||
h.PushComponents(scene, componentType, NewComponent(name, "", true, order, func(h *Handle) string {
|
||||
return strings.Join(slice.Map(fns, func(t func(*Handle) string) string {
|
||||
return t(h)
|
||||
}), "\n")
|
||||
}))
|
||||
}
|
||||
|
||||
func NewComponent(name, val string, cached bool, order float64, fn func(handle *Handle) string) Components[string] {
|
||||
return Components[string]{Fn: fn, Name: name, Cached: cached, Order: order, Val: val}
|
||||
}
|
||||
|
||||
func (h *Handle) AddCacheComponent(scene, componentType, name string, order float64, fn func(*Handle) string) {
|
||||
h.PushComponents(scene, componentType, NewComponent(name, "", true, order, fn))
|
||||
}
|
||||
|
||||
func (h *Handle) PushHeadScript(scene string, components ...Components[string]) {
|
||||
h.PushComponents(scene, constraints.HeadScript, components...)
|
||||
}
|
||||
func (h *Handle) PushGroupHeadScript(scene, name string, order float64, str ...string) {
|
||||
h.PushGroupComponentStr(scene, constraints.HeadScript, name, order, str...)
|
||||
}
|
||||
|
||||
func GetComponentsArgs[T any](k string, defaults T) T {
|
||||
v, ok := componentsArgs.Load(k)
|
||||
if ok {
|
||||
vv, ok := v.(T)
|
||||
if ok {
|
||||
return vv
|
||||
}
|
||||
}
|
||||
return defaults
|
||||
}
|
||||
|
||||
func PushComponentsArgsForSlice[T any](name string, v ...T) {
|
||||
val, ok := componentsArgs.Load(name)
|
||||
if !ok {
|
||||
var vv []T
|
||||
vv = append(vv, v...)
|
||||
componentsArgs.Store(name, vv)
|
||||
return
|
||||
}
|
||||
vv, ok := val.([]T)
|
||||
if ok {
|
||||
vv = append(vv, v...)
|
||||
componentsArgs.Store(name, vv)
|
||||
}
|
||||
}
|
||||
func SetComponentsArgsForMap[K comparable, V any](name string, key K, v V) {
|
||||
val, ok := componentsArgs.Load(name)
|
||||
if !ok {
|
||||
vv := make(map[K]V)
|
||||
vv[key] = v
|
||||
componentsArgs.Store(name, vv)
|
||||
return
|
||||
}
|
||||
vv, ok := val.(map[K]V)
|
||||
if ok {
|
||||
vv[key] = v
|
||||
componentsArgs.Store(name, vv)
|
||||
}
|
||||
}
|
||||
func MergeComponentsArgsForMap[K comparable, V any](name string, m map[K]V) {
|
||||
val, ok := componentsArgs.Load(name)
|
||||
if !ok {
|
||||
componentsArgs.Store(name, m)
|
||||
return
|
||||
}
|
||||
vv, ok := val.(map[K]V)
|
||||
if ok {
|
||||
componentsArgs.Store(name, maps.Merge(vv, m))
|
||||
}
|
||||
}
|
||||
|
||||
func SetComponentsArgs(key string, value any) {
|
||||
componentsArgs.Store(key, value)
|
||||
}
|
||||
|
||||
func (h *Handle) GetComponentFilterFn(name string) ([]func(*Handle, string, ...any) string, bool) {
|
||||
return componentFilterFns.Load(name)
|
||||
}
|
||||
|
||||
func (h *Handle) AddActionFilter(name string, fns ...func(*Handle, string, ...any) string) {
|
||||
v, _ := componentFilterFns.Load(name)
|
||||
v = append(v, fns...)
|
||||
componentFilterFns.Store(name, v)
|
||||
}
|
||||
func (h *Handle) DoActionFilter(name, s string, args ...any) string {
|
||||
calls, ok := componentFilterFns.Load(name)
|
||||
if ok {
|
||||
return slice.Reduce(calls, func(fn func(*Handle, string, ...any) string, r string) string {
|
||||
return fn(h, r, args...)
|
||||
}, s)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
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"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var blockFn = map[string]func(*wp.Handle, string, block.ParserBlock) (func() string, error){
|
||||
"core/categories": block.Category,
|
||||
}
|
||||
|
||||
func Block(id string) (func(*wp.Handle) string, string) {
|
||||
content := wpconfig.GetPHPArrayVal("widget_block", "", str.ToInteger[int64](id, 0), "content")
|
||||
if content == "" {
|
||||
return nil, ""
|
||||
}
|
||||
var name string
|
||||
v := block.ParseBlock(content)
|
||||
if len(v.Output) > 0 {
|
||||
name = v.Output[0].Name
|
||||
}
|
||||
return func(h *wp.Handle) string {
|
||||
var out []string
|
||||
for _, parserBlock := range v.Output {
|
||||
fn, ok := blockFn[parserBlock.Name]
|
||||
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())
|
||||
|
||||
}
|
||||
}
|
||||
return strings.Join(out, "\n")
|
||||
}, name
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package block
|
||||
|
||||
import (
|
||||
"github.com/dlclark/regexp2"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
)
|
||||
|
||||
type Block struct {
|
||||
Name string
|
||||
Attrs string
|
||||
Len int
|
||||
StartOffset int
|
||||
Type string
|
||||
}
|
||||
|
||||
type BockParser struct {
|
||||
Document string
|
||||
Offset int
|
||||
Output []ParserBlock
|
||||
}
|
||||
|
||||
type ParserBlock struct {
|
||||
Name string
|
||||
Attrs string
|
||||
InnerBlocks string
|
||||
InnerHtml string
|
||||
InnerContent string
|
||||
}
|
||||
|
||||
var block = regexp2.MustCompile(`<!--\s+(?<closer>/)?wp:(?<namespace>[a-z][a-z0-9_-]*\/)?(?<name>[a-z][a-z0-9_-]*)\s+(?<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*)?}\s+)?(?<void>\/)?-->`, regexp2.IgnoreCase|regexp2.Singleline)
|
||||
|
||||
func ParseBlock(content string) (r BockParser) {
|
||||
m, err := block.FindStringMatch(content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.Document = content
|
||||
for m != nil {
|
||||
if m.GroupCount() < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
b := token(m.Groups())
|
||||
bb := ParserBlock{}
|
||||
bb.Name = b.Name
|
||||
bb.Attrs = b.Attrs
|
||||
r.Output = append(r.Output, bb)
|
||||
m, _ = block.FindNextMatch(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func token(g []regexp2.Group) (b Block) {
|
||||
if len(g) < 1 {
|
||||
b.Type = "no-more-tokens"
|
||||
return
|
||||
}
|
||||
var closer, name, void, nameSpace = "", "", "", ""
|
||||
for i, group := range g {
|
||||
v := group.String()
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
b.Len = group.Length
|
||||
b.StartOffset = group.Index
|
||||
case 1:
|
||||
closer = v
|
||||
case 2:
|
||||
nameSpace = v
|
||||
case 3:
|
||||
name = v
|
||||
case 4:
|
||||
b.Attrs = v
|
||||
case 5:
|
||||
void = v
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
if nameSpace == "" {
|
||||
nameSpace = "core/"
|
||||
}
|
||||
b.Name = str.Join(nameSpace, name)
|
||||
if void != "" {
|
||||
b.Type = "void-block"
|
||||
return
|
||||
}
|
||||
if closer != "" {
|
||||
b.Type = "block-closer"
|
||||
return
|
||||
}
|
||||
b.Type = "block-opener"
|
||||
return b
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package block
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseBlock(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "t1",
|
||||
args: args{
|
||||
s: `<!-- wp:categories {"showPostCounts":true,"showEmpty":true} /-->`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ParseBlock(tt.args.s)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
package block
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func categoryConf() map[any]any {
|
||||
return map[any]any{
|
||||
"count": int64(0),
|
||||
"dropdown": int64(0),
|
||||
"hierarchical": int64(0),
|
||||
"title": "分类",
|
||||
}
|
||||
}
|
||||
|
||||
func categoryDefaultArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_widget}": `<aside id="%s" class="%s">`,
|
||||
"{$after_widget}": `</aside>`,
|
||||
"{$name}": "cat",
|
||||
"{$class}": "postform",
|
||||
"{$selectId}": "cat",
|
||||
"{$required}": "",
|
||||
"{$show_option_none}": "选择分类",
|
||||
"{$title}": "",
|
||||
}
|
||||
}
|
||||
|
||||
func parseAttr(attr map[any]any) string {
|
||||
var attrs []string
|
||||
class := maps.GetAnyAnyValWithDefaults(attr, "", "className")
|
||||
classes := strings.Split(class, " ")
|
||||
fontsize := maps.GetAnyAnyValWithDefaults(attr, "", "fontSize")
|
||||
if fontsize != "" {
|
||||
classes = append(classes, fmt.Sprintf("has-%s-font-size", fontsize))
|
||||
}
|
||||
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) {
|
||||
kk, ok := k.(string)
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
vv, ok := v.(string)
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
return kk, vv, true
|
||||
})
|
||||
styles := maps.FilterToSlice(styless, func(k string, v string) (string, bool) {
|
||||
k = str.CamelCaseTo(k, '-')
|
||||
return str.Join(k, ":", v), true
|
||||
})
|
||||
attrs = append(attrs, fmt.Sprintf(`style="%s;"`, strings.Join(styles, ";")))
|
||||
}
|
||||
attrs = append(attrs, fmt.Sprintf(`class="%s"`, strings.Join(classes, " ")))
|
||||
return strings.Join(attrs, " ")
|
||||
}
|
||||
|
||||
var GetCategoryAttr = reload.BuildValFn("block-category-attr", parseAttr)
|
||||
|
||||
var GetCategoryConf = reload.BuildValFnWithConfirm("block-category-conf", categoryConfFn, 5)
|
||||
|
||||
func categoryConfFn(blockParser ParserBlock) (map[any]any, bool) {
|
||||
var con any
|
||||
err := json.Unmarshal([]byte(blockParser.Attrs), &con)
|
||||
if err != nil {
|
||||
logs.Error(err, "解析category attr错误", blockParser.Attrs)
|
||||
return nil, false
|
||||
}
|
||||
var conf map[any]any
|
||||
switch con.(type) {
|
||||
case map[any]any:
|
||||
conf = con.(map[any]any)
|
||||
case map[string]any:
|
||||
conf = maps.StrAnyToAnyAny(con.(map[string]any))
|
||||
}
|
||||
conf = maps.FilterZeroMerge(categoryConf(), conf)
|
||||
|
||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showPostCounts") {
|
||||
conf["count"] = int64(1)
|
||||
}
|
||||
|
||||
if maps.GetAnyAnyValWithDefaults(conf, false, "displayAsDropdown") {
|
||||
conf["dropdown"] = int64(1)
|
||||
}
|
||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showHierarchy") {
|
||||
conf["hierarchical"] = int64(1)
|
||||
}
|
||||
|
||||
class := maps.GetAnyAnyValWithDefaults(conf, "", "className")
|
||||
classes := strings.Split(class, " ")
|
||||
classes = append(classes, "wp-block-categories")
|
||||
if conf["dropdown"].(int64) == 1 {
|
||||
classes = append(classes, "wp-block-categories-dropdown")
|
||||
conf["className"] = strings.Join(classes, " ")
|
||||
} else {
|
||||
classes = append(classes, "wp-block-categories-list")
|
||||
conf["className"] = strings.Join(classes, " ")
|
||||
}
|
||||
return conf, true
|
||||
}
|
||||
|
||||
var GetCategoryArgs = reload.BuildValFnWithAnyParams("block-category-args", categoryArgs)
|
||||
|
||||
func categoryArgs(_ ...any) map[string]string {
|
||||
args := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||
return maps.FilterZeroMerge(categoryDefaultArgs(), args)
|
||||
}
|
||||
|
||||
func Category(h *wp.Handle, id string, blockParser ParserBlock) (func() string, error) {
|
||||
counter := number.Counters[int]()
|
||||
var err error
|
||||
conf := GetCategoryConf(blockParser)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if conf == nil {
|
||||
return nil, errors.New("解析block-category配置错误")
|
||||
}
|
||||
|
||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showEmpty") {
|
||||
h.C.Set("showEmpty", true)
|
||||
}
|
||||
if maps.GetAnyAnyValWithDefaults(conf, false, "showOnlyTopLevel") {
|
||||
h.C.Set("showOnlyTopLevel", true)
|
||||
}
|
||||
args := GetCategoryArgs()
|
||||
|
||||
return func() string {
|
||||
return category(h, id, counter, args, conf)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func category(h *wp.Handle, id string, counter number.Counter[int], args map[string]string, conf map[any]any) string {
|
||||
var out = ""
|
||||
categories := cache.CategoriesTags(h.C, constraints2.Category)
|
||||
class := []string{"widget", "widget_block", "widget_categories"}
|
||||
if conf["dropdown"].(int64) == 1 {
|
||||
out = dropdown(h, categories, counter(), args, conf)
|
||||
} else {
|
||||
out = categoryUl(h, categories, conf)
|
||||
}
|
||||
before := fmt.Sprintf(args["{$before_widget}"], str.Join("block-", id), strings.Join(class, " "))
|
||||
return str.Join(before, out, args["{$after_widget}"])
|
||||
}
|
||||
|
||||
func categoryUl(h *wp.Handle, categories []models.TermsMy, confAttr map[any]any) string {
|
||||
s := str.NewBuilder()
|
||||
li := widget.CategoryLi(h, confAttr, categories)
|
||||
attrs := GetCategoryAttr(confAttr)
|
||||
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 {
|
||||
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)
|
||||
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()
|
||||
}
|
||||
|
||||
var categoryDropdownScript = `
|
||||
<script type='text/javascript'>
|
||||
/* <![CDATA[ */
|
||||
( function() {
|
||||
const dropdown = document.getElementById( '{$id}' );
|
||||
function onCatChange() {
|
||||
if ( dropdown.options[ dropdown.selectedIndex ].value > 0 ) {
|
||||
location.href = "/?cat=" + dropdown.options[ dropdown.selectedIndex ].value;
|
||||
}
|
||||
}
|
||||
dropdown.onchange = onCatChange;
|
||||
})();
|
||||
/* ]]> */
|
||||
</script>
|
||||
`
|
|
@ -1,128 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var archiveTemplate = `{$before_widget}
|
||||
{$title}
|
||||
{$nav}
|
||||
{$html}
|
||||
{$navCloser}
|
||||
{$after_widget}
|
||||
`
|
||||
|
||||
func archiveArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_sidebar}": "",
|
||||
"{$after_sidebar}": "",
|
||||
"{$nav}": "",
|
||||
"{$navCloser}": "",
|
||||
"{$title}": "",
|
||||
"{$dropdown_id}": "archives-dropdown-2",
|
||||
"{$dropdown_type}": "monthly",
|
||||
"{$dropdown_label}": "选择月份",
|
||||
}
|
||||
}
|
||||
|
||||
var archivesConfig = map[any]any{
|
||||
"count": int64(0),
|
||||
"dropdown": int64(0),
|
||||
"title": "归档",
|
||||
}
|
||||
|
||||
var GetArchiveConf = BuildconfigFn(archivesConfig, "widget_archives", int64(2))
|
||||
var GetArchiveArgs = reload.BuildValFnWithAnyParams("widget_archive-args", archiveArgsFn)
|
||||
|
||||
func archiveArgsFn(a ...any) map[string]string {
|
||||
h := a[0].(*wp.Handle)
|
||||
conf := a[1].(map[any]any)
|
||||
id := a[2].(string)
|
||||
archiveArgs := archiveArgs()
|
||||
commonArgs := wp.GetComponentsArgs(widgets.Widget, CommonArgs())
|
||||
args := wp.GetComponentsArgs(widgets.Archive, archiveArgs)
|
||||
args = maps.FilterZeroMerge(archiveArgs, CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("archives-", id), str.Join("widget widget_", "archive"))
|
||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"].(string))
|
||||
args["{$navCloser}"] = "</nav>"
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func Archive(h *wp.Handle, id string) string {
|
||||
conf := GetArchiveConf()
|
||||
args := GetArchiveArgs(h, conf, id)
|
||||
|
||||
s := archiveTemplate
|
||||
if int64(1) == conf["dropdown"].(int64) {
|
||||
s = strings.ReplaceAll(s, "{$html}", archiveDropDown(h, conf, args, cache.Archives(h.C)))
|
||||
} else {
|
||||
s = strings.ReplaceAll(s, "{$html}", archiveUl(h, conf, args, cache.Archives(h.C)))
|
||||
}
|
||||
return h.DoActionFilter(widgets.Archive, str.Replace(s, args))
|
||||
}
|
||||
|
||||
var dropdownScript = `
|
||||
<script>
|
||||
/* <![CDATA[ */
|
||||
(function() {
|
||||
const dropdown = document.getElementById("archives-dropdown-2");
|
||||
function onSelectChange() {
|
||||
if ( dropdown.options[ dropdown.selectedIndex ].value !== '' ) {
|
||||
document.location.href = this.options[ this.selectedIndex ].value;
|
||||
}
|
||||
}
|
||||
dropdown.onchange = onSelectChange;
|
||||
})();
|
||||
/* ]]> */
|
||||
</script>`
|
||||
|
||||
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")
|
||||
showCount := conf["count"].(int64)
|
||||
for _, archive := range archives {
|
||||
sel := ""
|
||||
if i.Param.Year == archive.Year && month == archive.Month {
|
||||
sel = "selected"
|
||||
}
|
||||
count := ""
|
||||
if showCount == int64(1) {
|
||||
count = fmt.Sprintf("(%v)", archive.Posts)
|
||||
}
|
||||
option.Sprintf(`<option %s value="/p/date/%s/%02s" >%s年%s月 %s</option>
|
||||
`, sel, archive.Year, archive.Month, archive.Year, archive.Month, count)
|
||||
}
|
||||
s := str.NewBuilder()
|
||||
s.Sprintf(`<label class="screen-reader-text" for="%s">%s</label>
|
||||
<select id="%s" name="archive-dropdown">%s</select>%s
|
||||
`, args["{$dropdown_id}"], args["{$title}"], args["{$dropdown_id}"], option.String(), dropdownScript)
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func archiveUl(h *wp.Handle, conf map[any]any, args map[string]string, archives []models.PostArchive) string {
|
||||
s := str.NewBuilder()
|
||||
s.WriteString(`<ul>`)
|
||||
showCount := conf["count"].(int64)
|
||||
for _, archive := range archives {
|
||||
count := ""
|
||||
if showCount == 1 {
|
||||
count = fmt.Sprintf("(%v)", archive.Posts)
|
||||
}
|
||||
s.Sprintf(`<li><a href="/p/date/%[1]s/%02[2]s">%[1]s年%[2]s月%[3]s</a></li>`, archive.Year, archive.Month, count)
|
||||
}
|
||||
return s.String()
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package widget
|
||||
|
||||
func CommonArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_widget}": `<aside id="%s" class="%s">`,
|
||||
"{$after_widget}": "</aside>",
|
||||
"{$before_title}": `<h2 class="widget-title">`,
|
||||
"{$after_title}": "</h2>",
|
||||
}
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var categoryTemplate = `{$before_widget}
|
||||
{$title}
|
||||
{$nav}
|
||||
{$html}
|
||||
{$navCloser}
|
||||
{$after_widget}
|
||||
`
|
||||
var categoryConfig = map[any]any{
|
||||
"count": int64(0),
|
||||
"dropdown": int64(0),
|
||||
"hierarchical": int64(0),
|
||||
"title": "分类",
|
||||
}
|
||||
|
||||
func categoryArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_sidebar}": "",
|
||||
"{$after_sidebar}": "",
|
||||
"{$class}": "postform",
|
||||
"{$show_option_none}": "选择分类",
|
||||
"{$name}": "cat",
|
||||
"{$selectId}": "cat",
|
||||
"{$required}": "",
|
||||
"{$nav}": "",
|
||||
"{$navCloser}": "",
|
||||
"{$title}": "",
|
||||
}
|
||||
}
|
||||
|
||||
var GetCategoryConf = BuildconfigFn(categoryConfig, "widget_categories", int64(2))
|
||||
|
||||
var GetCategoryArgs = reload.BuildValFnWithAnyParams("widget-category-args", categoryArgsFn)
|
||||
|
||||
func categoryArgsFn(a ...any) map[string]string {
|
||||
h := a[0].(*wp.Handle)
|
||||
conf := a[1].(map[any]any)
|
||||
id := a[2].(string)
|
||||
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||
args := wp.GetComponentsArgs(widgets.Categories, categoryArgs())
|
||||
args = maps.FilterZeroMerge(categoryArgs(), CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("categories-", id), str.Join("widget widget_", "categories"))
|
||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||
if conf["dropdown"].(int64) == 0 && slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{title}"])
|
||||
args["{$navCloser}"] = "</nav>"
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func Category(h *wp.Handle, id string) string {
|
||||
conf := GetCategoryConf()
|
||||
args := GetCategoryArgs(h, conf, id)
|
||||
t := categoryTemplate
|
||||
dropdown := conf["dropdown"].(int64)
|
||||
categories := cache.CategoriesTags(h.C, constraints.Category)
|
||||
if dropdown == 1 {
|
||||
t = strings.ReplaceAll(t, "{$html}", CategoryDropdown(h, args, conf, categories))
|
||||
} else {
|
||||
t = strings.ReplaceAll(t, "{$html}", categoryUL(h, args, conf, categories))
|
||||
}
|
||||
return h.DoActionFilter(widgets.Categories, str.Replace(t, args))
|
||||
}
|
||||
|
||||
func CategoryLi(h *wp.Handle, conf map[any]any, categories []models.TermsMy) string {
|
||||
s := str.NewBuilder()
|
||||
isCount := conf["count"].(int64)
|
||||
currentCate := models.TermsMy{}
|
||||
if h.Scene() == constraints.Category {
|
||||
cat := h.C.Param("category")
|
||||
_, currentCate = slice.SearchFirst(categories, func(my models.TermsMy) bool {
|
||||
return cat == my.Name
|
||||
})
|
||||
}
|
||||
if conf["hierarchical"].(int64) == 0 {
|
||||
for _, category := range categories {
|
||||
count := ""
|
||||
if isCount != 0 {
|
||||
count = fmt.Sprintf("(%d)", category.Count)
|
||||
}
|
||||
current := ""
|
||||
if category.TermTaxonomyId == currentCate.TermTaxonomyId {
|
||||
current = "current-cat"
|
||||
}
|
||||
s.Sprintf(` <li class="cat-item cat-item-%d %s">
|
||||
<a href="/p/category/%s">%s %s</a>
|
||||
</li>
|
||||
`, category.Terms.TermId, current, category.Name, category.Name, count)
|
||||
}
|
||||
} else {
|
||||
|
||||
m := tree.Roots(categories, 0, func(cate models.TermsMy) (child, parent uint64) {
|
||||
return cate.TermTaxonomyId, cate.Parent
|
||||
})
|
||||
cate := &tree.Node[models.TermsMy, uint64]{Data: models.TermsMy{}}
|
||||
if currentCate.TermTaxonomyId > 0 {
|
||||
cate = m[currentCate.TermTaxonomyId]
|
||||
}
|
||||
r := m[0]
|
||||
categoryLi(r, cate, tree.Ancestor(m, 0, cate), isCount, s)
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func categoryUL(h *wp.Handle, args map[string]string, conf map[any]any, categories []models.TermsMy) string {
|
||||
s := str.NewBuilder()
|
||||
s.WriteString("<ul>\n")
|
||||
s.WriteString(CategoryLi(h, conf, categories))
|
||||
s.WriteString("</ul>")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func categoryLi(root *tree.Node[models.TermsMy, uint64], cate, roots *tree.Node[models.TermsMy, uint64], isCount int64, s *str.Builder) {
|
||||
for _, child := range *root.Children {
|
||||
category := child.Data
|
||||
count := ""
|
||||
if isCount != 0 {
|
||||
count = fmt.Sprintf("(%d)", category.Count)
|
||||
}
|
||||
var class []string
|
||||
|
||||
if len(*child.Children) > 0 && cate.Data.TermTaxonomyId > 0 {
|
||||
if category.TermTaxonomyId == cate.Parent {
|
||||
class = append(class, "current-cat-parent")
|
||||
}
|
||||
|
||||
if cate.Parent > 0 && category.TermTaxonomyId == roots.Data.TermTaxonomyId {
|
||||
class = append(class, "current-cat-ancestor")
|
||||
}
|
||||
}
|
||||
aria := ""
|
||||
if category.TermTaxonomyId == cate.Data.TermTaxonomyId {
|
||||
class = append(class, "current-cat")
|
||||
aria = `aria-current="page"`
|
||||
}
|
||||
s.Sprintf(` <li class="cat-item cat-item-%d %s">
|
||||
<a %s href="/p/category/%s">%s %s</a>
|
||||
|
||||
`, category.Terms.TermId, strings.Join(class, " "), aria, category.Name, category.Name, count)
|
||||
|
||||
if len(*child.Children) > 0 {
|
||||
s.WriteString(` <ul class="children">
|
||||
`)
|
||||
categoryLi(&child, cate, roots, isCount, s)
|
||||
s.WriteString(`</ul>
|
||||
`)
|
||||
}
|
||||
s.Sprintf(`</li>`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var categoryDropdownJs = `/* <![CDATA[ */
|
||||
(function() {
|
||||
var dropdown = document.getElementById( "%s" );
|
||||
function onCatChange() {
|
||||
if ( dropdown.options[ dropdown.selectedIndex ].value > 0 ) {
|
||||
dropdown.parentNode.submit();
|
||||
}
|
||||
}
|
||||
dropdown.onchange = onCatChange;
|
||||
})();
|
||||
/* ]]> */
|
||||
`
|
||||
|
||||
func CategoryDropdown(h *wp.Handle, args map[string]string, conf map[any]any, categories []models.TermsMy) string {
|
||||
s := str.NewBuilder()
|
||||
s.WriteString(`<form action="/" method="get">
|
||||
`)
|
||||
s.Sprintf(` <label class="screen-reader-text" for="%s">%s</label>
|
||||
`, args["{$selectId}"], args["{$title}"])
|
||||
s.WriteString(DropdownCategories(h, args, conf, categories))
|
||||
s.WriteString("</form>\n")
|
||||
attr := ""
|
||||
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "script") {
|
||||
attr = ` type="text/javascript"`
|
||||
}
|
||||
s.Sprintf(`<script%s>
|
||||
`, attr)
|
||||
s.Sprintf(categoryDropdownJs, args["{$selectId}"])
|
||||
s.WriteString("</script>\n")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func DropdownCategories(h *wp.Handle, args map[string]string, conf map[any]any, categories []models.TermsMy) string {
|
||||
if len(categories) < 1 {
|
||||
return ""
|
||||
}
|
||||
s := str.NewBuilder()
|
||||
s.Sprintf(` <select %s name="%s" id="%s" class="%s">
|
||||
`, args["{$required}"], args["{$name}"], args["{$selectId}"], args["{$class}"])
|
||||
s.Sprintf(` <option value="-1">%s</option>
|
||||
`, args["{$show_option_none}"])
|
||||
currentCategory := ""
|
||||
i := h.GetIndexHandle()
|
||||
if h.Scene() == constraints.Category {
|
||||
currentCategory = i.Param.Category
|
||||
}
|
||||
showCount := conf["count"].(int64)
|
||||
fn := func(category models.TermsMy, deep int) {
|
||||
lv := fmt.Sprintf("level-%d", deep+1)
|
||||
sep := strings.Repeat(" ", deep*2)
|
||||
selected := ""
|
||||
if category.Name == currentCategory {
|
||||
selected = "selected"
|
||||
}
|
||||
count := ""
|
||||
if showCount != 0 {
|
||||
count = fmt.Sprintf("(%d)", category.Count)
|
||||
}
|
||||
s.Sprintf(` <option class="%s" %s value="%d">%s%s %s</option>
|
||||
`, lv, selected, category.Terms.TermId, sep, category.Name, count)
|
||||
}
|
||||
if conf["hierarchical"].(int64) == 0 {
|
||||
for _, category := range categories {
|
||||
fn(category, 0)
|
||||
}
|
||||
} else {
|
||||
tree.Root(categories, 0, func(t models.TermsMy) (child, parent uint64) {
|
||||
return t.TermTaxonomyId, t.Parent
|
||||
}).Loop(func(category models.TermsMy, deep int) {
|
||||
fn(category, deep)
|
||||
})
|
||||
}
|
||||
s.WriteString(" </select>\n")
|
||||
return h.DoActionFilter("wp_dropdown_cats", s.String())
|
||||
}
|
||||
|
||||
func IsCategory(h *wp.Handle) (category models.TermsMy, r bool) {
|
||||
cate := wp.GetComponentsArgs[map[string]string](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
|
||||
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 ""
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
func Fn(id string, fn func(*wp.Handle, string) string) func(h *wp.Handle) string {
|
||||
return func(h *wp.Handle) string {
|
||||
return fn(h, id)
|
||||
}
|
||||
}
|
||||
|
||||
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...)
|
||||
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...))
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var metaTemplate = `{$before_widget}
|
||||
{$h2title}
|
||||
{$nav}
|
||||
<ul>
|
||||
{$li}
|
||||
</ul>
|
||||
{$navCloser}
|
||||
{$after_widget}`
|
||||
|
||||
func defaultMetaArgs() 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)
|
||||
args = maps.FilterZeroMerge(metaArgs, CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("meta-", id), str.Join("widget widget_", "meta"))
|
||||
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_meta", "其它操作", int64(2), "title")
|
||||
if args["{$title}"] == "" {
|
||||
args["{$title}"] = "其他操作"
|
||||
}
|
||||
if args["{$title}"] != "" {
|
||||
args["{$h2title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
||||
}
|
||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, args["{$title}"])
|
||||
args["{$navCloser}"] = "</nav>"
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func Meta(h *wp.Handle, id string) string {
|
||||
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>`)
|
||||
}
|
||||
ss.Sprintf(`<li><a href="%s">登录</a></li>`, "/wp-login.php")
|
||||
ss.Sprintf(`<li><a href="%s">条目feed</a></li>`, "/feed")
|
||||
ss.Sprintf(`<li><a href="%s">评论feed</a></li>`, "/comments/feed")
|
||||
s := strings.ReplaceAll(metaTemplate, "{$li}", ss.String())
|
||||
return h.DoActionFilter(widgets.Meta, str.Replace(s, args))
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func recentCommentsArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_sidebar}": "",
|
||||
"{$after_sidebar}": "",
|
||||
"{$nav}": "",
|
||||
"{$navCloser}": "",
|
||||
"{$title}": "",
|
||||
"{$recent_comments_id}": "recentcomments",
|
||||
}
|
||||
}
|
||||
|
||||
var recentCommentConf = map[any]any{
|
||||
"number": int64(5),
|
||||
"title": "近期评论",
|
||||
}
|
||||
|
||||
var recentCommentsTemplate = `{$before_widget}
|
||||
{$nav}
|
||||
{$title}
|
||||
<ul id="{$recent_comments_id}">
|
||||
{$li}
|
||||
</ul>
|
||||
{$navCloser}
|
||||
{$after_widget}
|
||||
`
|
||||
|
||||
var GetRecentCommentConf = BuildconfigFn(recentCommentConf, "widget_recent-comments", int64(2))
|
||||
|
||||
var GetRecentCommentArgs = reload.BuildValFnWithAnyParams("widget-recent-comment-args", RecentCommentArgs)
|
||||
|
||||
func RecentCommentArgs(a ...any) map[string]string {
|
||||
h := a[0].(*wp.Handle)
|
||||
conf := a[1].(map[any]any)
|
||||
id := a[2].(string)
|
||||
commentsArgs := recentCommentsArgs()
|
||||
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||
args := wp.GetComponentsArgs(widgets.RecentComments, commentsArgs)
|
||||
args = maps.FilterZeroMerge(commentsArgs, CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-comments-", id), str.Join("widget widget_", "recent_comments"))
|
||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
||||
args["{$navCloser}"] = "</nav>"
|
||||
}
|
||||
return args
|
||||
}
|
||||
func RecentComments(h *wp.Handle, id string) string {
|
||||
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>
|
||||
》
|
||||
</li>`, t.CommentAuthor, t.CommentAuthorUrl, t.PostTitle)
|
||||
})
|
||||
s := strings.ReplaceAll(recentCommentsTemplate, "{$li}", strings.Join(comments, "\n"))
|
||||
return h.DoActionFilter(widgets.RecentComments, str.Replace(s, args))
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var recentPostsTemplate = `{$before_widget}
|
||||
{$nav}
|
||||
{$title}
|
||||
<ul>
|
||||
{$li}
|
||||
</ul>
|
||||
{$navCloser}
|
||||
{$after_widget}
|
||||
`
|
||||
|
||||
func DefaultRecentPostsArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$before_sidebar}": "",
|
||||
"{$after_sidebar}": "",
|
||||
"{$nav}": "",
|
||||
"{$navCloser}": "",
|
||||
"{$title}": "",
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultRecentConf() map[any]any {
|
||||
return map[any]any{
|
||||
"number": int64(5),
|
||||
"show_date": false,
|
||||
"title": "近期文章",
|
||||
}
|
||||
}
|
||||
|
||||
var GetRecentPostConf = reload.BuildValFnWithAnyParams("widget-recent-posts-conf", RecentPostConf)
|
||||
|
||||
func RecentPostConf(_ ...any) map[any]any {
|
||||
recent := DefaultRecentConf()
|
||||
conf := wpconfig.GetPHPArrayVal[map[any]any]("widget_recent-posts", recent, int64(2))
|
||||
conf = maps.FilterZeroMerge(recent, conf)
|
||||
return conf
|
||||
}
|
||||
|
||||
var GetRecentPostArgs = reload.BuildValFnWithAnyParams("widget-recent-posts-args", ParseRecentPostArgs)
|
||||
|
||||
func ParseRecentPostArgs(a ...any) map[string]string {
|
||||
h := a[0].(*wp.Handle)
|
||||
conf := a[1].(map[any]any)
|
||||
id := a[2].(string)
|
||||
recent := DefaultRecentPostsArgs()
|
||||
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||
args := wp.GetComponentsArgs(widgets.RecentPosts, recent)
|
||||
args = maps.FilterZeroMerge(recent, CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("recent-posts-", id), str.Join("widget widget_", "recent_entries"))
|
||||
args["{$title}"] = str.Join(args["{$before_title}"], conf["title"].(string), args["{$after_title}"])
|
||||
if slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
args["{$nav}"] = fmt.Sprintf(`<nav aria-label="%s">`, conf["title"])
|
||||
args["{$navCloser}"] = "</nav>"
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func RecentPosts(h *wp.Handle, id string) string {
|
||||
conf := GetRecentPostConf()
|
||||
args := GetRecentPostArgs(h, conf, id)
|
||||
currentPostId := uint64(0)
|
||||
if h.Scene() == constraints.Detail {
|
||||
currentPostId = str.ToInteger(h.C.Param("id"), uint64(0))
|
||||
}
|
||||
posts := slice.Map(cache.RecentPosts(h.C, int(conf["number"].(int64))), func(t models.Posts) string {
|
||||
t = wp.ProjectTitle(t)
|
||||
date := ""
|
||||
if v, ok := conf["show_date"].(bool); ok && v {
|
||||
date = fmt.Sprintf(`<span class="post-date">%s</span>`, t.PostDateGmt.Format("2006年01月02日"))
|
||||
}
|
||||
ariaCurrent := ""
|
||||
if currentPostId == t.Id {
|
||||
ariaCurrent = ` aria-current="page"`
|
||||
}
|
||||
return fmt.Sprintf(` <li>
|
||||
<a href="/p/%v"%s>%s</a>
|
||||
%s
|
||||
</li>`, t.Id, ariaCurrent, t.PostTitle, date)
|
||||
})
|
||||
s := strings.ReplaceAll(recentPostsTemplate, "{$li}", strings.Join(posts, "\n"))
|
||||
return h.DoActionFilter(widgets.RecentPosts, str.Replace(s, args))
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var searchTemplate = `{$before_widget}
|
||||
{$title}
|
||||
{$form}
|
||||
{$after_widget}`
|
||||
|
||||
var html5SearchForm = `<form role="search" {$aria_label} method="get" class="search-form" action="/">
|
||||
<label>
|
||||
<span class="screen-reader-text">{$label}</span>
|
||||
<input type="search" class="search-field" placeholder="{$placeholder}" value="{$value}" name="s" />
|
||||
</label>
|
||||
<input type="submit" class="search-submit" value="{$button}" />
|
||||
</form>`
|
||||
var xmlSearchForm = `<form role="search" {$aria_label} method="get" id="searchform" class="searchform" action="/">
|
||||
<div>
|
||||
<label class="screen-reader-text" for="s">{$label}</label>
|
||||
<input type="text" value="{$value}" name="s" id="s" />
|
||||
<input type="submit" id="searchsubmit" value="{$button}" />
|
||||
</div>
|
||||
</form>`
|
||||
|
||||
func searchArgs() map[string]string {
|
||||
return map[string]string{
|
||||
"{$aria_label}": "",
|
||||
"{$title}": "",
|
||||
"{$form}": "",
|
||||
"{$button}": "搜索",
|
||||
"{$placeholder}": "搜索…",
|
||||
"{$label}": "搜索:",
|
||||
}
|
||||
}
|
||||
|
||||
var form = html5SearchForm
|
||||
|
||||
var GetSearchArgs = reload.BuildValFnWithAnyParams("widget-search-args", ParseSearchArgs)
|
||||
|
||||
func ParseSearchArgs(a ...any) map[string]string {
|
||||
h := a[0].(*wp.Handle)
|
||||
id := a[1].(string)
|
||||
search := searchArgs()
|
||||
commonArgs := wp.GetComponentsArgs(widgets.Widget, map[string]string{})
|
||||
args := wp.GetComponentsArgs(widgets.Search, search)
|
||||
args = maps.FilterZeroMerge(search, CommonArgs(), commonArgs, args)
|
||||
args["{$before_widget}"] = fmt.Sprintf(args["{$before_widget}"], str.Join("search-", id), str.Join("widget widget_", "search"))
|
||||
if args["{$title}"] == "" {
|
||||
args["{$title}"] = wpconfig.GetPHPArrayVal("widget_search", "", int64(2), "title")
|
||||
}
|
||||
|
||||
if args["{$title}"] != "" {
|
||||
args["{$title}"] = str.Join(args["{$before_title}"], args["{$title}"], args["{$after_title}"])
|
||||
}
|
||||
if args["{$form}"] != "" {
|
||||
form = args["{$form}"]
|
||||
delete(args, "{$form}")
|
||||
}
|
||||
if !slice.IsContained(h.CommonThemeMods().ThemeSupport.HTML5, "navigation-widgets") {
|
||||
form = xmlSearchForm
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func Search(h *wp.Handle, id string) string {
|
||||
args := GetSearchArgs(h, id)
|
||||
s := strings.ReplaceAll(searchTemplate, "{$form}", form)
|
||||
val := ""
|
||||
if h.Scene() == constraints.Search {
|
||||
val = html.SpecialChars(h.GetIndexHandle().Param.Search)
|
||||
}
|
||||
s = strings.ReplaceAll(s, "{$value}", val)
|
||||
return h.DoActionFilter(widgets.Search, str.Replace(s, args))
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"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/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var widgetFn = map[string]widgetComponent{
|
||||
"search": {fn: widget.Search, name: "widget.search"},
|
||||
"recent-posts": {fn: widget.RecentPosts, name: "widget.recent-posts"},
|
||||
"recent-comments": {fn: widget.RecentComments, name: "widget.recent-comments"},
|
||||
"archives": {fn: widget.Archive, name: "widget.archives"},
|
||||
"categories": {fn: widget.Category, name: "widget.categories"},
|
||||
"meta": {fn: widget.Meta, name: "widget.meta", cached: true},
|
||||
}
|
||||
|
||||
type widgetComponent struct {
|
||||
fn func(h *wp.Handle, id string) string
|
||||
cached bool
|
||||
name string
|
||||
}
|
||||
|
||||
func WidgetArea(h *wp.Handle) {
|
||||
h.PushComponents(constraints.AllScene, constraints.SidebarsWidgets, sidebars()...)
|
||||
}
|
||||
|
||||
func sidebars() []wp.Components[string] {
|
||||
v := wpconfig.GetPHPArrayVal("sidebars_widgets", []any{}, "sidebar-1")
|
||||
var i = 10.5
|
||||
return slice.FilterAndMap(v, func(t any) (wp.Components[string], bool) {
|
||||
vv := t.(string)
|
||||
ss := strings.Split(vv, "-")
|
||||
id := ss[len(ss)-1]
|
||||
name := strings.Join(ss[0:len(ss)-1], "-")
|
||||
widgetComponents, ok := widgetFn[name]
|
||||
if name != "block" && !ok {
|
||||
return wp.Components[string]{}, false
|
||||
}
|
||||
var component wp.Components[string]
|
||||
if name == "block" {
|
||||
fn, fnName := Block(id)
|
||||
if fn == nil {
|
||||
return component, false
|
||||
}
|
||||
component.Fn = fn
|
||||
component.Name = str.Join("block.", fnName)
|
||||
} else {
|
||||
component.Fn = widget.Fn(id, widgetComponents.fn)
|
||||
component.Name = widgetComponents.name
|
||||
component.Cached = widgetComponents.cached
|
||||
}
|
||||
i -= 0.001
|
||||
component.Order = i
|
||||
return component, true
|
||||
})
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||
"github.com/fthvgb1/wp-go/helper/html"
|
||||
)
|
||||
|
||||
func CalCustomCss(h *Handle) (r string) {
|
||||
if h.themeMods.CustomCssPostId < 1 {
|
||||
return
|
||||
}
|
||||
post, err := cache.GetPostById(h.C, uint64(h.themeMods.CustomCssPostId))
|
||||
if err != nil || post.Id < 1 {
|
||||
return
|
||||
}
|
||||
r = fmt.Sprintf(`<style id="wp-custom-css">%s</style>`, html.StripTags(post.PostContent, ""))
|
||||
return
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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") {
|
||||
logs.Error(err, "获取页眉背景图失败")
|
||||
return
|
||||
}
|
||||
hs := slice.Copy(img)
|
||||
|
||||
if len(hs) < 1 {
|
||||
return
|
||||
}
|
||||
if len(hs) > 1 {
|
||||
isRand = true
|
||||
}
|
||||
r, _ = slice.RandPop(&hs)
|
||||
return
|
||||
}
|
||||
|
||||
type VideoPlay struct {
|
||||
Pause string `json:"pause,omitempty"`
|
||||
Play string `json:"play,omitempty"`
|
||||
PauseSpeak string `json:"pauseSpeak,omitempty"`
|
||||
PlaySpeak string `json:"playSpeak,omitempty"`
|
||||
}
|
||||
|
||||
type VideoSetting struct {
|
||||
MimeType string `json:"mimeType,omitempty"`
|
||||
PosterUrl string `json:"posterUrl,omitempty"`
|
||||
VideoUrl string `json:"videoUrl,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
MinWidth int `json:"minWidth,omitempty"`
|
||||
MinHeight int `json:"minHeight,omitempty"`
|
||||
L10n VideoPlay `json:"l10n"`
|
||||
}
|
||||
|
||||
var videoReg = regexp.MustCompile(`^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)`)
|
||||
|
||||
func GetVideoSetting(h *Handle, u string) (string, error) {
|
||||
|
||||
img, _ := h.GetCustomHeaderImg()
|
||||
v := VideoSetting{
|
||||
MimeType: GetMimeType(u),
|
||||
PosterUrl: img.Path,
|
||||
VideoUrl: u,
|
||||
Width: img.Width,
|
||||
Height: img.Height,
|
||||
MinWidth: 900,
|
||||
MinHeight: 500,
|
||||
L10n: VideoPlay{
|
||||
Pause: "暂停",
|
||||
Play: "播放",
|
||||
PauseSpeak: "视频已暂停",
|
||||
PlaySpeak: "视频正在播放",
|
||||
},
|
||||
}
|
||||
if is := videoReg.FindString(u); is != "" {
|
||||
v.MimeType = "video/x-youtube"
|
||||
}
|
||||
_ = h.DoActionFilter("videoSetting", "", &v)
|
||||
s, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
setting := fmt.Sprintf(`var %s = %s`, "_wpCustomHeaderSettings", string(s))
|
||||
script := str.Join(`<script id="wp-custom-header-js-extra">`, setting, "</script>\n")
|
||||
return script, nil
|
||||
}
|
||||
|
||||
func CustomVideo(h *Handle, scene ...string) (ok bool) {
|
||||
mod, err := wpconfig.GetThemeMods(h.theme)
|
||||
if err != nil {
|
||||
logs.Error(err, "getThemeMods fail", h.theme)
|
||||
return
|
||||
}
|
||||
if !mod.ThemeSupport.CustomHeader.Video || (mod.HeaderVideo < 1 && mod.ExternalHeaderVideo == "") {
|
||||
return
|
||||
}
|
||||
u := ""
|
||||
if mod.HeaderVideo > 0 {
|
||||
post, err := cache.GetPostById(h.C, uint64(mod.HeaderVideo))
|
||||
if err != nil {
|
||||
logs.Error(err, "get headerVideo fail", mod.HeaderVideo)
|
||||
return
|
||||
}
|
||||
u = post.Metas["_wp_attached_file"].(string)
|
||||
u = str.Join("/wp-content/uploads/", u)
|
||||
} else {
|
||||
u = mod.ExternalHeaderVideo
|
||||
}
|
||||
|
||||
hs, err := GetVideoSetting(h, u)
|
||||
if err != nil {
|
||||
logs.Error(err, "get headerVideo fail", mod.HeaderVideo)
|
||||
return
|
||||
}
|
||||
scripts := []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",
|
||||
"/wp-includes/js/dist/dom-ready.min.js",
|
||||
"/wp-includes/js/dist/hooks.min.js",
|
||||
"/wp-includes/js/dist/i18n.min.js",
|
||||
"/wp-includes/js/dist/a11y.min.js",
|
||||
"/wp-includes/js/wp-custom-header.min.js",
|
||||
}
|
||||
scripts = slice.Map(scripts, 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-",
|
||||
"",
|
||||
}))
|
||||
})
|
||||
|
||||
var tr = `<script id="wp-i18n-js-after">
|
||||
wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );
|
||||
</script>
|
||||
<script id='wp-a11y-js-translations'>
|
||||
( function( domain, translations ) {
|
||||
var localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
|
||||
localeData[""].domain = domain;
|
||||
wp.i18n.setLocaleData( localeData, domain );
|
||||
} )( "default", {"translation-revision-date":"2023-04-23 22:48:55+0000","generator":"GlotPress/4.0.0-alpha.4","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","plural-forms":"nplurals=1; plural=0;","lang":"zh_CN"},"Notifications":["u901au77e5"]}},"comment":{"reference":"wp-includes/js/dist/a11y.js"}} );
|
||||
</script>
|
||||
<script src='/wp-includes/js/dist/a11y.min.js?ver=ecce20f002eda4c19664' id='wp-a11y-js'></script>
|
||||
`
|
||||
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),
|
||||
}
|
||||
for _, s := range scene {
|
||||
h.PushGroupFooterScript(s, "wp-custom-header", 10.0066, scripts[0:len(scripts)-2]...)
|
||||
h.PushFooterScript(s, c...)
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Handle) GetHeaderImages(theme string) (r []models.PostThumbnail, err error) {
|
||||
meta, err := wpconfig.GetThemeMods(theme)
|
||||
if err != nil || meta.HeaderImage == "" {
|
||||
return
|
||||
}
|
||||
if "random-uploaded-image" != meta.HeaderImage {
|
||||
m, er := cache.GetPostById(h.C, uint64(meta.HeaderImagData.AttachmentId))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
m.Thumbnail = thumb(m, theme)
|
||||
r = []models.PostThumbnail{m.Thumbnail}
|
||||
return
|
||||
}
|
||||
|
||||
headers, er := model.Finds[models.Posts](h.C, model.Conditions(
|
||||
model.Where(model.SqlBuilder{
|
||||
{"post_type", "attachment"},
|
||||
{"post_status", "inherit"},
|
||||
{"meta_value", theme},
|
||||
{"meta_key", "_wp_attachment_is_custom_header"},
|
||||
}),
|
||||
model.Fields("a.ID"),
|
||||
model.Group("a.ID"),
|
||||
model.Join(model.SqlBuilder{
|
||||
{" a", "left join", "wp_postmeta b", "a.ID=b.post_id"},
|
||||
}),
|
||||
))
|
||||
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
if len(headers) > 0 {
|
||||
posts, er := cache.GetPostsByIds(h.C, slice.Map(headers, func(t models.Posts) uint64 {
|
||||
return t.Id
|
||||
}))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
r = slice.Map(posts, func(m models.Posts) models.PostThumbnail {
|
||||
return thumb(m, theme)
|
||||
})
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func thumb(m models.Posts, theme string) models.PostThumbnail {
|
||||
m.Thumbnail = wpconfig.Thumbnail(m.AttachmentMetadata, "full", "", "thumbnail", "post-thumbnail", fmt.Sprintf("%s-thumbnail-avatar", theme))
|
||||
m.Thumbnail.Width = m.AttachmentMetadata.Width
|
||||
m.Thumbnail.Height = m.AttachmentMetadata.Height
|
||||
if m.Thumbnail.Path != "" {
|
||||
if len(m.AttachmentMetadata.Sizes) > 0 {
|
||||
m.Thumbnail.Srcset = str.Join(m.Thumbnail.Path, " 2000w, ", m.Thumbnail.Srcset)
|
||||
}
|
||||
}
|
||||
return m.Thumbnail
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
func CalCustomLogo(h *Handle) (r string) {
|
||||
id := uint64(h.themeMods.CustomLogo)
|
||||
if id < 1 {
|
||||
id = str.ToInteger[uint64](wpconfig.GetOption("site_logo"), 0)
|
||||
if id < 1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
logo, err := cache.GetPostById(h.C, id)
|
||||
if err != nil || logo.AttachmentMetadata.File == "" {
|
||||
return
|
||||
}
|
||||
siz := "full"
|
||||
meta, _ := cache.GetPostMetaByPostId(h.C, id)
|
||||
alt := maps.WithDefaultVal(meta, "_wp_attachment_image_alt", any(wpconfig.GetOption("blogname")))
|
||||
desc := alt.(string)
|
||||
imgx := map[string]string{
|
||||
"class": "custom-logo",
|
||||
"alt": desc,
|
||||
"decoding": "async",
|
||||
//"loading":"lazy",
|
||||
}
|
||||
img := wpconfig.Thumbnail(logo.AttachmentMetadata, siz, "", "")
|
||||
imgx["srcset"] = img.Srcset
|
||||
imgx["sizes"] = img.Sizes
|
||||
imgx["src"] = img.Path
|
||||
r = fmt.Sprintf("%s />", maps.Reduce(imgx, func(k string, v string, t string) string {
|
||||
return fmt.Sprintf(`%s %s="%s"`, t, k, v)
|
||||
}, fmt.Sprintf(`<img wight="%v" height="%v"`, img.Width, img.Height)))
|
||||
r = fmt.Sprintf(`<a href="%s" class="custom-logo-link" rel="home"%s>%s</a>`, "/", ` aria-current="page"`, r)
|
||||
return
|
||||
}
|
||||
|
||||
var GetCustomLog = reload.BuildValFn("customLogo", CalCustomLogo)
|
||||
|
||||
func customLogo(h *Handle) func() string {
|
||||
return func() string {
|
||||
return GetCustomLog(h)
|
||||
}
|
||||
}
|
|
@ -1,211 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||
"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/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
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DetailHandle) BuildDetailData() (err error) {
|
||||
d.ginH["title"] = wpconfig.GetOption("blogname")
|
||||
err = d.CheckAndGetPost()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d.CommentData()
|
||||
d.ContextPost()
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DetailHandle) CheckAndGetPost() (err error) {
|
||||
id := str.ToInteger[uint64](d.C.Param("id"), 0)
|
||||
maxId, err := cache.GetMaxPostId(d.C)
|
||||
logs.IfError(err, "get max post id")
|
||||
if id > maxId || id <= 0 {
|
||||
d.Stats = constraints.ParamError
|
||||
err = errors.New("无效的文档id")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
post, err := cache.GetPostById(d.C, id)
|
||||
if post.Id == 0 || err != nil || post.PostStatus != "publish" {
|
||||
d.Stats = constraints.Error404
|
||||
logs.IfError(err, "获取id失败")
|
||||
err = errors.New(str.Join("无效的文档id "))
|
||||
return
|
||||
}
|
||||
|
||||
d.Post = post
|
||||
d.ginH["user"] = cache.GetUserById(d.C, post.PostAuthor)
|
||||
d.ginH["title"] = fmt.Sprintf("%s-%s", post.PostTitle, wpconfig.GetOption("blogname"))
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DetailHandle) PasswordProject() {
|
||||
if d.Post.PostPassword != "" {
|
||||
wpposts.PasswordProjectTitle(&d.Post)
|
||||
if d.GetPassword() != d.Post.PostPassword {
|
||||
wpposts.PasswdProjectContent(&d.Post)
|
||||
}
|
||||
}
|
||||
}
|
||||
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) RenderComment() {
|
||||
if d.CommentRender == nil {
|
||||
d.CommentRender = plugins.CommentRender()
|
||||
}
|
||||
ableComment := true
|
||||
if d.Post.CommentStatus != "open" ||
|
||||
(d.Post.PostPassword != "" && d.GetPassword() != d.Post.PostPassword) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DetailHandle) ContextPost() {
|
||||
prev, next, err := cache.GetContextPost(d.C, d.Post.Id, d.Post.PostDate)
|
||||
logs.IfError(err, "get pre and next post", d.Post.Id, d.Post.PostDate)
|
||||
d.ginH["next"] = next
|
||||
d.ginH["prev"] = prev
|
||||
}
|
||||
|
||||
func DetailRender(h *Handle) {
|
||||
if h.Stats != constraints.Ok {
|
||||
return
|
||||
}
|
||||
d := h.GetDetailHandle()
|
||||
d.PasswordProject()
|
||||
d.RenderComment()
|
||||
d.ginH["post"] = d.Post
|
||||
}
|
||||
|
||||
func Detail(h *Handle) {
|
||||
d := h.GetDetailHandle()
|
||||
err := d.BuildDetailData()
|
||||
if err != nil {
|
||||
d.SetErr(err, High)
|
||||
}
|
||||
h.SetData("scene", h.Scene())
|
||||
}
|
||||
|
||||
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" {
|
||||
reply = `<script src='/wp-includes/js/comment-reply.min.js' id='comment-reply-js'></script>`
|
||||
}
|
||||
return reply
|
||||
}))
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package wp
|
||||
|
||||
const (
|
||||
None = iota
|
||||
Low
|
||||
High
|
||||
Fatal
|
||||
)
|
|
@ -1,121 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var exts = map[string]string{
|
||||
"jpg|jpeg|jpe": "image/jpeg",
|
||||
"gif": "image/gif",
|
||||
"png": "image/png",
|
||||
"bmp": "image/bmp",
|
||||
"tiff|tif": "image/tiff",
|
||||
"webp": "image/webp",
|
||||
"ico": "image/x-icon",
|
||||
"heic": "image/heic",
|
||||
"asf|asx": "video/x-ms-asf",
|
||||
"wmv": "video/x-ms-wmv",
|
||||
"wmx": "video/x-ms-wmx",
|
||||
"wm": "video/x-ms-wm",
|
||||
"avi": "video/avi",
|
||||
"divx": "video/divx",
|
||||
"flv": "video/x-flv",
|
||||
"mov|qt": "video/quicktime",
|
||||
"mpeg|mpg|mpe": "video/mpeg",
|
||||
"mp4|m4v": "video/mp4",
|
||||
"ogv": "video/ogg",
|
||||
"webm": "video/webm",
|
||||
"mkv": "video/x-matroska",
|
||||
"3gp|3gpp": "video/3gpp",
|
||||
"3g2|3gp2": "video/3gpp2",
|
||||
"txt|asc|c|cc|h|srt": "text/plain",
|
||||
"csv": "text/csv",
|
||||
"tsv": "text/tab-separated-values",
|
||||
"ics": "text/calendar",
|
||||
"rtx": "text/richtext",
|
||||
"css": "text/css",
|
||||
"htm|html": "text/html",
|
||||
"vtt": "text/vtt",
|
||||
"dfxp": "application/ttaf+xml",
|
||||
"mp3|m4a|m4b": "audio/mpeg",
|
||||
"aac": "audio/aac",
|
||||
"ra|ram": "audio/x-realaudio",
|
||||
"wav": "audio/wav",
|
||||
"ogg|oga": "audio/ogg",
|
||||
"flac": "audio/flac",
|
||||
"mid|midi": "audio/midi",
|
||||
"wma": "audio/x-ms-wma",
|
||||
"wax": "audio/x-ms-wax",
|
||||
"mka": "audio/x-matroska",
|
||||
"rtf": "application/rtf",
|
||||
"js": "application/javascript",
|
||||
"pdf": "application/pdf",
|
||||
"swf": "application/x-shockwave-flash",
|
||||
"class": "application/java",
|
||||
"tar": "application/x-tar",
|
||||
"zip": "application/zip",
|
||||
"gz|gzip": "application/x-gzip",
|
||||
"rar": "application/rar",
|
||||
"7z": "application/x-7z-compressed",
|
||||
"exe": "application/x-msdownload",
|
||||
"psd": "application/octet-stream",
|
||||
"xcf": "application/octet-stream",
|
||||
"doc": "application/msword",
|
||||
"pot|pps|ppt": "application/vnd.ms-powerpoint",
|
||||
"wri": "application/vnd.ms-write",
|
||||
"xla|xls|xlt|xlw": "application/vnd.ms-excel",
|
||||
"mdb": "application/vnd.ms-access",
|
||||
"mpp": "application/vnd.ms-project",
|
||||
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"docm": "application/vnd.ms-word.document.macroEnabled.12",
|
||||
"dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||
"dotm": "application/vnd.ms-word.template.macroEnabled.12",
|
||||
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||
"xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||
"xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
"xltm": "application/vnd.ms-excel.template.macroEnabled.12",
|
||||
"xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
|
||||
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
"ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||
"ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
||||
"potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||
"potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||
"ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
||||
"sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
||||
"sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
|
||||
"onetoc|onetoc2|onetmp|onepkg": "application/onenote",
|
||||
"oxps": "application/oxps",
|
||||
"xps": "application/vnd.ms-xpsdocument",
|
||||
"odt": "application/vnd.oasis.opendocument.text",
|
||||
"odp": "application/vnd.oasis.opendocument.presentation",
|
||||
"ods": "application/vnd.oasis.opendocument.spreadsheet",
|
||||
"odg": "application/vnd.oasis.opendocument.graphics",
|
||||
"odc": "application/vnd.oasis.opendocument.chart",
|
||||
"odb": "application/vnd.oasis.opendocument.database",
|
||||
"odf": "application/vnd.oasis.opendocument.formula",
|
||||
"wp|wpd": "application/wordperfect",
|
||||
"key": "application/vnd.apple.keynote",
|
||||
"numbers": "application/vnd.apple.numbers",
|
||||
"pages": "application/vnd.apple.pages",
|
||||
}
|
||||
|
||||
var regs = func() map[string]*regexp.Regexp {
|
||||
var r = make(map[string]*regexp.Regexp)
|
||||
for k, v := range exts {
|
||||
r[v] = regexp.MustCompile(`(?i:\.(` + k + `)$)`)
|
||||
}
|
||||
return r
|
||||
}()
|
||||
|
||||
func GetMimeType(file string) string {
|
||||
ext := filepath.Ext(file)
|
||||
for mime, reg := range regs {
|
||||
if reg.FindString(ext) != "" {
|
||||
return mime
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
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]()
|
||||
|
||||
func GetFn[T any](fnType string, name string) []T {
|
||||
v, ok := fnMap.Load(fnType)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
vv, ok := v[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return vv.([]T)
|
||||
}
|
||||
func GetFnHook[T any](fnType string, name string) []T {
|
||||
v, ok := fnHook.Load(fnType)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
vv, ok := v[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return vv.([]T)
|
||||
}
|
||||
|
||||
func PushFn[T any](fnType string, name string, fns ...T) error {
|
||||
v, ok := fnMap.Load(fnType)
|
||||
if !ok {
|
||||
v = make(map[string]any)
|
||||
fnMap.Store(fnType, v)
|
||||
v[name] = fns
|
||||
return nil
|
||||
}
|
||||
vv, ok := v[name]
|
||||
if !ok || vv == nil {
|
||||
v[name] = fns
|
||||
return nil
|
||||
}
|
||||
s, ok := vv.([]T)
|
||||
if ok {
|
||||
s = append(s, fns...)
|
||||
v[name] = s
|
||||
}
|
||||
return errors.New("error fn type")
|
||||
}
|
||||
|
||||
func PushFnHook[T any](fnType string, name string, fns ...T) error {
|
||||
v, ok := fnHook.Load(fnType)
|
||||
if !ok {
|
||||
v = make(map[string]any)
|
||||
fnHook.Store(fnType, v)
|
||||
v[name] = fns
|
||||
return nil
|
||||
}
|
||||
vv, ok := v[name]
|
||||
if !ok || vv == nil {
|
||||
v[name] = fns
|
||||
return nil
|
||||
}
|
||||
s, ok := vv.([]T)
|
||||
if ok {
|
||||
s = append(s, fns...)
|
||||
v[name] = s
|
||||
}
|
||||
return errors.New("error fn type")
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/fthvgb1/wp-go/plugin/pagination"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexHandle struct {
|
||||
*Handle
|
||||
Param *IndexParams
|
||||
Posts []models.Posts
|
||||
pageEle pagination.Render
|
||||
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
|
||||
}
|
||||
|
||||
func (i *IndexHandle) SetListPlugin(listPlugin func(*Handle, *models.Posts)) {
|
||||
i.postsPlugin = listPlugin
|
||||
}
|
||||
|
||||
func (i *IndexHandle) PageEle() pagination.Render {
|
||||
return i.pageEle
|
||||
}
|
||||
|
||||
func (i *IndexHandle) SetPageEle(pageEle pagination.Render) {
|
||||
i.pageEle = pageEle
|
||||
}
|
||||
|
||||
func NewIndexHandle(handle *Handle) *IndexHandle {
|
||||
return &IndexHandle{Handle: handle}
|
||||
}
|
||||
|
||||
func PushIndexHandler(pipeScene string, h *Handle, call HandleCall) {
|
||||
h.PushHandlers(pipeScene, call, constraints.Home,
|
||||
constraints.Category, constraints.Search, constraints.Tag,
|
||||
constraints.Archive, constraints.Author,
|
||||
)
|
||||
}
|
||||
|
||||
func (i *IndexHandle) ParseIndex(parm *IndexParams) (err error) {
|
||||
i.Param = parm
|
||||
switch i.scene {
|
||||
case constraints.Search:
|
||||
i.Param.ParseSearch()
|
||||
case constraints.Category:
|
||||
err = i.Param.ParseCategory()
|
||||
case constraints.Tag:
|
||||
err = i.Param.ParseTag()
|
||||
case constraints.Archive:
|
||||
err = i.Param.ParseArchive()
|
||||
case constraints.Author:
|
||||
err = i.Param.ParseAuthor()
|
||||
}
|
||||
if err != nil {
|
||||
i.Stats = constraints.ParamError
|
||||
return
|
||||
}
|
||||
i.Param.ParseParams()
|
||||
i.Param.CacheKey = i.Param.getSearchKey()
|
||||
i.ginH["title"] = i.Param.getTitle()
|
||||
i.ginH["search"] = i.Param.Search
|
||||
i.ginH["header"] = i.Param.Header
|
||||
return
|
||||
}
|
||||
|
||||
func (i *IndexHandle) GetIndexData() (posts []models.Posts, totalRaw int, err error) {
|
||||
|
||||
q := &model.QueryCondition{
|
||||
Where: i.Param.Where,
|
||||
Order: model.SqlBuilder{{i.Param.OrderBy, i.Param.Order}},
|
||||
Join: i.Param.Join,
|
||||
In: [][]any{i.Param.PostType, i.Param.PostStatus},
|
||||
}
|
||||
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)
|
||||
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)
|
||||
|
||||
case constraints.Archive:
|
||||
i.ginH["archiveYear"] = i.Param.Year
|
||||
i.ginH["archiveMonth"] = strings.TrimLeft(i.Param.Month, "0")
|
||||
posts, totalRaw, err = cache.GetMonthPostIds(i.C, i.Param.Year, i.Param.Month, i.Param.Page, i.Param.PageSize, i.Param.Order)
|
||||
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (i *IndexHandle) Pagination() {
|
||||
if i.pageEle == nil {
|
||||
i.pageEle = plugins.TwentyFifteenPagination()
|
||||
}
|
||||
q := i.C.Request.URL.Query().Encode()
|
||||
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())
|
||||
|
||||
}
|
||||
|
||||
func (i *IndexHandle) BuildIndexData() (err error) {
|
||||
if i.Param == nil {
|
||||
i.Param = NewIndexParams(i.C)
|
||||
}
|
||||
err = i.ParseIndex(i.Param)
|
||||
if err != nil {
|
||||
i.Stats = constraints.ParamError
|
||||
return
|
||||
}
|
||||
posts, totalRows, err := i.GetIndexData()
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
i.Stats = constraints.Error404
|
||||
return
|
||||
}
|
||||
i.Posts = posts
|
||||
i.TotalRows = totalRows
|
||||
i.ginH["totalPage"] = number.DivideCeil(totalRows, i.Param.PageSize)
|
||||
return
|
||||
}
|
||||
|
||||
var GetPostsPlugin = reload.BuildValFnWithAnyParams("postPlugins", UsePostsPlugins)
|
||||
|
||||
func (i *IndexHandle) ExecPostsPlugin() {
|
||||
fn := i.postsPlugin
|
||||
if fn == nil {
|
||||
fn = GetPostsPlugin()
|
||||
}
|
||||
for j := range i.Posts {
|
||||
fn(i.Handle, &i.Posts[j])
|
||||
}
|
||||
}
|
||||
|
||||
func IndexRender(h *Handle) {
|
||||
i := h.GetIndexHandle()
|
||||
i.ExecPostsPlugin()
|
||||
i.Pagination()
|
||||
i.ginH["posts"] = i.Posts
|
||||
}
|
||||
|
||||
func Index(h *Handle) {
|
||||
i := h.GetIndexHandle()
|
||||
err := i.BuildIndexData()
|
||||
if err != nil {
|
||||
i.SetErr(err, High)
|
||||
}
|
||||
h.SetData("scene", h.Scene())
|
||||
}
|
||||
|
||||
func (i *IndexHandle) MarkSticky(posts *[]models.Posts) {
|
||||
a := GetStickPosts(i.Handle)
|
||||
if len(a) < 1 {
|
||||
return
|
||||
}
|
||||
m := GetStickMapPosts(i.Handle)
|
||||
*posts = append(a, slice.Filter(*posts, func(post models.Posts, _ int) bool {
|
||||
_, ok := m[post.Id]
|
||||
return !ok
|
||||
})...)
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/dao"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/fthvgb1/wp-go/helper/number"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IndexParams struct {
|
||||
ParseSearch func()
|
||||
ParseArchive func() error
|
||||
ParseCategory func() error
|
||||
ParseTag func() error
|
||||
ParseAuthor func() error
|
||||
CategoryCondition func()
|
||||
ParseParams func()
|
||||
Ctx *gin.Context
|
||||
Page int
|
||||
PageSize int
|
||||
Title string
|
||||
TitleL string
|
||||
TitleR string
|
||||
Search string
|
||||
Author string
|
||||
TotalPage int
|
||||
Category string
|
||||
CategoryType string
|
||||
Where model.SqlBuilder
|
||||
OrderBy string
|
||||
Order string
|
||||
Month string
|
||||
Year string
|
||||
Join model.SqlBuilder
|
||||
PostType []any
|
||||
PostStatus []any
|
||||
Header string
|
||||
PaginationStep int
|
||||
CacheKey string
|
||||
BlogName string
|
||||
}
|
||||
|
||||
var months = slice.SimpleToMap(number.Range(1, 12, 1), func(v int) int {
|
||||
return v
|
||||
})
|
||||
|
||||
var orders = map[string]struct{}{"asc": {}, "desc": {}}
|
||||
|
||||
func (i *IndexParams) setTitleLR(l, r string) {
|
||||
i.TitleL = l
|
||||
i.TitleR = r
|
||||
}
|
||||
|
||||
func (i *IndexParams) getTitle() string {
|
||||
i.Title = fmt.Sprintf("%s - %s", i.TitleL, i.TitleR)
|
||||
return i.Title
|
||||
}
|
||||
|
||||
func (i *IndexParams) getSearchKey() string {
|
||||
return fmt.Sprintf("action:%s|%s|%s|%s|%s|%s|%d|%d", i.Author, i.Search, i.OrderBy, i.Order, i.Category, i.CategoryType, i.Page, i.PageSize)
|
||||
}
|
||||
|
||||
func NewIndexParams(ctx *gin.Context) *IndexParams {
|
||||
blogName := wpconfig.GetOption("blogname")
|
||||
size := str.ToInteger(wpconfig.GetOption("posts_per_page"), 10)
|
||||
i := &IndexParams{
|
||||
Ctx: ctx,
|
||||
Page: 1,
|
||||
PageSize: size,
|
||||
PaginationStep: number.Max(1, config.GetConfig().PaginationStep),
|
||||
TitleL: blogName,
|
||||
TitleR: wpconfig.GetOption("blogdescription"),
|
||||
Where: model.SqlBuilder{
|
||||
{"post_type", "in", ""},
|
||||
{"post_status", "in", ""},
|
||||
},
|
||||
OrderBy: "post_date",
|
||||
Join: model.SqlBuilder{},
|
||||
PostType: []any{"post"},
|
||||
PostStatus: []any{"publish"},
|
||||
BlogName: wpconfig.GetOption("blogname"),
|
||||
}
|
||||
i.ParseSearch = i.ParseSearchs
|
||||
i.ParseArchive = i.ParseArchives
|
||||
i.ParseCategory = i.ParseCategorys
|
||||
i.ParseTag = i.ParseTags
|
||||
i.CategoryCondition = i.CategoryConditions
|
||||
i.ParseAuthor = i.ParseAuthors
|
||||
i.ParseParams = i.ParseParamss
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *IndexParams) ParseSearchs() {
|
||||
s := i.Ctx.Query("s")
|
||||
q := str.Join("%", s, "%")
|
||||
i.Where = append(i.Where, []string{
|
||||
"and", "post_title", "like", q, "",
|
||||
"or", "post_content", "like", q, "",
|
||||
"or", "post_excerpt", "like", q, "",
|
||||
}, []string{"post_password", ""})
|
||||
i.PostType = append(i.PostType, "Page", "attachment")
|
||||
i.Header = fmt.Sprintf("<span>%s</span>的搜索结果", s)
|
||||
i.setTitleLR(str.Join(`"`, s, `"`, "的搜索结果"), i.BlogName)
|
||||
i.Search = s
|
||||
}
|
||||
func (i *IndexParams) ParseArchives() error {
|
||||
year := i.Ctx.Param("year")
|
||||
if year != "" {
|
||||
y := str.ToInteger(year, -1)
|
||||
if y > time.Now().Year() || y <= 1970 {
|
||||
return errors.New(str.Join("year err : ", year))
|
||||
}
|
||||
i.Where = append(i.Where, []string{
|
||||
"year(post_date)", year,
|
||||
})
|
||||
i.Year = year
|
||||
}
|
||||
month := i.Ctx.Param("month")
|
||||
if month != "" {
|
||||
m := str.ToInteger(month, -1)
|
||||
if !maps.IsExists(months, m) {
|
||||
return errors.New(str.Join("months err ", month))
|
||||
}
|
||||
|
||||
i.Where = append(i.Where, []string{
|
||||
"month(post_date)", month,
|
||||
})
|
||||
ss := fmt.Sprintf("%s年%s月", year, strings.TrimLeft(month, "0"))
|
||||
i.Header = fmt.Sprintf("月度归档: <span>%s</span>", ss)
|
||||
i.setTitleLR(ss, i.BlogName)
|
||||
i.Month = month
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IndexParams) ParseCategorys() error {
|
||||
category := i.Ctx.Param("category")
|
||||
if category != "" {
|
||||
if !maps.IsExists(cache.AllCategoryTagsNames(i.Ctx, constraints.Category), category) {
|
||||
return errors.New(str.Join("not exists category ", category))
|
||||
}
|
||||
i.CategoryType = "category"
|
||||
i.Header = fmt.Sprintf("分类: <span>%s</span>", category)
|
||||
i.Category = category
|
||||
i.CategoryCondition()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (i *IndexParams) ParseTags() error {
|
||||
tag := i.Ctx.Param("tag")
|
||||
if tag != "" {
|
||||
if !maps.IsExists(cache.AllCategoryTagsNames(i.Ctx, constraints.Tag), tag) {
|
||||
return errors.New(str.Join("not exists tag ", tag))
|
||||
}
|
||||
i.CategoryType = "post_tag"
|
||||
i.Header = fmt.Sprintf("标签: <span>%s</span>", tag)
|
||||
i.Category = tag
|
||||
i.CategoryCondition()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IndexParams) CategoryConditions() {
|
||||
if i.Category != "" {
|
||||
i.Where = append(i.Where, []string{
|
||||
"d.name", i.Category,
|
||||
}, []string{"taxonomy", i.CategoryType})
|
||||
i.Join = append(i.Join, []string{
|
||||
"a", "left Join", "wp_term_relationships b", "a.Id=b.object_id",
|
||||
}, []string{
|
||||
"left Join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id",
|
||||
}, []string{
|
||||
"left Join", "wp_terms d", "c.term_id=d.term_id",
|
||||
})
|
||||
i.setTitleLR(i.Category, i.BlogName)
|
||||
}
|
||||
}
|
||||
func (i *IndexParams) ParseAuthors() (err error) {
|
||||
username := i.Ctx.Param("author")
|
||||
if username != "" {
|
||||
allUsername, er := cache.GetAllUsername(i.Ctx)
|
||||
if err != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
if !maps.IsExists(allUsername, username) {
|
||||
err = errors.New(str.Join("user ", username, " is not exists"))
|
||||
return
|
||||
}
|
||||
user, er := cache.GetUserByName(i.Ctx, username)
|
||||
if er != nil {
|
||||
return
|
||||
}
|
||||
i.Author = username
|
||||
i.Where = append(i.Where, []string{
|
||||
"post_author", "=", strconv.FormatUint(user.Id, 10), "int",
|
||||
})
|
||||
i.Header = str.Join("作者:", username)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *IndexParams) ParseParamss() {
|
||||
i.Order = i.Ctx.Query("Order")
|
||||
if !maps.IsExists(orders, i.Order) {
|
||||
order := config.GetConfig().PostOrder
|
||||
i.Order = "asc"
|
||||
if order != "" && maps.IsExists(orders, order) {
|
||||
i.Order = order
|
||||
}
|
||||
}
|
||||
|
||||
i.Page = str.ToInteger(i.Ctx.Param("page"), 1)
|
||||
total := int(atomic.LoadInt64(&dao.TotalRaw))
|
||||
if total > 0 && total < (i.Page-1)*i.PageSize {
|
||||
i.Page = 1
|
||||
}
|
||||
if i.Page > 1 && (i.Category != "" || i.Search != "" || i.Month != "") {
|
||||
i.setTitleLR(fmt.Sprintf("%s-第%d页", i.TitleL, i.Page), i.BlogName)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type PostsPlugin func(*Handle, *models.Posts)
|
||||
|
||||
func PostsPlugins(initial PostsPlugin, calls ...func(PostsPlugin, *Handle, *models.Posts)) PostsPlugin {
|
||||
return slice.ReverseReduce(calls, func(t func(PostsPlugin, *Handle, *models.Posts), r PostsPlugin) PostsPlugin {
|
||||
return func(handle *Handle, posts *models.Posts) {
|
||||
t(r, handle, posts)
|
||||
}
|
||||
}, initial)
|
||||
}
|
||||
|
||||
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()
|
||||
m[name] = fn
|
||||
}
|
||||
|
||||
// PasswordProject 标题和内容密码保护
|
||||
func PasswordProject(next PostsPlugin, h *Handle, post *models.Posts) {
|
||||
r := post
|
||||
if post.PostPassword != "" {
|
||||
wpposts.PasswordProjectTitle(r)
|
||||
if h.GetPassword() != post.PostPassword {
|
||||
wpposts.PasswdProjectContent(r)
|
||||
return
|
||||
}
|
||||
}
|
||||
next(h, r)
|
||||
}
|
||||
|
||||
// Digest 生成摘要
|
||||
func Digest(next PostsPlugin, h *Handle, post *models.Posts) {
|
||||
if post.PostExcerpt != "" {
|
||||
plugins.PostExcerpt(post)
|
||||
} else {
|
||||
plugins.Digest(h.C, post, config.GetConfig().DigestWordCount)
|
||||
}
|
||||
next(h, post)
|
||||
}
|
||||
|
||||
var ordinaryPlugin = reload.Vars([]PostsPlugin{}, "ordinaryPlugin")
|
||||
|
||||
func (h *Handle) PushPostPlugin(plugin ...PostsPlugin) {
|
||||
p := ordinaryPlugin.Load()
|
||||
p = append(p, plugin...)
|
||||
ordinaryPlugin.Store(p)
|
||||
}
|
||||
|
||||
func PostPlugin(calls ...PostsPlugin) PostsPlugin {
|
||||
return func(h *Handle, posts *models.Posts) {
|
||||
for _, call := range calls {
|
||||
call(h, posts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UsePostsPlugins(_ ...any) PostsPlugin {
|
||||
m := pluginFns.Load()
|
||||
pluginss := slice.FilterAndMap(config.GetConfig().ListPagePlugins, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
|
||||
f, ok := m[t]
|
||||
return f, ok
|
||||
})
|
||||
slice.Unshift(&pluginss, PasswordProject)
|
||||
return PostsPlugins(PostPlugin(ordinaryPlugin.Load()...), pluginss...)
|
||||
}
|
||||
|
||||
func ListPostPlugins() map[string]func(PostsPlugin, *Handle, *models.Posts) {
|
||||
return maps.Copy(pluginFns.Load())
|
||||
}
|
||||
|
||||
func ProjectTitle(t models.Posts) models.Posts {
|
||||
if t.PostPassword != "" {
|
||||
wpposts.PasswordProjectTitle(&t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func GetListPostPlugins(name []string, m map[string]func(PostsPlugin, *Handle, *models.Posts)) []func(PostsPlugin, *Handle, *models.Posts) {
|
||||
return slice.FilterAndMap(name, func(t string) (func(PostsPlugin, *Handle, *models.Posts), bool) {
|
||||
v, ok := m[t]
|
||||
if ok {
|
||||
return v, true
|
||||
}
|
||||
return nil, false
|
||||
})
|
||||
}
|
|
@ -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"),
|
||||
)
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||
"github.com/fthvgb1/wp-go/cache/reload"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
)
|
||||
|
||||
type HandlePipeFn[T any] func(HandleFn[T], T)
|
||||
|
||||
type Pipe struct {
|
||||
Name string
|
||||
Order float64
|
||||
Fn HandlePipeFn[*Handle]
|
||||
}
|
||||
|
||||
func NewPipe(name string, order float64, fn HandlePipeFn[*Handle]) Pipe {
|
||||
return Pipe{Name: name, Order: order, Fn: fn}
|
||||
}
|
||||
|
||||
// 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 func(t T) {
|
||||
next(f, t)
|
||||
}
|
||||
}, initial)
|
||||
}
|
||||
|
||||
func (h *Handle) PushPipe(scene string, pipes ...Pipe) error {
|
||||
return PushFn("pipe", scene, pipes...)
|
||||
}
|
||||
func (h *Handle) PushPipeHook(scene string, pipes ...func(Pipe) (Pipe, bool)) error {
|
||||
return PushFnHook("pipeHook", scene, pipes...)
|
||||
}
|
||||
|
||||
func (h *Handle) DeletePipe(scene, pipeName string) error {
|
||||
return PushFnHook("pipeHook", scene, func(pipe Pipe) (Pipe, bool) {
|
||||
return pipe, pipeName != pipe.Name
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handle) ReplacePipe(scene, pipeName string, pipe Pipe) error {
|
||||
return PushFnHook("pipeHook", scene, func(p Pipe) (Pipe, bool) {
|
||||
if pipeName == p.Name {
|
||||
p = pipe
|
||||
}
|
||||
return p, true
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handle) PushHandler(pipScene string, scene string, fns ...HandleCall) {
|
||||
v, ok := handlerss.Load(pipScene)
|
||||
if !ok {
|
||||
v = make(map[string][]HandleCall)
|
||||
}
|
||||
v[scene] = append(v[scene], fns...)
|
||||
handlerss.Store(pipScene, v)
|
||||
}
|
||||
|
||||
func (h *Handle) PushRender(statsOrScene string, fns ...HandleCall) {
|
||||
h.PushHandler(constraints.PipeRender, statsOrScene, fns...)
|
||||
}
|
||||
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))
|
||||
|
||||
return func(next HandleFn[*Handle], h *Handle) {
|
||||
key := keyFn(h, pipeScene)
|
||||
handlers := pipeHandlerFn(key, h)
|
||||
for _, handler := range handlers {
|
||||
handler.Fn(h)
|
||||
if h.abort {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !h.stopPipe {
|
||||
next(h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
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()
|
||||
for _, fn := range hooks {
|
||||
pipe, ok = fn(pipe)
|
||||
if !ok {
|
||||
return pipe, false
|
||||
}
|
||||
}
|
||||
return pipe, pipe.Fn != nil
|
||||
})
|
||||
slice.SimpleSort(pipes, slice.DESC, func(t Pipe) float64 {
|
||||
return t.Order
|
||||
})
|
||||
|
||||
arr := slice.Map(pipes, func(t Pipe) HandlePipeFn[*Handle] {
|
||||
return t.Fn
|
||||
})
|
||||
return HandlePipe(NothingToDo, arr...)
|
||||
}
|
||||
|
||||
func MiddlewareKey(h *Handle, pipScene string) string {
|
||||
return h.DoActionFilter("middleware", str.Join("pipe-middleware-", h.scene), 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 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 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
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) PushPipeHandleHook(name string, fn ...func([]HandleCall) []HandleCall) error {
|
||||
return PushFnHook("pipeHandleHook", name, fn...)
|
||||
}
|
||||
|
||||
func (h *Handle) PipeHandleHook(name string, calls []HandleCall, m map[string][]HandleCall, key string) []HandleCall {
|
||||
fn := GetFnHook[func(*Handle, []HandleCall, map[string][]HandleCall, string) []HandleCall]("pipeHandleHook", name)
|
||||
return slice.Reduce(fn, func(t func(*Handle, []HandleCall, map[string][]HandleCall, string) []HandleCall, r []HandleCall) []HandleCall {
|
||||
return t(h, r, m, key)
|
||||
}, calls)
|
||||
}
|
||||
|
||||
func InitPipe(h *Handle) {
|
||||
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeMiddleware, 300,
|
||||
BuildHandlers(constraints.PipeMiddleware, MiddlewareKey, PipeMiddlewareHandle)))
|
||||
|
||||
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeData, 200,
|
||||
BuildHandlers(constraints.PipeData, PipeKey, PipeDataHandle)))
|
||||
h.PushPipe(constraints.AllScene, NewPipe(constraints.PipeRender, 100,
|
||||
BuildHandlers(constraints.PipeRender, PipeKey, PipeRender)))
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"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
|
||||
Method []string
|
||||
Type string
|
||||
}
|
||||
|
||||
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() {
|
||||
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+)`,
|
||||
// Scene: constraints.Home,
|
||||
// Method: []string{"GET"},
|
||||
// Type: "reg",
|
||||
// }
|
||||
func PushRoute(path string, route Route) error {
|
||||
if route.Type == "const" {
|
||||
routes.Store(path, route)
|
||||
return nil
|
||||
}
|
||||
re, err := regexp.Compile(route.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
regRoutes.Store(path, re)
|
||||
routes.Store(path, route)
|
||||
return err
|
||||
}
|
||||
|
||||
func Delete(path string) {
|
||||
routeHook = append(routeHook, func(route Route) (Route, bool) {
|
||||
return route, route.Path != path
|
||||
})
|
||||
}
|
||||
|
||||
func Replace(path string, route Route) {
|
||||
routeHook = append(routeHook, func(r Route) (Route, bool) {
|
||||
return route, path == route.Path
|
||||
})
|
||||
}
|
||||
|
||||
func Hook(path string, fn func(Route) Route) {
|
||||
routeHook = append(routeHook, func(r Route) (Route, bool) {
|
||||
if path == r.Path {
|
||||
r = fn(r)
|
||||
}
|
||||
return r, path == r.Path
|
||||
})
|
||||
}
|
||||
|
||||
var RegRouteFn = reload.BuildValFnWithAnyParams("regexRoute", RegRouteHook)
|
||||
|
||||
func RegRouteHook(_ ...any) func() (map[string]Route, map[string]*regexp.Regexp) {
|
||||
m := map[string]Route{}
|
||||
rrs := map[string]*regexp.Regexp{}
|
||||
routes.Range(func(key string, value Route) bool {
|
||||
vv, _ := regRoutes.Load(key)
|
||||
if len(routeHook) > 0 {
|
||||
for _, fn := range routeHook {
|
||||
v, ok := fn(value)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
m[v.Path] = v
|
||||
if v.Type != "reg" {
|
||||
continue
|
||||
}
|
||||
if v.Path != key {
|
||||
vvv, err := regexp.Compile(v.Path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vv = vvv
|
||||
}
|
||||
rrs[v.Path] = vv
|
||||
}
|
||||
} else {
|
||||
m[key] = value
|
||||
rrs[key] = vv
|
||||
}
|
||||
return true
|
||||
})
|
||||
return func() (map[string]Route, map[string]*regexp.Regexp) {
|
||||
return m, rrs
|
||||
}
|
||||
}
|
||||
func ResolveRoute(h *wp.Handle) {
|
||||
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)
|
||||
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)
|
||||
wp.Run(h, nil)
|
||||
h.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
h.C.Status(http.StatusNotFound)
|
||||
h.Abort()
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var iconSizes = []string{"site_icon-270", "site_icon-32", "site_icon-192", "site_icon-180"}
|
||||
|
||||
func CalSiteIcon(h *Handle) (r string) {
|
||||
id := str.ToInteger[uint64](wpconfig.GetOption("site_icon"), 0)
|
||||
if id < 1 {
|
||||
return
|
||||
}
|
||||
icon, err := cache.GetPostById(h.C, id)
|
||||
if err != nil || icon.AttachmentMetadata.File == "" {
|
||||
return
|
||||
}
|
||||
m := strings.Join(strings.Split(icon.AttachmentMetadata.File, "/")[:2], "/")
|
||||
size := slice.FilterAndMap(iconSizes, func(t string) (string, bool) {
|
||||
s, ok := icon.AttachmentMetadata.Sizes[t]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
switch t {
|
||||
case "site_icon-270":
|
||||
return fmt.Sprintf(`<meta name="msapplication-TileImage" content="/wp-content/uploads/%s/%s" />`, m, s.File), true
|
||||
case "site_icon-180":
|
||||
return fmt.Sprintf(`<link rel="apple-touch-icon" href="/wp-content/uploads/%s/%s" />`, m, s.File), true
|
||||
default:
|
||||
ss := strings.Replace(t, "site_icon-", "", 1)
|
||||
return fmt.Sprintf(`<link rel="icon" href="/wp-content/uploads/%s/%s" sizes="%sx%s" />`, m, s.File, ss, ss), true
|
||||
}
|
||||
})
|
||||
r = strings.Join(size, "\n")
|
||||
return
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/elliotchance/phpserialize"
|
||||
"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"
|
||||
)
|
||||
|
||||
var GetStickPosts = reload.BuildValFnWithConfirm("stickPostsSlice", ParseStickPosts)
|
||||
|
||||
func ParseStickPosts(h *Handle) (r []models.Posts, ok bool) {
|
||||
v := wpconfig.GetOption("sticky_posts")
|
||||
if v == "" {
|
||||
return
|
||||
}
|
||||
array, err := phpserialize.UnmarshalIndexedArray([]byte(v))
|
||||
if err != nil {
|
||||
logs.Error(err, "解析option sticky_posts错误", v)
|
||||
return
|
||||
}
|
||||
r = slice.FilterAndMap(array, func(t any) (models.Posts, bool) {
|
||||
id := str.ToInt[uint64](fmt.Sprintf("%v", t))
|
||||
post, err := cache.GetPostById(h.C, id)
|
||||
post.IsSticky = true
|
||||
return post, err == nil
|
||||
})
|
||||
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 {
|
||||
return v.Id
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handle) IsStick(id uint64) bool {
|
||||
_, ok := GetStickMapPosts(h)[id]
|
||||
return ok
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
{{define "common/head"}}
|
||||
|
||||
{{ callFuncString .calComponent "headScript"}}
|
||||
|
||||
{{if .externHead}}
|
||||
{{.externHead|unescaped}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "common/footer"}}
|
||||
{{ callFuncString .calComponent "footerScript"}}
|
||||
{{if .externFooter}}
|
||||
{{.externFooter|unescaped}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
{{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>
|
||||
{{end}}
|
||||
{{end}}
|
|
@ -1,305 +0,0 @@
|
|||
package wp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Handle struct {
|
||||
C *gin.Context
|
||||
theme string
|
||||
isInited bool
|
||||
Session sessions.Session
|
||||
ginH gin.H
|
||||
password string
|
||||
scene string
|
||||
Code int
|
||||
Stats string
|
||||
templ string
|
||||
themeMods wpconfig.ThemeMods
|
||||
err error
|
||||
errLevel int8
|
||||
abort bool
|
||||
stopPipe bool
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (h *Handle) GinH() gin.H {
|
||||
return h.ginH
|
||||
}
|
||||
|
||||
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) ComponentHook() *safety.Map[string, map[string][]func(Components[string]) (Components[string], bool)] {
|
||||
return handleComponentHook
|
||||
}
|
||||
|
||||
func (h *Handle) Handlers() *safety.Map[string, map[string][]HandleCall] {
|
||||
return handlerss
|
||||
}
|
||||
|
||||
func (h *Handle) HandleHook() *safety.Map[string, map[string][]func(HandleCall) (HandleCall, bool)] {
|
||||
return handleHooks
|
||||
}
|
||||
|
||||
type HandlePlugins map[string]HandleFn[*Handle]
|
||||
|
||||
// Components Order 为执行顺序,降序执行
|
||||
type Components[T any] struct {
|
||||
Name string
|
||||
Val T
|
||||
Fn func(*Handle) T
|
||||
Order float64
|
||||
Cached bool
|
||||
}
|
||||
|
||||
type HandleFn[T any] func(T)
|
||||
|
||||
type HandleCall struct {
|
||||
Fn HandleFn[*Handle]
|
||||
Order float64
|
||||
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()
|
||||
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()
|
||||
pluginFn, ok := v.(func(*Handle))
|
||||
if ok {
|
||||
pluginFn(h)
|
||||
}
|
||||
return *h
|
||||
}
|
||||
|
||||
var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, false)
|
||||
|
||||
func InitHandle(configFn func(*Handle), h *Handle) {
|
||||
hh := GetInitHandleFn(configFn, h)
|
||||
mods, err := wpconfig.GetThemeMods(h.theme)
|
||||
logs.IfError(err, "获取mods失败")
|
||||
h.themeMods = mods
|
||||
h.ginH = maps.Copy(hh.ginH)
|
||||
h.ginH["calPostClass"] = postClass(h)
|
||||
h.ginH["calBodyClass"] = bodyClass(h)
|
||||
h.ginH["customLogo"] = customLogo(h)
|
||||
h.ginH["calComponent"] = CalComponent(h)
|
||||
h.isInited = true
|
||||
}
|
||||
|
||||
func (h *Handle) Abort() {
|
||||
h.abort = true
|
||||
h.stopPipe = true
|
||||
}
|
||||
func (h *Handle) StopPipe() {
|
||||
h.stopPipe = true
|
||||
}
|
||||
func (h *Handle) StopHandle() {
|
||||
h.abort = true
|
||||
}
|
||||
|
||||
func (h *Handle) CommonThemeMods() wpconfig.ThemeMods {
|
||||
return h.themeMods
|
||||
}
|
||||
|
||||
func (h *Handle) Err() error {
|
||||
return h.err
|
||||
}
|
||||
|
||||
func (h *Handle) SetErr(err error, level int8) {
|
||||
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
|
||||
}
|
||||
|
||||
func (h *Handle) SetDatas(GinH gin.H) {
|
||||
maps.Merge(h.ginH, GinH)
|
||||
}
|
||||
func (h *Handle) SetData(k string, v any) {
|
||||
h.ginH[k] = v
|
||||
}
|
||||
|
||||
func NewHandle(c *gin.Context, scene string, theme string) *Handle {
|
||||
return &Handle{
|
||||
C: c,
|
||||
theme: theme,
|
||||
Session: sessions.Default(c),
|
||||
scene: scene,
|
||||
Stats: constraints.Ok,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handle) GetPassword() string {
|
||||
if h.password != "" {
|
||||
return h.password
|
||||
}
|
||||
pw := h.Session.Get("post_password")
|
||||
if pw != nil {
|
||||
h.password = pw.(string)
|
||||
}
|
||||
return h.password
|
||||
}
|
||||
|
||||
func PreTemplate(h *Handle) {
|
||||
if h.templ == "" {
|
||||
h.templ = str.Join(h.theme, "/posts/index.gohtml")
|
||||
if h.scene == constraints.Detail {
|
||||
h.templ = str.Join(h.theme, "/posts/detail.gohtml")
|
||||
}
|
||||
}
|
||||
}
|
||||
func PreCodeAndStats(h *Handle) {
|
||||
if h.Stats != "" && h.Code != 0 {
|
||||
return
|
||||
}
|
||||
switch h.Stats {
|
||||
case constraints.Ok:
|
||||
h.Code = http.StatusOK
|
||||
case constraints.ParamError, constraints.Error404:
|
||||
h.Code = http.StatusNotFound
|
||||
case constraints.InternalErr:
|
||||
h.Code = http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
||||
|
||||
func (h *Handle) RenderHtml(t *template.Template, statsCode int, name string) {
|
||||
header := h.C.Writer.Header()
|
||||
if val := header["Content-Type"]; len(val) == 0 {
|
||||
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)
|
||||
}
|
||||
h.Abort()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handle) PushHandlers(pipeScene string, call HandleCall, statsOrScene ...string) {
|
||||
for _, s := range statsOrScene {
|
||||
h.PushHandler(pipeScene, s, call)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handle) CommonComponents() {
|
||||
h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss)
|
||||
h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate"))
|
||||
ReplyCommentJs(h)
|
||||
AdditionScript(h)
|
||||
}
|
||||
|
||||
func AdditionScript(h *Handle) {
|
||||
s := config.GetConfig().ExternScript
|
||||
if len(s) < 1 {
|
||||
return
|
||||
}
|
||||
fn := func(f, name string) {
|
||||
if f == "" {
|
||||
return
|
||||
}
|
||||
ss, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
logs.Error(err, str.Join("解析", name, "失败"), f)
|
||||
} else {
|
||||
h.PushComponents(constraints.AllScene, constraints.HeadScript, NewComponent(name, string(ss), false, 0, nil))
|
||||
}
|
||||
}
|
||||
switch len(s) {
|
||||
case 1:
|
||||
fn(s[0], "externHead")
|
||||
case 2:
|
||||
fn(s[0], "externHead")
|
||||
fn(s[1], "externFooter")
|
||||
}
|
||||
}
|
||||
|
||||
func PreRenderTemplate(h *Handle) {
|
||||
h.C.HTML(h.Code, h.templ, h.ginH)
|
||||
h.Abort()
|
||||
}
|
||||
|
||||
func NewHandleFn(fn HandleFn[*Handle], order float64, name string) HandleCall {
|
||||
return HandleCall{Fn: fn, Order: order, Name: name}
|
||||
}
|
||||
|
||||
func NothingToDo(h *Handle) {
|
||||
h.Abort()
|
||||
}
|
||||
|
||||
func (h *Handle) IsHttps() bool {
|
||||
if h.C.Request.TLS != nil {
|
||||
return true
|
||||
}
|
||||
return "https" == strings.ToLower(h.C.Request.Header.Get("X-Forwarded-Proto"))
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package wpconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fthvgb1/wp-go/app/phphelper"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var options safety.Map[string, string]
|
||||
var phpArr safety.Map[string, map[any]any]
|
||||
|
||||
var ctx context.Context
|
||||
|
||||
func InitOptions() error {
|
||||
options.Flush()
|
||||
phpArr.Flush()
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
ops, err := model.FindToStringMap[models.Options](ctx, model.Conditions(
|
||||
model.Where(model.SqlBuilder{{"autoload", "yes"}}),
|
||||
model.Fields("option_name k, option_value v"),
|
||||
))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, option := range ops {
|
||||
options.Store(option["k"], option["v"])
|
||||
}
|
||||
return 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(
|
||||
model.Where(
|
||||
model.SqlBuilder{{"option_name", k}},
|
||||
),
|
||||
),
|
||||
)
|
||||
options.Store(k, vv)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return vv
|
||||
}
|
||||
|
||||
func GetLang() string {
|
||||
s, ok := options.Load("WPLANG")
|
||||
if !ok {
|
||||
s = "zh-CN"
|
||||
}
|
||||
return strings.Replace(s, "_", "-", 1)
|
||||
}
|
||||
|
||||
func GetPHPArrayVal[T any](optionName string, defaults T, key ...any) T {
|
||||
op, ok := phpArr.Load(optionName)
|
||||
if ok {
|
||||
return maps.GetAnyAnyValWithDefaults(op, defaults, key...)
|
||||
}
|
||||
v := GetOption(optionName)
|
||||
if v == "" {
|
||||
return defaults
|
||||
}
|
||||
arr, err := phphelper.UnPHPSerializeToAnyAnyMap(v)
|
||||
if err != nil {
|
||||
return defaults
|
||||
}
|
||||
phpArr.Store(optionName, arr)
|
||||
return maps.GetAnyAnyValWithDefaults(arr, defaults, key...)
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package wpconfig
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||
"github.com/fthvgb1/wp-go/model"
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
)
|
||||
|
||||
var terms = safety.NewMap[uint64, models.Terms]()
|
||||
var termTaxonomies = safety.NewMap[uint64, models.TermTaxonomy]()
|
||||
|
||||
var my = safety.NewMap[uint64, models.TermsMy]()
|
||||
|
||||
func GetTerm(termId uint64) (models.Terms, bool) {
|
||||
return terms.Load(termId)
|
||||
}
|
||||
|
||||
func GetTermTaxonomy(termId uint64) (models.TermTaxonomy, bool) {
|
||||
return termTaxonomies.Load(termId)
|
||||
}
|
||||
func GetTermMy(termId uint64) (models.TermsMy, bool) {
|
||||
return my.Load(termId)
|
||||
}
|
||||
|
||||
func InitTerms() (err error) {
|
||||
terms.Flush()
|
||||
termTaxonomies.Flush()
|
||||
term, err := model.SimpleFind[models.Terms](ctx, nil, "*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, wpTerms := range term {
|
||||
terms.Store(wpTerms.TermId, wpTerms)
|
||||
}
|
||||
termTax, err := model.SimpleFind[models.TermTaxonomy](ctx, nil, "*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, taxonomy := range termTax {
|
||||
termTaxonomies.Store(taxonomy.TermTaxonomyId, taxonomy)
|
||||
if term, ok := terms.Load(taxonomy.TermId); ok {
|
||||
my.Store(taxonomy.TermId, models.TermsMy{
|
||||
Terms: term,
|
||||
TermTaxonomy: taxonomy,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
package wpconfig
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var templateFs embed.FS
|
||||
|
||||
func SetTemplateFs(fs embed.FS) {
|
||||
templateFs = fs
|
||||
}
|
||||
|
||||
// ThemeMods 只有部分公共的参数,其它的参数调用 GetThemeModsVal 函数获取
|
||||
type ThemeMods struct {
|
||||
CustomCssPostId int `json:"custom_css_post_id,omitempty"`
|
||||
NavMenuLocations map[string]int `json:"nav_menu_locations,omitempty"`
|
||||
CustomLogo int `json:"custom_logo,omitempty"`
|
||||
HeaderImage string `json:"header_image,omitempty"`
|
||||
BackgroundImage string `json:"background_image,omitempty"`
|
||||
BackgroundSize string `json:"background_size,omitempty"`
|
||||
BackgroundRepeat string `json:"background_repeat,omitempty"`
|
||||
BackgroundColor string `json:"background_color,omitempty"`
|
||||
BackgroundPreset string `json:"background_preset"`
|
||||
BackgroundPositionX string `json:"background_position_x,omitempty"`
|
||||
BackgroundPositionY string `json:"background_position_y"`
|
||||
BackgroundAttachment string `json:"background_attachment"`
|
||||
ColorScheme string `json:"color_scheme"`
|
||||
SidebarTextcolor string `json:"sidebar_textcolor,omitempty"`
|
||||
HeaderBackgroundColor string `json:"header_background_color,omitempty"`
|
||||
HeaderTextcolor string `json:"header_textcolor,omitempty"`
|
||||
HeaderVideo int `json:"header_video,omitempty"`
|
||||
ExternalHeaderVideo string `json:"external_header_video,omitempty"`
|
||||
HeaderImagData ImageData `json:"header_image_data,omitempty"`
|
||||
SidebarsWidgets Sidebars `json:"sidebars_widgets,omitempty"`
|
||||
ThemeSupport ThemeSupport
|
||||
}
|
||||
|
||||
type Sidebars struct {
|
||||
Time int `json:"time,omitempty"`
|
||||
Data SidebarsData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type ColorScheme struct {
|
||||
Label string `json:"label,omitempty"`
|
||||
Colors []string `json:"colors,omitempty"`
|
||||
}
|
||||
|
||||
type SidebarsData struct {
|
||||
WpInactiveWidgets []string `json:"wp_inactive_widgets,omitempty"`
|
||||
Sidebar1 []string `json:"sidebar-1,omitempty"`
|
||||
Sidebar2 []string `json:"sidebar-2,omitempty"`
|
||||
Sidebar3 []string `json:"sidebar-3,omitempty"`
|
||||
}
|
||||
|
||||
type ImageData struct {
|
||||
AttachmentId int64 `json:"attachment_id,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
ThumbnailUrl string `json:"thumbnail_url,omitempty"`
|
||||
Height int64 `json:"height,omitempty"`
|
||||
Width int64 `json:"width,omitempty"`
|
||||
}
|
||||
|
||||
func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except ...string) (r models.PostThumbnail) {
|
||||
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,
|
||||
Height: metadata.Height,
|
||||
MimeType: mimeType,
|
||||
FileSize: metadata.FileSize,
|
||||
}
|
||||
}
|
||||
if siz, ok := metadata.Sizes[Type]; ok {
|
||||
r.Path = fmt.Sprintf("%s/wp-content/uploads/%s", host, strings.ReplaceAll(metadata.File, filepath.Base(metadata.File), siz.File))
|
||||
r.Width = metadata.Sizes[Type].Width
|
||||
r.Height = metadata.Sizes[Type].Height
|
||||
|
||||
r.Srcset = strings.Join(maps.FilterToSlice[string](metadata.Sizes, func(s string, size models.MetaDataFileSize) (r string, ok bool) {
|
||||
up[len(up)-1] = size.File
|
||||
for _, s2 := range except {
|
||||
if s == s2 {
|
||||
return
|
||||
}
|
||||
}
|
||||
r = fmt.Sprintf("%s/wp-content/uploads/%s %dw", host, strings.Join(up, "/"), size.Width)
|
||||
ok = true
|
||||
return
|
||||
}), ", ")
|
||||
r.Sizes = fmt.Sprintf("(max-width: %dpx) 100vw, %dpx", r.Width, r.Width)
|
||||
if r.Width >= 740 && r.Width < 767 {
|
||||
r.Sizes = "(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px"
|
||||
} else if r.Width >= 767 {
|
||||
r.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||
}
|
||||
r.OriginAttachmentData = metadata
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var themeModes = func() *safety.Map[string, ThemeMods] {
|
||||
m := safety.NewMap[string, ThemeMods]()
|
||||
themeModsRaw = safety.NewMap[string, map[string]any]()
|
||||
reload.Append(func() {
|
||||
m.Flush()
|
||||
themeModsRaw.Flush()
|
||||
}, "theme-modes")
|
||||
|
||||
return m
|
||||
}()
|
||||
|
||||
var themeModsRaw *safety.Map[string, map[string]any]
|
||||
|
||||
var themeSupport = map[string]ThemeSupport{}
|
||||
|
||||
func SetThemeSupport(theme string, support ThemeSupport) {
|
||||
themeSupport[theme] = support
|
||||
}
|
||||
|
||||
func GetThemeModsVal[T any](theme, k string, defaults T) (r T) {
|
||||
m, ok := themeModsRaw.Load(theme)
|
||||
if !ok {
|
||||
r = defaults
|
||||
return
|
||||
}
|
||||
r = maps.GetStrAnyValWithDefaults(m, k, defaults)
|
||||
return
|
||||
}
|
||||
|
||||
func GetThemeMods(theme string) (r ThemeMods, err error) {
|
||||
r, ok := themeModes.Load(theme)
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
mods := GetOption(fmt.Sprintf("theme_mods_%s", theme))
|
||||
if mods == "" {
|
||||
return
|
||||
}
|
||||
m, err := phphelper.UnPHPSerializeToStrAnyMap(mods)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
themeModsRaw.Store(theme, m)
|
||||
//这里在的err可以不用处理,因为php的默认值和有设置过的类型可能不一样,直接按有设置的类型处理就行
|
||||
r, err = maps.StrAnyMapToStruct[ThemeMods](m)
|
||||
if err != nil {
|
||||
logs.Error(err, "解析thememods错误(可忽略)")
|
||||
err = nil
|
||||
}
|
||||
r.setThemeSupport(theme)
|
||||
themeModes.Store(theme, r)
|
||||
return
|
||||
}
|
||||
|
||||
func IsCustomBackground(theme string) bool {
|
||||
mods, err := GetThemeMods(theme)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if mods.BackgroundColor != "" && mods.BackgroundColor != "default-color" || mods.BackgroundImage != "" && mods.BackgroundImage != "default-image" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *ThemeMods) setThemeSupport(themeName string) {
|
||||
var v ThemeSupport
|
||||
vv, ok := themeSupport[themeName]
|
||||
if ok {
|
||||
m.ThemeSupport = vv
|
||||
} else {
|
||||
m.ThemeSupport = v
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user