feat(unsubscribe): Add unsubscribed page and update api

This commit is contained in:
Bouncey
2018-09-06 14:59:52 +01:00
committed by Stuart Taylor
parent 435e856cdb
commit 5b7b3ca33c
6 changed files with 158 additions and 112 deletions

View File

@ -1,5 +1,5 @@
{ {
"semi": false, "semi": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "es5" "trailingComma": "es5"
} }

View File

@ -1,5 +1,7 @@
import request from 'request'; import request from 'request';
import { homeLocation } from '../../../config/env';
import constantStrings from '../utils/constantStrings.json'; import constantStrings from '../utils/constantStrings.json';
import testimonials from '../resources/testimonials.json'; import testimonials from '../resources/testimonials.json';
@ -8,13 +10,14 @@ const githubSecret = process.env.GITHUB_SECRET;
module.exports = function(app) { module.exports = function(app) {
const router = app.loopback.Router(); const router = app.loopback.Router();
const api = app.loopback.Router();
const User = app.models.User; const User = app.models.User;
router.get('/api/github', githubCalls); router.get('/api/github', githubCalls);
router.get('/chat', chat); router.get('/chat', chat);
router.get('/twitch', twitch); router.get('/twitch', twitch);
router.get('/u/:email', unsubscribe); router.get('/u/:email', unsubscribeDepricated);
router.get('/unsubscribe/:email', unsubscribe); router.get('/unsubscribe/:email', unsubscribeDepricated);
router.get('/ue/:unsubscribeId', unsubscribeById); router.get('/ue/:unsubscribeId', unsubscribeById);
router.get( router.get(
'/the-fastest-web-page-on-the-internet', '/the-fastest-web-page-on-the-internet',
@ -22,7 +25,7 @@ module.exports = function(app) {
); );
router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId); router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId);
router.get('/unsubscribed', unsubscribed); router.get('/unsubscribed', unsubscribed);
router.get('/resubscribe/:unsubscribeId', resubscribe); api.get('/resubscribe/:unsubscribeId', resubscribe);
router.get('/nonprofits', nonprofits); router.get('/nonprofits', nonprofits);
router.get('/nonprofits-form', nonprofitsForm); router.get('/nonprofits-form', nonprofitsForm);
router.get('/pmi-acp-agile-project-managers', agileProjectManagers); router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
@ -32,21 +35,24 @@ module.exports = function(app) {
router.get('/all-stories', showAllTestimonials); router.get('/all-stories', showAllTestimonials);
router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork); router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork);
router.get( router.get(
'/software-resources-for-nonprofits', '/software-resources-for-nonprofits',
softwareResourcesForNonprofits softwareResourcesForNonprofits
); );
router.get('/academic-honesty', academicHonesty); router.get('/academic-honesty', academicHonesty);
app.use(router); app.use(router);
app.use('/internal', api);
function chat(req, res) { function chat(req, res) {
res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp'); res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
} }
function howNonprofitProjectsWork(req, res) { function howNonprofitProjectsWork(req, res) {
res.redirect(301, res.redirect(
'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a'); 301,
'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a'
);
} }
function softwareResourcesForNonprofits(req, res) { function softwareResourcesForNonprofits(req, res) {
@ -56,9 +62,9 @@ module.exports = function(app) {
} }
function academicHonesty(req, res) { function academicHonesty(req, res) {
res.render('resources/academic-honesty', { res.render('resources/academic-honesty', {
title: 'Academic Honesty policy' title: 'Academic Honesty policy'
}); });
} }
function theFastestWebPageOnTheInternet(req, res) { function theFastestWebPageOnTheInternet(req, res) {
@ -69,7 +75,8 @@ module.exports = function(app) {
function showTestimonials(req, res) { function showTestimonials(req, res) {
res.render('resources/stories', { res.render('resources/stories', {
title: 'Testimonials from Happy freeCodeCamp Students ' + title:
'Testimonials from Happy freeCodeCamp Students ' +
'who got Software Engineer Jobs', 'who got Software Engineer Jobs',
stories: testimonials.slice(0, 72), stories: testimonials.slice(0, 72),
moreStories: true moreStories: true
@ -78,7 +85,8 @@ module.exports = function(app) {
function showAllTestimonials(req, res) { function showAllTestimonials(req, res) {
res.render('resources/stories', { res.render('resources/stories', {
title: 'Testimonials from Happy freeCodeCamp Students ' + title:
'Testimonials from Happy freeCodeCamp Students ' +
'who got Software Engineer Jobs', 'who got Software Engineer Jobs',
stories: testimonials, stories: testimonials,
moreStories: false moreStories: false
@ -119,88 +127,51 @@ module.exports = function(app) {
res.redirect('https://twitch.tv/freecodecamp'); res.redirect('https://twitch.tv/freecodecamp');
} }
function unsubscribe(req, res, next) { function unsubscribeDepricated(req, res) {
req.checkParams( req.flash(
'email', 'info',
`"${req.params.email}" isn't a valid email address.` 'We are no longer able to process this unsubscription request. ' +
).isEmail(); 'Please go to your settings to update your email preferences'
const errors = req.validationErrors(true); );
if (errors) { res.redirectWithFlash(homeLocation);
req.flash('error', { msg: errors.email.msg });
return res.redirect('/');
}
return User.find({
where: {
email: req.params.email
}
}, (err, users) => {
if (err) { return next(err); }
if (!users.length) {
req.flash('info', {
msg: 'Email address not found. Please update your Email ' +
'preferences from your settings.'
});
return res.redirect('/');
}
const updates = users.map(user => {
return new Promise((resolve, reject) =>
user.updateAttributes({
sendQuincyEmail: false
}, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
})
);
});
return Promise.all(updates)
.then(() => {
req.flash('info', {
msg: 'We\'ve successfully updated your Email preferences.'
});
return res.redirect('/unsubscribed');
})
.catch(next);
});
} }
function unsubscribeById(req, res, next) { function unsubscribeById(req, res, next) {
const { unsubscribeId } = req.params; const { unsubscribeId } = req.params;
if (!unsubscribeId) { if (!unsubscribeId) {
req.flash('info', { req.flash('info', 'We could not find an account to unsubscribe');
msg: 'We could not find an account to unsubscribe' return res.redirectWithFlash(homeLocation);
});
return res.redirect('/');
} }
return User.find({ where: { unsubscribeId } }, (err, users) => { return User.find({ where: { unsubscribeId } }, (err, users) => {
if (err || !users.length) { if (err || !users.length) {
req.flash('info', { req.flash('info', 'We could not find an account to unsubscribe');
msg: 'We could not find an account to unsubscribe' return res.redirectWithFlash(homeLocation);
});
return res.redirect('/');
} }
const updates = users.map(user => { const updates = users.map(user => {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
user.updateAttributes({ user.updateAttributes(
sendQuincyEmail: false {
}, (err) => { sendQuincyEmail: false
if (err) { },
reject(err); err => {
} else { if (err) {
resolve(); reject(err);
} else {
resolve();
}
} }
}) )
); );
}); });
return Promise.all(updates) return Promise.all(updates)
.then(() => { .then(() => {
req.flash('success', { req.flash(
msg: 'We\'ve successfully updated your email preferences.' 'success',
}); "We've successfully updated your email preferences."
return res.redirect(`/unsubscribed/${unsubscribeId}`); );
return res.redirectWithFlash(
`${homeLocation}/unsubscribed/${unsubscribeId}`
);
}) })
.catch(next); .catch(next);
}); });
@ -222,40 +193,45 @@ module.exports = function(app) {
function resubscribe(req, res, next) { function resubscribe(req, res, next) {
const { unsubscribeId } = req.params; const { unsubscribeId } = req.params;
return User.find({ where: { unsubscribeId } }, if (!unsubscribeId) {
(err, users) => { req.flash(
if (err || !users.length) { 'info',
req.flash('info', { 'We we unable to process this request, please check and try againÍ'
msg: 'We could not find an account to unsubscribe' );
}); res.redirect(homeLocation);
return res.redirect('/'); }
return User.find({ where: { unsubscribeId } }, (err, users) => {
} if (err || !users.length) {
const [ user ] = users; req.flash('info', 'We could not find an account to resubscribe');
return new Promise((resolve, reject) => return res.redirectWithFlash(homeLocation);
user.updateAttributes({ }
const [user] = users;
return new Promise((resolve, reject) =>
user.updateAttributes(
{
sendQuincyEmail: true sendQuincyEmail: true
}, (err) => { },
err => {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
resolve(); resolve();
} }
}) }
) )
)
.then(() => { .then(() => {
req.flash('success', { req.flash(
msg: 'success',
'We\'ve successfully updated your email preferences. Thank you ' + "We've successfully updated your email preferences. Thank you " +
'for resubscribing.' 'for resubscribing.'
}); );
return res.redirect('/'); return res.redirectWithFlash(homeLocation);
}) })
.catch(next); .catch(next);
}); });
} }
function githubCalls(req, res, next) { function githubCalls(req, res, next) {
var githubHeaders = { var githubHeaders = {
headers: { headers: {
@ -273,10 +249,12 @@ module.exports = function(app) {
].join(''), ].join(''),
githubHeaders, githubHeaders,
function(err, status1, pulls) { function(err, status1, pulls) {
if (err) { return next(err); } if (err) {
pulls = pulls ? return next(err);
Object.keys(JSON.parse(pulls)).length : }
'Can\'t connect to github'; pulls = pulls
? Object.keys(JSON.parse(pulls)).length
: "Can't connect to github";
return request( return request(
[ [
@ -288,10 +266,13 @@ module.exports = function(app) {
].join(''), ].join(''),
githubHeaders, githubHeaders,
function(err, status2, issues) { function(err, status2, issues) {
if (err) { return next(err); } if (err) {
issues = ((pulls === parseInt(pulls, 10)) && issues) ? return next(err);
Object.keys(JSON.parse(issues)).length - pulls : }
"Can't connect to GitHub"; issues =
pulls === parseInt(pulls, 10) && issues
? Object.keys(JSON.parse(issues)).length - pulls
: "Can't connect to GitHub";
return res.send({ return res.send({
issues: issues, issues: issues,
pulls: pulls pulls: pulls

View File

@ -11,7 +11,7 @@ module.exports = {
'gatsby-plugin-react-helmet', 'gatsby-plugin-react-helmet',
{ {
resolve: 'gatsby-plugin-create-client-paths', resolve: 'gatsby-plugin-create-client-paths',
options: { prefixes: ['/certification/*'] } options: { prefixes: ['/certification/*', '/unsubscribed/*'] }
}, },
{ {
resolve: 'gatsby-plugin-manifest', resolve: 'gatsby-plugin-manifest',
@ -27,4 +27,4 @@ module.exports = {
}, },
'gatsby-plugin-sitemap' 'gatsby-plugin-sitemap'
] ]
}; }

View File

@ -0,0 +1,41 @@
import React from 'react'
import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap'
import { apiLocation } from '../../config/env.json'
import Layout from '../components/Layout'
import FullWidthRow from '../components/helpers/FullWidthRow'
import { Spacer } from '../components/helpers'
function ShowUnsubscribed({ unsubscribeId }) {
return (
<Layout>
<Grid>
<FullWidthRow>
<Spacer />
<Spacer />
<Panel bsStyle="primary" className="text-center">
<Spacer />
<h2>You have successfully been unsubscribed</h2>
<p>Whatever you go on to, keep coding!</p>
</Panel>
</FullWidthRow>
{unsubscribeId ? (
<FullWidthRow>
<Button
bsStyle="primary"
bsSize="lg"
block={true}
href={`${apiLocation}/internal/resubscribe/${unsubscribeId}`}
>
You can click here to resubscribe
</Button>
</FullWidthRow>
) : null}
</Grid>
</Layout>
)
}
ShowUnsubscribed.displayName = 'ShowUnsubscribed'
export default ShowUnsubscribed

View File

@ -0,0 +1,19 @@
import React from 'react'
import { Router } from '@reach/router'
import RedirectHome from '../components/RedirectHome'
import ShowUnsubscribed from '../client-only-routes/ShowUnsubscribed'
function Unsubscribed() {
return (
<Router>
<ShowUnsubscribed path="/unsubscribed/:unsubscribeId" />
<ShowUnsubscribed path='/unsubscribed' />
<RedirectHome default={true} />
</Router>
)
}
Unsubscribed.displayName = 'Unsubscribed'
export default Unsubscribed

View File

@ -10,4 +10,9 @@
/:username/front-end-certification /certification/:username/legacy-front-end 301 /:username/front-end-certification /certification/:username/legacy-front-end 301
/:username/data-visualization-certification /certification/:username/legacy-data-visualization 301 /:username/data-visualization-certification /certification/:username/legacy-data-visualization 301
/:username/back-end-certification /certification/:username/legacy-back-end 301 /:username/back-end-certification /certification/:username/legacy-back-end 301
/:username/full-stack-certification /certification/:username/full-stack 301 /:username/full-stack-certification /certification/:username/full-stack 301
# unsunscribe redirects
/u/* https://api.freecodecamp.org/u/:splat
/unsunscribe/* https://api.freecodecamp.org/unsunscribe/:splat
/ue/* https://api.freecodecamp.org/ue/:splat