Add Contact Page and Update Navigation Links
- Updated navigation links across multiple blog pages to replace "Index" with "Home" and added "Contact" link. - Created a new contact page with a form for user inquiries. - Added a new contact template to render the contact page content. - Updated blog index page to enhance structure and styling. - Added a contact markdown file for content management.
This commit is contained in:
298
main.go
298
main.go
@@ -11,6 +11,8 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"sort"
|
||||
"io/fs"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -23,6 +25,7 @@ type NavItem struct {
|
||||
|
||||
type PageMeta struct {
|
||||
Title string `yaml:"title"`
|
||||
NavTitle string `yaml:"navTitle"`
|
||||
Description string `yaml:"description"`
|
||||
Date string `yaml:"date"`
|
||||
Categories []string `yaml:"categories"`
|
||||
@@ -35,8 +38,7 @@ func main() {
|
||||
contentDir := "./content"
|
||||
outputDir := "./public"
|
||||
|
||||
// Load all templates in the templates folder
|
||||
tpl, err := template.New("").Funcs(templateFuncs()).ParseGlob("templates/*.html")
|
||||
tpl, err := loadTemplates()
|
||||
if err != nil {
|
||||
fmt.Printf("Template parsing error: %v\n", err)
|
||||
return
|
||||
@@ -48,69 +50,159 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
entries, _ := os.ReadDir(contentDir)
|
||||
nav := buildNav(entries, contentDir)
|
||||
renderStaticPages(files, contentDir, outputDir, tpl, nav)
|
||||
blogPosts, categoryMap := processBlogPosts(contentDir, outputDir, tpl, nav)
|
||||
buildBlogIndex(blogPosts, tpl, outputDir, nav)
|
||||
buildCategoryPages(categoryMap, tpl, outputDir, nav)
|
||||
|
||||
fmt.Println("✅ Site generation complete.")
|
||||
}
|
||||
|
||||
func loadTemplates() (*template.Template, error) {
|
||||
return template.New("").Funcs(templateFuncs()).ParseGlob("templates/*.html")
|
||||
}
|
||||
|
||||
func buildNav(entries []fs.DirEntry, contentDir string) []NavItem {
|
||||
var nav []NavItem
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) == ".md" {
|
||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
url := "/"
|
||||
if name != "index" {
|
||||
url = "/" + name + "/"
|
||||
}
|
||||
nav = append(nav, NavItem{Title: strings.Title(name), URL: url})
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.Type().IsDir() || !strings.HasSuffix(entry.Name(), ".md") {
|
||||
continue
|
||||
}
|
||||
|
||||
name := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name()))
|
||||
path := filepath.Join(contentDir, entry.Name())
|
||||
|
||||
rawContent, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read %s: %v\n", entry.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
meta, _ := parseFrontMatter(rawContent)
|
||||
|
||||
title := meta.NavTitle
|
||||
if title == "" {
|
||||
title = meta.Title
|
||||
}
|
||||
if title == "" && name == "index" {
|
||||
title = "Home"
|
||||
} else if title == "" {
|
||||
title = strings.Title(name)
|
||||
}
|
||||
|
||||
url := "/"
|
||||
if name != "index" {
|
||||
url = "/" + name + "/"
|
||||
}
|
||||
|
||||
nav = append(nav, NavItem{Title: title, URL: url})
|
||||
}
|
||||
|
||||
// Add blog to main nav
|
||||
// Add Blog manually
|
||||
nav = append(nav, NavItem{Title: "Blog", URL: "/blog/"})
|
||||
|
||||
// Static page rendering
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) == ".md" {
|
||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
title := strings.Title(name)
|
||||
mdPath := filepath.Join(contentDir, file.Name())
|
||||
rawContent, err := ioutil.ReadFile(mdPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read %s: %v\n", file.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
meta, content := parseFrontMatter(rawContent)
|
||||
htmlContent := blackfriday.Run(content)
|
||||
if meta.Title == "" {
|
||||
meta.Title = title
|
||||
}
|
||||
|
||||
var outPath string
|
||||
if name == "index" {
|
||||
outPath = filepath.Join(outputDir, "index.html")
|
||||
} else {
|
||||
subDir := filepath.Join(outputDir, name)
|
||||
os.MkdirAll(subDir, os.ModePerm)
|
||||
outPath = filepath.Join(subDir, "index.html")
|
||||
}
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create %s: %v\n", outPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
tpl.ExecuteTemplate(outFile, "static_page", map[string]interface{}{
|
||||
"Title": meta.Title,
|
||||
"Description": meta.Description,
|
||||
"Date": meta.Date,
|
||||
"Categories": meta.Categories,
|
||||
"Content": template.HTML(htmlContent),
|
||||
"Nav": nav,
|
||||
"Year": time.Now().Year(),
|
||||
"PageTemplate": "static",
|
||||
})
|
||||
|
||||
fmt.Printf("Generated: %s\n", outPath)
|
||||
}
|
||||
// Optional: order the nav explicitly
|
||||
preferredOrder := map[string]int{
|
||||
"Home": 0,
|
||||
"Blog": 1,
|
||||
"About": 2,
|
||||
"Contact": 3,
|
||||
}
|
||||
|
||||
sort.SliceStable(nav, func(i, j int) bool {
|
||||
return preferredOrder[nav[i].Title] < preferredOrder[nav[j].Title]
|
||||
})
|
||||
|
||||
return nav
|
||||
}
|
||||
|
||||
func renderStaticPages(files []os.FileInfo, contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) == ".md" {
|
||||
renderStaticPage(file, contentDir, outputDir, tpl, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderStaticPage(file os.FileInfo, contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
title := strings.Title(name)
|
||||
mdPath := filepath.Join(contentDir, file.Name())
|
||||
rawContent, err := ioutil.ReadFile(mdPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read %s: %v\n", file.Name(), err)
|
||||
return
|
||||
}
|
||||
|
||||
meta, content := parseFrontMatter(rawContent)
|
||||
htmlContent := blackfriday.Run(content)
|
||||
if meta.Title == "" {
|
||||
meta.Title = title
|
||||
}
|
||||
|
||||
var outPath string
|
||||
if name == "index" {
|
||||
outPath = filepath.Join(outputDir, "index.html")
|
||||
} else {
|
||||
subDir := filepath.Join(outputDir, name)
|
||||
os.MkdirAll(subDir, os.ModePerm)
|
||||
outPath = filepath.Join(subDir, "index.html")
|
||||
}
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create %s: %v\n", outPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
tpl.ExecuteTemplate(outFile, "static_page", map[string]interface{}{
|
||||
"Title": meta.Title,
|
||||
"Description": meta.Description,
|
||||
"Date": meta.Date,
|
||||
"Categories": meta.Categories,
|
||||
"Content": template.HTML(htmlContent),
|
||||
"Nav": nav,
|
||||
"Year": time.Now().Year(),
|
||||
"PageTemplate": "static",
|
||||
})
|
||||
|
||||
fmt.Printf("Generated: %s\n", outPath)
|
||||
}
|
||||
|
||||
func renderContactPage(contentDir, outputDir string, tpl *template.Template, nav []NavItem) {
|
||||
contactPath := filepath.Join(contentDir, "contact.md")
|
||||
rawContent, err := os.ReadFile(contactPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Contact page not found: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
meta, _ := parseFrontMatter(rawContent)
|
||||
|
||||
outPath := filepath.Join(outputDir, "contact", "index.html")
|
||||
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create contact page: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tpl.ExecuteTemplate(outFile, "contact_page", map[string]interface{}{
|
||||
"Title": meta.Title,
|
||||
"Description": meta.Description,
|
||||
"Nav": nav,
|
||||
"Year": time.Now().Year(),
|
||||
"PageTemplate": "contact_page",
|
||||
})
|
||||
|
||||
fmt.Println("Generated: /contact/")
|
||||
}
|
||||
|
||||
func processBlogPosts(contentDir, outputDir string, tpl *template.Template, nav []NavItem) ([]PageMeta, map[string][]PageMeta) {
|
||||
var blogPosts []PageMeta
|
||||
categoryMap := make(map[string][]PageMeta)
|
||||
|
||||
@@ -122,51 +214,57 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
slug := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
blogPath := filepath.Join(blogDir, file.Name())
|
||||
rawContent, err := ioutil.ReadFile(blogPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read blog post %s: %v\n", file.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
meta, content := parseFrontMatter(rawContent)
|
||||
html := blackfriday.Run(content)
|
||||
meta.Slug = slug
|
||||
if meta.Title == "" {
|
||||
meta.Title = strings.Title(slug)
|
||||
}
|
||||
|
||||
for _, cat := range meta.Categories {
|
||||
slug := slugify(cat)
|
||||
categoryMap[slug] = append(categoryMap[slug], meta)
|
||||
}
|
||||
|
||||
outPath := filepath.Join(outputDir, "blog", slug, "index.html")
|
||||
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create blog file %s: %v\n", outPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
tpl.ExecuteTemplate(outFile, "blog_post_page", map[string]interface{}{
|
||||
"Title": meta.Title,
|
||||
"Description": meta.Description,
|
||||
"Date": meta.Date,
|
||||
"Categories": meta.Categories,
|
||||
"Content": template.HTML(html),
|
||||
"Nav": nav,
|
||||
"Year": time.Now().Year(),
|
||||
"PageTemplate": "blog_post",
|
||||
})
|
||||
|
||||
blogPosts = append(blogPosts, meta)
|
||||
processBlogPost(file, blogDir, outputDir, tpl, nav, &blogPosts, categoryMap)
|
||||
}
|
||||
|
||||
buildBlogIndex(blogPosts, tpl, outputDir, nav)
|
||||
return blogPosts, categoryMap
|
||||
}
|
||||
|
||||
func processBlogPost(file os.FileInfo, blogDir, outputDir string, tpl *template.Template, nav []NavItem, blogPosts *[]PageMeta, categoryMap map[string][]PageMeta) {
|
||||
slug := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
blogPath := filepath.Join(blogDir, file.Name())
|
||||
rawContent, err := ioutil.ReadFile(blogPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read blog post %s: %v\n", file.Name(), err)
|
||||
return
|
||||
}
|
||||
|
||||
meta, content := parseFrontMatter(rawContent)
|
||||
html := blackfriday.Run(content)
|
||||
meta.Slug = slug
|
||||
if meta.Title == "" {
|
||||
meta.Title = strings.Title(slug)
|
||||
}
|
||||
|
||||
for _, cat := range meta.Categories {
|
||||
slug := slugify(cat)
|
||||
categoryMap[slug] = append(categoryMap[slug], meta)
|
||||
}
|
||||
|
||||
outPath := filepath.Join(outputDir, "blog", slug, "index.html")
|
||||
os.MkdirAll(filepath.Dir(outPath), os.ModePerm)
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create blog file %s: %v\n", outPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
tpl.ExecuteTemplate(outFile, "blog_post_page", map[string]interface{}{
|
||||
"Title": meta.Title,
|
||||
"Description": meta.Description,
|
||||
"Date": meta.Date,
|
||||
"Categories": meta.Categories,
|
||||
"Content": template.HTML(html),
|
||||
"Nav": nav,
|
||||
"Year": time.Now().Year(),
|
||||
"PageTemplate": "blog_post",
|
||||
})
|
||||
|
||||
*blogPosts = append(*blogPosts, meta)
|
||||
}
|
||||
|
||||
func buildCategoryPages(categoryMap map[string][]PageMeta, tpl *template.Template, outputDir string, nav []NavItem) {
|
||||
for catSlug, posts := range categoryMap {
|
||||
outDir := filepath.Join(outputDir, "blog", "category", catSlug)
|
||||
os.MkdirAll(outDir, os.ModePerm)
|
||||
@@ -188,8 +286,6 @@ func main() {
|
||||
|
||||
fmt.Printf("Generated category: /blog/category/%s/\n", catSlug)
|
||||
}
|
||||
|
||||
fmt.Println("✅ Site generation complete.")
|
||||
}
|
||||
|
||||
func parseFrontMatter(raw []byte) (PageMeta, []byte) {
|
||||
|
||||
Reference in New Issue
Block a user