mirror of https://github.com/jlelse/GoBlog
Add option for plugins to add custom template assets, add custom CSS plugin (fixes #54)
parent
49a0053044
commit
503b98327f
|
@ -92,6 +92,14 @@ Third-party modules
|
|||
|
||||
Some simple plugins are included in the main GoBlog repository. Some can be found elsewhere.
|
||||
|
||||
### Custom CSS (Path `embedded:customcss`, Import `customcss`)
|
||||
|
||||
A plugin that can add custom CSS to every HTML page. Just specify a CSS file and it will minify the file and append it to the rendered HTML head.
|
||||
|
||||
#### Config
|
||||
|
||||
`file` (string): Path to the custom CSS file.
|
||||
|
||||
### Demo (Path `embedded:demo`, Import `demo`)
|
||||
|
||||
A simple demo plugin showcasing some of the features plugins can implement. Take a look at the source code, if you want to implement your own plugin.
|
||||
|
|
5
go.mod
5
go.mod
|
@ -48,7 +48,7 @@ require (
|
|||
github.com/pquerna/otp v1.4.0
|
||||
github.com/samber/lo v1.37.0
|
||||
github.com/schollz/sqlite3dump v1.3.1
|
||||
github.com/snabb/sitemap v1.0.3
|
||||
github.com/snabb/sitemap v1.0.4
|
||||
github.com/sourcegraph/conc v0.2.0
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/viper v1.15.0
|
||||
|
@ -91,7 +91,6 @@ require (
|
|||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.13.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20221205040414-01c1adfc98ea // indirect
|
||||
github.com/go-test/deep v1.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
|
@ -118,7 +117,7 @@ require (
|
|||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/rs/zerolog v1.29.0 // indirect
|
||||
github.com/snabb/diagio v1.0.1 // indirect
|
||||
github.com/snabb/diagio v1.0.4 // indirect
|
||||
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221216004406-749998a2ac74 // indirect
|
||||
github.com/spf13/afero v1.9.3 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
|
|
9
go.sum
9
go.sum
|
@ -198,7 +198,6 @@ github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZp
|
|||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
|
@ -495,10 +494,10 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
|
|||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/snabb/diagio v1.0.1 h1:l7HODYLuGuPfom3Rbm/HHdp1RdrVyAy5iWEzLkRXlH0=
|
||||
github.com/snabb/diagio v1.0.1/go.mod h1:ZyGaWFhfBVqstGUw6laYetzeTwZ2xxVPqTALx1QQa1w=
|
||||
github.com/snabb/sitemap v1.0.3 h1:4DFB7bcDELJ3nL8OXohX6G3IbahS1vOMrHxMhU5SNzI=
|
||||
github.com/snabb/sitemap v1.0.3/go.mod h1:sVjDylXD+ZHu+xplNJZ8o5GMfbxNEx+lGVg7YUuXnac=
|
||||
github.com/snabb/diagio v1.0.4 h1:XnlKoBarZWiAEnNBYE5t1nbvJhdaoTaW7IBzu0R4AqM=
|
||||
github.com/snabb/diagio v1.0.4/go.mod h1:Y+Pja4UJrskCOKaLxOfa8b8wYSVb0JWpR4YFNHuzjDI=
|
||||
github.com/snabb/sitemap v1.0.4 h1:BC6cPW5jXLsKWtlYQKD2s1W58CarvNzqOmdl680uQPw=
|
||||
github.com/snabb/sitemap v1.0.4/go.mod h1:815/fxQQ8Tt7Eqwe8Lcat4ax73zuHyPxWBZySnbaxkc=
|
||||
github.com/sourcegraph/conc v0.2.0 h1:96VpOCAtXDCQ8Oycz0ftHqdPyMi8w12ltN4L2noYg7s=
|
||||
github.com/sourcegraph/conc v0.2.0/go.mod h1:8lmPpTLA0hsWqw4lw7wS1e694U2tMjRrc1Asvupb4QM=
|
||||
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221216004406-749998a2ac74 h1:4yKiBHEHJXHu9umlQzhX4sRK622p+Aw4TGvvAw9X9j8=
|
||||
|
|
4
http.go
4
http.go
|
@ -329,8 +329,8 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
}
|
||||
// No post, check regex redirects or serve 404 error
|
||||
alice.New(a.cacheMiddleware, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r)
|
||||
// No post, check template assets (dynamically registered), regex redirects or serve 404 error
|
||||
alice.New(a.cacheMiddleware, a.checkTemplateAssets, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package plugintypes
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -16,6 +17,10 @@ type App interface {
|
|||
PurgeCache()
|
||||
// Get the HTTP client used by GoBlog
|
||||
GetHTTPClient() *http.Client
|
||||
// Compile an asset (like CSS, JS, etc.) and add it to use when rendering, for some filetypes, it also get's compressed
|
||||
CompileAsset(filename string, reader io.Reader) error
|
||||
// Get the asset path with the filename used when compiling the assert
|
||||
AssetPath(filename string) string
|
||||
}
|
||||
|
||||
// Database is used to provide access to GoBlog's database.
|
||||
|
|
|
@ -65,12 +65,20 @@ func init() {
|
|||
// _go_goblog_app_app_pkgs_plugintypes_App is an interface wrapper for App type
|
||||
type _go_goblog_app_app_pkgs_plugintypes_App struct {
|
||||
IValue interface{}
|
||||
WAssetPath func(filename string) string
|
||||
WCompileAsset func(filename string, reader io.Reader) error
|
||||
WGetDatabase func() plugintypes.Database
|
||||
WGetHTTPClient func() *http.Client
|
||||
WGetPost func(path string) (plugintypes.Post, error)
|
||||
WPurgeCache func()
|
||||
}
|
||||
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_App) AssetPath(filename string) string {
|
||||
return W.WAssetPath(filename)
|
||||
}
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_App) CompileAsset(filename string, reader io.Reader) error {
|
||||
return W.WCompileAsset(filename, reader)
|
||||
}
|
||||
func (W _go_goblog_app_app_pkgs_plugintypes_App) GetDatabase() plugintypes.Database {
|
||||
return W.WGetDatabase()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
@ -89,6 +90,14 @@ func (a *goBlog) GetHTTPClient() *http.Client {
|
|||
return a.httpClient
|
||||
}
|
||||
|
||||
func (a *goBlog) CompileAsset(filename string, reader io.Reader) error {
|
||||
return a.compileAsset(filename, reader)
|
||||
}
|
||||
|
||||
func (a *goBlog) AssetPath(filename string) string {
|
||||
return a.assetFileName(filename)
|
||||
}
|
||||
|
||||
func (p *post) GetPath() string {
|
||||
return p.Path
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package customcss
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
app plugintypes.App
|
||||
|
||||
customCSS string
|
||||
init sync.Once
|
||||
inited bool
|
||||
}
|
||||
|
||||
func GetPlugin() (plugintypes.SetConfig, plugintypes.SetApp, plugintypes.UI2) {
|
||||
p := &plugin{}
|
||||
return p, p, p
|
||||
}
|
||||
|
||||
func (p *plugin) SetConfig(config map[string]any) {
|
||||
if filePath, ok := config["file"]; ok {
|
||||
if filePathString, ok := filePath.(string); ok {
|
||||
p.customCSS = filePathString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *plugin) SetApp(app plugintypes.App) {
|
||||
p.app = app
|
||||
}
|
||||
|
||||
func (p *plugin) RenderWithDocument(rc plugintypes.RenderContext, doc *goquery.Document) {
|
||||
if p.app == nil || p.customCSS == "" {
|
||||
return
|
||||
}
|
||||
|
||||
p.init.Do(func() {
|
||||
f, err := os.Open(p.customCSS)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to open custom css file: ", err.Error())
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = p.app.CompileAsset("plugincustomcss.css", f)
|
||||
if err != nil {
|
||||
fmt.Println("Failed compile custom css: ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
p.inited = true
|
||||
fmt.Println("Custom CSS compiled")
|
||||
})
|
||||
|
||||
if !p.inited {
|
||||
return
|
||||
}
|
||||
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
hb := htmlbuilder.NewHtmlBuilder(buf)
|
||||
|
||||
hb.WriteElementOpen("link", "rel", "stylesheet", "href", p.app.AssetPath("plugincustomcss.css"))
|
||||
|
||||
doc.Find("head").AppendHtml(buf.String())
|
||||
}
|
|
@ -89,6 +89,17 @@ func (a *goBlog) allAssetPaths() []string {
|
|||
return paths
|
||||
}
|
||||
|
||||
func (a *goBlog) checkTemplateAssets(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
af, ok := a.assetFiles[strings.TrimPrefix(r.URL.Path, "/")]
|
||||
if !ok {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
a.serveAssetFile(w, af)
|
||||
})
|
||||
}
|
||||
|
||||
// Gets only called by registered paths
|
||||
func (a *goBlog) serveAsset(w http.ResponseWriter, r *http.Request) {
|
||||
af, ok := a.assetFiles[strings.TrimPrefix(r.URL.Path, "/")]
|
||||
|
@ -96,6 +107,10 @@ func (a *goBlog) serveAsset(w http.ResponseWriter, r *http.Request) {
|
|||
a.serve404(w, r)
|
||||
return
|
||||
}
|
||||
a.serveAssetFile(w, af)
|
||||
}
|
||||
|
||||
func (*goBlog) serveAssetFile(w http.ResponseWriter, af *assetFile) {
|
||||
w.Header().Set(cacheControl, "public,max-age=31536000,immutable")
|
||||
w.Header().Set(contentType, af.contentType+contenttype.CharsetUtf8Suffix)
|
||||
_, _ = w.Write(af.body)
|
||||
|
|
Loading…
Reference in New Issue