修复文章发布报错
This commit is contained in:
@@ -307,6 +307,15 @@ func (c *BackendArticleController) List() {
|
||||
qs = qs.Filter("cate_id", cid)
|
||||
}
|
||||
}
|
||||
// 创建时间区间筛选(前端日期控件传 yyyy-MM-dd)
|
||||
startDate := strings.TrimSpace(c.GetString("startDate"))
|
||||
endDate := strings.TrimSpace(c.GetString("endDate"))
|
||||
if startDate != "" {
|
||||
qs = qs.Filter("create_time__gte", startDate+" 00:00:00")
|
||||
}
|
||||
if endDate != "" {
|
||||
qs = qs.Filter("create_time__lte", endDate+" 23:59:59")
|
||||
}
|
||||
|
||||
total, _ := qs.Count()
|
||||
var rows []models.CmsArticle
|
||||
|
||||
@@ -119,7 +119,32 @@ func (c *TenantSiteController) resolveTemplateCode(tid uint64) string {
|
||||
func (c *TenantSiteController) writeHTML(status int, html string) {
|
||||
c.Ctx.Output.SetStatus(status)
|
||||
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
|
||||
c.Ctx.WriteString(html)
|
||||
c.Ctx.WriteString(c.forceHTTPSStorageURLs(html))
|
||||
}
|
||||
|
||||
// forceHTTPSStorageURLs 访客以 HTTPS 访问站点时,把页面中存储(CDN)域名的
|
||||
// http:// 资源升级为 https://,避免混合内容(Mixed Content)被浏览器拦截。
|
||||
// 仅替换存储配置里那个域名,不动正文里的其他外部链接。
|
||||
func (c *TenantSiteController) forceHTTPSStorageURLs(html string) string {
|
||||
proto := c.Ctx.Input.Header("X-Forwarded-Proto")
|
||||
if proto == "" && c.Ctx.Input.IsSecure() {
|
||||
proto = "https"
|
||||
}
|
||||
if proto != "https" {
|
||||
return html
|
||||
}
|
||||
cfg, err := models.GetStorageConfig()
|
||||
if err != nil || strings.TrimSpace(cfg.QiniuDomain) == "" {
|
||||
return html
|
||||
}
|
||||
host := strings.TrimSpace(cfg.QiniuDomain)
|
||||
host = strings.TrimPrefix(host, "http://")
|
||||
host = strings.TrimPrefix(host, "https://")
|
||||
host = strings.TrimRight(host, "/")
|
||||
if host == "" {
|
||||
return html
|
||||
}
|
||||
return strings.ReplaceAll(html, "http://"+host, "https://"+host)
|
||||
}
|
||||
|
||||
// render 解析并输出模板文件
|
||||
|
||||
+34
-21
@@ -42,8 +42,10 @@ func RegisterSelfClose(name string, h SelfCloseHandler) { selfCloseHandlers[name
|
||||
func RegisterGlobal(p GlobalProvider) { globalProvider = p }
|
||||
|
||||
var (
|
||||
// 最内层块标签(内部不再嵌套块标签),反复匹配直至收敛
|
||||
reBlock = regexp.MustCompile(`(?s)\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*\}((?:(?!\{/?yz:).)*?)\{/yz:\1\}`)
|
||||
// 块标签开标签(带属性);自闭合标签因 "/" 不会被匹配
|
||||
reBlockOpen = regexp.MustCompile(`\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*\}`)
|
||||
// 块标签闭标签
|
||||
reBlockClose = regexp.MustCompile(`\{/yz:([a-zA-Z]+)\}`)
|
||||
// 自闭合标签 {yz:name attr="v" /}
|
||||
reSelfClose = regexp.MustCompile(`\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*/\}`)
|
||||
// 全局标签 {yz:word}
|
||||
@@ -122,28 +124,40 @@ func expandIncludes(dir, content string, depth int) (string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// renderBlocks 反复解析最内层块标签直至全部展开
|
||||
// renderBlocks 逐个解析块标签:每次取全文第一个闭标签(必属最内层块),
|
||||
// 向前回溯同名开标签,按行展开后回填,重复至无块标签(Go RE2 不支持负向先行断言,故手工配对)
|
||||
func renderBlocks(content string, ctx *RenderCtx) (string, error) {
|
||||
out := content
|
||||
for i := 0; i < 10; i++ {
|
||||
if !reBlock.MatchString(out) {
|
||||
for i := 0; i < 100; i++ {
|
||||
c := reBlockClose.FindStringIndex(out)
|
||||
if c == nil {
|
||||
break
|
||||
}
|
||||
var ferr error
|
||||
out = reBlock.ReplaceAllStringFunc(out, func(m string) string {
|
||||
sub := reBlock.FindStringSubmatch(m)
|
||||
name := strings.ToLower(sub[1])
|
||||
attrs := parseAttrs(sub[2])
|
||||
body := sub[3]
|
||||
p, ok := blockProviders[name]
|
||||
if !ok {
|
||||
return "" // 未注册的块标签直接置空,避免标签泄漏到前台
|
||||
closeName := strings.ToLower(reBlockClose.FindStringSubmatch(out[c[0]:c[1]])[1])
|
||||
|
||||
// 在闭标签之前找最后一个同名开标签
|
||||
openIdx, openEnd := -1, -1
|
||||
attrs := ""
|
||||
for _, om := range reBlockOpen.FindAllStringSubmatchIndex(out[:c[0]], -1) {
|
||||
if strings.ToLower(out[om[2]:om[3]]) != closeName {
|
||||
continue
|
||||
}
|
||||
rows, err := p(ctx.Tid, attrs, ctx)
|
||||
openIdx, openEnd = om[0], om[1]
|
||||
attrs = out[om[4]:om[5]]
|
||||
}
|
||||
if openIdx < 0 {
|
||||
// 孤立的闭标签:移除后继续,避免死循环
|
||||
out = out[:c[0]] + out[c[1]:]
|
||||
continue
|
||||
}
|
||||
|
||||
rendered := ""
|
||||
if p, ok := blockProviders[closeName]; ok {
|
||||
rows, err := p(ctx.Tid, parseAttrs(attrs), ctx)
|
||||
if err != nil {
|
||||
ferr = err
|
||||
return ""
|
||||
return "", err
|
||||
}
|
||||
body := out[openEnd:c[0]]
|
||||
var sb strings.Builder
|
||||
for _, row := range rows {
|
||||
sb.WriteString(reField.ReplaceAllStringFunc(body, func(fm string) string {
|
||||
@@ -151,11 +165,10 @@ func renderBlocks(content string, ctx *RenderCtx) (string, error) {
|
||||
return row[field]
|
||||
}))
|
||||
}
|
||||
return sb.String()
|
||||
})
|
||||
if ferr != nil {
|
||||
return "", ferr
|
||||
rendered = sb.String()
|
||||
}
|
||||
// 未注册的块标签置空,避免标签泄漏到前台
|
||||
out = out[:openIdx] + rendered + out[c[1]:]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user