diff --git a/admin/settings.go b/admin/settings.go index cb78e87..d08fe8b 100644 --- a/admin/settings.go +++ b/admin/settings.go @@ -60,6 +60,7 @@ var settingGroups = []settingGroupDef{ {Key: "favicon", Label: "Favicon", Help: "Icon shown in the browser tab."}, {Key: "custom_header_code", Label: "Custom header code", Help: "HTML added to the of every page."}, {Key: "custom_footer_code", Label: "Custom footer code", Help: "HTML added before on every page."}, + {Key: "show_powered_by", Label: "Show \"Powered by goblog\"", Help: "Credit line with a link to goblog.live in the footer."}, }}, {ID: "comments", Title: "Comments", Settings: []settingMeta{ {Key: "comments_require_login", Label: "Require login to comment", Help: "When off, anyone can leave a comment."}, diff --git a/admin/settings_test.go b/admin/settings_test.go index eaafb24..a0ac91b 100644 --- a/admin/settings_test.go +++ b/admin/settings_test.go @@ -14,7 +14,7 @@ var seededSettings = map[string]string{ "landing_page_image": "file", "favicon": "file", "custom_header_code": "textarea", "custom_footer_code": "textarea", "theme": "text", "robots_tag": "text", "site_url": "text", "comments_require_login": "checkbox", "plugin_directory_url": "text", "theme_directory_url": "text", - "site_description": "textarea", "site_image": "file", + "site_description": "textarea", "site_image": "file", "show_powered_by": "checkbox", } func seededSettingMap() map[string]blog.Setting { diff --git a/plugins/docs/content/writing-a-theme.md b/plugins/docs/content/writing-a-theme.md index fc2108d..f34ca42 100644 --- a/plugins/docs/content/writing-a-theme.md +++ b/plugins/docs/content/writing-a-theme.md @@ -8,7 +8,7 @@ This page is about the templates: how they are loaded, what data they get, how t At startup, and again whenever the `theme` setting changes, goblog builds one Go [`html/template`](https://pkg.go.dev/html/template) set in three layers, each parsed on top of the last: -1. **Shared:** `templates/shared/*.html` — partials goblog owns because they carry behaviour rather than look. `_head.html` defines `_head`, the `` every page starts with (meta tags, the CDN stylesheets, `/theme/css/goblog.css`, the `custom_header_code` setting and the plugins' head HTML). `_search_results.html` defines `_search_results`, the search page's result list: the count, the no-results message, and one entry per result — posts and whatever installed plugins contribute (the plugin and theme directories, the docs). A theme includes it from `search.html` and never needs to know which kinds of result exist; a release that adds a new kind shows up on every theme. +1. **Shared:** `templates/shared/*.html` — partials goblog owns because they carry behaviour rather than look. `_head.html` defines `_head`, the `` every page starts with (meta tags, the CDN stylesheets, `/theme/css/goblog.css`, the `custom_header_code` setting and the plugins' head HTML). `_search_results.html` defines `_search_results`, the search page's result list: the count, the no-results message, and one entry per result — posts and whatever installed plugins contribute (the plugin and theme directories, the docs). A theme includes it from `search.html` and never needs to know which kinds of result exist; a release that adds a new kind shows up on every theme. `_powered_by.html` defines `_powered_by`, the "Powered by goblog" credit: a `` holding the text and the link, or nothing when the site has switched the `show_powered_by` setting off. Include it from your `footer.html` inside the element you style the line with, rather than writing the link yourself, so the setting works on your theme too. 2. **Default:** `themes/default/templates/*.html`. 3. **Yours:** `/templates/*.html`. diff --git a/plugins/docs/content_test.go b/plugins/docs/content_test.go index 27947ef..089a668 100644 --- a/plugins/docs/content_test.go +++ b/plugins/docs/content_test.go @@ -131,7 +131,7 @@ func knownIdentifiers() map[string]bool { // Setting keys seeded in tools/migrate.go and read via // blog.SettingValue / ctx.Settings elsewhere. "plugin_directory_url", "theme_directory_url", "refresh_minutes", "github_token", - "site_url", "site_description", "site_image", "custom_header_code", "custom_footer_code", + "site_url", "site_description", "site_image", "custom_header_code", "custom_footer_code", "show_powered_by", // comments_require_login is both a template data key (blog/blog.go // post.html render calls) and the setting blog.CommentsRequireLogin diff --git a/templates/shared/_powered_by.html b/templates/shared/_powered_by.html new file mode 100644 index 0000000..0c93f30 --- /dev/null +++ b/templates/shared/_powered_by.html @@ -0,0 +1,11 @@ +{{/* +_powered_by renders the "Powered by goblog" credit, unless the +show_powered_by setting is off. A theme's footer includes it with +{{ template "_powered_by" . }} inside whatever element it styles the line +with, so the link and the setting live here rather than in every theme. +The wizard pages have no settings yet and always show it. +*/}} +{{ define "_powered_by" -}} +{{ $show := true }}{{ with .settings }}{{ if eq (index . "show_powered_by").Value "false" }}{{ $show = false }}{{ end }}{{ end -}} +{{ if $show }}Powered by goblog {{ .version }}{{ end }} +{{- end }} diff --git a/templates_powered_by_test.go b/templates_powered_by_test.go new file mode 100644 index 0000000..ddd3dcd --- /dev/null +++ b/templates_powered_by_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "bytes" + "strings" + "testing" + + "github.com/gin-gonic/gin" + "goblog/blog" + "goblog/theme" +) + +// TestPoweredBy: the shared _powered_by partial shows the credit unless the +// show_powered_by setting is "false" — including before the setting exists +// (an upgraded site, the wizard pages). +func TestPoweredBy(t *testing.T) { + tmpl, _, err := theme.Load(theme.DefaultName, theme.FuncMap()) + if err != nil { + t.Fatalf("load default theme: %v", err) + } + setting := func(v string) map[string]blog.Setting { + return map[string]blog.Setting{"show_powered_by": {Key: "show_powered_by", Type: "checkbox", Value: v}} + } + for name, tc := range map[string]struct { + data gin.H + want bool + }{ + "no settings (wizard)": {gin.H{"version": "v1"}, true}, + "setting not seeded": {gin.H{"version": "v1", "settings": map[string]blog.Setting{}}, true}, + "on": {gin.H{"version": "v1", "settings": setting("true")}, true}, + "off": {gin.H{"version": "v1", "settings": setting("false")}, false}, + } { + var out bytes.Buffer + if err := tmpl.ExecuteTemplate(&out, "_powered_by", tc.data); err != nil { + t.Fatalf("%s: %v", name, err) + } + got := strings.Contains(out.String(), `Powered by - +
{{ template "_powered_by" . }}
diff --git a/themes/default/templates/wizard_auth.html b/themes/default/templates/wizard_auth.html index 7ee53be..d22ab1a 100644 --- a/themes/default/templates/wizard_auth.html +++ b/themes/default/templates/wizard_auth.html @@ -42,7 +42,7 @@
Create Admin Account
Sign in with GitHub - +
{{ template "_powered_by" . }}
diff --git a/themes/default/templates/wizard_db.html b/themes/default/templates/wizard_db.html index 7a752b0..33b770d 100644 --- a/themes/default/templates/wizard_db.html +++ b/themes/default/templates/wizard_db.html @@ -108,7 +108,7 @@
Database:
Test Database - +
{{ template "_powered_by" . }}
diff --git a/themes/default/templates/wizard_settings.html b/themes/default/templates/wizard_settings.html index 0d1edbe..c322313 100644 --- a/themes/default/templates/wizard_settings.html +++ b/themes/default/templates/wizard_settings.html @@ -125,7 +125,7 @@
Socials: (leave blank if you don't want to display)
Save Settings - +
{{ template "_powered_by" . }}
diff --git a/tools/migrate.go b/tools/migrate.go index 5a93e5f..65036b8 100644 --- a/tools/migrate.go +++ b/tools/migrate.go @@ -328,6 +328,7 @@ func seedDefaultSettings(db *gorm.DB) { {Key: "favicon", Type: "file", Value: "/img/favicon.ico"}, {Key: "custom_header_code", Type: "textarea", Value: ""}, {Key: "custom_footer_code", Type: "textarea", Value: ""}, + {Key: "show_powered_by", Type: "checkbox", Value: "true"}, {Key: "theme", Type: "text", Value: "default"}, {Key: "robots_tag", Type: "text", Value: "index, follow"}, {Key: "site_url", Type: "text", Value: "https://www.example.com"},