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
|
||||
}
|
||||
|
||||
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} ]+$`)
|
||||
|
||||
func IsEmail(address string) bool {
|
||||
|
|
|
@ -112,6 +112,20 @@ func BuildTimeMachine() string {
|
|||
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?
|
||||
|
||||
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 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>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -8,126 +8,6 @@
|
|||
{{ end }}
|
||||
|
||||
{{ 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>
|
||||
|
|
|
@ -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.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.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