Implement remote embedding of guestbooks #25
@ -1,6 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@ -136,6 +137,30 @@ func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Requ
 | 
			
		||||
	views.GuestbookDashboardCommentsView("Comments", data, website, website.Guestbook, comments).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getGuestbookCommentsSerialized(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrNoRecord) {
 | 
			
		||||
			http.NotFound(w, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !website.Guestbook.Settings.IsVisible || !website.Guestbook.Settings.AllowRemoteHostAccess {
 | 
			
		||||
		app.clientError(w, http.StatusForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	comments, err := app.guestbookComments.GetAllSerialized(website.Guestbook.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b, err := json.Marshal(comments)
 | 
			
		||||
	w.Write(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	// TODO: This will be the embeddable form
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
@ -148,13 +173,21 @@ func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	s := website.Guestbook.Settings
 | 
			
		||||
	if !s.IsVisible || !s.AllowRemoteHostAccess || !website.Guestbook.CanComment() {
 | 
			
		||||
		app.clientError(w, http.StatusForbidden)
 | 
			
		||||
	}
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	form := forms.CommentCreateForm{}
 | 
			
		||||
	views.CreateGuestbookComment("New Comment", data, website, website.Guestbook, form).Render(r.Context(), w)
 | 
			
		||||
	views.EmbeddableGuestbookCommentForm(data, website, form).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	headless, err := strconv.ParseBool(r.URL.Query().Get("headless"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		headless = false
 | 
			
		||||
	}
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrNoRecord) {
 | 
			
		||||
@ -184,14 +217,17 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
 | 
			
		||||
	form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
 | 
			
		||||
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		data := app.newCommonData(r)
 | 
			
		||||
		w.WriteHeader(http.StatusUnprocessableEntity)
 | 
			
		||||
		if headless {
 | 
			
		||||
			views.EmbeddableGuestbookCommentForm(data, website, form).Render(r.Context(), w)
 | 
			
		||||
		}
 | 
			
		||||
		// TODO: use htmx to avoid getting comments again
 | 
			
		||||
		comments, err := app.guestbookComments.GetAll(website.Guestbook.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		data := app.newCommonData(r)
 | 
			
		||||
		w.WriteHeader(http.StatusUnprocessableEntity)
 | 
			
		||||
		views.GuestbookView("Guestbook", data, website, website.Guestbook, comments, form).Render(r.Context(), w)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@ -203,9 +239,70 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!")
 | 
			
		||||
	if headless {
 | 
			
		||||
		http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook/comments/create", slug), http.StatusSeeOther)
 | 
			
		||||
	}
 | 
			
		||||
	http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook", slug), http.StatusSeeOther)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrNoRecord) {
 | 
			
		||||
			http.NotFound(w, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !matchOrigin(r.Header.Get("Origin"), website.Url) {
 | 
			
		||||
		app.clientError(w, http.StatusForbidden)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !website.Guestbook.CanComment() {
 | 
			
		||||
		app.clientError(w, http.StatusForbidden)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var form forms.CommentCreateForm
 | 
			
		||||
	err = app.decodePostForm(r, &form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.clientError(w, http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	form.CheckField(validator.NotBlank(form.AuthorName), "authorName", "This field cannot be blank")
 | 
			
		||||
	form.CheckField(validator.MaxChars(form.AuthorName, 256), "authorName", "This field cannot be more than 256 characters long")
 | 
			
		||||
	form.CheckField(validator.MaxChars(form.AuthorEmail, 256), "authorEmail", "This field cannot be more than 256 characters long")
 | 
			
		||||
	form.CheckField(validator.MaxChars(form.AuthorSite, 256), "authorSite", "This field cannot be more than 256 characters long")
 | 
			
		||||
	form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
 | 
			
		||||
 | 
			
		||||
	// if redirect path is filled out, redirect to that path on the website host
 | 
			
		||||
	// otherwise redirect to the guestbook by default
 | 
			
		||||
	redirectUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(website.ShortId))
 | 
			
		||||
	if form.Redirect != "" {
 | 
			
		||||
		u, err := website.Url.Parse(form.Redirect)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			redirectUrl = u.String()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		views.GuestbookCommentCreateRemoteErrorView(redirectUrl, "Invalid Input").Render(r.Context(), w)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shortId := app.createShortId()
 | 
			
		||||
	_, err = app.guestbookComments.Insert(shortId, website.Guestbook.ID, 0, form.AuthorName, form.AuthorEmail, form.AuthorSite, form.Content, "", true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	views.GuestbookCommentCreateRemoteSuccessView(redirectUrl).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getCommentQueue(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
 | 
			
		||||
@ -144,3 +144,88 @@ func TestPostGuestbookCommentCreate(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPostGuestbookCommentCreateRemote(t *testing.T) {
 | 
			
		||||
	app := newTestApplication(t)
 | 
			
		||||
	ts := newTestServer(t, app.routes())
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
 | 
			
		||||
	_, _, body := ts.get(t, fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(1)))
 | 
			
		||||
	validCSRFToken := extractCSRFToken(t, body)
 | 
			
		||||
 | 
			
		||||
	const (
 | 
			
		||||
		validAuthorName  = "John Test"
 | 
			
		||||
		validAuthorEmail = "test@example.com"
 | 
			
		||||
		validAuthorSite  = "example.com"
 | 
			
		||||
		validContent     = "This is a comment"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		authorName  string
 | 
			
		||||
		authorEmail string
 | 
			
		||||
		authorSite  string
 | 
			
		||||
		content     string
 | 
			
		||||
		csrfToken   string
 | 
			
		||||
		wantCode    int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:        "Valid input",
 | 
			
		||||
			authorName:  validAuthorName,
 | 
			
		||||
			authorEmail: validAuthorEmail,
 | 
			
		||||
			authorSite:  validAuthorSite,
 | 
			
		||||
			content:     validContent,
 | 
			
		||||
			csrfToken:   validCSRFToken,
 | 
			
		||||
			wantCode:    http.StatusSeeOther,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "Blank name",
 | 
			
		||||
			authorName:  "",
 | 
			
		||||
			authorEmail: validAuthorEmail,
 | 
			
		||||
			authorSite:  validAuthorSite,
 | 
			
		||||
			content:     validContent,
 | 
			
		||||
			csrfToken:   validCSRFToken,
 | 
			
		||||
			wantCode:    http.StatusUnprocessableEntity,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "Blank email",
 | 
			
		||||
			authorName:  validAuthorName,
 | 
			
		||||
			authorEmail: "",
 | 
			
		||||
			authorSite:  validAuthorSite,
 | 
			
		||||
			content:     validContent,
 | 
			
		||||
			csrfToken:   validCSRFToken,
 | 
			
		||||
			wantCode:    http.StatusSeeOther,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "Blank site",
 | 
			
		||||
			authorName:  validAuthorName,
 | 
			
		||||
			authorEmail: validAuthorEmail,
 | 
			
		||||
			authorSite:  "",
 | 
			
		||||
			content:     validContent,
 | 
			
		||||
			csrfToken:   validCSRFToken,
 | 
			
		||||
			wantCode:    http.StatusSeeOther,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "Blank content",
 | 
			
		||||
			authorName:  validAuthorName,
 | 
			
		||||
			authorEmail: validAuthorEmail,
 | 
			
		||||
			authorSite:  validAuthorSite,
 | 
			
		||||
			content:     "",
 | 
			
		||||
			csrfToken:   validCSRFToken,
 | 
			
		||||
			wantCode:    http.StatusUnprocessableEntity,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			form := url.Values{}
 | 
			
		||||
			form.Add("authorname", tt.authorName)
 | 
			
		||||
			form.Add("authoremail", tt.authorEmail)
 | 
			
		||||
			form.Add("authorsite", tt.authorSite)
 | 
			
		||||
			form.Add("content", tt.content)
 | 
			
		||||
			form.Add("csrf_token", tt.csrfToken)
 | 
			
		||||
			code, _, body := ts.postForm(t, fmt.Sprintf("/websites/%s/guestbook/comments/create/remote", shortIdToSlug(1)), form)
 | 
			
		||||
			assert.Equal(t, code, tt.wantCode)
 | 
			
		||||
			assert.Equal(t, body, body)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
@ -32,14 +33,20 @@ func (app *application) postWebsiteCreate(w http.ResponseWriter, r *http.Request
 | 
			
		||||
	form.CheckField(validator.MaxChars(form.Name, 256), "sitename", "This field cannot exceed 256 characters")
 | 
			
		||||
	form.CheckField(validator.NotBlank(form.SiteUrl), "siteurl", "This field cannot be blank")
 | 
			
		||||
	form.CheckField(validator.MaxChars(form.SiteUrl, 512), "siteurl", "This field cannot exceed 512 characters")
 | 
			
		||||
	form.CheckField(validator.Matches(form.SiteUrl, validator.WebRX), "siteurl", "This field must be a valid URL (including http:// or https://)")
 | 
			
		||||
 | 
			
		||||
	u, err := url.Parse(form.SiteUrl)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		form.CheckField(false, "siteurl", "This field must be a valid URL")
 | 
			
		||||
	}
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		data := app.newCommonData(r)
 | 
			
		||||
		w.WriteHeader(http.StatusUnprocessableEntity)
 | 
			
		||||
		views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	websiteShortID := app.createShortId()
 | 
			
		||||
	_, err = app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName)
 | 
			
		||||
	_, err = app.websites.Insert(websiteShortID, userId, form.Name, u.String(), form.AuthorName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
@ -75,10 +82,6 @@ func (app *application) getWebsiteList(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if r.Header.Get("HX-Request") == "true" {
 | 
			
		||||
		views.HxWebsiteList(websites)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	views.WebsiteList("My Websites", data, websites).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
@ -109,6 +110,7 @@ func (app *application) newCommonData(r *http.Request) views.CommonData {
 | 
			
		||||
		CSRFToken:       nosurf.Token(r),
 | 
			
		||||
		CurrentUser:     app.getCurrentUser(r),
 | 
			
		||||
		IsHtmx:          r.Header.Get("Hx-Request") == "true",
 | 
			
		||||
		RootUrl:         app.rootUrl,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -127,3 +129,14 @@ func (app *application) durationToTime(duration string) (time.Time, error) {
 | 
			
		||||
	result = time.Now().UTC().Add(offset)
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func matchOrigin(origin string, u *url.URL) bool {
 | 
			
		||||
	o, err := url.Parse(origin)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if o.Host != u.Host {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,12 +28,14 @@ type application struct {
 | 
			
		||||
	formDecoder       *schema.Decoder
 | 
			
		||||
	debug             bool
 | 
			
		||||
	timezones         []string
 | 
			
		||||
	rootUrl           string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	addr := flag.String("addr", ":3000", "HTTP network address")
 | 
			
		||||
	dsn := flag.String("dsn", "guestbook.db", "data source name")
 | 
			
		||||
	debug := flag.Bool("debug", false, "enable debug mode")
 | 
			
		||||
	root := flag.String("root", "localhost:3000", "root URL of application")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
 | 
			
		||||
@ -62,6 +64,7 @@ func main() {
 | 
			
		||||
		formDecoder:       formDecoder,
 | 
			
		||||
		debug:             *debug,
 | 
			
		||||
		timezones:         getAvailableTimezones(),
 | 
			
		||||
		rootUrl:           *root,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = app.users.InitializeSettingsMap()
 | 
			
		||||
 | 
			
		||||
@ -8,87 +8,94 @@ import (
 | 
			
		||||
	"github.com/justinas/nosurf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (app *application) logRequest (next http.Handler) http.Handler {
 | 
			
		||||
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        var (
 | 
			
		||||
            ip = r.RemoteAddr
 | 
			
		||||
            proto = r.Proto
 | 
			
		||||
            method = r.Method
 | 
			
		||||
            uri = r.URL.RequestURI()
 | 
			
		||||
        )
 | 
			
		||||
        app.logger.Info("received request", "ip", ip, "proto", proto, "method", method, "uri", uri)
 | 
			
		||||
        next.ServeHTTP(w, r)
 | 
			
		||||
    })
 | 
			
		||||
func (app *application) logRequest(next http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var (
 | 
			
		||||
			ip     = r.RemoteAddr
 | 
			
		||||
			proto  = r.Proto
 | 
			
		||||
			method = r.Method
 | 
			
		||||
			uri    = r.URL.RequestURI()
 | 
			
		||||
		)
 | 
			
		||||
		app.logger.Info("received request", "ip", ip, "proto", proto, "method", method, "uri", uri)
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func commonHeaders (next http.Handler) http.Handler {
 | 
			
		||||
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com")
 | 
			
		||||
        w.Header().Set("Referrer-Policy", "origin-when-cross-origin")
 | 
			
		||||
        w.Header().Set("X-Content-Type-Options", "nosniff")
 | 
			
		||||
        // w.Header().Set("X-Frame-Options", "deny")
 | 
			
		||||
        w.Header().Set("X-XSS-Protection", "0")
 | 
			
		||||
        next.ServeHTTP(w, r)
 | 
			
		||||
    })
 | 
			
		||||
func commonHeaders(next http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com")
 | 
			
		||||
		w.Header().Set("Referrer-Policy", "origin-when-cross-origin")
 | 
			
		||||
		w.Header().Set("X-Content-Type-Options", "nosniff")
 | 
			
		||||
		// w.Header().Set("X-Frame-Options", "deny")
 | 
			
		||||
		w.Header().Set("X-XSS-Protection", "0")
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) recoverPanic(next http.Handler) http.Handler {
 | 
			
		||||
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        defer func() {
 | 
			
		||||
            if err := recover(); err != nil {
 | 
			
		||||
                w.Header().Set("Connection", "close")
 | 
			
		||||
                app.serverError(w, r, fmt.Errorf("%s", err))
 | 
			
		||||
            }
 | 
			
		||||
        }()
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := recover(); err != nil {
 | 
			
		||||
				w.Header().Set("Connection", "close")
 | 
			
		||||
				app.serverError(w, r, fmt.Errorf("%s", err))
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
        next.ServeHTTP(w, r)
 | 
			
		||||
    })
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) requireAuthentication(next http.Handler) http.Handler {
 | 
			
		||||
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        if !app.isAuthenticated(r) {
 | 
			
		||||
            http.Redirect(w, r, "/users/login", http.StatusSeeOther)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        w.Header().Add("Cache-Control", "no-store")
 | 
			
		||||
        next.ServeHTTP(w, r)
 | 
			
		||||
    })
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if !app.isAuthenticated(r) {
 | 
			
		||||
			http.Redirect(w, r, "/users/login", http.StatusSeeOther)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		w.Header().Add("Cache-Control", "no-store")
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func noSurf(next http.Handler) http.Handler {
 | 
			
		||||
    csrfHandler := nosurf.New(next)
 | 
			
		||||
    csrfHandler.SetBaseCookie(http.Cookie{
 | 
			
		||||
        HttpOnly: true,
 | 
			
		||||
        Path: "/",
 | 
			
		||||
        Secure: true,
 | 
			
		||||
    })
 | 
			
		||||
	csrfHandler := nosurf.New(next)
 | 
			
		||||
	csrfHandler.SetBaseCookie(http.Cookie{
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
		Path:     "/",
 | 
			
		||||
		Secure:   true,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
    return csrfHandler
 | 
			
		||||
	return csrfHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) authenticate(next http.Handler) http.Handler {
 | 
			
		||||
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
        id := app.sessionManager.GetInt64(r.Context(), "authenticatedUserId")
 | 
			
		||||
        if id == 0 {
 | 
			
		||||
            next.ServeHTTP(w, r)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        exists, err := app.users.Exists(id)
 | 
			
		||||
        if err != nil {
 | 
			
		||||
            app.serverError(w, r, err)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        user, err := app.users.GetById(id)
 | 
			
		||||
        if err != nil {
 | 
			
		||||
            app.serverError(w, r, err)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if exists {
 | 
			
		||||
            ctx := context.WithValue(r.Context(), isAuthenticatedContextKey, true)
 | 
			
		||||
            ctx = context.WithValue(ctx, userNameContextKey, user)
 | 
			
		||||
            r = r.WithContext(ctx)
 | 
			
		||||
        }
 | 
			
		||||
        next.ServeHTTP(w, r)
 | 
			
		||||
    })
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		id := app.sessionManager.GetInt64(r.Context(), "authenticatedUserId")
 | 
			
		||||
		if id == 0 {
 | 
			
		||||
			next.ServeHTTP(w, r)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		exists, err := app.users.Exists(id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		user, err := app.users.GetById(id)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if exists {
 | 
			
		||||
			ctx := context.WithValue(r.Context(), isAuthenticatedContextKey, true)
 | 
			
		||||
			ctx = context.WithValue(ctx, userNameContextKey, user)
 | 
			
		||||
			r = r.WithContext(ctx)
 | 
			
		||||
		}
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) enableCors(next http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("Access-Control-Allow-Origin", "*")
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,10 +15,14 @@ func (app *application) routes() http.Handler {
 | 
			
		||||
 | 
			
		||||
	dynamic := alice.New(app.sessionManager.LoadAndSave, noSurf, app.authenticate)
 | 
			
		||||
	standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders)
 | 
			
		||||
	withCors := standard.Append(app.enableCors)
 | 
			
		||||
 | 
			
		||||
	mux.Handle("/{$}", dynamic.ThenFunc(app.home))
 | 
			
		||||
	mux.Handle("POST /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/guestbook", dynamic.ThenFunc(app.getGuestbook))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/guestbook/comments", withCors.ThenFunc(app.getGuestbookCommentsSerialized))
 | 
			
		||||
	mux.Handle("POST /websites/{id}/guestbook/comments/create/remote", standard.ThenFunc(app.postGuestbookCommentCreateRemote))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.getGuestbookCommentCreate))
 | 
			
		||||
	mux.Handle("POST /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate))
 | 
			
		||||
	mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister))
 | 
			
		||||
	mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister))
 | 
			
		||||
	mux.Handle("GET /users/login", dynamic.ThenFunc(app.getUserLogin))
 | 
			
		||||
@ -48,7 +52,6 @@ func (app *application) routes() http.Handler {
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/comments/trash", protected.ThenFunc(app.getCommentTrash))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/themes", protected.ThenFunc(app.getComingSoon))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/customize", protected.ThenFunc(app.getComingSoon))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/guestbook/comments/create", protected.ThenFunc(app.getGuestbookCommentCreate))
 | 
			
		||||
 | 
			
		||||
	return standard.Then(mux)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ type CommentCreateForm struct {
 | 
			
		||||
	AuthorEmail         string `schema:"authoremail"`
 | 
			
		||||
	AuthorSite          string `schema:"authorsite"`
 | 
			
		||||
	Content             string `schema:"content"`
 | 
			
		||||
	Redirect            string `schema:"redirect"`
 | 
			
		||||
	validator.Validator `schema:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,12 @@ type GuestbookComment struct {
 | 
			
		||||
	IsPublished bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookCommentSerialized struct {
 | 
			
		||||
	AuthorName  string
 | 
			
		||||
	CommentText string
 | 
			
		||||
	Created     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookCommentModel struct {
 | 
			
		||||
	DB *sql.DB
 | 
			
		||||
}
 | 
			
		||||
@ -28,6 +34,7 @@ type GuestbookCommentModelInterface interface {
 | 
			
		||||
	Insert(shortId uint64, guestbookId, parentId int64, authorName, authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error)
 | 
			
		||||
	Get(shortId uint64) (GuestbookComment, error)
 | 
			
		||||
	GetAll(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	GetAllSerialized(guestbookId int64) ([]GuestbookCommentSerialized, error)
 | 
			
		||||
	GetDeleted(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	GetUnpublished(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	UpdateComment(comment *GuestbookComment) error
 | 
			
		||||
@ -93,6 +100,30 @@ func (m *GuestbookCommentModel) GetAll(guestbookId int64) ([]GuestbookComment, e
 | 
			
		||||
	return comments, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetAllSerialized(guestbookId int64) ([]GuestbookCommentSerialized, error) {
 | 
			
		||||
	stmt := `SELECT AuthorName, CommentText, Created
 | 
			
		||||
	    FROM guestbook_comments 
 | 
			
		||||
	    WHERE GuestbookId = ? AND IsPublished = TRUE AND DELETED IS NULL
 | 
			
		||||
	    ORDER BY Created DESC`
 | 
			
		||||
	rows, err := m.DB.Query(stmt, guestbookId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var comments []GuestbookCommentSerialized
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var c GuestbookCommentSerialized
 | 
			
		||||
		err = rows.Scan(&c.AuthorName, &c.CommentText, &c.Created)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		comments = append(comments, c)
 | 
			
		||||
	}
 | 
			
		||||
	if err = rows.Err(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return comments, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetDeleted(guestbookId int64) ([]GuestbookComment, error) {
 | 
			
		||||
	stmt := `SELECT Id, ShortId, GuestbookId, ParentId, AuthorName, AuthorEmail, AuthorSite,
 | 
			
		||||
    CommentText, PageUrl, Created, IsPublished, Deleted
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,12 @@ var mockGuestbookComment = models.GuestbookComment{
 | 
			
		||||
	IsPublished: true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mockSerializedGuestbookComment = models.GuestbookCommentSerialized{
 | 
			
		||||
	AuthorName:  "John Test",
 | 
			
		||||
	CommentText: "Hello, world",
 | 
			
		||||
	Created:     time.Now().Format(time.RFC3339),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookCommentModel struct{}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName,
 | 
			
		||||
@ -45,6 +51,17 @@ func (m *GuestbookCommentModel) GetAll(guestbookId int64) ([]models.GuestbookCom
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetAllSerialized(guestbookId int64) ([]models.GuestbookCommentSerialized, error) {
 | 
			
		||||
	switch guestbookId {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return []models.GuestbookCommentSerialized{mockSerializedGuestbookComment}, nil
 | 
			
		||||
	case 2:
 | 
			
		||||
		return []models.GuestbookCommentSerialized{}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return []models.GuestbookCommentSerialized{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetDeleted(guestbookId int64) ([]models.GuestbookComment, error) {
 | 
			
		||||
	switch guestbookId {
 | 
			
		||||
	default:
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
@ -22,10 +23,14 @@ var mockGuestbook = models.Guestbook{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mockWebsite = models.Website{
 | 
			
		||||
	ID:         1,
 | 
			
		||||
	ShortId:    1,
 | 
			
		||||
	Name:       "Example",
 | 
			
		||||
	SiteUrl:    "example.com",
 | 
			
		||||
	ID:      1,
 | 
			
		||||
	ShortId: 1,
 | 
			
		||||
	Name:    "Example",
 | 
			
		||||
	// SiteUrl:    "example.com",
 | 
			
		||||
	Url: &url.URL{
 | 
			
		||||
		Scheme: "http",
 | 
			
		||||
		Host:   "example.com",
 | 
			
		||||
	},
 | 
			
		||||
	AuthorName: "John Test",
 | 
			
		||||
	UserId:     1,
 | 
			
		||||
	Created:    time.Now(),
 | 
			
		||||
 | 
			
		||||
@ -3,15 +3,17 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Website struct {
 | 
			
		||||
	ID         int64
 | 
			
		||||
	ShortId    uint64
 | 
			
		||||
	Name       string
 | 
			
		||||
	SiteUrl    string
 | 
			
		||||
	ID      int64
 | 
			
		||||
	ShortId uint64
 | 
			
		||||
	Name    string
 | 
			
		||||
	// SiteUrl    string
 | 
			
		||||
	Url        *url.URL
 | 
			
		||||
	AuthorName string
 | 
			
		||||
	UserId     int64
 | 
			
		||||
	Created    time.Time
 | 
			
		||||
@ -179,7 +181,8 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) {
 | 
			
		||||
	}
 | 
			
		||||
	row := tx.QueryRow(stmt, shortId)
 | 
			
		||||
	var w Website
 | 
			
		||||
	err = row.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created)
 | 
			
		||||
	var u string
 | 
			
		||||
	err = row.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
			err = ErrNoRecord
 | 
			
		||||
@ -189,6 +192,10 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return Website{}, err
 | 
			
		||||
	}
 | 
			
		||||
	w.Url, err = url.Parse(u)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Website{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stmt = `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks
 | 
			
		||||
    WHERE WebsiteId = ? AND Deleted IS NULL`
 | 
			
		||||
@ -244,11 +251,16 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) {
 | 
			
		||||
	var websites []Website
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var w Website
 | 
			
		||||
		err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
		var u string
 | 
			
		||||
		err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
			&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		w.Url, err = url.Parse(u)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		websites = append(websites, w)
 | 
			
		||||
	}
 | 
			
		||||
	if err = rows.Err(); err != nil {
 | 
			
		||||
@ -268,11 +280,16 @@ func (m *WebsiteModel) GetAll() ([]Website, error) {
 | 
			
		||||
	var websites []Website
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var w Website
 | 
			
		||||
		err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
		var u string
 | 
			
		||||
		err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
			&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		w.Url, err = url.Parse(u)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		websites = append(websites, w)
 | 
			
		||||
	}
 | 
			
		||||
	if err = rows.Err(); err != nil {
 | 
			
		||||
 | 
			
		||||
@ -8,51 +8,52 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var EmailRX = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
 | 
			
		||||
var WebRX = regexp.MustCompile("^https?:\\/\\/")
 | 
			
		||||
 | 
			
		||||
type Validator struct {
 | 
			
		||||
    NonFieldErrors []string
 | 
			
		||||
    FieldErrors map[string]string
 | 
			
		||||
	NonFieldErrors []string
 | 
			
		||||
	FieldErrors    map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Validator) Valid() bool {
 | 
			
		||||
    return len(v.FieldErrors) == 0 && len(v.NonFieldErrors) == 0
 | 
			
		||||
	return len(v.FieldErrors) == 0 && len(v.NonFieldErrors) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Validator) AddFieldError(key, message string) {
 | 
			
		||||
    if v.FieldErrors == nil {
 | 
			
		||||
        v.FieldErrors = make(map[string]string)
 | 
			
		||||
    }
 | 
			
		||||
    if _, exists := v.FieldErrors[key]; !exists {
 | 
			
		||||
        v.FieldErrors[key] = message
 | 
			
		||||
    }
 | 
			
		||||
	if v.FieldErrors == nil {
 | 
			
		||||
		v.FieldErrors = make(map[string]string)
 | 
			
		||||
	}
 | 
			
		||||
	if _, exists := v.FieldErrors[key]; !exists {
 | 
			
		||||
		v.FieldErrors[key] = message
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Validator) AddNonFieldError(message string) {
 | 
			
		||||
    v.NonFieldErrors = append(v.NonFieldErrors, message)
 | 
			
		||||
	v.NonFieldErrors = append(v.NonFieldErrors, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Validator) CheckField(ok bool, key, message string) {
 | 
			
		||||
    if !ok {
 | 
			
		||||
        v.AddFieldError(key, message)
 | 
			
		||||
    }
 | 
			
		||||
	if !ok {
 | 
			
		||||
		v.AddFieldError(key, message)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NotBlank(value string) bool {
 | 
			
		||||
    return strings.TrimSpace(value) != ""
 | 
			
		||||
	return strings.TrimSpace(value) != ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MaxChars(value string, n int) bool {
 | 
			
		||||
    return utf8.RuneCountInString(value) <= n
 | 
			
		||||
	return utf8.RuneCountInString(value) <= n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PermittedValue[T comparable](value T, permittedValues ...T) bool {
 | 
			
		||||
    return slices.Contains(permittedValues, value)
 | 
			
		||||
	return slices.Contains(permittedValues, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MinChars(value string, n int) bool {
 | 
			
		||||
    return utf8.RuneCountInString(value) >= n
 | 
			
		||||
	return utf8.RuneCountInString(value) >= n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Matches(value string, rx *regexp.Regexp) bool {
 | 
			
		||||
    return rx.MatchString(value)
 | 
			
		||||
	return rx.MatchString(value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								migrations/000005_normalize_site_urls.down.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								migrations/000005_normalize_site_urls.down.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
UPDATE websites SET SiteUrl = substr(SiteUrl, 8) WHERE substr(SiteUrl, 1, 4) = 'http';
 | 
			
		||||
							
								
								
									
										1
									
								
								migrations/000005_normalize_site_urls.up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								migrations/000005_normalize_site_urls.up.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
UPDATE websites SET SiteUrl = 'http://' || SiteUrl WHERE substr(SiteUrl, 1, 4) <> 'http';
 | 
			
		||||
@ -58,10 +58,17 @@ div#dashboard {
 | 
			
		||||
div#dashboard nav {
 | 
			
		||||
  flex: 1 1 25%;
 | 
			
		||||
  margin-top: 2rem;
 | 
			
		||||
  min-width: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div#dashboard > div {
 | 
			
		||||
  flex: 10 1 40%;
 | 
			
		||||
  min-width: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div > pre {
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main nav ul {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										177
									
								
								ui/static/js/guestbook.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								ui/static/js/guestbook.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,177 @@
 | 
			
		||||
class GuestbookForm extends HTMLElement {
 | 
			
		||||
    static get observedAttributes() {
 | 
			
		||||
        return ['guestbook'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        this.attachShadow({ mode: 'open' });
 | 
			
		||||
        this._guestbook = this.getAttribute('guestbook') || '';
 | 
			
		||||
        this._postUrl = `${this._guestbook}/comments/create/remote`
 | 
			
		||||
        this.render();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attributeChangedCallback(name, oldValue, newValue) {
 | 
			
		||||
        if (name === 'guestbook') {
 | 
			
		||||
            this._guestbook = newValue;
 | 
			
		||||
            this.render();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        this.shadowRoot.innerHTML = `
 | 
			
		||||
      <style>
 | 
			
		||||
        form div {
 | 
			
		||||
          margin-bottom: 0.75em;
 | 
			
		||||
        }
 | 
			
		||||
        label {
 | 
			
		||||
          display: block;
 | 
			
		||||
          font-weight: bold;
 | 
			
		||||
          margin-bottom: 0.25em;
 | 
			
		||||
        }
 | 
			
		||||
        input[type="text"], textarea {
 | 
			
		||||
          width: 100%;
 | 
			
		||||
          box-sizing: border-box;
 | 
			
		||||
        }
 | 
			
		||||
        input[type="submit"] {
 | 
			
		||||
          font-size: 1em;
 | 
			
		||||
          padding: 0.5em 1em;
 | 
			
		||||
        }
 | 
			
		||||
      </style>
 | 
			
		||||
      <form action="${this._postUrl}" method="post">
 | 
			
		||||
        <div>
 | 
			
		||||
          <label for="authorname">Name</label>
 | 
			
		||||
          <input type="text" name="authorname" id="authorname"/>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <label for="authoremail">Email (Optional)</label>
 | 
			
		||||
          <input type="text" name="authoremail" id="authoremail"/>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <label for="authorsite">Site Url (Optional)</label>
 | 
			
		||||
          <input type="text" name="authorsite" id="authorsite"/>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <label for="content">Comment</label>
 | 
			
		||||
          <textarea name="content" id="content"></textarea>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <input type="hidden" value="${window.location.pathname}" name="redirect" id="redirect" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <input type="submit" value="Submit"/>
 | 
			
		||||
        </div>
 | 
			
		||||
      </form>
 | 
			
		||||
    `;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CommentList extends HTMLElement {
 | 
			
		||||
    static get observedAttributes() {
 | 
			
		||||
        return ['guestbook'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        this.attachShadow({ mode: 'open' });
 | 
			
		||||
        this.comments = [];
 | 
			
		||||
        this.loading = false;
 | 
			
		||||
        this.error = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
        if (this.hasAttribute('guestbook')) {
 | 
			
		||||
            this.fetchComments();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attributeChangedCallback(name, oldValue, newValue) {
 | 
			
		||||
        if (name === 'guestbook' && oldValue !== newValue) {
 | 
			
		||||
            this.fetchComments();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fetchComments() {
 | 
			
		||||
        const guestbook = this.getAttribute('guestbook');
 | 
			
		||||
        if (!guestbook) return;
 | 
			
		||||
        this.loading = true;
 | 
			
		||||
        this.error = null;
 | 
			
		||||
        this.render();
 | 
			
		||||
 | 
			
		||||
        const commentsUrl = `${guestbook}/comments`
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const response = await fetch(commentsUrl);
 | 
			
		||||
            if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
 | 
			
		||||
            const data = await response.json();
 | 
			
		||||
            this.comments = Array.isArray(data) ? data : [];
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
            this.render();
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
            this.error = err.message;
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
            this.render();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formatDate(isoString) {
 | 
			
		||||
        if (!isoString) return '';
 | 
			
		||||
        const date = new Date(isoString);
 | 
			
		||||
        return date.toLocaleString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        this.shadowRoot.innerHTML = `
 | 
			
		||||
      <style>
 | 
			
		||||
        .comment-list {
 | 
			
		||||
          font-family: Arial, sans-serif;
 | 
			
		||||
          border: 1px solid #ddd;
 | 
			
		||||
          border-radius: 6px;
 | 
			
		||||
          padding: 1em;
 | 
			
		||||
          background: #fafafa;
 | 
			
		||||
        }
 | 
			
		||||
        .comment {
 | 
			
		||||
          border-bottom: 1px solid #eee;
 | 
			
		||||
          padding: 0.7em 0;
 | 
			
		||||
        }
 | 
			
		||||
        .comment:last-child {
 | 
			
		||||
          border-bottom: none;
 | 
			
		||||
        }
 | 
			
		||||
        .author {
 | 
			
		||||
          font-weight: bold;
 | 
			
		||||
          margin-right: 1em;
 | 
			
		||||
        }
 | 
			
		||||
        .timestamp {
 | 
			
		||||
          color: #888;
 | 
			
		||||
          font-size: 0.85em;
 | 
			
		||||
        }
 | 
			
		||||
        .text {
 | 
			
		||||
          margin: 0.2em 0 0 0;
 | 
			
		||||
        }
 | 
			
		||||
        .error {
 | 
			
		||||
          color: red;
 | 
			
		||||
        }
 | 
			
		||||
      </style>
 | 
			
		||||
      <div class="comment-list">
 | 
			
		||||
        ${this.loading
 | 
			
		||||
                ? `<div>Loading comments...</div>`
 | 
			
		||||
                : this.error
 | 
			
		||||
                    ? `<div class="error">Error: ${this.error}</div>`
 | 
			
		||||
                    : this.comments.length === 0
 | 
			
		||||
                        ? `<div>No comments found.</div>`
 | 
			
		||||
                        : this.comments.map(comment => `
 | 
			
		||||
                  <div class="comment">
 | 
			
		||||
                    <span class="author">${comment.AuthorName || 'Unknown Author'}</span>
 | 
			
		||||
                    <span class="timestamp">${this.formatDate(comment.Created)}</span>
 | 
			
		||||
                    <div class="text">${comment.CommentText || ''}</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                `).join('')
 | 
			
		||||
            }
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define('guestbook-form', GuestbookForm);
 | 
			
		||||
customElements.define('guestbook-comments', CommentList);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								ui/static/js/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								ui/static/js/main.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
 | 
			
		||||
const convertDates = () => {
 | 
			
		||||
    const dates = document.getElementsByTagName("time")
 | 
			
		||||
    for (let i = 0; i < dates.length; i++) {
 | 
			
		||||
        const e = dates.item(i)
 | 
			
		||||
        const d = e.attributes.getNamedItem("datetime").value
 | 
			
		||||
        const dt = new Date(Date.parse(d))
 | 
			
		||||
        const localtime = dt.toLocaleString("en-US", { "year": "numeric", "month": "short", "day": "numeric", "hour": "numeric", "minute": "numeric", "timeZoneName": "short"})
 | 
			
		||||
        e.innerText = localtime
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
convertDates()
 | 
			
		||||
@ -6,87 +6,85 @@ import "fmt"
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
type CommonData struct {
 | 
			
		||||
    CurrentYear int
 | 
			
		||||
    Flash string
 | 
			
		||||
    IsAuthenticated bool
 | 
			
		||||
    CSRFToken string
 | 
			
		||||
    CurrentUser *models.User
 | 
			
		||||
    IsHtmx bool
 | 
			
		||||
	CurrentYear     int
 | 
			
		||||
	Flash           string
 | 
			
		||||
	IsAuthenticated bool
 | 
			
		||||
	CSRFToken       string
 | 
			
		||||
	CurrentUser     *models.User
 | 
			
		||||
	IsHtmx          bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shortIdToSlug(shortId uint64) string {
 | 
			
		||||
    return strconv.FormatUint(shortId, 36)
 | 
			
		||||
	return strconv.FormatUint(shortId, 36)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func slugToShortId(slug string) uint64 {
 | 
			
		||||
    id, _ := strconv.ParseUint(slug, 36, 64)
 | 
			
		||||
    return id
 | 
			
		||||
	id, _ := strconv.ParseUint(slug, 36, 64)
 | 
			
		||||
	return id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func externalUrl(url string) string {
 | 
			
		||||
    if !strings.HasPrefix(url, "http") {
 | 
			
		||||
        return "http://" + url
 | 
			
		||||
    }
 | 
			
		||||
    return url
 | 
			
		||||
	if !strings.HasPrefix(url, "http") {
 | 
			
		||||
		return "http://" + url
 | 
			
		||||
	}
 | 
			
		||||
	return url
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ commonHeader() {
 | 
			
		||||
    <header>
 | 
			
		||||
        <h1><a href="/">webweav.ing</a></h1>
 | 
			
		||||
    </header>
 | 
			
		||||
	<header>
 | 
			
		||||
		<h1><a href="/">webweav.ing</a></h1>
 | 
			
		||||
	</header>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ topNav(data CommonData) {
 | 
			
		||||
    {{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }}
 | 
			
		||||
    <nav>
 | 
			
		||||
        <div>
 | 
			
		||||
            if data.IsAuthenticated {
 | 
			
		||||
                Welcome, { data.CurrentUser.Username }
 | 
			
		||||
            }
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            if data.IsAuthenticated {
 | 
			
		||||
                <a href="/guestbooks">All Guestbooks</a> |
 | 
			
		||||
                <a href="/websites">My Websites</a> | 
 | 
			
		||||
                <a href="/users/settings">Settings</a> | 
 | 
			
		||||
                <a href="#" hx-post="/users/logout" hx-headers={ hxHeaders }>Logout</a>
 | 
			
		||||
            } else {
 | 
			
		||||
                <a href="/users/register">Create an Account</a> | 
 | 
			
		||||
                <a href="/users/login">Login</a>
 | 
			
		||||
            }
 | 
			
		||||
        </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
	{{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }}
 | 
			
		||||
	<nav>
 | 
			
		||||
		<div>
 | 
			
		||||
			if data.IsAuthenticated {
 | 
			
		||||
				Welcome, { data.CurrentUser.Username }
 | 
			
		||||
			}
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			if data.IsAuthenticated {
 | 
			
		||||
				<a href="/guestbooks">All Guestbooks</a> |
 | 
			
		||||
				<a href="/websites">My Websites</a> | 
 | 
			
		||||
				<a href="/users/settings">Settings</a> | 
 | 
			
		||||
				<a href="#" hx-post="/users/logout" hx-headers={ hxHeaders }>Logout</a>
 | 
			
		||||
			} else {
 | 
			
		||||
				<a href="/users/register">Create an Account</a> | 
 | 
			
		||||
				<a href="/users/login">Login</a>
 | 
			
		||||
			}
 | 
			
		||||
		</div>
 | 
			
		||||
	</nav>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
templ commonFooter() {
 | 
			
		||||
    <footer>
 | 
			
		||||
        <p>A <a href="https://32bit.cafe">32bit.cafe</a> Project</p>
 | 
			
		||||
    </footer>
 | 
			
		||||
	<footer>
 | 
			
		||||
		<p>A <a href="https://32bit.cafe">32bit.cafe</a> Project</p>
 | 
			
		||||
	</footer>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ base(title string, data CommonData) {
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html lang="en">
 | 
			
		||||
        <head>
 | 
			
		||||
            <title>{ title } - webweav.ing</title>
 | 
			
		||||
            <meta charset="UTF-8">
 | 
			
		||||
            <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
            <link href="/static/css/classless.min.css" rel="stylesheet">
 | 
			
		||||
            <link href="/static/css/style.css" rel="stylesheet">
 | 
			
		||||
            <script src="/static/js/htmx.min.js"></script>
 | 
			
		||||
        </head>
 | 
			
		||||
        <body>
 | 
			
		||||
            @commonHeader()
 | 
			
		||||
            @topNav(data)
 | 
			
		||||
            <main>
 | 
			
		||||
                if data.Flash != "" {
 | 
			
		||||
                    <div class="flash">{ data.Flash }</div>
 | 
			
		||||
                }
 | 
			
		||||
                { children... }
 | 
			
		||||
            </main>
 | 
			
		||||
            @commonFooter()
 | 
			
		||||
        </body>
 | 
			
		||||
    </html>
 | 
			
		||||
	<!DOCTYPE html>
 | 
			
		||||
	<html lang="en">
 | 
			
		||||
		<head>
 | 
			
		||||
			<title>{ title } - webweav.ing</title>
 | 
			
		||||
			<meta charset="UTF-8"/>
 | 
			
		||||
			<meta name="viewport" content="width=device-width, initial-scale=1"/>
 | 
			
		||||
			<link href="/static/css/classless.min.css" rel="stylesheet"/>
 | 
			
		||||
			<link href="/static/css/style.css" rel="stylesheet"/>
 | 
			
		||||
			<script src="/static/js/htmx.min.js"></script>
 | 
			
		||||
		</head>
 | 
			
		||||
		<body>
 | 
			
		||||
			@commonHeader()
 | 
			
		||||
			@topNav(data)
 | 
			
		||||
			<main>
 | 
			
		||||
				if data.Flash != "" {
 | 
			
		||||
					<div class="flash">{ data.Flash }</div>
 | 
			
		||||
				}
 | 
			
		||||
				{ children... }
 | 
			
		||||
			</main>
 | 
			
		||||
			@commonFooter()
 | 
			
		||||
		</body>
 | 
			
		||||
	</html>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ type CommonData struct {
 | 
			
		||||
	CSRFToken       string
 | 
			
		||||
	CurrentUser     *models.User
 | 
			
		||||
	IsHtmx          bool
 | 
			
		||||
	RootUrl         string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shortIdToSlug(shortId uint64) string {
 | 
			
		||||
@ -101,7 +102,7 @@ func topNav(data CommonData) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var3 string
 | 
			
		||||
			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CurrentUser.Username)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 44, Col: 52}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 44, Col: 40}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -120,7 +121,7 @@ func topNav(data CommonData) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var4 string
 | 
			
		||||
			templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 52, Col: 74}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 52, Col: 62}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -201,7 +202,7 @@ func base(title string, data CommonData) templ.Component {
 | 
			
		||||
		var templ_7745c5c3_Var7 string
 | 
			
		||||
		templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(title)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 72, Col: 26}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 71, Col: 17}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -231,7 +232,7 @@ func base(title string, data CommonData) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var8 string
 | 
			
		||||
			templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.Flash)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 84, Col: 51}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 83, Col: 36}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ templ GuestbookDashboardCommentsView(title string, data CommonData, website mode
 | 
			
		||||
		<div id="dashboard">
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>Comments on { website.SiteUrl }</h1>
 | 
			
		||||
				<h1>Comments on { website.Name }</h1>
 | 
			
		||||
				<hr/>
 | 
			
		||||
				if len(comments) == 0 {
 | 
			
		||||
					<p>No comments yet!</p>
 | 
			
		||||
@ -61,7 +61,7 @@ templ GuestbookDashboardCommentView(data CommonData, w models.Website, c models.
 | 
			
		||||
 | 
			
		||||
templ commentForm(form forms.CommentCreateForm) {
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authorname">Name: </label>
 | 
			
		||||
		<label for="authorname">Name</label>
 | 
			
		||||
		{{ error, exists := form.FieldErrors["authorName"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
@ -69,7 +69,7 @@ templ commentForm(form forms.CommentCreateForm) {
 | 
			
		||||
		<input type="text" name="authorname" id="authorname"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authoremail">Email: </label>
 | 
			
		||||
		<label for="authoremail">Email (Optional) </label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["authorEmail"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
@ -77,7 +77,7 @@ templ commentForm(form forms.CommentCreateForm) {
 | 
			
		||||
		<input type="text" name="authoremail" id="authoremail"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authorsite">Site Url: </label>
 | 
			
		||||
		<label for="authorsite">Site Url (Optional) </label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["authorSite"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
@ -85,7 +85,7 @@ templ commentForm(form forms.CommentCreateForm) {
 | 
			
		||||
		<input type="text" name="authorsite" id="authorsite"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="content">Comment: </label>
 | 
			
		||||
		<label for="content">Comment</label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["content"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
@ -106,11 +106,13 @@ templ GuestbookView(title string, data CommonData, website models.Website, guest
 | 
			
		||||
			<head>
 | 
			
		||||
				<title>{ title }</title>
 | 
			
		||||
				<link href="/static/css/classless.min.css" rel="stylesheet"/>
 | 
			
		||||
				<script src="/static/js/main.js" defer></script>
 | 
			
		||||
			</head>
 | 
			
		||||
			<body>
 | 
			
		||||
				<main>
 | 
			
		||||
					<div>
 | 
			
		||||
						<h1>Guestbook for { website.Name }</h1>
 | 
			
		||||
						{ data.Flash }
 | 
			
		||||
						<form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
							<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
							@commentForm(form)
 | 
			
		||||
@ -122,8 +124,14 @@ templ GuestbookView(title string, data CommonData, website models.Website, guest
 | 
			
		||||
						}
 | 
			
		||||
						for _, c := range comments {
 | 
			
		||||
							<div>
 | 
			
		||||
								<h3>{ c.AuthorName }</h3>
 | 
			
		||||
								{ c.Created.Format("01-02-2006 03:04PM") }
 | 
			
		||||
								<h3>
 | 
			
		||||
									if c.AuthorSite != "" {
 | 
			
		||||
										<a href={ templ.URL(externalUrl(c.AuthorSite)) } target="_blank">{ c.AuthorName }</a>
 | 
			
		||||
									} else {
 | 
			
		||||
										{ c.AuthorName }
 | 
			
		||||
									}
 | 
			
		||||
								</h3>
 | 
			
		||||
								<time datetime={ c.Created.Format(time.RFC3339) }>{ c.Created.Format("01-02-2006 03:04PM") }</time>
 | 
			
		||||
								<p>
 | 
			
		||||
									{ c.CommentText }
 | 
			
		||||
								</p>
 | 
			
		||||
@ -136,21 +144,6 @@ templ GuestbookView(title string, data CommonData, website models.Website, guest
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ CreateGuestbookComment(title string, data CommonData, website models.Website, guestbook models.Guestbook, form forms.CommentCreateForm) {
 | 
			
		||||
	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
	if data.IsHtmx {
 | 
			
		||||
		<form hx-post={ postUrl } hx-target="closest div">
 | 
			
		||||
			@commentForm(form)
 | 
			
		||||
		</form>
 | 
			
		||||
	} else {
 | 
			
		||||
		@base(title, data) {
 | 
			
		||||
			<form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
				@commentForm(form)
 | 
			
		||||
			</form>
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ settingRadio(selected bool, name, id, value string) {
 | 
			
		||||
	<input type="radio" name={ name } id={ id } value={ value } selected?={ selected }/>
 | 
			
		||||
}
 | 
			
		||||
@ -211,6 +204,22 @@ templ GuestbookSettingsView(data CommonData, website models.Website) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ EmbeddableGuestbookCommentForm(data CommonData, w models.Website, f forms.CommentCreateForm) {
 | 
			
		||||
	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create?headless=true", shortIdToSlug(w.ShortId)) }}
 | 
			
		||||
	<html>
 | 
			
		||||
		<head>
 | 
			
		||||
			<link href="/static/css/classless.min.css" rel="stylesheet"/>
 | 
			
		||||
		</head>
 | 
			
		||||
		<body>
 | 
			
		||||
			{ data.Flash }
 | 
			
		||||
			<form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
				<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
				@commentForm(f)
 | 
			
		||||
			</form>
 | 
			
		||||
		</body>
 | 
			
		||||
	</html>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ AllGuestbooksView(data CommonData, websites []models.Website) {
 | 
			
		||||
	@base("All Guestbooks", data) {
 | 
			
		||||
		<div>
 | 
			
		||||
@ -229,3 +238,35 @@ templ AllGuestbooksView(data CommonData, websites []models.Website) {
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ GuestbookCommentCreateRemoteErrorView(url, err string) {
 | 
			
		||||
	<html>
 | 
			
		||||
		<head>
 | 
			
		||||
			<meta http-equiv="refresh" content={ fmt.Sprintf("3; url='%s'", templ.URL(externalUrl(url))) }/>
 | 
			
		||||
		</head>
 | 
			
		||||
		<body>
 | 
			
		||||
			<p>
 | 
			
		||||
				An error occurred while posting comment. { err }. Redirecting.
 | 
			
		||||
			</p>
 | 
			
		||||
			<p>
 | 
			
		||||
				<a href={ templ.URL(url) }>Redirect</a>
 | 
			
		||||
			</p>
 | 
			
		||||
		</body>
 | 
			
		||||
	</html>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ GuestbookCommentCreateRemoteSuccessView(url string) {
 | 
			
		||||
	<html>
 | 
			
		||||
		<head>
 | 
			
		||||
			<meta http-equiv="refresh" content={ fmt.Sprintf("3; url='%s'", templ.URL(externalUrl(url))) }/>
 | 
			
		||||
		</head>
 | 
			
		||||
		<body>
 | 
			
		||||
			<p>
 | 
			
		||||
				Comment successfully posted. Redirecting.
 | 
			
		||||
			</p>
 | 
			
		||||
			<p>
 | 
			
		||||
				<a href={ templ.URL(url) }>Redirect</a>
 | 
			
		||||
			</p>
 | 
			
		||||
		</body>
 | 
			
		||||
	</html>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,9 +59,9 @@ func GuestbookDashboardCommentsView(title string, data CommonData, website model
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var3 string
 | 
			
		||||
			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl)
 | 
			
		||||
			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 37}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 34}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -326,7 +326,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
			templ_7745c5c3_Var16 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<div><label for=\"authorname\">Name: </label>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<div><label for=\"authorname\">Name</label>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -350,7 +350,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input type=\"text\" name=\"authorname\" id=\"authorname\"></div><div><label for=\"authoremail\">Email: </label>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input type=\"text\" name=\"authorname\" id=\"authorname\"></div><div><label for=\"authoremail\">Email (Optional) </label>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -374,7 +374,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<input type=\"text\" name=\"authoremail\" id=\"authoremail\"></div><div><label for=\"authorsite\">Site Url: </label>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<input type=\"text\" name=\"authoremail\" id=\"authoremail\"></div><div><label for=\"authorsite\">Site Url (Optional) </label>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -398,7 +398,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<input type=\"text\" name=\"authorsite\" id=\"authorsite\"></div><div><label for=\"content\">Comment: </label>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<input type=\"text\" name=\"authorsite\" id=\"authorsite\"></div><div><label for=\"content\">Comment</label>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -471,42 +471,55 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</title><link href=\"/static/css/classless.min.css\" rel=\"stylesheet\"></head><body><main><div><h1>Guestbook for ")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</title><link href=\"/static/css/classless.min.css\" rel=\"stylesheet\"><script src=\"/static/js/main.js\" defer></script></head><body><main><div><h1>Guestbook for ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var23 string
 | 
			
		||||
			templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 113, Col: 38}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 114, Col: 38}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</h1><form action=\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</h1>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var24 templ.SafeURL = templ.URL(postUrl)
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var24)))
 | 
			
		||||
			var templ_7745c5c3_Var24 string
 | 
			
		||||
			templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(data.Flash)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 115, Col: 18}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<form action=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var25 string
 | 
			
		||||
			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 115, Col: 68}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
 | 
			
		||||
			var templ_7745c5c3_Var25 templ.SafeURL = templ.URL(postUrl)
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var25)))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var26 string
 | 
			
		||||
			templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 117, Col: 68}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -514,155 +527,104 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</form></div><div id=\"comments\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</form></div><div id=\"comments\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if len(comments) == 0 {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<p>No comments yet!</p>")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<p>No comments yet!</p>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			for _, c := range comments {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<div><h3>")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div><h3>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var26 string
 | 
			
		||||
				templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 125, Col: 26}
 | 
			
		||||
				if c.AuthorSite != "" {
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<a href=\"")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var27 templ.SafeURL = templ.URL(externalUrl(c.AuthorSite))
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var27)))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\" target=\"_blank\">")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var28 string
 | 
			
		||||
					templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 129, Col: 89}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</a>")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					var templ_7745c5c3_Var29 string
 | 
			
		||||
					templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 131, Col: 24}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</h3><time datetime=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</h3>")
 | 
			
		||||
				var templ_7745c5c3_Var30 string
 | 
			
		||||
				templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format(time.RFC3339))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 134, Col: 55}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var27 string
 | 
			
		||||
				templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 126, Col: 48}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<p>")
 | 
			
		||||
				var templ_7745c5c3_Var31 string
 | 
			
		||||
				templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 134, Col: 98}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var28 string
 | 
			
		||||
				templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 128, Col: 24}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</time><p>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</p></div>")
 | 
			
		||||
				var templ_7745c5c3_Var32 string
 | 
			
		||||
				templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 136, Col: 24}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</p></div>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</div></main></body></html>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateGuestbookComment(title string, data CommonData, website models.Website, guestbook models.Guestbook, form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var29 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var29 == nil {
 | 
			
		||||
			templ_7745c5c3_Var29 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId))
 | 
			
		||||
		if data.IsHtmx {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<form hx-post=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var30 string
 | 
			
		||||
			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(postUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 142, Col: 25}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" hx-target=\"closest div\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = commentForm(form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</form>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			templ_7745c5c3_Var31 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
				templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
				templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
				if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
					defer func() {
 | 
			
		||||
						templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
						if templ_7745c5c3_Err == nil {
 | 
			
		||||
							templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
						}
 | 
			
		||||
					}()
 | 
			
		||||
				}
 | 
			
		||||
				ctx = templ.InitializeContext(ctx)
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<form action=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var32 templ.SafeURL = templ.URL(postUrl)
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var32)))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" method=\"post\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = commentForm(form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</form>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				return nil
 | 
			
		||||
			})
 | 
			
		||||
			templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var31), templ_7745c5c3_Buffer)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</div></main></body></html>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -692,56 +654,56 @@ func settingRadio(selected bool, name, id, value string) templ.Component {
 | 
			
		||||
			templ_7745c5c3_Var33 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<input type=\"radio\" name=\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<input type=\"radio\" name=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var34 string
 | 
			
		||||
		templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(name)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 32}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 148, Col: 32}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" id=\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" id=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var35 string
 | 
			
		||||
		templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(id)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 42}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 148, Col: 42}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" value=\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var36 string
 | 
			
		||||
		templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(value)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 58}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 148, Col: 58}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		if selected {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " selected")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, " selected")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, ">")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, ">")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -784,7 +746,7 @@ func GuestbookSettingsView(data CommonData, website models.Website) templ.Compon
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<div id=\"dashboard\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<div id=\"dashboard\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -792,143 +754,143 @@ func GuestbookSettingsView(data CommonData, website models.Website) templ.Compon
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<div><h1>Guestbook Settings</h1><form hx-put=\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<div><h1>Guestbook Settings</h1><form hx-put=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var39 string
 | 
			
		||||
			templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(putUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 166, Col: 25}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 159, Col: 25}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var40 string
 | 
			
		||||
			templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 167, Col: 66}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 160, Col: 66}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\"><div><label>Guestbook Visibility</label> <label for=\"gb_visible_true\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_true\" value=\"true\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\"><div><label>Guestbook Visibility</label> <label for=\"gb_visible_true\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_true\" value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.IsVisible {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, " checked")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "> Public</label> <label for=\"gb_visible_false\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_false\" value=\"false\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "> Public</label> <label for=\"gb_visible_false\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_false\" value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.IsVisible {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " checked")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "> Private</label></div><div><label>Guestbook Commenting</label> <select name=\"gb_commenting\" id=\"gb-commenting\"><option value=\"true\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "> Private</label></div><div><label>Guestbook Commenting</label> <select name=\"gb_commenting\" id=\"gb-commenting\"><option value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.IsCommentingEnabled {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " selected")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, " selected")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, ">Enabled</option> <option value=\"1h\">Disabled for 1 Hour</option> <option value=\"4h\">Disabled for 4 Hours</option> <option value=\"8h\">Disabled for 8 Hours</option> <option value=\"24h\">Disabled for 1 Day</option> <option value=\"72h\">Disabled for 3 Days</option> <option value=\"168h\">Disabled for 7 Days</option> <option value=\"false\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, ">Enabled</option> <option value=\"1h\">Disabled for 1 Hour</option> <option value=\"4h\">Disabled for 4 Hours</option> <option value=\"8h\">Disabled for 8 Hours</option> <option value=\"24h\">Disabled for 1 Day</option> <option value=\"72h\">Disabled for 3 Days</option> <option value=\"168h\">Disabled for 7 Days</option> <option value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.IsCommentingEnabled {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, " selected")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " selected")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, ">Disabled</option></select> ")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, ">Disabled</option></select> ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !website.Guestbook.CanComment() {
 | 
			
		||||
				localtime := gb.Settings.ReenableCommenting.In(data.CurrentUser.Settings.LocalTimezone)
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<label>Commenting re-enabled on <time value=\"")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<label>Commenting re-enabled on <time value=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var41 string
 | 
			
		||||
				templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format(time.RFC3339))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 83}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 186, Col: 83}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\">")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var42 string
 | 
			
		||||
				templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format("2 January 2006"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 122}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 186, Col: 122}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " at ")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, " at ")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var43 string
 | 
			
		||||
				templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format("3:04PM MST"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 160}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 186, Col: 160}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</time></label>")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</time></label>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</div><div><label>Enable Widgets</label> <label for=\"gb_remote_true\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_true\" value=\"true\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</div><div><label>Enable Widgets</label> <label for=\"gb_remote_true\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_true\" value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.AllowRemoteHostAccess {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, " checked")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "> Yes</label> <label for=\"gb_remote_false\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_false\" value=\"false\"")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "> Yes</label> <label for=\"gb_remote_false\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_false\" value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.AllowRemoteHostAccess {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, " checked")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "> No</label></div><input type=\"submit\" value=\"Submit\"></form></div></div>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "> No</label></div><input type=\"submit\" value=\"Submit\"></form></div></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -942,6 +904,79 @@ func GuestbookSettingsView(data CommonData, website models.Website) templ.Compon
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EmbeddableGuestbookCommentForm(data CommonData, w models.Website, f forms.CommentCreateForm) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var44 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var44 == nil {
 | 
			
		||||
			templ_7745c5c3_Var44 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create?headless=true", shortIdToSlug(w.ShortId))
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<html><head><link href=\"/static/css/classless.min.css\" rel=\"stylesheet\"></head><body>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var45 string
 | 
			
		||||
		templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(data.Flash)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 214, Col: 15}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<form action=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var46 templ.SafeURL = templ.URL(postUrl)
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var46)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var47 string
 | 
			
		||||
		templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 216, Col: 65}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "\">")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = commentForm(f).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</form></body></html>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AllGuestbooksView(data CommonData, websites []models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
@ -958,12 +993,12 @@ func AllGuestbooksView(data CommonData, websites []models.Website) templ.Compone
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var44 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var44 == nil {
 | 
			
		||||
			templ_7745c5c3_Var44 = templ.NopComponent
 | 
			
		||||
		templ_7745c5c3_Var48 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var48 == nil {
 | 
			
		||||
			templ_7745c5c3_Var48 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var45 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_Var49 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
@ -975,50 +1010,165 @@ func AllGuestbooksView(data CommonData, websites []models.Website) templ.Compone
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<div><h1>All Guestbooks</h1><p>This page exists only for testing the service.</p><ul>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<div><h1>All Guestbooks</h1><p>This page exists only for testing the service.</p><ul>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			for _, w := range websites {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<li>")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<li>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<a href=\"")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<a href=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var46 templ.SafeURL = templ.URL(gbUrl)
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var46)))
 | 
			
		||||
				var templ_7745c5c3_Var50 templ.SafeURL = templ.URL(gbUrl)
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var50)))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "\" target=\"_blank\">")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "\" target=\"_blank\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var47 string
 | 
			
		||||
				templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name)
 | 
			
		||||
				var templ_7745c5c3_Var51 string
 | 
			
		||||
				templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 225, Col: 59}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 234, Col: 59}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</a></li>")
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</a></li>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</ul></div>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "</ul></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base("All Guestbooks", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var45), templ_7745c5c3_Buffer)
 | 
			
		||||
		templ_7745c5c3_Err = base("All Guestbooks", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var49), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GuestbookCommentCreateRemoteErrorView(url, err string) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var52 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var52 == nil {
 | 
			
		||||
			templ_7745c5c3_Var52 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "<html><head><meta http-equiv=\"refresh\" content=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var53 string
 | 
			
		||||
		templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("3; url='%s'", templ.URL(externalUrl(url))))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 245, Col: 95}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "\"></head><body><p>An error occurred while posting comment. ")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var54 string
 | 
			
		||||
		templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 249, Col: 50}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, ". Redirecting.</p><p><a href=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var55 templ.SafeURL = templ.URL(url)
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var55)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "\">Redirect</a></p></body></html>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GuestbookCommentCreateRemoteSuccessView(url string) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var56 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var56 == nil {
 | 
			
		||||
			templ_7745c5c3_Var56 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "<html><head><meta http-equiv=\"refresh\" content=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var57 string
 | 
			
		||||
		templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("3; url='%s'", templ.URL(externalUrl(url))))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 261, Col: 95}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "\"></head><body><p>Comment successfully posted. Redirecting.</p><p><a href=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var58 templ.SafeURL = templ.URL(url)
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var58)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "\">Redirect</a></p></body></html>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,19 @@
 | 
			
		||||
package views
 | 
			
		||||
 | 
			
		||||
templ Home(title string, data CommonData) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <h2>Welcome</h2>
 | 
			
		||||
        <p>
 | 
			
		||||
            Welcome to webweav.ing, a collection of webmastery tools created by the <a href="https://32bit.cafe">32-Bit Cafe</a>.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
            Note this service is in a pre-alpha state. Your account and data can disappear at any time. 
 | 
			
		||||
        </p>
 | 
			
		||||
    }
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h2>Welcome</h2>
 | 
			
		||||
		<p>
 | 
			
		||||
			Welcome to webweav.ing, a collection of webmastery tools created by the <a href="https://32bit.cafe">32-Bit Cafe</a>.
 | 
			
		||||
		</p>
 | 
			
		||||
		<p>
 | 
			
		||||
			Note this service is in a pre-alpha state. Your account and data can disappear at any time. 
 | 
			
		||||
		</p>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ ComingSoon(title string, data CommonData) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <h2>Coming Soon</h2>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h2>Coming Soon</h2>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ templ wSidebar(website models.Website) {
 | 
			
		||||
			<h2>{ website.Name }</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl) }>Dashboard</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(externalUrl(website.Url.String())) } target="_blank">View Website</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
			<h3>Guestbook</h3>
 | 
			
		||||
			<ul>
 | 
			
		||||
@ -70,7 +70,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="sitename" id="sitename" required/>
 | 
			
		||||
		<input type="text" name="sitename" id="sitename" value={ form.Name } required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		{{ err, exists = form.FieldErrors["siteurl"] }}
 | 
			
		||||
@ -78,7 +78,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="siteurl" id="siteurl" required/>
 | 
			
		||||
		<input type="text" name="siteurl" id="siteurl" value={ form.SiteUrl } required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		{{ err, exists = form.FieldErrors["authorname"] }}
 | 
			
		||||
@ -86,22 +86,18 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="authorname" id="authorname" required/>
 | 
			
		||||
		<input type="text" name="authorname" id="authorname" value={ form.AuthorName } required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<button type="submit">Submit</button>
 | 
			
		||||
	</div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteCreateButton() {
 | 
			
		||||
	<button hx-get="/websites/create" hx-target="closest div">Add Website</button>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteList(title string, data CommonData, websites []models.Website) {
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h1>My Websites</h1>
 | 
			
		||||
		<div>
 | 
			
		||||
			@WebsiteCreateButton()
 | 
			
		||||
			<a href="/websites/create">Add Website</a>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			@displayWebsites(websites)
 | 
			
		||||
@ -115,9 +111,42 @@ templ WebsiteDashboard(title string, data CommonData, website models.Website) {
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>{ website.Name }</h1>
 | 
			
		||||
				<h2>Embed your Guestbook</h2>
 | 
			
		||||
				<p>
 | 
			
		||||
					Stats and stuff will go here.
 | 
			
		||||
					Upload <a href="/static/js/guestbook.js" download>this JavaScript WebComponent</a> to your site and include it in your <code>{ `<head>` }</code> tag.
 | 
			
		||||
				</p>
 | 
			
		||||
				<div>
 | 
			
		||||
					//<button>Copy to Clipboard</button>
 | 
			
		||||
					<pre>
 | 
			
		||||
						<code id="guestbookSnippet">
 | 
			
		||||
							{ 
 | 
			
		||||
`<head>
 | 
			
		||||
    <script type="module" src="js/guestbook.js"></script>
 | 
			
		||||
</head>` }
 | 
			
		||||
						</code>
 | 
			
		||||
					</pre>
 | 
			
		||||
					<p>
 | 
			
		||||
						Then add the custom elements where you want your form and comments to show up
 | 
			
		||||
					</p>
 | 
			
		||||
					{{ gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
					//<button>Copy to Clipboard</button>
 | 
			
		||||
					<pre>
 | 
			
		||||
						<code>
 | 
			
		||||
							{ fmt.Sprintf(`<guestbook-form guestbook="%s"></guestbook-form>
 | 
			
		||||
<guestbook-comments guestbook="%s"></guestbook-comments>`, gbUrl, gbUrl) }
 | 
			
		||||
						</code>
 | 
			
		||||
					</pre>
 | 
			
		||||
				</div>
 | 
			
		||||
				<p>
 | 
			
		||||
					If your web host does not allow CORS requests, use an iframe instead
 | 
			
		||||
				</p>
 | 
			
		||||
				<div>
 | 
			
		||||
					<pre>
 | 
			
		||||
						<code>
 | 
			
		||||
							{ fmt.Sprintf(`<iframe src="%s" title="Guestbook"></iframe>`, gbUrl) }
 | 
			
		||||
						</code>
 | 
			
		||||
					</pre>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
@ -138,7 +167,9 @@ templ WebsiteDashboardComingSoon(title string, data CommonData, website models.W
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) {
 | 
			
		||||
	<form hx-post="/websites/create" hx-target="closest div">
 | 
			
		||||
		@websiteCreateForm(data.CSRFToken, form)
 | 
			
		||||
	</form>
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<form action="/websites/create" method="post">
 | 
			
		||||
			@websiteCreateForm(data.CSRFToken, form)
 | 
			
		||||
		</form>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ func wSidebar(website models.Website) templ.Component {
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.SiteUrl))
 | 
			
		||||
		var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.Url.String()))
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
@ -271,92 +271,102 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" name=\"sitename\" id=\"sitename\" required></div><div>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" name=\"sitename\" id=\"sitename\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var18 string
 | 
			
		||||
		templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 73, Col: 68}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" required></div><div>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		err, exists = form.FieldErrors["siteurl"]
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<label for=\"siteurl\">Site URL: </label> ")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label for=\"siteurl\">Site URL: </label> ")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		if exists {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label class=\"error\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var18 string
 | 
			
		||||
			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 79, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</label> ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<input type=\"text\" name=\"siteurl\" id=\"siteurl\" required></div><div>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		err, exists = form.FieldErrors["authorname"]
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<label for=\"authorname\">Site Author: </label> ")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		if exists {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<label class=\"error\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<label class=\"error\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var19 string
 | 
			
		||||
			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 87, Col: 29}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 79, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</label> ")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</label> ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<input type=\"text\" name=\"authorname\" id=\"authorname\" required></div><div><button type=\"submit\">Submit</button></div>")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<input type=\"text\" name=\"siteurl\" id=\"siteurl\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebsiteCreateButton() templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		var templ_7745c5c3_Var20 string
 | 
			
		||||
		templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(form.SiteUrl)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 81, Col: 69}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var20 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var20 == nil {
 | 
			
		||||
			templ_7745c5c3_Var20 = templ.NopComponent
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" required></div><div>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<button hx-get=\"/websites/create\" hx-target=\"closest div\">Add Website</button>")
 | 
			
		||||
		err, exists = form.FieldErrors["authorname"]
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<label for=\"authorname\">Site Author: </label> ")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		if exists {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<label class=\"error\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var21 string
 | 
			
		||||
			templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 87, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</label> ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<input type=\"text\" name=\"authorname\" id=\"authorname\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var22 string
 | 
			
		||||
		templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(form.AuthorName)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 89, Col: 78}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\" required></div><div><button type=\"submit\">Submit</button></div>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -365,69 +375,6 @@ func WebsiteCreateButton() templ.Component {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebsiteList(title string, data CommonData, websites []models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var21 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var21 == nil {
 | 
			
		||||
			templ_7745c5c3_Var21 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<h1>My Websites</h1><div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = WebsiteCreateButton().Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div><div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebsiteDashboard(title string, data CommonData, website models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
@ -460,7 +407,62 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<div id=\"dashboard\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<h1>My Websites</h1><div><a href=\"/websites/create\">Add Website</a></div><div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebsiteDashboard(title string, data CommonData, website models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var25 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var25 == nil {
 | 
			
		||||
			templ_7745c5c3_Var25 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var26 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div id=\"dashboard\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -468,26 +470,87 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><h1>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div><h1>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var25 string
 | 
			
		||||
			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			var templ_7745c5c3_Var27 string
 | 
			
		||||
			templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 117, Col: 22}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 113, Col: 22}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</h1><p>Stats and stuff will go here.</p></div></div>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</h1><h2>Embed your Guestbook</h2><p>Upload <a href=\"/static/js/guestbook.js\" download>this JavaScript WebComponent</a> to your site and include it in your <code>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var28 string
 | 
			
		||||
			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(`<head>`)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 116, Col: 140}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</code> tag.</p><div><pre><code id=\"guestbookSnippet\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var29 string
 | 
			
		||||
			templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(
 | 
			
		||||
				`<head>
 | 
			
		||||
    <script type="module" src="js/guestbook.js"></script>
 | 
			
		||||
</head>`)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 125, Col: 8}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</code></pre><p>Then add the custom elements where you want your form and comments to show up</p>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId))
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<pre><code>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var30 string
 | 
			
		||||
			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`<guestbook-form guestbook="%s"></guestbook-form>
 | 
			
		||||
<guestbook-comments guestbook="%s"></guestbook-comments>`, gbUrl, gbUrl))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 136, Col: 72}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</code></pre></div><p>If your web host does not allow CORS requests, use an iframe instead</p><div><pre><code>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var31 string
 | 
			
		||||
			templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`<iframe src="%s" title="Guestbook"></iframe>`, gbUrl))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 146, Col: 75}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</code></pre></div></div></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer)
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var26), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -511,12 +574,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var26 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var26 == nil {
 | 
			
		||||
			templ_7745c5c3_Var26 = templ.NopComponent
 | 
			
		||||
		templ_7745c5c3_Var32 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var32 == nil {
 | 
			
		||||
			templ_7745c5c3_Var32 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var27 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_Var33 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
@ -528,7 +591,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div id=\"dashboard\">")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div id=\"dashboard\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
@ -536,26 +599,26 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<div><h1>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<div><h1>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var28 string
 | 
			
		||||
			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			var templ_7745c5c3_Var34 string
 | 
			
		||||
			templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 131, Col: 22}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 160, Col: 22}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</h1><p>Coming Soon</p></div></div>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</h1><p>Coming Soon</p></div></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var27), templ_7745c5c3_Buffer)
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var33), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -579,20 +642,38 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm)
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var29 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var29 == nil {
 | 
			
		||||
			templ_7745c5c3_Var29 = templ.NopComponent
 | 
			
		||||
		templ_7745c5c3_Var35 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var35 == nil {
 | 
			
		||||
			templ_7745c5c3_Var35 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<form hx-post=\"/websites/create\" hx-target=\"closest div\">")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</form>")
 | 
			
		||||
		templ_7745c5c3_Var36 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<form action=\"/websites/create\" method=\"post\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</form>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var36), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user