From 812cff1941f77e6c9e3eb9f7925b8b4c6f256a59 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Mon, 13 Feb 2023 20:22:42 +0100 Subject: [PATCH] Add UI2 plugin type for more resource efficiency and rewrite the UI plugins to use the new type --- activityPub.go | 2 +- docs/plugins.md | 1 + pkgs/plugintypes/plugins.go | 9 +++++++ .../github_com-carlmjohnson-requests.go | 1 + .../go_goblog_app-app-pkgs-plugintypes.go | 13 ++++++++++ plugins.go | 2 ++ plugins/demo/src/demo/demo.go | 16 +++++++++++-- .../src/syndication/syndication.go | 19 ++------------- plugins/webrings/src/webrings/webrings.go | 11 ++------- render.go | 24 +++++++++++++++---- 10 files changed, 65 insertions(+), 33 deletions(-) diff --git a/activityPub.go b/activityPub.go index 4e3e6fc..9059ea8 100644 --- a/activityPub.go +++ b/activityPub.go @@ -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 diff --git a/docs/plugins.md b/docs/plugins.md index 253f599..19be7fa 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -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. diff --git a/pkgs/plugintypes/plugins.go b/pkgs/plugintypes/plugins.go index 2590873..809675d 100644 --- a/pkgs/plugintypes/plugins.go +++ b/pkgs/plugintypes/plugins.go @@ -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) +} diff --git a/pkgs/yaegiwrappers/github_com-carlmjohnson-requests.go b/pkgs/yaegiwrappers/github_com-carlmjohnson-requests.go index 5b544f0..e011911 100644 --- a/pkgs/yaegiwrappers/github_com-carlmjohnson-requests.go +++ b/pkgs/yaegiwrappers/github_com-carlmjohnson-requests.go @@ -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(), diff --git a/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go b/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go index d48c684..a769630 100644 --- a/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go +++ b/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go @@ -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) +} diff --git a/plugins.go b/plugins.go index f451a7e..91c16b4 100644 --- a/plugins.go +++ b/plugins.go @@ -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(), }, diff --git a/plugins/demo/src/demo/demo.go b/plugins/demo/src/demo/demo.go index 5087d2c..3119ccf 100644 --- a/plugins/demo/src/demo/demo.go +++ b/plugins/demo/src/demo/demo.go @@ -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!") diff --git a/plugins/syndication/src/syndication/syndication.go b/plugins/syndication/src/syndication/syndication.go index 20d698f..fd81319 100644 --- a/plugins/syndication/src/syndication/syndication.go +++ b/plugins/syndication/src/syndication/syndication.go @@ -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) } diff --git a/plugins/webrings/src/webrings/webrings.go b/plugins/webrings/src/webrings/webrings.go index 719c8ee..95bceed 100644 --- a/plugins/webrings/src/webrings/webrings.go +++ b/plugins/webrings/src/webrings/webrings.go @@ -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) { diff --git a/render.go b/render.go index 17c6291..a722e66 100644 --- a/render.go +++ b/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)) }