diff --git a/articles.go b/articles.go deleted file mode 100644 index 49ada2b..0000000 --- a/articles.go +++ /dev/null @@ -1,204 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "os" - "path" - "sort" - "strings" - "time" - - "github.com/yuin/goldmark" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/util" - - chromahtml "github.com/alecthomas/chroma/formatters/html" - mathjax "github.com/litao91/goldmark-mathjax" - highlighting "github.com/yuin/goldmark-highlighting" - - "gopkg.in/yaml.v3" -) - -var md goldmark.Markdown - -// https://github.com/yuin/goldmark-highlighting/blob/9216f9c5aa010c549cc9fc92bb2593ab299f90d4/highlighting_test.go#L27 -func customCodeBlockWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) { - lang, ok := c.Language() - if entering { - if ok { - w.WriteString(fmt.Sprintf(`
`, lang))
- return
- }
- w.WriteString("")
- } else {
- if ok {
- w.WriteString("
")
- return
- }
- w.WriteString("")
- }
-}
-
-func init() {
- md = goldmark.New(
- goldmark.WithExtensions(
- extension.GFM,
- extension.Typographer,
- // Questo pacchetto ha un nome stupido perché in realtà si occupa solo del parsing lato server del Markdown mentre lato client usiamo KaTeX.
- mathjax.NewMathJax(),
- highlighting.NewHighlighting(
- highlighting.WithStyle("github"),
- highlighting.WithWrapperRenderer(customCodeBlockWrapper),
- highlighting.WithFormatOptions(
- chromahtml.PreventSurroundingPre(true),
- ),
- ),
- ),
- goldmark.WithParserOptions(
- parser.WithAutoHeadingID(),
- ),
- goldmark.WithRendererOptions(
- html.WithHardWraps(),
- html.WithXHTML(),
- ),
- )
-}
-
-type ArticleFrontMatter struct {
- Title string `yaml:"title"`
- Description string `yaml:"description"`
- Tags string `yaml:"tags"`
- PublishDate string `yaml:"publish_date"`
-}
-
-type Article struct {
- Id string
- Title string
- Description string
- Tags []string
- PublishDate time.Time
-
- MarkdownSource string
- renderedHTML string
-}
-
-func (article *Article) HasTag(tag string) bool {
- for _, t := range article.Tags {
- if t == tag {
- return true
- }
- }
-
- return false
-}
-
-func (article *Article) Render() (string, error) {
- if article.renderedHTML == "" {
- var buf bytes.Buffer
- if err := md.Convert([]byte(article.MarkdownSource), &buf); err != nil {
- return "", err
- }
-
- article.renderedHTML = buf.String()
- }
-
- return article.renderedHTML, nil
-}
-
-type ArticleRenderer struct {
- RootPath string
-}
-
-func trimAll(vs []string) []string {
- r := []string{}
- for _, v := range vs {
- r = append(r, strings.TrimSpace(v))
- }
- return r
-}
-
-func removeBlanks(v []string) []string {
- r := []string{}
-
- for _, s := range v {
- if len(strings.TrimSpace(s)) > 0 {
- r = append(r, s)
- }
- }
-
- return r
-}
-
-func (registry *ArticleRenderer) Load(articlePath string) (*Article, error) {
- fileBytes, err := os.ReadFile(path.Join(registry.RootPath, articlePath+".md"))
- if err != nil {
- return nil, err
- }
-
- source := string(fileBytes)
-
- // TODO: Ehm bugia esiste "https://github.com/yuin/goldmark-meta" però boh per ora funge tutto
- parts := removeBlanks(strings.Split(source, "---"))
-
- frontMatterSource := parts[0]
- markdownSource := strings.Join(parts[1:], "---")
-
- var frontMatter ArticleFrontMatter
- if err := yaml.Unmarshal([]byte(frontMatterSource), &frontMatter); err != nil {
- return nil, err
- }
-
- publishDate, err := time.Parse("2006/01/02 15:04", frontMatter.PublishDate)
- if err != nil {
- return nil, err
- }
-
- tags := trimAll(strings.Split(frontMatter.Tags, ","))
-
- article := &Article{
- Id: articlePath,
- Title: frontMatter.Title,
- Description: frontMatter.Description,
- Tags: tags,
- PublishDate: publishDate,
- MarkdownSource: markdownSource,
- }
-
- return article, nil
-}
-
-func (registry *ArticleRenderer) LoadAll() ([]*Article, error) {
- entries, err := os.ReadDir(registry.RootPath)
- if err != nil {
- return nil, err
- }
-
- articles := []*Article{}
-
- for _, entry := range entries {
- if !entry.IsDir() {
- name := strings.TrimRight(entry.Name(), ".md")
- article, err := registry.Load(name)
- if err != nil {
- return nil, err
- }
-
- articles = append(articles, article)
- }
- }
-
- sort.Slice(articles, func(i, j int) bool {
- return articles[i].PublishDate.After(articles[j].PublishDate)
- })
-
- return articles, nil
-}
-
-func NewArticleRenderer(rootPath string) *ArticleRenderer {
- return &ArticleRenderer{
- rootPath,
- }
-}
diff --git a/articles/article.go b/articles/article.go
new file mode 100644
index 0000000..d914ff9
--- /dev/null
+++ b/articles/article.go
@@ -0,0 +1,113 @@
+package articles
+
+import (
+ "bytes"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/phc-dm/phc-server/config"
+ "gopkg.in/yaml.v3"
+)
+
+type Article struct {
+ Id string
+ Title string
+ Description string
+ Tags []string
+ PublishDate time.Time
+
+ ArticlePath string
+ markdownSource string
+ renderedHTML string
+}
+
+func (article *Article) HasTag(tag string) bool {
+ for _, t := range article.Tags {
+ if t == tag {
+ return true
+ }
+ }
+
+ return false
+}
+
+func NewArticle(articlePath string) (*Article, error) {
+ article := &Article{
+ ArticlePath: articlePath,
+ }
+
+ err := article.load()
+ if err != nil {
+ return nil, err
+ }
+
+ return article, nil
+}
+
+func trimAll(vs []string) []string {
+ r := []string{}
+ for _, v := range vs {
+ r = append(r, strings.TrimSpace(v))
+ }
+ return r
+}
+
+func (article *Article) load() error {
+ fileBytes, err := os.ReadFile(article.ArticlePath)
+ if err != nil {
+ return err
+ }
+
+ source := string(fileBytes)
+
+ // TODO: Ehm bugia pare che esista "https://github.com/yuin/goldmark-meta" però non penso valga la pena aggiungerlo
+ parts := strings.SplitN(source, "---", 3)[1:]
+
+ frontMatterSource := parts[0]
+ markdownSource := parts[1]
+
+ var frontMatter struct {
+ Id string `yaml:"id"`
+ Title string `yaml:"title"`
+ Description string `yaml:"description"`
+ Tags string `yaml:"tags"`
+ PublishDate string `yaml:"publish_date"`
+ }
+ if err := yaml.Unmarshal([]byte(frontMatterSource), &frontMatter); err != nil {
+ return err
+ }
+
+ publishDate, err := time.Parse("2006/01/02 15:04", frontMatter.PublishDate)
+ if err != nil {
+ return err
+ }
+
+ article.Id = frontMatter.Id
+ article.Title = frontMatter.Title
+ article.Description = frontMatter.Description
+ article.Tags = trimAll(strings.Split(frontMatter.Tags, ","))
+ article.PublishDate = publishDate
+
+ article.markdownSource = markdownSource
+ article.renderedHTML = ""
+
+ return nil
+}
+
+func (article *Article) Render() (string, error) {
+ if config.Mode == "development" {
+ article.load()
+ }
+
+ if article.renderedHTML == "" {
+ var buf bytes.Buffer
+ if err := articleMarkdown.Convert([]byte(article.markdownSource), &buf); err != nil {
+ return "", err
+ }
+
+ article.renderedHTML = buf.String()
+ }
+
+ return article.renderedHTML, nil
+}
diff --git a/articles/articles.go b/articles/articles.go
new file mode 100644
index 0000000..46140e5
--- /dev/null
+++ b/articles/articles.go
@@ -0,0 +1,77 @@
+package articles
+
+import (
+ "fmt"
+ "os"
+ "path"
+ "sort"
+)
+
+type Registry struct {
+ RootPath string
+ ArticleCache map[string]*Article
+}
+
+func NewRegistry(rootPath string) *Registry {
+ return &Registry{
+ rootPath,
+ map[string]*Article{},
+ }
+}
+
+func (registry *Registry) loadArticles() error {
+ entries, err := os.ReadDir(registry.RootPath)
+ if err != nil {
+ return err
+ }
+
+ for _, entry := range entries {
+ if !entry.IsDir() {
+ article, err := NewArticle(path.Join(registry.RootPath, entry.Name()))
+ if err != nil {
+ return err
+ }
+
+ registry.ArticleCache[article.Id] = article
+ }
+ }
+
+ return nil
+}
+
+func (registry *Registry) GetArticle(id string) (*Article, error) {
+ article, present := registry.ArticleCache[id]
+ if !present {
+ err := registry.loadArticles()
+ if err != nil {
+ return nil, err
+ }
+
+ article, present := registry.ArticleCache[id]
+ if !present {
+ return nil, fmt.Errorf(`no article with id "%s"`, id)
+ }
+
+ return article, nil
+ }
+
+ return article, nil
+}
+
+func (registry *Registry) GetArticles() ([]*Article, error) {
+ err := registry.loadArticles()
+ if err != nil {
+ return nil, err
+ }
+
+ articles := []*Article{}
+ for _, article := range registry.ArticleCache {
+ articles = append(articles, article)
+ }
+
+ sort.Slice(articles, func(i, j int) bool {
+ return articles[i].PublishDate.After(articles[j].PublishDate)
+ })
+
+ return articles, nil
+}
diff --git a/articles/markdown.go b/articles/markdown.go
new file mode 100644
index 0000000..f081395
--- /dev/null
+++ b/articles/markdown.go
@@ -0,0 +1,60 @@
+package articles
+
+import (
+ "fmt"
+
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer/html"
+ "github.com/yuin/goldmark/util"
+
+ chromahtml "github.com/alecthomas/chroma/formatters/html"
+ mathjax "github.com/litao91/goldmark-mathjax"
+ highlighting "github.com/yuin/goldmark-highlighting"
+)
+
+var articleMarkdown goldmark.Markdown
+
+// https://github.com/yuin/goldmark-highlighting/blob/9216f9c5aa010c549cc9fc92bb2593ab299f90d4/highlighting_test.go#L27
+func customCodeBlockWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) {
+ lang, ok := c.Language()
+ if entering {
+ if ok {
+ w.WriteString(fmt.Sprintf(``, lang))
+ return
+ }
+ w.WriteString("")
+ } else {
+ if ok {
+ w.WriteString("
")
+ return
+ }
+ w.WriteString("")
+ }
+}
+
+func init() {
+ articleMarkdown = goldmark.New(
+ goldmark.WithExtensions(
+ extension.GFM,
+ extension.Typographer,
+ // Questo pacchetto ha un nome stupido perché in realtà si occupa solo del parsing lato server del Markdown mentre lato client usiamo KaTeX.
+ mathjax.NewMathJax(),
+ highlighting.NewHighlighting(
+ highlighting.WithStyle("github"),
+ highlighting.WithWrapperRenderer(customCodeBlockWrapper),
+ highlighting.WithFormatOptions(
+ chromahtml.PreventSurroundingPre(true),
+ ),
+ ),
+ ),
+ goldmark.WithParserOptions(
+ parser.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ html.WithHardWraps(),
+ html.WithXHTML(),
+ ),
+ )
+}
diff --git a/main.go b/main.go
index 8360911..2965146 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
+ "github.com/phc-dm/phc-server/articles"
"github.com/phc-dm/phc-server/config"
"github.com/phc-dm/phc-server/templates"
"github.com/phc-dm/phc-server/util"
@@ -31,7 +32,7 @@ func main() {
templates.File("./views/base.html"),
templates.Pattern("./views/partials/*.html"),
)
- newsArticlesRegistry := NewArticleRenderer("./news")
+ newsArticlesRegistry := articles.NewRegistry("./news")
// Routes
@@ -71,7 +72,7 @@ func main() {
})
r.Get("/news", func(w http.ResponseWriter, r *http.Request) {
- articles, err := newsArticlesRegistry.LoadAll()
+ articles, err := newsArticlesRegistry.GetArticles()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -86,9 +87,9 @@ func main() {
})
r.Get("/news/{article}", func(w http.ResponseWriter, r *http.Request) {
- articleName := chi.URLParam(r, "article")
+ articleID := chi.URLParam(r, "article")
- article, err := newsArticlesRegistry.Load(articleName)
+ article, err := newsArticlesRegistry.GetArticle(articleID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
diff --git a/news/2021-12-22-notizia-1.md b/news/2021-12-22-notizia-1.md
index a6c63b0..d7660f4 100644
--- a/news/2021-12-22-notizia-1.md
+++ b/news/2021-12-22-notizia-1.md
@@ -1,4 +1,5 @@
---
+id: 2021-12-22-notizia-1
title: "Notizia 1"
tags: important, prova, test, foo, bar
publish_date: 2021/12/22 22:00
diff --git a/news/2021-12-23-notizia-2.md b/news/2021-12-23-notizia-2.md
index 5b970f9..64523ce 100644
--- a/news/2021-12-23-notizia-2.md
+++ b/news/2021-12-23-notizia-2.md
@@ -1,4 +1,5 @@
---
+id: 2021-12-23-notizia-2
title: "Notizia 2"
tags: prova, test, foo, bar
publish_date: 2021/12/23 22:00
diff --git a/news/2021-12-24-notizia-3.md b/news/2021-12-24-notizia-3.md
index ee576fb..8132c17 100644
--- a/news/2021-12-24-notizia-3.md
+++ b/news/2021-12-24-notizia-3.md
@@ -1,4 +1,5 @@
---
+id: 2021-12-24-notizia-3
title: "Notizia 3"
tags: prova, test, foo, bar
publish_date: 2021/12/24 18:00