add new templates and template helper functions, render webring on homepage

This commit is contained in:
yequari 2023-07-22 01:23:47 -07:00
parent 4c3811bc87
commit 2b761b31c3
9 changed files with 136 additions and 47 deletions

View File

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"text/template"
"git.32bit.cafe/yequari/webring/internal/models" "git.32bit.cafe/yequari/webring/internal/models"
) )
@ -15,21 +14,14 @@ func (app *application) home(w http.ResponseWriter, r *http.Request) {
return return
} }
files := []string{ sites, err := app.siteEntries.Latest()
"./ui/html/base.tmpl.html",
"./ui/html/pages/home.tmpl.html",
}
ts, err := template.ParseFiles(files...)
if err != nil {
app.serverError(w, err)
return
}
err = ts.ExecuteTemplate(w, "base", nil)
if err != nil { if err != nil {
app.serverError(w, err) app.serverError(w, err)
} }
app.render(w, http.StatusOK, "home.tmpl.html", &templateData{
SiteEntries: sites,
})
} }
func (app *application) webmasterView(w http.ResponseWriter, r *http.Request) { func (app *application) webmasterView(w http.ResponseWriter, r *http.Request) {
@ -52,19 +44,13 @@ func (app *application) webmasterCreate(w http.ResponseWriter, r *http.Request)
} }
func (app *application) siteEntryView(w http.ResponseWriter, r *http.Request) { func (app *application) siteEntryView(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/sites/view" {
entries, err := app.siteEntries.Latest()
if err != nil {
app.serverError(w, err)
return
}
for _, entry := range entries {
fmt.Fprintf(w, "%+v\n", entry)
}
return
}
id := models.SiteId(r.URL.Query().Get("id")) id := models.SiteId(r.URL.Query().Get("id"))
if id == "" {
app.notFound(w)
return
}
siteEntry, err := app.siteEntries.Get(id) siteEntry, err := app.siteEntries.Get(id)
if err != nil { if err != nil {
if errors.Is(err, models.ErrNoRecord) { if errors.Is(err, models.ErrNoRecord) {
@ -75,7 +61,20 @@ func (app *application) siteEntryView(w http.ResponseWriter, r *http.Request) {
return return
} }
fmt.Fprintf(w, "%+v", siteEntry) webmaster, err := app.webmasters.Get(siteEntry.Webmaster)
if err != nil {
if errors.Is(err, models.ErrNoRecord) {
app.notFound(w)
} else {
app.serverError(w, err)
}
return
}
app.render(w, http.StatusOK, "view.tmpl.html", &templateData{
SiteEntry: siteEntry,
Webmaster: webmaster,
})
} }
func (app *application) siteEntryCreate(w http.ResponseWriter, r *http.Request) { func (app *application) siteEntryCreate(w http.ResponseWriter, r *http.Request) {

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"net/http" "net/http"
"path" "path"
@ -30,3 +31,26 @@ func (app *application) cleanUrl(url string) string {
s = "http://" + s s = "http://" + s
return s return s
} }
func (app *application) render(w http.ResponseWriter, status int, page string, data *templateData) {
ts, ok := app.templateCache[page]
if !ok {
err := fmt.Errorf("the template %s does not exist", page)
app.serverError(w, err)
return
}
// a buffer to attempt to write the template to
// before writing it to the ResponseWriter w
buf := new(bytes.Buffer)
err := ts.ExecuteTemplate(buf, "base", data)
if err != nil {
app.serverError(w, err)
return
}
w.WriteHeader(status)
buf.WriteTo(w)
}

View File

@ -1,11 +1,12 @@
package main package main
import ( import (
"database/sql"
"flag" "flag"
"log" "log"
"net/http" "net/http"
"os" "os"
"database/sql" "text/template"
"git.32bit.cafe/yequari/webring/internal/models" "git.32bit.cafe/yequari/webring/internal/models"
@ -17,6 +18,7 @@ type application struct {
infoLog *log.Logger infoLog *log.Logger
siteEntries *models.SiteEntryModel siteEntries *models.SiteEntryModel
webmasters *models.WebmasterModel webmasters *models.WebmasterModel
templateCache map[string]*template.Template
} }
func main() { func main() {
@ -33,11 +35,17 @@ func main() {
} }
defer db.Close() defer db.Close()
templateCache, err := newTemplateCache()
if err != nil {
errorLog.Fatal(err)
}
app := &application{ app := &application{
errorLog: errorLog, errorLog: errorLog,
infoLog: infoLog, infoLog: infoLog,
siteEntries: &models.SiteEntryModel{DB: db}, siteEntries: &models.SiteEntryModel{DB: db},
webmasters: &models.WebmasterModel{DB: db}, webmasters: &models.WebmasterModel{DB: db},
templateCache: templateCache,
} }
srv := &http.Server{ srv := &http.Server{

48
cmd/web/templates.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"path/filepath"
"text/template"
"git.32bit.cafe/yequari/webring/internal/models"
)
type templateData struct {
SiteEntry *models.SiteEntry
Webmaster *models.Webmaster
SiteEntries []*models.SiteEntry
}
func newTemplateCache() (map[string]*template.Template, error) {
cache := map[string]*template.Template{}
pages, err := filepath.Glob("./ui/html/pages/*.tmpl.html")
if err != nil {
return nil, err
}
for _, page := range pages {
name := filepath.Base(page)
ts, err := template.ParseFiles("./ui/html/base.tmpl.html")
if err != nil {
return nil, err
}
// parse all partials into the template set
ts, err = ts.ParseGlob("./ui/html/partials/*.tmpl.html")
if err != nil {
return nil, err
}
// parse the page template last
ts, err = ts.ParseFiles(page)
if err != nil {
return nil, err
}
cache[name] = ts
}
return cache, nil
}

View File

@ -47,8 +47,7 @@ func (m *SiteEntryModel) Get(id SiteId) (*SiteEntry, error) {
s := &SiteEntry{} s := &SiteEntry{}
var timeStr string err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &s.DateAdded, &s.Next, &s.Prev)
err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &timeStr, &s.Next, &s.Prev)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNoRecord return nil, ErrNoRecord
@ -56,13 +55,6 @@ func (m *SiteEntryModel) Get(id SiteId) (*SiteEntry, error) {
return nil, err return nil, err
} }
} }
// time is stored as a string in sqlite, so we need to parse it
// probably is something taken care of in the driver
dateAdded, err := time.Parse(time.DateTime, timeStr)
if err != nil {
return nil, err
}
s.DateAdded = dateAdded
return s, nil return s, nil
} }
@ -76,8 +68,7 @@ func (m *SiteEntryModel) GetByUrl(url string) (*SiteEntry, error) {
s := &SiteEntry{} s := &SiteEntry{}
var timeStr string err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &s.DateAdded, &s.Next, &s.Prev)
err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &timeStr, &s.Next, &s.Prev)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNoRecord return nil, ErrNoRecord
@ -85,15 +76,8 @@ func (m *SiteEntryModel) GetByUrl(url string) (*SiteEntry, error) {
return nil, err return nil, err
} }
} }
// time is stored as a string in sqlite, so we need to parse it
// probably is something taken care of in the driver
s.DateAdded, err = time.Parse(time.DateTime, timeStr)
if err != nil {
return nil, err
}
return s, nil return s, nil
} }
// Update existing SiteEntry with the values of passed entry // Update existing SiteEntry with the values of passed entry
@ -119,15 +103,14 @@ func (m *SiteEntryModel) Latest() ([]*SiteEntry, error) {
defer rows.Close() defer rows.Close()
entries := []*SiteEntry{} entries := []*SiteEntry{}
var timeStr string // var timeStr string
for rows.Next() { for rows.Next() {
s := &SiteEntry{} s := &SiteEntry{}
err = rows.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &timeStr) err = rows.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &s.DateAdded)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s.DateAdded, err = time.Parse(time.DateTime, timeStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -8,6 +8,7 @@
<body> <body>
<header> <header>
<h1>Webring</h1> <h1>Webring</h1>
{{template "nav" .}}
</header> </header>
<main> <main>
{{template "main" .}} {{template "main" .}}

View File

@ -2,5 +2,13 @@
{{define "main"}} {{define "main"}}
<h2>Latest Websites</h2> <h2>Latest Websites</h2>
{{ if .SiteEntries }}
<ul>
{{ range .SiteEntries }}
<li><a href="{{.Url}}">{{.Name}}</a> (<a href="/sites/view?id={{.Id}}">info</a>)</li>
{{ end }}
</ul>
{{ else }}
<p>There's nothing to see here yet!</p> <p>There's nothing to see here yet!</p>
{{ end }}
{{end}} {{end}}

View File

@ -0,0 +1,13 @@
{{define "title"}}Site Entry #{{.SiteEntry.Id}}{{end}}
{{define "main"}}
<div class='snippet'>
<div class='metadata'>
<strong>{{.SiteEntry.Name}}</strong>
<span>#{{.SiteEntry.Id}}</span>
</div>
<div class='metadata'>
<time>Created: {{.SiteEntry.DateAdded}}</time>
<p>Webmaster: {{.Webmaster.Name}}</p>
</div>
</div>
{{end}}

View File

@ -0,0 +1,5 @@
{{define "nav"}}
<nav>
<a href="/">Home</a>
</nav>
{{end}}