Better error-handling, emails for not-attending, language strings for JS errors

This commit contains the following changes:
* Better error handling: Previously, there was no proper handling in
  of email-related errors. Now, all error messages are caught and the
  user gets a nice landing page. The script will always try to contact
  the organizers of the wedding with information on the error.
* Emails for not-attending: This commit adds the possibility to send
  seperate emails to guests that attend and guests that cannot attend.
* Language strings for JavaScript errors: Previously, all JS errors were
  in Dutch. Now, they are configurable.
This commit is contained in:
Dennis Potter 2019-09-04 01:27:43 +02:00
parent 454175755a
commit ac77c9064a
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
4 changed files with 191 additions and 32 deletions

View File

@ -33,8 +33,8 @@
languages: { languages: {
en: { en: {
# Email which will be sent to a guest # Email which will be sent to a guest which will attend
"email": { "attending_email": {
"subject": "You RSVPed to our wedding!", "subject": "You RSVPed to our wedding!",
"message": "message":
@ -43,13 +43,34 @@
<br /><br /> <br /><br />
Thanks for letting us know that you will attent to our wedding. Thanks for letting us know that you will attend to our wedding.
... ...
''', ''',
# ICS File to sent with email # ICS File to sent with email
"attachment": "<ATTACHMENT>.ics", "attachment": "<ATTACHMENT>.ics",
},
"not_attending_email": {
"subject": "We received your message :-(",
"message":
'''
Hi {name}!
<br /><br />
Bummer! You can't attend our wedding.
<br /><br />
In case you get a free slot in your calendar after all, you are of course still welcome to join us! If that would be the case, please fill out the contact form again.
<br /><br />
Warm regards,
<br /><br />
...
''',
# ICS File to sent with email
"attachment": "",
}, },
# Text in form # Text in form
@ -68,6 +89,9 @@
"five_guest": "5 guests", "five_guest": "5 guests",
"submit": "Let us know!", "submit": "Let us know!",
"additional_info": "Is there something particular we need to know (for example, will you join us later or do you have allergies)?" "additional_info": "Is there something particular we need to know (for example, will you join us later or do you have allergies)?"
"name_error": "Please state a name",
"email_error": "Please state a valid email",
"guest_error": "Select number of guests"
}, },
# Text on page that thanks user for e-mail # Text on page that thanks user for e-mail
@ -82,11 +106,18 @@
"title": "<NAME_1> & <NAME_2> Wedding", "title": "<NAME_1> & <NAME_2> Wedding",
"h1": "Oops ⁠— excuse us!", "h1": "Oops ⁠— excuse us!",
"message": "Apparently, you already sent a message less than <NUMBER OF MINUTES> minutes ago. Wait at least <NUMBER OF MINUTES> minutes before attempting to sent another message. Unfortunately, we had to add this feature to our website to protect ourselves from spam. Do you think this is not right? Please send an email to <EMAILADDRESS>.", "message": "Apparently, you already sent a message less than <NUMBER OF MINUTES> minutes ago. Wait at least <NUMBER OF MINUTES> minutes before attempting to sent another message. Unfortunately, we had to add this feature to our website to protect ourselves from spam. Do you think this is not right? Please send an email to <EMAILADDRESS>.",
},
# Text on page that tells user that something went wrong while sending the email
"email_issue": {
"title": "<NAME_1> & <NAME_@> Wedding",
"h1": "Something went wrong!",
"message": "The email could not be sent. The reason for this could be that you provided an invalid email address, that the email address couldn't be recognized as such, or because the receiving email service rejected us. Please shoot us an email at <EMAILADDRESS>. Sorry for the inconvenience!",
} }
}, },
nl: { nl: {
# Email which will be sent to a guest # Email which will be sent to a guest which will attend
"email": { "attending_email": {
"subject": "Je hebt je aangemeld voor onze bruiloft!", "subject": "Je hebt je aangemeld voor onze bruiloft!",
"message": "message":
@ -104,6 +135,27 @@
"attachment": "<ATTACHMENT>.ics", "attachment": "<ATTACHMENT>.ics",
}, },
"not_attending_email": {
"subject": "We hebben je bericht ontvangen :-(",
"message":
'''
Hoi {name}!
<br /><br />
Wat jammer dat het je/jullie niet lukt om erbij te zijn!
<br /><br />
Mocht de situatie toch nog veranderen, ben je/zijn jullie natuurlijk van harte welkom om alsnog langs te komen. Vul in dat geval het contact formulier opnieuw in om ons hiervan op de hoogte te stellen.
<br /><br />
Groetjes,
<br /><br />
Dennis en Lisanne
''',
# ICS File to sent with email
"attachment": "",
},
# Text in form # Text in form
"form": { "form": {
"title": "<NAME_1> & <NAME_2> Bruiloft", "title": "<NAME_1> & <NAME_2> Bruiloft",
@ -120,6 +172,9 @@
"five_guest": "5 gasten", "five_guest": "5 gasten",
"submit": "Laat ons iets weten!", "submit": "Laat ons iets weten!",
"additional_info": "Is er iets wat we moeten weten (kom je bijvoorbeeld later of heb je allergiën?" "additional_info": "Is er iets wat we moeten weten (kom je bijvoorbeeld later of heb je allergiën?"
"name_error": "Vul aub een naam in",
"email_error": "Vul aub een geldig adres in",
"guest_error": "Selecteer aantal gasten"
}, },
# Text on page that thanks user for e-mail # Text on page that thanks user for e-mail
@ -134,12 +189,19 @@
"title": "<NAME_1> & <NAME_2> Bruiloft", "title": "<NAME_1> & <NAME_2> Bruiloft",
"h1": "Oops ⁠— sorry voor het ongemak!", "h1": "Oops ⁠— sorry voor het ongemak!",
"message": "Het lijkt erop dat je minder dan <NUMBER OF MINUTES> minuten geleden al eens een bericht verstuurd hebt via deze pagina. Wacht tenminste <NUMBER OF MINUTES> minuten voordat je nog een bericht verstuurd. We moesten deze bescherming helaas op de website zetten ter protectie tegen spam. Denk je dat dit niet klopt? Stuur dan even een e-mail naar <EMAILADDRESS>.", "message": "Het lijkt erop dat je minder dan <NUMBER OF MINUTES> minuten geleden al eens een bericht verstuurd hebt via deze pagina. Wacht tenminste <NUMBER OF MINUTES> minuten voordat je nog een bericht verstuurd. We moesten deze bescherming helaas op de website zetten ter protectie tegen spam. Denk je dat dit niet klopt? Stuur dan even een e-mail naar <EMAILADDRESS>.",
},
# Text on page that tells user that something went wrong while sending the email
"email_issue": {
"title": "<NAME_1> & <NAME_@> Bruiloft",
"h1": "Iets ging er mis!",
"message": "Het is niet gelukt de e-mail te versturen. Dit kan komen omdat het e-mailadres wat je opgegeven hebt niet geldig was, het niet gelukt is het te herkennen, of omdat de ontvangende e-mailservice de e-mail heeft afgewezen. We zouden het fijn vinden als je ons alsnog een berichtje stuurt via <EMAILADDRESS>. Sorry voor het ongemakt!",
} }
}, },
de: { de: {
# Email which will be sent to a guest # Email which will be sent to a guest which will attend
"email": { "attending_email": {
"subject": "Du hast dich angemeldet zu unserer Hochzeit!", "subject": "Du hast dich angemeldet zu unserer Hochzeit!",
"message": "message":
@ -155,6 +217,27 @@
"attachment": "<ATTACHMENT>.ics", "attachment": "<ATTACHMENT>.ics",
}, },
"not_attending_email": {
"subject": "Wir haben deine Nachricht empfangen :-(",
"message":
'''
Hi {name}!
<br /><br />
Schade, dass du nicht kommen kannst!
<br /><br />
Wenn sich deine Situation ändern würde, und du trotzdem Zeit hättest, bist du natürlich noch immer herzlich Wilkommen. In diesem Fall würden wir dich jedoch bitten, das Kontaktformular neu auszufüllen.
<br /><br />
Liebe Grüße,
<br /><br />
...
''',
# ICS File to sent with email
"attachment": "",
},
# Text in form # Text in form
"form": { "form": {
"title": "<NAME_1> & <NAME_2> Hochzeit", "title": "<NAME_1> & <NAME_2> Hochzeit",
@ -171,6 +254,9 @@
"five_guest": "5 Gäste", "five_guest": "5 Gäste",
"submit": "Formular absenden!", "submit": "Formular absenden!",
"additional_info": "Gibt es zusätzliche Sachen die wir wissen müssen (kommst du zum Beispiel später oder hast du Allergien)?" "additional_info": "Gibt es zusätzliche Sachen die wir wissen müssen (kommst du zum Beispiel später oder hast du Allergien)?"
"name_error": "Bitte Namen ergänzen",
"email_error": "Bitte gültige Adresse ergänzen",
"guest_error": "Bitte Anzahl Gäste angeben"
}, },
# Text on page that thanks user for e-mail # Text on page that thanks user for e-mail
@ -185,6 +271,13 @@
"title": "<NAME_1> & <NAME_2> Hochzeit", "title": "<NAME_1> & <NAME_2> Hochzeit",
"h1": "Oops ⁠— entschuldigung für die Unannehmlichkeiten!," "h1": "Oops ⁠— entschuldigung für die Unannehmlichkeiten!,"
"message": "Es sieht danach aus, dass du in den vergangenen <NUMBER OF MINUTES> Minuten versucht hast, mehrere Nachrichten über diese Website zu schicken. Warte bitte wenigstens <NUMBER_OF_MINUTES> Minuten, vor dass du es nochmal versuchst. Wir mussten dies leider in die Seite mit aufnehmen, als Schutz gegen Spam. Glaubst du, dass diese Blockade unzurecht ist? Schicke uns dann eine E-Mail an <EMAILADDRESS>.", "message": "Es sieht danach aus, dass du in den vergangenen <NUMBER OF MINUTES> Minuten versucht hast, mehrere Nachrichten über diese Website zu schicken. Warte bitte wenigstens <NUMBER_OF_MINUTES> Minuten, vor dass du es nochmal versuchst. Wir mussten dies leider in die Seite mit aufnehmen, als Schutz gegen Spam. Glaubst du, dass diese Blockade unzurecht ist? Schicke uns dann eine E-Mail an <EMAILADDRESS>.",
},
# Text on page that tells user that something went wrong while sending the email
"email_issue": {
"title": "<NAME_1> & <NAME_2> Hochzeit",
"h1": "Da ist etwas schief gelaufen!",
"message": "Leider ist es nicht gelungen die E-Mail zu verschicken. Der Grund kann sein, dass es keine gültige E-Mail-Adresse war, ich die Addresse nicht erkannt habe, oder weil der empfangende E-Mail-Service die Nachricht nicht angenommen hat. Es wäre gut, wenn du uns die Nachricht direkt an <EMAILADDRESS> schickst. Entschuldigung für die Unannehmlichkeiten!",
} }
} }
} }

View File

@ -102,7 +102,8 @@ fn validate_name(name: &String) -> bool {
fn validate_email(email: &String) -> bool { fn validate_email(email: &String) -> bool {
// Check if the emailaddress is valid // Check if the emailaddress is valid
Regex::new(r"^[a-zA-Z0-9\.\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z]+$").unwrap().is_match(email) // This function is not completely correct: emailregex.com
Regex::new(r"^[a-zA-Z0-9\.\-_]+@[a-zA-Z0-9\-\.]+\.[a-zA-Z]+$").unwrap().is_match(email)
} }
fn strip_tld(url: &str) -> Option<String> { fn strip_tld(url: &str) -> Option<String> {
@ -121,7 +122,7 @@ fn strip_tld(url: &str) -> Option<String> {
fn send_email(to_name: &String, to_address: &String, fn send_email(to_name: &String, to_address: &String,
subject: &String, message: &String, subject: &String, message: &String,
attachment: Option<&String>, settings: &State<Settings>) { attachment: Option<&String>, settings: &State<Settings>) -> Result<(), String> {
// Load HJSON config // Load HJSON config
let email_credentials = &settings.email_credentials; let email_credentials = &settings.email_credentials;
@ -166,10 +167,17 @@ fn send_email(to_name: &String, to_address: &String,
// Configure expected authentication mechanism // Configure expected authentication mechanism
.authentication_mechanism(Mechanism::Plain).transport(); .authentication_mechanism(Mechanism::Plain).transport();
let result = mailer.send(email.into()); match mailer.send(email.into()) {
assert!(result.is_ok()); Ok(_x) => {
mailer.close();
return Ok(());
},
Err(e) => {
mailer.close();
return Err(e.to_string());
}
};
mailer.close();
} }
////////////////////////// //////////////////////////
@ -181,9 +189,12 @@ fn submit_task (host: HostHeader,
user_input: Option<Form<OwnedUserInput>>, user_input: Option<Form<OwnedUserInput>>,
client_addr: &ClientRealAddr, client_addr: &ClientRealAddr,
ip_epoch: State<Mutex<HashMap<String, u64>>>) -> Redirect { ip_epoch: State<Mutex<HashMap<String, u64>>>) -> Redirect {
// Create struct with all language strings // Create struct with all language strings
let language_strings = lang_strings(host, &settings); let language_strings = lang_strings(host, &settings);
let result : Result<(), String>;
match user_input { match user_input {
Some(x) => { Some(x) => {
// Check input for anything strange or something that does not look like // Check input for anything strange or something that does not look like
@ -219,15 +230,25 @@ fn submit_task (host: HostHeader,
} }
}; };
send_email(&x.name, if x.guests > 0 {
&x.email, // Send email about attendaning wedding
&language_strings.email.subject, result = send_email(&x.name,
&strfmt(&language_strings.email.message, &vars).unwrap(), &x.email,
Some(&language_strings.email.attachment), &language_strings.attending_email.subject,
&settings); &strfmt(&language_strings.attending_email.message, &vars).unwrap(),
} Some(&language_strings.attending_email.attachment),
else { &settings);
return Redirect::to("404"); } else {
// Send email about not attending wedding
result = send_email(&x.name,
&x.email,
&language_strings.not_attending_email.subject,
&strfmt(&language_strings.not_attending_email.message, &vars).unwrap(),
None,
&settings);
}
} else {
result = Err("Validate e-mail or name went wrong.".to_string());
} }
// Send a message to the sender's e-mailaddres to inform about // Send a message to the sender's e-mailaddres to inform about
@ -238,9 +259,6 @@ fn submit_task (host: HostHeader,
let mut rsvp_subject = String::new(); let mut rsvp_subject = String::new();
let mut rsvp_message = String::new(); let mut rsvp_message = String::new();
rsvp_subject.push_str(&x.name);
rsvp_subject.push_str(" just responded to your event!");
rsvp_message.push_str("Name: "); rsvp_message.push_str("Name: ");
rsvp_message.push_str(&x.name); rsvp_message.push_str(&x.name);
rsvp_message.push_str("<br />Emailaddress: "); rsvp_message.push_str("<br />Emailaddress: ");
@ -250,10 +268,43 @@ fn submit_task (host: HostHeader,
rsvp_message.push_str("<br />Message:<br /><br />"); rsvp_message.push_str("<br />Message:<br /><br />");
rsvp_message.push_str(&x.message); rsvp_message.push_str(&x.message);
send_email(&smtp_name, &smtp_username, &rsvp_subject, &rsvp_message, None, &settings); // Create subject and add error message (if necessary)
Redirect::to(format!("/thanks/{}", urlencoding::encode(&x.name))) let mut error = false;
rsvp_subject.push_str(&x.name);
match result {
Ok(_x) => {
rsvp_subject.push_str(" just responded to your event!");
},
Err(e) => {
rsvp_subject.push_str(" just responded to your event! (ERROR)");
rsvp_message.push_str("<br />Error message:<br />");
rsvp_message.push_str(&e);
error = true;
}
};
// Also check if e-mail to the organisation didn't go through!
match send_email(&smtp_name,
&smtp_username,
&rsvp_subject,
&rsvp_message,
None,
&settings) {
Ok(_x) => {},
Err(_e) => {
return Redirect::to("email-issue");
}
};
// Redirect to thank you page or to error page
if error {
return Redirect::to("email-issue");
} else {
return Redirect::to(format!("/thanks/{}", urlencoding::encode(&x.name)));
}
} }
None => Redirect::to("404") None => Redirect::to("email-issue")
} }
} }
@ -274,6 +325,15 @@ fn thanks(name: String, host:HostHeader, settings: State<Settings>) -> Template
message : language_strings.thanks.message.clone()}) message : language_strings.thanks.message.clone()})
} }
#[get("/email-issue")]
fn email_issue(host:HostHeader, settings: State<Settings>) -> Template {
// Create struct with all language strings
let language_strings = lang_strings(host, &settings).clone();
// Create template with appropriate strings
Template::render("message", &language_strings.email_issue)
}
#[get("/spam")] #[get("/spam")]
fn spam(host:HostHeader, settings: State<Settings>) -> Template { fn spam(host:HostHeader, settings: State<Settings>) -> Template {
// Create struct with all language strings // Create struct with all language strings
@ -314,6 +374,7 @@ fn main() {
.mount("/", routes![index]) .mount("/", routes![index])
.mount("/", routes![thanks]) .mount("/", routes![thanks])
.mount("/", routes![spam]) .mount("/", routes![spam])
.mount("/", routes![email_issue])
.mount("/", routes![submit_task]) .mount("/", routes![submit_task])
.mount("/css", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/css"))) .mount("/css", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/css")))
.mount("/js", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/js"))) .mount("/js", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/js")))

View File

@ -32,6 +32,9 @@ pub struct FormStrings {
pub five_guest: String, pub five_guest: String,
pub submit: String, pub submit: String,
pub additional_info: String, pub additional_info: String,
pub name_error: String,
pub email_error: String,
pub guest_error: String,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -43,10 +46,12 @@ pub struct MessageStrings {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LanguageStrings { pub struct LanguageStrings {
pub email: EmailStrings, pub attending_email: EmailStrings,
pub not_attending_email: EmailStrings,
pub form: FormStrings, pub form: FormStrings,
pub thanks: MessageStrings, pub thanks: MessageStrings,
pub spam:MessageStrings, pub spam:MessageStrings,
pub email_issue:MessageStrings,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View File

@ -26,12 +26,12 @@
{{ h2 }} {{ h2 }}
</span> </span>
<div class="wrap-input1 validate-input" data-validate = "Vul aub een naam in"> <div class="wrap-input1 validate-input" data-validate = "{{ name_error }}">
<input class="input1" type="text" name="name" placeholder="{{ name }}"> <input class="input1" type="text" name="name" placeholder="{{ name }}">
<span class="shadow-input1"></span> <span class="shadow-input1"></span>
</div> </div>
<div class="wrap-input1 validate-input" data-validate = "Vul aub een geldig adres in"> <div class="wrap-input1 validate-input" data-validate = "{{ email_error }}">
<input class="input1" type="text" name="email" placeholder="{{ email }}"> <input class="input1" type="text" name="email" placeholder="{{ email }}">
<span class="shadow-input1"></span> <span class="shadow-input1"></span>
</div> </div>
@ -39,7 +39,7 @@
<div class="select-box"> <div class="select-box">
<div class="select-box__current" tabindex="1"> <div class="select-box__current" tabindex="1">
<span class="shadow-input1"></span> <span class="shadow-input1"></span>
<div class="select-box__value validate-input" data-validate = "Selecteer aantal gasten" > <div class="select-box__value validate-input" data-validate = "{{ guest_error }}" >
<input class="select-box__input" type="radio" id="255" value="255" name="guests" checked="checked"/> <input class="select-box__input" type="radio" id="255" value="255" name="guests" checked="checked"/>
<p class="select-box__input-text select-box__default-text">{{ num_guests }}</p> <p class="select-box__input-text select-box__default-text">{{ num_guests }}</p>
</div> </div>