mirror of https://github.com/jlelse/GoBlog
Add database-based settings, settings screen and migrate sections to db and allow to configure them
This commit is contained in:
parent
22fce13246
commit
cd9d500a55
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -15,15 +14,10 @@ import (
|
|||
func Test_loadActivityPubPrivateKey(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
err := app.initDatabase(false)
|
||||
err := app.initConfig(false)
|
||||
require.NoError(t, err)
|
||||
defer app.db.close()
|
||||
require.NotNil(t, app.db)
|
||||
|
||||
// Generate
|
||||
|
@ -55,9 +49,7 @@ func Test_webfinger(t *testing.T) {
|
|||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.com"
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.prepareWebfinger()
|
||||
|
|
|
@ -29,9 +29,7 @@ func Test_authMiddleware(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -17,13 +16,12 @@ func Test_blogroll(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: fc.Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
app.cfg.Cache.Enable = false
|
||||
app.cfg.DefaultBlog = "en"
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
Blogroll: &configBlogroll{
|
||||
|
@ -35,16 +33,9 @@ func Test_blogroll(t *testing.T) {
|
|||
Categories: []string{"A", "B"},
|
||||
},
|
||||
},
|
||||
},
|
||||
User: &configUser{},
|
||||
Cache: &configCache{
|
||||
Enable: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
fc.setFakeResponse(http.StatusOK, `
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -14,15 +13,12 @@ import (
|
|||
)
|
||||
|
||||
func Test_blogStats(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
BlogStats: &configBlogStats{
|
||||
|
@ -33,17 +29,10 @@ func Test_blogStats(t *testing.T) {
|
|||
"test": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
Webmention: &configWebmention{
|
||||
DisableSending: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Insert post
|
||||
|
|
|
@ -23,9 +23,7 @@ func Test_captchaMiddleware(t *testing.T) {
|
|||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = alice.New(app.checkIsCaptcha, app.captchaMiddleware).ThenFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -18,25 +17,20 @@ import (
|
|||
|
||||
func Test_comments(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
Comments: &configComments{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
}
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
err := app.initConfig(false)
|
||||
require.NoError(t, err)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Successful comment", func(t *testing.T) {
|
||||
|
@ -44,7 +38,7 @@ func Test_comments(t *testing.T) {
|
|||
// Create comment
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "This is just a test")
|
||||
data.Add("name", "Test name")
|
||||
data.Add("website", "https://goblog.app")
|
||||
|
@ -115,7 +109,7 @@ func Test_comments(t *testing.T) {
|
|||
// Create comment
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "This is just a test")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, commentPath, strings.NewReader(data.Encode()))
|
||||
|
@ -151,7 +145,7 @@ func Test_comments(t *testing.T) {
|
|||
t.Run("Empty comment", func(t *testing.T) {
|
||||
|
||||
data := url.Values{}
|
||||
data.Add("target", "https://example.com/test")
|
||||
data.Add("target", "http://localhost:8080/test")
|
||||
data.Add("comment", "")
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, commentPath, strings.NewReader(data.Encode()))
|
||||
|
|
26
config.go
26
config.go
|
@ -341,13 +341,17 @@ func (a *goBlog) loadConfigFile(file string) error {
|
|||
return v.Unmarshal(a.cfg)
|
||||
}
|
||||
|
||||
func (a *goBlog) initConfig() error {
|
||||
func (a *goBlog) initConfig(logging bool) error {
|
||||
if a.cfg == nil {
|
||||
a.cfg = createDefaultConfig()
|
||||
}
|
||||
if a.cfg.initialized {
|
||||
return nil
|
||||
}
|
||||
// Init database
|
||||
if err := a.initDatabase(logging); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check config
|
||||
// Parse addresses and hostnames
|
||||
if a.cfg.Server.PublicAddress == "" {
|
||||
|
@ -402,12 +406,24 @@ func (a *goBlog) initConfig() error {
|
|||
b.Comments = &configComments{Enabled: false}
|
||||
}
|
||||
}
|
||||
// Check if sections already migrated to db
|
||||
const sectionMigrationKey = "sections_migrated"
|
||||
if val, err := a.getSettingValue(sectionMigrationKey); err != nil {
|
||||
return err
|
||||
} else if val == "" {
|
||||
if err = a.saveAllSections(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = a.saveSettingValue(sectionMigrationKey, "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Load db sections
|
||||
if err = a.loadSections(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Check config for each blog
|
||||
for _, blog := range a.cfg.Blogs {
|
||||
// Copy sections key to section name
|
||||
for k, s := range blog.Sections {
|
||||
s.Name = k
|
||||
}
|
||||
// Check if language is set
|
||||
if blog.Lang == "" {
|
||||
blog.Lang = "en"
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -24,14 +23,9 @@ func Test_contact(t *testing.T) {
|
|||
|
||||
// Init everything
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
// Config for contact
|
||||
|
@ -46,13 +40,10 @@ func Test_contact(t *testing.T) {
|
|||
EmailSubject: "Neue Kontaktnachricht",
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
app.cfg.DefaultBlog = "en"
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Make contact form request
|
||||
|
|
|
@ -31,6 +31,9 @@ type database struct {
|
|||
}
|
||||
|
||||
func (a *goBlog) initDatabase(logging bool) (err error) {
|
||||
if a.db != nil {
|
||||
return
|
||||
}
|
||||
if logging {
|
||||
log.Println("Initialize database...")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
create table sections (
|
||||
blog text not null,
|
||||
name text not null,
|
||||
title text not null default '',
|
||||
description text not null default '',
|
||||
pathtemplate text not null default '',
|
||||
showfull boolean not null default false,
|
||||
primary key (blog, name)
|
||||
);
|
|
@ -0,0 +1,5 @@
|
|||
create table settings (
|
||||
name text not null,
|
||||
value text not null default '',
|
||||
primary key (name)
|
||||
);
|
|
@ -15,9 +15,7 @@ func Test_editorPreview(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -13,25 +12,10 @@ import (
|
|||
|
||||
func Test_errors(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Test 404, no HTML", func(t *testing.T) {
|
||||
|
|
|
@ -11,14 +11,9 @@ import (
|
|||
|
||||
func Test_export(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
|
|
@ -15,10 +15,9 @@ func Test_feeds(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
handlerClient := newHandlerClient(app.d)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -11,24 +10,18 @@ import (
|
|||
|
||||
func Test_geoTrack(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
"de": {
|
||||
Lang: "de",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// First test (just with track)
|
||||
|
|
|
@ -16,9 +16,8 @@ func Test_geo(t *testing.T) {
|
|||
httpClient: fc.Client,
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
p := &post{
|
||||
|
|
10
http.go
10
http.go
|
@ -32,7 +32,7 @@ const (
|
|||
func (a *goBlog) startServer() (err error) {
|
||||
log.Println("Start server(s)...")
|
||||
// Load router
|
||||
a.d = a.buildRouter()
|
||||
a.reloadRouter()
|
||||
// Set basic middlewares
|
||||
h := alice.New()
|
||||
h = h.Append(middleware.Heartbeat("/ping"))
|
||||
|
@ -43,7 +43,9 @@ func (a *goBlog) startServer() (err error) {
|
|||
if a.httpsConfigured(false) {
|
||||
h = h.Append(a.securityHeaders)
|
||||
}
|
||||
finalHandler := h.Then(a.d)
|
||||
finalHandler := h.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
a.d.ServeHTTP(w, r)
|
||||
})
|
||||
// Start Onion service
|
||||
if a.cfg.Server.Tor {
|
||||
go func() {
|
||||
|
@ -116,6 +118,10 @@ const (
|
|||
feedPath = ".{feed:(rss|json|atom)}"
|
||||
)
|
||||
|
||||
func (a *goBlog) reloadRouter() {
|
||||
a.d = a.buildRouter()
|
||||
}
|
||||
|
||||
func (a *goBlog) buildRouter() http.Handler {
|
||||
mapRouter := &maprouter.MapRouter{
|
||||
Handlers: map[string]http.Handler{},
|
||||
|
|
|
@ -15,7 +15,7 @@ func Test_httpLogsConfig(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
assert.Equal(t, false, app.cfg.Server.Logging)
|
||||
assert.Equal(t, "data/access.log", app.cfg.Server.LogFile)
|
||||
|
|
|
@ -170,6 +170,9 @@ func (a *goBlog) blogRouter(blog string, conf *configBlog) func(r chi.Router) {
|
|||
// Sitemap
|
||||
r.Group(a.blogSitemapRouter(conf))
|
||||
|
||||
// Settings
|
||||
r.Route(conf.getRelativePath(settingsPath), a.blogSettingsRouter(conf))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,3 +445,14 @@ func (a *goBlog) blogSitemapRouter(conf *configBlog) func(r chi.Router) {
|
|||
r.Get(conf.getRelativePath(sitemapBlogPostsPath), a.serveSitemapBlogPosts)
|
||||
}
|
||||
}
|
||||
|
||||
// Blog - Settings
|
||||
func (a *goBlog) blogSettingsRouter(_ *configBlog) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Use(a.authMiddleware)
|
||||
r.Get("/", a.serveSettings)
|
||||
r.Post(settingsDeleteSectionPath, a.settingsDeleteSection)
|
||||
r.Post(settingsCreateSectionPath, a.settingsCreateSection)
|
||||
r.Post(settingsUpdateSectionPath, a.settingsUpdateSection)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ func Test_indexNow(t *testing.T) {
|
|||
httpClient: fc.Client,
|
||||
}
|
||||
app.cfg.IndexNow = &configIndexNow{Enabled: true}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create http router
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -22,35 +21,25 @@ func Test_indieAuthServer(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: newFakeHttpClient().Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.org",
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.org"
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
User: &configUser{
|
||||
}
|
||||
app.cfg.User = &configUser{
|
||||
Name: "John Doe",
|
||||
Nick: "jdoe",
|
||||
},
|
||||
Cache: &configCache{
|
||||
Enable: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
app.cfg.Cache.Enable = false
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
app.initComponents(false)
|
||||
|
||||
app.ias.Client = newHandlerClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -15,22 +14,10 @@ func Test_checkIndieAuth(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: newFakeHttpClient().Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
|
|
8
main.go
8
main.go
|
@ -64,7 +64,7 @@ func main() {
|
|||
app.logErrAndQuit("Failed to load config file:", err.Error())
|
||||
return
|
||||
}
|
||||
if err = app.initConfig(); err != nil {
|
||||
if err = app.initConfig(false); err != nil {
|
||||
app.logErrAndQuit("Failed to init config:", err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -129,12 +129,6 @@ func main() {
|
|||
// Execute pre-start hooks
|
||||
app.preStartHooks()
|
||||
|
||||
// Initialize database
|
||||
if err = app.initDatabase(true); err != nil {
|
||||
app.logErrAndQuit("Failed to init database:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Link check tool after init of markdown
|
||||
if len(os.Args) >= 2 && os.Args[1] == "check" {
|
||||
app.initMarkdown()
|
||||
|
|
|
@ -14,9 +14,7 @@ func Test_micropubQuery(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create a test post with tags
|
||||
|
|
|
@ -18,9 +18,7 @@ func Test_ntfySending(t *testing.T) {
|
|||
httpClient: fakeClient.Client,
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -25,9 +24,7 @@ func Test_postsDb(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
now := toLocalSafe(time.Now().String())
|
||||
|
@ -229,14 +226,9 @@ func Test_ftsWithoutTitle(t *testing.T) {
|
|||
// Added because there was a bug where there were no search results without title
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
@ -261,14 +253,9 @@ func Test_postsPriority(t *testing.T) {
|
|||
// Added because there was a bug where there were no search results without title
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initMarkdown()
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
@ -312,14 +299,9 @@ func Test_postsPriority(t *testing.T) {
|
|||
|
||||
func Test_usesOfMediaFile(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
Path: "/test/abc",
|
||||
|
@ -368,8 +350,7 @@ func Test_replaceParams(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
Path: "/test/abc",
|
||||
|
@ -399,9 +380,7 @@ func Test_postDeletesParams(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.createPost(&post{
|
||||
|
|
|
@ -11,9 +11,8 @@ func Test_checkDeletedPosts(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Create a post
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,28 +11,18 @@ import (
|
|||
func Test_postsScheduler(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
Micropub: &configMicropub{},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.db.savePost(&post{
|
||||
|
|
|
@ -16,9 +16,7 @@ func Test_serveDate(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
@ -106,9 +104,7 @@ func Test_servePost(t *testing.T) {
|
|||
Username: "test",
|
||||
Password: "test",
|
||||
})
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
@ -16,15 +15,11 @@ func Test_privateMode(t *testing.T) {
|
|||
// Init
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "db.db"),
|
||||
},
|
||||
Server: &configServer{},
|
||||
PrivateMode: &configPrivateMode{
|
||||
Enabled: true,
|
||||
},
|
||||
User: &configUser{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.PrivateMode = &configPrivateMode{Enabled: true}
|
||||
app.cfg.User =
|
||||
&configUser{
|
||||
Name: "Test",
|
||||
Nick: "test",
|
||||
Password: "testpw",
|
||||
|
@ -34,18 +29,14 @@ func Test_privateMode(t *testing.T) {
|
|||
Password: "pw",
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
}
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
handler := alice.New(middleware.WithValue(blogKey, "en"), app.privateModeHandler).ThenFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,14 +11,9 @@ import (
|
|||
func Test_queue(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
time1 := time.Now()
|
||||
|
||||
|
@ -65,30 +59,3 @@ func Test_queue(t *testing.T) {
|
|||
require.Equal(t, []byte("1"), qi.content)
|
||||
|
||||
}
|
||||
|
||||
func Benchmark_queue(b *testing.B) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(b.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
err := app.enqueue("test", []byte("1"), time.Now())
|
||||
require.NoError(b, err)
|
||||
|
||||
b.Run("Peek with item", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = app.peekQueue(context.Background(), "test")
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Peek without item", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = app.peekQueue(context.Background(), "abc")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ func Test_reactionsLowLevel(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
err := app.saveReaction("🖕", "/testpost")
|
||||
|
@ -97,9 +95,7 @@ func Test_reactionsHighLevel(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
// Send unsuccessful reaction
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const settingsPath = "/settings"
|
||||
|
||||
func (a *goBlog) serveSettings(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
|
||||
sections := lo.Values(bc.Sections)
|
||||
sort.Slice(sections, func(i, j int) bool { return sections[i].Name < sections[j].Name })
|
||||
|
||||
a.render(w, r, a.renderSettings, &renderData{
|
||||
Data: &settingsRenderData{
|
||||
blog: blog,
|
||||
sections: sections,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const settingsDeleteSectionPath = "/deletesection"
|
||||
|
||||
func (a *goBlog) settingsDeleteSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
section := r.FormValue("sectionname")
|
||||
// Check if any post uses this section
|
||||
count, err := a.db.countPosts(&postsRequestConfig{
|
||||
blog: blog,
|
||||
sections: []string{section},
|
||||
})
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to check if section is still used", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if count > 0 {
|
||||
a.serveError(w, r, "Section is still used", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Delete section
|
||||
err = a.deleteSection(blog, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to delete section from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
||||
|
||||
const settingsCreateSectionPath = "/createsection"
|
||||
|
||||
func (a *goBlog) settingsCreateSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
// Read values
|
||||
sectionName := r.FormValue("sectionname")
|
||||
sectionTitle := r.FormValue("sectiontitle")
|
||||
if sectionName == "" || sectionTitle == "" {
|
||||
a.serveError(w, r, "Missing values for name or title", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Create section
|
||||
section := &configSection{
|
||||
Name: sectionName,
|
||||
Title: sectionTitle,
|
||||
}
|
||||
err := a.addSection(blog, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to insert section into database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
||||
|
||||
const settingsUpdateSectionPath = "/updatesection"
|
||||
|
||||
func (a *goBlog) settingsUpdateSection(w http.ResponseWriter, r *http.Request) {
|
||||
blog, bc := a.getBlog(r)
|
||||
// Read values
|
||||
sectionName := r.FormValue("sectionname")
|
||||
sectionTitle := r.FormValue("sectiontitle")
|
||||
if sectionName == "" || sectionTitle == "" {
|
||||
a.serveError(w, r, "Missing values for name or title", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sectionDescription := r.FormValue("sectiondescription")
|
||||
sectionPathTemplate := r.FormValue("sectionpathtemplate")
|
||||
sectionShowFull := r.FormValue("sectionshowfull") == "on"
|
||||
// Create section
|
||||
section := &configSection{
|
||||
Name: sectionName,
|
||||
Title: sectionTitle,
|
||||
Description: sectionDescription,
|
||||
PathTemplate: sectionPathTemplate,
|
||||
ShowFull: sectionShowFull,
|
||||
}
|
||||
err := a.updateSection(blog, sectionName, section)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to update section in database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Reload sections
|
||||
err = a.loadSections()
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to reload section configuration from the database", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.reloadRouter()
|
||||
a.cache.purge()
|
||||
http.Redirect(w, r, bc.getRelativePath(settingsPath), http.StatusFound)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (a *goBlog) getSettingValue(name string) (string, error) {
|
||||
row, err := a.db.queryRow("select value from settings where name = @name", sql.Named("name", name))
|
||||
if err != nil {
|
||||
return "",
|
||||
err
|
||||
}
|
||||
var value string
|
||||
err = row.Scan(&value)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (a *goBlog) saveSettingValue(name, value string) error {
|
||||
_, err := a.db.exec(
|
||||
"insert into settings (name, value) values (@name, @value) on conflict (name) do update set value = @value2",
|
||||
sql.Named("name", name),
|
||||
sql.Named("value", value),
|
||||
sql.Named("value2", value),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *goBlog) loadSections() error {
|
||||
for blog, bc := range a.cfg.Blogs {
|
||||
sections, err := a.getSections(blog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bc.Sections = sections
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *goBlog) getSections(blog string) (map[string]*configSection, error) {
|
||||
rows, err := a.db.query("select name, title, description, pathtemplate, showfull from sections where blog = @blog", sql.Named("blog", blog))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections := map[string]*configSection{}
|
||||
for rows.Next() {
|
||||
section := &configSection{}
|
||||
err = rows.Scan(§ion.Name, §ion.Title, §ion.Description, §ion.PathTemplate, §ion.ShowFull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections[section.Name] = section
|
||||
}
|
||||
return sections, nil
|
||||
}
|
||||
|
||||
func (a *goBlog) saveAllSections() error {
|
||||
for blog, bc := range a.cfg.Blogs {
|
||||
for k, s := range bc.Sections {
|
||||
s.Name = k
|
||||
if err := a.addSection(blog, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *goBlog) addSection(blog string, section *configSection) error {
|
||||
_, err := a.db.exec(
|
||||
"insert into sections (blog, name, title, description, pathtemplate, showfull) values (@blog, @name, @title, @description, @pathtemplate, @showfull)",
|
||||
sql.Named("blog", blog),
|
||||
sql.Named("name", section.Name),
|
||||
sql.Named("title", section.Title),
|
||||
sql.Named("description", section.Description),
|
||||
sql.Named("pathtemplate", section.PathTemplate),
|
||||
sql.Named("showfull", section.ShowFull),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *goBlog) deleteSection(blog string, name string) error {
|
||||
_, err := a.db.exec("delete from sections where blog = @blog and name = @name", sql.Named("blog", blog), sql.Named("name", name))
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *goBlog) updateSection(blog string, name string, section *configSection) error {
|
||||
_, err := a.db.exec(
|
||||
"update sections set title = @title, description = @description, pathtemplate = @pathtemplate, showfull = @showfull where blog = @blog and name = @name",
|
||||
sql.Named("title", section.Title),
|
||||
sql.Named("description", section.Description),
|
||||
sql.Named("pathtemplate", section.PathTemplate),
|
||||
sql.Named("showfull", section.ShowFull),
|
||||
sql.Named("blog", blog),
|
||||
sql.Named("name", section.Name),
|
||||
)
|
||||
return err
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -10,14 +9,10 @@ import (
|
|||
|
||||
func Test_shortenPath(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
db := app.db
|
||||
|
||||
res1, err := db.shortenPath("/a")
|
||||
|
|
|
@ -16,9 +16,8 @@ func Test_sitemap(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
|
|
@ -43,6 +43,7 @@ noposts: "Hier sind keine Posts."
|
|||
oldcontent: "⚠️ Dieser Eintrag ist bereits über ein Jahr alt. Er ist möglicherweise nicht mehr aktuell. Meinungen können sich geändert haben."
|
||||
pinned: "Angepinnt"
|
||||
posts: "Posts"
|
||||
postsections: "Post-Bereiche"
|
||||
prev: "Zurück"
|
||||
privateposts: "Private Posts"
|
||||
privatepostsdesc: "Posts mit dem Status `private`, die nur eingeloggt sichtbar sind."
|
||||
|
@ -51,7 +52,13 @@ replyto: "Antwort an"
|
|||
scheduledposts: "Geplante Posts"
|
||||
scheduledpostsdesc: "Beiträge mit dem Status `scheduled`, die veröffentlicht werden, wenn das `published`-Datum erreicht ist."
|
||||
search: "Suchen"
|
||||
sectiondescription: "Beschreibung"
|
||||
sectionname: "Name"
|
||||
sectionpathtemplate: "Pfadvorlage"
|
||||
sectionshowfull: "Vollständigen Inhalt in der Zusammenfassung anzeigen"
|
||||
sectiontitle: "Title"
|
||||
send: "Senden (zur Überprüfung)"
|
||||
settings: "Einstellungen"
|
||||
share: "Online teilen"
|
||||
shorturl: "Kurz-Link:"
|
||||
speak: "Vorlesen"
|
||||
|
|
|
@ -53,6 +53,7 @@ oldcontent: "⚠️ This entry is already over one year old. It may no longer be
|
|||
password: "Password"
|
||||
pinned: "Pinned"
|
||||
posts: "Posts"
|
||||
postsections: "Post sections"
|
||||
prev: "Previous"
|
||||
privateposts: "Private posts"
|
||||
privatepostsdesc: "Posts with status `private` that are visible only when logged in."
|
||||
|
@ -63,7 +64,13 @@ scheduledposts: "Scheduled posts"
|
|||
scheduledpostsdesc: "Posts with status `scheduled` that are published when the `published` date is reached."
|
||||
scopes: "Scopes"
|
||||
search: "Search"
|
||||
sectiondescription: "Description"
|
||||
sectionname: "Name"
|
||||
sectionpathtemplate: "Path template"
|
||||
sectionshowfull: "Show full content in summary"
|
||||
sectiontitle: "Title"
|
||||
send: "Send (to review)"
|
||||
settings: "Settings"
|
||||
share: "Share online"
|
||||
shorturl: "Short link:"
|
||||
speak: "Read aloud"
|
||||
|
|
|
@ -122,9 +122,7 @@ func Test_telegram(t *testing.T) {
|
|||
cfg: cfg,
|
||||
httpClient: fakeClient.Client,
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
app.initMarkdown()
|
||||
app.initTelegram()
|
||||
|
@ -156,9 +154,7 @@ func Test_telegram(t *testing.T) {
|
|||
httpClient: fakeClient.Client,
|
||||
}
|
||||
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
|
||||
app.initTelegram()
|
||||
|
||||
|
|
105
ui.go
105
ui.go
|
@ -125,6 +125,10 @@ func (a *goBlog) renderBase(hb *htmlBuilder, rd *renderData, title, main func(hb
|
|||
hb.writeElementClose("a")
|
||||
}
|
||||
hb.write(" • ")
|
||||
hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/settings"))
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "settings"))
|
||||
hb.writeElementClose("a")
|
||||
hb.write(" • ")
|
||||
hb.writeElementOpen("a", "href", "/logout")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "logout"))
|
||||
hb.writeElementClose("a")
|
||||
|
@ -1480,3 +1484,104 @@ func (a *goBlog) renderEditor(hb *htmlBuilder, rd *renderData) {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
type settingsRenderData struct {
|
||||
blog string
|
||||
sections []*configSection
|
||||
}
|
||||
|
||||
func (a *goBlog) renderSettings(hb *htmlBuilder, rd *renderData) {
|
||||
srd, ok := rd.Data.(*settingsRenderData)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
a.renderBase(
|
||||
hb, rd,
|
||||
func(hb *htmlBuilder) {
|
||||
a.renderTitleTag(hb, rd.Blog, a.ts.GetTemplateStringVariant(rd.Blog.Lang, "settings"))
|
||||
},
|
||||
func(hb *htmlBuilder) {
|
||||
hb.writeElementOpen("main")
|
||||
|
||||
// Title
|
||||
hb.writeElementOpen("h1")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "settings"))
|
||||
hb.writeElementClose("h1")
|
||||
|
||||
// Post sections
|
||||
hb.writeElementOpen("h2")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "postsections"))
|
||||
hb.writeElementClose("h2")
|
||||
|
||||
for _, section := range srd.sections {
|
||||
hb.writeElementOpen("details")
|
||||
|
||||
hb.writeElementOpen("summary")
|
||||
hb.writeElementOpen("h3")
|
||||
hb.writeEscaped(section.Name)
|
||||
hb.writeElementClose("h3")
|
||||
hb.writeElementClose("summary")
|
||||
|
||||
hb.writeElementOpen("form", "class", "fw p", "method", "post")
|
||||
|
||||
hb.writeElementOpen("input", "type", "hidden", "name", "sectionname", "value", section.Name)
|
||||
|
||||
// Title
|
||||
hb.writeElementOpen("input", "type", "text", "name", "sectiontitle", "placeholder", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectiontitle"), "required", "", "value", section.Title)
|
||||
// Description
|
||||
hb.writeElementOpen(
|
||||
"textarea",
|
||||
"name", "sectiondescription",
|
||||
"class", "monospace",
|
||||
"placeholder", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectiondescription"),
|
||||
)
|
||||
hb.writeEscaped(section.Description)
|
||||
hb.writeElementClose("textarea")
|
||||
// Path template
|
||||
hb.writeElementOpen("input", "type", "text", "name", "sectionpathtemplate", "placeholder", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectionpathtemplate"), "value", section.PathTemplate)
|
||||
// Show full
|
||||
hb.writeElementOpen("input", "type", "checkbox", "name", "sectionshowfull", "id", "showfull-"+section.Name, lo.If(section.ShowFull, "checked").Else(""), "")
|
||||
hb.writeElementOpen("label", "for", "showfull-"+section.Name)
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectionshowfull"))
|
||||
hb.writeElementClose("label")
|
||||
|
||||
// Actions
|
||||
hb.writeElementOpen("div", "class", "p")
|
||||
// Update
|
||||
hb.writeElementOpen(
|
||||
"input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "update"),
|
||||
"formaction", rd.Blog.getRelativePath(settingsPath+settingsUpdateSectionPath),
|
||||
)
|
||||
// Delete
|
||||
hb.writeElementOpen(
|
||||
"input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "delete"),
|
||||
"formaction", rd.Blog.getRelativePath(settingsPath+settingsDeleteSectionPath),
|
||||
"class", "confirm", "data-confirmmessage", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "confirmdelete"),
|
||||
)
|
||||
hb.writeElementOpen("script", "src", a.assetFileName("js/formconfirm.js"), "defer", "")
|
||||
hb.writeElementClose("script")
|
||||
hb.writeElementClose("div")
|
||||
|
||||
hb.writeElementClose("form")
|
||||
hb.writeElementClose("details")
|
||||
}
|
||||
|
||||
// Create new section
|
||||
hb.writeElementOpen("form", "class", "fw p", "method", "post")
|
||||
// Name
|
||||
hb.writeElementOpen("input", "type", "text", "name", "sectionname", "placeholder", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectionname"), "required", "")
|
||||
// Title
|
||||
hb.writeElementOpen("input", "type", "text", "name", "sectiontitle", "placeholder", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "sectiontitle"), "required", "")
|
||||
// Create button
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeElementOpen(
|
||||
"input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "create"),
|
||||
"formaction", rd.Blog.getRelativePath(settingsPath+settingsCreateSectionPath),
|
||||
)
|
||||
hb.writeElementClose("div")
|
||||
hb.writeElementClose("form")
|
||||
|
||||
hb.writeElementClose("main")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
19
ui_test.go
19
ui_test.go
|
@ -20,9 +20,7 @@ func Test_renderPostTax(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
p := &post{
|
||||
|
@ -48,9 +46,7 @@ func Test_renderOldContentWarning(t *testing.T) {
|
|||
app := &goBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
p := &post{
|
||||
|
@ -76,10 +72,10 @@ func Test_renderInteractions(t *testing.T) {
|
|||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.com"
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
app.d = app.buildRouter()
|
||||
|
||||
err = app.createPost(&post{
|
||||
|
@ -145,9 +141,8 @@ func Test_renderAuthor(t *testing.T) {
|
|||
}
|
||||
app.cfg.User.Picture = "https://example.com/picture.jpg"
|
||||
app.cfg.User.Name = "John Doe"
|
||||
_ = app.initConfig()
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -21,23 +20,16 @@ func Test_verifyMention(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: mockClient.Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.org",
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
d: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusFound)
|
||||
}
|
||||
}),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.org"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
m := &mention{
|
||||
|
@ -71,21 +63,14 @@ func Test_verifyMentionBidgy(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: mockClient.Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.org",
|
||||
},
|
||||
},
|
||||
cfg: createDefaultTestConfig(t),
|
||||
d: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// do nothing
|
||||
}),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.org"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
m := &mention{
|
||||
|
@ -115,21 +100,14 @@ func Test_verifyMentionColin(t *testing.T) {
|
|||
|
||||
app := &goBlog{
|
||||
httpClient: mockClient.Client,
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://jlelse.blog",
|
||||
},
|
||||
},
|
||||
cfg: createDefaultConfig(),
|
||||
d: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// do nothing
|
||||
}),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://jlelse.blog"
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
m := &mention{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -11,25 +10,16 @@ import (
|
|||
|
||||
func Test_webmentions(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
Blogs: map[string]*configBlog{
|
||||
cfg: createDefaultTestConfig(t),
|
||||
}
|
||||
app.cfg.Server.PublicAddress = "https://example.com"
|
||||
app.cfg.Blogs = map[string]*configBlog{
|
||||
"en": {
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
User: &configUser{},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
defer app.db.close()
|
||||
_ = app.initConfig(false)
|
||||
app.initComponents(false)
|
||||
|
||||
_ = app.db.insertWebmention(&mention{
|
||||
|
|
Loading…
Reference in New Issue