Compare commits
296 Commits
Author | SHA1 | Date | |
---|---|---|---|
8aeec60181 | |||
58d3e4f645 | |||
e592a753bf | |||
d598fb5ce5 | |||
63f769796f | |||
7cbfdf8601 | |||
9c60d10568 | |||
dcbe760f09 | |||
d220bb2a6c | |||
eb7ccd4f2d | |||
39c9dc1b09 | |||
68beb0fcbb | |||
1f5f7f465d | |||
b7c09cb5a2 | |||
5942f76972 | |||
1f40810b78 | |||
da858d55e0 | |||
166bb08ed0 | |||
5eaf798a6c | |||
e2e6bcc8ce | |||
daa7101493 | |||
6f51f1c295 | |||
a8d1dcdd5b | |||
9af2257650 | |||
63591899bb | |||
ee9ba3fcf0 | |||
a78815f3d3 | |||
6209f2b5e5 | |||
08d3bca39c | |||
9b8e597066 | |||
3215b0c8ea | |||
c7c97c469f | |||
b45ad0e54e | |||
a0c6e48e83 | |||
3c3ed73971 | |||
be58a35245 | |||
67bdf1e070 | |||
981e0ad85b | |||
74159940b5 | |||
206e5902d1 | |||
2d38b36525 | |||
af712f06a5 | |||
73575965d3 | |||
f49ad731e3 | |||
802c7c7dcc | |||
20ea36c727 | |||
fd3199a83c | |||
e25f679ea6 | |||
4842d53316 | |||
65bfccdd48 | |||
c60146d614 | |||
9285698ee4 | |||
5e18c9babd | |||
8bdc53d175 | |||
ad9bdc7574 | |||
0352dd0fd0 | |||
6044b8eaec | |||
8d3197ee98 | |||
8fcf3ecca2 | |||
4d9d011213 | |||
12b75b9d82 | |||
568ab15a34 | |||
c38b62c82a | |||
0774b122ee | |||
f7d2377101 | |||
088fc306de | |||
57d345e445 | |||
f257a966e6 | |||
ceffeccf8d | |||
eed45f51ba | |||
d72bed0c8c | |||
0cdb3ba040 | |||
227de8bdc8 | |||
74304b5b12 | |||
ac29cf2448 | |||
137b2a9738 | |||
9f49a274cd | |||
7c3f8baaa2 | |||
547d8e59e6 | |||
7bbc961f72 | |||
66c02f7fc9 | |||
a5294e2e20 | |||
7978e9ee74 | |||
f6a5cf4255 | |||
928a608878 | |||
42d2795a05 | |||
041d06104b | |||
d1fb560578 | |||
42484e3f96 | |||
64a2c2e33b | |||
86d1616732 | |||
2eb58f732b | |||
936d033547 | |||
cd4787991d | |||
71ddf299e4 | |||
0f467bbc98 | |||
171b3bc59c | |||
f369cf4f22 | |||
ddf6e62e5a | |||
e502f581e1 | |||
acb064b762 | |||
f0c1744998 | |||
876a81c062 | |||
78f5157efe | |||
acbcbf455f | |||
179545f2bb | |||
48a3fe6588 | |||
3fd4f412b1 | |||
5273f4ecf9 | |||
b9c2527f4c | |||
1286338af0 | |||
21a4ff3b3e | |||
2302aa7ff8 | |||
bc71be7238 | |||
a4274b74dd | |||
03b448e70c | |||
879238cda1 | |||
a00bfff91c | |||
96daa93656 | |||
173eef2adc | |||
c5c3d5f78b | |||
228e19ee85 | |||
b1ebedd24c | |||
282e0c26e0 | |||
e3d57830b0 | |||
937e766294 | |||
19c0952005 | |||
3493eee919 | |||
e4fa93e893 | |||
a08b9538ae | |||
7431823ac7 | |||
23c9cc1596 | |||
ae6e496dd8 | |||
e408efacc8 | |||
448d6a6baf | |||
b6091c6b42 | |||
f2d69196bc | |||
b41b6e0a09 | |||
fe9ac0d126 | |||
1c33665e34 | |||
bafd839637 | |||
b4d3a41459 | |||
4705d17e0e | |||
60fd016b5f | |||
95e3298b7e | |||
d5a546c01a | |||
9580678b55 | |||
2d90b05cf8 | |||
0827cdc551 | |||
7c50f2b1a1 | |||
c95fd7e5da | |||
4242d850ff | |||
6caf07b575 | |||
044e55a399 | |||
a6ee333232 | |||
1353541c94 | |||
8f995d23dc | |||
871649a7e9 | |||
debcad6292 | |||
65a0ca94f6 | |||
40451970a0 | |||
dfe7bb3181 | |||
b0618cf800 | |||
21d0b8c041 | |||
471a58658f | |||
68c345f928 | |||
4058ff9b7b | |||
036f1a1212 | |||
87f639da58 | |||
ec211e8970 | |||
b69b01f27b | |||
7c571654a6 | |||
90d2432575 | |||
972ff7d815 | |||
2d2c6445e8 | |||
f9b565294f | |||
b87a1b4a1c | |||
625da92f10 | |||
0d3481f722 | |||
4323c508ba | |||
3ba2c02db5 | |||
2d61243ef1 | |||
120c874a8d | |||
da7dd410cd | |||
43ec093e4c | |||
43754377e8 | |||
3f299e5a84 | |||
4760f65b9b | |||
345cdcd4e0 | |||
72bad0fa36 | |||
023212ba7b | |||
8a9209196e | |||
8da369b166 | |||
9e71de8e97 | |||
f735211a92 | |||
5266e85f21 | |||
98cee2f18b | |||
b53819f733 | |||
125764711d | |||
db49f99c04 | |||
0d8ee89580 | |||
e505ee2e03 | |||
df7bd8c1c6 | |||
9e293e7f39 | |||
1f3ca99441 | |||
c09f54b9c0 | |||
78f0c0a87a | |||
1ecb338af5 | |||
7e55253126 | |||
e7e643f5d3 | |||
522a358819 | |||
96368234de | |||
49e4210365 | |||
54448f68b0 | |||
e10dc4e45e | |||
13520a0d43 | |||
bebe6bac81 | |||
a1f36da904 | |||
6f800230b0 | |||
e1c1da6083 | |||
ab56f45790 | |||
341b7197b8 | |||
26bdcb44ac | |||
e7ea2bf334 | |||
d48a156983 | |||
00c16c03a4 | |||
ec4dfad86a | |||
322d2ebe0a | |||
57a21010a8 | |||
14aad9e15b | |||
3f96c09d36 | |||
ca94295eb7 | |||
aaa1f3c937 | |||
decea28316 | |||
f6e2f86ee7 | |||
cb1ce2e878 | |||
e03194b768 | |||
8d398016ac | |||
a313210f71 | |||
2d476ea4f6 | |||
b6b92ede34 | |||
12a1fea5ed | |||
d211e49036 | |||
cc09668fd7 | |||
179e1e14e2 | |||
ad875857d8 | |||
1da5062356 | |||
1f7e51858b | |||
096514a677 | |||
240a41f1eb | |||
4f1a2f717e | |||
8b7edea000 | |||
01e10f69dd | |||
2f456f62c0 | |||
a905f59eae | |||
997839e98a | |||
f3595874ae | |||
5665e0021b | |||
|
a3b1e054d0 | ||
|
2ea52f1752 | ||
|
d3be997b8f | ||
|
0d62ebd5a5 | ||
|
5ce707e427 | ||
|
2ca1f10bfc | ||
|
ae20829209 | ||
|
9af2a04a97 | ||
|
4d50f60c62 | ||
|
7d27668159 | ||
|
cbf3cabb24 | ||
|
dd384b0169 | ||
|
e1d8c0098f | ||
|
ccc02399ef | ||
|
9b46a86d5d | ||
|
26950a37bb | ||
|
eab6db83eb | ||
|
1b1e3bf8f3 | ||
|
5ae0ad646e | ||
|
477d8fe6f9 | ||
|
b4cc570e8a | ||
|
9c89f44841 | ||
|
11e0c32c6f | ||
|
0f0c3997c0 | ||
|
e0786f7f8b | ||
|
00e42c2d56 | ||
|
e978e86304 | ||
|
8a5fc02247 | ||
|
ccbba30ae2 | ||
|
865b37cf89 | ||
|
bd7d20160a | ||
|
2a87ebdfd6 | ||
|
6d1fde655d | ||
|
efeeb8b675 | ||
|
4aa54b1ca7 | ||
|
2aacb682e1 | ||
|
2a68b73773 | ||
|
f885b5c8f0 |
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,3 +1,7 @@
|
||||||
.idea
|
.idea
|
||||||
wp-go.iml
|
/wp-go.iml
|
||||||
config.yaml
|
/config.yaml
|
||||||
|
err.log
|
||||||
|
/plugins/
|
||||||
|
/config.json
|
||||||
|
go.sum
|
|
@ -1,8 +1,7 @@
|
||||||
FROM golang:latest as gobulidIso
|
FROM golang:1.22.2-alpine as gobulidIso
|
||||||
COPY ./ /go/src/wp-go
|
COPY ./ /go/src/wp-go
|
||||||
WORKDIR /go/src/wp-go
|
WORKDIR /go/src/wp-go
|
||||||
ENV GOPROXY="https://goproxy.cn"
|
RUN go build -ldflags "-w" -tags netgo -o wp-go app/cmd/main.go
|
||||||
RUN go build -ldflags "-w" -tags netgo -o wp-go internal/cmd/main.go
|
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
WORKDIR /opt/wp-go
|
WORKDIR /opt/wp-go
|
||||||
|
|
79
README.md
79
README.md
|
@ -1,18 +1,87 @@
|
||||||
## wp-go
|
## wp-go
|
||||||
|
|
||||||
一个go写的WordPress的前端,功能比较简单,只有列表页和详情页,rss2,主题只有twentyfifteen和twentyseventeen两套主题,插件的话只有一个简单的列表页的摘要生成和enlighter代码高亮。本身只用于展示文章,添加评论走的转发请求到php的WordPress。因为大量用了泛型功能,所以要求go的版本在1.19及以上,越新越好。。。。
|
[en readme](https://github.com/fthvgb1/wp-go/blob/master/readme_en.md)
|
||||||
|
|
||||||
|
一个go写的WordPress的前端,功能比较简单,只有列表页和详情页,rss2,主题只有twentyfifteen和twentyseventeen两套主题,插件的话只有一个简单的列表页的摘要生成和enlighter代码高亮。本身只用于展示文章及评论。要求go的版本在1.20以上,越新越好。。。
|
||||||
|
|
||||||
#### 特色功能
|
#### 特色功能
|
||||||
|
|
||||||
- 多种缓存配置
|
- 基本实现全站缓存,并且可防止缓存击穿
|
||||||
|
- 列表页也可以高亮语法格式化显示代码
|
||||||
|
- 简易插件扩展开发机制、配置后支持热加载更新
|
||||||
|
- 使用.so扩展主题、插件、路由等
|
||||||
|
- 丰富繁杂的配置,呃,配置是有点儿多,虽然大部分都是可选项。。。
|
||||||
- 添加评论或panic时发邮件通知,包涵栈调用和请求信息
|
- 添加评论或panic时发邮件通知,包涵栈调用和请求信息
|
||||||
- 简单的流量限制中间件
|
- 简单的流量限制中间件,可以限制全瞬时最大请求数量
|
||||||
- 除配置文件外将所有静态资源都打包到执行文件中
|
- 除配置文件外将所有静态资源都打包到执行文件中
|
||||||
- 支持密码查看,且cookie信息可被php版所验证
|
- 支持密码查看,且cookie信息可被php版所验证
|
||||||
- 支持rss2订阅
|
- 支持rss2订阅
|
||||||
- 热更新配置、清空缓存
|
- 热更新配置、切换主题、清空缓存
|
||||||
- kill -SIGUSR1 PID 更新配置和清空缓存
|
- kill -SIGUSR1 PID 更新配置和清空缓存
|
||||||
- kill -SIGUSR2 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 隐藏登录入口 |
|
||||||
|
|
||||||
#### 其它
|
#### 其它
|
||||||
用的gin框架和sqlx,在外面封装了层查询的方法。后台可以设置的比较少,大部分设置还没打通。
|
|
||||||
|
用的gin框架和sqlx,在外面封装了层查询的方法。
|
||||||
|
|
||||||
|
#### 鸣谢
|
||||||
|
|
||||||
|
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." width="30%"></a>
|
||||||
|
|
||||||
|
|
293
app/actions/comment.go
Normal file
293
app/actions/comment.go
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
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,8 +1,8 @@
|
||||||
package actions
|
package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/cache"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/cache"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -10,6 +10,30 @@ import (
|
||||||
|
|
||||||
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
var tmp = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||||
|
|
||||||
|
func Feed(c *gin.Context) {
|
||||||
|
v, ok := c.GetQuery("feed")
|
||||||
|
if !ok || v == "" {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch v {
|
||||||
|
case "rss2":
|
||||||
|
p, ok := c.GetQuery("p")
|
||||||
|
if ok && p != "" {
|
||||||
|
c.AddParam("id", p)
|
||||||
|
PostFeed(c)
|
||||||
|
} else {
|
||||||
|
SiteFeed(c)
|
||||||
|
}
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
case "comments-rss2":
|
||||||
|
CommentsFeed(c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
||||||
eTag := str.Md5(lastTime.Format(tmp))
|
eTag := str.Md5(lastTime.Format(tmp))
|
||||||
since := c.Request.Header.Get("If-Modified-Since")
|
since := c.Request.Header.Get("If-Modified-Since")
|
||||||
|
@ -24,20 +48,21 @@ func isCacheExpired(c *gin.Context, lastTime time.Time) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Feed(c *gin.Context) {
|
func SiteFeed(c *gin.Context) {
|
||||||
if !isCacheExpired(c, cache.FeedCache().GetLastSetTime()) {
|
feed := cache.FeedCache()
|
||||||
|
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := cache.FeedCache().GetCache(c, time.Second, c)
|
r, err := feed.GetCache(c, time.Second, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(r[0], c, cache.FeedCache().GetLastSetTime())
|
setFeed(r[0], c, feed.GetLastSetTime(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFeed(s string, c *gin.Context, t time.Time) {
|
func setFeed(s string, c *gin.Context, t time.Time) {
|
||||||
|
@ -51,31 +76,33 @@ func setFeed(s string, c *gin.Context, t time.Time) {
|
||||||
|
|
||||||
func PostFeed(c *gin.Context) {
|
func PostFeed(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if !isCacheExpired(c, cache.PostFeedCache().GetLastSetTime(c, id)) {
|
postFeed := cache.PostFeedCache()
|
||||||
|
if !isCacheExpired(c, postFeed.GetLastSetTime(c, id)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s, err := cache.PostFeedCache().GetCache(c, id, time.Second, c, id)
|
s, err := postFeed.GetCache(c, id, time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(s, c, cache.PostFeedCache().GetLastSetTime(c, id))
|
setFeed(s, c, postFeed.GetLastSetTime(c, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommentsFeed(c *gin.Context) {
|
func CommentsFeed(c *gin.Context) {
|
||||||
if !isCacheExpired(c, cache.CommentsFeedCache().GetLastSetTime()) {
|
feed := cache.CommentsFeedCache()
|
||||||
|
if !isCacheExpired(c, feed.GetLastSetTime(c)) {
|
||||||
c.Status(http.StatusNotModified)
|
c.Status(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r, err := cache.CommentsFeedCache().GetCache(c, time.Second, c)
|
r, err := feed.GetCache(c, time.Second, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFeed(r[0], c, cache.CommentsFeedCache().GetLastSetTime())
|
setFeed(r[0], c, feed.GetLastSetTime(c))
|
||||||
}
|
}
|
|
@ -2,9 +2,9 @@ package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/phphelper"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/phphelper"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
15
app/actions/themehook.go
Normal file
15
app/actions/themehook.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
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
Normal file
111
app/cmd/main.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ package mail
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/soxfmr/gomail"
|
"gopkg.in/gomail.v2"
|
||||||
"mime"
|
"mime"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,7 @@ func SendMail(mailTo []string, subject string, body string, files ...string) err
|
||||||
c.Mail.User,
|
c.Mail.User,
|
||||||
c.Mail.Pass,
|
c.Mail.Pass,
|
||||||
)
|
)
|
||||||
if !c.Mail.Ssl {
|
if c.Mail.InsecureSkipVerify {
|
||||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
}
|
}
|
||||||
err := d.DialAndSend(m)
|
err := d.DialAndSend(m)
|
|
@ -1,7 +1,7 @@
|
||||||
package mail
|
package mail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,9 +19,9 @@ func FlowLimit(maxRequestSleepNum, maxRequestNum int64, sleepTime []time.Duratio
|
||||||
}
|
}
|
||||||
s := safety.Var[[]time.Duration]{}
|
s := safety.Var[[]time.Duration]{}
|
||||||
s.Store(sleepTime)
|
s.Store(sleepTime)
|
||||||
fn := func(msn, mn int64, st []time.Duration) {
|
fn := func(sleepNum, maxNum int64, st []time.Duration) {
|
||||||
atomic.StoreInt64(&maxRequestSleepNum, msn)
|
atomic.StoreInt64(&maxRequestSleepNum, sleepNum)
|
||||||
atomic.StoreInt64(&maxRequestNum, mn)
|
atomic.StoreInt64(&maxRequestNum, maxNum)
|
||||||
s.Store(st)
|
s.Store(st)
|
||||||
}
|
}
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
|
@ -25,14 +25,25 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
|
||||||
fn(num)
|
fn(num)
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
if atomic.LoadInt64(m.limitNum) <= 0 {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
ip := c.ClientIP()
|
ip := c.ClientIP()
|
||||||
s := false
|
|
||||||
m.mux.RLock()
|
m.mux.RLock()
|
||||||
i, ok := m.m[ip]
|
i, ok := m.m[ip]
|
||||||
m.mux.RUnlock()
|
m.mux.RUnlock()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
m.mux.Lock()
|
||||||
|
i = new(int64)
|
||||||
|
m.m[ip] = i
|
||||||
|
m.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
ii := atomic.LoadInt64(i)
|
ii := atomic.LoadInt64(i)
|
||||||
if s && ii > 0 {
|
if ii > 0 {
|
||||||
atomic.AddInt64(i, -1)
|
atomic.AddInt64(i, -1)
|
||||||
if atomic.LoadInt64(i) == 0 {
|
if atomic.LoadInt64(i) == 0 {
|
||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
|
@ -42,20 +53,12 @@ func IpLimit(num int64) (func(ctx *gin.Context), func(int64)) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !ok {
|
if atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) {
|
||||||
m.mux.Lock()
|
|
||||||
i = new(int64)
|
|
||||||
m.m[ip] = i
|
|
||||||
m.mux.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if atomic.LoadInt64(m.limitNum) > 0 && atomic.LoadInt64(i) >= atomic.LoadInt64(m.limitNum) {
|
|
||||||
c.Status(http.StatusForbidden)
|
c.Status(http.StatusForbidden)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
atomic.AddInt64(i, 1)
|
atomic.AddInt64(i, 1)
|
||||||
s = true
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}, fn
|
}, fn
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fthvgb1/wp-go/internal/cmd/reload"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SearchLimit(num int64) func(ctx *gin.Context) {
|
func SearchLimit(num int64) func(ctx *gin.Context) {
|
||||||
fn, reFn := IpLimit(num)
|
fn, reFn := IpLimit(num)
|
||||||
reload.Push(func() {
|
reload.Append(func() {
|
||||||
reFn(config.GetConfig().SingleIpSearchNum)
|
reFn(config.GetConfig().SingleIpSearchNum)
|
||||||
})
|
}, "search-ip-limit-number")
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
if c.Query("s") != "" {
|
if c.Query("s") != "" {
|
||||||
fn(c)
|
fn(c)
|
|
@ -3,16 +3,16 @@ package middleware
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/mail"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/mail"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/logs"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -48,7 +48,7 @@ func RecoverAndSendMail(w io.Writer) func(ctx *gin.Context) {
|
||||||
fmt.Sprintf("%s%s %s 发生错误", fmt.Sprintf(wpconfig.GetOption("siteurl")), c.FullPath(), time.Now().Format(time.RFC1123Z)), content)
|
fmt.Sprintf("%s%s %s 发生错误", fmt.Sprintf(wpconfig.GetOption("siteurl")), c.FullPath(), time.Now().Format(time.RFC1123Z)), content)
|
||||||
|
|
||||||
if er != nil {
|
if er != nil {
|
||||||
logs.ErrPrintln(er, "recover send mail fail", fmt.Sprintf("%v", err))
|
logs.IfError(er, "recover send mail fail", fmt.Sprintf("%v", err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ func stack(skip int) []byte {
|
||||||
// Print this much at least. If we can't find the source, it won't show.
|
// Print this much at least. If we can't find the source, it won't show.
|
||||||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
||||||
if file != lastFile {
|
if file != lastFile {
|
||||||
data, err := ioutil.ReadFile(file)
|
data, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
|
@ -2,8 +2,10 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +18,11 @@ var path = map[string]struct{}{
|
||||||
func SetStaticFileCache(c *gin.Context) {
|
func SetStaticFileCache(c *gin.Context) {
|
||||||
f := strings.Split(strings.TrimLeft(c.FullPath(), "/"), "/")
|
f := strings.Split(strings.TrimLeft(c.FullPath(), "/"), "/")
|
||||||
if _, ok := path[f[0]]; ok {
|
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
|
t := config.GetConfig().CacheTime.CacheControl
|
||||||
if t > 0 {
|
if t > 0 {
|
||||||
c.Header("Cache-Control", fmt.Sprintf("private, max-age=%d", int(t.Seconds())))
|
c.Header("Cache-Control", fmt.Sprintf("private, max-age=%d", int(t.Seconds())))
|
|
@ -1,9 +1,9 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
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/fthvgb1/wp-go/helper/maps"
|
||||||
"github.com/fthvgb1/wp-go/internal/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,7 +19,7 @@ func ValidateServerNames() func(ctx *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
})
|
}, "site-names")
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
m := sites.Load()
|
m := sites.Load()
|
69
app/ossigns/signs.go
Normal file
69
app/ossigns/signs.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
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()
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ func UnPHPSerializeToStruct[T any](s string) (r T, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnPHPSerializeToAnyMap(s string) (map[string]any, error) {
|
func UnPHPSerializeToStrAnyMap(s string) (map[string]any, error) {
|
||||||
m := map[string]any{}
|
m := map[string]any{}
|
||||||
var r map[any]any
|
var r map[any]any
|
||||||
err := phpserialize.Unmarshal([]byte(s), &r)
|
err := phpserialize.Unmarshal([]byte(s), &r)
|
||||||
|
@ -27,3 +27,11 @@ func UnPHPSerializeToAnyMap(s string) (map[string]any, error) {
|
||||||
m = maps.AnyAnyToStrAny(r)
|
m = maps.AnyAnyToStrAny(r)
|
||||||
return m, err
|
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
Normal file
126
app/pkg/cache/cache.go
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
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
Normal file
39
app/pkg/cache/categoryandtag.go
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
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
Normal file
124
app/pkg/cache/comments.go
vendored
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -1,17 +1,20 @@
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/logs"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/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"
|
"github.com/fthvgb1/wp-go/cache"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/cachemanager"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/logs"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/plugins"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"github.com/fthvgb1/wp-go/plugin/digest"
|
"github.com/fthvgb1/wp-go/plugin/digest"
|
||||||
"github.com/fthvgb1/wp-go/rss2"
|
"github.com/fthvgb1/wp-go/rss2"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -32,20 +35,25 @@ func InitFeed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommentsFeedCache query func see CommentsFeed
|
||||||
func CommentsFeedCache() *cache.VarCache[[]string] {
|
func CommentsFeedCache() *cache.VarCache[[]string] {
|
||||||
return commentsFeedCache
|
r, _ := cachemanager.GetVarCache[[]string]("commentsFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FeedCache query func see SiteFeed
|
||||||
func FeedCache() *cache.VarCache[[]string] {
|
func FeedCache() *cache.VarCache[[]string] {
|
||||||
return feedCache
|
r, _ := cachemanager.GetVarCache[[]string]("siteFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostFeedCache query func see PostFeed
|
||||||
func PostFeedCache() *cache.MapCache[string, string] {
|
func PostFeedCache() *cache.MapCache[string, string] {
|
||||||
return postFeedCache
|
r, _ := cachemanager.GetMapCache[string, string]("postFeed")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func feed(arg ...any) (xml []string, err error) {
|
func SiteFeed(c context.Context, _ ...any) (xml []string, err error) {
|
||||||
c := arg[0].(*gin.Context)
|
|
||||||
r := RecentPosts(c, 10)
|
r := RecentPosts(c, 10)
|
||||||
ids := slice.Map(r, func(t models.Posts) uint64 {
|
ids := slice.Map(r, func(t models.Posts) uint64 {
|
||||||
return t.Id
|
return t.Id
|
||||||
|
@ -60,10 +68,10 @@ func feed(arg ...any) (xml []string, err error) {
|
||||||
rs.Items = slice.Map(posts, func(t models.Posts) rss2.Item {
|
rs.Items = slice.Map(posts, func(t models.Posts) rss2.Item {
|
||||||
desc := "无法提供摘要。这是一篇受保护的文章。"
|
desc := "无法提供摘要。这是一篇受保护的文章。"
|
||||||
if t.PostPassword != "" {
|
if t.PostPassword != "" {
|
||||||
plugins.PasswordProjectTitle(&t)
|
wpposts.PasswordProjectTitle(&t)
|
||||||
plugins.PasswdProjectContent(&t)
|
wpposts.PasswdProjectContent(&t)
|
||||||
} else {
|
} else {
|
||||||
desc = digest.Raw(t.PostContent, 55, fmt.Sprintf("/p/%d", t.Id))
|
desc = plugins.Digests(t.PostContent, t.Id, 55, nil)
|
||||||
}
|
}
|
||||||
l := ""
|
l := ""
|
||||||
if t.CommentStatus == "open" && t.CommentCount > 0 {
|
if t.CommentStatus == "open" && t.CommentCount > 0 {
|
||||||
|
@ -91,12 +99,10 @@ func feed(arg ...any) (xml []string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func postFeed(arg ...any) (x string, err error) {
|
func PostFeed(c context.Context, id string, _ ...any) (x string, err error) {
|
||||||
c := arg[0].(*gin.Context)
|
|
||||||
id := arg[1].(string)
|
|
||||||
ID := str.ToInteger[uint64](id, 0)
|
ID := str.ToInteger[uint64](id, 0)
|
||||||
maxId, err := GetMaxPostId(c)
|
maxId, err := GetMaxPostId(c)
|
||||||
logs.ErrPrintln(err, "get max post id")
|
logs.IfError(err, "get max post id")
|
||||||
if ID < 1 || ID > maxId || err != nil {
|
if ID < 1 || ID > maxId || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -104,7 +110,12 @@ func postFeed(arg ...any) (x string, err error) {
|
||||||
if post.Id == 0 || err != nil {
|
if post.Id == 0 || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
comments, err := PostComments(c, post.Id)
|
limit := str.ToInteger(wpconfig.GetOption("comments_per_page"), 10)
|
||||||
|
ids, err := PostTopLevelCommentIds(c, ID, 1, limit, 0, "desc", "latest-comment")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
comments, err := GetCommentDataByIds(c, ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -116,14 +127,18 @@ func postFeed(arg ...any) (x string, err error) {
|
||||||
rs.Link = fmt.Sprintf("%s/p/%d", site, post.Id)
|
rs.Link = fmt.Sprintf("%s/p/%d", site, post.Id)
|
||||||
rs.LastBuildDate = time.Now().Format(timeFormat)
|
rs.LastBuildDate = time.Now().Format(timeFormat)
|
||||||
if post.PostPassword != "" {
|
if post.PostPassword != "" {
|
||||||
plugins.PasswordProjectTitle(&post)
|
wpposts.PasswordProjectTitle(&post)
|
||||||
plugins.PasswdProjectContent(&post)
|
wpposts.PasswdProjectContent(&post)
|
||||||
if len(comments) > 0 {
|
if len(comments) > 0 {
|
||||||
t := comments[len(comments)-1]
|
t := comments[len(comments)-1]
|
||||||
|
u, err := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
rs.Items = []rss2.Item{
|
rs.Items = []rss2.Item{
|
||||||
{
|
{
|
||||||
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: fmt.Sprintf("%s%s", site, u),
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
||||||
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
||||||
|
@ -134,9 +149,14 @@ func postFeed(arg ...any) (x string, err error) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
|
rs.Items = slice.Map(comments, func(t models.Comments) rss2.Item {
|
||||||
|
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if er != nil {
|
||||||
|
err = errors.Join(err, er)
|
||||||
|
return rss2.Item{}
|
||||||
|
}
|
||||||
return rss2.Item{
|
return rss2.Item{
|
||||||
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
Title: fmt.Sprintf("评价者:%s", t.CommentAuthor),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: fmt.Sprintf("%s%s", site, u),
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
||||||
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
Guid: fmt.Sprintf("%s#comment-%d", post.Guid, t.CommentId),
|
||||||
|
@ -149,15 +169,14 @@ func postFeed(arg ...any) (x string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func commentsFeed(args ...any) (r []string, err error) {
|
func CommentsFeed(c context.Context, _ ...any) (r []string, err error) {
|
||||||
c := args[0].(*gin.Context)
|
|
||||||
commens := RecentComments(c, 10)
|
commens := RecentComments(c, 10)
|
||||||
rs := templateRss
|
rs := templateRss
|
||||||
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
|
rs.Title = fmt.Sprintf("\"%s\"的评论", wpconfig.GetOption("blogname"))
|
||||||
rs.LastBuildDate = time.Now().Format(timeFormat)
|
rs.LastBuildDate = time.Now().Format(timeFormat)
|
||||||
site := wpconfig.GetOption("siteurl")
|
site := wpconfig.GetOption("siteurl")
|
||||||
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
|
rs.AtomLink = fmt.Sprintf("%s/comments/feed", site)
|
||||||
com, err := GetCommentByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
|
com, err := GetCommentDataByIds(c, slice.Map(commens, func(t models.Comments) uint64 {
|
||||||
return t.CommentId
|
return t.CommentId
|
||||||
}))
|
}))
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -168,16 +187,20 @@ func commentsFeed(args ...any) (r []string, err error) {
|
||||||
desc := "评论受保护:要查看请输入密码。"
|
desc := "评论受保护:要查看请输入密码。"
|
||||||
content := t.CommentContent
|
content := t.CommentContent
|
||||||
if post.PostPassword != "" {
|
if post.PostPassword != "" {
|
||||||
plugins.PasswordProjectTitle(&post)
|
wpposts.PasswordProjectTitle(&post)
|
||||||
plugins.PasswdProjectContent(&post)
|
wpposts.PasswdProjectContent(&post)
|
||||||
content = post.PostContent
|
content = post.PostContent
|
||||||
} else {
|
} else {
|
||||||
desc = digest.ClearHtml(t.CommentContent)
|
content = digest.StripTags(t.CommentContent, "")
|
||||||
content = desc
|
|
||||||
}
|
}
|
||||||
|
u, er := GetCommentUrl(c, t.CommentId, t.CommentPostId)
|
||||||
|
if er != nil {
|
||||||
|
errors.Join(err, er)
|
||||||
|
}
|
||||||
|
u = str.Join(site, u)
|
||||||
return rss2.Item{
|
return rss2.Item{
|
||||||
Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle),
|
Title: fmt.Sprintf("%s对《%s》的评论", t.CommentAuthor, post.PostTitle),
|
||||||
Link: fmt.Sprintf("%s/p/%d#comment-%d", site, post.Id, t.CommentId),
|
Link: u,
|
||||||
Creator: t.CommentAuthor,
|
Creator: t.CommentAuthor,
|
||||||
Description: desc,
|
Description: desc,
|
||||||
PubDate: t.CommentDateGmt.Format(timeFormat),
|
PubDate: t.CommentDateGmt.Format(timeFormat),
|
17
app/pkg/cache/postmeta.go
vendored
Normal file
17
app/pkg/cache/postmeta.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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
Normal file
91
app/pkg/cache/posts.go
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
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
Normal file
26
app/pkg/cache/users.go
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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
|
||||||
|
}
|
147
app/pkg/config/config.go
Normal file
147
app/pkg/config/config.go
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
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"`
|
||||||
|
}
|
43
app/pkg/config/validationtraslaor.go
Normal file
43
app/pkg/config/validationtraslaor.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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
|
||||||
|
}
|
10
app/pkg/constraints/blocks/blocks.go
Normal file
10
app/pkg/constraints/blocks/blocks.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package blocks
|
||||||
|
|
||||||
|
const (
|
||||||
|
Search = "block-search"
|
||||||
|
RecentPosts = "block-recent-posts"
|
||||||
|
RecentComments = "block-recent-comments"
|
||||||
|
Archive = "block-archives"
|
||||||
|
Categories = "block-categories"
|
||||||
|
Meta = "block-meta"
|
||||||
|
)
|
30
app/pkg/constraints/const.go
Normal file
30
app/pkg/constraints/const.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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"
|
||||||
|
)
|
12
app/pkg/constraints/widgets/constraints.go
Normal file
12
app/pkg/constraints/widgets/constraints.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
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"
|
||||||
|
)
|
183
app/pkg/dao/comments.go
Normal file
183
app/pkg/dao/comments.go
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -2,8 +2,10 @@ package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/constraints"
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
"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"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,26 +21,42 @@ type PostContext struct {
|
||||||
Next models.Posts
|
Next models.Posts
|
||||||
}
|
}
|
||||||
|
|
||||||
func CategoriesAndTags(a ...any) (terms []models.TermsMy, err error) {
|
func CategoriesAndTags(ctx context.Context, t string, _ ...any) (terms []models.TermsMy, err error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
var in = []any{"category", "post_tag"}
|
var in = []any{"category", "post_tag"}
|
||||||
terms, err = model.Finds[models.TermsMy](ctx, model.Conditions(
|
switch t {
|
||||||
model.Where(model.SqlBuilder{
|
case constraints.Category:
|
||||||
{"tt.count", ">", "0", "int"},
|
in = []any{"category"}
|
||||||
|
case constraints.Tag:
|
||||||
|
in = []any{"post_tag"}
|
||||||
|
}
|
||||||
|
w := model.SqlBuilder{
|
||||||
{"tt.taxonomy", "in", ""},
|
{"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.Fields("t.term_id"),
|
||||||
model.Order(model.SqlBuilder{{"t.name", "asc"}}),
|
model.Order(model.SqlBuilder{order}),
|
||||||
model.Join(model.SqlBuilder{
|
model.Join(model.SqlBuilder{
|
||||||
{"t", "inner join", "wp_term_taxonomy tt", "t.term_id = tt.term_id"},
|
{"t", "inner join", "wp_term_taxonomy tt", "t.term_id = tt.term_id"},
|
||||||
}),
|
}),
|
||||||
model.In(in),
|
model.In(in),
|
||||||
))
|
))
|
||||||
for i := 0; i < len(terms); i++ {
|
for i := 0; i < len(terms); i++ {
|
||||||
if v, ok := wpconfig.Terms.Load(terms[i].Terms.TermId); ok {
|
if v, ok := wpconfig.GetTerm(terms[i].Terms.TermId); ok {
|
||||||
terms[i].Terms = v
|
terms[i].Terms = v
|
||||||
}
|
}
|
||||||
if v, ok := wpconfig.TermTaxonomies.Load(terms[i].Terms.TermId); ok {
|
if v, ok := wpconfig.GetTermTaxonomy(terms[i].Terms.TermId); ok {
|
||||||
terms[i].TermTaxonomy = v
|
terms[i].TermTaxonomy = v
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,19 +2,17 @@ package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"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/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
"github.com/fthvgb1/wp-go/internal/phphelper"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/logs"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) {
|
func GetPostMetaByPostIds(ctx context.Context, ids []uint64, _ ...any) (r map[uint64]map[string]any, err error) {
|
||||||
r = make(map[uint64]map[string]any)
|
r = make(map[uint64]map[string]any)
|
||||||
ctx := args[0].(context.Context)
|
|
||||||
ids := args[1].([]uint64)
|
|
||||||
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
|
rr, err := model.Finds[models.PostMeta](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
|
model.Where(model.SqlBuilder{{"post_id", "in", ""}}),
|
||||||
model.In(slice.ToAnySlice(ids)),
|
model.In(slice.ToAnySlice(ids)),
|
||||||
|
@ -30,7 +28,7 @@ func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error)
|
||||||
if postmeta.MetaKey == "_wp_attachment_metadata" {
|
if postmeta.MetaKey == "_wp_attachment_metadata" {
|
||||||
metadata, err := phphelper.UnPHPSerializeToStruct[models.WpAttachmentMetadata](postmeta.MetaValue)
|
metadata, err := phphelper.UnPHPSerializeToStruct[models.WpAttachmentMetadata](postmeta.MetaValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.ErrPrintln(err, "解析postmeta失败", postmeta.MetaId, postmeta.MetaValue)
|
logs.Error(err, "解析postmeta失败", postmeta.MetaId, postmeta.MetaValue)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r[postmeta.PostId][postmeta.MetaKey] = metadata
|
r[postmeta.PostId][postmeta.MetaKey] = metadata
|
|
@ -3,30 +3,35 @@ package dao
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models/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/helper/slice"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
func GetPostsByIds(ctx context.Context, ids []uint64, _ ...any) (m map[uint64]models.Posts, err error) {
|
||||||
ctx := a[0].(context.Context)
|
|
||||||
m = make(map[uint64]models.Posts)
|
m = make(map[uint64]models.Posts)
|
||||||
ids := a[1].([]uint64)
|
q := model.Conditions(
|
||||||
rawPosts, err := model.Finds[models.Posts](ctx, model.Conditions(
|
|
||||||
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
||||||
model.Join(model.SqlBuilder{
|
model.Join(model.SqlBuilder{
|
||||||
{"a", "left join", "wp_term_relationships b", "a.Id=b.object_id"},
|
{"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_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id"},
|
||||||
{"left join", "wp_terms d", "c.term_id=d.term_id"},
|
{"left join", "wp_terms d", "c.term_id=d.term_id"},
|
||||||
}),
|
}),
|
||||||
model.Fields("a.*,ifnull(d.name,'') category_name,ifnull(taxonomy,'') `taxonomy`"),
|
model.Fields("a.*,ifnull(d.name,'') category_name,ifnull(c.term_id,0) terms_id,ifnull(taxonomy,'') `taxonomy`"),
|
||||||
model.In(slice.ToAnySlice(ids)),
|
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 {
|
if err != nil {
|
||||||
return m, err
|
return m, err
|
||||||
|
@ -42,6 +47,9 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
||||||
} else if post.Taxonomy == "post_tag" {
|
} else if post.Taxonomy == "post_tag" {
|
||||||
v.Tags = append(v.Tags, post.CategoryName)
|
v.Tags = append(v.Tags, post.CategoryName)
|
||||||
}
|
}
|
||||||
|
if post.TermsId > 0 {
|
||||||
|
v.TermIds = append(v.TermIds, post.TermsId)
|
||||||
|
}
|
||||||
postsMap[post.Id] = v
|
postsMap[post.Id] = v
|
||||||
}
|
}
|
||||||
//host, _ := wpconfig.Options.Load("siteurl")
|
//host, _ := wpconfig.Options.Load("siteurl")
|
||||||
|
@ -57,6 +65,7 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
||||||
}
|
}
|
||||||
mm, ok := meta[pp.Id]
|
mm, ok := meta[pp.Id]
|
||||||
if ok {
|
if ok {
|
||||||
|
pp.Metas = mm
|
||||||
attMeta, ok := mm["_wp_attachment_metadata"]
|
attMeta, ok := mm["_wp_attachment_metadata"]
|
||||||
if ok {
|
if ok {
|
||||||
att, ok := attMeta.(models.WpAttachmentMetadata)
|
att, ok := attMeta.(models.WpAttachmentMetadata)
|
||||||
|
@ -89,20 +98,12 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchPostIds(args ...any) (ids PostIds, err error) {
|
func SearchPostIds(ctx context.Context, _ string, args ...any) (ids PostIds, err error) {
|
||||||
ctx := args[0].(context.Context)
|
q := args[0].(*model.QueryCondition)
|
||||||
where := args[1].(model.SqlBuilder)
|
page := args[1].(int)
|
||||||
page := args[2].(int)
|
pageSize := args[2].(int)
|
||||||
limit := args[3].(int)
|
q.Fields = "ID"
|
||||||
order := args[4].(model.SqlBuilder)
|
res, total, err := model.Pagination[models.Posts](ctx, q, page, pageSize)
|
||||||
join := args[5].(model.SqlBuilder)
|
|
||||||
postType := args[6].([]any)
|
|
||||||
postStatus := args[7].([]any)
|
|
||||||
res, total, err := model.SimplePagination[models.Posts](
|
|
||||||
ctx, where, "ID",
|
|
||||||
"", page, limit, order,
|
|
||||||
join, nil, postType, postStatus,
|
|
||||||
)
|
|
||||||
for _, posts := range res {
|
for _, posts := range res {
|
||||||
ids.Ids = append(ids.Ids, posts.Id)
|
ids.Ids = append(ids.Ids, posts.Id)
|
||||||
}
|
}
|
||||||
|
@ -115,11 +116,19 @@ func SearchPostIds(args ...any) (ids PostIds, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxPostId(a ...any) (uint64, error) {
|
func GetMaxPostId(ctx context.Context, _ ...any) (uint64, error) {
|
||||||
ctx := a[0].(context.Context)
|
r, err := model.Finds[models.Posts](ctx,
|
||||||
r, err := model.SimpleFind[models.Posts](ctx,
|
model.Conditions(
|
||||||
model.SqlBuilder{{"post_type", "post"}, {"post_status", "publish"}},
|
model.Where(model.SqlBuilder{
|
||||||
"max(ID) ID",
|
{"post_type", "post"},
|
||||||
|
{"post_status", "publish"}},
|
||||||
|
),
|
||||||
|
model.Fields("ID"),
|
||||||
|
model.Order(model.SqlBuilder{
|
||||||
|
{"ID", "desc"},
|
||||||
|
}),
|
||||||
|
model.Limit(1),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
var id uint64
|
var id uint64
|
||||||
if len(r) > 0 {
|
if len(r) > 0 {
|
||||||
|
@ -128,30 +137,28 @@ func GetMaxPostId(a ...any) (uint64, error) {
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecentPosts(a ...any) (r []models.Posts, err error) {
|
func RecentPosts(ctx context.Context, a ...any) (r []models.Posts, err error) {
|
||||||
ctx := a[0].(context.Context)
|
num := helper.ParseArgs(10, a...)
|
||||||
num := a[1].(int)
|
|
||||||
r, err = model.Finds[models.Posts](ctx, model.Conditions(
|
r, err = model.Finds[models.Posts](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{
|
model.Where(model.SqlBuilder{
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
{"post_status", "publish"},
|
{"post_status", "publish"},
|
||||||
}),
|
}),
|
||||||
model.Fields("ID,post_title,post_password"),
|
model.Fields("ID,post_title,post_password,post_date_gmt"),
|
||||||
model.Order(model.SqlBuilder{{"post_date", "desc"}}),
|
model.Order([][]string{{"post_date", "desc"}}),
|
||||||
model.Limit(num),
|
model.Limit(num),
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPostContext(arg ...any) (r PostContext, err error) {
|
func GetPostContext(ctx context.Context, _ uint64, arg ...any) (r PostContext, err error) {
|
||||||
ctx := arg[0].(context.Context)
|
t := arg[0].(time.Time)
|
||||||
t := arg[1].(time.Time)
|
|
||||||
next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{
|
next, err := model.FirstOne[models.Posts](ctx, model.SqlBuilder{
|
||||||
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
{"post_date", ">", t.Format("2006-01-02 15:04:05")},
|
||||||
{"post_status", "in", ""},
|
{"post_status", "in", ""},
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
}, "ID,post_title,post_password", nil, []any{"publish"})
|
}, "ID,post_title,post_password", nil, []any{"publish"})
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -162,7 +169,7 @@ func GetPostContext(arg ...any) (r PostContext, err error) {
|
||||||
{"post_status", "in", ""},
|
{"post_status", "in", ""},
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
}, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
}, "ID,post_title", model.SqlBuilder{{"post_date", "desc"}}, []any{"publish"})
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,19 +182,23 @@ func GetPostContext(arg ...any) (r PostContext, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonthPost(args ...any) (r []uint64, err error) {
|
func MonthPost(ctx context.Context, _ string, args ...any) (r []uint64, err error) {
|
||||||
ctx := args[0].(context.Context)
|
year, month := args[0].(string), args[1].(string)
|
||||||
year, month := args[1].(string), args[2].(string)
|
|
||||||
where := model.SqlBuilder{
|
where := model.SqlBuilder{
|
||||||
{"post_type", "post"},
|
{"post_type", "post"},
|
||||||
{"post_status", "publish"},
|
{"post_status", "publish"},
|
||||||
{"year(post_date)", year},
|
{"year(post_date)", year},
|
||||||
{"month(post_date)", month},
|
{"month(post_date)", month},
|
||||||
}
|
}
|
||||||
return model.Column[models.Posts, uint64](ctx, func(v models.Posts) (uint64, bool) {
|
r, err = model.Column[models.Posts, uint64](ctx, func(v models.Posts) (uint64, bool) {
|
||||||
return v.Id, true
|
return v.Id, true
|
||||||
}, model.Conditions(
|
}, model.Conditions(
|
||||||
model.Fields("ID"),
|
model.Fields("ID"),
|
||||||
model.Where(where),
|
model.Where(where),
|
||||||
))
|
))
|
||||||
|
l := int64(len(r))
|
||||||
|
if l > atomic.LoadInt64(&TotalRaw) {
|
||||||
|
atomic.StoreInt64(&TotalRaw, l)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
32
app/pkg/dao/users.go
Normal file
32
app/pkg/dao/users.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -2,23 +2,31 @@ package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/config"
|
"github.com/fthvgb1/wp-go/app/pkg/config"
|
||||||
|
"github.com/fthvgb1/wp-go/cache/reload"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
|
"github.com/fthvgb1/wp-go/safety"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"log"
|
"log"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var db *sqlx.DB
|
var safeDb = safety.NewVar[*sqlx.DB](nil)
|
||||||
|
var showQuerySql func() bool
|
||||||
|
|
||||||
func InitDb() (*sqlx.DB, error) {
|
func GetSqlxDB() *sqlx.DB {
|
||||||
|
return safeDb.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDb() (*safety.Var[*sqlx.DB], error) {
|
||||||
c := config.GetConfig()
|
c := config.GetConfig()
|
||||||
dsn := c.Mysql.Dsn.GetDsn()
|
dsn := c.Mysql.Dsn.GetDsn()
|
||||||
var err error
|
db, err := sqlx.Open("mysql", dsn)
|
||||||
db, err = sqlx.Open("mysql", dsn)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
preDb := safeDb.Load()
|
||||||
if c.Mysql.Pool.ConnMaxIdleTime != 0 {
|
if c.Mysql.Pool.ConnMaxIdleTime != 0 {
|
||||||
db.SetConnMaxIdleTime(c.Mysql.Pool.ConnMaxLifetime)
|
db.SetConnMaxIdleTime(c.Mysql.Pool.ConnMaxLifetime)
|
||||||
}
|
}
|
||||||
|
@ -31,23 +39,37 @@ func InitDb() (*sqlx.DB, error) {
|
||||||
if c.Mysql.Pool.ConnMaxLifetime != 0 {
|
if c.Mysql.Pool.ConnMaxLifetime != 0 {
|
||||||
db.SetConnMaxLifetime(c.Mysql.Pool.ConnMaxLifetime)
|
db.SetConnMaxLifetime(c.Mysql.Pool.ConnMaxLifetime)
|
||||||
}
|
}
|
||||||
return db, err
|
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 *sqlx.DB) *model.SqlxQuery {
|
func QueryDb(db *safety.Var[*sqlx.DB]) *model.SqlxQuery {
|
||||||
query := model.NewSqlxQuery(db, model.NewUniversalDb(
|
query := model.NewSqlxQuery(db, model.NewUniversalDb(
|
||||||
nil,
|
nil,
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
|
model.SetSelect(query, func(ctx context.Context, a any, s string, args ...any) error {
|
||||||
if config.GetConfig().ShowQuerySql {
|
if showQuerySql() {
|
||||||
go log.Println(model.FormatSql(s, args...))
|
_, 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...)
|
return query.Selects(ctx, a, s, args...)
|
||||||
})
|
})
|
||||||
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
|
model.SetGet(query, func(ctx context.Context, a any, s string, args ...any) error {
|
||||||
if config.GetConfig().ShowQuerySql {
|
if showQuerySql() {
|
||||||
go log.Println(model.FormatSql(s, args...))
|
_, 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.Gets(ctx, a, s, args...)
|
||||||
})
|
})
|
76
app/pkg/logs/log.go
Normal file
76
app/pkg/logs/log.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
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...)
|
||||||
|
}
|
19
app/pkg/models/relation/posts.go
Normal file
19
app/pkg/models/relation/posts.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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",
|
||||||
|
})
|
|
@ -20,6 +20,7 @@ type Comments struct {
|
||||||
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
|
UserId uint64 `gorm:"column:user_id" db:"user_id" json:"user_id" form:"user_id"`
|
||||||
//扩展字段
|
//扩展字段
|
||||||
PostTitle string `db:"post_title"`
|
PostTitle string `db:"post_title"`
|
||||||
|
UpdateTime time.Time `gorm:"update_time" form:"update_time" json:"update_time" db:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Comments) PrimaryKey() string {
|
func (w Comments) PrimaryKey() string {
|
||||||
|
@ -29,3 +30,8 @@ func (w Comments) PrimaryKey() string {
|
||||||
func (w Comments) Table() string {
|
func (w Comments) Table() string {
|
||||||
return "wp_comments"
|
return "wp_comments"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PostComments struct {
|
||||||
|
Comments
|
||||||
|
Children []uint64
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ type WpAttachmentMetadata struct {
|
||||||
FileSize int `json:"filesize,omitempty"`
|
FileSize int `json:"filesize,omitempty"`
|
||||||
Sizes map[string]MetaDataFileSize `json:"sizes,omitempty"`
|
Sizes map[string]MetaDataFileSize `json:"sizes,omitempty"`
|
||||||
ImageMeta ImageMeta `json:"image_meta"`
|
ImageMeta ImageMeta `json:"image_meta"`
|
||||||
|
VideoMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageMeta struct {
|
type ImageMeta struct {
|
||||||
|
@ -39,6 +40,16 @@ type ImageMeta struct {
|
||||||
Keywords []string `json:"keywords,omitempty"`
|
Keywords []string `json:"keywords,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VideoMeta struct {
|
||||||
|
Bitrate int `json:"bitrate,omitempty"`
|
||||||
|
MimeType string `json:"mime_type,omitempty"`
|
||||||
|
Length int `json:"length,omitempty"`
|
||||||
|
LengthFormatted string `json:"length_formatted,omitempty"`
|
||||||
|
FileFormat string `json:"fileformat,omitempty"`
|
||||||
|
DataFormat string `json:"dataformat,omitempty"`
|
||||||
|
CreatedTimestamp int64 `json:"created_timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
type MetaDataFileSize struct {
|
type MetaDataFileSize struct {
|
||||||
File string `json:"file,omitempty"`
|
File string `json:"file,omitempty"`
|
||||||
Width int `json:"width,omitempty"`
|
Width int `json:"width,omitempty"`
|
|
@ -29,14 +29,19 @@ type Posts struct {
|
||||||
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
|
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
|
||||||
|
|
||||||
//扩展字段
|
//扩展字段
|
||||||
|
TermsId uint64 `db:"terms_id" json:"terms_id"`
|
||||||
|
TermIds []uint64 `db:"term_ids" json:"term_ids"`
|
||||||
Taxonomy string `db:"taxonomy" json:"taxonomy"`
|
Taxonomy string `db:"taxonomy" json:"taxonomy"`
|
||||||
CategoryName string `db:"category_name" json:"category_name"`
|
CategoryName string `db:"category_name" json:"category_name"`
|
||||||
Categories []string `json:"categories"`
|
Categories []string `json:"categories"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
CategoriesHtml string
|
CategoriesHtml string
|
||||||
TagsHtml string
|
TagsHtml string
|
||||||
|
IsSticky bool
|
||||||
Thumbnail PostThumbnail
|
Thumbnail PostThumbnail
|
||||||
AttachmentMetadata WpAttachmentMetadata
|
AttachmentMetadata WpAttachmentMetadata
|
||||||
|
Metas map[string]any
|
||||||
|
Author *Users
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostThumbnail struct {
|
type PostThumbnail struct {
|
|
@ -1,11 +1,13 @@
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -17,6 +19,7 @@ type CommentHandler struct {
|
||||||
depth int
|
depth int
|
||||||
isTls bool
|
isTls bool
|
||||||
i CommentHtml
|
i CommentHtml
|
||||||
|
isThreadComments bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Comments struct {
|
type Comments struct {
|
||||||
|
@ -25,19 +28,18 @@ type Comments struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommentHtml interface {
|
type CommentHtml interface {
|
||||||
Sort(i, j *Comments) bool
|
FormatLi(c context.Context, m models.Comments, depth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string
|
||||||
FormatLi(c *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string
|
FloorOrder(i, j models.Comments) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
|
func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, maxDepth int) string {
|
||||||
tree := treeComments(comments)
|
tree := treeComments(comments)
|
||||||
u := c.Request.Header.Get("Referer")
|
|
||||||
var isTls bool
|
var isTls bool
|
||||||
if u != "" {
|
if c.Request.TLS != nil {
|
||||||
uu, _ := url.Parse(u)
|
|
||||||
if uu.Scheme == "https" {
|
|
||||||
isTls = true
|
isTls = true
|
||||||
}
|
} else {
|
||||||
|
isTls = "https" == strings.ToLower(c.Request.Header.Get("X-Forwarded-Proto"))
|
||||||
}
|
}
|
||||||
h := CommentHandler{
|
h := CommentHandler{
|
||||||
Context: c,
|
Context: c,
|
||||||
|
@ -47,15 +49,21 @@ func FormatComments(c *gin.Context, i CommentHtml, comments []models.Comments, m
|
||||||
isTls: isTls,
|
isTls: isTls,
|
||||||
i: i,
|
i: i,
|
||||||
}
|
}
|
||||||
return h.formatComment(h.comments, true)
|
return h.formatComment(h.comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html string) {
|
func (d CommentHandler) formatComment(comments []*Comments) (html string) {
|
||||||
s := str.NewBuilder()
|
s := str.NewBuilder()
|
||||||
if d.depth > d.maxDepth {
|
if d.depth >= d.maxDepth {
|
||||||
comments = d.findComments(comments)
|
comments = d.findComments(comments)
|
||||||
}
|
}
|
||||||
slice.SortSelf(comments, d.i.Sort)
|
order := wpconfig.GetOption("comment_order")
|
||||||
|
slice.Sort(comments, func(i, j *Comments) bool {
|
||||||
|
if order == "asc" {
|
||||||
|
return i.CommentDate.Sub(j.CommentDate) < 0
|
||||||
|
}
|
||||||
|
return i.CommentDate.Sub(j.CommentDate) > 0
|
||||||
|
})
|
||||||
for i, comment := range comments {
|
for i, comment := range comments {
|
||||||
eo := "even"
|
eo := "even"
|
||||||
if (i+1)%2 == 0 {
|
if (i+1)%2 == 0 {
|
||||||
|
@ -67,13 +75,11 @@ func (d CommentHandler) formatComment(comments []*Comments, isTop bool) (html st
|
||||||
parent = "parent"
|
parent = "parent"
|
||||||
fl = true
|
fl = true
|
||||||
}
|
}
|
||||||
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.isTls, eo, parent))
|
s.WriteString(d.i.FormatLi(d.Context, comment.Comments, d.depth, d.maxDepth, 1, d.isTls, d.isThreadComments, eo, parent))
|
||||||
if fl {
|
if fl {
|
||||||
d.depth++
|
d.depth++
|
||||||
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children, false), `</ol>`)
|
s.WriteString(`<ol class="children">`, d.formatComment(comment.Children), `</ol>`)
|
||||||
if isTop {
|
d.depth--
|
||||||
d.depth = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s.WriteString("</li><!-- #comment-## -->")
|
s.WriteString("</li><!-- #comment-## -->")
|
||||||
}
|
}
|
||||||
|
@ -86,7 +92,7 @@ func (d CommentHandler) findComments(comments []*Comments) []*Comments {
|
||||||
var r []*Comments
|
var r []*Comments
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
tmp := *comment
|
tmp := *comment
|
||||||
comment.Children = nil
|
tmp.Children = nil
|
||||||
r = append(r, &tmp)
|
r = append(r, &tmp)
|
||||||
if len(comment.Children) > 0 {
|
if len(comment.Children) > 0 {
|
||||||
t := d.findComments(comment.Children)
|
t := d.findComments(comment.Children)
|
||||||
|
@ -136,12 +142,35 @@ func CommentRender() CommonCommentFormat {
|
||||||
type CommonCommentFormat struct {
|
type CommonCommentFormat struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CommonCommentFormat) Sort(i, j *Comments) bool {
|
func (c CommonCommentFormat) FormatLi(_ context.Context, m models.Comments, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
|
||||||
return i.CommentDate.UnixNano() < j.CommentDate.UnixNano()
|
return FormatLi(li, m, respondsFn, currentDepth, maxDepth, page, isTls, isThreadComments, eo, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CommonCommentFormat) FormatLi(ctx *gin.Context, m models.Comments, depth int, isTls bool, eo, parent string) string {
|
func (c CommonCommentFormat) FloorOrder(i, j models.Comments) bool {
|
||||||
return FormatLi(CommonLi(), ctx, m, depth, isTls, eo, parent)
|
return i.CommentId > j.CommentId
|
||||||
|
}
|
||||||
|
|
||||||
|
type RespondFn func(m models.Comments, depth, maxDepth int, isThreadComments bool) string
|
||||||
|
|
||||||
|
var respondsFn = Responds(respondTml)
|
||||||
|
|
||||||
|
func RespondsFn() RespondFn {
|
||||||
|
return respondsFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func Responds(respondTml string) RespondFn {
|
||||||
|
return func(m models.Comments, depth, maxDepth int, isThreadComments bool) string {
|
||||||
|
if !isThreadComments || depth >= maxDepth {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
pId := number.IntToString(m.CommentPostId)
|
||||||
|
cId := number.IntToString(m.CommentId)
|
||||||
|
return str.Replace(respondTml, map[string]string{
|
||||||
|
"{{PostId}}": pId,
|
||||||
|
"{{CommentId}}": cId,
|
||||||
|
"{{CommentAuthor}}": m.CommentAuthor,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var li = `
|
var li = `
|
||||||
|
@ -153,14 +182,11 @@ var li = `
|
||||||
src="{{Gravatar}}"
|
src="{{Gravatar}}"
|
||||||
srcset="{{Gravatar}} 2x"
|
srcset="{{Gravatar}} 2x"
|
||||||
class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
|
class="avatar avatar-56 photo" height="56" width="56" loading="lazy">
|
||||||
<b class="fn">
|
<b class="fn">{{CommentAuthor}}</b>
|
||||||
<a href="{{CommentAuthorUrl}}" rel="external nofollow ugc"
|
|
||||||
class="url">{{CommentAuthor}}</a>
|
|
||||||
</b>
|
|
||||||
<span class="says">说道:</span></div><!-- .comment-author -->
|
<span class="says">说道:</span></div><!-- .comment-author -->
|
||||||
|
|
||||||
<div class="comment-metadata">
|
<div class="comment-metadata">
|
||||||
<a href="/p/{{PostId}}#comment-{{CommentId}}">
|
<a href="/p/{{PostId}}/comment-page-{{page}}#comment-{{CommentId}}">
|
||||||
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
|
<time datetime="{{CommentDateGmt}}">{{CommentDate}}</time>
|
||||||
</a></div><!-- .comment-metadata -->
|
</a></div><!-- .comment-metadata -->
|
||||||
|
|
||||||
|
@ -170,30 +196,34 @@ var li = `
|
||||||
<p>{{CommentContent}}</p>
|
<p>{{CommentContent}}</p>
|
||||||
</div><!-- .comment-content -->
|
</div><!-- .comment-content -->
|
||||||
|
|
||||||
<div class="reply">
|
{{respond}}
|
||||||
|
</article><!-- .comment-body -->
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
var respondTml = `<div class="reply">
|
||||||
<a rel="nofollow" class="comment-reply-link"
|
<a rel="nofollow" class="comment-reply-link"
|
||||||
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
href="/p/{{PostId}}?replytocom={{CommentId}}#respond" data-commentid="{{CommentId}}" data-postid="{{PostId}}"
|
||||||
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
data-belowelement="div-comment-{{CommentId}}" data-respondelement="respond"
|
||||||
data-replyto="回复给{{CommentAuthor}}"
|
data-replyto="回复给{{CommentAuthor}}"
|
||||||
aria-label="回复给{{CommentAuthor}}">回复</a>
|
aria-label="回复给{{CommentAuthor}}">回复</a>
|
||||||
</div>
|
</div>`
|
||||||
</article><!-- .comment-body -->
|
|
||||||
|
|
||||||
`
|
func FormatLi(li string, comments models.Comments, respond RespondFn, currentDepth, maxDepth, page int, isTls, isThreadComments bool, eo, parent string) string {
|
||||||
|
|
||||||
func FormatLi(li string, c *gin.Context, comments models.Comments, depth int, isTls bool, eo, parent string) string {
|
|
||||||
for k, v := range map[string]string{
|
for k, v := range map[string]string{
|
||||||
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
|
"{{CommentId}}": strconv.FormatUint(comments.CommentId, 10),
|
||||||
"{{Depth}}": strconv.Itoa(depth),
|
"{{Depth}}": strconv.Itoa(currentDepth),
|
||||||
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
|
"{{Gravatar}}": Gravatar(comments.CommentAuthorEmail, isTls),
|
||||||
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
|
"{{CommentAuthorUrl}}": comments.CommentAuthorUrl,
|
||||||
"{{CommentAuthor}}": comments.CommentAuthor,
|
"{{CommentAuthor}}": comments.CommentAuthor,
|
||||||
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
|
"{{PostId}}": strconv.FormatUint(comments.CommentPostId, 10),
|
||||||
|
"{{page}}": strconv.Itoa(page),
|
||||||
"{{CommentDateGmt}}": comments.CommentDateGmt.String(),
|
"{{CommentDateGmt}}": comments.CommentDateGmt.String(),
|
||||||
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
|
"{{CommentDate}}": comments.CommentDate.Format("2006-01-02 15:04"),
|
||||||
"{{CommentContent}}": comments.CommentContent,
|
"{{CommentContent}}": comments.CommentContent,
|
||||||
"{{eo}}": eo,
|
"{{eo}}": eo,
|
||||||
"{{parent}}": parent,
|
"{{parent}}": parent,
|
||||||
|
"{{respond}}": respond(comments, currentDepth, maxDepth, isThreadComments),
|
||||||
} {
|
} {
|
||||||
li = strings.Replace(li, k, v, -1)
|
li = strings.Replace(li, k, v, -1)
|
||||||
}
|
}
|
9
app/plugins/devexample/plugintt/a.gohtml
Normal file
9
app/plugins/devexample/plugintt/a.gohtml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<div>
|
||||||
|
{{.aa}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ range $k,$v := .posts}}
|
||||||
|
<h2>{{$v.PostTitle}} </h2>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
81
app/plugins/devexample/plugintt/main.go.dev
Normal file
81
app/plugins/devexample/plugintt/main.go.dev
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
}
|
7
app/plugins/devexample/plugintt/make.sh
Normal file
7
app/plugins/devexample/plugintt/make.sh
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#/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
|
141
app/plugins/devexample/plugintt/redisCache.go.dev
Normal file
141
app/plugins/devexample/plugintt/redisCache.go.dev
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
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")
|
||||||
|
}
|
50
app/plugins/devexample/plugintt/redisCache.go.mod.dev
Normal file
50
app/plugins/devexample/plugintt/redisCache.go.mod.dev
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
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
|
||||||
|
)
|
11
app/plugins/devexample/plugintt/xx/oo.go.dev
Normal file
11
app/plugins/devexample/plugintt/xx/oo.go.dev
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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())
|
||||||
|
}
|
187
app/plugins/digest.go
Normal file
187
app/plugins/digest.go
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -2,9 +2,9 @@ package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
"github.com/fthvgb1/wp-go/helper/number"
|
"github.com/fthvgb1/wp-go/helper/number"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/wpconfig"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
176
app/plugins/pagination.go
Normal file
176
app/plugins/pagination.go
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
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)
|
||||||
|
}
|
30
app/plugins/wphandle/apply/apply.go
Normal file
30
app/plugins/wphandle/apply/apply.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
||||||
|
}
|
83
app/plugins/wphandle/enlightjs/enlighterjs.go
Normal file
83
app/plugins/wphandle/enlightjs/enlighterjs.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
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>`
|
72
app/plugins/wphandle/handle.go
Normal file
72
app/plugins/wphandle/handle.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
17
app/plugins/wphandle/hiddenlogin/hiddenlogin.go
Normal file
17
app/plugins/wphandle/hiddenlogin/hiddenlogin.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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>`: "",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
22
app/plugins/wphandle/tests/tt.go
Normal file
22
app/plugins/wphandle/tests/tt.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
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,8 +1,8 @@
|
||||||
package plugins
|
package wpposts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PasswordProjectTitle(post *models.Posts) {
|
func PasswordProjectTitle(post *models.Posts) {
|
147
app/route/route.go
Normal file
147
app/route/route.go
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
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
|
||||||
|
}
|
82
app/theme/fs.go
Normal file
82
app/theme/fs.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
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)
|
||||||
|
}
|
35
app/theme/hook.go
Normal file
35
app/theme/hook.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
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"))
|
||||||
|
}
|
37
app/theme/templateFuncs.go
Normal file
37
app/theme/templateFuncs.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
24
app/theme/theme.go
Normal file
24
app/theme/theme.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -2,13 +2,14 @@ package twentyfifteen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *handle) colorSchemeCss() string {
|
func colorSchemeCss(h *wp.Handle) string {
|
||||||
s := slice.Filter([]string{h.calColorSchemeCss(), h.calSidebarTextColorCss(), h.calHeaderBackgroundColorCss()}, func(s string) bool {
|
s := slice.Filter([]string{calColorSchemeCss(h), calSidebarTextColorCss(h), calHeaderBackgroundColorCss(h)}, func(s string, i int) bool {
|
||||||
return s != ""
|
return s != ""
|
||||||
})
|
})
|
||||||
if len(s) < 1 {
|
if len(s) < 1 {
|
||||||
|
@ -16,9 +17,9 @@ func (h *handle) colorSchemeCss() string {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`<style id='%s-inline-css'%s>\n%s\n</style>`, "twentyfifteen-style", "", strings.Join(s, "\n"))
|
return fmt.Sprintf(`<style id='%s-inline-css'%s>\n%s\n</style>`, "twentyfifteen-style", "", strings.Join(s, "\n"))
|
||||||
}
|
}
|
||||||
func (h *handle) calColorSchemeCss() (r string) {
|
func calColorSchemeCss(h *wp.Handle) (r string) {
|
||||||
color := h.getColorScheme()
|
color := getColorScheme(h)
|
||||||
if "default" == h.IndexHandle.ThemeMods.ColorScheme || len(color) < 1 {
|
if "default" == h.CommonThemeMods().ColorScheme || len(color) < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
textColorRgb := slice.ToAnySlice(Hex2RgbUint8(color[3]))
|
textColorRgb := slice.ToAnySlice(Hex2RgbUint8(color[3]))
|
||||||
|
@ -45,31 +46,33 @@ func (h *handle) calColorSchemeCss() (r string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handle) calSidebarTextColorCss() (r string) {
|
func calSidebarTextColorCss(h *wp.Handle) (r string) {
|
||||||
colors := h.getColorScheme()
|
colors := getColorScheme(h)
|
||||||
if h.IndexHandle.ThemeMods.SidebarTextcolor == "" || h.IndexHandle.ThemeMods.SidebarTextcolor == colors[4] {
|
themeMods := h.CommonThemeMods()
|
||||||
|
if themeMods.SidebarTextcolor == "" || themeMods.SidebarTextcolor == colors[4] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
linkColorRgb := Hex2RgbUint8(h.IndexHandle.ThemeMods.SidebarTextcolor)
|
linkColorRgb := Hex2RgbUint8(themeMods.SidebarTextcolor)
|
||||||
color := slice.ToAnySlice(linkColorRgb)
|
color := slice.ToAnySlice(linkColorRgb)
|
||||||
textColor := fmt.Sprintf(`rgba( %[1]v, %[2]v, %[3]v, 0.7)`, color...)
|
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...)
|
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...)
|
borderFocusColor := fmt.Sprintf(`rgba( %[1]v, %[2]v, %[3]v, 0.3)`, color...)
|
||||||
r = fmt.Sprintf(sidebarTextColorTemplate, h.IndexHandle.ThemeMods.SidebarTextcolor, textColor, borderColor, borderFocusColor)
|
r = fmt.Sprintf(sidebarTextColorTemplate, themeMods.SidebarTextcolor, textColor, borderColor, borderFocusColor)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handle) calHeaderBackgroundColorCss() (r string) {
|
func calHeaderBackgroundColorCss(h *wp.Handle) (r string) {
|
||||||
colors := h.getColorScheme()
|
colors := getColorScheme(h)
|
||||||
if h.IndexHandle.ThemeMods.HeaderBackgroundColor == "" || h.IndexHandle.ThemeMods.HeaderBackgroundColor == colors[1] {
|
themeMods := h.CommonThemeMods()
|
||||||
|
if themeMods.HeaderBackgroundColor == "" || themeMods.HeaderBackgroundColor == colors[1] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r = fmt.Sprintf(headerBackgroundColorCssTemplate, h.IndexHandle.ThemeMods.HeaderBackgroundColor, h.IndexHandle.ThemeMods.HeaderBackgroundColor)
|
r = fmt.Sprintf(headerBackgroundColorCssTemplate, themeMods.HeaderBackgroundColor, themeMods.HeaderBackgroundColor)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handle) getColorScheme() (r []string) {
|
func getColorScheme(h *wp.Handle) (r []string) {
|
||||||
x, ok := colorscheme[h.IndexHandle.ThemeMods.ColorScheme]
|
x, ok := colorscheme[h.CommonThemeMods().ColorScheme]
|
||||||
if ok {
|
if ok {
|
||||||
r = x.Colors
|
r = x.Colors
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package twentyfifteen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fthvgb1/wp-go/app/theme/wp"
|
||||||
"github.com/fthvgb1/wp-go/helper"
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"github.com/fthvgb1/wp-go/helper/maps"
|
"github.com/fthvgb1/wp-go/helper/maps"
|
||||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
|
@ -29,30 +30,31 @@ var repeat = map[string]string{
|
||||||
"no-repeat": "no-repeat",
|
"no-repeat": "no-repeat",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handle) CalCustomBackGround() (r string) {
|
func CalCustomBackGround(h *wp.Handle) (r string) {
|
||||||
if h.IndexHandle.ThemeMods.BackgroundImage == "" && h.IndexHandle.ThemeMods.BackgroundColor == themesupport.CustomBackground.DefaultColor {
|
themeMods := h.CommonThemeMods()
|
||||||
|
if themeMods.BackgroundImage == "" && (themeMods.BackgroundColor == "" || themeMods.BackgroundColor == themesupport.CustomBackground.DefaultColor) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s := str.NewBuilder()
|
s := str.NewBuilder()
|
||||||
if h.IndexHandle.ThemeMods.BackgroundImage != "" {
|
if themeMods.BackgroundImage != "" {
|
||||||
s.Sprintf(` background-image: url("%s");`, helper.CutUrlHost(h.IndexHandle.ThemeMods.BackgroundImage))
|
s.Sprintf(` background-image: url("%s");`, helper.CutUrlHost(themeMods.BackgroundImage))
|
||||||
}
|
}
|
||||||
backgroundPositionX := helper.Defaults(h.IndexHandle.ThemeMods.BackgroundPositionX, themesupport.CustomBackground.DefaultPositionX)
|
backgroundPositionX := helper.Defaults(themeMods.BackgroundPositionX, themesupport.CustomBackground.DefaultPositionX)
|
||||||
backgroundPositionX = maps.WithDefaultVal(postx, backgroundPositionX, "left")
|
backgroundPositionX = maps.WithDefaultVal(postx, backgroundPositionX, "left")
|
||||||
|
|
||||||
backgroundPositionY := helper.Defaults(h.IndexHandle.ThemeMods.BackgroundPositionY, themesupport.CustomBackground.DefaultPositionY)
|
backgroundPositionY := helper.Defaults(themeMods.BackgroundPositionY, themesupport.CustomBackground.DefaultPositionY)
|
||||||
backgroundPositionY = maps.WithDefaultVal(posty, backgroundPositionY, "top")
|
backgroundPositionY = maps.WithDefaultVal(posty, backgroundPositionY, "top")
|
||||||
positon := fmt.Sprintf(" background-position: %s %s;", backgroundPositionX, backgroundPositionY)
|
positon := fmt.Sprintf(" background-position: %s %s;", backgroundPositionX, backgroundPositionY)
|
||||||
|
|
||||||
siz := helper.DefaultVal(h.IndexHandle.ThemeMods.BackgroundSize, themesupport.CustomBackground.DefaultSize)
|
siz := helper.DefaultVal(themeMods.BackgroundSize, themesupport.CustomBackground.DefaultSize)
|
||||||
siz = maps.WithDefaultVal(size, siz, "auto")
|
siz = maps.WithDefaultVal(size, siz, "auto")
|
||||||
siz = fmt.Sprintf(" background-size: %s;", siz)
|
siz = fmt.Sprintf(" background-size: %s;", siz)
|
||||||
|
|
||||||
repeats := helper.Defaults(h.IndexHandle.ThemeMods.BackgroundRepeat, themesupport.CustomBackground.DefaultRepeat)
|
repeats := helper.Defaults(themeMods.BackgroundRepeat, themesupport.CustomBackground.DefaultRepeat)
|
||||||
repeats = maps.WithDefaultVal(repeat, repeats, "repeat")
|
repeats = maps.WithDefaultVal(repeat, repeats, "repeat")
|
||||||
repeats = fmt.Sprintf(" background-repeat: %s;", repeats)
|
repeats = fmt.Sprintf(" background-repeat: %s;", repeats)
|
||||||
|
|
||||||
attachment := helper.Defaults(h.IndexHandle.ThemeMods.BackgroundAttachment, themesupport.CustomBackground.DefaultAttachment)
|
attachment := helper.Defaults(themeMods.BackgroundAttachment, themesupport.CustomBackground.DefaultAttachment)
|
||||||
attachment = helper.Or(attachment == "fixed", "fixed", "scroll")
|
attachment = helper.Or(attachment == "fixed", "fixed", "scroll")
|
||||||
attachment = fmt.Sprintf(" background-attachment: %s;", attachment)
|
attachment = fmt.Sprintf(" background-attachment: %s;", attachment)
|
||||||
s.WriteString(positon, siz, repeats, attachment)
|
s.WriteString(positon, siz, repeats, attachment)
|
|
@ -1,9 +1,10 @@
|
||||||
package twentyfifteen
|
package twentyfifteen
|
||||||
|
|
||||||
import (
|
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"
|
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||||
"github.com/fthvgb1/wp-go/internal/cmd/reload"
|
|
||||||
"github.com/fthvgb1/wp-go/internal/pkg/constraints"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var style = `<style type="text/css" id="twentyfifteen-header-css">`
|
var style = `<style type="text/css" id="twentyfifteen-header-css">`
|
||||||
|
@ -80,22 +81,22 @@ var imgStyle = `.site-header {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var header = reload.Vars(constraints.Defaults)
|
var header = reload.Vars(constraints.Defaults, "twentyfifteen-customheader")
|
||||||
|
|
||||||
func (h *handle) CalCustomHeader() (r string, rand bool) {
|
func calCustomHeaderImg(h *wp.Handle) (r string, rand bool) {
|
||||||
img, rand := h.IndexHandle.GetCustomHeader()
|
img, rand := h.GetCustomHeaderImg()
|
||||||
if img.Path == "" && h.IndexHandle.DisplayHeaderText() {
|
if img.Path == "" && h.DisplayHeaderText() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ss := str.NewBuilder()
|
ss := str.NewBuilder()
|
||||||
ss.WriteString(style)
|
ss.WriteString(style)
|
||||||
if img.Path == "" && !h.IndexHandle.DisplayHeaderText() {
|
if img.Path == "" && !h.DisplayHeaderText() {
|
||||||
ss.WriteString(defaultTextStyle)
|
ss.WriteString(defaultTextStyle)
|
||||||
}
|
}
|
||||||
if img.Path != "" {
|
if img.Path != "" {
|
||||||
ss.Sprintf(imgStyle, img.Path, img.Path)
|
ss.Sprintf(imgStyle, img.Path, img.Path)
|
||||||
}
|
}
|
||||||
if !h.IndexHandle.DisplayHeaderText() {
|
if !h.DisplayHeaderText() {
|
||||||
ss.WriteString(`.site-title,
|
ss.WriteString(`.site-title,
|
||||||
.site-description {
|
.site-description {
|
||||||
clip: rect(1px, 1px, 1px, 1px);
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
|
@ -107,15 +108,16 @@ func (h *handle) CalCustomHeader() (r string, rand bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handle) CustomHeader() {
|
func customHeader(h *wp.Handle) func() string {
|
||||||
|
return func() string {
|
||||||
headers := header.Load()
|
headers := header.Load()
|
||||||
if headers == constraints.Defaults {
|
if headers == constraints.Defaults {
|
||||||
headerss, rand := h.CalCustomHeader()
|
headerss, rand := calCustomHeaderImg(h)
|
||||||
headers = headerss
|
headers = headerss
|
||||||
if !rand {
|
if !rand {
|
||||||
header.Store(headers)
|
header.Store(headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.IndexHandle.GinH["customHeader"] = headers
|
return headers
|
||||||
return
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
<html lang="{{getLang}}" class="no-js">
|
<html lang="{{getLang}}" class="no-js">
|
||||||
{{template "layout/head" .}}
|
{{template "layout/head" .}}
|
||||||
|
|
||||||
<body class="{{.bodyClass}}">
|
<body class="{{.calBodyClass|exec}}">
|
||||||
{{template "svg"}}
|
{{template "svg"}}
|
||||||
<div id="page" class="hfeed site">
|
<div id="page" class="hfeed site">
|
||||||
<a class="skip-link screen-reader-text" href="#content">
|
<a class="skip-link screen-reader-text" href="#content">
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<div id="sidebar" class="sidebar" style="position: relative; ">
|
<div id="sidebar" class="sidebar" style="position: relative; ">
|
||||||
<header id="masthead" class="site-header">
|
<header id="masthead" class="site-header">
|
||||||
<div class="site-branding">
|
<div class="site-branding">
|
||||||
{{template "common/customLogo" .}}
|
{{.customLogo|exec}}
|
||||||
<h1 class="site-title">
|
<h1 class="site-title">
|
||||||
<a href="/" rel="home">{{ "blogname"| getOption }}</a>
|
<a href="/" rel="home">{{ "blogname"| getOption }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -31,11 +31,7 @@
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<footer id="colophon" class="site-footer">
|
{{template "common/colophon" .}}
|
||||||
<div class="site-info">
|
|
||||||
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{template "layout/footer" .}}
|
{{template "layout/footer" .}}
|
|
@ -8,10 +8,8 @@
|
||||||
var screenReaderText = {"expand":"<span class=\"screen-reader-text\">\u5c55\u5f00\u5b50\u83dc\u5355<\/span>","collapse":"<span class=\"screen-reader-text\">\u6298\u53e0\u5b50\u83dc\u5355<\/span>"};
|
var screenReaderText = {"expand":"<span class=\"screen-reader-text\">\u5c55\u5f00\u5b50\u83dc\u5355<\/span>","collapse":"<span class=\"screen-reader-text\">\u6298\u53e0\u5b50\u83dc\u5355<\/span>"};
|
||||||
</script>
|
</script>
|
||||||
<script src='/wp-content/themes/twentyfifteen/js/functions.js?ver=20220524' id='twentyfifteen-script-js'></script>
|
<script src='/wp-content/themes/twentyfifteen/js/functions.js?ver=20220524' id='twentyfifteen-script-js'></script>
|
||||||
<script src='/wp-content/plugins/enlighter/cache/enlighterjs.min.js?ver=0A0B0C' id='enlighterjs-js'></script>
|
|
||||||
<script id='enlighterjs-js-after'>
|
{{template "common/footer" .}}
|
||||||
!function(e,n){if("undefined"!=typeof EnlighterJS){var o={"selectors":{"block":"pre.EnlighterJSRAW","inline":"code.EnlighterJSRAW"},"options":{"indent":4,"ampersandCleanup":true,"linehover":true,"rawcodeDbclick":false,"textOverflow":"break","linenumbers":true,"theme":"enlighter","language":"generic","retainCssClasses":false,"collapse":false,"toolbarOuter":"","toolbarTop":"{BTN_RAW}{BTN_COPY}{BTN_WINDOW}{BTN_WEBSITE}","toolbarBottom":""}};(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>
|
|
||||||
{{ block "footer" .}}
|
{{ block "footer" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
File diff suppressed because one or more lines are too long
5
app/theme/twentyfifteen/layout/sidebar.gohtml
Normal file
5
app/theme/twentyfifteen/layout/sidebar.gohtml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "layout/sidebar" }}
|
||||||
|
<div id="widget-area" class="widget-area" role="complementary">
|
||||||
|
{{template "common/sidebarWidget" .}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
|
@ -1,11 +1,15 @@
|
||||||
{{template "layout/base" .}}
|
{{template "layout/base" .}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
{{ if .post.PostContent}}
|
{{ if and (.post) (gt .post.Id 0)}}
|
||||||
<div id="primary" class="content-area">
|
<div id="primary" class="content-area">
|
||||||
<main id="main" class="site-main">
|
<main id="main" class="site-main">
|
||||||
<article id="post-{{.post.Id}}"
|
<article class="{{ .post|postsFn .calPostClass}}">
|
||||||
class="post-{{.post.Id}} post type-post status-publish format-standard hentry category-uncategorized">
|
{{if .post.Thumbnail.Path }}
|
||||||
|
<div class="post-thumbnail">
|
||||||
|
<img width="{{.post.Thumbnail.Width}}" height="{{.post.Thumbnail.Height}}" src="{{.post.Thumbnail.Path}}" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async">
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<header class="entry-header">
|
<header class="entry-header">
|
||||||
<h1 class="entry-title">{{.post.PostTitle}}</h1></header><!-- .entry-header -->
|
<h1 class="entry-title">{{.post.PostTitle}}</h1></header><!-- .entry-header -->
|
||||||
|
@ -38,8 +42,7 @@
|
||||||
|
|
||||||
{{if .post.TagsHtml}}
|
{{if .post.TagsHtml}}
|
||||||
<span class="tags-links">
|
<span class="tags-links">
|
||||||
<span class="screen-reader-text">标签 </span>
|
<span class="screen-reader-text">标签 </span>{{.post.TagsHtml|unescaped}}
|
||||||
{{.post.TagsHtml|unescaped}}
|
|
||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -49,11 +52,27 @@
|
||||||
|
|
||||||
{{ if .showComment}}
|
{{ if .showComment}}
|
||||||
<div id="comments" class="comments-area">
|
<div id="comments" class="comments-area">
|
||||||
{{ if gt .post.CommentCount 0}}
|
{{ if ne .comments ""}}
|
||||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
|
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.totalCommentNum}}条评论 </h2>
|
||||||
|
{{if gt .totalCommentPage 1}}
|
||||||
|
<nav class="navigation comment-navigation">
|
||||||
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
<ol class="comment-list">
|
<ol class="comment-list">
|
||||||
{{.comments|unescaped}}
|
{{.comments|unescaped}}
|
||||||
</ol>
|
</ol>
|
||||||
|
{{if gt .totalCommentPage 1}}
|
||||||
|
<nav class="navigation comment-navigation">
|
||||||
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .post.CommentStatus "open"}}
|
{{if eq .post.CommentStatus "open"}}
|
||||||
|
@ -130,6 +149,3 @@
|
||||||
{{template "layout/empty"}}
|
{{template "layout/empty"}}
|
||||||
{{end }}
|
{{end }}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ define "footer"}}
|
|
||||||
<script src='/wp-includes/js/comment-reply.min.js?ver=6.0.2' id='comment-reply-js'></script>
|
|
||||||
{{end}}
|
|
|
@ -12,8 +12,7 @@
|
||||||
</header>
|
</header>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ range $k,$v:=.posts}}
|
{{ range $k,$v:=.posts}}
|
||||||
<article id="post-{{$v.Id}}"
|
<article class="{{ $v|postsFn $.calPostClass}}">
|
||||||
class="post-{{$v.Id}} post {{if $v.Thumbnail.Path}}has-post-thumbnail{{end}} type-post status-publish format-standard hentry category">
|
|
||||||
{{if $v.Thumbnail.Path}}
|
{{if $v.Thumbnail.Path}}
|
||||||
<a class="post-thumbnail" href="/p/{{$v.Id}}" aria-hidden="true">
|
<a class="post-thumbnail" href="/p/{{$v.Id}}" aria-hidden="true">
|
||||||
<img width="{{$v.Thumbnail.Width}}" height="{{$v.Thumbnail.Height}}" src="{{$v.Thumbnail.Path}}" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="{{$v.PostTitle}}" decoding="async">
|
<img width="{{$v.Thumbnail.Width}}" height="{{$v.Thumbnail.Height}}" src="{{$v.Thumbnail.Path}}" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="{{$v.PostTitle}}" decoding="async">
|
||||||
|
@ -32,6 +31,9 @@
|
||||||
</div><!-- .entry-content -->
|
</div><!-- .entry-content -->
|
||||||
|
|
||||||
<footer class="entry-footer">
|
<footer class="entry-footer">
|
||||||
|
{{if $v.IsSticky}}
|
||||||
|
<span class="sticky-post">特色</span>
|
||||||
|
{{end}}
|
||||||
<span class="posted-on">
|
<span class="posted-on">
|
||||||
<span class="screen-reader-text">发布于 </span>
|
<span class="screen-reader-text">发布于 </span>
|
||||||
<a href="/p/{{$v.Id}}" rel="bookmark">
|
<a href="/p/{{$v.Id}}" rel="bookmark">
|
295
app/theme/twentyfifteen/themesupport.go
Normal file
295
app/theme/twentyfifteen/themesupport.go
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
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{}{}
|
||||||
|
}()
|
63
app/theme/twentyfifteen/twentyfifteen.go
Normal file
63
app/theme/twentyfifteen/twentyfifteen.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
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))
|
||||||
|
}
|
574
app/theme/twentyseventeen/colorscheme.go
Normal file
574
app/theme/twentyseventeen/colorscheme.go
Normal file
|
@ -0,0 +1,574 @@
|
||||||
|
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; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
48
app/theme/twentyseventeen/customheader.go
Normal file
48
app/theme/twentyseventeen/customheader.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
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,19 +1,17 @@
|
||||||
{{ define "layout/base"}}
|
{{ define "layout/base"}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{getLang}}" class="no-js no-svg">
|
<html lang="{{getLang}}" class="no-js no-svg">
|
||||||
<head>
|
|
||||||
{{template "layout/head" .}}
|
{{template "layout/head" .}}
|
||||||
{{block "head" .}}
|
|
||||||
{{end}}
|
<body class="{{.calBodyClass|exec}}">
|
||||||
</head>
|
|
||||||
<body class="{{.bodyClass}} wp-embed-responsive hfeed has-header-image has-sidebar colors-light">
|
|
||||||
{{template "svg"}}
|
{{template "svg"}}
|
||||||
<div id="page" class="site">
|
<div id="page" class="site">
|
||||||
<a class="skip-link screen-reader-text" href="#content">跳至内容</a>
|
<a class="skip-link screen-reader-text" href="#content">跳至内容</a>
|
||||||
|
|
||||||
<header id="masthead" class="site-header">
|
<header id="masthead" class="site-header">
|
||||||
|
|
||||||
<div class="custom-header" style="margin-bottom: 0px;">
|
<div class="custom-header" style="margin-bottom: 0;">
|
||||||
<div class="custom-header-media">
|
<div class="custom-header-media">
|
||||||
<div id="wp-custom-header" class="wp-custom-header">
|
<div id="wp-custom-header" class="wp-custom-header">
|
||||||
<img src="{{.HeaderImage.Path}}" width="{{.HeaderImage.Width}}" height="{{.HeaderImage.Height}}" alt="" {{if .HeaderImage.Srcset}}srcset="{{.HeaderImage.Srcset}}" {{end}} {{if .HeaderImage.Sizes}}sizes="{{.HeaderImage.Sizes}}" {{end}}>
|
<img src="{{.HeaderImage.Path}}" width="{{.HeaderImage.Width}}" height="{{.HeaderImage.Height}}" alt="" {{if .HeaderImage.Srcset}}srcset="{{.HeaderImage.Srcset}}" {{end}} {{if .HeaderImage.Sizes}}sizes="{{.HeaderImage.Sizes}}" {{end}}>
|
||||||
|
@ -22,14 +20,14 @@
|
||||||
|
|
||||||
<div class="site-branding" style="margin-bottom: 0px;">
|
<div class="site-branding" style="margin-bottom: 0px;">
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
{{template "common/customLogo" .}}
|
{{.customLogo|exec}}
|
||||||
<div class="site-branding-text">
|
<div class="site-branding-text">
|
||||||
<h1 class="site-title">
|
<h1 class="site-title">
|
||||||
<a href="/" rel="home">{{ "blogname"| getOption }}</a>
|
<a href="/" rel="home">{{ "blogname"| getOption }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="site-description">{{"blogdescription"| getOption}}</p>
|
<p class="site-description">{{"blogdescription"| getOption}}</p>
|
||||||
</div><!-- .site-branding-text -->
|
</div><!-- .site-branding-text -->
|
||||||
{{if eq .scene 1}}
|
{{if eq .scene "Home"}}
|
||||||
<a href="#content" class="menu-scroll-down">
|
<a href="#content" class="menu-scroll-down">
|
||||||
<svg class="icon icon-arrow-right" aria-hidden="true" role="img">
|
<svg class="icon icon-arrow-right" aria-hidden="true" role="img">
|
||||||
<use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use>
|
<use href="#icon-arrow-right" xlink:href="#icon-arrow-right"></use>
|
||||||
|
@ -42,24 +40,12 @@
|
||||||
</div><!-- .site-branding -->
|
</div><!-- .site-branding -->
|
||||||
|
|
||||||
</div><!-- .custom-header -->
|
</div><!-- .custom-header -->
|
||||||
|
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
{{block "content" .}}
|
{{block "content" .}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<footer id="colophon" class="site-footer">
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="site-info">
|
|
||||||
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{template "layout/footer" .}}
|
{{template "layout/footer" .}}
|
||||||
</body>
|
</body>
|
5
app/theme/twentyseventeen/layout/footer.gohtml
Normal file
5
app/theme/twentyseventeen/layout/footer.gohtml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "layout/footer"}}
|
||||||
|
{{template "common/footer" .}}
|
||||||
|
{{ block "footer" .}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
|
@ -161,7 +161,4 @@
|
||||||
</symbol>
|
</symbol>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{{block "footerx" .}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
{{end}}
|
|
@ -1,11 +1,9 @@
|
||||||
{{define "layout/head"}}
|
{{define "layout/head"}}
|
||||||
|
<head>
|
||||||
<meta charset="{{"blog_charset"| getOption}}">
|
<meta charset="{{"blog_charset"| getOption}}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="profile" href="https://gmpg.org/xfn/11">
|
<link rel="profile" href="https://gmpg.org/xfn/11">
|
||||||
<link rel="pingback" href="/xmlrpc.php">
|
<link rel="pingback" href="/xmlrpc.php">
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="/wp-content/themes/twentyfifteen/js/html5.js?ver=3.7.0"></script>
|
|
||||||
<![endif]-->
|
|
||||||
<script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
|
<script>(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);</script>
|
||||||
<title>{{ .title }}</title>
|
<title>{{ .title }}</title>
|
||||||
<meta name='robots' content='max-image-preview:large' />
|
<meta name='robots' content='max-image-preview:large' />
|
||||||
|
@ -47,24 +45,8 @@
|
||||||
.wp-block-pullquote{font-size: 1.5em;line-height: 1.6;}
|
.wp-block-pullquote{font-size: 1.5em;line-height: 1.6;}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<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]-->
|
|
||||||
<link rel='stylesheet' id='enlighterjs-css' href='/wp-content/plugins/enlighter/cache/enlighterjs.min.css?ver=0A0B0C' media='all' />
|
|
||||||
<!--[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>
|
|
||||||
|
|
||||||
{{template "common/head" .}}
|
{{template "common/head" .}}
|
||||||
|
{{block "head" .}}
|
||||||
|
{{end}}
|
||||||
|
</head>
|
||||||
{{end}}
|
{{end}}
|
3
app/theme/twentyseventeen/layout/sidebar.gohtml
Normal file
3
app/theme/twentyseventeen/layout/sidebar.gohtml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{{define "layout/sidebar" }}
|
||||||
|
{{template "common/sidebarWidget" .}}
|
||||||
|
{{end}}
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "layout/base" .}}
|
{{template "layout/base" .}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
{{ if .post.PostContent}}
|
{{ if and (.post) (gt .post.Id 0) }}
|
||||||
{{if .post.Thumbnail.Path}}
|
{{if .post.Thumbnail.Path}}
|
||||||
<div class="single-featured-image-header">
|
<div class="single-featured-image-header">
|
||||||
<img width="{{.post.Thumbnail.OriginAttachmentData.Width}}" height="{{.post.Thumbnail.OriginAttachmentData.Height}}" src="{{.post.Thumbnail.Path}}" class="attachment-twentyseventeen-featured-image size-twentyseventeen-featured-image wp-post-image" alt="{{.post.PostTitle}}" decoding="async" loading="lazy" srcset="{{.post.Thumbnail.Srcset}}" sizes="{{.post.Thumbnail.Sizes}}">
|
<img width="{{.post.Thumbnail.OriginAttachmentData.Width}}" height="{{.post.Thumbnail.OriginAttachmentData.Height}}" src="{{.post.Thumbnail.Path}}" class="attachment-twentyseventeen-featured-image size-twentyseventeen-featured-image wp-post-image" alt="{{.post.PostTitle}}" decoding="async" loading="lazy" srcset="{{.post.Thumbnail.Srcset}}" sizes="{{.post.Thumbnail.Sizes}}">
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<div id="primary" class="content-area">
|
<div id="primary" class="content-area">
|
||||||
<main id="main" class="site-main">
|
<main id="main" class="site-main">
|
||||||
<article id="post-{{.post.Id}}" class="post-{{.post.Id}} post type-post status-publish format-standard hentry {{if .post.Thumbnail.Path}}has-post-thumbnail{{end}} category-uncategorized">
|
<article id="post-{{.post.Id}}" class="{{ .post|postsFn .calPostClass}}">
|
||||||
|
|
||||||
<header class="entry-header">
|
<header class="entry-header">
|
||||||
<div class="entry-meta">
|
<div class="entry-meta">
|
||||||
|
@ -64,12 +64,21 @@
|
||||||
|
|
||||||
{{ if .showComment}}
|
{{ if .showComment}}
|
||||||
<div id="comments" class="comments-area">
|
<div id="comments" class="comments-area">
|
||||||
{{ if gt .post.CommentCount 0}}
|
{{ if ne .comments ""}}
|
||||||
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.post.CommentCount}}个回复 </h2>
|
<h2 class="comments-title">“{{.post.PostTitle}}”的{{.totalCommentNum}}个回复 </h2>
|
||||||
<ol class="comment-list">
|
<ol class="comment-list">
|
||||||
{{.comments|unescaped}}
|
{{.comments|unescaped}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
{{if gt .totalCommentPage 1}}
|
||||||
|
<nav class="navigation comments-pagination" aria-label="评论">
|
||||||
|
<h2 class="screen-reader-text">评论导航</h2>
|
||||||
|
<div class="nav-links">
|
||||||
|
{{ .commentPageNav|unescaped}}
|
||||||
|
</div><!-- .nav-links -->
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if eq .post.CommentStatus "open"}}
|
{{if eq .post.CommentStatus "open"}}
|
||||||
{{template "respond" .}}
|
{{template "respond" .}}
|
||||||
|
@ -122,6 +131,9 @@
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{template "common/colophon" .}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -129,8 +141,3 @@
|
||||||
{{end }}
|
{{end }}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
{{ define "footerx"}}
|
|
||||||
<script src='/wp-includes/js/comment-reply.min.js?ver=6.0.2' id='comment-reply-js'></script>
|
|
||||||
{{end}}
|
|
||||||
|
|
16
app/theme/twentyseventeen/posts/error.gohtml
Normal file
16
app/theme/twentyseventeen/posts/error.gohtml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{{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}}
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
<div id="primary" class="content-area">
|
<div id="primary" class="content-area">
|
||||||
<main id="main" class="site-main">
|
<main id="main" class="site-main">
|
||||||
{{ range $k,$v:=.posts}}
|
{{ range $k,$v:=.posts}}
|
||||||
<article id="post-{{$v.Id}}"
|
<article id="post-{{$v.Id}}" class="{{ $v|postsFn $.calPostClass}}">
|
||||||
class="post-{{$v.Id}} post {{if $v.Thumbnail.Path}}has-post-thumbnail{{end}} type-post status-publish format-standard hentry category">
|
{{if $v.IsSticky}}
|
||||||
|
<svg class="icon icon-thumb-tack" aria-hidden="true" role="img">
|
||||||
|
<use href="#icon-thumb-tack" xlink:href="#icon-thumb-tack"></use>
|
||||||
|
</svg>
|
||||||
|
{{end}}
|
||||||
<header class="entry-header">
|
<header class="entry-header">
|
||||||
<div class="entry-meta">
|
<div class="entry-meta">
|
||||||
<span class="screen-reader-text">发布于 </span>
|
<span class="screen-reader-text">发布于 </span>
|
||||||
|
@ -73,7 +76,7 @@
|
||||||
{{template "layout/empty" .}}
|
{{template "layout/empty" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{template "common/colophon" .}}
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
43
app/theme/twentyseventeen/script.go
Normal file
43
app/theme/twentyseventeen/script.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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>`
|
212
app/theme/twentyseventeen/themesupport.go
Normal file
212
app/theme/twentyseventeen/themesupport.go
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
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{}{}
|
||||||
|
}()
|
229
app/theme/twentyseventeen/twentyseventeen.go
Normal file
229
app/theme/twentyseventeen/twentyseventeen.go
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
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 ""
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user