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"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"text/template"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/yequari/webring/internal/models"
 | 
			
		||||
)
 | 
			
		||||
@ -15,21 +14,14 @@ func (app *application) home(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    files := []string{
 | 
			
		||||
        "./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)
 | 
			
		||||
    sites, err := app.siteEntries.Latest()
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        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) {
 | 
			
		||||
@ -52,18 +44,12 @@ func (app *application) webmasterCreate(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)
 | 
			
		||||
        }
 | 
			
		||||
    id := models.SiteId(r.URL.Query().Get("id"))
 | 
			
		||||
 | 
			
		||||
    if id == "" {
 | 
			
		||||
        app.notFound(w)
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
    id := models.SiteId(r.URL.Query().Get("id"))
 | 
			
		||||
 | 
			
		||||
    siteEntry, err := app.siteEntries.Get(id)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
@ -75,7 +61,20 @@ func (app *application) siteEntryView(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        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) {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"path"
 | 
			
		||||
@ -30,3 +31,26 @@ func (app *application) cleanUrl(url string) string {
 | 
			
		||||
    s = "http://" + 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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
    "database/sql"
 | 
			
		||||
	"text/template"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/yequari/webring/internal/models"
 | 
			
		||||
 | 
			
		||||
    _ "modernc.org/sqlite"
 | 
			
		||||
	_ "modernc.org/sqlite"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type application struct {
 | 
			
		||||
@ -17,6 +18,7 @@ type application struct {
 | 
			
		||||
    infoLog *log.Logger
 | 
			
		||||
    siteEntries *models.SiteEntryModel
 | 
			
		||||
    webmasters *models.WebmasterModel
 | 
			
		||||
    templateCache map[string]*template.Template
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
@ -33,11 +35,17 @@ func main() {
 | 
			
		||||
    }
 | 
			
		||||
    defer db.Close()
 | 
			
		||||
 | 
			
		||||
    templateCache, err := newTemplateCache()
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        errorLog.Fatal(err)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app := &application{
 | 
			
		||||
        errorLog: errorLog,
 | 
			
		||||
        infoLog: infoLog,
 | 
			
		||||
        siteEntries: &models.SiteEntryModel{DB: db},
 | 
			
		||||
        webmasters: &models.WebmasterModel{DB: db},
 | 
			
		||||
        templateCache: templateCache,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    srv := &http.Server{
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								cmd/web/templates.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cmd/web/templates.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
@ -47,8 +47,7 @@ func (m *SiteEntryModel) Get(id SiteId) (*SiteEntry, error) {
 | 
			
		||||
    s := &SiteEntry{}
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    var timeStr string
 | 
			
		||||
    err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &timeStr, &s.Next, &s.Prev)
 | 
			
		||||
    err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &s.DateAdded, &s.Next, &s.Prev)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
            return nil, ErrNoRecord
 | 
			
		||||
@ -56,13 +55,6 @@ func (m *SiteEntryModel) Get(id SiteId) (*SiteEntry, error) {
 | 
			
		||||
            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
 | 
			
		||||
}
 | 
			
		||||
@ -76,8 +68,7 @@ func (m *SiteEntryModel) GetByUrl(url string) (*SiteEntry, error) {
 | 
			
		||||
    s := &SiteEntry{}
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    var timeStr string
 | 
			
		||||
    err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &timeStr, &s.Next, &s.Prev)
 | 
			
		||||
    err := row.Scan(&s.Id, &s.Name, &s.Url, &s.Webmaster, &s.DateAdded, &s.Next, &s.Prev)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
            return nil, ErrNoRecord
 | 
			
		||||
@ -85,15 +76,8 @@ func (m *SiteEntryModel) GetByUrl(url string) (*SiteEntry, error) {
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update existing SiteEntry with the values of passed entry
 | 
			
		||||
@ -119,15 +103,14 @@ func (m *SiteEntryModel) Latest() ([]*SiteEntry, error) {
 | 
			
		||||
    defer rows.Close()
 | 
			
		||||
 | 
			
		||||
    entries := []*SiteEntry{}
 | 
			
		||||
    var timeStr string
 | 
			
		||||
    // var timeStr string
 | 
			
		||||
 | 
			
		||||
    for rows.Next() {
 | 
			
		||||
        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 {
 | 
			
		||||
            return nil, err
 | 
			
		||||
        }
 | 
			
		||||
        s.DateAdded, err = time.Parse(time.DateTime, timeStr)
 | 
			
		||||
        if err != nil {
 | 
			
		||||
            return nil, err
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
    <body>
 | 
			
		||||
        <header>
 | 
			
		||||
            <h1>Webring</h1>
 | 
			
		||||
            {{template "nav" .}}
 | 
			
		||||
        </header>
 | 
			
		||||
        <main>
 | 
			
		||||
            {{template "main" .}}
 | 
			
		||||
 | 
			
		||||
@ -2,5 +2,13 @@
 | 
			
		||||
 | 
			
		||||
{{define "main"}}
 | 
			
		||||
<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>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								ui/html/pages/view.tmpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								ui/html/pages/view.tmpl.html
									
									
									
									
									
										Normal 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}}
 | 
			
		||||
							
								
								
									
										5
									
								
								ui/html/partials/nav.tmpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ui/html/partials/nav.tmpl.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
{{define "nav"}}
 | 
			
		||||
<nav>
 | 
			
		||||
    <a href="/">Home</a>
 | 
			
		||||
</nav>
 | 
			
		||||
{{end}}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user