hugo-micropub/entry.go

262 lines
7.2 KiB
Go
Raw Normal View History

2019-11-07 10:00:24 +00:00
package main
import (
"bufio"
"bytes"
2019-11-07 10:00:24 +00:00
"errors"
"fmt"
"gopkg.in/yaml.v2"
2019-11-07 11:10:45 +00:00
"io/ioutil"
2019-11-07 10:00:24 +00:00
"math/rand"
2019-11-07 11:10:45 +00:00
"net/http"
2019-11-07 10:00:24 +00:00
"net/url"
"strings"
"time"
)
type Entry struct {
content string
title string
2019-11-14 17:46:49 +00:00
date string
lastmod string
2019-11-07 10:00:24 +00:00
section string
tags []string
link string
slug string
replyLink string
replyTitle string
likeLink string
likeTitle string
2019-11-07 10:00:24 +00:00
filename string
location string
2019-11-07 10:00:24 +00:00
token string
}
2019-11-07 11:10:45 +00:00
func CreateEntry(contentType ContentType, r *http.Request) (*Entry, error) {
2019-11-07 10:00:24 +00:00
if contentType == WwwForm {
2019-11-07 11:10:45 +00:00
bodyString, err := parseRequestBody(r)
if err != nil {
return nil, err
}
bodyValues, err := url.ParseQuery(bodyString)
2019-11-07 10:00:24 +00:00
if err != nil {
return nil, errors.New("failed to parse query")
}
return createEntryFromValueMap(bodyValues)
2019-11-07 11:10:45 +00:00
} else if contentType == Multipart {
err := r.ParseMultipartForm(1024 * 1024 * 16)
if err != nil {
return nil, errors.New("failed to parse Multipart")
}
return createEntryFromValueMap(r.MultipartForm.Value)
2019-11-07 11:10:45 +00:00
} else if contentType == Json {
bodyString, err := parseRequestBody(r)
if err != nil {
return nil, err
}
parsedMfItem := &MicroformatItem{}
err = yaml.Unmarshal([]byte(bodyString), &parsedMfItem)
if err != nil {
return nil, errors.New("failed to parse Json")
}
return createEntryFromMicroformat(parsedMfItem)
2019-11-07 10:00:24 +00:00
} else {
return nil, errors.New("unsupported content-type")
}
}
2019-11-07 11:10:45 +00:00
func parseRequestBody(r *http.Request) (string, error) {
defer r.Body.Close()
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
return "", errors.New("failed to read body")
}
return string(bodyBytes), nil
}
func createEntryFromValueMap(values map[string][]string) (*Entry, error) {
if h, ok := values["h"]; ok && (len(h) != 1 || h[0] != "entry") {
2019-11-07 10:00:24 +00:00
return nil, errors.New("only entry type is supported so far")
}
if _, ok := values["content"]; ok {
entry := &Entry{
content: values["content"][0],
}
if name, ok := values["name"]; ok {
entry.title = name[0]
2019-11-07 10:00:24 +00:00
}
if category, ok := values["category"]; ok {
entry.tags = category
} else if categories, ok := values["category[]"]; ok {
entry.tags = categories
2019-11-07 10:00:24 +00:00
} else {
entry.tags = nil
2019-11-07 10:00:24 +00:00
}
if slug, ok := values["mp-slug"]; ok && len(slug) > 0 && slug[0] != "" {
entry.slug = slug[0]
2019-11-07 10:00:24 +00:00
}
if inReplyTo, ok := values["in-reply-to"]; ok {
entry.replyLink = inReplyTo[0]
2019-11-07 10:00:24 +00:00
}
if likeOf, ok := values["like-of"]; ok {
entry.likeLink = likeOf[0]
}
if token, ok := values["access_token"]; ok {
2019-11-07 10:00:24 +00:00
entry.token = "Bearer " + token[0]
}
err := computeExtraSettings(entry)
if err != nil {
return nil, err
}
return entry, nil
}
2019-11-07 11:10:45 +00:00
return nil, errors.New("error parsing the entry")
2019-11-07 10:00:24 +00:00
}
func createEntryFromMicroformat(mfEntry *MicroformatItem) (*Entry, error) {
if len(mfEntry.Type) != 1 || mfEntry.Type[0] != "h-entry" {
return nil, errors.New("only entry type is supported so far")
}
if mfEntry.Properties != nil && len(mfEntry.Properties.Content) == 1 && len(mfEntry.Properties.Content[0]) > 0 {
entry := &Entry{
content: mfEntry.Properties.Content[0],
}
if len(mfEntry.Properties.Name) == 1 {
entry.title = mfEntry.Properties.Name[0]
}
if len(mfEntry.Properties.Category) > 0 {
entry.tags = mfEntry.Properties.Category
}
if len(mfEntry.Properties.MpSlug) == 1 && len(mfEntry.Properties.MpSlug[0]) > 0 {
entry.slug = mfEntry.Properties.MpSlug[0]
}
if len(mfEntry.Properties.InReplyTo) == 1 {
entry.replyLink = mfEntry.Properties.InReplyTo[0]
}
if len(mfEntry.Properties.LikeOf) == 1 {
entry.likeLink = mfEntry.Properties.LikeOf[0]
}
err := computeExtraSettings(entry)
if err != nil {
return nil, err
}
return entry, nil
}
return nil, errors.New("error parsing the entry")
}
2019-11-07 10:00:24 +00:00
func computeExtraSettings(entry *Entry) error {
2019-12-06 09:32:30 +00:00
now := time.Now()
// Set date
entry.date = now.Format(time.RFC3339)
// Find settings hidden in content
var filteredContent bytes.Buffer
contentScanner := bufio.NewScanner(strings.NewReader(entry.content))
for contentScanner.Scan() {
text := contentScanner.Text()
if strings.HasPrefix(text, "section: ") {
// Section
entry.section = strings.TrimPrefix(text, "section: ")
2019-11-08 23:40:55 +00:00
} else if strings.HasPrefix(text, "title: ") {
// Title
entry.title = strings.TrimPrefix(text, "title: ")
} else if strings.HasPrefix(text, "slug: ") {
// Slug
entry.slug = strings.TrimPrefix(text, "slug: ")
} else if strings.HasPrefix(text, "tags: ") {
// Tags
entry.tags = strings.Split(strings.TrimPrefix(text, "tags: "), ",")
} else if strings.HasPrefix(text, "link: ") {
// Link
entry.link = strings.TrimPrefix(text, "link: ")
} else if strings.HasPrefix(text, "reply-link: ") {
// Reply link
entry.replyLink = strings.TrimPrefix(text, "reply-link: ")
} else if strings.HasPrefix(text, "reply-title: ") {
// Reply title
entry.replyTitle = strings.TrimPrefix(text, "reply-title: ")
} else if strings.HasPrefix(text, "like-link: ") {
// Like link
entry.likeLink = strings.TrimPrefix(text, "like-link: ")
} else if strings.HasPrefix(text, "like-title: ") {
// Like title
entry.likeTitle = strings.TrimPrefix(text, "like-title: ")
2019-11-07 10:00:24 +00:00
} else {
_, _ = fmt.Fprintln(&filteredContent, text)
2019-11-07 10:00:24 +00:00
}
}
entry.content = filteredContent.String()
2019-11-07 10:00:24 +00:00
// Compute slug if empty
if len(entry.slug) == 0 || entry.slug == "" {
2019-11-07 10:00:24 +00:00
random := generateRandomString(now, 5)
entry.slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
2019-11-07 10:00:24 +00:00
}
// Compute filename and location
2019-12-03 16:46:16 +00:00
if len(entry.section) < 1 {
entry.section = "micro"
}
entry.section = strings.ToLower(entry.section)
if entry.section == "thoughts" || entry.section == "links" || entry.section == "micro" {
entry.filename = fmt.Sprintf("content/%v/%02d/%02d/%v.md", entry.section, now.Year(), int(now.Month()), entry.slug)
entry.location = fmt.Sprintf("%v%v/%02d/%02d/%v/", BlogUrl, entry.section, now.Year(), int(now.Month()), entry.slug)
2019-11-07 10:00:24 +00:00
} else {
entry.filename = fmt.Sprintf("content/%v/%v.md", entry.section, entry.slug)
entry.location = fmt.Sprintf("%v%v/%v/", BlogUrl, entry.section, entry.slug)
2019-11-07 10:00:24 +00:00
}
return nil
}
func generateRandomString(now time.Time, n int) string {
rand.Seed(now.UnixNano())
letters := []rune("abcdefghijklmnopqrstuvwxyz")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func WriteEntry(entry *Entry) (location string, err error) {
file, err := WriteHugoPost(entry)
2019-11-07 10:00:24 +00:00
if err != nil {
return
2019-11-07 10:00:24 +00:00
}
err = CreateFile(entry.filename, file, entry.title)
if err != nil {
return
}
location = entry.location
return
2019-11-07 10:00:24 +00:00
}
2019-11-14 17:46:49 +00:00
func analyzeURL(url string) (filePath string, section string, slug string, err error) {
2019-12-06 09:32:30 +00:00
if !strings.HasPrefix(url, BlogUrl) {
2019-11-14 17:46:49 +00:00
return
}
2019-12-06 09:32:30 +00:00
path := strings.TrimSuffix(strings.TrimPrefix(url, BlogUrl), "/")
2019-11-14 17:46:49 +00:00
pathParts := strings.Split(path, "/")
filePath = "content/" + path + ".md"
section = pathParts[0]
slug = pathParts[len(pathParts)-1]
return
}
func ReadEntry(url string) (entry *Entry, err error) {
filePath, section, slug, err := analyzeURL(url)
if err != nil {
return
}
2019-12-12 15:29:26 +00:00
fileContent, err := ReadFileContent(filePath)
2019-11-14 17:46:49 +00:00
if err != nil {
return
}
entry, err = ReadHugoPost(fileContent)
if entry != nil {
entry.location = url
entry.filename = filePath
entry.section = section
entry.slug = slug
}
return
}