From e1edd5c18ce3507993d67e0f1615710e74f95bdd Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Mon, 31 Oct 2022 10:52:35 +0100 Subject: [PATCH] Add webrings plugin --- config.go | 5 ++ docs/plugins.md | 18 ++++- pkgs/plugintypes/goblog.go | 14 ++++ .../go_goblog_app-app-pkgs-plugintypes.go | 25 ++++++ plugins.go | 16 ++++ plugins/webrings/src/webrings/webrings.go | 80 +++++++++++++++++++ ui.go | 50 ++++++------ 7 files changed, 183 insertions(+), 25 deletions(-) create mode 100644 plugins/webrings/src/webrings/webrings.go diff --git a/config.go b/config.go index 802d939..146f171 100644 --- a/config.go +++ b/config.go @@ -95,6 +95,7 @@ type configBlog struct { Map *configGeoMap `mapstructure:"map"` Contact *configContact `mapstructure:"contact"` Announcement *configAnnouncement `mapstructure:"announcement"` + name string // Configs read from database hideOldContentWarning bool hideShareButton bool @@ -429,6 +430,10 @@ func (a *goBlog) initConfig(logging bool) error { if a.cfg.Blogs[a.cfg.DefaultBlog] == nil { return errors.New("default blog does not exist") } + // Set name attribute for every blog + for name, blog := range a.cfg.Blogs { + blog.name = name + } // Check media storage config if ms := a.cfg.Micropub.MediaStorage; ms != nil && ms.MediaURL != "" { ms.MediaURL = strings.TrimSuffix(ms.MediaURL, "/") diff --git a/docs/plugins.md b/docs/plugins.md index ebcb781..8b84461 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -41,4 +41,20 @@ Adds hidden `u-syndication` `data` elements to post page when the configured pos #### Config -`parameter` (string): Name for the post parameter containing the syndication links. \ No newline at end of file +`parameter` (string): Name for the post parameter containing the syndication links. + +### Webrings (plugins/webrings) + +Adds webring links to the bottom of the blog footer to easily participate in webrings. + +#### Config + +You can add webring links like this: + +```yaml +config: + default: # Name of the blog + - title: Webring # Title to show for the webring + prev: https://example.com/ # Link to previous webring site + next: https://example.net/ # Link to next webring site +``` \ No newline at end of file diff --git a/pkgs/plugintypes/goblog.go b/pkgs/plugintypes/goblog.go index 8cb8c81..79f8d02 100644 --- a/pkgs/plugintypes/goblog.go +++ b/pkgs/plugintypes/goblog.go @@ -27,6 +27,11 @@ type Post interface { GetParameters() map[string][]string } +// Blog +type Blog interface { + GetBlog() string +} + // RenderType type RenderType string @@ -46,3 +51,12 @@ type PostRenderData interface { RenderData GetPost() Post } + +// Render footer element on every blog page, data = BlogRenderData +const BlogFooterRenderType RenderType = "blog-footer" + +// BlogRenderData is RenderData containing a Blog +type BlogRenderData interface { + RenderData + GetBlog() Blog +} diff --git a/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go b/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go index 29980f5..4b0ba44 100644 --- a/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go +++ b/pkgs/yaegiwrappers/go_goblog_app-app-pkgs-plugintypes.go @@ -36,10 +36,13 @@ import ( func init() { Symbols["go.goblog.app/app/pkgs/plugintypes/plugintypes"] = map[string]reflect.Value{ // function, constant and variable definitions + "BlogFooterRenderType": reflect.ValueOf(plugintypes.BlogFooterRenderType), "PostMainElementRenderType": reflect.ValueOf(plugintypes.PostMainElementRenderType), // type definitions "App": reflect.ValueOf((*plugintypes.App)(nil)), + "Blog": reflect.ValueOf((*plugintypes.Blog)(nil)), + "BlogRenderData": reflect.ValueOf((*plugintypes.BlogRenderData)(nil)), "Database": reflect.ValueOf((*plugintypes.Database)(nil)), "Exec": reflect.ValueOf((*plugintypes.Exec)(nil)), "Middleware": reflect.ValueOf((*plugintypes.Middleware)(nil)), @@ -54,6 +57,8 @@ func init() { // interface wrapper definitions "_App": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_App)(nil)), + "_Blog": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_Blog)(nil)), + "_BlogRenderData": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_BlogRenderData)(nil)), "_Database": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_Database)(nil)), "_Exec": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_Exec)(nil)), "_Middleware": reflect.ValueOf((*_go_goblog_app_app_pkgs_plugintypes_Middleware)(nil)), @@ -76,6 +81,26 @@ func (W _go_goblog_app_app_pkgs_plugintypes_App) GetDatabase() plugintypes.Datab return W.WGetDatabase() } +// _go_goblog_app_app_pkgs_plugintypes_Blog is an interface wrapper for Blog type +type _go_goblog_app_app_pkgs_plugintypes_Blog struct { + IValue interface{} + WGetBlog func() string +} + +func (W _go_goblog_app_app_pkgs_plugintypes_Blog) GetBlog() string { + return W.WGetBlog() +} + +// _go_goblog_app_app_pkgs_plugintypes_BlogRenderData is an interface wrapper for BlogRenderData type +type _go_goblog_app_app_pkgs_plugintypes_BlogRenderData struct { + IValue interface{} + WGetBlog func() plugintypes.Blog +} + +func (W _go_goblog_app_app_pkgs_plugintypes_BlogRenderData) GetBlog() plugintypes.Blog { + return W.WGetBlog() +} + // _go_goblog_app_app_pkgs_plugintypes_Database is an interface wrapper for Database type type _go_goblog_app_app_pkgs_plugintypes_Database struct { IValue interface{} diff --git a/plugins.go b/plugins.go index c6460e2..f907c46 100644 --- a/plugins.go +++ b/plugins.go @@ -72,3 +72,19 @@ func (d *pluginPostRenderData) GetPost() plugintypes.Post { func (p *post) pluginRenderData() plugintypes.PostRenderData { return &pluginPostRenderData{p: p} } + +func (b *configBlog) GetBlog() string { + return b.name +} + +type pluginBlogRenderData struct { + b *configBlog +} + +func (d *pluginBlogRenderData) GetBlog() plugintypes.Blog { + return d.b +} + +func (b *configBlog) pluginRenderData() plugintypes.BlogRenderData { + return &pluginBlogRenderData{b: b} +} diff --git a/plugins/webrings/src/webrings/webrings.go b/plugins/webrings/src/webrings/webrings.go new file mode 100644 index 0000000..61bccec --- /dev/null +++ b/plugins/webrings/src/webrings/webrings.go @@ -0,0 +1,80 @@ +package webrings + +import ( + "fmt" + + "go.goblog.app/app/pkgs/htmlbuilder" + "go.goblog.app/app/pkgs/plugintypes" +) + +func GetPlugin() plugintypes.UI { + return &plugin{} +} + +type plugin struct { + config map[string]any +} + +func (*plugin) SetApp(_ plugintypes.App) { + // Ignore +} + +func (p *plugin) SetConfig(config map[string]any) { + p.config = config +} + +func (p *plugin) Render(hb *htmlbuilder.HtmlBuilder, t plugintypes.RenderType, data plugintypes.RenderData, render plugintypes.RenderNextFunc) { + render(hb) + if t == plugintypes.BlogFooterRenderType { + bd, ok := data.(plugintypes.BlogRenderData) + if !ok { + fmt.Println("webrings plugin: data is not BlogRenderData!") + return + } + blogData := bd.GetBlog() + if blogData == nil { + fmt.Println("webrings plugin: blog is nil!") + return + } + blog := blogData.GetBlog() + if blog == "" { + fmt.Println("webrings plugin: blog is empty!") + return + } + if blogWebringsAny, ok := p.config[blog]; ok { + if blogWebrings, ok := blogWebringsAny.([]any); ok { + for _, webringAny := range blogWebrings { + if webring, ok := webringAny.(map[string]any); ok { + title, titleOk := unwrapToString(webring["title"]) + prev, prevOk := unwrapToString(webring["prev"]) + next, nextOk := unwrapToString(webring["next"]) + if titleOk && (prevOk || nextOk) { + hb.WriteElementOpen("p") + if prevOk { + hb.WriteElementOpen("a", "href", prev) + hb.WriteEscaped("←") + hb.WriteElementClose("a") + } + hb.WriteEscaped(" " + title + " ") + if nextOk { + hb.WriteElementOpen("a", "href", next) + hb.WriteEscaped("→") + hb.WriteElementClose("a") + } + hb.WriteElementClose("p") + } + } + } + } + } + + } +} + +func unwrapToString(o any) (string, bool) { + if o == nil { + return "", false + } + s, ok := o.(string) + return s, ok && s != "" +} diff --git a/ui.go b/ui.go index d3c84cf..61ea85c 100644 --- a/ui.go +++ b/ui.go @@ -162,32 +162,34 @@ func (a *goBlog) renderBase(hb *htmlbuilder.HtmlBuilder, rd *renderData, title, } // 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(" • ") + a.renderWithPlugins(hb, plugintypes.BlogFooterRenderType, rd.Blog.pluginRenderData(), func(hb *htmlbuilder.HtmlBuilder) { + // 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.WriteElementOpen("a", "href", item.Link) - hb.WriteEscaped(a.renderMdTitle(item.Title)) - hb.WriteElementClose("a") + hb.WriteElementClose("nav") } - 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) + // 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 if rd.EasterEgg {