Compare commits

..

No commits in common. "133d8bcfe9033283ade32fc15261f2a3b5e3ccc4" and "c3b10ae2390114196557cc57ac9e44efbdb53366" have entirely different histories.

16 changed files with 382 additions and 484 deletions

View File

@ -257,10 +257,11 @@ func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter,
return return
} }
if !matchOrigin(r.Header.Get("Origin"), website.Url) { if normalizeUrl(r.Header.Get("Origin")) != normalizeUrl(website.SiteUrl) {
app.clientError(w, http.StatusForbidden) app.clientError(w, http.StatusForbidden)
return return
} }
if !website.Guestbook.CanComment() { if !website.Guestbook.CanComment() {
app.clientError(w, http.StatusForbidden) app.clientError(w, http.StatusForbidden)
return return
@ -278,16 +279,10 @@ func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter,
form.CheckField(validator.MaxChars(form.AuthorEmail, 256), "authorEmail", "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.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") form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
// TODO: Add optional field filled with window.location.href
// if redirect path is filled out, redirect to that path on the website host // If it is populated, use as redirect URL
// otherwise redirect to the guestbook by default // Else redirect to homepage as stored in the website struct
redirectUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(website.ShortId)) redirectUrl := r.Header.Get("Referer")
if form.Redirect != "" {
u, err := website.Url.Parse(form.Redirect)
if err == nil {
redirectUrl = u.String()
}
}
if !form.Valid() { if !form.Valid() {
views.GuestbookCommentCreateRemoteErrorView(redirectUrl, "Invalid Input").Render(r.Context(), w) views.GuestbookCommentCreateRemoteErrorView(redirectUrl, "Invalid Input").Render(r.Context(), w)

View File

@ -144,88 +144,3 @@ 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)
})
}
}

View File

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"git.32bit.cafe/32bitcafe/guestbook/internal/forms" "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
"git.32bit.cafe/32bitcafe/guestbook/internal/models" "git.32bit.cafe/32bitcafe/guestbook/internal/models"
@ -33,20 +32,14 @@ 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.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.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.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() { if !form.Valid() {
data := app.newCommonData(r) data := app.newCommonData(r)
w.WriteHeader(http.StatusUnprocessableEntity) w.WriteHeader(http.StatusUnprocessableEntity)
views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w) views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w)
return
} }
websiteShortID := app.createShortId() websiteShortID := app.createShortId()
_, err = app.websites.Insert(websiteShortID, userId, form.Name, u.String(), form.AuthorName) _, err = app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName)
if err != nil { if err != nil {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
@ -82,6 +75,10 @@ func (app *application) getWebsiteList(w http.ResponseWriter, r *http.Request) {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
if r.Header.Get("HX-Request") == "true" {
views.HxWebsiteList(websites)
return
}
data := app.newCommonData(r) data := app.newCommonData(r)
views.WebsiteList("My Websites", data, websites).Render(r.Context(), w) views.WebsiteList("My Websites", data, websites).Render(r.Context(), w)
} }

View File

@ -5,9 +5,9 @@ import (
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
"net/url"
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"strings"
"time" "time"
"git.32bit.cafe/32bitcafe/guestbook/internal/models" "git.32bit.cafe/32bitcafe/guestbook/internal/models"
@ -130,13 +130,11 @@ func (app *application) durationToTime(duration string) (time.Time, error) {
return result, nil return result, nil
} }
func matchOrigin(origin string, u *url.URL) bool { func normalizeUrl(url string) string {
o, err := url.Parse(origin) r, f := strings.CutPrefix(url, "http://")
if err != nil { if f {
return false return r
} }
if o.Host != u.Host { r, _ = strings.CutPrefix(url, "https://")
return false return r
}
return true
} }

View File

@ -20,7 +20,6 @@ type CommentCreateForm struct {
AuthorEmail string `schema:"authoremail"` AuthorEmail string `schema:"authoremail"`
AuthorSite string `schema:"authorsite"` AuthorSite string `schema:"authorsite"`
Content string `schema:"content"` Content string `schema:"content"`
Redirect string `schema:"redirect"`
validator.Validator `schema:"-"` validator.Validator `schema:"-"`
} }

View File

@ -18,12 +18,6 @@ var mockGuestbookComment = models.GuestbookComment{
IsPublished: true, IsPublished: true,
} }
var mockSerializedGuestbookComment = models.GuestbookCommentSerialized{
AuthorName: "John Test",
CommentText: "Hello, world",
Created: time.Now().Format(time.RFC3339),
}
type GuestbookCommentModel struct{} type GuestbookCommentModel struct{}
func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName, func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName,
@ -51,17 +45,6 @@ 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) { func (m *GuestbookCommentModel) GetDeleted(guestbookId int64) ([]models.GuestbookComment, error) {
switch guestbookId { switch guestbookId {
default: default:

View File

@ -1,7 +1,6 @@
package mocks package mocks
import ( import (
"net/url"
"time" "time"
"git.32bit.cafe/32bitcafe/guestbook/internal/models" "git.32bit.cafe/32bitcafe/guestbook/internal/models"
@ -26,11 +25,7 @@ var mockWebsite = models.Website{
ID: 1, ID: 1,
ShortId: 1, ShortId: 1,
Name: "Example", Name: "Example",
// SiteUrl: "example.com", SiteUrl: "example.com",
Url: &url.URL{
Scheme: "http",
Host: "example.com",
},
AuthorName: "John Test", AuthorName: "John Test",
UserId: 1, UserId: 1,
Created: time.Now(), Created: time.Now(),

View File

@ -3,7 +3,6 @@ package models
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"net/url"
"strconv" "strconv"
"time" "time"
) )
@ -12,8 +11,7 @@ type Website struct {
ID int64 ID int64
ShortId uint64 ShortId uint64
Name string Name string
// SiteUrl string SiteUrl string
Url *url.URL
AuthorName string AuthorName string
UserId int64 UserId int64
Created time.Time Created time.Time
@ -181,8 +179,7 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) {
} }
row := tx.QueryRow(stmt, shortId) row := tx.QueryRow(stmt, shortId)
var w Website var w Website
var u string err = row.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created)
err = row.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
err = ErrNoRecord err = ErrNoRecord
@ -192,10 +189,6 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) {
} }
return Website{}, err 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 stmt = `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks
WHERE WebsiteId = ? AND Deleted IS NULL` WHERE WebsiteId = ? AND Deleted IS NULL`
@ -251,16 +244,11 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) {
var websites []Website var websites []Website
for rows.Next() { for rows.Next() {
var w Website var w Website
var u string err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
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) &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.Url, err = url.Parse(u)
if err != nil {
return nil, err
}
websites = append(websites, w) websites = append(websites, w)
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {
@ -280,16 +268,11 @@ func (m *WebsiteModel) GetAll() ([]Website, error) {
var websites []Website var websites []Website
for rows.Next() { for rows.Next() {
var w Website var w Website
var u string err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
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) &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.Url, err = url.Parse(u)
if err != nil {
return nil, err
}
websites = append(websites, w) websites = append(websites, w)
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {

View File

@ -8,7 +8,6 @@ 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 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 { type Validator struct {
NonFieldErrors []string NonFieldErrors []string

View File

@ -1 +0,0 @@
UPDATE websites SET SiteUrl = substr(SiteUrl, 8) WHERE substr(SiteUrl, 1, 4) = 'http';

View File

@ -1 +0,0 @@
UPDATE websites SET SiteUrl = 'http://' || SiteUrl WHERE substr(SiteUrl, 1, 4) <> 'http';

View File

@ -1,74 +1,6 @@
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 { class CommentList extends HTMLElement {
static get observedAttributes() { static get observedAttributes() {
return ['guestbook']; return ['src'];
} }
constructor() { constructor() {
@ -80,28 +12,26 @@ class CommentList extends HTMLElement {
} }
connectedCallback() { connectedCallback() {
if (this.hasAttribute('guestbook')) { if (this.hasAttribute('src')) {
this.fetchComments(); this.fetchComments();
} }
} }
attributeChangedCallback(name, oldValue, newValue) { attributeChangedCallback(name, oldValue, newValue) {
if (name === 'guestbook' && oldValue !== newValue) { if (name === 'src' && oldValue !== newValue) {
this.fetchComments(); this.fetchComments();
} }
} }
async fetchComments() { async fetchComments() {
const guestbook = this.getAttribute('guestbook'); const src = this.getAttribute('src');
if (!guestbook) return; if (!src) return;
this.loading = true; this.loading = true;
this.error = null; this.error = null;
this.render(); this.render();
const commentsUrl = `${guestbook}/comments`
try { try {
const response = await fetch(commentsUrl); const response = await fetch(src);
if (!response.ok) throw new Error(`HTTP error: ${response.status}`); if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
const data = await response.json(); const data = await response.json();
this.comments = Array.isArray(data) ? data : []; this.comments = Array.isArray(data) ? data : [];
@ -172,6 +102,4 @@ class CommentList extends HTMLElement {
} }
} }
customElements.define('guestbook-form', GuestbookForm); customElements.define('comment-list', CommentList);
customElements.define('guestbook-comments', CommentList);

View File

@ -10,7 +10,7 @@ templ GuestbookDashboardCommentsView(title string, data CommonData, website mode
<div id="dashboard"> <div id="dashboard">
@wSidebar(website) @wSidebar(website)
<div> <div>
<h1>Comments on { website.Name }</h1> <h1>Comments on { website.SiteUrl }</h1>
<hr/> <hr/>
if len(comments) == 0 { if len(comments) == 0 {
<p>No comments yet!</p> <p>No comments yet!</p>

View File

@ -59,9 +59,9 @@ func GuestbookDashboardCommentsView(title string, data CommonData, website model
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 34} return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {

View File

@ -16,7 +16,7 @@ templ wSidebar(website models.Website) {
<h2>{ website.Name }</h2> <h2>{ website.Name }</h2>
<ul> <ul>
<li><a href={ templ.URL(dashUrl) }>Dashboard</a></li> <li><a href={ templ.URL(dashUrl) }>Dashboard</a></li>
<li><a href={ templ.URL(externalUrl(website.Url.String())) } target="_blank">View Website</a></li> <li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li>
</ul> </ul>
<h3>Guestbook</h3> <h3>Guestbook</h3>
<ul> <ul>
@ -70,7 +70,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
if exists { if exists {
<label class="error">{ err }</label> <label class="error">{ err }</label>
} }
<input type="text" name="sitename" id="sitename" value={ form.Name } required/> <input type="text" name="sitename" id="sitename" required/>
</div> </div>
<div> <div>
{{ err, exists = form.FieldErrors["siteurl"] }} {{ err, exists = form.FieldErrors["siteurl"] }}
@ -78,7 +78,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
if exists { if exists {
<label class="error">{ err }</label> <label class="error">{ err }</label>
} }
<input type="text" name="siteurl" id="siteurl" value={ form.SiteUrl } required/> <input type="text" name="siteurl" id="siteurl" required/>
</div> </div>
<div> <div>
{{ err, exists = form.FieldErrors["authorname"] }} {{ err, exists = form.FieldErrors["authorname"] }}
@ -86,18 +86,22 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
if exists { if exists {
<label class="error">{ err }</label> <label class="error">{ err }</label>
} }
<input type="text" name="authorname" id="authorname" value={ form.AuthorName } required/> <input type="text" name="authorname" id="authorname" required/>
</div> </div>
<div> <div>
<button type="submit">Submit</button> <button type="submit">Submit</button>
</div> </div>
} }
templ WebsiteCreateButton() {
<button hx-get="/websites/create" hx-target="closest div">Add Website</button>
}
templ WebsiteList(title string, data CommonData, websites []models.Website) { templ WebsiteList(title string, data CommonData, websites []models.Website) {
@base(title, data) { @base(title, data) {
<h1>My Websites</h1> <h1>My Websites</h1>
<div> <div>
<a href="/websites/create">Add Website</a> @WebsiteCreateButton()
</div> </div>
<div> <div>
@displayWebsites(websites) @displayWebsites(websites)
@ -112,6 +116,15 @@ templ WebsiteDashboard(title string, data CommonData, website models.Website) {
<div> <div>
<h1>{ website.Name }</h1> <h1>{ website.Name }</h1>
<h2>Embed your Guestbook</h2> <h2>Embed your Guestbook</h2>
<h3>Comment form</h3>
<p>
Use this form to allow readers of your website to comment on your guestbook!
</p>
<div>
//<button>Copy to Clipboard</button>
@embeddableForm(data.RootUrl, website)
</div>
<h3>Embed your comments</h3>
<p> <p>
Upload <a href="/static/js/guestbook.js" download>this JavaScript WebComponent</a> to your site and include it in your <code>{ `<head>` }</code> tag. 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> </p>
@ -126,26 +139,16 @@ templ WebsiteDashboard(title string, data CommonData, website models.Website) {
</code> </code>
</pre> </pre>
<p> <p>
Then add the custom elements where you want your form and comments to show up Then add the custom element where you want the comments to show up
</p> </p>
{{ gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) }} {{ getUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments", data.RootUrl, shortIdToSlug(website.ShortId)) }}
//<button>Copy to Clipboard</button> //<button>Copy to Clipboard</button>
<pre> <pre>
<code> <code>
{ fmt.Sprintf(`<guestbook-form guestbook="%s"></guestbook-form> { fmt.Sprintf(`<comment-list src="%s"></comment-list>`, getUrl) }
<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> </code>
</pre> </pre>
@embedJavaScriptSnippet(data.RootUrl, website)
</div> </div>
</div> </div>
</div> </div>
@ -167,9 +170,42 @@ templ WebsiteDashboardComingSoon(title string, data CommonData, website models.W
} }
templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) { templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) {
@base(title, data) { <form hx-post="/websites/create" hx-target="closest div">
<form action="/websites/create" method="post">
@websiteCreateForm(data.CSRFToken, form) @websiteCreateForm(data.CSRFToken, form)
</form> </form>
} }
templ embeddableForm(root string, website models.Website) {
{{ postUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments/create/remote", root, shortIdToSlug(website.ShortId)) }}
{{formStr :=
`<form action="%s" 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="submit" value="Submit"/>
</div>
</form>`
}}
<pre>
<code>
{ fmt.Sprintf(formStr, postUrl) }
</code>
</pre>
}
templ embedJavaScriptSnippet(root string, website models.Website) {
} }

View File

@ -65,7 +65,7 @@ func wSidebar(website models.Website) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.Url.String())) var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.SiteUrl))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
@ -271,102 +271,92 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" name=\"sitename\" id=\"sitename\" value=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" name=\"sitename\" id=\"sitename\" 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> ")
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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var18 string var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name) templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(err)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 73, Col: 68} 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" required></div><div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</label> ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
err, exists = form.FieldErrors["siteurl"] }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label for=\"siteurl\">Site URL: </label> ") 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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if exists { if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<label class=\"error\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<label class=\"error\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var19 string var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err) templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 79, Col: 29} 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_Var19)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</label> ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</label> ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<input type=\"text\" name=\"siteurl\" id=\"siteurl\" value=\"") 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>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var20 string return nil
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}
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
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if templ_7745c5c3_Err != nil { if !templ_7745c5c3_IsBuffer {
return templ_7745c5c3_Err defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" required></div><div>") }()
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
} }
err, exists = form.FieldErrors["authorname"] ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<label for=\"authorname\">Site Author: </label> ") templ_7745c5c3_Var20 := templ.GetChildren(ctx)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Var20 == nil {
return templ_7745c5c3_Err templ_7745c5c3_Var20 = templ.NopComponent
} }
if exists { ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<label class=\"error\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<button hx-get=\"/websites/create\" hx-target=\"closest div\">Add Website</button>")
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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -375,6 +365,69 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
} }
func WebsiteList(title string, data CommonData, websites []models.Website) 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) { 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 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@ -407,62 +460,7 @@ func WebsiteList(title string, data CommonData, websites []models.Website) templ
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<h1>My Websites</h1><div><a href=\"/websites/create\">Add Website</a></div><div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<div id=\"dashboard\">")
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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -470,29 +468,37 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div><h1>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><h1>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var27 string var templ_7745c5c3_Var25 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 113, Col: 22} return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 117, Col: 22}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
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>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</h1><h2>Embed your Guestbook</h2><h3>Comment form</h3><p>Use this form to allow readers of your website to comment on your guestbook!</p><div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var28 string templ_7745c5c3_Err = embeddableForm(data.RootUrl, website).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(`<head>`)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 116, Col: 140} return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div><h3>Embed your comments</h3><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_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(`<head>`)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 129, Col: 140}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -500,57 +506,51 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var29 string var templ_7745c5c3_Var27 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs( templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(
`<head> `<head>
<script type="module" src="js/guestbook.js"></script> <script type="module" src="js/guestbook.js"></script>
</head>`) </head>`)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 125, Col: 8} return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 138, Col: 8}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err 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>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</code></pre><p>Then add the custom element where you want the comments to show up</p>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) getUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments", data.RootUrl, shortIdToSlug(website.ShortId))
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<pre><code>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<pre><code>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var30 string var templ_7745c5c3_Var28 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`<guestbook-form guestbook="%s"></guestbook-form> templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`<comment-list src="%s"></comment-list>`, getUrl))
<guestbook-comments guestbook="%s"></guestbook-comments>`, gbUrl, gbUrl))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 136, Col: 72} return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 148, Col: 70}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err 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>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</code></pre>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var31 string templ_7745c5c3_Err = embedJavaScriptSnippet(data.RootUrl, website).Render(ctx, templ_7745c5c3_Buffer)
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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</code></pre></div></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
return nil return nil
}) })
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var26), templ_7745c5c3_Buffer) templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -574,12 +574,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var32 := templ.GetChildren(ctx) templ_7745c5c3_Var29 := templ.GetChildren(ctx)
if templ_7745c5c3_Var32 == nil { if templ_7745c5c3_Var29 == nil {
templ_7745c5c3_Var32 = templ.NopComponent templ_7745c5c3_Var29 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var33 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_Var30 := 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_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
@ -603,12 +603,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var34 string var templ_7745c5c3_Var31 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 160, Col: 22} return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 163, Col: 22}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -618,7 +618,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
} }
return nil return nil
}) })
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var33), templ_7745c5c3_Buffer) templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var30), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -642,24 +642,12 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm)
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var35 := templ.GetChildren(ctx) templ_7745c5c3_Var32 := templ.GetChildren(ctx)
if templ_7745c5c3_Var35 == nil { if templ_7745c5c3_Var32 == nil {
templ_7745c5c3_Var35 = templ.NopComponent templ_7745c5c3_Var32 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var36 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<form hx-post=\"/websites/create\" hx-target=\"closest div\">")
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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -673,7 +661,66 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm)
} }
return nil return nil
}) })
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var36), templ_7745c5c3_Buffer) }
func embeddableForm(root string, 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_Var33 := templ.GetChildren(ctx)
if templ_7745c5c3_Var33 == nil {
templ_7745c5c3_Var33 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
postUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments/create/remote", root, shortIdToSlug(website.ShortId))
formStr :=
`<form action="%s" 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="submit" value="Submit"/>
</div>
</form>`
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<pre><code>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(formStr, postUrl))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 205, Col: 34}
}
_, 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, 51, "</code></pre>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -681,4 +728,29 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm)
}) })
} }
func embedJavaScriptSnippet(root string, 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_Var35 := templ.GetChildren(ctx)
if templ_7745c5c3_Var35 == nil {
templ_7745c5c3_Var35 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return nil
})
}
var _ = templruntime.GeneratedTemplate var _ = templruntime.GeneratedTemplate