add new templates and template helper functions, render webring on homepage
This commit is contained in:
parent
4c3811bc87
commit
2b761b31c3
|
@ -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,18 +44,12 @@ 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" {
|
id := models.SiteId(r.URL.Query().Get("id"))
|
||||||
entries, err := app.siteEntries.Latest()
|
|
||||||
if err != nil {
|
if id == "" {
|
||||||
app.serverError(w, err)
|
app.notFound(w)
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
fmt.Fprintf(w, "%+v\n", entry)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := models.SiteId(r.URL.Query().Get("id"))
|
|
||||||
|
|
||||||
siteEntry, err := app.siteEntries.Get(id)
|
siteEntry, err := app.siteEntries.Get(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
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"
|
||||||
|
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
type application struct {
|
type application struct {
|
||||||
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>Webring</h1>
|
<h1>Webring</h1>
|
||||||
|
{{template "nav" .}}
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{{template "main" .}}
|
{{template "main" .}}
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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}}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "nav"}}
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</nav>
|
||||||
|
{{end}}
|
Loading…
Reference in New Issue