mirror of https://github.com/jlelse/GoBlog
New plugin types: UISummary and UIFooter
This commit is contained in:
parent
144e7f4a41
commit
8c9d17006d
|
@ -27,13 +27,15 @@ You need to specify the path to the plugin (remember to mount the path to your G
|
||||||
|
|
||||||
## Types of plugins
|
## Types of plugins
|
||||||
|
|
||||||
- `SetApp` (Access more GoBlog functionalities like the database) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetApp
|
- `SetApp` (Access more GoBlog functionalities like the database) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetApp]
|
||||||
- `SetConfig` (Access the configuration provided for the plugin) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetConfig
|
- `SetConfig` (Access the configuration provided for the plugin) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#SetConfig]
|
||||||
|
|
||||||
- `Exec` (Command that is executed in a Go routine when starting GoBlog) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Exec
|
- `Exec` (Command that is executed in a Go routine when starting GoBlog) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Exec]
|
||||||
- `Middleware` (HTTP middleware to intercept or modify HTTP requests) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#Middleware
|
- `Middleware` (HTTP middleware to intercept or modify HTTP requests) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/]plugintypes#Middleware
|
||||||
- `UI` (Modify rendered HTML) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI
|
- `UI` (Modify rendered HTML) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI]
|
||||||
- `UI2` (Modify rendered HTML using a goquery document which improves performance and avoids multiple HTML parsing and rendering when using multiple plugins) - see https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI2
|
- `UI2` (Modify rendered HTML using a goquery document which improves performance and avoids multiple HTML parsing and rendering when using multiple plugins) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UI2]
|
||||||
|
- `UISummary` (like UI2 for only the post summary on indexes) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UISummary]
|
||||||
|
- `UIFooter` (like UI2 for only the post summary on indexes) - see [https://pkg.go.dev/go.goblog.app/app/pkgs/plugintypes#UIFooter]
|
||||||
|
|
||||||
More types will be added later. Any plugin can implement multiple types, see the demo plugin as example.
|
More types will be added later. Any plugin can implement multiple types, see the demo plugin as example.
|
||||||
|
|
||||||
|
|
|
@ -39,18 +39,24 @@ type Post interface {
|
||||||
GetPath() string
|
GetPath() string
|
||||||
// Get a string array map with all the post's parameters
|
// Get a string array map with all the post's parameters
|
||||||
GetParameters() map[string][]string
|
GetParameters() map[string][]string
|
||||||
|
// Get a single parameter array (a parameter can have multiple values)
|
||||||
|
GetParameter(parameter string) []string
|
||||||
// Get the post section name
|
// Get the post section name
|
||||||
GetSection() string
|
GetSection() string
|
||||||
// Get the published date string
|
// Get the published date string
|
||||||
GetPublished() string
|
GetPublished() string
|
||||||
// Get the updated date string
|
// Get the updated date string
|
||||||
GetUpdated() string
|
GetUpdated() string
|
||||||
|
// Get the post content (markdown)
|
||||||
|
GetContent() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderContext
|
// RenderContext
|
||||||
type RenderContext interface {
|
type RenderContext interface {
|
||||||
// Get the path of the request
|
// Get the path of the request
|
||||||
GetPath() string
|
GetPath() string
|
||||||
|
// Get the URL
|
||||||
|
GetURL() string
|
||||||
// Get the blog name
|
// Get the blog name
|
||||||
GetBlog() string
|
GetBlog() string
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,3 +43,18 @@ type UI2 interface {
|
||||||
// The document can be used to add or modify HTML.
|
// The document can be used to add or modify HTML.
|
||||||
RenderWithDocument(renderContext RenderContext, doc *goquery.Document)
|
RenderWithDocument(renderContext RenderContext, doc *goquery.Document)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UISummary plugins get called when rendering the summary on indexes for a post.
|
||||||
|
type UISummary interface {
|
||||||
|
// The renderContext provides information such as the path of the request or the blog name.
|
||||||
|
// The post contains information about the post for which to render the summary.
|
||||||
|
// The document can be used to add or modify the default HTML.
|
||||||
|
RenderSummaryForPost(renderContext RenderContext, post Post, doc *goquery.Document)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIFooter plugins get called when rendering the footer on each HTML page.
|
||||||
|
type UIFooter interface {
|
||||||
|
// The renderContext provides information such as the path of the request or the blog name.
|
||||||
|
// The document can be used to add or modify the default HTML.
|
||||||
|
RenderFooter(renderContext RenderContext, doc *goquery.Document)
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ func init() {
|
||||||
"SetConfig": reflect.ValueOf((*plugintypes.SetConfig)(nil)),
|
"SetConfig": reflect.ValueOf((*plugintypes.SetConfig)(nil)),
|
||||||
"UI": reflect.ValueOf((*plugintypes.UI)(nil)),
|
"UI": reflect.ValueOf((*plugintypes.UI)(nil)),
|
||||||
"UI2": reflect.ValueOf((*plugintypes.UI2)(nil)),
|
"UI2": reflect.ValueOf((*plugintypes.UI2)(nil)),
|
||||||
|
"UIFooter": reflect.ValueOf((*plugintypes.UIFooter)(nil)),
|
||||||
|
"UISummary": reflect.ValueOf((*plugintypes.UISummary)(nil)),
|
||||||
|
|
||||||
// interface wrapper definitions
|
// interface wrapper definitions
|
||||||
"_App": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_App)(nil)),
|
"_App": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_App)(nil)),
|
||||||
|
@ -59,6 +61,8 @@ func init() {
|
||||||
"_SetConfig": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_SetConfig)(nil)),
|
"_SetConfig": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_SetConfig)(nil)),
|
||||||
"_UI": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI)(nil)),
|
"_UI": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI)(nil)),
|
||||||
"_UI2": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI2)(nil)),
|
"_UI2": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI2)(nil)),
|
||||||
|
"_UIFooter": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UIFooter)(nil)),
|
||||||
|
"_UISummary": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UISummary)(nil)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +153,8 @@ func (W _go_goblog_app_app_pkgs_plugintypes_Middleware) Prio() int {
|
||||||
// _go_goblog_app_app_pkgs_plugintypes_Post is an interface wrapper for Post type
|
// _go_goblog_app_app_pkgs_plugintypes_Post is an interface wrapper for Post type
|
||||||
type _go_goblog_app_app_pkgs_plugintypes_Post struct {
|
type _go_goblog_app_app_pkgs_plugintypes_Post struct {
|
||||||
IValue interface{}
|
IValue interface{}
|
||||||
|
WGetContent func() string
|
||||||
|
WGetParameter func(parameter string) []string
|
||||||
WGetParameters func() map[string][]string
|
WGetParameters func() map[string][]string
|
||||||
WGetPath func() string
|
WGetPath func() string
|
||||||
WGetPublished func() string
|
WGetPublished func() string
|
||||||
|
@ -156,6 +162,12 @@ type _go_goblog_app_app_pkgs_plugintypes_Post struct {
|
||||||
WGetUpdated func() string
|
WGetUpdated func() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetContent() string {
|
||||||
|
return W.WGetContent()
|
||||||
|
}
|
||||||
|
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetParameter(parameter string) []string {
|
||||||
|
return W.WGetParameter(parameter)
|
||||||
|
}
|
||||||
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetParameters() map[string][]string {
|
func (W _go_goblog_app_app_pkgs_plugintypes_Post) GetParameters() map[string][]string {
|
||||||
return W.WGetParameters()
|
return W.WGetParameters()
|
||||||
}
|
}
|
||||||
|
@ -177,6 +189,7 @@ type _go_goblog_app_app_pkgs_plugintypes_RenderContext struct {
|
||||||
IValue interface{}
|
IValue interface{}
|
||||||
WGetBlog func() string
|
WGetBlog func() string
|
||||||
WGetPath func() string
|
WGetPath func() string
|
||||||
|
WGetURL func() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetBlog() string {
|
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetBlog() string {
|
||||||
|
@ -185,6 +198,9 @@ func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetBlog() string {
|
||||||
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetPath() string {
|
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetPath() string {
|
||||||
return W.WGetPath()
|
return W.WGetPath()
|
||||||
}
|
}
|
||||||
|
func (W _go_goblog_app_app_pkgs_plugintypes_RenderContext) GetURL() string {
|
||||||
|
return W.WGetURL()
|
||||||
|
}
|
||||||
|
|
||||||
// _go_goblog_app_app_pkgs_plugintypes_SetApp is an interface wrapper for SetApp type
|
// _go_goblog_app_app_pkgs_plugintypes_SetApp is an interface wrapper for SetApp type
|
||||||
type _go_goblog_app_app_pkgs_plugintypes_SetApp struct {
|
type _go_goblog_app_app_pkgs_plugintypes_SetApp struct {
|
||||||
|
@ -225,3 +241,23 @@ type _go_goblog_app_app_pkgs_plugintypes_UI2 struct {
|
||||||
func (W _go_goblog_app_app_pkgs_plugintypes_UI2) RenderWithDocument(renderContext plugintypes.RenderContext, doc *goquery.Document) {
|
func (W _go_goblog_app_app_pkgs_plugintypes_UI2) RenderWithDocument(renderContext plugintypes.RenderContext, doc *goquery.Document) {
|
||||||
W.WRenderWithDocument(renderContext, doc)
|
W.WRenderWithDocument(renderContext, doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _go_goblog_app_app_pkgs_plugintypes_UIFooter is an interface wrapper for UIFooter type
|
||||||
|
type _go_goblog_app_app_pkgs_plugintypes_UIFooter struct {
|
||||||
|
IValue interface{}
|
||||||
|
WRenderFooter func(renderContext plugintypes.RenderContext, doc *goquery.Document)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (W _go_goblog_app_app_pkgs_plugintypes_UIFooter) RenderFooter(renderContext plugintypes.RenderContext, doc *goquery.Document) {
|
||||||
|
W.WRenderFooter(renderContext, doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// _go_goblog_app_app_pkgs_plugintypes_UISummary is an interface wrapper for UISummary type
|
||||||
|
type _go_goblog_app_app_pkgs_plugintypes_UISummary struct {
|
||||||
|
IValue interface{}
|
||||||
|
WRenderSummaryForPost func(renderContext plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (W _go_goblog_app_app_pkgs_plugintypes_UISummary) RenderSummaryForPost(renderContext plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document) {
|
||||||
|
W.WRenderSummaryForPost(renderContext, post, doc)
|
||||||
|
}
|
||||||
|
|
12
plugins.go
12
plugins.go
|
@ -22,6 +22,8 @@ const (
|
||||||
pluginUi2Type = "ui2"
|
pluginUi2Type = "ui2"
|
||||||
pluginExecType = "exec"
|
pluginExecType = "exec"
|
||||||
pluginMiddlewareType = "middleware"
|
pluginMiddlewareType = "middleware"
|
||||||
|
pluginUiSummaryType = "uisummary"
|
||||||
|
pluginUiFooterType = "uifooter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) initPlugins() error {
|
func (a *goBlog) initPlugins() error {
|
||||||
|
@ -37,6 +39,8 @@ func (a *goBlog) initPlugins() error {
|
||||||
pluginUi2Type: reflect.TypeOf((*plugintypes.UI2)(nil)).Elem(),
|
pluginUi2Type: reflect.TypeOf((*plugintypes.UI2)(nil)).Elem(),
|
||||||
pluginExecType: reflect.TypeOf((*plugintypes.Exec)(nil)).Elem(),
|
pluginExecType: reflect.TypeOf((*plugintypes.Exec)(nil)).Elem(),
|
||||||
pluginMiddlewareType: reflect.TypeOf((*plugintypes.Middleware)(nil)).Elem(),
|
pluginMiddlewareType: reflect.TypeOf((*plugintypes.Middleware)(nil)).Elem(),
|
||||||
|
pluginUiSummaryType: reflect.TypeOf((*plugintypes.UISummary)(nil)).Elem(),
|
||||||
|
pluginUiFooterType: reflect.TypeOf((*plugintypes.UIFooter)(nil)).Elem(),
|
||||||
},
|
},
|
||||||
yaegiwrappers.Symbols,
|
yaegiwrappers.Symbols,
|
||||||
subFS,
|
subFS,
|
||||||
|
@ -106,6 +110,10 @@ func (p *post) GetParameters() map[string][]string {
|
||||||
return p.Parameters
|
return p.Parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *post) GetParameter(parameter string) []string {
|
||||||
|
return p.Parameters[parameter]
|
||||||
|
}
|
||||||
|
|
||||||
func (p *post) GetSection() string {
|
func (p *post) GetSection() string {
|
||||||
return p.Section
|
return p.Section
|
||||||
}
|
}
|
||||||
|
@ -117,3 +125,7 @@ func (p *post) GetPublished() string {
|
||||||
func (p *post) GetUpdated() string {
|
func (p *post) GetUpdated() string {
|
||||||
return p.Updated
|
return p.Updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *post) GetContent() string {
|
||||||
|
return p.Content
|
||||||
|
}
|
||||||
|
|
|
@ -22,9 +22,10 @@ func GetPlugin() (
|
||||||
plugintypes.UI2,
|
plugintypes.UI2,
|
||||||
plugintypes.Exec,
|
plugintypes.Exec,
|
||||||
plugintypes.Middleware,
|
plugintypes.Middleware,
|
||||||
|
plugintypes.UISummary,
|
||||||
) {
|
) {
|
||||||
p := &plugin{}
|
p := &plugin{}
|
||||||
return p, p, p, p, p, p
|
return p, p, p, p, p, p, p
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetApp
|
// SetApp
|
||||||
|
@ -97,3 +98,8 @@ func (p *plugin) Handler(next http.Handler) http.Handler {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UISummary
|
||||||
|
func (p *plugin) RenderSummaryForPost(rc plugintypes.RenderContext, post plugintypes.Post, doc *goquery.Document) {
|
||||||
|
doc.Find(".h-entry").AppendHtml(fmt.Sprintf("<p>Summary for post %s on %s</p>", post.GetPath(), rc.GetURL()))
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"go.goblog.app/app/pkgs/plugintypes"
|
"go.goblog.app/app/pkgs/plugintypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.UI2) {
|
func GetPlugin() (plugintypes.SetConfig, plugintypes.UIFooter) {
|
||||||
p := &plugin{}
|
p := &plugin{}
|
||||||
return p, p
|
return p, p
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func (p *plugin) SetConfig(config map[string]any) {
|
||||||
p.config = config
|
p.config = config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
func (p *plugin) RenderFooter(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||||
blog := rc.GetBlog()
|
blog := rc.GetBlog()
|
||||||
if blog == "" {
|
if blog == "" {
|
||||||
fmt.Println("webrings plugin: blog is empty!")
|
fmt.Println("webrings plugin: blog is empty!")
|
||||||
|
|
49
render.go
49
render.go
|
@ -20,6 +20,8 @@ type renderData struct {
|
||||||
WebmentionReceivingEnabled bool
|
WebmentionReceivingEnabled bool
|
||||||
TorUsed bool
|
TorUsed bool
|
||||||
EasterEgg bool
|
EasterEgg bool
|
||||||
|
// For plugins
|
||||||
|
prc *pluginRenderContext
|
||||||
// Not directly accessible
|
// Not directly accessible
|
||||||
app *goBlog
|
app *goBlog
|
||||||
req *http.Request
|
req *http.Request
|
||||||
|
@ -40,36 +42,26 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
||||||
w.Header().Set(contentType, contenttype.HTMLUTF8)
|
w.Header().Set(contentType, contenttype.HTMLUTF8)
|
||||||
// Write status code
|
// Write status code
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
// Render
|
// Render (with UI2 plugins)
|
||||||
renderPipeReader, renderPipeWriter := io.Pipe()
|
renderPipeReader, renderPipeWriter := io.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
|
hb, finish := a.wrapForPlugins(
|
||||||
|
renderPipeWriter,
|
||||||
|
a.getPlugins(pluginUi2Type),
|
||||||
|
func(plugin any, doc *goquery.Document) {
|
||||||
|
plugin.(plugintypes.UI2).RenderWithDocument(data.prc, doc)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
f(hb, data)
|
||||||
|
finish()
|
||||||
_ = renderPipeWriter.Close()
|
_ = renderPipeWriter.Close()
|
||||||
}()
|
}()
|
||||||
// Create render context for plugins
|
// Run io based UI plugins
|
||||||
rc := &pluginRenderContext{
|
|
||||||
blog: data.BlogString,
|
|
||||||
path: r.URL.Path,
|
|
||||||
}
|
|
||||||
// Run UI plugins
|
|
||||||
pluginPipeReader, pluginPipeWriter := io.Pipe()
|
pluginPipeReader, pluginPipeWriter := io.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
a.chainUiPlugins(a.getPlugins(pluginUiType), rc, renderPipeReader, pluginPipeWriter)
|
a.chainUiPlugins(a.getPlugins(pluginUiType), data.prc, renderPipeReader, pluginPipeWriter)
|
||||||
_ = pluginPipeWriter.Close()
|
_ = pluginPipeWriter.Close()
|
||||||
}()
|
}()
|
||||||
// Run UI2 plugins
|
|
||||||
ui2Plugins := a.getPlugins(pluginUi2Type)
|
|
||||||
if len(ui2Plugins) > 0 {
|
|
||||||
doc, _ := goquery.NewDocumentFromReader(pluginPipeReader)
|
|
||||||
_ = pluginPipeReader.Close()
|
|
||||||
for _, plugin := range ui2Plugins {
|
|
||||||
plugin.(plugintypes.UI2).RenderWithDocument(rc, doc)
|
|
||||||
}
|
|
||||||
pluginPipeReader, pluginPipeWriter = io.Pipe()
|
|
||||||
go func() {
|
|
||||||
_ = pluginPipeWriter.CloseWithError(goquery.Render(pluginPipeWriter, doc.Selection))
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
// Return minified HTML
|
// Return minified HTML
|
||||||
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
|
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
|
||||||
}
|
}
|
||||||
|
@ -125,6 +117,14 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
||||||
if ee := a.cfg.EasterEgg; ee != nil && ee.Enabled {
|
if ee := a.cfg.EasterEgg; ee != nil && ee.Enabled {
|
||||||
data.EasterEgg = true
|
data.EasterEgg = true
|
||||||
}
|
}
|
||||||
|
// Plugins
|
||||||
|
if data.prc == nil {
|
||||||
|
data.prc = &pluginRenderContext{
|
||||||
|
blog: data.BlogString,
|
||||||
|
path: r.URL.Path,
|
||||||
|
url: a.getFullAddress(r.URL.Path),
|
||||||
|
}
|
||||||
|
}
|
||||||
// Data
|
// Data
|
||||||
if data.Data == nil {
|
if data.Data == nil {
|
||||||
data.Data = map[string]any{}
|
data.Data = map[string]any{}
|
||||||
|
@ -136,6 +136,7 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
||||||
type pluginRenderContext struct {
|
type pluginRenderContext struct {
|
||||||
blog string
|
blog string
|
||||||
path string
|
path string
|
||||||
|
url string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *pluginRenderContext) GetBlog() string {
|
func (d *pluginRenderContext) GetBlog() string {
|
||||||
|
@ -145,3 +146,7 @@ func (d *pluginRenderContext) GetBlog() string {
|
||||||
func (d *pluginRenderContext) GetPath() string {
|
func (d *pluginRenderContext) GetPath() string {
|
||||||
return d.path
|
return d.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *pluginRenderContext) GetURL() string {
|
||||||
|
return d.url
|
||||||
|
}
|
||||||
|
|
31
ui.go
31
ui.go
|
@ -140,34 +140,7 @@ func (a *goBlog) renderBase(hb *htmlbuilder.HtmlBuilder, rd *renderData, title,
|
||||||
main(hb)
|
main(hb)
|
||||||
}
|
}
|
||||||
// Footer
|
// Footer
|
||||||
hb.WriteElementOpen("footer")
|
a.renderFooter(hb, rd)
|
||||||
// Footer menu
|
|
||||||
if fm, ok := rd.Blog.Menus["footer"]; ok {
|
|
||||||
hb.WriteElementOpen("nav")
|
|
||||||
for i, item := range fm.Items {
|
|
||||||
if i > 0 {
|
|
||||||
hb.WriteUnescaped(" • ")
|
|
||||||
}
|
|
||||||
hb.WriteElementOpen("a", "href", item.Link)
|
|
||||||
hb.WriteEscaped(a.renderMdTitle(item.Title))
|
|
||||||
hb.WriteElementClose("a")
|
|
||||||
}
|
|
||||||
hb.WriteElementClose("nav")
|
|
||||||
}
|
|
||||||
// Copyright
|
|
||||||
hb.WriteElementOpen("p", "translate", "no")
|
|
||||||
hb.WriteUnescaped("© ")
|
|
||||||
hb.WriteEscaped(time.Now().Format("2006"))
|
|
||||||
hb.WriteUnescaped(" ")
|
|
||||||
if user != nil && user.Name != "" {
|
|
||||||
hb.WriteEscaped(user.Name)
|
|
||||||
} else {
|
|
||||||
hb.WriteEscaped(renderedBlogTitle)
|
|
||||||
}
|
|
||||||
hb.WriteElementClose("p")
|
|
||||||
// Tor
|
|
||||||
a.renderTorNotice(hb, rd)
|
|
||||||
hb.WriteElementClose("footer")
|
|
||||||
// Easter egg
|
// Easter egg
|
||||||
if rd.EasterEgg {
|
if rd.EasterEgg {
|
||||||
hb.WriteElementOpen("script", "src", a.assetFileName("js/easteregg.js"), "defer", "")
|
hb.WriteElementOpen("script", "src", a.assetFileName("js/easteregg.js"), "defer", "")
|
||||||
|
@ -409,7 +382,7 @@ func (a *goBlog) renderIndex(hb *htmlbuilder.HtmlBuilder, rd *renderData) {
|
||||||
if id.posts != nil && len(id.posts) > 0 {
|
if id.posts != nil && len(id.posts) > 0 {
|
||||||
// Posts
|
// Posts
|
||||||
for _, p := range id.posts {
|
for _, p := range id.posts {
|
||||||
a.renderSummary(hb, rd.Blog, p, id.summaryTemplate)
|
a.renderSummary(hb, rd, rd.Blog, p, id.summaryTemplate)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No posts
|
// No posts
|
||||||
|
|
|
@ -5,8 +5,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||||
|
"go.goblog.app/app/pkgs/plugintypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type summaryTyp string
|
type summaryTyp string
|
||||||
|
@ -17,13 +19,18 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// post summary on index pages
|
// post summary on index pages
|
||||||
func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *post, typ summaryTyp) {
|
func (a *goBlog) renderSummary(origHb *htmlbuilder.HtmlBuilder, rd *renderData, bc *configBlog, p *post, typ summaryTyp) {
|
||||||
if bc == nil || p == nil {
|
if bc == nil || p == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if typ == "" {
|
if typ == "" {
|
||||||
typ = defaultSummary
|
typ = defaultSummary
|
||||||
}
|
}
|
||||||
|
// Plugin handling
|
||||||
|
hb, finish := a.wrapForPlugins(origHb, a.getPlugins(pluginUiSummaryType), func(plugin any, doc *goquery.Document) {
|
||||||
|
plugin.(plugintypes.UISummary).RenderSummaryForPost(rd.prc, p, doc)
|
||||||
|
})
|
||||||
|
defer finish()
|
||||||
// Start article
|
// Start article
|
||||||
hb.WriteElementOpen("article", "class", "h-entry border-bottom")
|
hb.WriteElementOpen("article", "class", "h-entry border-bottom")
|
||||||
if p.Priority > 0 {
|
if p.Priority > 0 {
|
||||||
|
@ -674,3 +681,40 @@ func (a *goBlog) renderUserSettings(hb *htmlbuilder.HtmlBuilder, rd *renderData,
|
||||||
)
|
)
|
||||||
hb.WriteElementClose("form")
|
hb.WriteElementClose("form")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) renderFooter(origHb *htmlbuilder.HtmlBuilder, rd *renderData) {
|
||||||
|
// Wrap plugins
|
||||||
|
hb, finish := a.wrapForPlugins(origHb, a.getPlugins(pluginUiFooterType), func(plugin any, doc *goquery.Document) {
|
||||||
|
plugin.(plugintypes.UIFooter).RenderFooter(rd.prc, doc)
|
||||||
|
})
|
||||||
|
defer finish()
|
||||||
|
// Render footer
|
||||||
|
hb.WriteElementOpen("footer")
|
||||||
|
// Footer menu
|
||||||
|
if fm, ok := rd.Blog.Menus["footer"]; ok {
|
||||||
|
hb.WriteElementOpen("nav")
|
||||||
|
for i, item := range fm.Items {
|
||||||
|
if i > 0 {
|
||||||
|
hb.WriteUnescaped(" • ")
|
||||||
|
}
|
||||||
|
hb.WriteElementOpen("a", "href", item.Link)
|
||||||
|
hb.WriteEscaped(a.renderMdTitle(item.Title))
|
||||||
|
hb.WriteElementClose("a")
|
||||||
|
}
|
||||||
|
hb.WriteElementClose("nav")
|
||||||
|
}
|
||||||
|
// Copyright
|
||||||
|
hb.WriteElementOpen("p", "translate", "no")
|
||||||
|
hb.WriteUnescaped("© ")
|
||||||
|
hb.WriteEscaped(time.Now().Format("2006"))
|
||||||
|
hb.WriteUnescaped(" ")
|
||||||
|
if user := a.cfg.User; user != nil && user.Name != "" {
|
||||||
|
hb.WriteEscaped(user.Name)
|
||||||
|
} else {
|
||||||
|
hb.WriteEscaped(a.renderMdTitle(rd.Blog.Title))
|
||||||
|
}
|
||||||
|
hb.WriteElementClose("p")
|
||||||
|
// Tor
|
||||||
|
a.renderTorNotice(hb, rd)
|
||||||
|
hb.WriteElementClose("footer")
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (*goBlog) wrapForPlugins(
|
||||||
|
originalWriter io.Writer,
|
||||||
|
plugins []any,
|
||||||
|
pluginRender func(plugin any, doc *goquery.Document),
|
||||||
|
) (wrappedHb *htmlbuilder.HtmlBuilder, finish func()) {
|
||||||
|
if len(plugins) == 0 {
|
||||||
|
// No plugins, nothing to wrap
|
||||||
|
if hb, ok := (originalWriter).(*htmlbuilder.HtmlBuilder); ok {
|
||||||
|
return hb, func() {}
|
||||||
|
}
|
||||||
|
return htmlbuilder.NewHtmlBuilder(originalWriter), func() {}
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
finish = func() {
|
||||||
|
_ = pw.Close()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
doc, err := goquery.NewDocumentFromReader(pr)
|
||||||
|
_ = pr.CloseWithError(err)
|
||||||
|
for _, plugin := range plugins {
|
||||||
|
pluginRender(plugin, doc)
|
||||||
|
}
|
||||||
|
_ = goquery.Render(originalWriter, doc.Selection)
|
||||||
|
}()
|
||||||
|
return htmlbuilder.NewHtmlBuilder(pw), finish
|
||||||
|
}
|
Loading…
Reference in New Issue