2023-07-09 20:12:06 +00:00
|
|
|
package importer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"os"
|
2023-07-18 19:03:33 +00:00
|
|
|
"owl-blogs/app"
|
|
|
|
entrytypes "owl-blogs/entry_types"
|
2023-07-09 20:12:06 +00:00
|
|
|
"path"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ReplyData struct {
|
|
|
|
Url string `yaml:"url"`
|
|
|
|
Text string `yaml:"text"`
|
|
|
|
}
|
|
|
|
type BookmarkData struct {
|
|
|
|
Url string `yaml:"url"`
|
|
|
|
Text string `yaml:"text"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type RecipeData struct {
|
|
|
|
Yield string `yaml:"yield"`
|
|
|
|
Duration string `yaml:"duration"`
|
|
|
|
Ingredients []string `yaml:"ingredients"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PostMeta struct {
|
|
|
|
Type string `yaml:"type"`
|
|
|
|
Title string `yaml:"title"`
|
|
|
|
Description string `yaml:"description"`
|
|
|
|
Aliases []string `yaml:"aliases"`
|
|
|
|
Date time.Time `yaml:"date"`
|
|
|
|
Draft bool `yaml:"draft"`
|
|
|
|
Reply ReplyData `yaml:"reply"`
|
|
|
|
Bookmark BookmarkData `yaml:"bookmark"`
|
|
|
|
Recipe RecipeData `yaml:"recipe"`
|
|
|
|
PhotoPath string `yaml:"photo"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Post struct {
|
|
|
|
Id string
|
|
|
|
Meta PostMeta
|
|
|
|
Content string
|
|
|
|
}
|
|
|
|
|
2023-07-13 18:39:24 +00:00
|
|
|
func (post *Post) MediaDir() string {
|
|
|
|
return path.Join("public", post.Id, "media")
|
|
|
|
}
|
|
|
|
|
2023-07-09 20:12:06 +00:00
|
|
|
func (pm *PostMeta) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
|
|
type T struct {
|
|
|
|
Type string `yaml:"type"`
|
|
|
|
Title string `yaml:"title"`
|
|
|
|
Description string `yaml:"description"`
|
|
|
|
Aliases []string `yaml:"aliases"`
|
|
|
|
Draft bool `yaml:"draft"`
|
|
|
|
Reply ReplyData `yaml:"reply"`
|
|
|
|
Bookmark BookmarkData `yaml:"bookmark"`
|
|
|
|
Recipe RecipeData `yaml:"recipe"`
|
|
|
|
PhotoPath string `yaml:"photo"`
|
|
|
|
}
|
|
|
|
type S struct {
|
|
|
|
Date string `yaml:"date"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var t T
|
|
|
|
var s S
|
|
|
|
if err := unmarshal(&t); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := unmarshal(&s); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pm.Type = t.Type
|
|
|
|
if pm.Type == "" {
|
|
|
|
pm.Type = "article"
|
|
|
|
}
|
|
|
|
pm.Title = t.Title
|
|
|
|
pm.Description = t.Description
|
|
|
|
pm.Aliases = t.Aliases
|
|
|
|
pm.Draft = t.Draft
|
|
|
|
pm.Reply = t.Reply
|
|
|
|
pm.Bookmark = t.Bookmark
|
|
|
|
pm.Recipe = t.Recipe
|
|
|
|
pm.PhotoPath = t.PhotoPath
|
|
|
|
|
|
|
|
possibleFormats := []string{
|
|
|
|
"2006-01-02",
|
|
|
|
time.Layout,
|
|
|
|
time.ANSIC,
|
|
|
|
time.UnixDate,
|
|
|
|
time.RubyDate,
|
|
|
|
time.RFC822,
|
|
|
|
time.RFC822Z,
|
|
|
|
time.RFC850,
|
|
|
|
time.RFC1123,
|
|
|
|
time.RFC1123Z,
|
|
|
|
time.RFC3339,
|
|
|
|
time.RFC3339Nano,
|
|
|
|
time.Stamp,
|
|
|
|
time.StampMilli,
|
|
|
|
time.StampMicro,
|
|
|
|
time.StampNano,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, format := range possibleFormats {
|
|
|
|
if t, err := time.Parse(format, s.Date); err == nil {
|
|
|
|
pm.Date = t
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadContent(data []byte) string {
|
|
|
|
|
|
|
|
// trim yaml block
|
|
|
|
// TODO this can be done nicer
|
|
|
|
trimmedData := bytes.TrimSpace(data)
|
|
|
|
// ensure that data ends with a newline
|
|
|
|
trimmedData = append(trimmedData, []byte("\n")...)
|
|
|
|
// check first line is ---
|
|
|
|
if string(trimmedData[0:4]) == "---\n" {
|
|
|
|
trimmedData = trimmedData[4:]
|
|
|
|
// find --- end
|
|
|
|
end := bytes.Index(trimmedData, []byte("\n---\n"))
|
|
|
|
if end != -1 {
|
|
|
|
data = trimmedData[end+5:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadMeta(data []byte) (PostMeta, error) {
|
|
|
|
|
|
|
|
// get yaml metadata block
|
|
|
|
meta := PostMeta{}
|
|
|
|
trimmedData := bytes.TrimSpace(data)
|
|
|
|
// ensure that data ends with a newline
|
|
|
|
trimmedData = append(trimmedData, []byte("\n")...)
|
|
|
|
// check first line is ---
|
|
|
|
if string(trimmedData[0:4]) == "---\n" {
|
|
|
|
trimmedData = trimmedData[4:]
|
|
|
|
// find --- end
|
|
|
|
end := bytes.Index(trimmedData, []byte("---\n"))
|
|
|
|
if end != -1 {
|
|
|
|
metaData := trimmedData[:end]
|
|
|
|
err := yaml.Unmarshal(metaData, &meta)
|
|
|
|
if err != nil {
|
|
|
|
return PostMeta{}, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return meta, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func AllUserPosts(userPath string) ([]Post, error) {
|
2023-07-13 18:39:24 +00:00
|
|
|
postFiles := ListDir(path.Join(userPath, "public"))
|
2023-07-09 20:12:06 +00:00
|
|
|
posts := make([]Post, 0)
|
|
|
|
for _, id := range postFiles {
|
|
|
|
// if is a directory and has index.md, add to posts
|
|
|
|
if dirExists(path.Join(userPath, "public", id)) {
|
|
|
|
if fileExists(path.Join(userPath, "public", id, "index.md")) {
|
|
|
|
postData, err := os.ReadFile(path.Join(userPath, "public", id, "index.md"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
meta, err := LoadMeta(postData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
post := Post{
|
|
|
|
Id: id,
|
|
|
|
Content: LoadContent(postData),
|
|
|
|
Meta: meta,
|
|
|
|
}
|
|
|
|
posts = append(posts, post)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return posts, nil
|
|
|
|
}
|
|
|
|
|
2023-07-13 18:39:24 +00:00
|
|
|
func ListDir(path string) []string {
|
2023-07-09 20:12:06 +00:00
|
|
|
dir, _ := os.Open(path)
|
|
|
|
defer dir.Close()
|
|
|
|
files, _ := dir.Readdirnames(-1)
|
|
|
|
return files
|
|
|
|
}
|
|
|
|
|
|
|
|
func dirExists(path string) bool {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func fileExists(path string) bool {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
return err == nil
|
|
|
|
}
|
2023-07-18 19:03:33 +00:00
|
|
|
|
|
|
|
func ConvertTypeList(v1 []string, registry *app.EntryTypeRegistry) []string {
|
|
|
|
v2 := make([]string, len(v1))
|
|
|
|
for i, v1Type := range v1 {
|
|
|
|
switch v1Type {
|
|
|
|
case "article":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Article{})
|
|
|
|
v2[i] = name
|
|
|
|
case "bookmark":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Bookmark{})
|
|
|
|
v2[i] = name
|
|
|
|
case "reply":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Reply{})
|
|
|
|
v2[i] = name
|
|
|
|
case "photo":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Image{})
|
|
|
|
v2[i] = name
|
|
|
|
case "note":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Note{})
|
|
|
|
v2[i] = name
|
|
|
|
case "recipe":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Recipe{})
|
|
|
|
v2[i] = name
|
|
|
|
case "page":
|
|
|
|
name, _ := registry.TypeName(&entrytypes.Page{})
|
|
|
|
v2[i] = name
|
|
|
|
default:
|
|
|
|
v2[i] = v1Type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v2
|
|
|
|
}
|