commit
68d045157c
@ -48,3 +48,10 @@ func AnyAnyToStrAny(m map[any]any) (r map[string]any) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Reduce[T, V any, K comparable](m map[K]V, fn func(K, V, T) T, r T) T {
|
||||
for k, v := range m {
|
||||
r = fn(k, v, r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -53,6 +53,23 @@ func DiffByFn[T any](a []T, fn func(i, j T) bool, b ...[]T) (r []T) {
|
||||
return
|
||||
}
|
||||
|
||||
func DiffNewByFn[T, V any](a []T, fn func(i, j T) bool, fnV func(T) V, b ...[]T) (r []V) {
|
||||
for _, t := range a {
|
||||
f := false
|
||||
for _, ts := range b {
|
||||
if IsContainedByFn(ts, t, fn) {
|
||||
f = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if f {
|
||||
continue
|
||||
}
|
||||
r = append(r, fnV(t))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Intersect[T comparable](a []T, b ...[]T) (r []T) {
|
||||
for _, t := range a {
|
||||
f := false
|
||||
@ -87,6 +104,23 @@ func IntersectByFn[T any](a []T, fn func(i, j T) bool, b ...[]T) (r []T) {
|
||||
return
|
||||
}
|
||||
|
||||
func IntersectNewByFn[T, V any](a []T, fn func(i, j T) bool, fnV func(T) V, b ...[]T) (r []V) {
|
||||
for _, t := range a {
|
||||
f := false
|
||||
for _, ts := range b {
|
||||
if !IsContainedByFn(ts, t, fn) {
|
||||
f = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if f {
|
||||
continue
|
||||
}
|
||||
r = append(r, fnV(t))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Unique[T comparable](a ...[]T) (r []T) {
|
||||
m := map[T]struct{}{}
|
||||
for _, ts := range a {
|
||||
@ -112,3 +146,15 @@ func UniqueByFn[T any](fn func(T, T) bool, a ...[]T) (r []T) {
|
||||
}
|
||||
return r
|
||||
}
|
||||
func UniqueNewByFn[T, V any](fn func(T, T) bool, fnVal func(T) V, a ...[]T) (r []V) {
|
||||
var rr []T
|
||||
for _, ts := range a {
|
||||
for _, t := range ts {
|
||||
if !IsContainedByFn(rr, t, fn) {
|
||||
rr = append(rr, t)
|
||||
r = append(r, fnVal(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -83,6 +83,46 @@ func TestDiffByFn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffNewByFn(t *testing.T) {
|
||||
type args[T x, V int] struct {
|
||||
a []T
|
||||
fn func(i, j T) bool
|
||||
fnV func(T) V
|
||||
b [][]T
|
||||
}
|
||||
type testCase[T x, V int] struct {
|
||||
name string
|
||||
args args[T, V]
|
||||
wantR []V
|
||||
}
|
||||
tests := []testCase[x, int]{
|
||||
{
|
||||
name: "t1",
|
||||
args: args[x, int]{
|
||||
a: y(number.Range(1, 10, 1)),
|
||||
fn: func(i, j x) bool {
|
||||
return i.int == j.int
|
||||
},
|
||||
fnV: func(v x) int {
|
||||
return v.int
|
||||
},
|
||||
b: [][]x{
|
||||
y(number.Range(3, 7, 1)),
|
||||
y(number.Range(6, 9, 1)),
|
||||
},
|
||||
},
|
||||
wantR: []int{1, 2, 10},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotR := DiffNewByFn(tt.args.a, tt.args.fn, tt.args.fnV, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
|
||||
t.Errorf("DiffNewByFn() = %v, want %v", gotR, tt.wantR)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersect(t *testing.T) {
|
||||
type args[T int] struct {
|
||||
a []T
|
||||
@ -148,6 +188,46 @@ func TestIntersectByFn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersectNewByFn(t *testing.T) {
|
||||
type args[T x, V int] struct {
|
||||
a []T
|
||||
fn func(i, j T) bool
|
||||
fnV func(T) V
|
||||
b [][]T
|
||||
}
|
||||
type testCase[T x, V int] struct {
|
||||
name string
|
||||
args args[T, V]
|
||||
wantR []V
|
||||
}
|
||||
tests := []testCase[x, int]{
|
||||
{
|
||||
name: "t1",
|
||||
args: args[x, int]{
|
||||
a: y(number.Range(1, 10, 1)),
|
||||
fn: func(i, j x) bool {
|
||||
return i.int == j.int
|
||||
},
|
||||
fnV: func(v x) int {
|
||||
return v.int
|
||||
},
|
||||
b: [][]x{
|
||||
y(number.Range(3, 7, 1)),
|
||||
y(number.Range(6, 9, 1)),
|
||||
},
|
||||
},
|
||||
wantR: []int{6, 7},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotR := IntersectNewByFn(tt.args.a, tt.args.fn, tt.args.fnV, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
|
||||
t.Errorf("IntersectNewByFn() = %v, want %v", gotR, tt.wantR)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnique(t *testing.T) {
|
||||
type args[T int] struct {
|
||||
a [][]T
|
||||
@ -169,6 +249,15 @@ func TestUnique(t *testing.T) {
|
||||
},
|
||||
wantR: number.Range(1, 15, 1),
|
||||
},
|
||||
{
|
||||
name: "t2",
|
||||
args: args[int]{
|
||||
a: [][]int{
|
||||
{1, 1, 1, 2, 2, 2, 3, 3, 4, 5},
|
||||
},
|
||||
},
|
||||
wantR: number.Range(1, 5, 1),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -209,3 +298,38 @@ func TestUniqueByFn(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUniqueNewByFn(t *testing.T) {
|
||||
type args[T x, V int] struct {
|
||||
fn func(T, T) bool
|
||||
fnVal func(T) V
|
||||
a [][]T
|
||||
}
|
||||
type testCase[T x, V int] struct {
|
||||
name string
|
||||
args args[T, V]
|
||||
wantR []V
|
||||
}
|
||||
tests := []testCase[x, int]{
|
||||
{
|
||||
name: "t1",
|
||||
args: args[x, int]{
|
||||
fn: func(i, j x) bool {
|
||||
return i.int == j.int
|
||||
},
|
||||
fnVal: func(i x) int {
|
||||
return i.int
|
||||
},
|
||||
a: [][]x{y([]int{1, 1, 2, 2, 3, 3}), y([]int{2, 2, 4, 4})},
|
||||
},
|
||||
wantR: []int{1, 2, 3, 4},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotR := UniqueNewByFn(tt.args.fn, tt.args.fnVal, tt.args.a...); !reflect.DeepEqual(gotR, tt.wantR) {
|
||||
t.Errorf("UniqueNewByFn() = %v, want %v", gotR, tt.wantR)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ func Detail(c *gin.Context) {
|
||||
ginH["comments"] = hh.formatComment(commentss, 1, d)
|
||||
ginH["next"] = next
|
||||
ginH["user"] = user
|
||||
ginH["scene"] = plugins.Detail
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
|
@ -312,5 +312,6 @@ func Index(c *gin.Context) {
|
||||
ginH["totalPage"] = h.getTotalPage(totalRaw)
|
||||
ginH["currentPage"] = h.page
|
||||
ginH["title"] = h.getTitle()
|
||||
ginH["scene"] = h.scene
|
||||
ginH["pagination"] = pagination.NewParsePagination(totalRaw, h.pageSize, h.page, h.paginationStep, q, c.Request.URL.Path)
|
||||
}
|
||||
|
@ -2,14 +2,12 @@ package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
"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/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) {
|
||||
@ -64,35 +62,9 @@ func ToPostThumb(c context.Context, meta map[string]any, host string) (r models.
|
||||
if ok {
|
||||
metadata, ok := x.(models.WpAttachmentMetadata)
|
||||
if ok {
|
||||
r = thumbnail(metadata, "post-thumbnail", host)
|
||||
r = plugins.Thumbnail(metadata, "post-thumbnail", host, "thumbnail")
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func thumbnail(metadata models.WpAttachmentMetadata, thumbType, host string) (r models.PostThumbnail) {
|
||||
if _, ok := metadata.Sizes[thumbType]; ok {
|
||||
r.Path = fmt.Sprintf("%s/wp-content/uploads/%s", host, metadata.File)
|
||||
r.Width = metadata.Sizes[thumbType].Width
|
||||
r.Height = metadata.Sizes[thumbType].Height
|
||||
up := strings.Split(metadata.File, "/")
|
||||
r.Srcset = strings.Join(maps.FilterToSlice[string](metadata.Sizes, func(s string, size models.MetaDataFileSize) (r string, ok bool) {
|
||||
up[2] = size.File
|
||||
if s == "post-thumbnail" {
|
||||
return
|
||||
}
|
||||
r = fmt.Sprintf("%s/wp-content/uploads/%s %dw", host, strings.Join(up, "/"), size.Width)
|
||||
ok = true
|
||||
return
|
||||
}), ", ")
|
||||
r.Sizes = fmt.Sprintf("(max-width: %dpx) 100vw, %dpx", r.Width, r.Width)
|
||||
if r.Width >= 740 && r.Width < 767 {
|
||||
r.Sizes = "(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px"
|
||||
} else if r.Width >= 767 {
|
||||
r.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||
}
|
||||
r.OriginAttachmentData = metadata
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
"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/model"
|
||||
"strings"
|
||||
@ -68,7 +69,7 @@ func GetPostsByIds(ids ...any) (m map[uint64]models.Posts, err error) {
|
||||
pp.Thumbnail = thumb
|
||||
}
|
||||
} else if pp.PostType == "attachment" && pp.AttachmentMetadata.File != "" {
|
||||
thumb := thumbnail(pp.AttachmentMetadata, "thumbnail", host)
|
||||
thumb := plugins.Thumbnail(pp.AttachmentMetadata, "thumbnail", host, "thumbnail", "post-thumbnail")
|
||||
if thumb.Path != "" {
|
||||
pp.Thumbnail = thumb
|
||||
}
|
||||
|
36
internal/plugins/thumbnail.go
Normal file
36
internal/plugins/thumbnail.go
Normal file
@ -0,0 +1,36 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Thumbnail(metadata models.WpAttachmentMetadata, Type, host string, except ...string) (r models.PostThumbnail) {
|
||||
if _, ok := metadata.Sizes[Type]; ok {
|
||||
r.Path = fmt.Sprintf("%s/wp-content/uploads/%s", host, metadata.File)
|
||||
r.Width = metadata.Sizes[Type].Width
|
||||
r.Height = metadata.Sizes[Type].Height
|
||||
up := strings.Split(metadata.File, "/")
|
||||
r.Srcset = strings.Join(maps.FilterToSlice[string](metadata.Sizes, func(s string, size models.MetaDataFileSize) (r string, ok bool) {
|
||||
up[2] = size.File
|
||||
for _, s2 := range except {
|
||||
if s == s2 {
|
||||
return
|
||||
}
|
||||
}
|
||||
r = fmt.Sprintf("%s/wp-content/uploads/%s %dw", host, strings.Join(up, "/"), size.Width)
|
||||
ok = true
|
||||
return
|
||||
}), ", ")
|
||||
r.Sizes = fmt.Sprintf("(max-width: %dpx) 100vw, %dpx", r.Width, r.Width)
|
||||
if r.Width >= 740 && r.Width < 767 {
|
||||
r.Sizes = "(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px"
|
||||
} else if r.Width >= 767 {
|
||||
r.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||
}
|
||||
r.OriginAttachmentData = metadata
|
||||
}
|
||||
return
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
{{block "head" .}}
|
||||
{{end}}
|
||||
</head>
|
||||
<body class="home blog wp-embed-responsive hfeed has-header-image has-sidebar colors-light">
|
||||
<body class="{{.bodyClass}} wp-embed-responsive hfeed has-header-image has-sidebar colors-light">
|
||||
{{template "svg"}}
|
||||
<div id="page" class="site">
|
||||
<a class="skip-link screen-reader-text" href="#content">跳至内容</a>
|
||||
@ -16,7 +16,7 @@
|
||||
<div class="custom-header" style="margin-bottom: 0px;">
|
||||
<div class="custom-header-media">
|
||||
<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.Srcset}}" {{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}}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -44,21 +44,17 @@
|
||||
</header>
|
||||
|
||||
|
||||
<div class="site-content-contain">
|
||||
<div id="content" class="site-content">
|
||||
{{block "content" .}}
|
||||
{{block "content" .}}
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
{{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>
|
||||
<footer id="colophon" class="site-footer">
|
||||
<div class="wrap">
|
||||
<div class="site-info">
|
||||
<a href="https://cn.wordpress.org/" class="imprint">自豪地采用WordPress</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -161,4 +161,7 @@
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
{{block "footerx" .}}
|
||||
{{end}}
|
||||
{{end}}
|
@ -14,7 +14,6 @@
|
||||
<link href='https://fonts.gstatic.com' crossorigin rel='preconnect' />
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ .title }} » Feed" href="/feed" />
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ .title }} » 评论Feed" href="/comments/feed" />
|
||||
<link rel='stylesheet' id='enlighterjs-css' href='/wp-content/plugins/enlighter/cache/enlighterjs.min.css?ver=0A0B0C' media='all' />
|
||||
|
||||
<script>
|
||||
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/svg\/","svgExt":".svg","source":{"concatemoji":"\/wp-includes\/js\/wp-emoji-release.min.js?ver=6.0.2"}};
|
||||
|
@ -2,134 +2,160 @@
|
||||
|
||||
{{define "content"}}
|
||||
{{ if .post.PostContent}}
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main">
|
||||
<article id="post-{{.post.Id}}"
|
||||
class="post-{{.post.Id}} post type-post status-publish format-standard hentry category-uncategorized">
|
||||
{{if .post.Thumbnail.Path}}
|
||||
<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}}">
|
||||
</div>
|
||||
{{end}}
|
||||
<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">
|
||||
<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">
|
||||
|
||||
<header class="entry-header">
|
||||
<h1 class="entry-title">{{.post.PostTitle}}</h1></header><!-- .entry-header -->
|
||||
<header class="entry-header">
|
||||
<div class="entry-meta">
|
||||
<span class="posted-on">
|
||||
<span class="screen-reader-text">发布于</span>
|
||||
<a href="/p/{{.post.Id}}" rel="bookmark">
|
||||
<time class="entry-date published" datetime="{{.post.PostDateGmt}}">{{.post.PostDate|dateCh}}
|
||||
</time>
|
||||
<time class="updated" datetime="{{.post.PostModifiedGmt}}">{{.post.PostModified|dateCh}}
|
||||
</time>
|
||||
</a>
|
||||
</span>
|
||||
<span class="byline">
|
||||
由
|
||||
<span class="author vcard">
|
||||
<a class="url fn n" href="/p/author/{{.user.UserLogin}}">{{.user.UserLogin}}</a>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<h1 class="entry-title">{{.post.PostTitle}}</h1>
|
||||
</header><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
{{.post.PostContent|unescaped}}
|
||||
</div><!-- .entry-content -->
|
||||
<div class="entry-content">
|
||||
{{.post.PostContent|unescaped}}
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<footer class="entry-footer">
|
||||
<span class="posted-on">
|
||||
<span class="screen-reader-text">发布于 </span>
|
||||
<a href="/p/{{.post.Id}}" rel="bookmark">
|
||||
<time class="entry-date published updated"
|
||||
datetime="{{.post.PostDateGmt}}">{{.post.PostDate|dateCh}}
|
||||
</time>
|
||||
</a>
|
||||
</span>
|
||||
<span class="byline">
|
||||
<span class="author vcard">
|
||||
<span class="screen-reader-text">作者 </span>
|
||||
<a class="url fn n" href="/p/author/{{.user.UserLogin}}">{{.user.UserLogin}}</a>
|
||||
</span>
|
||||
</span>
|
||||
{{if .post.CategoriesHtml}}
|
||||
<span class="cat-links">
|
||||
<span class="screen-reader-text">分类 </span>
|
||||
{{.post.CategoriesHtml|unescaped}}
|
||||
</span>
|
||||
{{end}}
|
||||
<footer class="entry-footer">
|
||||
<span class="cat-tags-links">
|
||||
{{if .post.CategoriesHtml}}
|
||||
<span class="cat-links">
|
||||
<svg class="icon icon-folder-open" aria-hidden="true" role="img"> <use href="#icon-folder-open" xlink:href="#icon-folder-open"></use> </svg>
|
||||
<span class="screen-reader-text">分类 </span>
|
||||
{{.post.CategoriesHtml|unescaped}}
|
||||
</span>
|
||||
{{end}}
|
||||
|
||||
{{if .post.TagsHtml}}
|
||||
<span class="tags-links">
|
||||
<span class="screen-reader-text">标签 </span>
|
||||
{{.post.TagsHtml|unescaped}}
|
||||
</span>
|
||||
{{end}}
|
||||
</footer>
|
||||
<!-- .entry-footer -->
|
||||
{{if .post.TagsHtml}}
|
||||
<span class="tags-links">
|
||||
<svg class="icon icon-hashtag" aria-hidden="true" role="img"> <use href="#icon-hashtag" xlink:href="#icon-hashtag"></use> </svg>
|
||||
<span class="screen-reader-text">标签 </span>
|
||||
{{.post.TagsHtml|unescaped}}
|
||||
</span>
|
||||
{{end}}
|
||||
</span>
|
||||
</footer>
|
||||
<!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-1 -->
|
||||
</article><!-- #post-1 -->
|
||||
|
||||
{{ if .showComment}}
|
||||
<div id="comments" class="comments-area">
|
||||
{{ if gt .post.CommentCount 0}}
|
||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
|
||||
<ol class="comment-list">
|
||||
{{.comments|unescaped}}
|
||||
</ol>
|
||||
{{ if .showComment}}
|
||||
<div id="comments" class="comments-area">
|
||||
{{ if gt .post.CommentCount 0}}
|
||||
<h2 class="comments-title">《{{.post.PostTitle}}》上有{{.post.CommentCount}}条评论 </h2>
|
||||
<ol class="comment-list">
|
||||
{{.comments|unescaped}}
|
||||
</ol>
|
||||
|
||||
{{end}}
|
||||
{{if eq .post.CommentStatus "open"}}
|
||||
<div id="respond" class="comment-respond">
|
||||
<h3 id="reply-title" class="comment-reply-title">发表回复
|
||||
<small>
|
||||
<a rel="nofollow" id="cancel-comment-reply-link" href="/p/{{.post.Id}}#respond" style="display:none;">取消回复</a>
|
||||
</small>
|
||||
</h3>
|
||||
<form action="/comment" method="post" id="commentform" class="comment-form"
|
||||
novalidate="">
|
||||
<p class="comment-notes">
|
||||
<span id="email-notes">您的电子邮箱地址不会被公开。</span>
|
||||
<span class="required-field-message" aria-hidden="true">必填项已用<span class="required" aria-hidden="true">*</span>标注</span>
|
||||
</p>
|
||||
<p class="comment-form-comment">
|
||||
<label for="comment">评论 <span class="required" aria-hidden="true">*</span></label>
|
||||
<textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required=""></textarea></p>
|
||||
<p class="comment-form-author">
|
||||
<label for="author">显示名称 <span class="required" aria-hidden="true">*</span></label>
|
||||
<input id="author" name="author" type="text" value="" size="30" maxlength="245"
|
||||
required=""></p>
|
||||
<p class="comment-form-email">
|
||||
<label for="email">电子邮箱地址 <span class="required" aria-hidden="true">*</span></label>
|
||||
<input id="email" name="email" type="email" value="" size="30" maxlength="100"
|
||||
aria-describedby="email-notes" required="">
|
||||
</p>
|
||||
<p class="comment-form-url"><label for="url">网站地址</label>
|
||||
<input id="url" name="url" type="url" value="" size="30" maxlength="200"></p>
|
||||
<p class="comment-form-cookies-consent">
|
||||
<input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes">
|
||||
<label for="wp-comment-cookies-consent">在此浏览器中保存我的显示名称、邮箱地址和网站地址,以便下次评论时使用。</label>
|
||||
</p>
|
||||
<p class="form-submit">
|
||||
<input name="submit" type="submit" id="submit" class="submit" value="发表评论">
|
||||
<input type="hidden" name="comment_post_ID" value="{{.post.Id}}" id="comment_post_ID">
|
||||
<input type="hidden" name="comment_parent" id="comment_parent" value="0">
|
||||
</p>
|
||||
</form>
|
||||
</div><!-- #respond -->
|
||||
{{else}}
|
||||
<p class="no-comments">评论已关闭。</p>
|
||||
{{end}}
|
||||
</div><!-- .comments-area -->
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if eq .post.CommentStatus "open"}}
|
||||
<div id="respond" class="comment-respond">
|
||||
<h3 id="reply-title" class="comment-reply-title">发表回复
|
||||
<small>
|
||||
<a rel="nofollow" id="cancel-comment-reply-link" href="/p/{{.post.Id}}#respond" style="display:none;">取消回复</a>
|
||||
</small>
|
||||
</h3>
|
||||
<form action="/comment" method="post" id="commentform" class="comment-form"
|
||||
novalidate="">
|
||||
<p class="comment-notes">
|
||||
<span id="email-notes">您的电子邮箱地址不会被公开。</span>
|
||||
<span class="required-field-message" aria-hidden="true">必填项已用<span class="required" aria-hidden="true">*</span>标注</span>
|
||||
</p>
|
||||
<p class="comment-form-comment">
|
||||
<label for="comment">评论 <span class="required" aria-hidden="true">*</span></label>
|
||||
<textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required=""></textarea></p>
|
||||
<p class="comment-form-author">
|
||||
<label for="author">显示名称 <span class="required" aria-hidden="true">*</span></label>
|
||||
<input id="author" name="author" type="text" value="" size="30" maxlength="245"
|
||||
required=""></p>
|
||||
<p class="comment-form-email">
|
||||
<label for="email">电子邮箱地址 <span class="required" aria-hidden="true">*</span></label>
|
||||
<input id="email" name="email" type="email" value="" size="30" maxlength="100"
|
||||
aria-describedby="email-notes" required="">
|
||||
</p>
|
||||
<p class="comment-form-url"><label for="url">网站地址</label>
|
||||
<input id="url" name="url" type="url" value="" size="30" maxlength="200"></p>
|
||||
<p class="comment-form-cookies-consent">
|
||||
<input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes">
|
||||
<label for="wp-comment-cookies-consent">在此浏览器中保存我的显示名称、邮箱地址和网站地址,以便下次评论时使用。</label>
|
||||
</p>
|
||||
<p class="form-submit">
|
||||
<input name="submit" type="submit" id="submit" class="submit" value="发表评论">
|
||||
<input type="hidden" name="comment_post_ID" value="{{.post.Id}}" id="comment_post_ID">
|
||||
<input type="hidden" name="comment_parent" id="comment_parent" value="0">
|
||||
</p>
|
||||
</form>
|
||||
</div><!-- #respond -->
|
||||
{{else}}
|
||||
<p class="no-comments">评论已关闭。</p>
|
||||
{{end}}
|
||||
</div><!-- .comments-area -->
|
||||
{{end}}
|
||||
|
||||
|
||||
<nav class="navigation post-navigation" aria-label="文章">
|
||||
<h2 class="screen-reader-text">文章导航</h2>
|
||||
<nav class="navigation post-navigation" aria-label="文章">
|
||||
<h2 class="screen-reader-text">文章导航</h2>
|
||||
|
||||
<div class="nav-links">
|
||||
{{if gt .prev.Id 0}}
|
||||
<div class="nav-previous">
|
||||
<a href="/p/{{.prev.Id}}" rel="prev">
|
||||
<span class="meta-nav" aria-hidden="true">上一篇</span>
|
||||
<span class="screen-reader-text">上篇文章:</span>
|
||||
<span class="post-title">{{.prev.PostTitle}}</span></a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if gt .next.Id 0}}
|
||||
<div class="nav-next">
|
||||
<a href="/p/{{.next.Id}}" rel="next">
|
||||
<span class="meta-nav" aria-hidden="true">下一篇</span>
|
||||
<span class="screen-reader-text">下篇文章:</span>
|
||||
<span class="post-title">{{.next.PostTitle}}</span></a>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="nav-links">
|
||||
{{if gt .prev.Id 0}}
|
||||
<div class="nav-previous">
|
||||
<a href="/p/{{.prev.Id}}" rel="prev">
|
||||
<span class="meta-nav" aria-hidden="true">上一篇</span>
|
||||
<span class="screen-reader-text">上篇文章:</span>
|
||||
<span class="post-title">{{.prev.PostTitle}}</span></a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if gt .next.Id 0}}
|
||||
<div class="nav-next">
|
||||
<a href="/p/{{.next.Id}}" rel="next">
|
||||
<span class="meta-nav" aria-hidden="true">下一篇</span>
|
||||
<span class="screen-reader-text">下篇文章:</span>
|
||||
<span class="post-title">{{.next.PostTitle}}</span></a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
</main><!-- .site-main -->
|
||||
</div>
|
||||
</nav>
|
||||
</main><!-- .site-main -->
|
||||
|
||||
<aside id="secondary" class="widget-area" aria-label="博客边栏">
|
||||
{{template "layout/sidebar" .}}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{else}}
|
||||
{{template "layout/empty"}}
|
||||
{{end }}
|
||||
{{end}}
|
||||
{{ define "footer"}}
|
||||
|
||||
|
||||
{{ define "footerx"}}
|
||||
<script src='/wp-includes/js/comment-reply.min.js?ver=6.0.2' id='comment-reply-js'></script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
@ -1,81 +1,86 @@
|
||||
{{template "layout/base" .}}
|
||||
|
||||
{{define "content" }}
|
||||
{{if .posts}}
|
||||
<div class="site-content-contain">
|
||||
<div id="content" class="site-content">
|
||||
{{if .posts}}
|
||||
|
||||
<div class="wrap">
|
||||
<header class="page-header">
|
||||
<h2 class="page-title">文章</h2>
|
||||
</header>
|
||||
<div class="wrap">
|
||||
<header class="page-header">
|
||||
<h2 class="page-title">文章</h2>
|
||||
</header>
|
||||
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main">
|
||||
{{if .header}}
|
||||
<header class="page-header">
|
||||
<h1 class="page-title">
|
||||
{{if .search}}
|
||||
{{.header}}
|
||||
{{else}}
|
||||
{{.header |unescaped}}
|
||||
{{end}}
|
||||
</h1>
|
||||
</header>
|
||||
{{end}}
|
||||
{{ range $k,$v:=.posts}}
|
||||
<article id="post-{{$v.Id}}"
|
||||
class="post-{{$v.Id}} post {{if $v.Thumbnail.Path}}has-post-thumbnail{{end}} type-post status-publish format-standard hentry category">
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main">
|
||||
{{if .header}}
|
||||
<header class="page-header">
|
||||
<h1 class="page-title">
|
||||
{{if .search}}
|
||||
{{.header}}
|
||||
{{else}}
|
||||
{{.header |unescaped}}
|
||||
{{end}}
|
||||
</h1>
|
||||
</header>
|
||||
{{end}}
|
||||
{{ range $k,$v:=.posts}}
|
||||
<article id="post-{{$v.Id}}"
|
||||
class="post-{{$v.Id}} post {{if $v.Thumbnail.Path}}has-post-thumbnail{{end}} type-post status-publish format-standard hentry category">
|
||||
|
||||
<header class="entry-header">
|
||||
<div class="entry-meta">
|
||||
<span class="screen-reader-text">发布于 </span>
|
||||
<a href="/p/{{$v.Id}}" rel="bookmark">
|
||||
<time class="entry-date published" datetime="{{$v.PostDateGmt}}">{{$v.PostDate|dateCh}}
|
||||
</time><time class="updated" datetime="{{$v.PostModifiedGmt}}">{{$v.PostModified|dateCh}}
|
||||
</time>
|
||||
</a>
|
||||
</div>
|
||||
<h3 class="entry-title">
|
||||
<a href="/p/{{$v.Id}}" rel="bookmark">{{$v.PostTitle}}</a>
|
||||
</h3>
|
||||
</header>
|
||||
<header class="entry-header">
|
||||
<div class="entry-meta">
|
||||
<span class="screen-reader-text">发布于 </span>
|
||||
<a href="/p/{{$v.Id}}" rel="bookmark">
|
||||
<time class="entry-date published" datetime="{{$v.PostDateGmt}}">{{$v.PostDate|dateCh}}
|
||||
</time><time class="updated" datetime="{{$v.PostModifiedGmt}}">{{$v.PostModified|dateCh}}
|
||||
</time>
|
||||
</a>
|
||||
</div>
|
||||
<h3 class="entry-title">
|
||||
<a href="/p/{{$v.Id}}" rel="bookmark">{{$v.PostTitle}}</a>
|
||||
</h3>
|
||||
</header>
|
||||
|
||||
|
||||
{{if $v.Thumbnail.Path}}
|
||||
<div class="post-thumbnail">
|
||||
<a href="/p/{{$v.Id}}" >
|
||||
<img width="{{$v.Thumbnail.Width}}" height="{{$v.Thumbnail.Height}}" src="{{$v.Thumbnail.Path}}" class="attachment-twentyseventeen-featured-image size-twentyseventeen-featured-image wp-post-image" alt="{{$v.PostTitle}}" decoding="async" loading="lazy" srcset="{{$v.Thumbnail.Srcset}}" sizes="{{$v.Thumbnail.Sizes}}">
|
||||
</a>
|
||||
</div>
|
||||
{{if $v.Thumbnail.Path}}
|
||||
<div class="post-thumbnail">
|
||||
<a href="/p/{{$v.Id}}" >
|
||||
<img width="{{$v.Thumbnail.Width}}" height="{{$v.Thumbnail.Height}}" src="{{$v.Thumbnail.Path}}" class="attachment-twentyseventeen-featured-image size-twentyseventeen-featured-image wp-post-image" alt="{{$v.PostTitle}}" decoding="async" loading="lazy" srcset="{{$v.Thumbnail.Srcset}}" sizes="{{$v.Thumbnail.Sizes}}">
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
<!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
{{$v.PostContent|unescaped}}
|
||||
</div>
|
||||
|
||||
<!-- .entry-content -->
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
<!-- #post-{{$v.Id}} -->
|
||||
{{end}}
|
||||
|
||||
{{template "layout/page" .}}
|
||||
</main>
|
||||
<!-- .site-main -->
|
||||
|
||||
<!-- .entry-header -->
|
||||
</div>
|
||||
|
||||
<div class="entry-content">
|
||||
{{$v.PostContent|unescaped}}
|
||||
</div>
|
||||
<aside id="secondary" class="widget-area" aria-label="博客边栏">
|
||||
{{template "layout/sidebar" .}}
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<!-- .entry-content -->
|
||||
{{else }}
|
||||
{{template "layout/empty" .}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
|
||||
</article>
|
||||
|
||||
<!-- #post-{{$v.Id}} -->
|
||||
{{end}}
|
||||
|
||||
{{template "layout/page" .}}
|
||||
</main>
|
||||
<!-- .site-main -->
|
||||
|
||||
</div>
|
||||
|
||||
<aside id="secondary" class="widget-area" aria-label="博客边栏">
|
||||
{{template "layout/sidebar" .}}
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
{{else }}
|
||||
{{template "layout/empty" .}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -1,8 +1,11 @@
|
||||
package twentyseventeen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/elliotchance/phpserialize"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/fthvgb1/wp-go/helper/slice"
|
||||
str "github.com/fthvgb1/wp-go/helper/strings"
|
||||
"github.com/fthvgb1/wp-go/internal/pkg/cache"
|
||||
"github.com/fthvgb1/wp-go/internal/pkg/logs"
|
||||
"github.com/fthvgb1/wp-go/internal/pkg/models"
|
||||
@ -44,6 +47,7 @@ func Hook(status int, c *gin.Context, h gin.H, scene, stats int) {
|
||||
templ := "twentyseventeen/posts/index.gohtml"
|
||||
if _, ok := plugins.IndexSceneMap[scene]; ok {
|
||||
h["HeaderImage"] = getHeaderImage(c)
|
||||
posts := h["posts"].([]models.Posts)
|
||||
p, ok := h["pagination"]
|
||||
if ok {
|
||||
pp, ok := p.(pagination.ParsePagination)
|
||||
@ -51,18 +55,58 @@ func Hook(status int, c *gin.Context, h gin.H, scene, stats int) {
|
||||
h["pagination"] = pagination.Paginate(paginate, pp)
|
||||
}
|
||||
}
|
||||
d := 0
|
||||
s := ""
|
||||
if scene == plugins.Search {
|
||||
if len(posts) > 0 {
|
||||
d = 1
|
||||
} else {
|
||||
d = 0
|
||||
}
|
||||
} else if scene == plugins.Category {
|
||||
cate := slice.Filter(cache.Categories(c), func(my models.TermsMy) bool {
|
||||
return my.Name == c.Param("category")
|
||||
})[0]
|
||||
d = int(cate.Terms.TermId)
|
||||
if cate.Slug[0] != '%' {
|
||||
s = cate.Slug
|
||||
}
|
||||
}
|
||||
h["bodyClass"] = bodyClass(scene, d, s)
|
||||
h["posts"] = postThumbnail(posts, scene)
|
||||
} else if scene == plugins.Detail {
|
||||
h["HeaderImage"] = getHeaderImage(c)
|
||||
post := h["post"].(models.Posts)
|
||||
h["bodyClass"] = bodyClass(scene, int(post.Id))
|
||||
host, _ := wpconfig.Options.Load("siteurl")
|
||||
img := plugins.Thumbnail(post.Thumbnail.OriginAttachmentData, "thumbnail", host, "thumbnail", "post-thumbnail")
|
||||
img.Width = img.OriginAttachmentData.Width
|
||||
img.Height = img.OriginAttachmentData.Height
|
||||
img.Sizes = "100vw"
|
||||
img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset)
|
||||
post.Thumbnail = img
|
||||
h["post"] = post
|
||||
templ = "twentyseventeen/posts/detail.gohtml"
|
||||
}
|
||||
c.HTML(status, templ, h)
|
||||
return
|
||||
}
|
||||
|
||||
func postThumbnail(posts []models.Posts, scene int) []models.Posts {
|
||||
return slice.Map(posts, func(t models.Posts) models.Posts {
|
||||
if t.Thumbnail.Path != "" {
|
||||
if slice.IsContained(scene, []int{plugins.Home, plugins.Archive, plugins.Search}) {
|
||||
t.Thumbnail.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
|
||||
} else {
|
||||
t.Thumbnail.Sizes = "100vw"
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
func getHeaderImage(c *gin.Context) (r models.PostThumbnail) {
|
||||
r.Path = "/wp-content/themes/twentyseventeen/assets/images/header.jpg"
|
||||
r.Width = 2000
|
||||
r.Height = 1200
|
||||
meta, err := getHeaderMarkup()
|
||||
if err != nil {
|
||||
logs.ErrPrintln(err, "解析主题背景图设置错误")
|
||||
@ -74,10 +118,18 @@ func getHeaderImage(c *gin.Context) (r models.PostThumbnail) {
|
||||
logs.ErrPrintln(err, "获取主题背景图信息错误")
|
||||
return
|
||||
}
|
||||
host, _ := wpconfig.Options.Load("siteurl")
|
||||
m.Thumbnail = plugins.Thumbnail(m.AttachmentMetadata, "thumbnail", host, "thumbnail", "post-thumbnail", "twentyseventeen-thumbnail-avatar")
|
||||
if m.Thumbnail.Path != "" {
|
||||
r = m.Thumbnail
|
||||
if len(m.AttachmentMetadata.Sizes) > 0 {
|
||||
r.Srcset = str.Join(r.Path, " 2000vw, ", r.Srcset)
|
||||
}
|
||||
}
|
||||
}
|
||||
r.Width = 2000
|
||||
r.Height = 1200
|
||||
r.Sizes = "100vw"
|
||||
return
|
||||
}
|
||||
|
||||
@ -93,3 +145,25 @@ func getHeaderMarkup() (r HeaderImageMeta, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bodyClass(scene, d int, a ...any) string {
|
||||
s := ""
|
||||
if scene == plugins.Search {
|
||||
if d > 0 {
|
||||
s = "search-results"
|
||||
} else {
|
||||
s = "search-no-results"
|
||||
}
|
||||
} else if scene == plugins.Category {
|
||||
s = fmt.Sprintf("category-%d %v", d, a[0])
|
||||
} else if scene == plugins.Detail {
|
||||
s = fmt.Sprintf("postid-%d", d)
|
||||
}
|
||||
return map[int]string{
|
||||
plugins.Home: "home blog ",
|
||||
plugins.Archive: "archive date page-two-column",
|
||||
plugins.Category: str.Join("archive category page-two-column ", s),
|
||||
plugins.Search: str.Join("search ", s),
|
||||
plugins.Detail: str.Join("post-template-default single single-post single-format-standard ", s),
|
||||
}[scene]
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"github.com/fthvgb1/wp-go/safety"
|
||||
"github.com/fthvgb1/wp-go/helper/maps"
|
||||
"github.com/fthvgb1/wp-go/taskPools"
|
||||
"sync"
|
||||
)
|
||||
@ -24,15 +24,8 @@ func newMapX[K comparable, V any]() mapX[K, V] {
|
||||
}
|
||||
}
|
||||
|
||||
func SimpleMapFilterAndMapToSlice[R any, K comparable, V any](mm SimpleMapStream[K, V], fn func(K, V) (R, bool), c int) SimpleSliceStream[R] {
|
||||
rr := safety.NewSlice([]R{})
|
||||
mm.ParallelForEach(func(k K, v V) {
|
||||
vv, ok := fn(k, v)
|
||||
if ok {
|
||||
rr.Append(vv)
|
||||
}
|
||||
}, c)
|
||||
return NewSimpleSliceStream(rr.Load())
|
||||
func SimpleMapFilterAndMapToSlice[R any, K comparable, V any](mm SimpleMapStream[K, V], fn func(K, V) (R, bool)) SimpleSliceStream[R] {
|
||||
return NewSimpleSliceStream(maps.FilterToSlice(mm.m, fn))
|
||||
}
|
||||
|
||||
func SimpleMapParallelFilterAndMapToMap[K comparable, V any, KK comparable, VV any](mm SimpleMapStream[KK, VV], fn func(KK, VV) (K, V, bool), c int) SimpleMapStream[K, V] {
|
||||
|
Loading…
Reference in New Issue
Block a user