Time machine submission form
This commit is contained in:
parent
2d61286831
commit
dcdbc67b6c
|
@ -91,6 +91,41 @@ func SendPasswordReset(toAddress string, toName string, username string, resetTo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TimeMachineEmailData struct {
|
||||||
|
ProfileUrl string
|
||||||
|
Username string
|
||||||
|
UserEmail string
|
||||||
|
DiscordUsername string
|
||||||
|
MediaUrl string
|
||||||
|
DeviceInfo string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendTimeMachineEmail(profileUrl, username, userEmail, discordUsername, mediaUrl, deviceInfo, description string, perf *perf.RequestPerf) error {
|
||||||
|
perf.StartBlock("EMAIL", "Time machine email")
|
||||||
|
defer perf.EndBlock()
|
||||||
|
|
||||||
|
contents, err := renderTemplate("email_time_machine.html", TimeMachineEmailData{
|
||||||
|
ProfileUrl: profileUrl,
|
||||||
|
Username: username,
|
||||||
|
UserEmail: userEmail,
|
||||||
|
DiscordUsername: discordUsername,
|
||||||
|
MediaUrl: mediaUrl,
|
||||||
|
DeviceInfo: deviceInfo,
|
||||||
|
Description: description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendMail("team@handmade.network", "HMN Team", "[time machine] New submission", contents)
|
||||||
|
if err != nil {
|
||||||
|
return oops.New(err, "Failed to send email")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var EmailRegex = regexp.MustCompile(`^[^:\p{Cc} ]+@[^:\p{Cc} ]+\.[^:\p{Cc} ]+$`)
|
var EmailRegex = regexp.MustCompile(`^[^:\p{Cc} ]+@[^:\p{Cc} ]+\.[^:\p{Cc} ]+$`)
|
||||||
|
|
||||||
func IsEmail(address string) bool {
|
func IsEmail(address string) bool {
|
||||||
|
|
|
@ -112,6 +112,20 @@ func BuildTimeMachine() string {
|
||||||
return Url("/timemachine", nil)
|
return Url("/timemachine", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var RegexTimeMachineForm = regexp.MustCompile("^/timemachine/submit$")
|
||||||
|
|
||||||
|
func BuildTimeMachineForm() string {
|
||||||
|
defer CatchPanic()
|
||||||
|
return Url("/timemachine/submit", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var RegexTimeMachineFormDone = regexp.MustCompile("^/timemachine/thanks$")
|
||||||
|
|
||||||
|
func BuildTimeMachineFormDone() string {
|
||||||
|
defer CatchPanic()
|
||||||
|
return Url("/timemachine/thanks", nil)
|
||||||
|
}
|
||||||
|
|
||||||
// QUESTION(ben): Can we change these routes?
|
// QUESTION(ben): Can we change these routes?
|
||||||
|
|
||||||
var RegexLoginAction = regexp.MustCompile("^/login$")
|
var RegexLoginAction = regexp.MustCompile("^/login$")
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<p>Submission by <b><a href="{{ .ProfileUrl }}">{{ .Username }}</a></b> ({{ .UserEmail }}) {{ if .DiscordUsername }} On Discord: {{ .DiscordUsername }} {{ end }}</p>
|
||||||
|
<p>Submitted url: <a href="{{ .MediaUrl }}">{{ .MediaUrl }}</a></p>
|
||||||
|
Device info:<br />
|
||||||
|
<pre>{{ .DeviceInfo }}</pre>
|
||||||
|
Description:<br />
|
||||||
|
<pre>{{ .Description }}</pre>
|
|
@ -33,6 +33,127 @@
|
||||||
<link href='https://fonts.googleapis.com/css?family=Fira+Mono:300,400,500,700' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Fira+Mono:300,400,500,700' rel='stylesheet' type='text/css'>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static "style.css" }}">
|
<link rel="stylesheet" type="text/css" href="{{ static "style.css" }}">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/*
|
||||||
|
This is too light for accessiblity imo. I'll darken them a bit and see how Ben feels
|
||||||
|
about it. -- Jake 5/26/2023
|
||||||
|
background: linear-gradient(15deg, #b4c4fe, #eb8cbf ); */
|
||||||
|
background: linear-gradient(15deg, #7391fd, #e25ca4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-options,
|
||||||
|
header form,
|
||||||
|
header .menu-bar .wiki,
|
||||||
|
header .menu-bar .library {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom-color: white;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hmn-logo {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a,
|
||||||
|
footer a {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .submenu {
|
||||||
|
background-color: #d661ad;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
width: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
font-family: "MohaveHMN", sans-serif;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* '95-esque styles */
|
||||||
|
.frame {
|
||||||
|
width: 100%;
|
||||||
|
background: #c3c3c3;
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #fff #333 #333 #fff;
|
||||||
|
border-image: url('{{ dataimg "timemachine/win95-border.gif" }}') 4;
|
||||||
|
padding: 1px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame ol,
|
||||||
|
.frame ul {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame ul {
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame .title {
|
||||||
|
padding: 0 0.1875rem; /* 0 3px */
|
||||||
|
height: 1.125rem; /* 18px */
|
||||||
|
line-height: 1.125rem; /* 18px */
|
||||||
|
font-size: 0.8125rem; /* 13px */
|
||||||
|
width: 100%;
|
||||||
|
background: rgb(0, 0, 130);
|
||||||
|
color: #fff;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame .frame-close {
|
||||||
|
width: 1rem; /* 16px */
|
||||||
|
height: 0.875rem; /* 14px */
|
||||||
|
position: absolute;
|
||||||
|
right: 0.125rem; /* 2px */
|
||||||
|
top: 0.125rem; /* 2px */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frame * {
|
||||||
|
font-family: "MS Sans Serif", "Microsoft Sans Serif", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inset {
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #828282 #ffffff #ffffff #828282;
|
||||||
|
border-style: solid;
|
||||||
|
border-image: url('{{ dataimg "timemachine/win95-border-inset.gif" }}') 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.pixelated {
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop-shadow {
|
||||||
|
box-shadow: 2px 2px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.win95-btn {
|
||||||
|
width: 5.6rem;
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #fff #333 #333 #fff;
|
||||||
|
border-image: url('{{ dataimg "timemachine/win95-border-btn.gif" }}') 4;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -8,126 +8,6 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
/*
|
|
||||||
This is too light for accessiblity imo. I'll darken them a bit and see how Ben feels
|
|
||||||
about it. -- Jake 5/26/2023
|
|
||||||
background: linear-gradient(15deg, #b4c4fe, #eb8cbf ); */
|
|
||||||
background: linear-gradient(15deg, #7391fd, #e25ca4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-options,
|
|
||||||
header form,
|
|
||||||
header .menu-bar .wiki,
|
|
||||||
header .menu-bar .library {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
border-bottom-color: white;
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hmn-logo {
|
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
header a,
|
|
||||||
footer a {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .submenu {
|
|
||||||
background-color: #d661ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
width: 16rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
font-family: "MohaveHMN", sans-serif;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* '95-esque styles */
|
|
||||||
.frame {
|
|
||||||
width: 100%;
|
|
||||||
background: #c3c3c3;
|
|
||||||
border-width: 2px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #fff #333 #333 #fff;
|
|
||||||
border-image: url('{{ dataimg "timemachine/win95-border.gif" }}') 4;
|
|
||||||
padding: 1px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame ol,
|
|
||||||
.frame ul {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame ul {
|
|
||||||
list-style: disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame .title {
|
|
||||||
padding: 0 0.1875rem; /* 0 3px */
|
|
||||||
height: 1.125rem; /* 18px */
|
|
||||||
line-height: 1.125rem; /* 18px */
|
|
||||||
font-size: 0.8125rem; /* 13px */
|
|
||||||
width: 100%;
|
|
||||||
background: rgb(0, 0, 130);
|
|
||||||
color: #fff;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame .frame-close {
|
|
||||||
width: 1rem; /* 16px */
|
|
||||||
height: 0.875rem; /* 14px */
|
|
||||||
position: absolute;
|
|
||||||
right: 0.125rem; /* 2px */
|
|
||||||
top: 0.125rem; /* 2px */
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.frame * {
|
|
||||||
font-family: "MS Sans Serif", "Microsoft Sans Serif", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inset {
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: #828282 #ffffff #ffffff #828282;
|
|
||||||
border-style: solid;
|
|
||||||
border-image: url('{{ dataimg "timemachine/win95-border-inset.gif" }}') 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.pixelated {
|
|
||||||
image-rendering: crisp-edges;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drop-shadow {
|
|
||||||
box-shadow: 2px 2px black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.win95-btn {
|
|
||||||
width: 5.6rem;
|
|
||||||
border-width: 2px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #fff #333 #333 #fff;
|
|
||||||
border-image: url('{{ dataimg "timemachine/win95-border-btn.gif" }}') 4;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="center-layout content mw7 ph3 flex flex-column g3">
|
<div class="center-layout content mw7 ph3 flex flex-column g3">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
{{ template "timemachine_base.html" . }}
|
||||||
|
|
||||||
|
{{ define "frame title" }}
|
||||||
|
<div class="title">
|
||||||
|
{{ . }}
|
||||||
|
<img class="frame-close" src="{{ dataimg "timemachine/win95-close.gif" }}">
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="center-layout content mw7 ph3 flex flex-column g3">
|
||||||
|
<div class="frame mv4">
|
||||||
|
{{ template "frame title" "Hello" }}
|
||||||
|
<form class="post-content" action="" method="POST">
|
||||||
|
{{ csrftoken .Session }}
|
||||||
|
<input type="url" name="media_url" required />
|
||||||
|
<textarea name="device_info" required></textarea>
|
||||||
|
<textarea name="description" required></textarea>
|
||||||
|
<input class="win95-btn" type="submit" value="Submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let form = document.querySelector("form");
|
||||||
|
let mediaUrl = document.querySelector("[name=media_url]");
|
||||||
|
let deviceInfo = document.querySelector("[name=device_info]");
|
||||||
|
let description = document.querySelector("[name=description]");
|
||||||
|
let submitBtn = document.querySelector("[type=submit]");
|
||||||
|
|
||||||
|
function saveData() {
|
||||||
|
localStorage.setItem("tm_media_url", mediaUrl.value);
|
||||||
|
localStorage.setItem("tm_device_info", deviceInfo.value);
|
||||||
|
localStorage.setItem("tm_description", description.value);
|
||||||
|
}
|
||||||
|
form.addEventListener("submit", function() {
|
||||||
|
saveData();
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("visibilitychange", function() {
|
||||||
|
saveData();
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaUrl.value = localStorage.getItem("tm_media_url") ?? "";
|
||||||
|
deviceInfo.value = localStorage.getItem("tm_device_info") ?? "";
|
||||||
|
description.value = localStorage.getItem("tm_description") ?? "";
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{{ template "timemachine_base.html" . }}
|
||||||
|
|
||||||
|
{{ define "frame title" }}
|
||||||
|
<div class="title">
|
||||||
|
{{ . }}
|
||||||
|
<img class="frame-close" src="{{ dataimg "timemachine/win95-close.gif" }}">
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="center-layout content mw7 ph3 flex flex-column g3">
|
||||||
|
<div class="frame">
|
||||||
|
Thank you for your submission
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
localStorage.removeItem("tm_media_url");
|
||||||
|
localStorage.removeItem("tm_device_info");
|
||||||
|
localStorage.removeItem("tm_description");
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
|
@ -68,6 +68,9 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
|
||||||
hmnOnly.GET(hmnurl.RegexJamRecap2023_Visibility, JamRecap2023_Visibility)
|
hmnOnly.GET(hmnurl.RegexJamRecap2023_Visibility, JamRecap2023_Visibility)
|
||||||
|
|
||||||
hmnOnly.GET(hmnurl.RegexTimeMachine, TimeMachine)
|
hmnOnly.GET(hmnurl.RegexTimeMachine, TimeMachine)
|
||||||
|
hmnOnly.GET(hmnurl.RegexTimeMachineForm, needsAuth(TimeMachineForm))
|
||||||
|
hmnOnly.GET(hmnurl.RegexTimeMachineFormDone, needsAuth(TimeMachineFormDone))
|
||||||
|
hmnOnly.POST(hmnurl.RegexTimeMachineForm, needsAuth(csrfMiddleware(TimeMachineFormSubmit)))
|
||||||
|
|
||||||
hmnOnly.GET(hmnurl.RegexStaffRolesIndex, StaffRolesIndex)
|
hmnOnly.GET(hmnurl.RegexStaffRolesIndex, StaffRolesIndex)
|
||||||
hmnOnly.GET(hmnurl.RegexStaffRole, StaffRole)
|
hmnOnly.GET(hmnurl.RegexStaffRole, StaffRole)
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package website
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/email"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TimeMachineForm(c *RequestContext) ResponseData {
|
||||||
|
var res ResponseData
|
||||||
|
res.MustWriteTemplate(
|
||||||
|
"time_machine_form.html",
|
||||||
|
getBaseDataAutocrumb(c, "Time Machine"),
|
||||||
|
c.Perf,
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeMachineFormSubmit(c *RequestContext) ResponseData {
|
||||||
|
c.Req.ParseForm()
|
||||||
|
|
||||||
|
mediaUrl := strings.TrimSpace(c.Req.Form.Get("media_url"))
|
||||||
|
deviceInfo := strings.TrimSpace(c.Req.Form.Get("device_info"))
|
||||||
|
description := strings.TrimSpace(c.Req.Form.Get("description"))
|
||||||
|
|
||||||
|
discordUsername := ""
|
||||||
|
if c.CurrentUser.DiscordUser != nil {
|
||||||
|
discordUsername = c.CurrentUser.DiscordUser.Username
|
||||||
|
}
|
||||||
|
err := email.SendTimeMachineEmail(
|
||||||
|
hmnurl.BuildUserProfile(c.CurrentUser.Username),
|
||||||
|
c.CurrentUser.BestName(),
|
||||||
|
c.CurrentUser.Email,
|
||||||
|
discordUsername,
|
||||||
|
mediaUrl,
|
||||||
|
deviceInfo,
|
||||||
|
description,
|
||||||
|
c.Perf,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send time machine email"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Redirect(hmnurl.BuildTimeMachineFormDone(), http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeMachineFormDone(c *RequestContext) ResponseData {
|
||||||
|
var res ResponseData
|
||||||
|
res.MustWriteTemplate(
|
||||||
|
"time_machine_form_done.html",
|
||||||
|
getBaseDataAutocrumb(c, "Time Machine"),
|
||||||
|
c.Perf,
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
}
|
Loading…
Reference in New Issue