diff --git a/export.go b/export.go new file mode 100644 index 0000000..c81dd7b --- /dev/null +++ b/export.go @@ -0,0 +1,29 @@ +package main + +import ( + "os" + "path/filepath" +) + +func (a *goBlog) exportMarkdownFiles(dir string) error { + posts, err := a.getPosts(&postsRequestConfig{ + withoutRenderedTitle: true, + }) + if err != nil { + return err + } + dir = defaultIfEmpty(dir, "export") + for _, p := range posts { + filename := filepath.Join(dir, p.Path+".md") + filedir := filepath.Dir(filename) + err = os.MkdirAll(filedir, 0644) + if err != nil { + return err + } + err = os.WriteFile(filename, []byte(p.contentWithParams()), 0644) + if err != nil { + return err + } + } + return nil +} diff --git a/export_test.go b/export_test.go new file mode 100644 index 0000000..1ba7ace --- /dev/null +++ b/export_test.go @@ -0,0 +1,52 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_export(t *testing.T) { + app := &goBlog{ + cfg: &config{ + Db: &configDb{ + File: filepath.Join(t.TempDir(), "test.db"), + }, + }, + } + _ = app.initDatabase(false) + app.initMarkdown() + + err := app.db.savePost(&post{ + Path: "/test/abc", + Content: "ABC", + Blog: "en", + Section: "test", + Status: statusDraft, + Parameters: map[string][]string{ + "title": {"Title"}, + }, + }, &postCreationOptions{new: true}) + require.NoError(t, err) + + exportPath := filepath.Join(t.TempDir(), "export") + err = app.exportMarkdownFiles(exportPath) + require.NoError(t, err) + + exportFilePath := filepath.Join(exportPath, "/test/abc.md") + require.FileExists(t, exportFilePath) + + fileContentBytes, err := os.ReadFile(exportFilePath) + require.NoError(t, err) + + fileContent := string(fileContentBytes) + assert.Contains(t, fileContent, `path: /test/abc`) + assert.Contains(t, fileContent, `title: Title`) + assert.Contains(t, fileContent, `updated: ""`) + assert.Contains(t, fileContent, `published: ""`) + assert.Contains(t, fileContent, `ABC`) + +} diff --git a/go.mod b/go.mod index 31b55de..861f34f 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b diff --git a/go.sum b/go.sum index 4518272..b2ceb3c 100644 --- a/go.sum +++ b/go.sum @@ -530,8 +530,8 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/main.go b/main.go index 271bbea..ed3cd72 100644 --- a/main.go +++ b/main.go @@ -100,6 +100,21 @@ func main() { return } + // Markdown export + if len(os.Args) >= 2 && os.Args[1] == "export" { + var dir string + if len(os.Args) >= 3 { + dir = os.Args[2] + } + err = app.exportMarkdownFiles(dir) + if err != nil { + app.logErrAndQuit("Failed to export markdown files:", err.Error()) + return + } + app.shutdown.ShutdownAndWait() + return + } + // Initialize components app.initComponents() diff --git a/original-assets/styles/styles.scss b/original-assets/styles/styles.scss index 4dfb1d3..65897e8 100644 --- a/original-assets/styles/styles.scss +++ b/original-assets/styles/styles.scss @@ -173,6 +173,13 @@ footer { } } +details summary { + // Show first child of summary inline + > *:first-child { + display: inline; + } +} + .border-top { @include color-border(border-top, 1px, solid, primary); } @@ -235,7 +242,7 @@ footer { height: 400px; } -/* Print */ +// Print @media print { html { @include lightmode; diff --git a/postsDb.go b/postsDb.go index 6fd4638..0d6b8cd 100644 --- a/postsDb.go +++ b/postsDb.go @@ -249,6 +249,7 @@ type postsRequestConfig struct { priorityOrder bool withoutParameters bool withOnlyParameters []string + withoutRenderedTitle bool } func buildPostsQuery(c *postsRequestConfig, selection string) (query string, args []interface{}) { @@ -429,9 +430,11 @@ func (a *goBlog) getPosts(config *postsRequestConfig) (posts []*post, err error) } } // Render post title - for _, p := range posts { - if t := p.Title(); t != "" { - p.RenderedTitle = a.renderMdTitle(t) + if !config.withoutRenderedTitle { + for _, p := range posts { + if t := p.Title(); t != "" { + p.RenderedTitle = a.renderMdTitle(t) + } } } return posts, nil diff --git a/postsFuncs.go b/postsFuncs.go index 32d41db..1f43adc 100644 --- a/postsFuncs.go +++ b/postsFuncs.go @@ -133,23 +133,6 @@ func (p *post) isPublishedSectionPost() bool { } func (a *goBlog) postToMfItem(p *post) *microformatItem { - params := map[string]interface{}{} - for k, v := range p.Parameters { - if l := len(v); l == 1 { - params[k] = v[0] - } else if l > 1 { - params[k] = v - } - } - params["path"] = p.Path - params["section"] = p.Section - params["blog"] = p.Blog - params["published"] = p.Published - params["updated"] = p.Updated - params["status"] = string(p.Status) - params["priority"] = p.Priority - pb, _ := yaml.Marshal(params) - content := fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content) var mfStatus, mfVisibility string switch p.Status { case statusDraft: @@ -173,7 +156,7 @@ func (a *goBlog) postToMfItem(p *post) *microformatItem { PostStatus: []string{mfStatus}, Visibility: []string{mfVisibility}, Category: p.Parameters[a.cfg.Micropub.CategoryParam], - Content: []string{content}, + Content: []string{p.contentWithParams()}, URL: []string{a.fullPostURL(p)}, InReplyTo: p.Parameters[a.cfg.Micropub.ReplyParam], LikeOf: p.Parameters[a.cfg.Micropub.LikeParam], @@ -222,6 +205,26 @@ func (a *goBlog) photoLinks(p *post) []string { return p.Parameters[a.cfg.Micropub.PhotoParam] } +func (p *post) contentWithParams() string { + params := map[string]interface{}{} + for k, v := range p.Parameters { + if l := len(v); l == 1 { + params[k] = v[0] + } else if l > 1 { + params[k] = v + } + } + params["path"] = p.Path + params["section"] = p.Section + params["blog"] = p.Blog + params["published"] = p.Published + params["updated"] = p.Updated + params["status"] = string(p.Status) + params["priority"] = p.Priority + pb, _ := yaml.Marshal(params) + return fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content) +} + // Public because of rendering func (p *post) Title() string { diff --git a/templates/assets/css/styles.css b/templates/assets/css/styles.css index dd75205..d8db4e4 100644 --- a/templates/assets/css/styles.css +++ b/templates/assets/css/styles.css @@ -131,6 +131,10 @@ footer * { margin-bottom: 0; } +details summary > *:first-child { + display: inline; +} + .border-top, footer { border-top: 1px solid #000; border-top: 1px solid var(--primary, #000); @@ -195,7 +199,6 @@ footer * { height: 400px; } -/* Print */ @media print { html { --background: #fff;