Skip to content

Commit

Permalink
Handle unsubscribe flow refreshes/unsub form resubmits
Browse files Browse the repository at this point in the history
- Moves unsubscribe survey into separate template
- Allows user to refresh an unsubmitted survey form without being redirected to ("/");
- Prevents users from submitting multiple unsubscribe surveys
- Throws "this email is not subscribed to Firefox Monitor" error when users refresh the unsubscribe survey page after submitting an answer.
  • Loading branch information
lesleyjanenorton committed Sep 12, 2018
1 parent 76c953a commit 9033f74
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 59 deletions.
39 changes: 31 additions & 8 deletions controllers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const HBSHelpers = require("../hbs-helpers");
const UNSUB_REASONS = require("../unsubscribe_reasons");
const sha1 = require("../sha1-utils");
const TIPS = require("../tips");
const crypto = require("crypto");


async function add(req, res) {
Expand Down Expand Up @@ -66,6 +67,7 @@ async function verify(req, res) {

async function getUnsubscribe(req, res) {
const subscriber = await DB.getSubscriberByToken(req.query.token);
//throws error if user backs into and refreshes unsubscribe page
if (!subscriber) {
throw new Error("This email address is not subscribed to Firefox Monitor.");
}
Expand All @@ -79,17 +81,36 @@ async function getUnsubscribe(req, res) {


async function postUnsubscribe(req, res) {

const unsubscribedUser = await DB.removeSubscriberByToken(req.body.token, req.body.emailHash);

// if user backs into unsubscribe page and clicks "unsubscribe" again
if (!unsubscribedUser) {
res.redirect("/");
return;
throw new Error("This email address is not subscribed to Firefox Monitor");
}
res.render("unsubscribe", {
title: "Firefox Monitor: Unsubscribe",
unsubscribed: unsubscribedUser,
UNSUB_REASONS,

const surveyTicket = crypto.randomBytes(40).toString("hex");
req.session.unsub = surveyTicket;

res.redirect("unsubscribe_survey");
}


async function getUnsubSurvey(req, res) {
//throws error if user refreshes unsubscribe survey page after they have submitted an answer
if(!req.session.unsub) {
throw new Error("This email address is not subscribed to Firefox Monitor.");
}
res.render("unsubscribe_survey", {
title: "Firefox Monitor: Unsubscribe Survey",
UNSUB_REASONS,
});
}


async function postUnsubSurvey(req, res) {
//clear session in case a user subscribes / unsubscribes multiple times or with multiple email addresses.
req.session.reset();
res.send({
title: "Firefox Monitor: Unsubscribed",
});
}

Expand All @@ -99,4 +120,6 @@ module.exports = {
verify,
getUnsubscribe,
postUnsubscribe,
getUnsubSurvey,
postUnsubSurvey,
};
13 changes: 11 additions & 2 deletions public/js/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ const addUser = (formEvent) => {
.catch(error => console.error(error));
};

const unsubscribeSurvey = (formEvent) => {
const unsubSurvey = formEvent.target;
ga_sendPing("unsubscribeSurvey", unsubSurvey.querySelector("input[type='radio']:checked").value);
const surveyObject = {};
postData(unsubSurvey.action, surveyObject)
.then( () => {
unsubSurvey.classList.add("show-thankyou");
});
};

const postData = (url, data = {}) => {
return fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
Expand Down Expand Up @@ -312,8 +322,7 @@ function handleFormSubmits(formEvent) {
formEvent.target.classList.add("invalid");
return;
}
formEvent.target.classList.add("show-thankyou");
ga_sendPing("unsubscribeSurvey", formEvent.target.querySelector("input[type='radio']:checked").value);
unsubscribeSurvey(formEvent);
return;
}
const thisForm = formEvent.target;
Expand Down
5 changes: 3 additions & 2 deletions routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const express = require("express");
const bodyParser = require("body-parser");

const { asyncMiddleware } = require("../middleware");
const { add, verify, getUnsubscribe, postUnsubscribe } = require("../controllers/user");
const { add, verify, getUnsubscribe, postUnsubscribe, getUnsubSurvey, postUnsubSurvey } = require("../controllers/user");

const router = express.Router();
const jsonParser = bodyParser.json();
Expand All @@ -16,6 +16,7 @@ router.get("/verify", jsonParser, asyncMiddleware(verify));
router.use("/unsubscribe", urlEncodedParser);
router.get("/unsubscribe", asyncMiddleware(getUnsubscribe));
router.post("/unsubscribe", asyncMiddleware(postUnsubscribe));

router.get("/unsubscribe_survey", asyncMiddleware(getUnsubSurvey));
router.post("/unsubscribe_survey", jsonParser, asyncMiddleware(postUnsubSurvey));

module.exports = router;
63 changes: 16 additions & 47 deletions views/unsubscribe.hbs
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
{{!< default }}
<div class="sub-page-content">
{{#if unsubscribed }}
<section class="unsubscribe-content section-wrapper">
<h2 class="section-headline whole">
You are no longer subscribed.
</h2>
<p class="whole sub-head">
Your email is unsubscribed from Firefox Monitor. Thank you for using this service. Will you take a moment to answer one question about your experience?
</p>
</section><!--.unsubscribe-content.section-wrapper-->
<section class="unsubscribe-survey section-wrapper drop-shadow">
<div class="whole">
<form action="" method="post" id="unsubscribe-survey-form" class="form-group">
<span class="secondary-title whole" tabindex="1">Why are you unsubscribing from Firefox Monitor alerts?</span>
{{#each UNSUB_REASONS}}
<div class="radio-button-group" tabindex="1">
<input id="reason{{ id_number }}" type="radio" name="unsubscribe-reason" value="{{ value }}" />
<span class="radio-dot"></span>
<label for="reason{{ id_number }}">{{ value }}</label>
</div>
{{/each}}
<div class="input-group-button">
<span class="thank-you-message">Thank you for your feedback.</span>
<span class="error-message">Please select one.</span>
<input id="unsubscribe-survey-submit" type="submit" class="button submit transparent-button" value="Unsubscribe" tabindex="1" />
<img class="loader" src="/img/loader.gif" alt="loading data" />
</div>
</form>
</div>
</section>
{{ else }}
<section class="unsubscribe-content section-wrapper">
<h2 class="section-headline whole" tabindex="1">Unsubscribe from Firefox Monitor</h2>
<p class="whole sub-head" tabindex="1">
This will remove your email from the Firefox Monitor list and you will no longer receive alerts when new breaches are announced.
</p>
<div class="half">
<form action="/user/unsubscribe" class="form-group" method="post" id="unsubscribe-form">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="emailHash" value="{{ hash }}">
<div class="input-group-button">
<input type="submit" class="button submit transparent-button" value="Unsubscribe" data-server-url="{{ SERVER_URL }}">
<img class="loader" src="/img/loader.gif" alt="loading data" />
</div>
</form>
</div>
</section>
{{/if}}
<section class="unsubscribe-content section-wrapper">
<h2 class="section-headline whole" tabindex="1">Unsubscribe from Firefox Monitor</h2>
<p class="whole sub-head" tabindex="1">
This will remove your email from the Firefox Monitor list and you will no longer receive alerts when new breaches are announced.
</p>
<div class="half">
<form action="/user/unsubscribe" class="form-group" method="post" id="unsubscribe-form">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="emailHash" value="{{ hash }}">
<div class="input-group-button">
<input type="submit" class="button submit transparent-button" value="Unsubscribe" data-server-url="{{ SERVER_URL }}">
<img class="loader" src="/img/loader.gif" alt="loading data" />
</div>
</form>
</div>
</section>
</div>
31 changes: 31 additions & 0 deletions views/unsubscribe_survey.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{!< default }}
<div class="sub-page-content">
<section class="unsubscribe-content section-wrapper">
<h2 class="section-headline whole">
You are no longer subscribed.
</h2>
<p class="whole sub-head">
Your email is unsubscribed from Firefox Monitor. Thank you for using this service. Will you take a moment to answer one question about your experience?
</p>
</section><!--.unsubscribe-content.section-wrapper-->
<section class="unsubscribe-survey section-wrapper drop-shadow">
<div class="whole">
<form action="" method="post" id="unsubscribe-survey-form" class="form-group">
<span class="secondary-title whole" tabindex="1">Why are you unsubscribing from Firefox Monitor alerts?</span>
{{#each UNSUB_REASONS}}
<div class="radio-button-group" tabindex="1">
<input id="reason{{ id_number }}" type="radio" name="unsubscribe-reason" value="{{ value }}" />
<span class="radio-dot"></span>
<label for="reason{{ id_number }}">{{ value }}</label>
</div>
{{/each}}
<div class="input-group-button">
<span class="thank-you-message">Thank you for your feedback.</span>
<span class="error-message">Please select one.</span>
<input id="unsubscribe-survey-submit" type="submit" class="button submit transparent-button" value="Unsubscribe" tabindex="1" />
<img class="loader" src="/img/loader.gif" alt="loading data" />
</div>
</form>
</div>
</section>
</div>

0 comments on commit 9033f74

Please sign in to comment.