Merge branch 'master' of ssh://git.libove.org:222/h4kor/owl-blogs
This commit is contained in:
commit
0723beb92a
|
@ -45,6 +45,7 @@ Posts are Markdown files with a mandatory metadata head.
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
title: My new Post
|
title: My new Post
|
||||||
|
date: 13 Aug 2022 17:07 UTC
|
||||||
aliases:
|
aliases:
|
||||||
- /my/new/post
|
- /my/new/post
|
||||||
- /old_blog_path/
|
- /old_blog_path/
|
||||||
|
|
|
@ -76,3 +76,124 @@ func TestNoRedirectOnNonExistingAliases(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoRedirectIfValidPostUrl(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
post2, _ := user.CreateNewPost("post-2")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test\n"
|
||||||
|
content += "aliases: \n"
|
||||||
|
content += " - " + post2.UrlPath() + "\n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("GET", post2.UrlPath(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusOK {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRedirectIfInvalidPostUrl(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test\n"
|
||||||
|
content += "aliases: \n"
|
||||||
|
content += " - " + user.UrlPath() + "posts/not-a-real-post/" + "\n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("GET", user.UrlPath()+"posts/not-a-real-post/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusMovedPermanently {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRedirectIfInvalidUserUrl(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test\n"
|
||||||
|
content += "aliases: \n"
|
||||||
|
content += " - /user/not-real/ \n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("GET", "/user/not-real/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusMovedPermanently {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRedirectIfInvalidMediaUrl(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test\n"
|
||||||
|
content += "aliases: \n"
|
||||||
|
content += " - " + post.UrlMediaPath("not-real") + "\n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("GET", post.UrlMediaPath("not-real"), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusMovedPermanently {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -41,8 +41,7 @@ func userIndexHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Requ
|
||||||
user, err := getUserFromRepo(repo, ps)
|
user, err := getUserFromRepo(repo, ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Error getting user: ", err.Error())
|
println("Error getting user: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("User not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
html, err := owl.RenderIndexPage(user)
|
html, err := owl.RenderIndexPage(user)
|
||||||
|
@ -57,6 +56,26 @@ func userIndexHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Requ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func userRSSHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
user, err := getUserFromRepo(repo, ps)
|
||||||
|
if err != nil {
|
||||||
|
println("Error getting user: ", err.Error())
|
||||||
|
notFoundHandler(repo)(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html, err := owl.RenderRSSFeed(user)
|
||||||
|
if err != nil {
|
||||||
|
println("Error rendering index page: ", err.Error())
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("Internal server error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("Rendering index page for user", user.Name())
|
||||||
|
w.Write([]byte(html))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func postHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func postHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
postId := ps.ByName("post")
|
postId := ps.ByName("post")
|
||||||
|
@ -64,15 +83,13 @@ func postHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request,
|
||||||
user, err := getUserFromRepo(repo, ps)
|
user, err := getUserFromRepo(repo, ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Error getting user: ", err.Error())
|
println("Error getting user: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("User not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
post, err := user.GetPost(postId)
|
post, err := user.GetPost(postId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Error getting post: ", err.Error())
|
println("Error getting post: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("Post not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
html, err := owl.RenderPost(post)
|
html, err := owl.RenderPost(post)
|
||||||
|
@ -96,22 +113,19 @@ func postMediaHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Requ
|
||||||
user, err := getUserFromRepo(repo, ps)
|
user, err := getUserFromRepo(repo, ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Error getting user: ", err.Error())
|
println("Error getting user: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("User not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
post, err := user.GetPost(postId)
|
post, err := user.GetPost(postId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("Error getting post: ", err.Error())
|
println("Error getting post: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("Post not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
filepath = path.Join(post.MediaDir(), filepath)
|
filepath = path.Join(post.MediaDir(), filepath)
|
||||||
if _, err := os.Stat(filepath); err != nil {
|
if _, err := os.Stat(filepath); err != nil {
|
||||||
println("Error getting file: ", err.Error())
|
println("Error getting file: ", err.Error())
|
||||||
w.WriteHeader(http.StatusNotFound)
|
notFoundHandler(repo)(w, r)
|
||||||
w.Write([]byte("File not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.ServeFile(w, r, filepath)
|
http.ServeFile(w, r, filepath)
|
||||||
|
|
|
@ -14,6 +14,7 @@ func Router(repo *owl.Repository) http.Handler {
|
||||||
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
||||||
router.GET("/", repoIndexHandler(repo))
|
router.GET("/", repoIndexHandler(repo))
|
||||||
router.GET("/user/:user/", userIndexHandler(repo))
|
router.GET("/user/:user/", userIndexHandler(repo))
|
||||||
|
router.GET("/user/:user/index.xml", userRSSHandler(repo))
|
||||||
router.GET("/user/:user/posts/:post/", postHandler(repo))
|
router.GET("/user/:user/posts/:post/", postHandler(repo))
|
||||||
router.GET("/user/:user/posts/:post/media/*filepath", postMediaHandler(repo))
|
router.GET("/user/:user/posts/:post/media/*filepath", postMediaHandler(repo))
|
||||||
router.NotFound = http.HandlerFunc(notFoundHandler(repo))
|
router.NotFound = http.HandlerFunc(notFoundHandler(repo))
|
||||||
|
@ -24,6 +25,7 @@ func SingleUserRouter(repo *owl.Repository) http.Handler {
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
||||||
router.GET("/", userIndexHandler(repo))
|
router.GET("/", userIndexHandler(repo))
|
||||||
|
router.GET("/index.xml", userRSSHandler(repo))
|
||||||
router.GET("/posts/:post/", postHandler(repo))
|
router.GET("/posts/:post/", postHandler(repo))
|
||||||
router.GET("/posts/:post/media/*filepath", postMediaHandler(repo))
|
router.GET("/posts/:post/media/*filepath", postMediaHandler(repo))
|
||||||
return router
|
return router
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
domain: "http://localhost:8080"
|
46
post.go
46
post.go
|
@ -6,9 +6,9 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
meta "github.com/yuin/goldmark-meta"
|
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
"github.com/yuin/goldmark/parser"
|
"github.com/yuin/goldmark/parser"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Post struct {
|
type Post struct {
|
||||||
|
@ -17,6 +17,12 @@ type Post struct {
|
||||||
title string
|
title string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PostMeta struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Aliases []string `yaml:"aliases"`
|
||||||
|
Date string `yaml:"date"`
|
||||||
|
}
|
||||||
|
|
||||||
func (post Post) Id() string {
|
func (post Post) Id() string {
|
||||||
return post.id
|
return post.id
|
||||||
}
|
}
|
||||||
|
@ -33,6 +39,10 @@ func (post Post) UrlPath() string {
|
||||||
return post.user.UrlPath() + "posts/" + post.id + "/"
|
return post.user.UrlPath() + "posts/" + post.id + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (post Post) FullUrl() string {
|
||||||
|
return post.user.FullUrl() + "posts/" + post.id + "/"
|
||||||
|
}
|
||||||
|
|
||||||
func (post Post) UrlMediaPath(filename string) string {
|
func (post Post) UrlMediaPath(filename string) string {
|
||||||
return post.UrlPath() + "media/" + filename
|
return post.UrlPath() + "media/" + filename
|
||||||
}
|
}
|
||||||
|
@ -51,11 +61,27 @@ func (post Post) Content() []byte {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (post Post) MarkdownData() (bytes.Buffer, map[string]interface{}) {
|
func (post Post) MarkdownData() (bytes.Buffer, PostMeta) {
|
||||||
data := post.Content()
|
data := post.Content()
|
||||||
|
|
||||||
|
// get yaml metadata block
|
||||||
|
meta := PostMeta{}
|
||||||
|
trimmedData := bytes.TrimSpace(data)
|
||||||
|
// 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 {
|
||||||
|
metaData := trimmedData[:end]
|
||||||
|
yaml.Unmarshal(metaData, &meta)
|
||||||
|
data = trimmedData[end+5:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
markdown := goldmark.New(
|
markdown := goldmark.New(
|
||||||
goldmark.WithExtensions(
|
goldmark.WithExtensions(
|
||||||
meta.Meta,
|
// meta.Meta,
|
||||||
extension.GFM,
|
extension.GFM,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -64,21 +90,13 @@ func (post Post) MarkdownData() (bytes.Buffer, map[string]interface{}) {
|
||||||
if err := markdown.Convert(data, &buf, parser.WithContext(context)); err != nil {
|
if err := markdown.Convert(data, &buf, parser.WithContext(context)); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
metaData := meta.Get(context)
|
// metaData := meta.Get(context)
|
||||||
|
|
||||||
return buf, metaData
|
return buf, meta
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (post Post) Aliases() []string {
|
func (post Post) Aliases() []string {
|
||||||
_, metaData := post.MarkdownData()
|
_, metaData := post.MarkdownData()
|
||||||
if metaData["aliases"] != nil {
|
return metaData.Aliases
|
||||||
alias_data := metaData["aliases"].([]interface{})
|
|
||||||
aliases := make([]string, 0)
|
|
||||||
for _, alias := range alias_data {
|
|
||||||
aliases = append(aliases, alias.(string))
|
|
||||||
}
|
|
||||||
return aliases
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
}
|
}
|
||||||
|
|
11
post_test.go
11
post_test.go
|
@ -34,6 +34,17 @@ func TestPostUrlPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostFullUrl(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
post, _ := user.CreateNewPost("testpost")
|
||||||
|
expected := "http://localhost:8080/user/" + user.Name() + "/posts/" + post.Id() + "/"
|
||||||
|
if !(post.FullUrl() == expected) {
|
||||||
|
t.Error("Wrong url path")
|
||||||
|
t.Error("Expected: " + expected)
|
||||||
|
t.Error(" Got: " + post.FullUrl())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPostUrlMediaPath(t *testing.T) {
|
func TestPostUrlMediaPath(t *testing.T) {
|
||||||
user := getTestUser()
|
user := getTestUser()
|
||||||
post, _ := user.CreateNewPost("testpost")
|
post, _ := user.CreateNewPost("testpost")
|
||||||
|
|
|
@ -25,6 +25,10 @@ type Repository struct {
|
||||||
active_user string
|
active_user string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RepoConfig struct {
|
||||||
|
Domain string `yaml:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
func CreateRepository(name string) (Repository, error) {
|
func CreateRepository(name string) (Repository, error) {
|
||||||
newRepo := Repository{name: name}
|
newRepo := Repository{name: name}
|
||||||
// check if repository already exists
|
// check if repository already exists
|
||||||
|
@ -46,9 +50,15 @@ func CreateRepository(name string) (Repository, error) {
|
||||||
os.WriteFile(newRepo.StaticDir()+"/"+file.Name(), src_data, 0644)
|
os.WriteFile(newRepo.StaticDir()+"/"+file.Name(), src_data, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy repo_base.html to base.html
|
// copy repo/ to newRepo.Dir()
|
||||||
src_data, _ := static_files.ReadFile("embed/initial/repo_base.html")
|
init_files, _ := static_files.ReadDir("embed/initial/repo")
|
||||||
os.WriteFile(newRepo.Dir()+"/base.html", src_data, 0644)
|
for _, file := range init_files {
|
||||||
|
if file.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
src_data, _ := static_files.ReadFile("embed/initial/repo/" + file.Name())
|
||||||
|
os.WriteFile(newRepo.Dir()+"/"+file.Name(), src_data, 0644)
|
||||||
|
}
|
||||||
return newRepo, nil
|
return newRepo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +113,11 @@ func (repo Repository) UserUrlPath(user User) string {
|
||||||
return "/user/" + user.name + "/"
|
return "/user/" + user.name + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo Repository) FullUrl() string {
|
||||||
|
config, _ := repo.Config()
|
||||||
|
return config.Domain
|
||||||
|
}
|
||||||
|
|
||||||
func (repo Repository) Template() (string, error) {
|
func (repo Repository) Template() (string, error) {
|
||||||
// load base.html
|
// load base.html
|
||||||
path := path.Join(repo.Dir(), "base.html")
|
path := path.Join(repo.Dir(), "base.html")
|
||||||
|
@ -179,3 +194,17 @@ func (repo Repository) PostAliases() (map[string]*Post, error) {
|
||||||
}
|
}
|
||||||
return aliases, nil
|
return aliases, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo Repository) Config() (RepoConfig, error) {
|
||||||
|
config_path := path.Join(repo.Dir(), "config.yml")
|
||||||
|
config_data, err := ioutil.ReadFile(config_path)
|
||||||
|
if err != nil {
|
||||||
|
return RepoConfig{}, err
|
||||||
|
}
|
||||||
|
var meta RepoConfig
|
||||||
|
err = yaml.Unmarshal(config_data, &meta)
|
||||||
|
if err != nil {
|
||||||
|
return RepoConfig{}, err
|
||||||
|
}
|
||||||
|
return meta, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package owl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RSS struct {
|
||||||
|
XMLName xml.Name `xml:"rss"`
|
||||||
|
Version string `xml:"version,attr"`
|
||||||
|
Channel RSSChannel `xml:"channel"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RSSChannel struct {
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Link string `xml:"link"`
|
||||||
|
Description string `xml:"description"`
|
||||||
|
Items []RSSItem `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RSSItem struct {
|
||||||
|
Guid string `xml:"guid"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Link string `xml:"link"`
|
||||||
|
PubDate string `xml:"pubDate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderRSSFeed(user User) (string, error) {
|
||||||
|
|
||||||
|
config, _ := user.Config()
|
||||||
|
|
||||||
|
rss := RSS{
|
||||||
|
Version: "2.0",
|
||||||
|
Channel: RSSChannel{
|
||||||
|
Title: config.Title,
|
||||||
|
Link: user.FullUrl(),
|
||||||
|
Description: config.SubTitle,
|
||||||
|
Items: make([]RSSItem, 0),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
posts, _ := user.Posts()
|
||||||
|
for _, postId := range posts {
|
||||||
|
post, _ := user.GetPost(postId)
|
||||||
|
_, meta := post.MarkdownData()
|
||||||
|
rss.Channel.Items = append(rss.Channel.Items, RSSItem{
|
||||||
|
Guid: postId,
|
||||||
|
Title: post.Title(),
|
||||||
|
Link: post.FullUrl(),
|
||||||
|
PubDate: meta.Date,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err := xml.NewEncoder(buf).Encode(rss)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return xml.Header + buf.String(), nil
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package owl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"h4kor/owl-blogs"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenderRSSFeedMeta(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
user.SetConfig(owl.UserConfig{
|
||||||
|
Title: "Test Title",
|
||||||
|
SubTitle: "Test SubTitle",
|
||||||
|
})
|
||||||
|
res, err := owl.RenderRSSFeed(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error rendering RSS feed: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>") {
|
||||||
|
t.Error("xml version not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "<rss version=\"2.0\">") {
|
||||||
|
t.Error("rss version not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRSSFeedUserData(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
user.SetConfig(owl.UserConfig{
|
||||||
|
Title: "Test Title",
|
||||||
|
SubTitle: "Test SubTitle",
|
||||||
|
})
|
||||||
|
res, err := owl.RenderRSSFeed(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error rendering RSS feed: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "Test Title") {
|
||||||
|
t.Error("Title not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "Test SubTitle") {
|
||||||
|
t.Error("SubTitle not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "http://localhost:8080/user/") {
|
||||||
|
t.Error("SubTitle not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRSSFeedPostData(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
post, _ := user.CreateNewPost("testpost")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test Post\n"
|
||||||
|
content += "date: 2015-01-01\n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
res, err := owl.RenderRSSFeed(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error rendering RSS feed: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "Test Post") {
|
||||||
|
t.Error("Title not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, post.FullUrl()) {
|
||||||
|
t.Error("SubTitle not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "2015-01-01") {
|
||||||
|
t.Error("Date not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRSSFeedPostDataWithoutDate(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
post, _ := user.CreateNewPost("testpost")
|
||||||
|
|
||||||
|
content := "---\n"
|
||||||
|
content += "title: Test Post\n"
|
||||||
|
content += "---\n"
|
||||||
|
content += "This is a test"
|
||||||
|
os.WriteFile(post.ContentFile(), []byte(content), 0644)
|
||||||
|
|
||||||
|
res, err := owl.RenderRSSFeed(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error rendering RSS feed: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, "Test Post") {
|
||||||
|
t.Error("Title not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
if !strings.Contains(res, post.FullUrl()) {
|
||||||
|
t.Error("SubTitle not rendered. Got: " + res)
|
||||||
|
}
|
||||||
|
}
|
14
user.go
14
user.go
|
@ -3,6 +3,7 @@ package owl
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
@ -29,6 +30,11 @@ func (user User) UrlPath() string {
|
||||||
return user.repo.UserUrlPath(user)
|
return user.repo.UserUrlPath(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user User) FullUrl() string {
|
||||||
|
url, _ := url.JoinPath(user.repo.FullUrl(), user.UrlPath())
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
func (user User) PostDir() string {
|
func (user User) PostDir() string {
|
||||||
return path.Join(user.Dir(), "public")
|
return path.Join(user.Dir(), "public")
|
||||||
}
|
}
|
||||||
|
@ -60,9 +66,14 @@ func (user User) Posts() ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user User) GetPost(id string) (Post, error) {
|
func (user User) GetPost(id string) (Post, error) {
|
||||||
|
// check if posts index.md exists
|
||||||
|
if !fileExists(path.Join(user.Dir(), "public", id, "index.md")) {
|
||||||
|
return Post{}, fmt.Errorf("post %s does not exist", id)
|
||||||
|
}
|
||||||
|
|
||||||
post := Post{user: &user, id: id}
|
post := Post{user: &user, id: id}
|
||||||
_, metaData := post.MarkdownData()
|
_, metaData := post.MarkdownData()
|
||||||
title := metaData["title"]
|
title := metaData.Title
|
||||||
post.title = fmt.Sprint(title)
|
post.title = fmt.Sprint(title)
|
||||||
|
|
||||||
return post, nil
|
return post, nil
|
||||||
|
@ -89,6 +100,7 @@ func (user User) CreateNewPost(title string) (Post, error) {
|
||||||
initial_content := ""
|
initial_content := ""
|
||||||
initial_content += "---\n"
|
initial_content += "---\n"
|
||||||
initial_content += "title: " + title + "\n"
|
initial_content += "title: " + title + "\n"
|
||||||
|
initial_content += "date: " + time.Now().UTC().Format("02 Jan 2006 15:04 MST") + "\n"
|
||||||
initial_content += "---\n"
|
initial_content += "---\n"
|
||||||
initial_content += "\n"
|
initial_content += "\n"
|
||||||
initial_content += "Write your post here.\n"
|
initial_content += "Write your post here.\n"
|
||||||
|
|
19
user_test.go
19
user_test.go
|
@ -35,6 +35,18 @@ func TestCreateNewPostCreatesMediaDir(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateNewPostAddsDateToMetaBlock(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
// Create a new post
|
||||||
|
user.CreateNewPost("testpost")
|
||||||
|
posts, _ := user.Posts()
|
||||||
|
post, _ := user.GetPost(posts[0])
|
||||||
|
_, meta := post.MarkdownData()
|
||||||
|
if meta.Date == "" {
|
||||||
|
t.Error("Found no date. Got: " + meta.Date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateNewPostMultipleCalls(t *testing.T) {
|
func TestCreateNewPostMultipleCalls(t *testing.T) {
|
||||||
// Create a new user
|
// Create a new user
|
||||||
repo, _ := owl.CreateRepository(testRepoName())
|
repo, _ := owl.CreateRepository(testRepoName())
|
||||||
|
@ -145,3 +157,10 @@ func TestUserUrlPath(t *testing.T) {
|
||||||
t.Error("Wrong url path, Expected: " + "/user/" + user.Name() + "/" + " Got: " + user.UrlPath())
|
t.Error("Wrong url path, Expected: " + "/user/" + user.Name() + "/" + " Got: " + user.UrlPath())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUserFullUrl(t *testing.T) {
|
||||||
|
user := getTestUser()
|
||||||
|
if !(user.FullUrl() == "http://localhost:8080/user/"+user.Name()+"/") {
|
||||||
|
t.Error("Wrong url path, Expected: " + "http://localhost:8080/user/" + user.Name() + "/" + " Got: " + user.FullUrl())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue