mirror of https://github.com/jlelse/GoBlog
"GoBlog export path" for exporting markdown files of posts
This commit is contained in:
parent
da9ea7c0d7
commit
ef66cd7c80
|
@ -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
|
||||
}
|
|
@ -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`)
|
||||
|
||||
}
|
2
go.mod
2
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
15
main.go
15
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()
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,11 +430,13 @@ func (a *goBlog) getPosts(config *postsRequestConfig) (posts []*post, err error)
|
|||
}
|
||||
}
|
||||
// Render post title
|
||||
if !config.withoutRenderedTitle {
|
||||
for _, p := range posts {
|
||||
if t := p.Title(); t != "" {
|
||||
p.RenderedTitle = a.renderMdTitle(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue