mirror of https://github.com/jlelse/GoBlog
Add UI2 plugin type for more resource efficiency and rewrite the UI plugins to use the new type
This commit is contained in:
parent
e1914d8610
commit
812cff1941
|
@ -162,7 +162,7 @@ func (a *goBlog) apCheckMentions(p *post) {
|
|||
}
|
||||
apc := a.apHttpClients[p.Blog]
|
||||
mentions := []string{}
|
||||
for _, link := range lo.Uniq(links) {
|
||||
for _, link := range links {
|
||||
act, err := apc.Actor(context.Background(), ap.IRI(link))
|
||||
if err != nil || act == nil || act.Type != ap.PersonType {
|
||||
continue
|
||||
|
|
|
@ -33,6 +33,7 @@ You need to specify the path to the plugin (remember to mount the path to your G
|
|||
- `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
|
||||
- `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
|
||||
|
||||
More types will be added later. Any plugin can implement multiple types, see the demo plugin as example.
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package plugintypes
|
|||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// SetApp is used to allow GoBlog set its app instance to be accessible by the plugin.
|
||||
|
@ -34,3 +36,10 @@ type UI interface {
|
|||
// The renderContext provides information such as the path of the request or the blog name.
|
||||
Render(renderContext RenderContext, rendered io.Reader, modified io.Writer)
|
||||
}
|
||||
|
||||
// UI2 plugins get called when rendering HTML.
|
||||
type UI2 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 HTML.
|
||||
RenderWithDocument(renderContext RenderContext, doc *goquery.Document)
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ func init() {
|
|||
"ErrorJSON": reflect.ValueOf(requests.ErrorJSON),
|
||||
"GzipConfig": reflect.ValueOf(requests.GzipConfig),
|
||||
"HasStatusErr": reflect.ValueOf(requests.HasStatusErr),
|
||||
"LogTransport": reflect.ValueOf(requests.LogTransport),
|
||||
"MaxFollow": reflect.ValueOf(requests.MaxFollow),
|
||||
"NewCookieJar": reflect.ValueOf(requests.NewCookieJar),
|
||||
"NoFollow": reflect.ValueOf(&requests.NoFollow).Elem(),
|
||||
|
|
|
@ -27,6 +27,7 @@ package yaegiwrappers
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -45,6 +46,7 @@ func init() {
|
|||
"SetApp": reflect.ValueOf((*plugintypes.SetApp)(nil)),
|
||||
"SetConfig": reflect.ValueOf((*plugintypes.SetConfig)(nil)),
|
||||
"UI": reflect.ValueOf((*plugintypes.UI)(nil)),
|
||||
"UI2": reflect.ValueOf((*plugintypes.UI2)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_App": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_App)(nil)),
|
||||
|
@ -56,6 +58,7 @@ func init() {
|
|||
"_SetApp": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_SetApp)(nil)),
|
||||
"_SetConfig": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_SetConfig)(nil)),
|
||||
"_UI": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI)(nil)),
|
||||
"_UI2": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_UI2)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,3 +207,13 @@ type _go_goblog_app_app_pkgs_plugintypes_UI struct {
|
|||
func (W _go_goblog_app_app_pkgs_plugintypes_UI) Render(renderContext plugintypes.RenderContext, rendered io.Reader, modified io.Writer) {
|
||||
W.WRender(renderContext, rendered, modified)
|
||||
}
|
||||
|
||||
// _go_goblog_app_app_pkgs_plugintypes_UI2 is an interface wrapper for UI2 type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_UI2 struct {
|
||||
IValue interface{}
|
||||
WRenderWithDocument func(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)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const (
|
|||
pluginSetAppType = "setapp"
|
||||
pluginSetConfigType = "setconfig"
|
||||
pluginUiType = "ui"
|
||||
pluginUi2Type = "ui2"
|
||||
pluginExecType = "exec"
|
||||
pluginMiddlewareType = "middleware"
|
||||
)
|
||||
|
@ -32,6 +33,7 @@ func (a *goBlog) initPlugins() error {
|
|||
pluginSetAppType: reflect.TypeOf((*plugintypes.SetApp)(nil)).Elem(),
|
||||
pluginSetConfigType: reflect.TypeOf((*plugintypes.SetConfig)(nil)).Elem(),
|
||||
pluginUiType: reflect.TypeOf((*plugintypes.UI)(nil)).Elem(),
|
||||
pluginUi2Type: reflect.TypeOf((*plugintypes.UI2)(nil)).Elem(),
|
||||
pluginExecType: reflect.TypeOf((*plugintypes.Exec)(nil)).Elem(),
|
||||
pluginMiddlewareType: reflect.TypeOf((*plugintypes.Middleware)(nil)).Elem(),
|
||||
},
|
||||
|
|
|
@ -19,11 +19,12 @@ type plugin struct {
|
|||
func GetPlugin() (
|
||||
plugintypes.SetApp, plugintypes.SetConfig,
|
||||
plugintypes.UI,
|
||||
plugintypes.UI2,
|
||||
plugintypes.Exec,
|
||||
plugintypes.Middleware,
|
||||
) {
|
||||
p := &plugin{}
|
||||
return p, p, p, p, p
|
||||
return p, p, p, p, p, p
|
||||
}
|
||||
|
||||
// SetApp
|
||||
|
@ -40,7 +41,7 @@ func (p *plugin) SetConfig(config map[string]any) {
|
|||
func (*plugin) Render(_ plugintypes.RenderContext, rendered io.Reader, modified io.Writer) {
|
||||
doc, err := goquery.NewDocumentFromReader(rendered)
|
||||
if err != nil {
|
||||
fmt.Println("demoui plugin: " + err.Error())
|
||||
fmt.Println("demo plugin: " + err.Error())
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
|
@ -53,6 +54,17 @@ func (*plugin) Render(_ plugintypes.RenderContext, rendered io.Reader, modified
|
|||
_ = goquery.Render(modified, doc.Selection)
|
||||
}
|
||||
|
||||
// UI
|
||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
hb := htmlbuilder.NewHtmlBuilder(buf)
|
||||
hb.WriteElementOpen("p")
|
||||
hb.WriteEscaped("Second end of post content")
|
||||
hb.WriteElementClose("p")
|
||||
doc.Find("main.h-entry article div.e-content").AppendHtml(buf.String())
|
||||
}
|
||||
|
||||
// Exec
|
||||
func (p *plugin) Exec() {
|
||||
fmt.Println("Hello World from the demo plugin!")
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package syndication
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
|
@ -15,7 +12,7 @@ type plugin struct {
|
|||
parameterName string
|
||||
}
|
||||
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.SetApp, plugintypes.UI) {
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.SetApp, plugintypes.UI2) {
|
||||
p := &plugin{}
|
||||
return p, p, p
|
||||
}
|
||||
|
@ -33,24 +30,13 @@ func (p *plugin) SetConfig(config map[string]any) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *plugin) Render(rc plugintypes.RenderContext, rendered io.Reader, modified io.Writer) {
|
||||
def := func() {
|
||||
_, _ = io.Copy(modified, rendered)
|
||||
}
|
||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
post, err := p.app.GetPost(rc.GetPath())
|
||||
if err != nil || post == nil {
|
||||
def()
|
||||
return
|
||||
}
|
||||
syndicationLinks, ok := post.GetParameters()[p.parameterName]
|
||||
if !ok || len(syndicationLinks) == 0 {
|
||||
def()
|
||||
return
|
||||
}
|
||||
doc, err := goquery.NewDocumentFromReader(rendered)
|
||||
if err != nil {
|
||||
fmt.Println("syndication plugin: " + err.Error())
|
||||
def()
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
|
@ -61,5 +47,4 @@ func (p *plugin) Render(rc plugintypes.RenderContext, rendered io.Reader, modifi
|
|||
hb.WriteElementClose("data")
|
||||
}
|
||||
doc.Find("main.h-entry article").AppendHtml(buf.String())
|
||||
_ = goquery.Render(modified, doc.Selection)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package webrings
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
|
@ -10,7 +9,7 @@ import (
|
|||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.UI) {
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.UI2) {
|
||||
p := &plugin{}
|
||||
return p, p
|
||||
}
|
||||
|
@ -23,17 +22,12 @@ func (p *plugin) SetConfig(config map[string]any) {
|
|||
p.config = config
|
||||
}
|
||||
|
||||
func (p *plugin) Render(rc plugintypes.RenderContext, rendered io.Reader, modified io.Writer) {
|
||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
blog := rc.GetBlog()
|
||||
if blog == "" {
|
||||
fmt.Println("webrings plugin: blog is empty!")
|
||||
return
|
||||
}
|
||||
doc, err := goquery.NewDocumentFromReader(rendered)
|
||||
if err != nil {
|
||||
fmt.Println("webrings plugin: " + err.Error())
|
||||
return
|
||||
}
|
||||
if blogWebringsAny, ok := p.config[blog]; ok {
|
||||
if blogWebrings, ok := blogWebringsAny.([]any); ok {
|
||||
buf := bufferpool.Get()
|
||||
|
@ -74,7 +68,6 @@ func (p *plugin) Render(rc plugintypes.RenderContext, rendered io.Reader, modifi
|
|||
}
|
||||
}
|
||||
}
|
||||
_ = goquery.Render(modified, doc.Selection)
|
||||
}
|
||||
|
||||
func unwrapToString(o any) (string, bool) {
|
||||
|
|
24
render.go
24
render.go
|
@ -4,6 +4,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
|
@ -45,15 +46,30 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
|||
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
|
||||
_ = renderPipeWriter.Close()
|
||||
}()
|
||||
// Create render context for plugins
|
||||
rc := &pluginRenderContext{
|
||||
blog: data.BlogString,
|
||||
path: r.URL.Path,
|
||||
}
|
||||
// Run UI plugins
|
||||
pluginPipeReader, pluginPipeWriter := io.Pipe()
|
||||
go func() {
|
||||
a.chainUiPlugins(a.getPlugins(pluginUiType), &pluginRenderContext{
|
||||
blog: data.BlogString,
|
||||
path: r.URL.Path,
|
||||
}, renderPipeReader, pluginPipeWriter)
|
||||
a.chainUiPlugins(a.getPlugins(pluginUiType), rc, renderPipeReader, pluginPipeWriter)
|
||||
_ = 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
|
||||
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue