diff --git a/package.json b/package.json
index c99b623ca0..35742ea05b 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,7 @@
"passport-twitter": "~1.0.2",
"ramda": "~0.10.0",
"request": "~2.53.0",
+ "rx": "^2.5.3",
"sanitize-html": "~1.6.1",
"sitemap": "~0.7.4",
"twit": "~1.1.20",
diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js
index c3275e6f62..038af52c08 100644
--- a/server/boot/fieldGuide.js
+++ b/server/boot/fieldGuide.js
@@ -1,122 +1,126 @@
var R = require('ramda'),
- express = require('express'),
+ // Rx = require('rx'),
// debug = require('debug')('freecc:fieldguides'),
resources = require('../resources/resources');
-var router = express.Router();
+module.exports = function(app) {
+ var router = app.Router();
+ var FieldGuide = app.models.FieldGuide;
-router.get('/field-guide/all-articles', showAllFieldGuides);
-router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
-router.get('/field-guide/', returnNextFieldGuide);
-router.post('/completed-field-guide/', completedFieldGuide);
+ router.get('/field-guide/all-articles', showAllFieldGuides);
+ router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
+ router.get('/field-guide/', returnNextFieldGuide);
+ router.post('/completed-field-guide/', completedFieldGuide);
-function returnIndividualFieldGuide(req, res, next) {
- var dashedName = req.params.fieldGuideName;
- if (req.user) {
- var completed = req.user.completedFieldGuides;
+ function returnIndividualFieldGuide(req, res, next) {
+ var dashedName = req.params.fieldGuideName;
+ if (req.user) {
+ var completed = req.user.completedFieldGuides;
- var uncompletedFieldGuides = resources.allFieldGuideIds()
- .filter(function (elem) {
- if (completed.indexOf(elem) === -1) {
- return elem;
- }
+ var uncompletedFieldGuides = resources.allFieldGuideIds()
+ .filter(function (elem) {
+ if (completed.indexOf(elem) === -1) {
+ return elem;
+ }
+ });
+ req.user.uncompletedFieldGuides = uncompletedFieldGuides;
+ // TODO(berks): handle callback properly
+ req.user.save(function(err) {
+ if (err) { return next(err); }
});
- req.user.uncompletedFieldGuides = uncompletedFieldGuides;
- // TODO(berks): handle callback properly
- req.user.save();
+ }
+
+ // NOTE(berks): loopback might have issue with regex here.
+ FieldGuide.find(
+ { dashedName: new RegExp(dashedName, 'i') },
+ function(err, fieldGuideFromMongo) {
+ if (err) {
+ return next(err);
+ }
+
+ if (fieldGuideFromMongo.length < 1) {
+ req.flash('errors', {
+ msg: "404: We couldn't find a field guide entry with that name. " +
+ 'Please double check the name.'
+ });
+
+ return res.redirect('/field-guide');
+ }
+
+ var fieldGuide = R.head(fieldGuideFromMongo);
+ fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
+
+ if (fieldGuide.dashedName !== dashedName) {
+ return res.redirect('../field-guide/' + fieldGuide.dashedName);
+ }
+ res.render('field-guide/show', {
+ title: fieldGuide.name,
+ fieldGuideId: fieldGuide._id,
+ description: fieldGuide.description.join('')
+ });
+ }
+ );
}
- FieldGuide.find(
- { dashedName: new RegExp(dashedName, 'i') },
- function(err, fieldGuideFromMongo) {
+ function showAllFieldGuides(req, res) {
+ var allFieldGuideNamesAndIds = resources.allFieldGuideNamesAndIds();
+
+ var completedFieldGuides = [];
+ if (req.user && req.user.completedFieldGuides) {
+ completedFieldGuides = req.user.completedFieldGuides;
+ }
+ res.render('field-guide/all-articles', {
+ allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
+ completedFieldGuides: completedFieldGuides
+ });
+ }
+
+ function returnNextFieldGuide(req, res, next) {
+ if (!req.user) {
+ return res.redirect('/field-guide/how-do-i-use-this-guide');
+ }
+
+ var displayedFieldGuides =
+ FieldGuide.find({'_id': req.user.uncompletedFieldGuides[0]});
+
+ displayedFieldGuides.exec(function(err, fieldGuide) {
+ if (err) { return next(err); }
+ fieldGuide = fieldGuide.pop();
+
+ if (typeof fieldGuide === 'undefined') {
+ if (req.user.completedFieldGuides.length > 0) {
+ req.flash('success', {
+ msg: [
+ "You've read all our current Field Guide entries. You can ",
+ 'contribute to our Field Guide ',
+ "here."
+ ].join('')
+ });
+ }
+ return res.redirect('../field-guide/how-do-i-use-this-guide');
+ }
+ var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
+ return res.redirect('../field-guide/' + nameString);
+ });
+ }
+
+ function completedFieldGuide(req, res, next) {
+ var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
+
+ req.user.completedFieldGuides.push(fieldGuideId);
+
+ var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId);
+ if (index > -1) {
+ req.user.progressTimestamps.push(Date.now());
+ req.user.uncompletedFieldGuides.splice(index, 1);
+ }
+
+ req.user.save(function (err) {
if (err) {
return next(err);
}
-
- if (fieldGuideFromMongo.length < 1) {
- req.flash('errors', {
- msg: "404: We couldn't find a field guide entry with that name. " +
- 'Please double check the name.'
- });
-
- return res.redirect('/field-guide');
- }
-
- var fieldGuide = R.head(fieldGuideFromMongo);
- fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
-
- if (fieldGuide.dashedName !== dashedName) {
- return res.redirect('../field-guide/' + fieldGuide.dashedName);
- }
- res.render('field-guide/show', {
- title: fieldGuide.name,
- fieldGuideId: fieldGuide._id,
- description: fieldGuide.description.join('')
- });
- }
- );
-}
-
-function showAllFieldGuides(req, res) {
- var allFieldGuideNamesAndIds = resources.allFieldGuideNamesAndIds();
-
- var completedFieldGuides = [];
- if (req.user && req.user.completedFieldGuides) {
- completedFieldGuides = req.user.completedFieldGuides;
+ res.send(true);
+ });
}
- res.render('field-guide/all-articles', {
- allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
- completedFieldGuides: completedFieldGuides
- });
-}
-
-function returnNextFieldGuide(req, res, next) {
- if (!req.user) {
- return res.redirect('/field-guide/how-do-i-use-this-guide');
- }
-
- var displayedFieldGuides =
- FieldGuide.find({'_id': req.user.uncompletedFieldGuides[0]});
-
- displayedFieldGuides.exec(function(err, fieldGuide) {
- if (err) { return next(err); }
- fieldGuide = fieldGuide.pop();
-
- if (typeof fieldGuide === 'undefined') {
- if (req.user.completedFieldGuides.length > 0) {
- req.flash('success', {
- msg: [
- "You've read all our current Field Guide entries. You can ",
- 'contribute to our Field Guide ',
- "here."
- ].join('')
- });
- }
- return res.redirect('../field-guide/how-do-i-use-this-guide');
- }
- var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
- return res.redirect('../field-guide/' + nameString);
- });
-}
-
-function completedFieldGuide(req, res, next) {
- var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
-
- req.user.completedFieldGuides.push(fieldGuideId);
-
- var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId);
- if (index > -1) {
- req.user.progressTimestamps.push(Date.now());
- req.user.uncompletedFieldGuides.splice(index, 1);
- }
-
- req.user.save(function (err) {
- if (err) {
- return next(err);
- }
- res.send(true);
- });
-}
-
-module.exports = router;
+};
diff --git a/server/boot/home.js b/server/boot/home.js
index 16cb3434a0..036d454945 100644
--- a/server/boot/home.js
+++ b/server/boot/home.js
@@ -1,22 +1,21 @@
-var express = require('express');
-var router = express.Router();
var message =
'Learn to Code JavaScript and get a Coding Job by Helping Nonprofits';
-router.get('/', index);
+module.exports = function(app) {
+ var router = app.Router();
+ router.get('/', index);
-function index(req, res, next) {
- if (req.user && !req.user.profile.picture) {
- req.user.profile.picture =
- 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png';
+ function index(req, res, next) {
+ if (req.user && !req.user.profile.picture) {
+ req.user.profile.picture =
+ 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png';
- req.user.save(function(err) {
- if (err) { return next(err); }
+ req.user.save(function(err) {
+ if (err) { return next(err); }
+ res.render('home', { title: message });
+ });
+ } else {
res.render('home', { title: message });
- });
- } else {
- res.render('home', { title: message });
+ }
}
-}
-
-module.exports = router;
+};
diff --git a/server/boot/nonprofits.js b/server/boot/nonprofits.js
index 2fe25a6194..507f366b49 100644
--- a/server/boot/nonprofits.js
+++ b/server/boot/nonprofits.js
@@ -1,126 +1,128 @@
-var express = require('express'),
- Nonprofit = require('../../common/models/Nonprofit');
-var router = express.Router();
+module.exports = function(app) {
+ var router = app.Router();
+ var Nonprofit = app.models.Nonprofit;
-router.get('/nonprofits/directory', nonprofitsDirectory);
-router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
+ router.get('/nonprofits/directory', nonprofitsDirectory);
+ router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
-function nonprofitsDirectory(req, res, next) {
- Nonprofit.find({ estimatedHours: { $gt: 0 } }, function(err, nonprofits) {
- if (err) { return next(err); }
-
- res.render('nonprofits/directory', {
- title: 'Nonprofits we help',
- nonprofits: nonprofits
- });
- });
-}
-
-function returnIndividualNonprofit(req, res, next) {
- var dashedName = req.params.nonprofitName;
- var nonprofitName = dashedName.replace(/\-/g, ' ');
-
- Nonprofit.find(
- { name: new RegExp(nonprofitName, 'i') },
- function(err, nonprofit) {
- if (err) {
- return next(err);
- }
-
- if (nonprofit.length < 1) {
- req.flash('errors', {
- msg: "404: We couldn't find a nonprofit with that name. " +
- 'Please double check the name.'
- });
-
- return res.redirect('/nonprofits');
- }
-
- nonprofit = nonprofit.pop();
- var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
- if (dashedNameFull !== dashedName) {
- return res.redirect('../nonprofit/' + dashedNameFull);
- }
- var buttonActive = false;
- if (req.user) {
- if (req.user.uncompletedBonfires.length === 0) {
- if (req.user.completedCoursewares.length > 63) {
- var hasShownInterest =
- nonprofit.interestedCampers.filter(function ( obj ) {
- return obj.username === req.user.profile.username;
- });
-
- if (hasShownInterest.length === 0) {
- buttonActive = true;
- }
- }
- }
- }
-
- res.render('nonprofits/show', {
- dashedName: dashedNameFull,
- title: nonprofit.name,
- logoUrl: nonprofit.logoUrl,
- estimatedHours: nonprofit.estimatedHours,
- projectDescription: nonprofit.projectDescription,
-
- approvedOther:
- nonprofit.approvedDeliverables.indexOf('other') > -1,
- approvedWebsite:
- nonprofit.approvedDeliverables.indexOf('website') > -1,
-
- approvedDonor:
- nonprofit.approvedDeliverables.indexOf('donor') > -1,
- approvedInventory:
- nonprofit.approvedDeliverables.indexOf('inventory') > -1,
-
- approvedVolunteer:
- nonprofit.approvedDeliverables.indexOf('volunteer') > -1,
- approvedForm:
- nonprofit.approvedDeliverables.indexOf('form') > -1,
-
- approvedCommunity:
- nonprofit.approvedDeliverables.indexOf('community') > -1,
- approvedELearning:
- nonprofit.approvedDeliverables.indexOf('eLearning') > -1,
-
- websiteLink: nonprofit.websiteLink,
- imageUrl: nonprofit.imageUrl,
- whatDoesNonprofitDo: nonprofit.whatDoesNonprofitDo,
- interestedCampers: nonprofit.interestedCampers,
- assignedCampers: nonprofit.assignedCampers,
- buttonActive: buttonActive,
- currentStatus: nonprofit.currentStatus
- });
- }
- );
-}
-
-/*
-function interestedInNonprofit(req, res, next) {
- if (req.user) {
- Nonprofit.findOne(
- { name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i') },
- function(err, nonprofit) {
+ function nonprofitsDirectory(req, res, next) {
+ Nonprofit.find(
+ { where: { estimatedHours: { $gt: 0 } } },
+ function(err, nonprofits) {
if (err) { return next(err); }
- nonprofit.interestedCampers.push({
- username: req.user.profile.username,
- picture: req.user.profile.picture,
- timeOfInterest: Date.now()
- });
- nonprofit.save(function(err) {
- if (err) { return next(err); }
- req.flash('success', {
- msg: 'Thanks for expressing interest in this nonprofit project! ' +
- "We've added you to this project as an interested camper!"
- });
- res.redirect('back');
+
+ res.render('nonprofits/directory', {
+ title: 'Nonprofits we help',
+ nonprofits: nonprofits
});
}
);
}
-}
-*/
-module.exports = router;
+ function returnIndividualNonprofit(req, res, next) {
+ var dashedName = req.params.nonprofitName;
+ var nonprofitName = dashedName.replace(/\-/g, ' ');
+
+ Nonprofit.find(
+ { where: { name: new RegExp(nonprofitName, 'i') } },
+ function(err, nonprofit) {
+ if (err) {
+ return next(err);
+ }
+
+ if (nonprofit.length < 1) {
+ req.flash('errors', {
+ msg: "404: We couldn't find a nonprofit with that name. " +
+ 'Please double check the name.'
+ });
+
+ return res.redirect('/nonprofits');
+ }
+
+ nonprofit = nonprofit.pop();
+ var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
+ if (dashedNameFull !== dashedName) {
+ return res.redirect('../nonprofit/' + dashedNameFull);
+ }
+ var buttonActive = false;
+ if (req.user) {
+ if (req.user.uncompletedBonfires.length === 0) {
+ if (req.user.completedCoursewares.length > 63) {
+ var hasShownInterest =
+ nonprofit.interestedCampers.filter(function ( obj ) {
+ return obj.username === req.user.profile.username;
+ });
+
+ if (hasShownInterest.length === 0) {
+ buttonActive = true;
+ }
+ }
+ }
+ }
+
+ res.render('nonprofits/show', {
+ dashedName: dashedNameFull,
+ title: nonprofit.name,
+ logoUrl: nonprofit.logoUrl,
+ estimatedHours: nonprofit.estimatedHours,
+ projectDescription: nonprofit.projectDescription,
+
+ approvedOther:
+ nonprofit.approvedDeliverables.indexOf('other') > -1,
+ approvedWebsite:
+ nonprofit.approvedDeliverables.indexOf('website') > -1,
+
+ approvedDonor:
+ nonprofit.approvedDeliverables.indexOf('donor') > -1,
+ approvedInventory:
+ nonprofit.approvedDeliverables.indexOf('inventory') > -1,
+
+ approvedVolunteer:
+ nonprofit.approvedDeliverables.indexOf('volunteer') > -1,
+ approvedForm:
+ nonprofit.approvedDeliverables.indexOf('form') > -1,
+
+ approvedCommunity:
+ nonprofit.approvedDeliverables.indexOf('community') > -1,
+ approvedELearning:
+ nonprofit.approvedDeliverables.indexOf('eLearning') > -1,
+
+ websiteLink: nonprofit.websiteLink,
+ imageUrl: nonprofit.imageUrl,
+ whatDoesNonprofitDo: nonprofit.whatDoesNonprofitDo,
+ interestedCampers: nonprofit.interestedCampers,
+ assignedCampers: nonprofit.assignedCampers,
+ buttonActive: buttonActive,
+ currentStatus: nonprofit.currentStatus
+ });
+ }
+ );
+ }
+
+ /*
+ function interestedInNonprofit(req, res, next) {
+ if (req.user) {
+ Nonprofit.findOne(
+ { name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i') },
+ function(err, nonprofit) {
+ if (err) { return next(err); }
+ nonprofit.interestedCampers.push({
+ username: req.user.profile.username,
+ picture: req.user.profile.picture,
+ timeOfInterest: Date.now()
+ });
+ nonprofit.save(function(err) {
+ if (err) { return next(err); }
+ req.flash('success', {
+ msg: 'Thanks for expressing interest in this nonprofit project! ' +
+ "We've added you to this project as an interested camper!"
+ });
+ res.redirect('back');
+ });
+ }
+ );
+ }
+ }
+ */
+};
diff --git a/server/boot/passport.js b/server/boot/passport.js
index 6c3c01bef4..53918dd346 100644
--- a/server/boot/passport.js
+++ b/server/boot/passport.js
@@ -1,68 +1,67 @@
-var express = require('express'),
- passport = require('passport'),
+var passport = require('passport'),
passportConf = require('../../config/passport');
-var router = express.Router();
-var passportOptions = {
- successRedirect: '/',
- failureRedirect: '/login'
-};
-
-router.all('/account', passportConf.isAuthenticated);
-
-router.get('/auth/twitter', passport.authenticate('twitter'));
-
-router.get(
- '/auth/twitter/callback',
- passport.authenticate('twitter', {
+module.exports = function(app) {
+ var router = app.Router();
+ var passportOptions = {
successRedirect: '/',
failureRedirect: '/login'
- })
-);
+ };
-router.get(
- '/auth/linkedin',
- passport.authenticate('linkedin', {
- state: 'SOME STATE'
- })
-);
+ router.all('/account', passportConf.isAuthenticated);
-router.get(
- '/auth/linkedin/callback',
- passport.authenticate('linkedin', passportOptions)
-);
+ router.get('/auth/twitter', passport.authenticate('twitter'));
-router.get(
- '/auth/facebook',
- passport.authenticate('facebook', {scope: ['email', 'user_location']})
-);
+ router.get(
+ '/auth/twitter/callback',
+ passport.authenticate('twitter', {
+ successRedirect: '/',
+ failureRedirect: '/login'
+ })
+ );
-router.get(
- '/auth/facebook/callback',
- passport.authenticate('facebook', passportOptions), function (req, res) {
- res.redirect(req.session.returnTo || '/');
- }
-);
+ router.get(
+ '/auth/linkedin',
+ passport.authenticate('linkedin', {
+ state: 'SOME STATE'
+ })
+ );
-router.get('/auth/github', passport.authenticate('github'));
+ router.get(
+ '/auth/linkedin/callback',
+ passport.authenticate('linkedin', passportOptions)
+ );
-router.get(
- '/auth/github/callback',
- passport.authenticate('github', passportOptions), function (req, res) {
- res.redirect(req.session.returnTo || '/');
- }
-);
+ router.get(
+ '/auth/facebook',
+ passport.authenticate('facebook', {scope: ['email', 'user_location']})
+ );
-router.get(
- '/auth/google',
- passport.authenticate('google', {scope: 'profile email'})
-);
+ router.get(
+ '/auth/facebook/callback',
+ passport.authenticate('facebook', passportOptions), function (req, res) {
+ res.redirect(req.session.returnTo || '/');
+ }
+ );
-router.get(
- '/auth/google/callback',
- passport.authenticate('google', passportOptions), function (req, res) {
- res.redirect(req.session.returnTo || '/');
- }
-);
+ router.get('/auth/github', passport.authenticate('github'));
-module.exports = router;
+ router.get(
+ '/auth/github/callback',
+ passport.authenticate('github', passportOptions), function (req, res) {
+ res.redirect(req.session.returnTo || '/');
+ }
+ );
+
+ router.get(
+ '/auth/google',
+ passport.authenticate('google', {scope: 'profile email'})
+ );
+
+ router.get(
+ '/auth/google/callback',
+ passport.authenticate('google', passportOptions), function (req, res) {
+ res.redirect(req.session.returnTo || '/');
+ }
+ );
+};
diff --git a/server/boot/redirects.js b/server/boot/redirects.js
index c5422b6986..2ad49ac83c 100644
--- a/server/boot/redirects.js
+++ b/server/boot/redirects.js
@@ -1,47 +1,48 @@
-var express = require('express');
-var router = express.Router();
+module.exports = function(app) {
+ var router = app.Router();
-router.get('/nonprofit-project-instructions', function(req, res) {
- res.redirect(
- 301,
- '/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work'
- );
-});
+ router.get('/nonprofit-project-instructions', function(req, res) {
+ res.redirect(
+ 301,
+ '/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work'
+ );
+ });
-router.get('/agile', function(req, res) {
- res.redirect(301, '/pmi-acp-agile-project-managers');
-});
+ router.get('/agile', function(req, res) {
+ res.redirect(301, '/pmi-acp-agile-project-managers');
+ });
-router.get('/live-pair-programming', function(req, res) {
- res.redirect(301, '/field-guide/live-stream-pair-programming-on-twitch.tv');
-});
+ router.get('/live-pair-programming', function(req, res) {
+ res.redirect(301, '/field-guide/live-stream-pair-programming-on-twitch.tv');
+ });
-router.get('/install-screenhero', function(req, res) {
- res.redirect(301, '/field-guide/install-screenhero');
-});
+ router.get('/install-screenhero', function(req, res) {
+ res.redirect(301, '/field-guide/install-screenhero');
+ });
-router.get('/guide-to-our-nonprofit-projects', function(req, res) {
- res.redirect(301, '/field-guide/a-guide-to-our-nonprofit-projects');
-});
+ router.get('/guide-to-our-nonprofit-projects', function(req, res) {
+ res.redirect(301, '/field-guide/a-guide-to-our-nonprofit-projects');
+ });
-router.get('/chromebook', function(req, res) {
- res.redirect(301, '/field-guide/chromebook');
-});
+ router.get('/chromebook', function(req, res) {
+ res.redirect(301, '/field-guide/chromebook');
+ });
-router.get('/deploy-a-website', function(req, res) {
- res.redirect(301, '/field-guide/deploy-a-website');
-});
+ router.get('/deploy-a-website', function(req, res) {
+ res.redirect(301, '/field-guide/deploy-a-website');
+ });
-router.get('/gmail-shortcuts', function(req, res) {
- res.redirect(301, '/field-guide/gmail-shortcuts');
-});
+ router.get('/gmail-shortcuts', function(req, res) {
+ res.redirect(301, '/field-guide/gmail-shortcuts');
+ });
-router.get('/nodeschool-challenges', function(req, res) {
- res.redirect(301, '/field-guide/nodeschool-challenges');
-});
+ router.get('/nodeschool-challenges', function(req, res) {
+ res.redirect(301, '/field-guide/nodeschool-challenges');
+ });
-router.get('/privacy', function(req, res) {
- res.redirect(301, '/field-guide/what-is-the-free-code-camp-privacy-policy?');
-});
-
-module.exports = router;
+ router.get('/privacy', function(req, res) {
+ res.redirect(
+ 301, '/field-guide/what-is-the-free-code-camp-privacy-policy?'
+ );
+ });
+};
diff --git a/server/boot/story.js b/server/boot/story.js
index ef5fc134ac..d2dd96d7ba 100755
--- a/server/boot/story.js
+++ b/server/boot/story.js
@@ -1,394 +1,454 @@
var nodemailer = require('nodemailer'),
sanitizeHtml = require('sanitize-html'),
- express = require('express'),
moment = require('moment'),
mongodb = require('mongodb'),
// debug = require('debug')('freecc:cntr:story'),
- Story = require('../../common/models/Story'),
- Comment = require('../../common/models/Comment'),
- User = require('../../common/models/User'),
resources = require('../resources/resources'),
MongoClient = mongodb.MongoClient,
- secrets = require('../../config/secrets'),
- router = express.Router();
+ secrets = require('../../config/secrets');
-router.get('/stories/hotStories', hotJSON);
-router.get('/stories/recentStories', recentJSON);
-router.get('/stories/comments/:id', comments);
-router.post('/stories/comment/', commentSubmit);
-router.post('/stories/comment/:id/comment', commentOnCommentSubmit);
-router.put('/stories/comment/:id/edit', commentEdit);
-router.get('/stories/submit', submitNew);
-router.get('/stories/submit/new-story', preSubmit);
-router.post('/stories/preliminary', newStory);
-router.post('/stories/', storySubmission);
-router.get('/news/', hot);
-router.post('/stories/search', getStories);
-router.get('/news/:storyName', returnIndividualStory);
-router.post('/stories/upvote/', upvote);
+module.exports = function(app) {
+ var router = app.Router();
+ var User = app.models.User;
+ var Story = app.models.Story;
-function hotRank(timeValue, rank) {
- /*
- * Hotness ranking algorithm: http://amix.dk/blog/post/19588
- * tMS = postedOnDate - foundationTime;
- * Ranking...
- * f(ts, 1, rank) = log(10)z + (ts)/45000;
- */
- var time48Hours = 172800000;
- var hotness;
- var z = Math.log(rank) / Math.log(10);
- hotness = z + (timeValue / time48Hours);
- return hotness;
+ router.get('/stories/hotStories', hotJSON);
+ router.get('/stories/recentStories', recentJSON);
+ router.get('/stories/comments/:id', comments);
+ router.post('/stories/comment/', commentSubmit);
+ router.post('/stories/comment/:id/comment', commentOnCommentSubmit);
+ router.put('/stories/comment/:id/edit', commentEdit);
+ router.get('/stories/submit', submitNew);
+ router.get('/stories/submit/new-story', preSubmit);
+ router.post('/stories/preliminary', newStory);
+ router.post('/stories/', storySubmission);
+ router.get('/news/', hot);
+ router.post('/stories/search', getStories);
+ router.get('/news/:storyName', returnIndividualStory);
+ router.post('/stories/upvote/', upvote);
-}
-
-function hotJSON(req, res, next) {
- var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
- story.exec(function(err, stories) {
- if (err) {
- return next(err);
- }
-
- var foundationDate = 1413298800000;
-
- var sliceVal = stories.length >= 100 ? 100 : stories.length;
- return res.json(stories.map(function(elem) {
- return elem;
- }).sort(function(a, b) {
- return hotRank(b.timePosted - foundationDate, b.rank, b.headline)
- - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
- }).slice(0, sliceVal));
-
- });
-}
-
-function recentJSON(req, res, next) {
- var story = Story.find({}).sort({'timePosted': -1}).limit(100);
- story.exec(function(err, stories) {
- if (err) {
- return next(err);
- }
- return res.json(stories);
- });
-}
-
-function hot(req, res) {
- return res.render('stories/index', {
- title: 'Hot stories currently trending on Camper News',
- page: 'hot'
- });
-}
-
-function submitNew(req, res) {
- return res.render('stories/index', {
- title: 'Submit a new story to Camper News',
- page: 'submit'
- });
-}
-
-/*
- * no used anywhere
-function search(req, res) {
- return res.render('stories/index', {
- title: 'Search the archives of Camper News',
- page: 'search'
- });
-}
-
-function recent(req, res) {
- return res.render('stories/index', {
- title: 'Recently submitted stories on Camper News',
- page: 'recent'
- });
-}
-*/
-
-function preSubmit(req, res) {
-
- var data = req.query;
- var cleanData = sanitizeHtml(data.url, {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/";/g, '"');
- if (data.url.replace(/&/g, '&') !== cleanData) {
-
- req.flash('errors', {
- msg: 'The data for this post is malformed'
- });
- return res.render('stories/index', {
- page: 'stories/submit'
- });
+ function hotRank(timeValue, rank) {
+ /*
+ * Hotness ranking algorithm: http://amix.dk/blog/post/19588
+ * tMS = postedOnDate - foundationTime;
+ * Ranking...
+ * f(ts, 1, rank) = log(10)z + (ts)/45000;
+ */
+ var time48Hours = 172800000;
+ var hotness;
+ var z = Math.log(rank) / Math.log(10);
+ hotness = z + (timeValue / time48Hours);
+ return hotness;
}
- var title = data.title || '';
- var image = data.image || '';
- var description = data.description || '';
- return res.render('stories/index', {
- title: 'Confirm your Camper News story submission',
- page: 'storySubmission',
- storyURL: data.url,
- storyTitle: title,
- storyImage: image,
- storyMetaDescription: description
- });
-}
-
-
-function returnIndividualStory(req, res, next) {
- var dashedName = req.params.storyName;
-
- var storyName = dashedName.replace(/\-/g, ' ').trim();
-
- Story.find({'storyLink': storyName}, function(err, story) {
- if (err) {
- return next(err);
- }
-
-
- if (story.length < 1) {
- req.flash('errors', {
- msg: "404: We couldn't find a story with that name. " +
- 'Please double check the name.'
- });
-
- return res.redirect('/news/');
- }
-
- story = story.pop();
- var dashedNameFull = story.storyLink.toLowerCase()
- .replace(/\s+/g, ' ')
- .replace(/\s/g, '-');
- if (dashedNameFull !== dashedName) {
- return res.redirect('../news/' + dashedNameFull);
- }
-
- var userVoted = false;
- try {
- var votedObj = story.upVotes.filter(function(a) {
- return a['upVotedByUsername'] === req.user['profile']['username'];
- });
- if (votedObj.length > 0) {
- userVoted = true;
- }
- } catch(e) {
- userVoted = false;
- }
- res.render('stories/index', {
- title: story.headline,
- link: story.link,
- originalStoryLink: dashedName,
- originalStoryAuthorEmail: story.author.email || '',
- author: story.author,
- description: story.description,
- rank: story.upVotes.length,
- upVotes: story.upVotes,
- comments: story.comments,
- id: story._id,
- timeAgo: moment(story.timePosted).fromNow(),
- image: story.image,
- page: 'show',
- storyMetaDescription: story.metaDescription,
- hasUserVoted: userVoted
- });
- });
-}
-
-function getStories(req, res, next) {
- MongoClient.connect(secrets.db, function(err, database) {
- if (err) {
- return next(err);
- }
- database.collection('stories').find({
- '$text': {
- '$search': req.body.data.searchValue
- }
- }, {
- headline: 1,
- timePosted: 1,
- link: 1,
- description: 1,
- rank: 1,
- upVotes: 1,
- author: 1,
- comments: 1,
- image: 1,
- storyLink: 1,
- metaDescription: 1,
- textScore: {
- $meta: 'textScore'
- }
- }, {
- sort: {
- textScore: {
- $meta: 'textScore'
- }
- }
- }).toArray(function(err, items) {
+ function hotJSON(req, res, next) {
+ var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
+ story.exec(function(err, stories) {
if (err) {
return next(err);
}
- if (items !== null && items.length !== 0) {
- return res.json(items);
- }
- return res.sendStatus(404);
+
+ var foundationDate = 1413298800000;
+
+ var sliceVal = stories.length >= 100 ? 100 : stories.length;
+ return res.json(stories.map(function(elem) {
+ return elem;
+ }).sort(function(a, b) {
+ return hotRank(b.timePosted - foundationDate, b.rank, b.headline)
+ - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
+ }).slice(0, sliceVal));
+
});
- });
-}
+ }
-function upvote(req, res, next) {
- var data = req.body.data;
- Story.find({'_id': data.id}, function(err, story) {
- if (err) {
- return next(err);
- }
- story = story.pop();
- story.rank++;
- story.upVotes.push(
- {
- upVotedBy: req.user._id,
- upVotedByUsername: req.user.profile.username
+ function recentJSON(req, res, next) {
+ var story = Story.find({}).sort({'timePosted': -1}).limit(100);
+ story.exec(function(err, stories) {
+ if (err) {
+ return next(err);
}
- );
- story.markModified('rank');
- story.save();
- // NOTE(Berks): This logic is full of wholes and race conditions
- // this could be the source of many 'can't set headers after they are sent'
- // errors. This needs cleaning
- User.findOne({'_id': story.author.userId}, function(err, user) {
- if (err) { return next(err); }
+ return res.json(stories);
+ });
+ }
- user.progressTimestamps.push(Date.now() || 0);
- user.save(function (err) {
- req.user.save(function (err) {
- if (err) { return next(err); }
+ function hot(req, res) {
+ return res.render('stories/index', {
+ title: 'Hot stories currently trending on Camper News',
+ page: 'hot'
+ });
+ }
+
+ function submitNew(req, res) {
+ return res.render('stories/index', {
+ title: 'Submit a new story to Camper News',
+ page: 'submit'
+ });
+ }
+
+ /*
+ * no used anywhere
+ function search(req, res) {
+ return res.render('stories/index', {
+ title: 'Search the archives of Camper News',
+ page: 'search'
+ });
+ }
+
+ function recent(req, res) {
+ return res.render('stories/index', {
+ title: 'Recently submitted stories on Camper News',
+ page: 'recent'
+ });
+ }
+ */
+
+ function preSubmit(req, res) {
+
+ var data = req.query;
+ var cleanData = sanitizeHtml(data.url, {
+ allowedTags: [],
+ allowedAttributes: []
+ }).replace(/";/g, '"');
+ if (data.url.replace(/&/g, '&') !== cleanData) {
+
+ req.flash('errors', {
+ msg: 'The data for this post is malformed'
+ });
+ return res.render('stories/index', {
+ page: 'stories/submit'
+ });
+ }
+
+ var title = data.title || '';
+ var image = data.image || '';
+ var description = data.description || '';
+ return res.render('stories/index', {
+ title: 'Confirm your Camper News story submission',
+ page: 'storySubmission',
+ storyURL: data.url,
+ storyTitle: title,
+ storyImage: image,
+ storyMetaDescription: description
+ });
+ }
+
+
+ function returnIndividualStory(req, res, next) {
+ var dashedName = req.params.storyName;
+
+ var storyName = dashedName.replace(/\-/g, ' ').trim();
+
+ Story.find({'storyLink': storyName}, function(err, story) {
+ if (err) {
+ return next(err);
+ }
+
+
+ if (story.length < 1) {
+ req.flash('errors', {
+ msg: "404: We couldn't find a story with that name. " +
+ 'Please double check the name.'
});
- req.user.progressTimestamps.push(Date.now() || 0);
+
+ return res.redirect('/news/');
+ }
+
+ story = story.pop();
+ var dashedNameFull = story.storyLink.toLowerCase()
+ .replace(/\s+/g, ' ')
+ .replace(/\s/g, '-');
+ if (dashedNameFull !== dashedName) {
+ return res.redirect('../news/' + dashedNameFull);
+ }
+
+ var userVoted = false;
+ try {
+ var votedObj = story.upVotes.filter(function(a) {
+ return a['upVotedByUsername'] === req.user['profile']['username'];
+ });
+ if (votedObj.length > 0) {
+ userVoted = true;
+ }
+ } catch(e) {
+ userVoted = false;
+ }
+ res.render('stories/index', {
+ title: story.headline,
+ link: story.link,
+ originalStoryLink: dashedName,
+ originalStoryAuthorEmail: story.author.email || '',
+ author: story.author,
+ description: story.description,
+ rank: story.upVotes.length,
+ upVotes: story.upVotes,
+ comments: story.comments,
+ id: story._id,
+ timeAgo: moment(story.timePosted).fromNow(),
+ image: story.image,
+ page: 'show',
+ storyMetaDescription: story.metaDescription,
+ hasUserVoted: userVoted
+ });
+ });
+ }
+
+ function getStories(req, res, next) {
+ MongoClient.connect(secrets.db, function(err, database) {
+ if (err) {
+ return next(err);
+ }
+ database.collection('stories').find({
+ '$text': {
+ '$search': req.body.data.searchValue
+ }
+ }, {
+ headline: 1,
+ timePosted: 1,
+ link: 1,
+ description: 1,
+ rank: 1,
+ upVotes: 1,
+ author: 1,
+ comments: 1,
+ image: 1,
+ storyLink: 1,
+ metaDescription: 1,
+ textScore: {
+ $meta: 'textScore'
+ }
+ }, {
+ sort: {
+ textScore: {
+ $meta: 'textScore'
+ }
+ }
+ }).toArray(function(err, items) {
if (err) {
return next(err);
}
+ if (items !== null && items.length !== 0) {
+ return res.json(items);
+ }
+ return res.sendStatus(404);
});
});
- return res.send(story);
- });
-}
-
-function comments(req, res, next) {
- var data = req.params.id;
- Comment.find({'_id': data}, function(err, comment) {
- if (err) {
- return next(err);
- }
- comment = comment.pop();
- return res.send(comment);
- });
-}
-
-function newStory(req, res, next) {
- if (!req.user) {
- return next(new Error('Must be logged in'));
}
- var url = req.body.data.url;
- var cleanURL = sanitizeHtml(url, {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/"/g, '"');
- if (cleanURL !== url) {
- req.flash('errors', {
- msg: "The URL you submitted doesn't appear valid"
- });
- return res.json({
- alreadyPosted: true,
- storyURL: '/stories/submit'
+
+ function upvote(req, res, next) {
+ var data = req.body.data;
+ Story.find({'_id': data.id}, function(err, story) {
+ if (err) {
+ return next(err);
+ }
+ story = story.pop();
+ story.rank++;
+ story.upVotes.push(
+ {
+ upVotedBy: req.user._id,
+ upVotedByUsername: req.user.profile.username
+ }
+ );
+ story.markModified('rank');
+ story.save();
+ // NOTE(Berks): This logic is full of wholes and race conditions
+ // this could be the source of many 'can't set headers after
+ // they are sent'
+ // errors. This needs cleaning
+ User.findOne(
+ { where: { id: story.author.userId } },
+ function(err, user) {
+ if (err) { return next(err); }
+
+ user.progressTimestamps.push(Date.now() || 0);
+ user.save(function (err) {
+ req.user.save(function (err) {
+ if (err) { return next(err); }
+ });
+ req.user.progressTimestamps.push(Date.now() || 0);
+ if (err) {
+ return next(err);
+ }
+ });
+ }
+ );
+ return res.send(story);
});
+ }
+ function comments(req, res, next) {
+ var data = req.params.id;
+ Comment.find(
+ { where: {'_id': data } },
+ function(err, comment) {
+ if (err) {
+ return next(err);
+ }
+ comment = comment.pop();
+ return res.send(comment);
+ });
}
- if (url.search(/^https?:\/\//g) === -1) {
- url = 'http://' + url;
- }
- Story.find({'link': url}, function(err, story) {
- if (err) {
- return next(err);
+
+ function newStory(req, res, next) {
+ if (!req.user) {
+ return next(new Error('Must be logged in'));
}
- if (story.length) {
+ var url = req.body.data.url;
+ var cleanURL = sanitizeHtml(url, {
+ allowedTags: [],
+ allowedAttributes: []
+ }).replace(/"/g, '"');
+ if (cleanURL !== url) {
req.flash('errors', {
- msg: "Someone's already posted that link. Here's the discussion."
+ msg: "The URL you submitted doesn't appear valid"
});
return res.json({
alreadyPosted: true,
- storyURL: '/news/' + story.pop().storyLink
+ storyURL: '/stories/submit'
});
- }
- resources.getURLTitle(url, processResponse);
- });
- function processResponse(err, story) {
- if (err) {
- res.json({
- alreadyPosted: false,
- storyURL: url,
- storyTitle: '',
- storyImage: '',
- storyMetaDescription: ''
- });
- } else {
- res.json({
- alreadyPosted: false,
- storyURL: url,
- storyTitle: story.title,
- storyImage: story.image,
- storyMetaDescription: story.description
- });
+ }
+ if (url.search(/^https?:\/\//g) === -1) {
+ url = 'http://' + url;
+ }
+ Story.find(
+ { where: {'link': url} },
+ function(err, story) {
+ if (err) {
+ return next(err);
+ }
+ if (story.length) {
+ req.flash('errors', {
+ msg: "Someone's already posted that link. Here's the discussion."
+ });
+ return res.json({
+ alreadyPosted: true,
+ storyURL: '/news/' + story.pop().storyLink
+ });
+ }
+ resources.getURLTitle(url, processResponse);
+ }
+ );
+
+ function processResponse(err, story) {
+ if (err) {
+ res.json({
+ alreadyPosted: false,
+ storyURL: url,
+ storyTitle: '',
+ storyImage: '',
+ storyMetaDescription: ''
+ });
+ } else {
+ res.json({
+ alreadyPosted: false,
+ storyURL: url,
+ storyTitle: story.title,
+ storyImage: story.image,
+ storyMetaDescription: story.description
+ });
+ }
}
}
-}
-function storySubmission(req, res, next) {
- var data = req.body.data;
- if (!req.user) {
- return next(new Error('Not authorized'));
- }
- var storyLink = data.headline
- .replace(/[^a-z0-9\s]/gi, '')
- .replace(/\s+/g, ' ')
- .toLowerCase()
- .trim();
-
- var link = data.link;
-
- if (link.search(/^https?:\/\//g) === -1) {
- link = 'http://' + link;
- }
-
- Story.count({
- storyLink: new RegExp('^' + storyLink + '(?: [0-9]+)?$', 'i')
- }, function (err, storyCount) {
- if (err) {
- return next(err);
+ function storySubmission(req, res, next) {
+ var data = req.body.data;
+ if (!req.user) {
+ return next(new Error('Not authorized'));
}
-
- // if duplicate storyLink add unique number
- storyLink = (storyCount === 0) ? storyLink : storyLink + ' ' + storyCount;
+ var storyLink = data.headline
+ .replace(/[^a-z0-9\s]/gi, '')
+ .replace(/\s+/g, ' ')
+ .toLowerCase()
+ .trim();
var link = data.link;
+
if (link.search(/^https?:\/\//g) === -1) {
link = 'http://' + link;
}
- var story = new Story({
- headline: sanitizeHtml(data.headline, {
+
+ Story.count({
+ storyLink: { like: new RegExp('^' + storyLink + '(?: [0-9]+)?$', 'i') }
+ }, function (err, storyCount) {
+ if (err) {
+ return next(err);
+ }
+
+ // if duplicate storyLink add unique number
+ storyLink = (storyCount === 0) ? storyLink : storyLink + ' ' + storyCount;
+
+ var link = data.link;
+ if (link.search(/^https?:\/\//g) === -1) {
+ link = 'http://' + link;
+ }
+ var story = new Story({
+ headline: sanitizeHtml(data.headline, {
+ allowedTags: [],
+ allowedAttributes: []
+ }).replace(/"/g, '"'),
+ timePosted: Date.now(),
+ link: link,
+ description: sanitizeHtml(data.description, {
+ allowedTags: [],
+ allowedAttributes: []
+ }).replace(/"/g, '"'),
+ rank: 1,
+ upVotes: [({
+ upVotedBy: req.user._id,
+ upVotedByUsername: req.user.profile.username
+ })],
+ author: {
+ picture: req.user.profile.picture,
+ userId: req.user._id,
+ username: req.user.profile.username,
+ email: req.user.email
+ },
+ comments: [],
+ image: data.image,
+ storyLink: storyLink,
+ metaDescription: data.storyMetaDescription,
+ originalStoryAuthorEmail: req.user.email
+ });
+ story.save(function (err) {
+ if (err) {
+ return next(err);
+ }
+ req.user.progressTimestamps.push(Date.now() || 0);
+ req.user.save(function (err) {
+ if (err) {
+ return next(err);
+ }
+ res.send(JSON.stringify({
+ storyLink: story.storyLink.replace(/\s+/g, '-').toLowerCase()
+ }));
+ });
+ });
+ });
+ }
+
+ function commentSubmit(req, res, next) {
+ var data = req.body.data;
+ if (!req.user) {
+ return next(new Error('Not authorized'));
+ }
+ var sanitizedBody = sanitizeHtml(data.body,
+ {
allowedTags: [],
allowedAttributes: []
- }).replace(/"/g, '"'),
- timePosted: Date.now(),
- link: link,
- description: sanitizeHtml(data.description, {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/"/g, '"'),
- rank: 1,
- upVotes: [({
- upVotedBy: req.user._id,
- upVotedByUsername: req.user.profile.username
- })],
+ }).replace(/"/g, '"');
+ if (data.body !== sanitizedBody) {
+ req.flash('errors', {
+ msg: 'HTML is not allowed'
+ });
+ return res.send(true);
+ }
+ var comment = new Comment({
+ associatedPost: data.associatedPost,
+ originalStoryLink: data.originalStoryLink,
+ originalStoryAuthorEmail: data.originalStoryAuthorEmail,
+ body: sanitizedBody,
+ rank: 0,
+ upvotes: 0,
author: {
picture: req.user.profile.picture,
userId: req.user._id,
@@ -396,214 +456,164 @@ function storySubmission(req, res, next) {
email: req.user.email
},
comments: [],
- image: data.image,
- storyLink: storyLink,
- metaDescription: data.storyMetaDescription,
- originalStoryAuthorEmail: req.user.email
+ topLevel: true,
+ commentOn: Date.now()
});
- story.save(function (err) {
- if (err) {
- return next(err);
- }
- req.user.progressTimestamps.push(Date.now() || 0);
- req.user.save(function (err) {
- if (err) {
- return next(err);
- }
- res.send(JSON.stringify({
- storyLink: story.storyLink.replace(/\s+/g, '-').toLowerCase()
- }));
- });
- });
- });
-}
-function commentSubmit(req, res, next) {
- var data = req.body.data;
- if (!req.user) {
- return next(new Error('Not authorized'));
- }
- var sanitizedBody = sanitizeHtml(data.body,
- {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/"/g, '"');
- if (data.body !== sanitizedBody) {
- req.flash('errors', {
- msg: 'HTML is not allowed'
- });
- return res.send(true);
- }
- var comment = new Comment({
- associatedPost: data.associatedPost,
- originalStoryLink: data.originalStoryLink,
- originalStoryAuthorEmail: data.originalStoryAuthorEmail,
- body: sanitizedBody,
- rank: 0,
- upvotes: 0,
- author: {
- picture: req.user.profile.picture,
- userId: req.user._id,
- username: req.user.profile.username,
- email: req.user.email
- },
- comments: [],
- topLevel: true,
- commentOn: Date.now()
- });
-
- commentSave(comment, Story, res, next);
-}
-
-function commentOnCommentSubmit(req, res, next) {
- var data = req.body.data;
- if (!req.user) {
- return next(new Error('Not authorized'));
+ commentSave(comment, Story, res, next);
}
- var sanitizedBody = sanitizeHtml(data.body,
- {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/"/g, '"');
- if (data.body !== sanitizedBody) {
- req.flash('errors', {
- msg: 'HTML is not allowed'
- });
- return res.send(true);
- }
- var comment = new Comment({
- associatedPost: data.associatedPost,
- body: sanitizedBody,
- rank: 0,
- upvotes: 0,
- originalStoryLink: data.originalStoryLink,
- originalStoryAuthorEmail: data.originalStoryAuthorEmail,
- author: {
- picture: req.user.profile.picture,
- userId: req.user._id,
- username: req.user.profile.username,
- email: req.user.email
- },
- comments: [],
- topLevel: false,
- commentOn: Date.now()
- });
- commentSave(comment, Comment, res, next);
-}
-
-function commentEdit(req, res, next) {
-
- Comment.find({'_id': req.params.id}, function(err, cmt) {
- if (err) {
- return next(err);
- }
- cmt = cmt.pop();
-
- if (!req.user && cmt.author.userId !== req.user._id) {
+ function commentOnCommentSubmit(req, res, next) {
+ var data = req.body.data;
+ if (!req.user) {
return next(new Error('Not authorized'));
}
+ var sanitizedBody = sanitizeHtml(
+ data.body,
+ {
+ allowedTags: [],
+ allowedAttributes: []
+ }
+ ).replace(/"/g, '"');
- var sanitizedBody = sanitizeHtml(req.body.body, {
- allowedTags: [],
- allowedAttributes: []
- }).replace(/"/g, '"');
- if (req.body.body !== sanitizedBody) {
+ if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
- cmt.body = sanitizedBody;
- cmt.commentOn = Date.now();
- cmt.save(function (err) {
+ var comment = new Comment({
+ associatedPost: data.associatedPost,
+ body: sanitizedBody,
+ rank: 0,
+ upvotes: 0,
+ originalStoryLink: data.originalStoryLink,
+ originalStoryAuthorEmail: data.originalStoryAuthorEmail,
+ author: {
+ picture: req.user.profile.picture,
+ userId: req.user._id,
+ username: req.user.profile.username,
+ email: req.user.email
+ },
+ comments: [],
+ topLevel: false,
+ commentOn: Date.now()
+ });
+ commentSave(comment, Comment, res, next);
+ }
+
+ function commentEdit(req, res, next) {
+
+ Comment.find({ id: req.params.id }, function(err, cmt) {
if (err) {
return next(err);
}
- res.send(true);
- });
+ cmt = cmt.pop();
- });
+ if (!req.user && cmt.author.userId !== req.user._id) {
+ return next(new Error('Not authorized'));
+ }
-}
+ var sanitizedBody = sanitizeHtml(req.body.body, {
+ allowedTags: [],
+ allowedAttributes: []
+ }).replace(/"/g, '"');
+ if (req.body.body !== sanitizedBody) {
+ req.flash('errors', {
+ msg: 'HTML is not allowed'
+ });
+ return res.send(true);
+ }
-function commentSave(comment, Context, res, next) {
- comment.save(function(err, data) {
- if (err) {
- return next(err);
- }
- try {
- // Based on the context retrieve the parent
- // object of the comment (Story/Comment)
- Context.find({
- '_id': data.associatedPost
- }, function (err, associatedContext) {
+ cmt.body = sanitizedBody;
+ cmt.commentOn = Date.now();
+ cmt.save(function(err) {
if (err) {
return next(err);
}
- associatedContext = associatedContext.pop();
- if (associatedContext) {
- associatedContext.comments.push(data._id);
- associatedContext.save(function (err) {
- if (err) {
- return next(err);
- }
- res.send(true);
- });
- }
- // Find the author of the parent object
- User.findOne({
- 'profile.username': associatedContext.author.username
- }, function(err, recipient) {
+ res.send(true);
+ });
+
+ });
+
+ }
+
+ function commentSave(comment, Context, res, next) {
+ comment.save(function(err, data) {
+ if (err) {
+ return next(err);
+ }
+ try {
+ // Based on the context retrieve the parent
+ // object of the comment (Story/Comment)
+ Context.find({
+ id: data.associatedPost
+ }, function (err, associatedContext) {
if (err) {
return next(err);
}
- // If the emails of both authors differ,
- // only then proceed with email notification
- if (
- typeof data.author !== 'undefined' &&
- data.author.email &&
- typeof recipient !== 'undefined' &&
- recipient.email &&
- (data.author.email !== recipient.email)
- ) {
- var transporter = nodemailer.createTransport({
- service: 'Mandrill',
- auth: {
- user: secrets.mandrill.user,
- pass: secrets.mandrill.password
- }
- });
-
- var mailOptions = {
- to: recipient.email,
- from: 'Team@freecodecamp.com',
- subject: data.author.username +
- ' replied to your post on Camper News',
- text: [
- 'Just a quick heads-up: ',
- data.author.username + ' replied to you on Camper News.',
- 'You can keep this conversation going.',
- 'Just head back to the discussion here: ',
- 'http://freecodecamp.com/news/' + data.originalStoryLink,
- '- the Free Code Camp Volunteer Team'
- ].join('\n')
- };
-
- transporter.sendMail(mailOptions, function (err) {
+ associatedContext = associatedContext.pop();
+ if (associatedContext) {
+ associatedContext.comments.push(data._id);
+ associatedContext.save(function (err) {
if (err) {
- return err;
+ return next(err);
}
+ res.send(true);
});
}
- });
- });
- } catch (e) {
- return next(err);
- }
- });
-}
+ // Find the author of the parent object
+ User.findOne({
+ 'profile.username': associatedContext.author.username
+ }, function(err, recipient) {
+ if (err) {
+ return next(err);
+ }
+ // If the emails of both authors differ,
+ // only then proceed with email notification
+ if (
+ typeof data.author !== 'undefined' &&
+ data.author.email &&
+ typeof recipient !== 'undefined' &&
+ recipient.email &&
+ (data.author.email !== recipient.email)
+ ) {
+ var transporter = nodemailer.createTransport({
+ service: 'Mandrill',
+ auth: {
+ user: secrets.mandrill.user,
+ pass: secrets.mandrill.password
+ }
+ });
-module.exports = router;
+ var mailOptions = {
+ to: recipient.email,
+ from: 'Team@freecodecamp.com',
+ subject: data.author.username +
+ ' replied to your post on Camper News',
+ text: [
+ 'Just a quick heads-up: ',
+ data.author.username + ' replied to you on Camper News.',
+ 'You can keep this conversation going.',
+ 'Just head back to the discussion here: ',
+ 'http://freecodecamp.com/news/' + data.originalStoryLink,
+ '- the Free Code Camp Volunteer Team'
+ ].join('\n')
+ };
+
+ transporter.sendMail(mailOptions, function (err) {
+ if (err) {
+ return err;
+ }
+ });
+ }
+ });
+ });
+ } catch (e) {
+ return next(err);
+ }
+ });
+ }
+};
diff --git a/server/boot/user.js b/server/boot/user.js
index 8d3f7f1cfb..5317fdddad 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -5,802 +5,817 @@ var _ = require('lodash'),
nodemailer = require('nodemailer'),
passport = require('passport'),
moment = require('moment'),
- express = require('express'),
debug = require('debug')('freecc:cntr:userController'),
- User = require('../../common/models/User'),
secrets = require('../../config/secrets'),
resources = require('./../resources/resources');
-var router = express.Router();
-router.get('/login', function(req, res) {
- res.redirect(301, '/signin');
-});
-router.get('/logout', function(req, res) {
- res.redirect(301, '/signout');
-});
-router.get('/signin', getSignin);
-router.post('/signin', postSignin);
-router.get('/signout', signout);
-router.get('/forgot', getForgot);
-router.post('/forgot', postForgot);
-router.get('/reset/:token', getReset);
-router.post('/reset/:token', postReset);
-router.get('/email-signup', getEmailSignup);
-router.get('/email-signin', getEmailSignin);
-router.post('/email-signup', postEmailSignup);
-router.post('/email-signin', postSignin);
-router.get('/account/api', getAccountAngular);
-router.get('/api/checkUniqueUsername/:username', checkUniqueUsername);
-router.get('/api/checkExistingUsername/:username', checkExistingUsername);
-router.get('/api/checkUniqueEmail/:email', checkUniqueEmail);
-router.post('/account/profile', postUpdateProfile);
-router.post('/account/password', postUpdatePassword);
-router.post('/account/delete', postDeleteAccount);
-router.get('/account/unlink/:provider', getOauthUnlink);
-router.get('/account', getAccount);
-// Ensure this is the last route!
-router.get('/:username', returnUser);
+module.exports = function(app) {
+ var router = app.Router();
+ var User = app.models.User;
-/**
- * GET /signin
- * Siginin page.
- */
-
-function getSignin (req, res) {
- if (req.user) {
- return res.redirect('/');
- }
- res.render('account/signin', {
- title: 'Free Code Camp Login'
+ router.get('/login', function(req, res) {
+ res.redirect(301, '/signin');
});
-}
+ router.get('/logout', function(req, res) {
+ res.redirect(301, '/signout');
+ });
+ router.get('/signin', getSignin);
+ router.post('/signin', postSignin);
+ router.get('/signout', signout);
+ router.get('/forgot', getForgot);
+ router.post('/forgot', postForgot);
+ router.get('/reset/:token', getReset);
+ router.post('/reset/:token', postReset);
+ router.get('/email-signup', getEmailSignup);
+ router.get('/email-signin', getEmailSignin);
+ router.post('/email-signup', postEmailSignup);
+ router.post('/email-signin', postSignin);
+ router.get('/account/api', getAccountAngular);
+ router.get('/api/checkUniqueUsername/:username', checkUniqueUsername);
+ router.get('/api/checkExistingUsername/:username', checkExistingUsername);
+ router.get('/api/checkUniqueEmail/:email', checkUniqueEmail);
+ router.post('/account/profile', postUpdateProfile);
+ router.post('/account/password', postUpdatePassword);
+ router.post('/account/delete', postDeleteAccount);
+ router.get('/account/unlink/:provider', getOauthUnlink);
+ router.get('/account', getAccount);
+ // Ensure this is the last route!
+ router.get('/:username', returnUser);
-/**
- * POST /signin
- * Sign in using email and password.
- */
+ /**
+ * GET /signin
+ * Siginin page.
+ */
-function postSignin (req, res, next) {
- req.assert('email', 'Email is not valid').isEmail();
- req.assert('password', 'Password cannot be blank').notEmpty();
-
- var errors = req.validationErrors();
-
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/signin');
+ function getSignin (req, res) {
+ if (req.user) {
+ return res.redirect('/');
+ }
+ res.render('account/signin', {
+ title: 'Free Code Camp Login'
+ });
}
- passport.authenticate('local', function(err, user, info) {
- if (err) {
- return next(err);
- }
- if (!user) {
- req.flash('errors', { msg: info.message });
+ /**
+ * POST /signin
+ * Sign in using email and password.
+ */
+
+ function postSignin (req, res, next) {
+ req.assert('email', 'Email is not valid').isEmail();
+ req.assert('password', 'Password cannot be blank').notEmpty();
+
+ var errors = req.validationErrors();
+
+ if (errors) {
+ req.flash('errors', errors);
return res.redirect('/signin');
}
- req.logIn(user, function(err) {
+
+ passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
- req.flash('success', { msg: 'Success! You are logged in.' });
- if (/hotStories/.test(req.session.returnTo)) {
- return res.redirect('../news');
+ if (!user) {
+ req.flash('errors', { msg: info.message });
+ return res.redirect('/signin');
}
- if (/field-guide/.test(req.session.returnTo)) {
- return res.redirect('../field-guide');
- }
- return res.redirect(req.session.returnTo || '/');
- });
- })(req, res, next);
-}
-
-/**
- * GET /signout
- * Log out.
- */
-
-function signout (req, res) {
- req.logout();
- res.redirect('/');
-}
-
-/**
- * GET /email-signup
- * Signup page.
- */
-
-function getEmailSignin (req, res) {
- if (req.user) {
- return res.redirect('/');
- }
- res.render('account/email-signin', {
- title: 'Sign in to your Free Code Camp Account'
- });
-}
-
-/**
- * GET /signin
- * Signup page.
- */
-
-function getEmailSignup (req, res) {
- if (req.user) {
- return res.redirect('/');
- }
- res.render('account/email-signup', {
- title: 'Create Your Free Code Camp Account'
- });
-}
-
-/**
- * POST /email-signup
- * Create a new local account.
- */
-
-function postEmailSignup (req, res, next) {
- req.assert('email', 'valid email required').isEmail();
- var errors = req.validationErrors();
-
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/email-signup');
- }
-
- var possibleUserData = req.body;
-
- if (possibleUserData.password.length < 8) {
- req.flash('errors', {
- msg: 'Your password is too short'
- });
- return res.redirect('email-signup');
- }
-
- if (possibleUserData.username.length < 5 || possibleUserData.length > 20) {
- req.flash('errors', {
- msg: 'Your username must be between 5 and 20 characters'
- });
- return res.redirect('email-signup');
- }
-
-
- var user = new User({
- email: req.body.email.trim(),
- password: req.body.password,
- profile: {
- username: req.body.username.trim(),
- picture:
- 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'
- }
- });
-
- User.findOne({ email: req.body.email }, function(err, existingEmail) {
- if (err) {
- return next(err);
- }
-
- if (existingEmail) {
- req.flash('errors', {
- msg: 'Account with that email address already exists.'
+ req.logIn(user, function(err) {
+ if (err) {
+ return next(err);
+ }
+ req.flash('success', { msg: 'Success! You are logged in.' });
+ if (/hotStories/.test(req.session.returnTo)) {
+ return res.redirect('../news');
+ }
+ if (/field-guide/.test(req.session.returnTo)) {
+ return res.redirect('../field-guide');
+ }
+ return res.redirect(req.session.returnTo || '/');
});
- return res.redirect('/email-signup');
+ })(req, res, next);
+ }
+
+ /**
+ * GET /signout
+ * Log out.
+ */
+
+ function signout (req, res) {
+ req.logout();
+ res.redirect('/');
+ }
+
+ /**
+ * GET /email-signup
+ * Signup page.
+ */
+
+ function getEmailSignin (req, res) {
+ if (req.user) {
+ return res.redirect('/');
}
- User.findOne(
- { 'profile.username': req.body.username },
- function(err, existingUsername) {
+ res.render('account/email-signin', {
+ title: 'Sign in to your Free Code Camp Account'
+ });
+ }
+
+ /**
+ * GET /signin
+ * Signup page.
+ */
+
+ function getEmailSignup (req, res) {
+ if (req.user) {
+ return res.redirect('/');
+ }
+ res.render('account/email-signup', {
+ title: 'Create Your Free Code Camp Account'
+ });
+ }
+
+ /**
+ * POST /email-signup
+ * Create a new local account.
+ */
+
+ function postEmailSignup (req, res, next) {
+ req.assert('email', 'valid email required').isEmail();
+ var errors = req.validationErrors();
+
+ if (errors) {
+ req.flash('errors', errors);
+ return res.redirect('/email-signup');
+ }
+
+ var possibleUserData = req.body;
+
+ if (possibleUserData.password.length < 8) {
+ req.flash('errors', {
+ msg: 'Your password is too short'
+ });
+ return res.redirect('email-signup');
+ }
+
+ if (possibleUserData.username.length < 5 || possibleUserData.length > 20) {
+ req.flash('errors', {
+ msg: 'Your username must be between 5 and 20 characters'
+ });
+ return res.redirect('email-signup');
+ }
+
+
+ var user = new User({
+ email: req.body.email.trim(),
+ password: req.body.password,
+ profile: {
+ username: req.body.username.trim(),
+ picture:
+ 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'
+ }
+ });
+
+ User.findOne({ email: req.body.email }, function(err, existingEmail) {
if (err) {
return next(err);
}
- if (existingUsername) {
+
+ if (existingEmail) {
req.flash('errors', {
- msg: 'Account with that username already exists.'
+ msg: 'Account with that email address already exists.'
});
return res.redirect('/email-signup');
}
+ User.findOne(
+ { 'profile.username': req.body.username },
+ function(err, existingUsername) {
+ if (err) {
+ return next(err);
+ }
+ if (existingUsername) {
+ req.flash('errors', {
+ msg: 'Account with that username already exists.'
+ });
+ return res.redirect('/email-signup');
+ }
- user.save(function(err) {
- if (err) { return next(err); }
- req.logIn(user, function(err) {
+ user.save(function(err) {
if (err) { return next(err); }
- res.redirect('/email-signup');
+ req.logIn(user, function(err) {
+ if (err) { return next(err); }
+ res.redirect('/email-signup');
+ });
+ });
+ var transporter = nodemailer.createTransport({
+ service: 'Mandrill',
+ auth: {
+ user: secrets.mandrill.user,
+ pass: secrets.mandrill.password
+ }
+ });
+ var mailOptions = {
+ to: user.email,
+ from: 'Team@freecodecamp.com',
+ subject: 'Welcome to Free Code Camp!',
+ text: [
+ 'Greetings from San Francisco!\n\n',
+ 'Thank you for joining our community.\n',
+ 'Feel free to email us at this address if you have ',
+ 'any questions about Free Code Camp.\n',
+ 'And if you have a moment, check out our blog: ',
+ 'blog.freecodecamp.com.\n',
+ 'Good luck with the challenges!\n\n',
+ '- the Free Code Camp Volunteer Team'
+ ].join('')
+ };
+ transporter.sendMail(mailOptions, function(err) {
+ if (err) { return err; }
});
});
- var transporter = nodemailer.createTransport({
- service: 'Mandrill',
- auth: {
- user: secrets.mandrill.user,
- pass: secrets.mandrill.password
- }
- });
- var mailOptions = {
- to: user.email,
- from: 'Team@freecodecamp.com',
- subject: 'Welcome to Free Code Camp!',
- text: [
- 'Greetings from San Francisco!\n\n',
- 'Thank you for joining our community.\n',
- 'Feel free to email us at this address if you have ',
- 'any questions about Free Code Camp.\n',
- 'And if you have a moment, check out our blog: ',
- 'blog.freecodecamp.com.\n',
- 'Good luck with the challenges!\n\n',
- '- the Free Code Camp Volunteer Team'
- ].join('')
- };
- transporter.sendMail(mailOptions, function(err) {
- if (err) { return err; }
- });
});
- });
-}
+ }
-/**
- * GET /account
- * Profile page.
- */
+ /**
+ * GET /account
+ * Profile page.
+ */
-function getAccount (req, res) {
- res.render('account/account', {
- title: 'Manage your Free Code Camp Account'
- });
-}
+ function getAccount (req, res) {
+ res.render('account/account', {
+ title: 'Manage your Free Code Camp Account'
+ });
+ }
-/**
- * Angular API Call
- */
+ /**
+ * Angular API Call
+ */
-function getAccountAngular (req, res) {
- res.json({
- user: req.user
- });
-}
+ function getAccountAngular (req, res) {
+ res.json({
+ user: req.user
+ });
+ }
-/**
- * Unique username check API Call
- */
+ /**
+ * Unique username check API Call
+ */
-function checkUniqueUsername (req, res, next) {
- User.count(
- { 'profile.username': req.params.username.toLowerCase() },
- function (err, data) {
- if (err) { return next(err); }
- if (data === 1) {
- return res.send(true);
- } else {
- return res.send(false);
- }
- });
-}
-
-/**
- * Existing username check
- */
-
-function checkExistingUsername (req, res, next) {
- User.count(
- { 'profile.username': req.params.username.toLowerCase() },
- function (err, data) {
- if (err) { return next(err); }
- if (data === 1) {
- return res.send(true);
- } else {
- return res.send(false);
- }
- }
- );
-}
-
-/**
- * Unique email check API Call
- */
-
-function checkUniqueEmail (req, res, next) {
- User.count(
- { email: decodeURIComponent(req.params.email).toLowerCase() },
- function (err, data) {
+ function checkUniqueUsername (req, res, next) {
+ User.count(
+ { 'profile.username': req.params.username.toLowerCase() },
+ function (err, data) {
if (err) { return next(err); }
if (data === 1) {
return res.send(true);
} else {
return res.send(false);
}
- }
- );
-}
+ });
+ }
+ /**
+ * Existing username check
+ */
-/**
- * GET /campers/:username
- * Public Profile page.
- */
-
-function returnUser (req, res, next) {
- User.find(
- { 'profile.username': req.params.username.toLowerCase() },
- function(err, user) {
- if (err) {
- debug('Username err: ', err);
- return next(err);
- }
- if (user[0]) {
- user = user[0];
-
- user.progressTimestamps = user.progressTimestamps.sort(function(a, b) {
- return a - b;
- });
-
- var timeObject = Object.create(null);
- R.forEach(function(time) {
- timeObject[moment(time).format('YYYY-MM-DD')] = time;
- }, user.progressTimestamps);
-
- var tmpLongest = 1;
- var timeKeys = R.keys(timeObject);
-
- user.longestStreak = 0;
- for (var i = 1; i <= timeKeys.length; i++) {
- if (moment(timeKeys[i - 1]).add(1, 'd').toString()
- === moment(timeKeys[i]).toString()) {
- tmpLongest++;
- if (tmpLongest > user.longestStreak) {
- user.longestStreak = tmpLongest;
- }
- } else {
- tmpLongest = 1;
- }
+ function checkExistingUsername (req, res, next) {
+ User.count(
+ { 'profile.username': req.params.username.toLowerCase() },
+ function (err, data) {
+ if (err) { return next(err); }
+ if (data === 1) {
+ return res.send(true);
+ } else {
+ return res.send(false);
}
+ }
+ );
+ }
- timeKeys = timeKeys.reverse();
- tmpLongest = 1;
+ /**
+ * Unique email check API Call
+ */
- user.currentStreak = 1;
- var today = moment(Date.now()).format('YYYY-MM-DD');
+ function checkUniqueEmail (req, res, next) {
+ User.count(
+ { email: decodeURIComponent(req.params.email).toLowerCase() },
+ function (err, data) {
+ if (err) { return next(err); }
+ if (data === 1) {
+ return res.send(true);
+ } else {
+ return res.send(false);
+ }
+ }
+ );
+ }
- if (
- moment(today).toString() === moment(timeKeys[0]).toString() ||
- moment(today).subtract(1, 'd').toString() ===
- moment(timeKeys[0]).toString()
- ) {
- for (var _i = 1; _i <= timeKeys.length; _i++) {
- if (
- moment(timeKeys[_i - 1]).subtract(1, 'd').toString() ===
- moment(timeKeys[_i]).toString()
- ) {
+ /**
+ * GET /campers/:username
+ * Public Profile page.
+ */
+ function returnUser (req, res, next) {
+ User.find(
+ { 'profile.username': req.params.username.toLowerCase() },
+ function(err, user) {
+ if (err) {
+ debug('Username err: ', err);
+ return next(err);
+ }
+ if (user[0]) {
+ user = user[0];
+ user.progressTimestamps =
+ user.progressTimestamps.sort(function(a, b) {
+ return a - b;
+ });
+
+ var timeObject = Object.create(null);
+ R.forEach(function(time) {
+ timeObject[moment(time).format('YYYY-MM-DD')] = time;
+ }, user.progressTimestamps);
+
+ var tmpLongest = 1;
+ var timeKeys = R.keys(timeObject);
+
+ user.longestStreak = 0;
+ for (var i = 1; i <= timeKeys.length; i++) {
+ if (moment(timeKeys[i - 1]).add(1, 'd').toString()
+ === moment(timeKeys[i]).toString()) {
tmpLongest++;
-
- if (tmpLongest > user.currentStreak) {
- user.currentStreak = tmpLongest;
+ if (tmpLongest > user.longestStreak) {
+ user.longestStreak = tmpLongest;
}
} else {
- break;
+ tmpLongest = 1;
}
}
- } else {
- user.currentStreak = 1;
- }
- user.save(function(err) {
- if (err) {
- return next(err);
+ timeKeys = timeKeys.reverse();
+ tmpLongest = 1;
+
+ user.currentStreak = 1;
+ var today = moment(Date.now()).format('YYYY-MM-DD');
+
+ if (
+ moment(today).toString() === moment(timeKeys[0]).toString() ||
+ moment(today).subtract(1, 'd').toString() ===
+ moment(timeKeys[0]).toString()
+ ) {
+ for (var _i = 1; _i <= timeKeys.length; _i++) {
+
+ if (
+ moment(timeKeys[_i - 1]).subtract(1, 'd').toString() ===
+ moment(timeKeys[_i]).toString()
+ ) {
+
+ tmpLongest++;
+
+ if (tmpLongest > user.currentStreak) {
+ user.currentStreak = tmpLongest;
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ user.currentStreak = 1;
}
- var data = {};
- var progressTimestamps = user.progressTimestamps;
- progressTimestamps.forEach(function(timeStamp) {
- data[(timeStamp / 1000)] = 1;
- });
+ user.save(function(err) {
+ if (err) {
+ return next(err);
+ }
- user.currentStreak = user.currentStreak || 1;
- user.longestStreak = user.longestStreak || 1;
- var challenges = user.completedChallenges.filter(function ( obj ) {
- return obj.challengeType === 3 || obj.challengeType === 4;
- });
+ var data = {};
+ var progressTimestamps = user.progressTimestamps;
+ progressTimestamps.forEach(function(timeStamp) {
+ data[(timeStamp / 1000)] = 1;
+ });
- res.render('account/show', {
- title: 'Camper ' + user.profile.username + '\'s portfolio',
- username: user.profile.username,
- name: user.profile.name,
- location: user.profile.location,
- githubProfile: user.profile.githubProfile,
- linkedinProfile: user.profile.linkedinProfile,
- codepenProfile: user.profile.codepenProfile,
- facebookProfile: user.profile.facebookProfile,
- twitterHandle: user.profile.twitterHandle,
- bio: user.profile.bio,
- picture: user.profile.picture,
- progressTimestamps: user.progressTimestamps,
- website1Link: user.portfolio.website1Link,
- website1Title: user.portfolio.website1Title,
- website1Image: user.portfolio.website1Image,
- website2Link: user.portfolio.website2Link,
- website2Title: user.portfolio.website2Title,
- website2Image: user.portfolio.website2Image,
- website3Link: user.portfolio.website3Link,
- website3Title: user.portfolio.website3Title,
- website3Image: user.portfolio.website3Image,
- challenges: challenges,
- bonfires: user.completedChallenges.filter(function(challenge) {
- return challenge.challengeType === 5;
- }),
- calender: data,
- moment: moment,
- longestStreak: user.longestStreak +
- (user.longestStreak === 1 ? ' day' : ' days'),
- currentStreak: user.currentStreak +
- (user.currentStreak === 1 ? ' day' : ' days')
+ user.currentStreak = user.currentStreak || 1;
+ user.longestStreak = user.longestStreak || 1;
+ var challenges = user.completedChallenges.filter(function ( obj ) {
+ return obj.challengeType === 3 || obj.challengeType === 4;
+ });
+
+ res.render('account/show', {
+ title: 'Camper ' + user.profile.username + '\'s portfolio',
+ username: user.profile.username,
+ name: user.profile.name,
+ location: user.profile.location,
+ githubProfile: user.profile.githubProfile,
+ linkedinProfile: user.profile.linkedinProfile,
+ codepenProfile: user.profile.codepenProfile,
+ facebookProfile: user.profile.facebookProfile,
+ twitterHandle: user.profile.twitterHandle,
+ bio: user.profile.bio,
+ picture: user.profile.picture,
+ progressTimestamps: user.progressTimestamps,
+ website1Link: user.portfolio.website1Link,
+ website1Title: user.portfolio.website1Title,
+ website1Image: user.portfolio.website1Image,
+ website2Link: user.portfolio.website2Link,
+ website2Title: user.portfolio.website2Title,
+ website2Image: user.portfolio.website2Image,
+ website3Link: user.portfolio.website3Link,
+ website3Title: user.portfolio.website3Title,
+ website3Image: user.portfolio.website3Image,
+ challenges: challenges,
+ bonfires: user.completedChallenges.filter(function(challenge) {
+ return challenge.challengeType === 5;
+ }),
+ calender: data,
+ moment: moment,
+ longestStreak: user.longestStreak +
+ (user.longestStreak === 1 ? ' day' : ' days'),
+ currentStreak: user.currentStreak +
+ (user.currentStreak === 1 ? ' day' : ' days')
+ });
});
- });
- } else {
- req.flash('errors', {
- msg: "404: We couldn't find a page with that url. " +
- 'Please double check the link.'
- });
- return res.redirect('/');
+ } else {
+ req.flash('errors', {
+ msg: "404: We couldn't find a page with that url. " +
+ 'Please double check the link.'
+ });
+ return res.redirect('/');
+ }
}
- }
- );
-}
+ );
+ }
-/**
- * POST /account/profile
- * Update profile information.
- */
+ /**
+ * POST /account/profile
+ * Update profile information.
+ */
-function postUpdateProfile (req, res, next) {
+ function postUpdateProfile (req, res, next) {
+
+ User.findById(req.user.id, function(err) {
+ if (err) { return next(err); }
+ var errors = req.validationErrors();
+ if (errors) {
+ req.flash('errors', errors);
+ return res.redirect('/account');
+ }
+
+ User.findOne({ email: req.body.email }, function(err, existingEmail) {
+ if (err) {
+ return next(err);
+ }
+ var user = req.user;
+ if (existingEmail && existingEmail.email !== user.email) {
+ req.flash('errors', {
+ msg: 'An account with that email address already exists.'
+ });
+ return res.redirect('/account');
+ }
+ User.findOne(
+ { 'profile.username': req.body.username },
+ function(err, existingUsername) {
+ if (err) {
+ return next(err);
+ }
+ var user = req.user;
+ if (
+ existingUsername &&
+ existingUsername.profile.username !== user.profile.username
+ ) {
+ req.flash('errors', {
+ msg: 'An account with that username already exists.'
+ });
+ return res.redirect('/account');
+ }
+ var body = req.body || {};
+ user.email = body.email.trim() || '';
+ user.profile.name = body.name.trim() || '';
+ user.profile.username = body.username.trim() || '';
+ user.profile.location = body.location.trim() || '';
+
+ user.profile.githubProfile = body.githubProfile.trim() || '';
+ user.profile.facebookProfile = body.facebookProfile.trim() || '';
+ user.profile.linkedinProfile = body.linkedinProfile.trim() || '';
+
+ user.profile.codepenProfile = body.codepenProfile.trim() || '';
+ user.profile.twitterHandle = body.twitterHandle.trim() || '';
+ user.profile.bio = body.bio.trim() || '';
+
+ user.profile.picture = body.picture.trim() ||
+ 'https://s3.amazonaws.com/freecodecamp/' +
+ 'camper-image-placeholder.png';
+ user.portfolio.website1Title = body.website1Title.trim() || '';
+ user.portfolio.website1Link = body.website1Link.trim() || '';
+ user.portfolio.website1Image = body.website1Image.trim() || '';
+
+ user.portfolio.website2Title = body.website2Title.trim() || '';
+ user.portfolio.website2Link = body.website2Link.trim() || '';
+ user.portfolio.website2Image = body.website2Image.trim() || '';
+
+ user.portfolio.website3Title = body.website3Title.trim() || '';
+ user.portfolio.website3Link = body.website3Link.trim() || '';
+ user.portfolio.website3Image = body.website3Image.trim() || '';
+
+
+ user.save(function (err) {
+ if (err) {
+ return next(err);
+ }
+ resources.updateUserStoryPictures(
+ user._id.toString(),
+ user.profile.picture,
+ user.profile.username,
+ function(err) {
+ if (err) { return next(err); }
+ req.flash('success', {
+ msg: 'Profile information updated.'
+ });
+ res.redirect('/account');
+ }
+ );
+ });
+ }
+ );
+ });
+ });
+ }
+
+ /**
+ * POST /account/password
+ * Update current password.
+ */
+
+ function postUpdatePassword (req, res, next) {
+ req.assert('password', 'Password must be at least 4 characters long')
+ .len(4);
+
+ req.assert('confirmPassword', 'Passwords do not match')
+ .equals(req.body.password);
- User.findById(req.user.id, function(err) {
- if (err) { return next(err); }
var errors = req.validationErrors();
+
if (errors) {
req.flash('errors', errors);
return res.redirect('/account');
}
- User.findOne({ email: req.body.email }, function(err, existingEmail) {
- if (err) {
- return next(err);
- }
- var user = req.user;
- if (existingEmail && existingEmail.email !== user.email) {
- req.flash('errors', {
- msg: 'An account with that email address already exists.'
- });
- return res.redirect('/account');
- }
- User.findOne(
- { 'profile.username': req.body.username },
- function(err, existingUsername) {
- if (err) {
- return next(err);
- }
- var user = req.user;
- if (
- existingUsername &&
- existingUsername.profile.username !== user.profile.username
- ) {
- req.flash('errors', {
- msg: 'An account with that username already exists.'
- });
- return res.redirect('/account');
- }
- user.email = req.body.email.trim() || '';
- user.profile.name = req.body.name.trim() || '';
- user.profile.username = req.body.username.trim() || '';
- user.profile.location = req.body.location.trim() || '';
- user.profile.githubProfile = req.body.githubProfile.trim() || '';
- user.profile.facebookProfile = req.body.facebookProfile.trim() || '';
- user.profile.linkedinProfile = req.body.linkedinProfile.trim() || '';
- user.profile.codepenProfile = req.body.codepenProfile.trim() || '';
- user.profile.twitterHandle = req.body.twitterHandle.trim() || '';
- user.profile.bio = req.body.bio.trim() || '';
-
- user.profile.picture = req.body.picture.trim() ||
- 'https://s3.amazonaws.com/freecodecamp/' +
- 'camper-image-placeholder.png';
- user.portfolio.website1Title = req.body.website1Title.trim() || '';
- user.portfolio.website1Link = req.body.website1Link.trim() || '';
- user.portfolio.website1Image = req.body.website1Image.trim() || '';
- user.portfolio.website2Title = req.body.website2Title.trim() || '';
- user.portfolio.website2Link = req.body.website2Link.trim() || '';
- user.portfolio.website2Image = req.body.website2Image.trim() || '';
- user.portfolio.website3Title = req.body.website3Title.trim() || '';
- user.portfolio.website3Link = req.body.website3Link.trim() || '';
- user.portfolio.website3Image = req.body.website3Image.trim() || '';
-
-
- user.save(function (err) {
- if (err) {
- return next(err);
- }
- resources.updateUserStoryPictures(
- user._id.toString(),
- user.profile.picture,
- user.profile.username,
- function(err) {
- if (err) { return next(err); }
- req.flash('success', {
- msg: 'Profile information updated.'
- });
- res.redirect('/account');
- }
- );
- });
- }
- );
- });
- });
-}
-
-/**
- * POST /account/password
- * Update current password.
- */
-
-function postUpdatePassword (req, res, next) {
- req.assert('password', 'Password must be at least 4 characters long').len(4);
- req.assert('confirmPassword', 'Passwords do not match')
- .equals(req.body.password);
-
- var errors = req.validationErrors();
-
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/account');
- }
-
- User.findById(req.user.id, function(err, user) {
- if (err) { return next(err); }
-
- user.password = req.body.password;
-
- user.save(function(err) {
+ User.findById(req.user.id, function(err, user) {
if (err) { return next(err); }
- req.flash('success', { msg: 'Password has been changed.' });
- res.redirect('/account');
- });
- });
-}
+ user.password = req.body.password;
-/**
- * POST /account/delete
- * Delete user account.
- */
+ user.save(function(err) {
+ if (err) { return next(err); }
-function postDeleteAccount (req, res, next) {
- User.remove({ _id: req.user.id }, function(err) {
- if (err) { return next(err); }
- req.logout();
- req.flash('info', { msg: 'Your account has been deleted.' });
- res.redirect('/');
- });
-}
-
-/**
- * GET /account/unlink/:provider
- * Unlink OAuth provider.
- */
-
-function getOauthUnlink (req, res, next) {
- var provider = req.params.provider;
- User.findById(req.user.id, function(err, user) {
- if (err) { return next(err); }
-
- user[provider] = null;
- user.tokens =
- _.reject(user.tokens, function(token) {
- return token.kind === provider;
- });
-
- user.save(function(err) {
- if (err) { return next(err); }
- req.flash('info', { msg: provider + ' account has been unlinked.' });
- res.redirect('/account');
- });
- });
-}
-
-/**
- * GET /reset/:token
- * Reset Password page.
- */
-
-function getReset (req, res, next) {
- if (req.isAuthenticated()) {
- return res.redirect('/');
- }
- User
- .findOne({ resetPasswordToken: req.params.token })
- .where('resetPasswordExpires').gt(Date.now())
- .exec(function(err, user) {
- if (err) { return next(err); }
- if (!user) {
- req.flash('errors', {
- msg: 'Password reset token is invalid or has expired.'
- });
- return res.redirect('/forgot');
- }
- res.render('account/reset', {
- title: 'Password Reset',
- token: req.params.token
+ req.flash('success', { msg: 'Password has been changed.' });
+ res.redirect('/account');
});
});
-}
-
-/**
- * POST /reset/:token
- * Process the reset password request.
- */
-
-function postReset (req, res, next) {
- var errors = req.validationErrors();
-
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('back');
}
- async.waterfall([
- function(done) {
- User
- .findOne({ resetPasswordToken: req.params.token })
- .where('resetPasswordExpires').gt(Date.now())
- .exec(function(err, user) {
- if (err) { return next(err); }
- if (!user) {
- req.flash('errors', {
- msg: 'Password reset token is invalid or has expired.'
- });
- return res.redirect('back');
- }
+ /**
+ * POST /account/delete
+ * Delete user account.
+ */
- user.password = req.body.password;
- user.resetPasswordToken = null;
- user.resetPasswordExpires = null;
+ function postDeleteAccount (req, res, next) {
+ User.destroyById(req.user.id, function(err) {
+ if (err) { return next(err); }
+ req.logout();
+ req.flash('info', { msg: 'Your account has been deleted.' });
+ res.redirect('/');
+ });
+ }
- user.save(function(err) {
- if (err) { return done(err); }
- req.logIn(user, function(err) {
- done(err, user);
- });
- });
+ /**
+ * GET /account/unlink/:provider
+ * Unlink OAuth provider.
+ */
+
+ function getOauthUnlink (req, res, next) {
+ var provider = req.params.provider;
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return next(err); }
+
+ user[provider] = null;
+ user.tokens =
+ _.reject(user.tokens, function(token) {
+ return token.kind === provider;
});
- },
- function(user, done) {
- var transporter = nodemailer.createTransport({
- service: 'Mandrill',
- auth: {
- user: secrets.mandrill.user,
- pass: secrets.mandrill.password
- }
- });
- var mailOptions = {
- to: user.email,
- from: 'Team@freecodecamp.com',
- subject: 'Your Free Code Camp password has been changed',
- text: [
- 'Hello,\n\n',
- 'This email is confirming that you requested to',
- 'reset your password for your Free Code Camp account.',
- 'This is your email:',
- user.email,
- '\n'
- ].join(' ')
- };
- transporter.sendMail(mailOptions, function(err) {
- if (err) { return done(err); }
- req.flash('success', {
- msg: 'Success! Your password has been changed.'
- });
- done();
+
+ user.save(function(err) {
+ if (err) { return next(err); }
+ req.flash('info', { msg: provider + ' account has been unlinked.' });
+ res.redirect('/account');
});
+ });
+ }
+
+ /**
+ * GET /reset/:token
+ * Reset Password page.
+ */
+
+ function getReset (req, res, next) {
+ if (req.isAuthenticated()) {
+ return res.redirect('/');
}
- ], function(err) {
- if (err) { return next(err); }
- res.redirect('/');
- });
-}
-
-/**
- * GET /forgot
- * Forgot Password page.
- */
-
-function getForgot (req, res) {
- if (req.isAuthenticated()) {
- return res.redirect('/');
- }
- res.render('account/forgot', {
- title: 'Forgot Password'
- });
-}
-
-/**
- * POST /forgot
- * Create a random token, then the send user an email with a reset link.
- */
-
-function postForgot (req, res, next) {
- var errors = req.validationErrors();
-
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/forgot');
- }
-
- async.waterfall([
- function(done) {
- crypto.randomBytes(16, function(err, buf) {
- if (err) { return done(err); }
- var token = buf.toString('hex');
- done(null, token);
- });
- },
- function(token, done) {
- User.findOne({
- email: req.body.email.toLowerCase()
- }, function(err, user) {
- if (err) { return done(err); }
+ User.findOne(
+ {
+ where: {
+ resetPasswordToken: req.params.token,
+ resetPasswordExpires: Date.now()
+ }
+ },
+ function(err, user) {
+ if (err) { return next(err); }
if (!user) {
req.flash('errors', {
- msg: 'No account with that email address exists.'
+ msg: 'Password reset token is invalid or has expired.'
});
return res.redirect('/forgot');
}
-
- user.resetPasswordToken = token;
- // 3600000 = 1 hour
- user.resetPasswordExpires = Date.now() + 3600000;
-
- user.save(function(err) {
- if (err) { return done(err); }
- done(null, token, user);
+ res.render('account/reset', {
+ title: 'Password Reset',
+ token: req.params.token
});
});
- },
- function(token, user, done) {
- var transporter = nodemailer.createTransport({
- service: 'Mandrill',
- auth: {
- user: secrets.mandrill.user,
- pass: secrets.mandrill.password
- }
- });
- var mailOptions = {
- to: user.email,
- from: 'Team@freecodecamp.com',
- subject: 'Reset your Free Code Camp password',
- text: [
- 'You are receiving this email because you (or someone else)\n',
- 'requested we reset your Free Code Camp account\'s password.\n\n',
- 'Please click on the following link, or paste this into your\n',
- 'browser to complete the process:\n\n',
- 'http://',
- req.headers.host,
- '/reset/',
- token,
- '\n\n',
- 'If you did not request this, please ignore this email and\n',
- 'your password will remain unchanged.\n'
- ].join('')
- };
- transporter.sendMail(mailOptions, function(err) {
- if (err) { return done(err); }
- req.flash('info', {
- msg: 'An e-mail has been sent to ' +
- user.email +
- ' with further instructions.'
- });
- done(null, 'done');
- });
+ }
+
+ /**
+ * POST /reset/:token
+ * Process the reset password request.
+ */
+
+ function postReset (req, res, next) {
+ var errors = req.validationErrors();
+
+ if (errors) {
+ req.flash('errors', errors);
+ return res.redirect('back');
}
- ], function(err) {
- if (err) { return next(err); }
- res.redirect('/forgot');
- });
-}
-module.exports = router;
+ async.waterfall([
+ function(done) {
+ User.findOne(
+ {
+ where: {
+ resetPasswordToken: req.params.token,
+ resetPasswordExpires: Date.now()
+ }
+ },
+ function(err, user) {
+ if (err) { return next(err); }
+ if (!user) {
+ req.flash('errors', {
+ msg: 'Password reset token is invalid or has expired.'
+ });
+ return res.redirect('back');
+ }
+
+ user.password = req.body.password;
+ user.resetPasswordToken = null;
+ user.resetPasswordExpires = null;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+ req.logIn(user, function(err) {
+ done(err, user);
+ });
+ });
+ });
+ },
+ function(user, done) {
+ var transporter = nodemailer.createTransport({
+ service: 'Mandrill',
+ auth: {
+ user: secrets.mandrill.user,
+ pass: secrets.mandrill.password
+ }
+ });
+ var mailOptions = {
+ to: user.email,
+ from: 'Team@freecodecamp.com',
+ subject: 'Your Free Code Camp password has been changed',
+ text: [
+ 'Hello,\n\n',
+ 'This email is confirming that you requested to',
+ 'reset your password for your Free Code Camp account.',
+ 'This is your email:',
+ user.email,
+ '\n'
+ ].join(' ')
+ };
+ transporter.sendMail(mailOptions, function(err) {
+ if (err) { return done(err); }
+ req.flash('success', {
+ msg: 'Success! Your password has been changed.'
+ });
+ done();
+ });
+ }
+ ], function(err) {
+ if (err) { return next(err); }
+ res.redirect('/');
+ });
+ }
+
+ /**
+ * GET /forgot
+ * Forgot Password page.
+ */
+
+ function getForgot (req, res) {
+ if (req.isAuthenticated()) {
+ return res.redirect('/');
+ }
+ res.render('account/forgot', {
+ title: 'Forgot Password'
+ });
+ }
+
+ /**
+ * POST /forgot
+ * Create a random token, then the send user an email with a reset link.
+ */
+
+ function postForgot (req, res, next) {
+ var errors = req.validationErrors();
+
+ if (errors) {
+ req.flash('errors', errors);
+ return res.redirect('/forgot');
+ }
+
+ async.waterfall([
+ function(done) {
+ crypto.randomBytes(16, function(err, buf) {
+ if (err) { return done(err); }
+ var token = buf.toString('hex');
+ done(null, token);
+ });
+ },
+ function(token, done) {
+ User.findOne({
+ email: req.body.email.toLowerCase()
+ }, function(err, user) {
+ if (err) { return done(err); }
+ if (!user) {
+ req.flash('errors', {
+ msg: 'No account with that email address exists.'
+ });
+ return res.redirect('/forgot');
+ }
+
+ user.resetPasswordToken = token;
+ // 3600000 = 1 hour
+ user.resetPasswordExpires = Date.now() + 3600000;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, token, user);
+ });
+ });
+ },
+ function(token, user, done) {
+ var transporter = nodemailer.createTransport({
+ service: 'Mandrill',
+ auth: {
+ user: secrets.mandrill.user,
+ pass: secrets.mandrill.password
+ }
+ });
+ var mailOptions = {
+ to: user.email,
+ from: 'Team@freecodecamp.com',
+ subject: 'Reset your Free Code Camp password',
+ text: [
+ 'You are receiving this email because you (or someone else)\n',
+ 'requested we reset your Free Code Camp account\'s password.\n\n',
+ 'Please click on the following link, or paste this into your\n',
+ 'browser to complete the process:\n\n',
+ 'http://',
+ req.headers.host,
+ '/reset/',
+ token,
+ '\n\n',
+ 'If you did not request this, please ignore this email and\n',
+ 'your password will remain unchanged.\n'
+ ].join('')
+ };
+ transporter.sendMail(mailOptions, function(err) {
+ if (err) { return done(err); }
+ req.flash('info', {
+ msg: 'An e-mail has been sent to ' +
+ user.email +
+ ' with further instructions.'
+ });
+ done(null, 'done');
+ });
+ }
+ ], function(err) {
+ if (err) { return next(err); }
+ res.redirect('/forgot');
+ });
+ }
+};
diff --git a/server/boot/utility.js b/server/boot/utility.js
index 84cb611630..1c78fb409c 100644
--- a/server/boot/utility.js
+++ b/server/boot/utility.js
@@ -1,254 +1,302 @@
-var express = require('express'),
+var Rx = require('rx'),
+ Twit = require('twit'),
async = require('async'),
moment = require('moment'),
- Twit = require('twit'),
Slack = require('node-slack'),
request = require('request'),
debug = require('debug')('freecc:cntr:resources'),
- constantStrings = require('../resources/constantStrings.json'),
- User = require('../../common/models/User'),
- Challenge = require('../../common/models/Challenge'),
- Story = require('../../common/models/Story'),
- FieldGuide = require('../../common/models/FieldGuide'),
- Nonprofit = require('../../common/models/Nonprofit'),
+ constantStrings = require('../resources/constantStrings.json'),
secrets = require('../../config/secrets');
var slack = new Slack(secrets.slackHook);
-var router = express.Router();
+module.exports = function(app) {
+ var router = app.Router();
+ var User = app.models.User;
+ var Challenge = app.models.Challenge;
+ var Story = app.models.Store;
+ var FieldGuide = app.models.FieldGuide;
+ var Nonprofit = app.models.Nonprofit;
-router.get('/api/github', githubCalls);
-router.get('/api/blogger', bloggerCalls);
-router.get('/api/trello', trelloCalls);
-router.get('/api/codepen/twitter/:screenName', twitter);
-router.get('/sitemap.xml', sitemap);
-router.post('/get-help', getHelp);
-router.post('/get-pair', getPair);
-router.get('/chat', chat);
-router.get('/twitch', twitch);
-router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
-router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
-router.get('/nonprofits', nonprofits);
-router.get('/nonprofits-form', nonprofitsForm);
-router.get('/jobs-form', jobsForm);
-router.get('/submit-cat-photo', catPhotoSubmit);
-router.get('/unsubscribe/:email', unsubscribe);
-router.get('/unsubscribed', unsubscribed);
-router.get('/cats.json', getCats);
+ router.get('/api/github', githubCalls);
+ router.get('/api/blogger', bloggerCalls);
+ router.get('/api/trello', trelloCalls);
+ router.get('/api/codepen/twitter/:screenName', twitter);
+ router.get('/sitemap.xml', sitemap);
+ router.post('/get-help', getHelp);
+ router.post('/get-pair', getPair);
+ router.get('/chat', chat);
+ router.get('/twitch', twitch);
+ router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
+ router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
+ router.get('/nonprofits', nonprofits);
+ router.get('/nonprofits-form', nonprofitsForm);
+ router.get('/jobs-form', jobsForm);
+ router.get('/submit-cat-photo', catPhotoSubmit);
+ router.get('/unsubscribe/:email', unsubscribe);
+ router.get('/unsubscribed', unsubscribed);
+ router.get('/cats.json', getCats);
-router.get('/api/slack', slackInvite);
+ router.get('/api/slack', slackInvite);
-function slackInvite(req, res, next) {
- if (req.user) {
- if (req.user.email) {
- var invite = {
- 'email': req.user.email,
- 'token': process.env.SLACK_KEY,
- 'set_active': true
- };
+ function slackInvite(req, res, next) {
+ if (req.user) {
+ if (req.user.email) {
+ var invite = {
+ 'email': req.user.email,
+ 'token': process.env.SLACK_KEY,
+ 'set_active': true
+ };
- var headers = {
- 'User-Agent': 'Node Browser/0.0.1',
- 'Content-Type': 'application/x-www-form-urlencoded'
- };
+ var headers = {
+ 'User-Agent': 'Node Browser/0.0.1',
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ };
- var options = {
- url: 'https://freecodecamp.slack.com/api/users.admin.invite',
- method: 'POST',
- headers: headers,
- form: invite
- };
+ var options = {
+ url: 'https://freecodecamp.slack.com/api/users.admin.invite',
+ method: 'POST',
+ headers: headers,
+ form: invite
+ };
- request(options, function (error, response) {
- if (!error && response.statusCode === 200) {
- req.flash('success', {
- msg: 'We\'ve successfully requested an invite for you.' +
- ' Please check your email and follow the instructions from Slack.'
- });
- req.user.sentSlackInvite = true;
- req.user.save(function(err) {
- if (err) {
- return next(err);
- }
+ request(options, function (error, response) {
+ if (!error && response.statusCode === 200) {
+ req.flash('success', {
+ msg: 'We\'ve successfully requested an invite for you.' +
+ ' Please check your email and follow the ' +
+ 'instructions from Slack.'
+ });
+ req.user.sentSlackInvite = true;
+ req.user.save(function(err) {
+ if (err) {
+ return next(err);
+ }
+ return res.redirect('back');
+ });
+ } else {
+ req.flash('errors', {
+ msg: 'The invitation email did not go through for some reason.' +
+ ' Please try again or ' +
+ 'email us.'
+ });
return res.redirect('back');
- });
- } else {
- req.flash('errors', {
- msg: 'The invitation email did not go through for some reason.' +
- ' Please try again or ' +
- 'email us.'
- });
- return res.redirect('back');
- }
- });
+ }
+ });
+ } else {
+ req.flash('notice', {
+ msg: 'Before we can send your Slack invite, we need your email ' +
+ 'address. Please update your profile information here.'
+ });
+ return res.redirect('/account');
+ }
} else {
req.flash('notice', {
- msg: 'Before we can send your Slack invite, we need your email ' +
- 'address. Please update your profile information here.'
+ msg: 'You need to sign in to Free Code Camp before ' +
+ 'we can send you a Slack invite.'
});
return res.redirect('/account');
}
- } else {
- req.flash('notice', {
- msg: 'You need to sign in to Free Code Camp before ' +
- 'we can send you a Slack invite.'
+ }
+
+ function twitter(req, res, next) {
+ // sends out random tweets about javascript
+ var T = new Twit({
+ 'consumer_key': secrets.twitter.consumerKey,
+ 'consumer_secret': secrets.twitter.consumerSecret,
+ 'access_token': secrets.twitter.token,
+ 'access_token_secret': secrets.twitter.tokenSecret
});
- return res.redirect('/account');
- }
-}
-function twitter(req, res, next) {
- // sends out random tweets about javascript
- var T = new Twit({
- 'consumer_key': secrets.twitter.consumerKey,
- 'consumer_secret': secrets.twitter.consumerSecret,
- 'access_token': secrets.twitter.token,
- 'access_token_secret': secrets.twitter.tokenSecret
- });
-
- var screenName;
- if (req.params.screenName) {
- screenName = req.params.screenName;
- } else {
- screenName = 'freecodecamp';
- }
-
- T.get(
- 'statuses/user_timeline',
- {
- 'screen_name': screenName,
- count: 10
- },
- function(err, data) {
- if (err) { return next(err); }
- return res.json(data);
+ var screenName;
+ if (req.params.screenName) {
+ screenName = req.params.screenName;
+ } else {
+ screenName = 'freecodecamp';
}
- );
-}
-
-function getHelp(req, res) {
- var userName = req.user.profile.username;
- var code = req.body.payload.code ? '\n```\n' +
- req.body.payload.code + '\n```\n'
- : '';
- var challenge = req.body.payload.challenge;
-
- slack.send({
- text: '*@' + userName + '* wants help with ' + challenge + '. ' +
- code + 'Hey, *@' + userName + '*, if no one helps you right ' +
- 'away, try typing out your problem in detail to me. Like this: ' +
- 'http://en.wikipedia.org/wiki/Rubber_duck_debugging',
- channel: '#help',
- username: 'Debuggy the Rubber Duck',
- 'icon_url': 'https://pbs.twimg.com/profile_images/' +
- '3609875545/569237541c920fa78d78902069615caf.jpeg'
- });
- return res.sendStatus(200);
-}
-
-function getPair(req, res) {
- var userName = req.user.profile.username;
- var challenge = req.body.payload.challenge;
- slack.send({
- text: [
- 'Anyone want to pair with *@',
- userName,
- '* on ',
- challenge,
- '?\nMake sure you install Screen Hero here: ',
- 'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n',
- 'Then start your pair program session with *@',
- userName,
- '* by typing \"/hero @',
- userName,
- '\" into Slack.\n And *@',
- userName,
- '*, be sure to launch Screen Hero, then keep coding. ',
- 'Another camper may pair with you soon.'
- ].join(''),
- channel: '#letspair',
- username: 'Companion Cube',
- 'icon_url': 'https://lh3.googleusercontent.com/-f6xDPDV2rPE/AAAAAAAAAAI/' +
- 'AAAAAAAAAAA/mdlESXQu11Q/photo.jpg'
- });
- return res.sendStatus(200);
-}
-
-function sitemap(req, res, next) {
- var appUrl = 'http://www.freecodecamp.com';
- var now = moment(new Date()).format('YYYY-MM-DD');
-
-
- async.parallel({
- users: function(callback) {
- User.aggregate()
- .group({_id: 1, usernames: { $addToSet: '$profile.username'}})
- .match({'profile.username': { $ne: ''}})
- .exec(function(err, users) {
- if (err) {
- debug('User err: ', err);
- callback(err);
- } else {
- callback(null, users[0].usernames);
- }
- });
+ T.get(
+ 'statuses/user_timeline',
+ {
+ 'screen_name': screenName,
+ count: 10
},
-
- challenges: function (callback) {
- Challenge.aggregate()
- .group({_id: 1, names: { $addToSet: '$name'}})
- .exec(function (err, challenges) {
- if (err) {
- debug('Challenge err: ', err);
- callback(err);
- } else {
- callback(null, challenges[0].names);
- }
- });
- },
- stories: function (callback) {
- Story.aggregate()
- .group({_id: 1, links: {$addToSet: '$link'}})
- .exec(function (err, stories) {
- if (err) {
- debug('Story err: ', err);
- callback(err);
- } else {
- callback(null, stories[0].links);
- }
- });
- },
- nonprofits: function (callback) {
- Nonprofit.aggregate()
- .group({_id: 1, names: { $addToSet: '$name'}})
- .exec(function (err, nonprofits) {
- if (err) {
- debug('User err: ', err);
- callback(err);
- } else {
- callback(null, nonprofits[0].names);
- }
- });
- },
- fieldGuides: function (callback) {
- FieldGuide.aggregate()
- .group({_id: 1, names: { $addToSet: '$name'}})
- .exec(function (err, fieldGuides) {
- if (err) {
- debug('User err: ', err);
- callback(err);
- } else {
- callback(null, fieldGuides[0].names);
- }
- });
+ function(err, data) {
+ if (err) { return next(err); }
+ return res.json(data);
}
- }, function (err, results) {
- if (err) {
- return next(err);
- } else {
+ );
+ }
+
+
+ function getHelp(req, res) {
+ var userName = req.user.profile.username;
+ var code = req.body.payload.code ? '\n```\n' +
+ req.body.payload.code + '\n```\n'
+ : '';
+ var challenge = req.body.payload.challenge;
+
+ slack.send({
+ text: '*@' + userName + '* wants help with ' + challenge + '. ' +
+ code + 'Hey, *@' + userName + '*, if no one helps you right ' +
+ 'away, try typing out your problem in detail to me. Like this: ' +
+ 'http://en.wikipedia.org/wiki/Rubber_duck_debugging',
+ channel: '#help',
+ username: 'Debuggy the Rubber Duck',
+ 'icon_url': 'https://pbs.twimg.com/profile_images/' +
+ '3609875545/569237541c920fa78d78902069615caf.jpeg'
+ });
+ return res.sendStatus(200);
+ }
+
+ function getPair(req, res) {
+ var userName = req.user.profile.username;
+ var challenge = req.body.payload.challenge;
+ slack.send({
+ text: [
+ 'Anyone want to pair with *@',
+ userName,
+ '* on ',
+ challenge,
+ '?\nMake sure you install Screen Hero here: ',
+ 'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n',
+ 'Then start your pair program session with *@',
+ userName,
+ '* by typing \"/hero @',
+ userName,
+ '\" into Slack.\n And *@',
+ userName,
+ '*, be sure to launch Screen Hero, then keep coding. ',
+ 'Another camper may pair with you soon.'
+ ].join(''),
+ channel: '#letspair',
+ username: 'Companion Cube',
+ 'icon_url':
+ 'https://lh3.googleusercontent.com/-f6xDPDV2rPE/AAAAAAAAAAI/' +
+ 'AAAAAAAAAAA/mdlESXQu11Q/photo.jpg'
+ });
+ return res.sendStatus(200);
+ }
+
+ function sitemap(req, res, next) {
+ var appUrl = 'http://www.freecodecamp.com';
+ var now = moment(new Date()).format('YYYY-MM-DD');
+
+ // TODO(berks): refactor async to rx
+ async.parallel({
+ users: function(callback) {
+ User.find(
+ {
+ where: { 'profile.username': { nlike: '' } },
+ fields: { 'profile.username': true }
+ },
+ function(err, users) {
+ if (err) {
+ debug('User err: ', err);
+ callback(err);
+ } else {
+ Rx.Observable.from(users)
+ .map(function(user) {
+ return user.profile.username;
+ })
+ .toArray()
+ .subscribe(
+ function(usernames) {
+ callback(null, usernames);
+ },
+ callback
+ );
+ }
+ });
+ },
+
+ challenges: function (callback) {
+ Challenge.find(
+ { fields: { name: true } },
+ function (err, challenges) {
+ if (err) {
+ debug('Challenge err: ', err);
+ callback(err);
+ } else {
+ Rx.Observable.from(challenges)
+ .map(function(challenge) {
+ return challenge.name;
+ })
+ .toArray()
+ .subscribe(
+ callback.bind(callback, null),
+ callback
+ );
+ }
+ });
+ },
+ stories: function (callback) {
+ Story.find(
+ { field: { link: true } },
+ function (err, stories) {
+ if (err) {
+ debug('Story err: ', err);
+ callback(err);
+ } else {
+ Rx.Observable.from(stories)
+ .map(function(story) {
+ return story.link;
+ })
+ .toArray()
+ .subscribe(
+ callback.bind(callback, null),
+ callback
+ );
+ }
+ }
+ );
+ },
+ nonprofits: function (callback) {
+ Nonprofit.find(
+ { field: { name: true } },
+ function(err, nonprofits) {
+ if (err) {
+ debug('User err: ', err);
+ callback(err);
+ } else {
+ Rx.Observable.from(nonprofits)
+ .map(function(nonprofit) {
+ return nonprofit.name;
+ })
+ .toArray()
+ .subscribe(
+ callback.bind(callback, null),
+ callback
+ );
+ }
+ });
+ },
+ fieldGuides: function(callback) {
+ FieldGuide.find(
+ { field: { name: true } },
+ function(err, fieldGuides) {
+ if (err) {
+ debug('User err: ', err);
+ callback(err);
+ } else {
+ Rx.Observable.from(fieldGuides)
+ .map(function(fieldGuide) {
+ return fieldGuide.name;
+ })
+ .toArray()
+ .subscribe(
+ callback.bind(callback, null),
+ callback
+ );
+ }
+ });
+ }
+ }, function(err, results) {
+ if (err) {
+ return next(err);
+ }
setTimeout(function() {
res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', {
@@ -262,185 +310,183 @@ function sitemap(req, res, next) {
});
}, 0);
}
- }
- );
-}
+ );
+ }
-function chat(req, res) {
- if (req.user && req.user.progressTimestamps.length > 5) {
- res.redirect('http://freecodecamp.slack.com');
- } else {
- res.render('resources/chat', {
- title: 'Watch us code live on Twitch.tv'
+ function chat(req, res) {
+ if (req.user && req.user.progressTimestamps.length > 5) {
+ res.redirect('http://freecodecamp.slack.com');
+ } else {
+ res.render('resources/chat', {
+ title: 'Watch us code live on Twitch.tv'
+ });
+ }
+ }
+
+ function jobsForm(req, res) {
+ res.render('resources/jobs-form', {
+ title: 'Employer Partnership Form for Job Postings,' +
+ ' Recruitment and Corporate Sponsorships'
});
}
-}
-function jobsForm(req, res) {
- res.render('resources/jobs-form', {
- title: 'Employer Partnership Form for Job Postings,' +
- ' Recruitment and Corporate Sponsorships'
- });
-}
+ function catPhotoSubmit(req, res) {
+ res.send(
+ 'Success! You have submitted your cat photo. Return to your website ' +
+ 'by typing any letter into your code editor.'
+ );
+ }
-function catPhotoSubmit(req, res) {
- res.send(
- 'Success! You have submitted your cat photo. Return to your website ' +
- 'by typing any letter into your code editor.'
- );
-}
+ function nonprofits(req, res) {
+ res.render('resources/nonprofits', {
+ title: 'A guide to our Nonprofit Projects'
+ });
+ }
-function nonprofits(req, res) {
- res.render('resources/nonprofits', {
- title: 'A guide to our Nonprofit Projects'
- });
-}
+ function nonprofitsForm(req, res) {
+ res.render('resources/nonprofits-form', {
+ title: 'Nonprofit Projects Proposal Form'
+ });
+ }
-function nonprofitsForm(req, res) {
- res.render('resources/nonprofits-form', {
- title: 'Nonprofit Projects Proposal Form'
- });
-}
+ function agileProjectManagers(req, res) {
+ res.render('resources/pmi-acp-agile-project-managers', {
+ title: 'Get Agile Project Management Experience for the PMI-ACP'
+ });
+ }
-function agileProjectManagers(req, res) {
- res.render('resources/pmi-acp-agile-project-managers', {
- title: 'Get Agile Project Management Experience for the PMI-ACP'
- });
-}
+ function agileProjectManagersForm(req, res) {
+ res.render('resources/pmi-acp-agile-project-managers-form', {
+ title: 'Agile Project Management Program Application Form'
+ });
+ }
-function agileProjectManagersForm(req, res) {
- res.render('resources/pmi-acp-agile-project-managers-form', {
- title: 'Agile Project Management Program Application Form'
- });
-}
+ function twitch(req, res) {
+ res.render('resources/twitch', {
+ title: 'Enter Free Code Camp\'s Chat Rooms'
+ });
+ }
-function twitch(req, res) {
- res.render('resources/twitch', {
- title: 'Enter Free Code Camp\'s Chat Rooms'
- });
-}
-
-function unsubscribe(req, res, next) {
- User.findOne({ email: req.params.email }, function(err, user) {
- if (user) {
- if (err) {
- return next(err);
- }
- user.sendMonthlyEmail = false;
- user.save(function () {
+ function unsubscribe(req, res, next) {
+ User.findOne({ email: req.params.email }, function(err, user) {
+ if (user) {
if (err) {
return next(err);
}
+ user.sendMonthlyEmail = false;
+ user.save(function () {
+ if (err) {
+ return next(err);
+ }
+ res.redirect('/unsubscribed');
+ });
+ } else {
res.redirect('/unsubscribed');
- });
- } else {
- res.redirect('/unsubscribed');
- }
- });
-}
-
-function unsubscribed(req, res) {
- res.render('resources/unsubscribed', {
- title: 'You have been unsubscribed'
- });
-}
-
-function githubCalls(req, res, next) {
- var githubHeaders = {
- headers: {
- 'User-Agent': constantStrings.gitHubUserAgent
- },
- port: 80
- };
- request(
- [
- 'https://api.github.com/repos/freecodecamp/',
- 'freecodecamp/pulls?client_id=',
- secrets.github.clientID,
- '&client_secret=',
- secrets.github.clientSecret
- ].join(''),
- githubHeaders,
- function(err, status1, pulls) {
- if (err) { return next(err); }
- pulls = pulls ?
- Object.keys(JSON.parse(pulls)).length :
- 'Can\'t connect to github';
-
- request(
- [
- 'https://api.github.com/repos/freecodecamp/',
- 'freecodecamp/issues?client_id=',
- secrets.github.clientID,
- '&client_secret=',
- secrets.github.clientSecret
- ].join(''),
- githubHeaders,
- function (err, status2, issues) {
- if (err) { return next(err); }
- issues = ((pulls === parseInt(pulls, 10)) && issues) ?
- Object.keys(JSON.parse(issues)).length - pulls :
- "Can't connect to GitHub";
- res.send({
- issues: issues,
- pulls: pulls
- });
- }
- );
- }
- );
-}
-
-function trelloCalls(req, res, next) {
- request(
- 'https://trello.com/1/boards/BA3xVpz9/cards?key=' +
- secrets.trello.key,
- function(err, status, trello) {
- if (err) { return next(err); }
- trello = (status && status.statusCode === 200) ?
- (JSON.parse(trello)) :
- 'Can\'t connect to to Trello';
-
- res.end(JSON.stringify(trello));
- });
-}
-
-function bloggerCalls(req, res, next) {
- request(
- 'https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/' +
- 'posts?key=' +
- secrets.blogger.key,
- function (err, status, blog) {
- if (err) { return next(err); }
-
- blog = (status && status.statusCode === 200) ?
- JSON.parse(blog) :
- 'Can\'t connect to Blogger';
- res.end(JSON.stringify(blog));
- }
- );
-}
-
-function getCats(req, res) {
- res.send(
- [
- {
- 'name': 'cute',
- 'imageLink': 'https://encrypted-tbn3.gstatic.com/images' +
- '?q=tbn:ANd9GcRaP1ecF2jerISkdhjr4R9yM9-8ClUy-TA36MnDiFBukd5IvEME0g'
- },
- {
- 'name': 'grumpy',
- 'imageLink': 'http://cdn.grumpycats.com/wp-content/uploads/' +
- '2012/09/GC-Gravatar-copy.png'
- },
- {
- 'name': 'mischievous',
- 'imageLink': 'http://www.kittenspet.com/wp-content' +
- '/uploads/2012/08/cat_with_funny_face_3-200x200.jpg'
}
- ]
- );
-}
+ });
+ }
-module.exports = router;
+ function unsubscribed(req, res) {
+ res.render('resources/unsubscribed', {
+ title: 'You have been unsubscribed'
+ });
+ }
+
+ function githubCalls(req, res, next) {
+ var githubHeaders = {
+ headers: {
+ 'User-Agent': constantStrings.gitHubUserAgent
+ },
+ port: 80
+ };
+ request(
+ [
+ 'https://api.github.com/repos/freecodecamp/',
+ 'freecodecamp/pulls?client_id=',
+ secrets.github.clientID,
+ '&client_secret=',
+ secrets.github.clientSecret
+ ].join(''),
+ githubHeaders,
+ function(err, status1, pulls) {
+ if (err) { return next(err); }
+ pulls = pulls ?
+ Object.keys(JSON.parse(pulls)).length :
+ 'Can\'t connect to github';
+
+ request(
+ [
+ 'https://api.github.com/repos/freecodecamp/',
+ 'freecodecamp/issues?client_id=',
+ secrets.github.clientID,
+ '&client_secret=',
+ secrets.github.clientSecret
+ ].join(''),
+ githubHeaders,
+ function (err, status2, issues) {
+ if (err) { return next(err); }
+ issues = ((pulls === parseInt(pulls, 10)) && issues) ?
+ Object.keys(JSON.parse(issues)).length - pulls :
+ "Can't connect to GitHub";
+ res.send({
+ issues: issues,
+ pulls: pulls
+ });
+ }
+ );
+ }
+ );
+ }
+
+ function trelloCalls(req, res, next) {
+ request(
+ 'https://trello.com/1/boards/BA3xVpz9/cards?key=' +
+ secrets.trello.key,
+ function(err, status, trello) {
+ if (err) { return next(err); }
+ trello = (status && status.statusCode === 200) ?
+ (JSON.parse(trello)) :
+ 'Can\'t connect to to Trello';
+
+ res.end(JSON.stringify(trello));
+ });
+ }
+
+ function bloggerCalls(req, res, next) {
+ request(
+ 'https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/' +
+ 'posts?key=' +
+ secrets.blogger.key,
+ function (err, status, blog) {
+ if (err) { return next(err); }
+
+ blog = (status && status.statusCode === 200) ?
+ JSON.parse(blog) :
+ 'Can\'t connect to Blogger';
+ res.end(JSON.stringify(blog));
+ }
+ );
+ }
+
+ function getCats(req, res) {
+ res.send(
+ [
+ {
+ 'name': 'cute',
+ 'imageLink': 'https://encrypted-tbn3.gstatic.com/images' +
+ '?q=tbn:ANd9GcRaP1ecF2jerISkdhjr4R9yM9-8ClUy-TA36MnDiFBukd5IvEME0g'
+ },
+ {
+ 'name': 'grumpy',
+ 'imageLink': 'http://cdn.grumpycats.com/wp-content/uploads/' +
+ '2012/09/GC-Gravatar-copy.png'
+ },
+ {
+ 'name': 'mischievous',
+ 'imageLink': 'http://www.kittenspet.com/wp-content' +
+ '/uploads/2012/08/cat_with_funny_face_3-200x200.jpg'
+ }
+ ]
+ );
+ }
+};